@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.
Files changed (232) hide show
  1. package/README.md +164 -0
  2. package/dist/adapters/ai-sdk-stub.d.ts +5 -0
  3. package/dist/adapters/ai-sdk-stub.js +13 -0
  4. package/dist/adapters/blocknote-stub.d.ts +6 -0
  5. package/dist/adapters/blocknote-stub.js +31 -0
  6. package/dist/adapters/dnd-kit-adapter.d.ts +13 -0
  7. package/dist/adapters/dnd-kit-adapter.js +44 -0
  8. package/dist/adapters/dnd-kit-stub.d.ts +6 -0
  9. package/dist/adapters/dnd-kit-stub.js +8 -0
  10. package/dist/adapters/floating-ui-stub.d.ts +6 -0
  11. package/dist/adapters/floating-ui-stub.js +19 -0
  12. package/dist/adapters/index.d.ts +11 -0
  13. package/dist/adapters/index.js +176 -0
  14. package/dist/adapters/interfaces.d.ts +75 -0
  15. package/dist/adapters/interfaces.js +1 -0
  16. package/dist/adapters/motion-stub.d.ts +7 -0
  17. package/dist/adapters/motion-stub.js +27 -0
  18. package/dist/adapters/motion-stub.test.d.ts +1 -0
  19. package/dist/adapters/resizable-panels-stub.d.ts +6 -0
  20. package/dist/adapters/resizable-panels-stub.js +46 -0
  21. package/dist/adapters/resizable-panels-stub.test.d.ts +1 -0
  22. package/dist/browser/adapters/ai-sdk-stub.js +12 -0
  23. package/dist/browser/adapters/blocknote-stub.js +30 -0
  24. package/dist/browser/adapters/dnd-kit-adapter.js +43 -0
  25. package/dist/browser/adapters/dnd-kit-stub.js +7 -0
  26. package/dist/browser/adapters/floating-ui-stub.js +18 -0
  27. package/dist/browser/adapters/index.js +175 -0
  28. package/dist/browser/adapters/interfaces.js +0 -0
  29. package/dist/browser/adapters/motion-stub.js +26 -0
  30. package/dist/browser/adapters/resizable-panels-stub.js +45 -0
  31. package/dist/browser/evals/golden-context.js +0 -0
  32. package/dist/browser/evals/golden-harness.js +848 -0
  33. package/dist/browser/examples/pm-workbench.bundle.js +476 -0
  34. package/dist/browser/i18n/catalogs/en.js +71 -0
  35. package/dist/browser/i18n/catalogs/es.js +32 -0
  36. package/dist/browser/i18n/catalogs/fr.js +32 -0
  37. package/dist/browser/i18n/catalogs/index.js +133 -0
  38. package/dist/browser/i18n/index.js +173 -0
  39. package/dist/browser/i18n/keys.js +19 -0
  40. package/dist/browser/i18n/messages.js +143 -0
  41. package/dist/browser/index.js +2466 -0
  42. package/dist/browser/react/BundleProvider.js +47 -0
  43. package/dist/browser/react/BundleRenderer.js +726 -0
  44. package/dist/browser/react/OverlayConflictResolver.js +255 -0
  45. package/dist/browser/react/PatchProposalCard.js +255 -0
  46. package/dist/browser/react/RegionRenderer.js +128 -0
  47. package/dist/browser/react/SlotRenderer.js +118 -0
  48. package/dist/browser/react/WidgetPalette.js +59 -0
  49. package/dist/browser/react/index.js +792 -0
  50. package/dist/browser/runtime/apply-surface-patch.js +322 -0
  51. package/dist/browser/runtime/audit-events.js +137 -0
  52. package/dist/browser/runtime/build-context.js +55 -0
  53. package/dist/browser/runtime/extension-registry.js +58 -0
  54. package/dist/browser/runtime/field-renderer-registry.js +145 -0
  55. package/dist/browser/runtime/index.js +1496 -0
  56. package/dist/browser/runtime/overlay-alignment.js +83 -0
  57. package/dist/browser/runtime/overlay-signer.js +15 -0
  58. package/dist/browser/runtime/override-store.js +52 -0
  59. package/dist/browser/runtime/planner-prompt.js +67 -0
  60. package/dist/browser/runtime/planner-tools.js +77 -0
  61. package/dist/browser/runtime/policy-eval.js +155 -0
  62. package/dist/browser/runtime/preference-adapter.js +67 -0
  63. package/dist/browser/runtime/resolve-bundle.js +767 -0
  64. package/dist/browser/runtime/resolve-preferences.js +59 -0
  65. package/dist/browser/runtime/rollback.js +347 -0
  66. package/dist/browser/runtime/widget-registry.js +36 -0
  67. package/dist/browser/spec/define-module-bundle.js +113 -0
  68. package/dist/browser/spec/index.js +319 -0
  69. package/dist/browser/spec/types.js +0 -0
  70. package/dist/browser/spec/validate-bundle.js +65 -0
  71. package/dist/browser/spec/validate-surface-patch.js +206 -0
  72. package/dist/browser/spec/verification-snapshot-types.js +0 -0
  73. package/dist/browser/telemetry/index.js +20 -0
  74. package/dist/browser/telemetry/surface-metrics.js +20 -0
  75. package/dist/evals/golden-context.d.ts +24 -0
  76. package/dist/evals/golden-context.js +1 -0
  77. package/dist/evals/golden-harness.d.ts +29 -0
  78. package/dist/evals/golden-harness.js +849 -0
  79. package/dist/evals/golden-harness.test.d.ts +1 -0
  80. package/dist/examples/pm-workbench.bundle.d.ts +177 -0
  81. package/dist/examples/pm-workbench.bundle.js +477 -0
  82. package/dist/i18n/catalogs/en.d.ts +1 -0
  83. package/dist/i18n/catalogs/en.js +72 -0
  84. package/dist/i18n/catalogs/es.d.ts +1 -0
  85. package/dist/i18n/catalogs/es.js +33 -0
  86. package/dist/i18n/catalogs/fr.d.ts +1 -0
  87. package/dist/i18n/catalogs/fr.js +33 -0
  88. package/dist/i18n/catalogs/index.d.ts +3 -0
  89. package/dist/i18n/catalogs/index.js +134 -0
  90. package/dist/i18n/index.d.ts +5 -0
  91. package/dist/i18n/index.js +174 -0
  92. package/dist/i18n/keys.d.ts +20 -0
  93. package/dist/i18n/keys.js +20 -0
  94. package/dist/i18n/messages.d.ts +5 -0
  95. package/dist/i18n/messages.js +144 -0
  96. package/dist/index.d.ts +4 -0
  97. package/dist/index.js +2467 -0
  98. package/dist/node/adapters/ai-sdk-stub.js +12 -0
  99. package/dist/node/adapters/blocknote-stub.js +30 -0
  100. package/dist/node/adapters/dnd-kit-adapter.js +43 -0
  101. package/dist/node/adapters/dnd-kit-stub.js +7 -0
  102. package/dist/node/adapters/floating-ui-stub.js +18 -0
  103. package/dist/node/adapters/index.js +175 -0
  104. package/dist/node/adapters/interfaces.js +0 -0
  105. package/dist/node/adapters/motion-stub.js +26 -0
  106. package/dist/node/adapters/resizable-panels-stub.js +45 -0
  107. package/dist/node/evals/golden-context.js +0 -0
  108. package/dist/node/evals/golden-harness.js +848 -0
  109. package/dist/node/examples/pm-workbench.bundle.js +476 -0
  110. package/dist/node/i18n/catalogs/en.js +71 -0
  111. package/dist/node/i18n/catalogs/es.js +32 -0
  112. package/dist/node/i18n/catalogs/fr.js +32 -0
  113. package/dist/node/i18n/catalogs/index.js +133 -0
  114. package/dist/node/i18n/index.js +173 -0
  115. package/dist/node/i18n/keys.js +19 -0
  116. package/dist/node/i18n/messages.js +143 -0
  117. package/dist/node/index.js +2466 -0
  118. package/dist/node/react/BundleProvider.js +47 -0
  119. package/dist/node/react/BundleRenderer.js +726 -0
  120. package/dist/node/react/OverlayConflictResolver.js +255 -0
  121. package/dist/node/react/PatchProposalCard.js +255 -0
  122. package/dist/node/react/RegionRenderer.js +128 -0
  123. package/dist/node/react/SlotRenderer.js +118 -0
  124. package/dist/node/react/WidgetPalette.js +59 -0
  125. package/dist/node/react/index.js +792 -0
  126. package/dist/node/runtime/apply-surface-patch.js +322 -0
  127. package/dist/node/runtime/audit-events.js +137 -0
  128. package/dist/node/runtime/build-context.js +55 -0
  129. package/dist/node/runtime/extension-registry.js +58 -0
  130. package/dist/node/runtime/field-renderer-registry.js +145 -0
  131. package/dist/node/runtime/index.js +1496 -0
  132. package/dist/node/runtime/overlay-alignment.js +83 -0
  133. package/dist/node/runtime/overlay-signer.js +15 -0
  134. package/dist/node/runtime/override-store.js +52 -0
  135. package/dist/node/runtime/planner-prompt.js +67 -0
  136. package/dist/node/runtime/planner-tools.js +77 -0
  137. package/dist/node/runtime/policy-eval.js +155 -0
  138. package/dist/node/runtime/preference-adapter.js +67 -0
  139. package/dist/node/runtime/resolve-bundle.js +767 -0
  140. package/dist/node/runtime/resolve-preferences.js +59 -0
  141. package/dist/node/runtime/rollback.js +347 -0
  142. package/dist/node/runtime/widget-registry.js +36 -0
  143. package/dist/node/spec/define-module-bundle.js +113 -0
  144. package/dist/node/spec/index.js +319 -0
  145. package/dist/node/spec/types.js +0 -0
  146. package/dist/node/spec/validate-bundle.js +65 -0
  147. package/dist/node/spec/validate-surface-patch.js +206 -0
  148. package/dist/node/spec/verification-snapshot-types.js +0 -0
  149. package/dist/node/telemetry/index.js +20 -0
  150. package/dist/node/telemetry/surface-metrics.js +20 -0
  151. package/dist/react/BundleProvider.d.ts +13 -0
  152. package/dist/react/BundleProvider.js +48 -0
  153. package/dist/react/BundleRenderer.d.ts +22 -0
  154. package/dist/react/BundleRenderer.js +727 -0
  155. package/dist/react/OverlayConflictResolver.d.ts +15 -0
  156. package/dist/react/OverlayConflictResolver.js +256 -0
  157. package/dist/react/PatchProposalCard.d.ts +13 -0
  158. package/dist/react/PatchProposalCard.js +256 -0
  159. package/dist/react/RegionRenderer.d.ts +13 -0
  160. package/dist/react/RegionRenderer.js +129 -0
  161. package/dist/react/SlotRenderer.d.ts +13 -0
  162. package/dist/react/SlotRenderer.js +119 -0
  163. package/dist/react/WidgetPalette.d.ts +12 -0
  164. package/dist/react/WidgetPalette.js +60 -0
  165. package/dist/react/index.d.ts +7 -0
  166. package/dist/react/index.js +793 -0
  167. package/dist/runtime/apply-surface-patch.d.ts +15 -0
  168. package/dist/runtime/apply-surface-patch.js +323 -0
  169. package/dist/runtime/apply-surface-patch.test.d.ts +1 -0
  170. package/dist/runtime/audit-events.d.ts +70 -0
  171. package/dist/runtime/audit-events.js +138 -0
  172. package/dist/runtime/audit-events.test.d.ts +1 -0
  173. package/dist/runtime/build-context.d.ts +9 -0
  174. package/dist/runtime/build-context.js +56 -0
  175. package/dist/runtime/extension-registry.d.ts +39 -0
  176. package/dist/runtime/extension-registry.js +59 -0
  177. package/dist/runtime/field-renderer-registry.d.ts +23 -0
  178. package/dist/runtime/field-renderer-registry.js +146 -0
  179. package/dist/runtime/field-renderer-registry.test.d.ts +1 -0
  180. package/dist/runtime/index.d.ts +16 -0
  181. package/dist/runtime/index.js +1497 -0
  182. package/dist/runtime/overlay-alignment.d.ts +49 -0
  183. package/dist/runtime/overlay-alignment.js +84 -0
  184. package/dist/runtime/overlay-alignment.test.d.ts +1 -0
  185. package/dist/runtime/overlay-signer.d.ts +15 -0
  186. package/dist/runtime/overlay-signer.js +16 -0
  187. package/dist/runtime/override-store.d.ts +44 -0
  188. package/dist/runtime/override-store.js +53 -0
  189. package/dist/runtime/override-store.test.d.ts +1 -0
  190. package/dist/runtime/planner-prompt.d.ts +39 -0
  191. package/dist/runtime/planner-prompt.js +68 -0
  192. package/dist/runtime/planner-prompt.test.d.ts +1 -0
  193. package/dist/runtime/planner-tools.d.ts +106 -0
  194. package/dist/runtime/planner-tools.js +78 -0
  195. package/dist/runtime/planner-tools.test.d.ts +1 -0
  196. package/dist/runtime/policy-eval.d.ts +23 -0
  197. package/dist/runtime/policy-eval.js +156 -0
  198. package/dist/runtime/preference-adapter.d.ts +6 -0
  199. package/dist/runtime/preference-adapter.js +68 -0
  200. package/dist/runtime/resolve-bundle.d.ts +68 -0
  201. package/dist/runtime/resolve-bundle.js +768 -0
  202. package/dist/runtime/resolve-bundle.test.d.ts +1 -0
  203. package/dist/runtime/resolve-preferences.d.ts +9 -0
  204. package/dist/runtime/resolve-preferences.js +60 -0
  205. package/dist/runtime/resolve-preferences.test.d.ts +1 -0
  206. package/dist/runtime/rollback.d.ts +21 -0
  207. package/dist/runtime/rollback.js +348 -0
  208. package/dist/runtime/rollback.test.d.ts +1 -0
  209. package/dist/runtime/widget-registry.d.ts +26 -0
  210. package/dist/runtime/widget-registry.js +37 -0
  211. package/dist/runtime/widget-registry.test.d.ts +1 -0
  212. package/dist/spec/define-module-bundle.d.ts +17 -0
  213. package/dist/spec/define-module-bundle.js +114 -0
  214. package/dist/spec/define-module-bundle.test.d.ts +1 -0
  215. package/dist/spec/index.d.ts +5 -0
  216. package/dist/spec/index.js +320 -0
  217. package/dist/spec/types.d.ts +494 -0
  218. package/dist/spec/types.js +1 -0
  219. package/dist/spec/validate-bundle.d.ts +23 -0
  220. package/dist/spec/validate-bundle.js +66 -0
  221. package/dist/spec/validate-bundle.test.d.ts +1 -0
  222. package/dist/spec/validate-surface-patch.d.ts +39 -0
  223. package/dist/spec/validate-surface-patch.js +207 -0
  224. package/dist/spec/validate-surface-patch.test.d.ts +1 -0
  225. package/dist/spec/verification-snapshot-types.d.ts +23 -0
  226. package/dist/spec/verification-snapshot-types.js +1 -0
  227. package/dist/spec/verification-snapshot.test.d.ts +5 -0
  228. package/dist/telemetry/index.d.ts +5 -0
  229. package/dist/telemetry/index.js +21 -0
  230. package/dist/telemetry/surface-metrics.d.ts +17 -0
  231. package/dist/telemetry/surface-metrics.js +21 -0
  232. package/package.json +920 -0
