@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,15 @@
|
|
|
1
|
+
import type { ResolvedSurfacePlan } from './resolve-bundle';
|
|
2
|
+
import type { SurfacePatchOp } from '../spec/types';
|
|
3
|
+
export interface ApplySurfacePatchResult {
|
|
4
|
+
plan: ResolvedSurfacePlan;
|
|
5
|
+
inverseOps: SurfacePatchOp[];
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Applies surface patch operations to a resolved plan.
|
|
9
|
+
* Returns a new plan and inverse ops for undo; does not mutate the input.
|
|
10
|
+
*
|
|
11
|
+
* @param plan - The current resolved surface plan
|
|
12
|
+
* @param ops - Patch operations to apply
|
|
13
|
+
* @returns Updated plan and inverse ops for undo
|
|
14
|
+
*/
|
|
15
|
+
export declare function applySurfacePatch(plan: ResolvedSurfacePlan, ops: SurfacePatchOp[]): ApplySurfacePatchResult;
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// src/spec/validate-surface-patch.ts
|
|
3
|
+
var VALID_OPS = [
|
|
4
|
+
"insert-node",
|
|
5
|
+
"replace-node",
|
|
6
|
+
"remove-node",
|
|
7
|
+
"move-node",
|
|
8
|
+
"resize-panel",
|
|
9
|
+
"set-layout",
|
|
10
|
+
"reveal-field",
|
|
11
|
+
"hide-field",
|
|
12
|
+
"promote-action",
|
|
13
|
+
"set-focus"
|
|
14
|
+
];
|
|
15
|
+
var VALID_NODE_KINDS = [
|
|
16
|
+
"metric-strip",
|
|
17
|
+
"data-view",
|
|
18
|
+
"entity-card",
|
|
19
|
+
"entity-header",
|
|
20
|
+
"entity-summary",
|
|
21
|
+
"entity-section",
|
|
22
|
+
"entity-field",
|
|
23
|
+
"entity-activity",
|
|
24
|
+
"entity-relations",
|
|
25
|
+
"entity-timeline",
|
|
26
|
+
"entity-comments",
|
|
27
|
+
"entity-attachments",
|
|
28
|
+
"entity-view-switcher",
|
|
29
|
+
"entity-automation-panel",
|
|
30
|
+
"rich-doc",
|
|
31
|
+
"chat-thread",
|
|
32
|
+
"assistant-panel",
|
|
33
|
+
"action-bar",
|
|
34
|
+
"timeline",
|
|
35
|
+
"board",
|
|
36
|
+
"table",
|
|
37
|
+
"calendar",
|
|
38
|
+
"form",
|
|
39
|
+
"chart",
|
|
40
|
+
"relation-graph",
|
|
41
|
+
"custom-widget"
|
|
42
|
+
];
|
|
43
|
+
function validateSurfaceNode(node, path) {
|
|
44
|
+
if (!node.nodeId || typeof node.nodeId !== "string") {
|
|
45
|
+
throw new Error(`${path}: nodeId must be a non-empty string`);
|
|
46
|
+
}
|
|
47
|
+
if (!node.kind || !VALID_NODE_KINDS.includes(node.kind)) {
|
|
48
|
+
throw new Error(`${path}: kind must be one of ${VALID_NODE_KINDS.join(", ")}`);
|
|
49
|
+
}
|
|
50
|
+
if (node.children) {
|
|
51
|
+
for (let i = 0;i < node.children.length; i++) {
|
|
52
|
+
const child = node.children[i];
|
|
53
|
+
if (child)
|
|
54
|
+
validateSurfaceNode(child, `${path}.children[${i}]`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function validateSurfacePatchOp(op, index) {
|
|
59
|
+
const path = `ops[${index}]`;
|
|
60
|
+
if (!op || typeof op !== "object" || !("op" in op)) {
|
|
61
|
+
throw new Error(`${path}: must be an object with op field`);
|
|
62
|
+
}
|
|
63
|
+
const opType = op.op;
|
|
64
|
+
if (!VALID_OPS.includes(opType)) {
|
|
65
|
+
throw new Error(`${path}: op must be one of ${VALID_OPS.join(", ")}`);
|
|
66
|
+
}
|
|
67
|
+
switch (op.op) {
|
|
68
|
+
case "insert-node":
|
|
69
|
+
if (!op.slotId || typeof op.slotId !== "string") {
|
|
70
|
+
throw new Error(`${path}: insert-node requires slotId string`);
|
|
71
|
+
}
|
|
72
|
+
if (!op.node) {
|
|
73
|
+
throw new Error(`${path}: insert-node requires node`);
|
|
74
|
+
}
|
|
75
|
+
validateSurfaceNode(op.node, `${path}.node`);
|
|
76
|
+
if (op.index !== undefined && typeof op.index !== "number") {
|
|
77
|
+
throw new Error(`${path}: insert-node index must be number if present`);
|
|
78
|
+
}
|
|
79
|
+
break;
|
|
80
|
+
case "replace-node":
|
|
81
|
+
if (!op.nodeId || typeof op.nodeId !== "string") {
|
|
82
|
+
throw new Error(`${path}: replace-node requires nodeId string`);
|
|
83
|
+
}
|
|
84
|
+
if (!op.node) {
|
|
85
|
+
throw new Error(`${path}: replace-node requires node`);
|
|
86
|
+
}
|
|
87
|
+
validateSurfaceNode(op.node, `${path}.node`);
|
|
88
|
+
break;
|
|
89
|
+
case "remove-node":
|
|
90
|
+
if (!op.nodeId || typeof op.nodeId !== "string") {
|
|
91
|
+
throw new Error(`${path}: remove-node requires nodeId string`);
|
|
92
|
+
}
|
|
93
|
+
break;
|
|
94
|
+
case "move-node":
|
|
95
|
+
if (!op.nodeId || typeof op.nodeId !== "string") {
|
|
96
|
+
throw new Error(`${path}: move-node requires nodeId string`);
|
|
97
|
+
}
|
|
98
|
+
if (!op.toSlotId || typeof op.toSlotId !== "string") {
|
|
99
|
+
throw new Error(`${path}: move-node requires toSlotId string`);
|
|
100
|
+
}
|
|
101
|
+
if (op.index !== undefined && typeof op.index !== "number") {
|
|
102
|
+
throw new Error(`${path}: move-node index must be number if present`);
|
|
103
|
+
}
|
|
104
|
+
break;
|
|
105
|
+
case "resize-panel":
|
|
106
|
+
if (!op.persistKey || typeof op.persistKey !== "string") {
|
|
107
|
+
throw new Error(`${path}: resize-panel requires persistKey string`);
|
|
108
|
+
}
|
|
109
|
+
if (!Array.isArray(op.sizes) || op.sizes.some((s) => typeof s !== "number")) {
|
|
110
|
+
throw new Error(`${path}: resize-panel requires sizes number[]`);
|
|
111
|
+
}
|
|
112
|
+
break;
|
|
113
|
+
case "set-layout":
|
|
114
|
+
if (!op.layoutId || typeof op.layoutId !== "string") {
|
|
115
|
+
throw new Error(`${path}: set-layout requires layoutId string`);
|
|
116
|
+
}
|
|
117
|
+
break;
|
|
118
|
+
case "reveal-field":
|
|
119
|
+
case "hide-field":
|
|
120
|
+
if (!op.fieldId || typeof op.fieldId !== "string") {
|
|
121
|
+
throw new Error(`${path}: ${op.op} requires fieldId string`);
|
|
122
|
+
}
|
|
123
|
+
break;
|
|
124
|
+
case "promote-action": {
|
|
125
|
+
if (!op.actionId || typeof op.actionId !== "string") {
|
|
126
|
+
throw new Error(`${path}: promote-action requires actionId string`);
|
|
127
|
+
}
|
|
128
|
+
const validPlacements = ["header", "inline", "context", "assistant"];
|
|
129
|
+
if (!op.placement || !validPlacements.includes(op.placement)) {
|
|
130
|
+
throw new Error(`${path}: promote-action placement must be one of ${validPlacements.join(", ")}`);
|
|
131
|
+
}
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
case "set-focus":
|
|
135
|
+
if (!op.targetId || typeof op.targetId !== "string") {
|
|
136
|
+
throw new Error(`${path}: set-focus requires targetId string`);
|
|
137
|
+
}
|
|
138
|
+
break;
|
|
139
|
+
default:
|
|
140
|
+
throw new Error(`${path}: unknown op "${opType}"`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
function validateSurfacePatch(ops) {
|
|
144
|
+
if (!Array.isArray(ops)) {
|
|
145
|
+
throw new Error("Patch ops must be an array");
|
|
146
|
+
}
|
|
147
|
+
for (let i = 0;i < ops.length; i++) {
|
|
148
|
+
const op = ops[i];
|
|
149
|
+
if (op)
|
|
150
|
+
validateSurfacePatchOp(op, i);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
function validateSurfaceNodeAgainstKinds(node, allowedKinds, path) {
|
|
154
|
+
if (!allowedKinds.includes(node.kind)) {
|
|
155
|
+
throw new Error(`${path}: kind "${node.kind}" not in allowed list [${allowedKinds.join(", ")}]`);
|
|
156
|
+
}
|
|
157
|
+
if (node.children) {
|
|
158
|
+
for (let i = 0;i < node.children.length; i++) {
|
|
159
|
+
const child = node.children[i];
|
|
160
|
+
if (child)
|
|
161
|
+
validateSurfaceNodeAgainstKinds(child, allowedKinds, `${path}.children[${i}]`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
function validatePatchProposal(ops, constraints) {
|
|
166
|
+
if (!Array.isArray(ops)) {
|
|
167
|
+
throw new Error("Patch ops must be an array");
|
|
168
|
+
}
|
|
169
|
+
const { allowedOps, allowedSlots, allowedNodeKinds } = constraints;
|
|
170
|
+
for (let i = 0;i < ops.length; i++) {
|
|
171
|
+
const op = ops[i];
|
|
172
|
+
if (!op)
|
|
173
|
+
continue;
|
|
174
|
+
const path = `ops[${i}]`;
|
|
175
|
+
if (!allowedOps.includes(op.op)) {
|
|
176
|
+
throw new Error(`${path}: op "${op.op}" not in allowed list [${allowedOps.join(", ")}]`);
|
|
177
|
+
}
|
|
178
|
+
switch (op.op) {
|
|
179
|
+
case "insert-node":
|
|
180
|
+
if (!allowedSlots.includes(op.slotId)) {
|
|
181
|
+
throw new Error(`${path}: slotId "${op.slotId}" not in allowed slots [${allowedSlots.join(", ")}]`);
|
|
182
|
+
}
|
|
183
|
+
if (op.node) {
|
|
184
|
+
validateSurfaceNodeAgainstKinds(op.node, allowedNodeKinds, `${path}.node`);
|
|
185
|
+
}
|
|
186
|
+
break;
|
|
187
|
+
case "move-node":
|
|
188
|
+
if (!allowedSlots.includes(op.toSlotId)) {
|
|
189
|
+
throw new Error(`${path}: toSlotId "${op.toSlotId}" not in allowed slots [${allowedSlots.join(", ")}]`);
|
|
190
|
+
}
|
|
191
|
+
break;
|
|
192
|
+
case "replace-node":
|
|
193
|
+
if (op.node) {
|
|
194
|
+
validateSurfaceNodeAgainstKinds(op.node, allowedNodeKinds, `${path}.node`);
|
|
195
|
+
}
|
|
196
|
+
break;
|
|
197
|
+
default:
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
validateSurfacePatch(ops);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// src/runtime/apply-surface-patch.ts
|
|
205
|
+
import { Logger } from "@contractspec/lib.observability";
|
|
206
|
+
var logger = new Logger("@contractspec/lib.surface-runtime");
|
|
207
|
+
function findNode(nodes, nodeId) {
|
|
208
|
+
for (const n of nodes) {
|
|
209
|
+
if (n.nodeId === nodeId)
|
|
210
|
+
return n;
|
|
211
|
+
if (n.children) {
|
|
212
|
+
const found = findNode(n.children, nodeId);
|
|
213
|
+
if (found)
|
|
214
|
+
return found;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
function collectNodeIds(nodes) {
|
|
220
|
+
const ids = new Set;
|
|
221
|
+
for (const n of nodes) {
|
|
222
|
+
ids.add(n.nodeId);
|
|
223
|
+
if (n.children)
|
|
224
|
+
collectNodeIds(n.children).forEach((id) => ids.add(id));
|
|
225
|
+
}
|
|
226
|
+
return ids;
|
|
227
|
+
}
|
|
228
|
+
function validateOp(op, nodeIds) {
|
|
229
|
+
if (op.op === "remove-node" || op.op === "replace-node" || op.op === "move-node") {
|
|
230
|
+
if (!nodeIds.has(op.nodeId)) {
|
|
231
|
+
throw new Error(`Patch op references unknown nodeId: ${op.nodeId}`);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
if (op.op === "insert-node" && !op.node?.nodeId) {
|
|
235
|
+
throw new Error("insert-node requires node with nodeId");
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
function produceInverse(op, plan) {
|
|
239
|
+
switch (op.op) {
|
|
240
|
+
case "insert-node":
|
|
241
|
+
return op.node ? { op: "remove-node", nodeId: op.node.nodeId } : null;
|
|
242
|
+
case "remove-node": {
|
|
243
|
+
const node = findNode(plan.nodes, op.nodeId);
|
|
244
|
+
return node ? { op: "insert-node", slotId: "primary", node } : null;
|
|
245
|
+
}
|
|
246
|
+
case "replace-node": {
|
|
247
|
+
const prev = findNode(plan.nodes, op.nodeId);
|
|
248
|
+
return prev ? { op: "replace-node", nodeId: op.nodeId, node: prev } : null;
|
|
249
|
+
}
|
|
250
|
+
case "set-layout":
|
|
251
|
+
return { op: "set-layout", layoutId: plan.layoutId };
|
|
252
|
+
case "reveal-field":
|
|
253
|
+
return { op: "hide-field", fieldId: op.fieldId };
|
|
254
|
+
case "hide-field":
|
|
255
|
+
return { op: "reveal-field", fieldId: op.fieldId };
|
|
256
|
+
case "move-node":
|
|
257
|
+
case "resize-panel":
|
|
258
|
+
case "set-focus":
|
|
259
|
+
case "promote-action":
|
|
260
|
+
return null;
|
|
261
|
+
default:
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
function applySurfacePatch(plan, ops) {
|
|
266
|
+
if (ops.length === 0)
|
|
267
|
+
return { plan, inverseOps: [] };
|
|
268
|
+
validateSurfacePatch(ops);
|
|
269
|
+
const nodeIds = collectNodeIds(plan.nodes);
|
|
270
|
+
for (const op of ops) {
|
|
271
|
+
validateOp(op, nodeIds);
|
|
272
|
+
}
|
|
273
|
+
let nextNodes = [...plan.nodes];
|
|
274
|
+
let nextLayoutId = plan.layoutId;
|
|
275
|
+
const inverseOps = [];
|
|
276
|
+
for (const op of ops) {
|
|
277
|
+
const inv = produceInverse(op, {
|
|
278
|
+
...plan,
|
|
279
|
+
nodes: nextNodes,
|
|
280
|
+
layoutId: nextLayoutId
|
|
281
|
+
});
|
|
282
|
+
if (inv)
|
|
283
|
+
inverseOps.unshift(inv);
|
|
284
|
+
switch (op.op) {
|
|
285
|
+
case "insert-node":
|
|
286
|
+
if (op.node)
|
|
287
|
+
nextNodes = [...nextNodes, op.node];
|
|
288
|
+
break;
|
|
289
|
+
case "remove-node":
|
|
290
|
+
nextNodes = nextNodes.filter((n) => n.nodeId !== op.nodeId);
|
|
291
|
+
break;
|
|
292
|
+
case "replace-node": {
|
|
293
|
+
const replace = (nodes) => nodes.map((n) => n.nodeId === op.nodeId ? op.node : { ...n, children: n.children ? replace(n.children) : undefined });
|
|
294
|
+
nextNodes = replace(nextNodes);
|
|
295
|
+
break;
|
|
296
|
+
}
|
|
297
|
+
case "set-layout":
|
|
298
|
+
nextLayoutId = op.layoutId;
|
|
299
|
+
break;
|
|
300
|
+
case "move-node":
|
|
301
|
+
case "resize-panel":
|
|
302
|
+
case "set-focus":
|
|
303
|
+
case "reveal-field":
|
|
304
|
+
case "hide-field":
|
|
305
|
+
case "promote-action":
|
|
306
|
+
break;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
const result = {
|
|
310
|
+
plan: { ...plan, nodes: nextNodes, layoutId: nextLayoutId },
|
|
311
|
+
inverseOps
|
|
312
|
+
};
|
|
313
|
+
logger.info("bundle.surface.patch.applied", {
|
|
314
|
+
bundleKey: plan.bundleKey,
|
|
315
|
+
surfaceId: plan.surfaceId,
|
|
316
|
+
opCount: ops.length,
|
|
317
|
+
opTypes: [...new Set(ops.map((o) => o.op))]
|
|
318
|
+
});
|
|
319
|
+
return result;
|
|
320
|
+
}
|
|
321
|
+
export {
|
|
322
|
+
applySurfacePatch
|
|
323
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Audit event helpers for surface runtime.
|
|
3
|
+
* Emit patch/overlay/policy events. Integrate with lib.observability or custom backend.
|
|
4
|
+
*/
|
|
5
|
+
import type { BundleAuditEmitter, SurfacePatchProposal } from '../spec/types';
|
|
6
|
+
/** Emit patch.proposed when AI or workspace proposes patches. */
|
|
7
|
+
export declare function emitPatchProposed(audit: BundleAuditEmitter, args: {
|
|
8
|
+
bundleKey: string;
|
|
9
|
+
surfaceId?: string;
|
|
10
|
+
proposal: SurfacePatchProposal;
|
|
11
|
+
actorId?: string;
|
|
12
|
+
}): void;
|
|
13
|
+
/** Emit patch.approved when user accepts a proposal. */
|
|
14
|
+
export declare function emitPatchApproved(audit: BundleAuditEmitter, args: {
|
|
15
|
+
bundleKey: string;
|
|
16
|
+
surfaceId?: string;
|
|
17
|
+
proposalId: string;
|
|
18
|
+
source: SurfacePatchProposal['source'];
|
|
19
|
+
opsCount: number;
|
|
20
|
+
actorId?: string;
|
|
21
|
+
reason?: string;
|
|
22
|
+
}): void;
|
|
23
|
+
/** Emit patch.rejected when user rejects a proposal. */
|
|
24
|
+
export declare function emitPatchRejected(audit: BundleAuditEmitter, args: {
|
|
25
|
+
bundleKey: string;
|
|
26
|
+
surfaceId?: string;
|
|
27
|
+
proposalId: string;
|
|
28
|
+
source: SurfacePatchProposal['source'];
|
|
29
|
+
opsCount: number;
|
|
30
|
+
actorId?: string;
|
|
31
|
+
reason?: string;
|
|
32
|
+
}): void;
|
|
33
|
+
/** Emit overlay.saved when overlay is persisted. */
|
|
34
|
+
export declare function emitOverlaySaved(audit: BundleAuditEmitter, args: {
|
|
35
|
+
bundleKey: string;
|
|
36
|
+
overlayId: string;
|
|
37
|
+
scope: string;
|
|
38
|
+
opsCount: number;
|
|
39
|
+
actorId?: string;
|
|
40
|
+
}): void;
|
|
41
|
+
/** Emit overlay.applied when overlay is applied at runtime. */
|
|
42
|
+
export declare function emitOverlayApplied(audit: BundleAuditEmitter, args: {
|
|
43
|
+
bundleKey: string;
|
|
44
|
+
surfaceId?: string;
|
|
45
|
+
overlayId: string;
|
|
46
|
+
opsCount: number;
|
|
47
|
+
}): void;
|
|
48
|
+
/** Emit overlay.failed when overlay application fails. */
|
|
49
|
+
export declare function emitOverlayFailed(audit: BundleAuditEmitter, args: {
|
|
50
|
+
bundleKey: string;
|
|
51
|
+
surfaceId?: string;
|
|
52
|
+
overlayId: string;
|
|
53
|
+
error: string;
|
|
54
|
+
}): void;
|
|
55
|
+
/** Emit policy.denied when policy blocks an action. */
|
|
56
|
+
export declare function emitPolicyDenied(audit: BundleAuditEmitter, args: {
|
|
57
|
+
bundleKey: string;
|
|
58
|
+
surfaceId?: string;
|
|
59
|
+
targetId: string;
|
|
60
|
+
reason?: string;
|
|
61
|
+
actorId?: string;
|
|
62
|
+
}): void;
|
|
63
|
+
/** Emit policy.redacted when policy redacts content. */
|
|
64
|
+
export declare function emitPolicyRedacted(audit: BundleAuditEmitter, args: {
|
|
65
|
+
bundleKey: string;
|
|
66
|
+
surfaceId?: string;
|
|
67
|
+
targetId: string;
|
|
68
|
+
redactions?: string[];
|
|
69
|
+
actorId?: string;
|
|
70
|
+
}): void;
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// src/runtime/audit-events.ts
|
|
3
|
+
function generateEventId() {
|
|
4
|
+
return `evt_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
|
|
5
|
+
}
|
|
6
|
+
function emitPatchProposed(audit, args) {
|
|
7
|
+
audit.emit({
|
|
8
|
+
eventId: generateEventId(),
|
|
9
|
+
at: new Date().toISOString(),
|
|
10
|
+
actorId: args.actorId,
|
|
11
|
+
source: args.proposal.source === "assistant" ? "assistant" : "user",
|
|
12
|
+
bundleKey: args.bundleKey,
|
|
13
|
+
surfaceId: args.surfaceId,
|
|
14
|
+
eventType: "patch.proposed",
|
|
15
|
+
payload: {
|
|
16
|
+
proposalId: args.proposal.proposalId,
|
|
17
|
+
source: args.proposal.source,
|
|
18
|
+
opsCount: args.proposal.ops.length
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
function emitPatchApproved(audit, args) {
|
|
23
|
+
audit.emit({
|
|
24
|
+
eventId: generateEventId(),
|
|
25
|
+
at: new Date().toISOString(),
|
|
26
|
+
actorId: args.actorId,
|
|
27
|
+
source: "user",
|
|
28
|
+
bundleKey: args.bundleKey,
|
|
29
|
+
surfaceId: args.surfaceId,
|
|
30
|
+
eventType: "patch.approved",
|
|
31
|
+
payload: {
|
|
32
|
+
proposalId: args.proposalId,
|
|
33
|
+
source: args.source,
|
|
34
|
+
opsCount: args.opsCount,
|
|
35
|
+
reason: args.reason
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
function emitPatchRejected(audit, args) {
|
|
40
|
+
audit.emit({
|
|
41
|
+
eventId: generateEventId(),
|
|
42
|
+
at: new Date().toISOString(),
|
|
43
|
+
actorId: args.actorId,
|
|
44
|
+
source: "user",
|
|
45
|
+
bundleKey: args.bundleKey,
|
|
46
|
+
surfaceId: args.surfaceId,
|
|
47
|
+
eventType: "patch.rejected",
|
|
48
|
+
payload: {
|
|
49
|
+
proposalId: args.proposalId,
|
|
50
|
+
source: args.source,
|
|
51
|
+
opsCount: args.opsCount,
|
|
52
|
+
reason: args.reason
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
function emitOverlaySaved(audit, args) {
|
|
57
|
+
audit.emit({
|
|
58
|
+
eventId: generateEventId(),
|
|
59
|
+
at: new Date().toISOString(),
|
|
60
|
+
actorId: args.actorId,
|
|
61
|
+
source: "user",
|
|
62
|
+
bundleKey: args.bundleKey,
|
|
63
|
+
eventType: "overlay.saved",
|
|
64
|
+
payload: {
|
|
65
|
+
overlayId: args.overlayId,
|
|
66
|
+
scope: args.scope,
|
|
67
|
+
opsCount: args.opsCount
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
function emitOverlayApplied(audit, args) {
|
|
72
|
+
audit.emit({
|
|
73
|
+
eventId: generateEventId(),
|
|
74
|
+
at: new Date().toISOString(),
|
|
75
|
+
source: "system",
|
|
76
|
+
bundleKey: args.bundleKey,
|
|
77
|
+
surfaceId: args.surfaceId,
|
|
78
|
+
eventType: "overlay.applied",
|
|
79
|
+
payload: {
|
|
80
|
+
overlayId: args.overlayId,
|
|
81
|
+
opsCount: args.opsCount
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
function emitOverlayFailed(audit, args) {
|
|
86
|
+
audit.emit({
|
|
87
|
+
eventId: generateEventId(),
|
|
88
|
+
at: new Date().toISOString(),
|
|
89
|
+
source: "system",
|
|
90
|
+
bundleKey: args.bundleKey,
|
|
91
|
+
surfaceId: args.surfaceId,
|
|
92
|
+
eventType: "overlay.failed",
|
|
93
|
+
payload: {
|
|
94
|
+
overlayId: args.overlayId,
|
|
95
|
+
error: args.error
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
function emitPolicyDenied(audit, args) {
|
|
100
|
+
audit.emit({
|
|
101
|
+
eventId: generateEventId(),
|
|
102
|
+
at: new Date().toISOString(),
|
|
103
|
+
actorId: args.actorId,
|
|
104
|
+
source: "policy",
|
|
105
|
+
bundleKey: args.bundleKey,
|
|
106
|
+
surfaceId: args.surfaceId,
|
|
107
|
+
eventType: "policy.denied",
|
|
108
|
+
payload: {
|
|
109
|
+
targetId: args.targetId,
|
|
110
|
+
reason: args.reason
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
function emitPolicyRedacted(audit, args) {
|
|
115
|
+
audit.emit({
|
|
116
|
+
eventId: generateEventId(),
|
|
117
|
+
at: new Date().toISOString(),
|
|
118
|
+
actorId: args.actorId,
|
|
119
|
+
source: "policy",
|
|
120
|
+
bundleKey: args.bundleKey,
|
|
121
|
+
surfaceId: args.surfaceId,
|
|
122
|
+
eventType: "policy.redacted",
|
|
123
|
+
payload: {
|
|
124
|
+
targetId: args.targetId,
|
|
125
|
+
redactions: args.redactions
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
export {
|
|
130
|
+
emitPolicyRedacted,
|
|
131
|
+
emitPolicyDenied,
|
|
132
|
+
emitPatchRejected,
|
|
133
|
+
emitPatchProposed,
|
|
134
|
+
emitPatchApproved,
|
|
135
|
+
emitOverlaySaved,
|
|
136
|
+
emitOverlayFailed,
|
|
137
|
+
emitOverlayApplied
|
|
138
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { BundleContext, BuildContextParams } from '../spec/types';
|
|
2
|
+
/**
|
|
3
|
+
* Builds a full BundleContext from minimal request params.
|
|
4
|
+
* Missing fields use sensible defaults for resolution.
|
|
5
|
+
*
|
|
6
|
+
* @param params - Minimal request params (route required)
|
|
7
|
+
* @returns Full BundleContext
|
|
8
|
+
*/
|
|
9
|
+
export declare function buildContext(params: BuildContextParams): BundleContext;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// src/runtime/build-context.ts
|
|
3
|
+
var DEFAULT_PREFERENCES = {
|
|
4
|
+
guidance: "hints",
|
|
5
|
+
density: "standard",
|
|
6
|
+
dataDepth: "detailed",
|
|
7
|
+
control: "standard",
|
|
8
|
+
media: "text",
|
|
9
|
+
pace: "balanced",
|
|
10
|
+
narrative: "top-down"
|
|
11
|
+
};
|
|
12
|
+
function buildContext(params) {
|
|
13
|
+
const {
|
|
14
|
+
route,
|
|
15
|
+
params: routeParams = {},
|
|
16
|
+
query = {},
|
|
17
|
+
tenantId = "default",
|
|
18
|
+
workspaceId,
|
|
19
|
+
actorId,
|
|
20
|
+
device = "desktop",
|
|
21
|
+
preferences: partialPrefs = {},
|
|
22
|
+
capabilities = [],
|
|
23
|
+
featureFlags = [],
|
|
24
|
+
mode,
|
|
25
|
+
locale,
|
|
26
|
+
timezone,
|
|
27
|
+
entity,
|
|
28
|
+
conversation,
|
|
29
|
+
activeViewId
|
|
30
|
+
} = params;
|
|
31
|
+
const preferences = {
|
|
32
|
+
...DEFAULT_PREFERENCES,
|
|
33
|
+
...partialPrefs
|
|
34
|
+
};
|
|
35
|
+
return {
|
|
36
|
+
tenantId,
|
|
37
|
+
workspaceId,
|
|
38
|
+
actorId,
|
|
39
|
+
route,
|
|
40
|
+
params: routeParams,
|
|
41
|
+
query,
|
|
42
|
+
device,
|
|
43
|
+
mode,
|
|
44
|
+
locale,
|
|
45
|
+
timezone,
|
|
46
|
+
entity,
|
|
47
|
+
conversation,
|
|
48
|
+
activeViewId,
|
|
49
|
+
preferences,
|
|
50
|
+
capabilities,
|
|
51
|
+
featureFlags
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
export {
|
|
55
|
+
buildContext
|
|
56
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Action and command registries for surface runtime.
|
|
3
|
+
* Aligns with 09_extension_and_override_model.md.
|
|
4
|
+
*
|
|
5
|
+
* BundleExtensionRegistry: registerWidget, registerFieldRenderer, registerAction, registerCommand.
|
|
6
|
+
*/
|
|
7
|
+
import type { ActionSpec, CommandSpec } from '../spec/types';
|
|
8
|
+
import type { WidgetRegistryEntry } from './widget-registry';
|
|
9
|
+
import type { FieldRendererSpec } from '../spec/types';
|
|
10
|
+
import type { MutableFieldRendererRegistry } from './field-renderer-registry';
|
|
11
|
+
export interface BundleExtensionRegistry {
|
|
12
|
+
registerWidget(entry: WidgetRegistryEntry): void;
|
|
13
|
+
registerFieldRenderer(kind: string, entry: FieldRendererSpec): void;
|
|
14
|
+
registerAction(entry: ActionSpec): void;
|
|
15
|
+
registerCommand(entry: CommandSpec): void;
|
|
16
|
+
}
|
|
17
|
+
export interface ActionRegistry {
|
|
18
|
+
register(entry: ActionSpec): void;
|
|
19
|
+
get(actionId: string): ActionSpec | undefined;
|
|
20
|
+
has(actionId: string): boolean;
|
|
21
|
+
list(): ActionSpec[];
|
|
22
|
+
}
|
|
23
|
+
export interface CommandRegistry {
|
|
24
|
+
register(entry: CommandSpec): void;
|
|
25
|
+
get(commandId: string): CommandSpec | undefined;
|
|
26
|
+
has(commandId: string): boolean;
|
|
27
|
+
list(): CommandSpec[];
|
|
28
|
+
}
|
|
29
|
+
export declare function createActionRegistry(): ActionRegistry;
|
|
30
|
+
export declare function createCommandRegistry(): CommandRegistry;
|
|
31
|
+
export interface CreateBundleExtensionRegistryOptions {
|
|
32
|
+
widgetRegistry: {
|
|
33
|
+
register(entry: WidgetRegistryEntry): void;
|
|
34
|
+
};
|
|
35
|
+
fieldRendererRegistry: MutableFieldRendererRegistry;
|
|
36
|
+
actionRegistry?: ActionRegistry;
|
|
37
|
+
commandRegistry?: CommandRegistry;
|
|
38
|
+
}
|
|
39
|
+
export declare function createBundleExtensionRegistry(options: CreateBundleExtensionRegistryOptions): BundleExtensionRegistry;
|