@@ -0,0 +1,322 @@
1
+ // src/spec/validate-surface-patch.ts
2
+ var VALID_OPS = [
3
+ "insert-node",
4
+ "replace-node",
5
+ "remove-node",
6
+ "move-node",
7
+ "resize-panel",
8
+ "set-layout",
9
+ "reveal-field",
10
+ "hide-field",
11
+ "promote-action",
12
+ "set-focus"
13
+ ];
14
+ var VALID_NODE_KINDS = [
15
+ "metric-strip",
16
+ "data-view",
17
+ "entity-card",
18
+ "entity-header",
19
+ "entity-summary",
20
+ "entity-section",
21
+ "entity-field",
22
+ "entity-activity",
23
+ "entity-relations",
24
+ "entity-timeline",
25
+ "entity-comments",
26
+ "entity-attachments",
27
+ "entity-view-switcher",
28
+ "entity-automation-panel",
29
+ "rich-doc",
30
+ "chat-thread",
31
+ "assistant-panel",
32
+ "action-bar",
33
+ "timeline",
34
+ "board",
35
+ "table",
36
+ "calendar",
37
+ "form",
38
+ "chart",
39
+ "relation-graph",
40
+ "custom-widget"
41
+ ];
42
+ function validateSurfaceNode(node, path) {
43
+ if (!node.nodeId || typeof node.nodeId !== "string") {
44
+ throw new Error(`${path}: nodeId must be a non-empty string`);
45
+ }
46
+ if (!node.kind || !VALID_NODE_KINDS.includes(node.kind)) {
47
+ throw new Error(`${path}: kind must be one of ${VALID_NODE_KINDS.join(", ")}`);
48
+ }
49
+ if (node.children) {
50
+ for (let i = 0;i < node.children.length; i++) {
51
+ const child = node.children[i];
52
+ if (child)
53
+ validateSurfaceNode(child, `${path}.children[${i}]`);
54
+ }
55
+ }
56
+ }
57
+ function validateSurfacePatchOp(op, index) {
58
+ const path = `ops[${index}]`;
59
+ if (!op || typeof op !== "object" || !("op" in op)) {
60
+ throw new Error(`${path}: must be an object with op field`);
61
+ }
62
+ const opType = op.op;
63
+ if (!VALID_OPS.includes(opType)) {
64
+ throw new Error(`${path}: op must be one of ${VALID_OPS.join(", ")}`);
65
+ }
66
+ switch (op.op) {
67
+ case "insert-node":
68
+ if (!op.slotId || typeof op.slotId !== "string") {
69
+ throw new Error(`${path}: insert-node requires slotId string`);
70
+ }
71
+ if (!op.node) {
72
+ throw new Error(`${path}: insert-node requires node`);
73
+ }
74
+ validateSurfaceNode(op.node, `${path}.node`);
75
+ if (op.index !== undefined && typeof op.index !== "number") {
76
+ throw new Error(`${path}: insert-node index must be number if present`);
77
+ }
78
+ break;
79
+ case "replace-node":
80
+ if (!op.nodeId || typeof op.nodeId !== "string") {
81
+ throw new Error(`${path}: replace-node requires nodeId string`);
82
+ }
83
+ if (!op.node) {
84
+ throw new Error(`${path}: replace-node requires node`);
85
+ }
86
+ validateSurfaceNode(op.node, `${path}.node`);
87
+ break;
88
+ case "remove-node":
89
+ if (!op.nodeId || typeof op.nodeId !== "string") {
90
+ throw new Error(`${path}: remove-node requires nodeId string`);
91
+ }
92
+ break;
93
+ case "move-node":
94
+ if (!op.nodeId || typeof op.nodeId !== "string") {
95
+ throw new Error(`${path}: move-node requires nodeId string`);
96
+ }
97
+ if (!op.toSlotId || typeof op.toSlotId !== "string") {
98
+ throw new Error(`${path}: move-node requires toSlotId string`);
99
+ }
100
+ if (op.index !== undefined && typeof op.index !== "number") {
101
+ throw new Error(`${path}: move-node index must be number if present`);
102
+ }
103
+ break;
104
+ case "resize-panel":
105
+ if (!op.persistKey || typeof op.persistKey !== "string") {
106
+ throw new Error(`${path}: resize-panel requires persistKey string`);
107
+ }
108
+ if (!Array.isArray(op.sizes) || op.sizes.some((s) => typeof s !== "number")) {
109
+ throw new Error(`${path}: resize-panel requires sizes number[]`);
110
+ }
111
+ break;
112
+ case "set-layout":
113
+ if (!op.layoutId || typeof op.layoutId !== "string") {
114
+ throw new Error(`${path}: set-layout requires layoutId string`);
115
+ }
116
+ break;
117
+ case "reveal-field":
118
+ case "hide-field":
119
+ if (!op.fieldId || typeof op.fieldId !== "string") {
120
+ throw new Error(`${path}: ${op.op} requires fieldId string`);
121
+ }
122
+ break;
123
+ case "promote-action": {
124
+ if (!op.actionId || typeof op.actionId !== "string") {
125
+ throw new Error(`${path}: promote-action requires actionId string`);
126
+ }
127
+ const validPlacements = ["header", "inline", "context", "assistant"];
128
+ if (!op.placement || !validPlacements.includes(op.placement)) {
129
+ throw new Error(`${path}: promote-action placement must be one of ${validPlacements.join(", ")}`);
130
+ }
131
+ break;
132
+ }
133
+ case "set-focus":
134
+ if (!op.targetId || typeof op.targetId !== "string") {
135
+ throw new Error(`${path}: set-focus requires targetId string`);
136
+ }
137
+ break;
138
+ default:
139
+ throw new Error(`${path}: unknown op "${opType}"`);
140
+ }
141
+ }
142
+ function validateSurfacePatch(ops) {
143
+ if (!Array.isArray(ops)) {
144
+ throw new Error("Patch ops must be an array");
145
+ }
146
+ for (let i = 0;i < ops.length; i++) {
147
+ const op = ops[i];
148
+ if (op)
149
+ validateSurfacePatchOp(op, i);
150
+ }
151
+ }
152
+ function validateSurfaceNodeAgainstKinds(node, allowedKinds, path) {
153
+ if (!allowedKinds.includes(node.kind)) {
154
+ throw new Error(`${path}: kind "${node.kind}" not in allowed list [${allowedKinds.join(", ")}]`);
155
+ }
156
+ if (node.children) {
157
+ for (let i = 0;i < node.children.length; i++) {
158
+ const child = node.children[i];
159
+ if (child)
160
+ validateSurfaceNodeAgainstKinds(child, allowedKinds, `${path}.children[${i}]`);
161
+ }
162
+ }
163
+ }
164
+ function validatePatchProposal(ops, constraints) {
165
+ if (!Array.isArray(ops)) {
166
+ throw new Error("Patch ops must be an array");
167
+ }
168
+ const { allowedOps, allowedSlots, allowedNodeKinds } = constraints;
169
+ for (let i = 0;i < ops.length; i++) {
170
+ const op = ops[i];
171
+ if (!op)
172
+ continue;
173
+ const path = `ops[${i}]`;
174
+ if (!allowedOps.includes(op.op)) {
175
+ throw new Error(`${path}: op "${op.op}" not in allowed list [${allowedOps.join(", ")}]`);
176
+ }
177
+ switch (op.op) {
178
+ case "insert-node":
179
+ if (!allowedSlots.includes(op.slotId)) {
180
+ throw new Error(`${path}: slotId "${op.slotId}" not in allowed slots [${allowedSlots.join(", ")}]`);
181
+ }
182
+ if (op.node) {
183
+ validateSurfaceNodeAgainstKinds(op.node, allowedNodeKinds, `${path}.node`);
184
+ }
185
+ break;
186
+ case "move-node":
187
+ if (!allowedSlots.includes(op.toSlotId)) {
188
+ throw new Error(`${path}: toSlotId "${op.toSlotId}" not in allowed slots [${allowedSlots.join(", ")}]`);
189
+ }
190
+ break;
191
+ case "replace-node":
192
+ if (op.node) {
193
+ validateSurfaceNodeAgainstKinds(op.node, allowedNodeKinds, `${path}.node`);
194
+ }
195
+ break;
196
+ default:
197
+ break;
198
+ }
199
+ }
200
+ validateSurfacePatch(ops);
201
+ }
202
+
203
+ // src/runtime/apply-surface-patch.ts
204
+ import { Logger } from "@contractspec/lib.observability";
205
+ var logger = new Logger("@contractspec/lib.surface-runtime");
206
+ function findNode(nodes, nodeId) {
207
+ for (const n of nodes) {
208
+ if (n.nodeId === nodeId)
209
+ return n;
210
+ if (n.children) {
211
+ const found = findNode(n.children, nodeId);
212
+ if (found)
213
+ return found;
214
+ }
215
+ }
216
+ return;
217
+ }
218
+ function collectNodeIds(nodes) {
219
+ const ids = new Set;
220
+ for (const n of nodes) {
221
+ ids.add(n.nodeId);
222
+ if (n.children)
223
+ collectNodeIds(n.children).forEach((id) => ids.add(id));
224
+ }
225
+ return ids;
226
+ }
227
+ function validateOp(op, nodeIds) {
228
+ if (op.op === "remove-node" || op.op === "replace-node" || op.op === "move-node") {
229
+ if (!nodeIds.has(op.nodeId)) {
230
+ throw new Error(`Patch op references unknown nodeId: ${op.nodeId}`);
231
+ }
232
+ }
233
+ if (op.op === "insert-node" && !op.node?.nodeId) {
234
+ throw new Error("insert-node requires node with nodeId");
235
+ }
236
+ }
237
+ function produceInverse(op, plan) {
238
+ switch (op.op) {
239
+ case "insert-node":
240
+ return op.node ? { op: "remove-node", nodeId: op.node.nodeId } : null;
241
+ case "remove-node": {
242
+ const node = findNode(plan.nodes, op.nodeId);
243
+ return node ? { op: "insert-node", slotId: "primary", node } : null;
244
+ }
245
+ case "replace-node": {
246
+ const prev = findNode(plan.nodes, op.nodeId);
247
+ return prev ? { op: "replace-node", nodeId: op.nodeId, node: prev } : null;
248
+ }
249
+ case "set-layout":
250
+ return { op: "set-layout", layoutId: plan.layoutId };
251
+ case "reveal-field":
252
+ return { op: "hide-field", fieldId: op.fieldId };
253
+ case "hide-field":
254
+ return { op: "reveal-field", fieldId: op.fieldId };
255
+ case "move-node":
256
+ case "resize-panel":
257
+ case "set-focus":
258
+ case "promote-action":
259
+ return null;
260
+ default:
261
+ return null;
262
+ }
263
+ }
264
+ function applySurfacePatch(plan, ops) {
265
+ if (ops.length === 0)
266
+ return { plan, inverseOps: [] };
267
+ validateSurfacePatch(ops);
268
+ const nodeIds = collectNodeIds(plan.nodes);
269
+ for (const op of ops) {
270
+ validateOp(op, nodeIds);
271
+ }
272
+ let nextNodes = [...plan.nodes];
273
+ let nextLayoutId = plan.layoutId;
274
+ const inverseOps = [];
275
+ for (const op of ops) {
276
+ const inv = produceInverse(op, {
277
+ ...plan,
278
+ nodes: nextNodes,
279
+ layoutId: nextLayoutId
280
+ });
281
+ if (inv)
282
+ inverseOps.unshift(inv);
283
+ switch (op.op) {
284
+ case "insert-node":
285
+ if (op.node)
286
+ nextNodes = [...nextNodes, op.node];
287
+ break;
288
+ case "remove-node":
289
+ nextNodes = nextNodes.filter((n) => n.nodeId !== op.nodeId);
290
+ break;
291
+ case "replace-node": {
292
+ const replace = (nodes) => nodes.map((n) => n.nodeId === op.nodeId ? op.node : { ...n, children: n.children ? replace(n.children) : undefined });
293
+ nextNodes = replace(nextNodes);
294
+ break;
295
+ }
296
+ case "set-layout":
297
+ nextLayoutId = op.layoutId;
298
+ break;
299
+ case "move-node":
300
+ case "resize-panel":
301
+ case "set-focus":
302
+ case "reveal-field":
303
+ case "hide-field":
304
+ case "promote-action":
305
+ break;
306
+ }
307
+ }
308
+ const result = {
309
+ plan: { ...plan, nodes: nextNodes, layoutId: nextLayoutId },
310
+ inverseOps
311
+ };
312
+ logger.info("bundle.surface.patch.applied", {
313
+ bundleKey: plan.bundleKey,
314
+ surfaceId: plan.surfaceId,
315
+ opCount: ops.length,
316
+ opTypes: [...new Set(ops.map((o) => o.op))]
317
+ });
318
+ return result;
319
+ }
320
+ export {
321
+ applySurfacePatch
322
+ };
@@ -0,0 +1,137 @@
1
+ // src/runtime/audit-events.ts
2
+ function generateEventId() {
3
+ return `evt_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
4
+ }
5
+ function emitPatchProposed(audit, args) {
6
+ audit.emit({
7
+ eventId: generateEventId(),
8
+ at: new Date().toISOString(),
9
+ actorId: args.actorId,
10
+ source: args.proposal.source === "assistant" ? "assistant" : "user",
11
+ bundleKey: args.bundleKey,
12
+ surfaceId: args.surfaceId,
13
+ eventType: "patch.proposed",
14
+ payload: {
15
+ proposalId: args.proposal.proposalId,
16
+ source: args.proposal.source,
17
+ opsCount: args.proposal.ops.length
18
+ }
19
+ });
20
+ }
21
+ function emitPatchApproved(audit, args) {
22
+ audit.emit({
23
+ eventId: generateEventId(),
24
+ at: new Date().toISOString(),
25
+ actorId: args.actorId,
26
+ source: "user",
27
+ bundleKey: args.bundleKey,
28
+ surfaceId: args.surfaceId,
29
+ eventType: "patch.approved",
30
+ payload: {
31
+ proposalId: args.proposalId,
32
+ source: args.source,
33
+ opsCount: args.opsCount,
34
+ reason: args.reason
35
+ }
36
+ });
37
+ }
38
+ function emitPatchRejected(audit, args) {
39
+ audit.emit({
40
+ eventId: generateEventId(),
41
+ at: new Date().toISOString(),
42
+ actorId: args.actorId,
43
+ source: "user",
44
+ bundleKey: args.bundleKey,
45
+ surfaceId: args.surfaceId,
46
+ eventType: "patch.rejected",
47
+ payload: {
48
+ proposalId: args.proposalId,
49
+ source: args.source,
50
+ opsCount: args.opsCount,
51
+ reason: args.reason
52
+ }
53
+ });
54
+ }
55
+ function emitOverlaySaved(audit, args) {
56
+ audit.emit({
57
+ eventId: generateEventId(),
58
+ at: new Date().toISOString(),
59
+ actorId: args.actorId,
60
+ source: "user",
61
+ bundleKey: args.bundleKey,
62
+ eventType: "overlay.saved",
63
+ payload: {
64
+ overlayId: args.overlayId,
65
+ scope: args.scope,
66
+ opsCount: args.opsCount
67
+ }
68
+ });
69
+ }
70
+ function emitOverlayApplied(audit, args) {
71
+ audit.emit({
72
+ eventId: generateEventId(),
73
+ at: new Date().toISOString(),
74
+ source: "system",
75
+ bundleKey: args.bundleKey,
76
+ surfaceId: args.surfaceId,
77
+ eventType: "overlay.applied",
78
+ payload: {
79
+ overlayId: args.overlayId,
80
+ opsCount: args.opsCount
81
+ }
82
+ });
83
+ }
84
+ function emitOverlayFailed(audit, args) {
85
+ audit.emit({
86
+ eventId: generateEventId(),
87
+ at: new Date().toISOString(),
88
+ source: "system",
89
+ bundleKey: args.bundleKey,
90
+ surfaceId: args.surfaceId,
91
+ eventType: "overlay.failed",
92
+ payload: {
93
+ overlayId: args.overlayId,
94
+ error: args.error
95
+ }
96
+ });
97
+ }
98
+ function emitPolicyDenied(audit, args) {
99
+ audit.emit({
100
+ eventId: generateEventId(),
101
+ at: new Date().toISOString(),
102
+ actorId: args.actorId,
103
+ source: "policy",
104
+ bundleKey: args.bundleKey,
105
+ surfaceId: args.surfaceId,
106
+ eventType: "policy.denied",
107
+ payload: {
108
+ targetId: args.targetId,
109
+ reason: args.reason
110
+ }
111
+ });
112
+ }
113
+ function emitPolicyRedacted(audit, args) {
114
+ audit.emit({
115
+ eventId: generateEventId(),
116
+ at: new Date().toISOString(),
117
+ actorId: args.actorId,
118
+ source: "policy",
119
+ bundleKey: args.bundleKey,
120
+ surfaceId: args.surfaceId,
121
+ eventType: "policy.redacted",
122
+ payload: {
123
+ targetId: args.targetId,
124
+ redactions: args.redactions
125
+ }
126
+ });
127
+ }
128
+ export {
129
+ emitPolicyRedacted,
130
+ emitPolicyDenied,
131
+ emitPatchRejected,
132
+ emitPatchProposed,
133
+ emitPatchApproved,
134
+ emitOverlaySaved,
135
+ emitOverlayFailed,
136
+ emitOverlayApplied
137
+ };
@@ -0,0 +1,55 @@
1
+ // src/runtime/build-context.ts
2
+ var DEFAULT_PREFERENCES = {
3
+ guidance: "hints",
4
+ density: "standard",
5
+ dataDepth: "detailed",
6
+ control: "standard",
7
+ media: "text",
8
+ pace: "balanced",
9
+ narrative: "top-down"
10
+ };
11
+ function buildContext(params) {
12
+ const {
13
+ route,
14
+ params: routeParams = {},
15
+ query = {},
16
+ tenantId = "default",
17
+ workspaceId,
18
+ actorId,
19
+ device = "desktop",
20
+ preferences: partialPrefs = {},
21
+ capabilities = [],
22
+ featureFlags = [],
23
+ mode,
24
+ locale,
25
+ timezone,
26
+ entity,
27
+ conversation,
28
+ activeViewId
29
+ } = params;
30
+ const preferences = {
31
+ ...DEFAULT_PREFERENCES,
32
+ ...partialPrefs
33
+ };
34
+ return {
35
+ tenantId,
36
+ workspaceId,
37
+ actorId,
38
+ route,
39
+ params: routeParams,
40
+ query,
41
+ device,
42
+ mode,
43
+ locale,
44
+ timezone,
45
+ entity,
46
+ conversation,
47
+ activeViewId,
48
+ preferences,
49
+ capabilities,
50
+ featureFlags
51
+ };
52
+ }
53
+ export {
54
+ buildContext
55
+ };
@@ -0,0 +1,58 @@
1
+ // src/runtime/extension-registry.ts
2
+ function createActionRegistry() {
3
+ const entries = new Map;
4
+ return {
5
+ register(entry) {
6
+ entries.set(entry.actionId, entry);
7
+ },
8
+ get(actionId) {
9
+ return entries.get(actionId);
10
+ },
11
+ has(actionId) {
12
+ return entries.has(actionId);
13
+ },
14
+ list() {
15
+ return Array.from(entries.values());
16
+ }
17
+ };
18
+ }
19
+ function createCommandRegistry() {
20
+ const entries = new Map;
21
+ return {
22
+ register(entry) {
23
+ entries.set(entry.commandId, entry);
24
+ },
25
+ get(commandId) {
26
+ return entries.get(commandId);
27
+ },
28
+ has(commandId) {
29
+ return entries.has(commandId);
30
+ },
31
+ list() {
32
+ return Array.from(entries.values());
33
+ }
34
+ };
35
+ }
36
+ function createBundleExtensionRegistry(options) {
37
+ const actionRegistry = options.actionRegistry ?? createActionRegistry();
38
+ const commandRegistry = options.commandRegistry ?? createCommandRegistry();
39
+ return {
40
+ registerWidget(entry) {
41
+ options.widgetRegistry.register(entry);
42
+ },
43
+ registerFieldRenderer(kind, entry) {
44
+ options.fieldRendererRegistry.registerFieldRenderer(kind, entry);
45
+ },
46
+ registerAction(entry) {
47
+ actionRegistry.register(entry);
48
+ },
49
+ registerCommand(entry) {
50
+ commandRegistry.register(entry);
51
+ }
52
+ };
53
+ }
54
+ export {
55
+ createCommandRegistry,
56
+ createBundleExtensionRegistry,
57
+ createActionRegistry
58
+ };
@@ -0,0 +1,145 @@
1
+ // src/runtime/field-renderer-registry.ts
2
+ var FALLBACK_FIELD_KIND = "text";
3
+ var CORE_FIELD_KINDS = {
4
+ text: {
5
+ fieldKind: "text",
6
+ viewer: "text-viewer",
7
+ editor: "text-editor",
8
+ summaryViewer: "text-summary",
9
+ tableCell: "text-cell",
10
+ filters: ["contains", "equals", "startsWith", "endsWith"],
11
+ validators: ["required", "maxLength", "regex"]
12
+ },
13
+ number: {
14
+ fieldKind: "number",
15
+ viewer: "number-viewer",
16
+ editor: "number-editor",
17
+ summaryViewer: "number-summary",
18
+ tableCell: "number-cell",
19
+ filters: ["equals", "gt", "gte", "lt", "lte", "between"],
20
+ validators: ["required", "min", "max", "integer"]
21
+ },
22
+ date: {
23
+ fieldKind: "date",
24
+ viewer: "date-viewer",
25
+ editor: "date-editor",
26
+ summaryViewer: "date-summary",
27
+ tableCell: "date-cell",
28
+ filters: ["equals", "before", "after", "between"],
29
+ validators: ["required", "min", "max"]
30
+ },
31
+ select: {
32
+ fieldKind: "select",
33
+ viewer: "select-viewer",
34
+ editor: "select-editor",
35
+ summaryViewer: "select-summary",
36
+ tableCell: "select-cell",
37
+ filters: ["equals", "in"],
38
+ validators: ["required", "oneOf"]
39
+ },
40
+ checkbox: {
41
+ fieldKind: "checkbox",
42
+ viewer: "checkbox-viewer",
43
+ editor: "checkbox-editor",
44
+ summaryViewer: "checkbox-summary",
45
+ tableCell: "checkbox-cell",
46
+ filters: ["equals"],
47
+ validators: ["required"]
48
+ }
49
+ };
50
+ var STUB_FIELD_KINDS = {
51
+ relation: {
52
+ fieldKind: "relation",
53
+ viewer: "relation-viewer-stub",
54
+ summaryViewer: "relation-chip-stub",
55
+ tableCell: "relation-chip-stub"
56
+ },
57
+ rollup: {
58
+ fieldKind: "rollup",
59
+ viewer: "rollup-viewer-stub",
60
+ summaryViewer: "rollup-chip-stub",
61
+ tableCell: "rollup-cell-stub"
62
+ },
63
+ formula: {
64
+ fieldKind: "formula",
65
+ viewer: "formula-viewer-stub",
66
+ summaryViewer: "formula-summary-stub",
67
+ tableCell: "formula-cell-stub"
68
+ },
69
+ people: {
70
+ fieldKind: "people",
71
+ viewer: "people-viewer-stub",
72
+ editor: "people-picker-stub",
73
+ summaryViewer: "people-chip-stub",
74
+ tableCell: "people-cell-stub"
75
+ },
76
+ options: {
77
+ fieldKind: "options",
78
+ viewer: "options-viewer-stub",
79
+ editor: "options-editor-stub",
80
+ summaryViewer: "options-chip-stub",
81
+ tableCell: "options-cell-stub"
82
+ },
83
+ instance: {
84
+ fieldKind: "instance",
85
+ viewer: "instance-viewer-stub",
86
+ summaryViewer: "instance-chip-stub",
87
+ tableCell: "instance-cell-stub"
88
+ },
89
+ url: {
90
+ fieldKind: "url",
91
+ viewer: "url-viewer",
92
+ editor: "url-editor",
93
+ summaryViewer: "url-summary",
94
+ tableCell: "url-cell",
95
+ validators: ["required", "regex"]
96
+ }
97
+ };
98
+ function createFieldRendererRegistry() {
99
+ const all = { ...CORE_FIELD_KINDS, ...STUB_FIELD_KINDS };
100
+ const fallback = CORE_FIELD_KINDS[FALLBACK_FIELD_KIND];
101
+ if (!fallback) {
102
+ throw new Error(`Fallback field kind "${FALLBACK_FIELD_KIND}" not found`);
103
+ }
104
+ return {
105
+ get(kind) {
106
+ return all[kind];
107
+ },
108
+ has(kind) {
109
+ return kind in all;
110
+ },
111
+ getOrFallback(kind) {
112
+ return all[kind] ?? fallback;
113
+ }
114
+ };
115
+ }
116
+ function createMutableFieldRendererRegistry() {
117
+ const base = { ...CORE_FIELD_KINDS, ...STUB_FIELD_KINDS };
118
+ const custom = new Map;
119
+ const fallback = CORE_FIELD_KINDS[FALLBACK_FIELD_KIND];
120
+ if (!fallback) {
121
+ throw new Error(`Fallback field kind "${FALLBACK_FIELD_KIND}" not found`);
122
+ }
123
+ return {
124
+ get(kind) {
125
+ return custom.get(kind) ?? base[kind];
126
+ },
127
+ has(kind) {
128
+ return custom.has(kind) || kind in base;
129
+ },
130
+ getOrFallback(kind) {
131
+ return custom.get(kind) ?? base[kind] ?? fallback;
132
+ },
133
+ registerFieldRenderer(kind, entry) {
134
+ if (entry.fieldKind !== kind) {
135
+ throw new Error(`Field renderer entry fieldKind "${entry.fieldKind}" must match kind "${kind}"`);
136
+ }
137
+ custom.set(kind, entry);
138
+ }
139
+ };
140
+ }
141
+ export {
142
+ createMutableFieldRendererRegistry,
143
+ createFieldRendererRegistry,
144
+ FALLBACK_FIELD_KIND
145
+ };