@ggui-ai/protocol 0.1.0-rc.1

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 (222) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +46 -0
  3. package/dist/bridge/invoke-agent.d.ts +65 -0
  4. package/dist/bridge/invoke-agent.d.ts.map +1 -0
  5. package/dist/bridge/invoke-agent.js +113 -0
  6. package/dist/envelope-adapters.d.ts +24 -0
  7. package/dist/envelope-adapters.d.ts.map +1 -0
  8. package/dist/envelope-adapters.js +14 -0
  9. package/dist/envelopes/builders.d.ts +145 -0
  10. package/dist/envelopes/builders.d.ts.map +1 -0
  11. package/dist/envelopes/builders.js +113 -0
  12. package/dist/errors/unknown-permission-name.d.ts +12 -0
  13. package/dist/errors/unknown-permission-name.d.ts.map +1 -0
  14. package/dist/errors/unknown-permission-name.js +29 -0
  15. package/dist/errors/version-mismatch.d.ts +55 -0
  16. package/dist/errors/version-mismatch.d.ts.map +1 -0
  17. package/dist/errors/version-mismatch.js +52 -0
  18. package/dist/gadgets/resolve-contract-gadgets.d.ts +93 -0
  19. package/dist/gadgets/resolve-contract-gadgets.d.ts.map +1 -0
  20. package/dist/gadgets/resolve-contract-gadgets.js +119 -0
  21. package/dist/gadgets/stdlib-gadgets.d.ts +43 -0
  22. package/dist/gadgets/stdlib-gadgets.d.ts.map +1 -0
  23. package/dist/gadgets/stdlib-gadgets.js +161 -0
  24. package/dist/iframe-bridge.d.ts +63 -0
  25. package/dist/iframe-bridge.d.ts.map +1 -0
  26. package/dist/iframe-bridge.js +166 -0
  27. package/dist/index.d.ts +62 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +79 -0
  30. package/dist/integrations/mcp-apps.d.ts +1218 -0
  31. package/dist/integrations/mcp-apps.d.ts.map +1 -0
  32. package/dist/integrations/mcp-apps.js +427 -0
  33. package/dist/navigation/index.d.ts +3 -0
  34. package/dist/navigation/index.d.ts.map +1 -0
  35. package/dist/navigation/index.js +1 -0
  36. package/dist/navigation/stack-navigation.d.ts +55 -0
  37. package/dist/navigation/stack-navigation.d.ts.map +1 -0
  38. package/dist/navigation/stack-navigation.js +80 -0
  39. package/dist/recommended-prompts.d.ts +56 -0
  40. package/dist/recommended-prompts.d.ts.map +1 -0
  41. package/dist/recommended-prompts.js +55 -0
  42. package/dist/registry/blueprint-key.d.ts +9 -0
  43. package/dist/registry/blueprint-key.d.ts.map +1 -0
  44. package/dist/registry/blueprint-key.js +28 -0
  45. package/dist/registry/canonicalize-contract.d.ts +35 -0
  46. package/dist/registry/canonicalize-contract.d.ts.map +1 -0
  47. package/dist/registry/canonicalize-contract.js +166 -0
  48. package/dist/registry/summarize-contract.d.ts +46 -0
  49. package/dist/registry/summarize-contract.d.ts.map +1 -0
  50. package/dist/registry/summarize-contract.js +63 -0
  51. package/dist/schema-learning/derive-contract.d.ts +67 -0
  52. package/dist/schema-learning/derive-contract.d.ts.map +1 -0
  53. package/dist/schema-learning/derive-contract.js +117 -0
  54. package/dist/schema-learning/merge.d.ts +32 -0
  55. package/dist/schema-learning/merge.d.ts.map +1 -0
  56. package/dist/schema-learning/merge.js +146 -0
  57. package/dist/schemas/blueprint.d.ts +32 -0
  58. package/dist/schemas/blueprint.d.ts.map +1 -0
  59. package/dist/schemas/blueprint.js +92 -0
  60. package/dist/schemas/data-contract.d.ts +750 -0
  61. package/dist/schemas/data-contract.d.ts.map +1 -0
  62. package/dist/schemas/data-contract.js +663 -0
  63. package/dist/schemas/gadget-name-grammar.d.ts +29 -0
  64. package/dist/schemas/gadget-name-grammar.d.ts.map +1 -0
  65. package/dist/schemas/gadget-name-grammar.js +28 -0
  66. package/dist/schemas/handshake-suggestion.d.ts +46 -0
  67. package/dist/schemas/handshake-suggestion.d.ts.map +1 -0
  68. package/dist/schemas/handshake-suggestion.js +107 -0
  69. package/dist/schemas/invoke.d.ts +337 -0
  70. package/dist/schemas/invoke.d.ts.map +1 -0
  71. package/dist/schemas/invoke.js +169 -0
  72. package/dist/schemas/mcp.d.ts +301 -0
  73. package/dist/schemas/mcp.d.ts.map +1 -0
  74. package/dist/schemas/mcp.js +373 -0
  75. package/dist/schemas/ops-blueprint.d.ts +176 -0
  76. package/dist/schemas/ops-blueprint.d.ts.map +1 -0
  77. package/dist/schemas/ops-blueprint.js +259 -0
  78. package/dist/schemas/sync-check.d.ts +11 -0
  79. package/dist/schemas/sync-check.d.ts.map +1 -0
  80. package/dist/schemas/sync-check.js +60 -0
  81. package/dist/screen-blueprints/define.d.ts +22 -0
  82. package/dist/screen-blueprints/define.d.ts.map +1 -0
  83. package/dist/screen-blueprints/define.js +3 -0
  84. package/dist/screen-blueprints/index.d.ts +4 -0
  85. package/dist/screen-blueprints/index.d.ts.map +1 -0
  86. package/dist/screen-blueprints/index.js +3 -0
  87. package/dist/screen-blueprints/match.d.ts +35 -0
  88. package/dist/screen-blueprints/match.d.ts.map +1 -0
  89. package/dist/screen-blueprints/match.js +51 -0
  90. package/dist/screen-blueprints/types.d.ts +164 -0
  91. package/dist/screen-blueprints/types.d.ts.map +1 -0
  92. package/dist/screen-blueprints/types.js +1 -0
  93. package/dist/stream/stream-parser.d.ts +62 -0
  94. package/dist/stream/stream-parser.d.ts.map +1 -0
  95. package/dist/stream/stream-parser.js +199 -0
  96. package/dist/transport/websocket.d.ts +178 -0
  97. package/dist/transport/websocket.d.ts.map +1 -0
  98. package/dist/transport/websocket.js +1 -0
  99. package/dist/types/app-config.d.ts +61 -0
  100. package/dist/types/app-config.d.ts.map +1 -0
  101. package/dist/types/app-config.js +1 -0
  102. package/dist/types/auth.d.ts +61 -0
  103. package/dist/types/auth.d.ts.map +1 -0
  104. package/dist/types/auth.js +1 -0
  105. package/dist/types/blueprint.d.ts +206 -0
  106. package/dist/types/blueprint.d.ts.map +1 -0
  107. package/dist/types/blueprint.js +1 -0
  108. package/dist/types/canvas-lifecycle.d.ts +105 -0
  109. package/dist/types/canvas-lifecycle.d.ts.map +1 -0
  110. package/dist/types/canvas-lifecycle.js +38 -0
  111. package/dist/types/capabilities.d.ts +40 -0
  112. package/dist/types/capabilities.d.ts.map +1 -0
  113. package/dist/types/capabilities.js +19 -0
  114. package/dist/types/contract-inference.d.ts +401 -0
  115. package/dist/types/contract-inference.d.ts.map +1 -0
  116. package/dist/types/contract-inference.js +44 -0
  117. package/dist/types/credential.d.ts +41 -0
  118. package/dist/types/credential.d.ts.map +1 -0
  119. package/dist/types/credential.js +32 -0
  120. package/dist/types/data-bindings.d.ts +322 -0
  121. package/dist/types/data-bindings.d.ts.map +1 -0
  122. package/dist/types/data-bindings.js +29 -0
  123. package/dist/types/data-contract.d.ts +1296 -0
  124. package/dist/types/data-contract.d.ts.map +1 -0
  125. package/dist/types/data-contract.js +111 -0
  126. package/dist/types/events.d.ts +182 -0
  127. package/dist/types/events.d.ts.map +1 -0
  128. package/dist/types/events.js +8 -0
  129. package/dist/types/feedback.d.ts +24 -0
  130. package/dist/types/feedback.d.ts.map +1 -0
  131. package/dist/types/feedback.js +7 -0
  132. package/dist/types/gadget.d.ts +121 -0
  133. package/dist/types/gadget.d.ts.map +1 -0
  134. package/dist/types/gadget.js +24 -0
  135. package/dist/types/handshake-suggestion.d.ts +264 -0
  136. package/dist/types/handshake-suggestion.d.ts.map +1 -0
  137. package/dist/types/handshake-suggestion.js +70 -0
  138. package/dist/types/host-context.d.ts +163 -0
  139. package/dist/types/host-context.d.ts.map +1 -0
  140. package/dist/types/host-context.js +142 -0
  141. package/dist/types/interface-context.d.ts +105 -0
  142. package/dist/types/interface-context.d.ts.map +1 -0
  143. package/dist/types/interface-context.js +115 -0
  144. package/dist/types/invoke.d.ts +28 -0
  145. package/dist/types/invoke.d.ts.map +1 -0
  146. package/dist/types/invoke.js +7 -0
  147. package/dist/types/live-channel.d.ts +613 -0
  148. package/dist/types/live-channel.d.ts.map +1 -0
  149. package/dist/types/live-channel.js +1 -0
  150. package/dist/types/llm.d.ts +61 -0
  151. package/dist/types/llm.d.ts.map +1 -0
  152. package/dist/types/llm.js +186 -0
  153. package/dist/types/mcp-proxy.d.ts +67 -0
  154. package/dist/types/mcp-proxy.d.ts.map +1 -0
  155. package/dist/types/mcp-proxy.js +46 -0
  156. package/dist/types/mcp.d.ts +637 -0
  157. package/dist/types/mcp.d.ts.map +1 -0
  158. package/dist/types/mcp.js +30 -0
  159. package/dist/types/openrouter-models.d.ts +22 -0
  160. package/dist/types/openrouter-models.d.ts.map +1 -0
  161. package/dist/types/openrouter-models.js +4843 -0
  162. package/dist/types/region.d.ts +26 -0
  163. package/dist/types/region.d.ts.map +1 -0
  164. package/dist/types/region.js +36 -0
  165. package/dist/types/session.d.ts +419 -0
  166. package/dist/types/session.d.ts.map +1 -0
  167. package/dist/types/session.js +1 -0
  168. package/dist/types/thread.d.ts +207 -0
  169. package/dist/types/thread.d.ts.map +1 -0
  170. package/dist/types/thread.js +57 -0
  171. package/dist/types/ui-generator.d.ts +100 -0
  172. package/dist/types/ui-generator.d.ts.map +1 -0
  173. package/dist/types/ui-generator.js +53 -0
  174. package/dist/validation/ajv-runtime.d.ts +140 -0
  175. package/dist/validation/ajv-runtime.d.ts.map +1 -0
  176. package/dist/validation/ajv-runtime.js +452 -0
  177. package/dist/validation/content-hash.d.ts +3 -0
  178. package/dist/validation/content-hash.d.ts.map +1 -0
  179. package/dist/validation/content-hash.js +21 -0
  180. package/dist/validation/contract-validator.d.ts +244 -0
  181. package/dist/validation/contract-validator.d.ts.map +1 -0
  182. package/dist/validation/contract-validator.js +711 -0
  183. package/dist/validation/cross-references.d.ts +105 -0
  184. package/dist/validation/cross-references.d.ts.map +1 -0
  185. package/dist/validation/cross-references.js +164 -0
  186. package/dist/validation/hygiene-rules.d.ts +250 -0
  187. package/dist/validation/hygiene-rules.d.ts.map +1 -0
  188. package/dist/validation/hygiene-rules.js +564 -0
  189. package/dist/validation/lint-contract.d.ts +130 -0
  190. package/dist/validation/lint-contract.d.ts.map +1 -0
  191. package/dist/validation/lint-contract.js +225 -0
  192. package/dist/validation/name-invariants.d.ts +117 -0
  193. package/dist/validation/name-invariants.d.ts.map +1 -0
  194. package/dist/validation/name-invariants.js +172 -0
  195. package/dist/validation/reserved-channels.d.ts +156 -0
  196. package/dist/validation/reserved-channels.d.ts.map +1 -0
  197. package/dist/validation/reserved-channels.js +356 -0
  198. package/dist/validation/resolve-stream-channel.d.ts +78 -0
  199. package/dist/validation/resolve-stream-channel.d.ts.map +1 -0
  200. package/dist/validation/resolve-stream-channel.js +64 -0
  201. package/dist/validation/sanitize-error.d.ts +46 -0
  202. package/dist/validation/sanitize-error.d.ts.map +1 -0
  203. package/dist/validation/sanitize-error.js +88 -0
  204. package/dist/validation/schema-compat-invariants.d.ts +140 -0
  205. package/dist/validation/schema-compat-invariants.d.ts.map +1 -0
  206. package/dist/validation/schema-compat-invariants.js +220 -0
  207. package/dist/validation/schema-meta-validation.d.ts +60 -0
  208. package/dist/validation/schema-meta-validation.d.ts.map +1 -0
  209. package/dist/validation/schema-meta-validation.js +131 -0
  210. package/dist/validation/schema-subset.d.ts +165 -0
  211. package/dist/validation/schema-subset.d.ts.map +1 -0
  212. package/dist/validation/schema-subset.js +295 -0
  213. package/dist/validation/ui-security.d.ts +54 -0
  214. package/dist/validation/ui-security.d.ts.map +1 -0
  215. package/dist/validation/ui-security.js +138 -0
  216. package/dist/validation/zod-to-json-schema.d.ts +63 -0
  217. package/dist/validation/zod-to-json-schema.d.ts.map +1 -0
  218. package/dist/validation/zod-to-json-schema.js +126 -0
  219. package/dist/version.d.ts +1458 -0
  220. package/dist/version.d.ts.map +1 -0
  221. package/dist/version.js +1459 -0
  222. package/package.json +113 -0
@@ -0,0 +1,452 @@
1
+ /**
2
+ * Ajv-backed JSON Schema validation runtime for ggui contracts.
3
+ *
4
+ * Owns layers B (inner JSON Schema meta-validation) and C (runtime
5
+ * data validation across propsSpec / actionSpec / streamSpec /
6
+ * contextSpec) of the six-layer model. The outer A-layers — protocol
7
+ * wrappers (DataContract envelope, PropsSpec, PropEntry, ActionEntry,
8
+ * etc.) — stay on zod where TS inference + structural strict-mode
9
+ * already do their job.
10
+ *
11
+ * Why Ajv:
12
+ * - Canonical JSON Schema validator (303M weekly downloads).
13
+ * - Single source of truth: same compiled validator powers all four
14
+ * runtime spec surfaces, so closed-shape semantics never diverge
15
+ * between props vs action vs stream vs context.
16
+ * - Compile-time meta-validation: `strict: true` rejects malformed
17
+ * JSON Schemas at `compile()` — agents discover bugs at
18
+ * handshake/push, not at first data flow.
19
+ *
20
+ * Closed-shape (load-bearing):
21
+ * JSON Schema's default is `additionalProperties: true` (extras
22
+ * allowed). Our "propsSpec IS the contract" promise needs
23
+ * closed-shape at EVERY depth. Rather than tax agents with
24
+ * `additionalProperties: false` at every object node, we inject it
25
+ * recursively via {@link injectClosedShape} before Ajv compiles.
26
+ * An author who explicitly sets `additionalProperties` (boolean or
27
+ * schema) keeps that intent — escape hatch for the rare case where
28
+ * open extension is intentional.
29
+ *
30
+ * Tolerated metadata keywords:
31
+ * - `example` (singular, OpenAPI-ish; JSON Schema standard is
32
+ * `examples` array). Treated as informational.
33
+ * - `nullable` (OpenAPI 3.0 shorthand). Tolerated; the canonical
34
+ * way to express nullability is `type: [<original>, 'null']`.
35
+ *
36
+ * Both are registered as no-op keywords so Ajv strict mode doesn't
37
+ * reject schemas that carry them.
38
+ */
39
+ import Ajv from 'ajv';
40
+ import addFormats from 'ajv-formats';
41
+ import standaloneCode from 'ajv/dist/standalone/index.js';
42
+ import equalImport from 'ajv/dist/runtime/equal.js';
43
+ import ucs2lengthImport from 'ajv/dist/runtime/ucs2length.js';
44
+ /**
45
+ * Resolve a runtime-helper module's exported function across the
46
+ * CJS↔ESM interop gap: a plain `module.exports = fn` CJS module
47
+ * surfaces the function directly, while a `__esModule`-flagged one
48
+ * (Ajv's `dist/runtime/*`) surfaces it under `.default`.
49
+ */
50
+ function resolveHelperFn(imported) {
51
+ if (typeof imported === 'function') {
52
+ return imported;
53
+ }
54
+ const inner = imported?.default;
55
+ if (typeof inner === 'function') {
56
+ return inner;
57
+ }
58
+ throw new Error('ajv-runtime: could not resolve a runtime-helper function');
59
+ }
60
+ /**
61
+ * Source text of the two Ajv runtime helpers a standalone validator for
62
+ * our closed contract schemas can reach: `equal` (fast-deep-equal —
63
+ * emitted for `uniqueItems` and object-valued `enum`/`const`) and
64
+ * `ucs2length` (emitted for string `minLength`/`maxLength`). Ajv
65
+ * references both by bare specifier; {@link compileValidatorModule}
66
+ * inlines this source so the emitted module is fully self-contained —
67
+ * no bare-specifier imports the CSP-sandboxed renderer iframe would
68
+ * fail to resolve. Captured once at module init; `toString()` on a
69
+ * pure function is deterministic.
70
+ */
71
+ const FAST_DEEP_EQUAL_SOURCE = resolveHelperFn(equalImport).toString();
72
+ const UCS2LENGTH_SOURCE = resolveHelperFn(ucs2lengthImport).toString();
73
+ /**
74
+ * Singleton Ajv instance. Configured once with:
75
+ * - `strict: true` — rejects unknown keywords + malformed schemas
76
+ * at compile-time (layer B meta-validation as a side effect).
77
+ * - `allErrors: true` — collect all violations per validation, not
78
+ * just the first. The agent sees the full picture in one round.
79
+ * - `useDefaults: false` — don't mutate input by filling defaults.
80
+ * Contract validation is read-only.
81
+ * - `coerceTypes: false` — strict types. `"5"` is not a number.
82
+ * - `removeAdditional: false` — extras MUST error, not be silently
83
+ * stripped. The closed-shape promise depends on this.
84
+ */
85
+ const AJV_OPTIONS = {
86
+ strict: true,
87
+ allErrors: true,
88
+ useDefaults: false,
89
+ coerceTypes: false,
90
+ removeAdditional: false,
91
+ verbose: true,
92
+ };
93
+ const ajv = new Ajv({ ...AJV_OPTIONS });
94
+ addFormats(ajv);
95
+ if (!ajv.getKeyword('example'))
96
+ ajv.addKeyword({ keyword: 'example' });
97
+ if (!ajv.getKeyword('nullable'))
98
+ ajv.addKeyword({ keyword: 'nullable' });
99
+ /**
100
+ * Dedicated Ajv instance for {@link compileValidatorModule}. Same
101
+ * options as the singleton plus `code.source`/`code.esm` so Ajv emits
102
+ * the validator as ESM source text instead of a live function.
103
+ *
104
+ * Why a second instance: `code.source` makes every compiled validator
105
+ * carry its generated source — a cost the runtime-validation singleton
106
+ * doesn't need. Keeping standalone emission isolated leaves the hot
107
+ * `compileForValidation` path unchanged.
108
+ */
109
+ const standaloneAjv = new Ajv({
110
+ ...AJV_OPTIONS,
111
+ code: { source: true, esm: true },
112
+ });
113
+ addFormats(standaloneAjv);
114
+ if (!standaloneAjv.getKeyword('example')) {
115
+ standaloneAjv.addKeyword({ keyword: 'example' });
116
+ }
117
+ if (!standaloneAjv.getKeyword('nullable')) {
118
+ standaloneAjv.addKeyword({ keyword: 'nullable' });
119
+ }
120
+ /**
121
+ * Recursively walk a JSON Schema and inject
122
+ * `additionalProperties: false` at every object node. Authors who
123
+ * explicitly set `additionalProperties` keep that intent (boolean
124
+ * preserved; schema recursed into).
125
+ *
126
+ * Walks:
127
+ * - `properties` (each entry)
128
+ * - `items` (array element schema)
129
+ * - `additionalProperties` (when it's a schema)
130
+ * - `oneOf` / `anyOf` (each branch)
131
+ *
132
+ * Returns a new schema tree; never mutates the input.
133
+ */
134
+ export function injectClosedShape(schema) {
135
+ const isObjectNode = schema.type === 'object' || schema.properties !== undefined;
136
+ if (isObjectNode) {
137
+ const out = { ...schema };
138
+ if (out.properties) {
139
+ const newProps = {};
140
+ for (const [k, v] of Object.entries(out.properties)) {
141
+ newProps[k] = injectClosedShape(v);
142
+ }
143
+ out.properties = newProps;
144
+ }
145
+ if (out.additionalProperties === undefined) {
146
+ out.additionalProperties = false;
147
+ }
148
+ else if (typeof out.additionalProperties !== 'boolean') {
149
+ out.additionalProperties = injectClosedShape(out.additionalProperties);
150
+ }
151
+ if (out.oneOf)
152
+ out.oneOf = out.oneOf.map(injectClosedShape);
153
+ if (out.anyOf)
154
+ out.anyOf = out.anyOf.map(injectClosedShape);
155
+ return out;
156
+ }
157
+ if (schema.type === 'array' && schema.items) {
158
+ return { ...schema, items: injectClosedShape(schema.items) };
159
+ }
160
+ if (schema.oneOf || schema.anyOf) {
161
+ const out = { ...schema };
162
+ if (out.oneOf)
163
+ out.oneOf = out.oneOf.map(injectClosedShape);
164
+ if (out.anyOf)
165
+ out.anyOf = out.anyOf.map(injectClosedShape);
166
+ return out;
167
+ }
168
+ return schema;
169
+ }
170
+ /**
171
+ * Compile a JSON Schema into an Ajv {@link ValidateFunction}, with
172
+ * closed-shape injected at every object node. Throws if the schema
173
+ * is malformed under Ajv strict mode — this is layer B meta-
174
+ * validation as a free side effect. Dedicated meta-validation call
175
+ * sites (handshake / push) wrap this in a structured error.
176
+ *
177
+ * Not cached. Compilation is fast and contracts are small; caching
178
+ * adds a memory cost without a measured win. Revisit if profiling
179
+ * shows compile dominating.
180
+ */
181
+ export function compileForValidation(schema) {
182
+ const injected = injectClosedShape(schema);
183
+ return ajv.compile(injected);
184
+ }
185
+ /**
186
+ * Compile a JSON Schema into a standalone, **fully self-contained ESM
187
+ * validator module** — source text, never a live function. Closed-shape
188
+ * is injected first, exactly as {@link compileForValidation} does, so
189
+ * the emitted validator enforces the same semantics.
190
+ *
191
+ * Why this exists: the renderer iframe runs under a strict CSP with no
192
+ * `'unsafe-eval'`, so `ajv.compile()` (which builds the validator via
193
+ * `new Function`) throws `EvalError` there. Codegen has to happen
194
+ * where `eval` is legal — the server, at push time, where the contract
195
+ * schema is already fixed. The iframe then loads this module source
196
+ * via a `blob:` dynamic import (governed by `script-src`, not
197
+ * `unsafe-eval`) and only ever *runs* the validator.
198
+ *
199
+ * The returned module `export default`s the validator function (and
200
+ * also names it `validate`). Ajv standalone references its runtime
201
+ * helpers by bare specifier (`ajv/dist/runtime/*`) — the
202
+ * CSP-sandboxed iframe has no bundler to resolve those, so this
203
+ * function **inlines** every helper a closed-contract validator can
204
+ * reach (in practice only `equal` / fast-deep-equal, for `uniqueItems`
205
+ * and object-valued `enum`/`const`). The result has zero imports. A
206
+ * survivor check throws if any un-inlined bare import remains, so a new
207
+ * Ajv helper surfaces as a loud server-side failure, never as silent
208
+ * iframe breakage.
209
+ *
210
+ * Throws if the schema is malformed under Ajv strict mode — same
211
+ * layer-B meta-validation side effect as {@link compileForValidation}.
212
+ */
213
+ export function compileValidatorModule(schema) {
214
+ const injected = injectClosedShape(schema);
215
+ const validate = standaloneAjv.compile(injected);
216
+ return inlineRuntimeHelpers(standaloneCode(standaloneAjv, validate));
217
+ }
218
+ /**
219
+ * Inlinable Ajv runtime helpers, keyed by bare specifier. Ajv standalone
220
+ * references these by `import`; the CSP-sandboxed iframe has no bundler
221
+ * to resolve a bare specifier, so {@link inlineRuntimeHelpers} replaces
222
+ * each `import` with the helper's source inline.
223
+ */
224
+ const RUNTIME_HELPER_SOURCES = {
225
+ 'ajv/dist/runtime/equal': FAST_DEEP_EQUAL_SOURCE,
226
+ 'ajv/dist/runtime/ucs2length': UCS2LENGTH_SOURCE,
227
+ };
228
+ /**
229
+ * Make an Ajv standalone module fully self-contained: normalize any
230
+ * CJS `require` of a runtime helper to ESM `import`, inline every
231
+ * helper we support, and assert nothing un-inlined survives.
232
+ */
233
+ function inlineRuntimeHelpers(source) {
234
+ // Ajv standalone may emit CJS `require(...)` for runtime helpers even
235
+ // under `code.esm`. Normalize to ESM `import` first so one inliner
236
+ // pass below handles both emission styles.
237
+ let out = source
238
+ .replace(/const (\w+) = require\("([^"]+)"\)\.default;/g, 'import $1 from "$2";')
239
+ .replace(/const (\w+) = require\("([^"]+)"\);/g, 'import * as $1 from "$2";');
240
+ // Inline each supported runtime helper — replacing the `import` with
241
+ // an inline `const` keeps the emitted module free of bare specifiers.
242
+ // Function form of `.replace` so a `$` in the helper source is never
243
+ // treated as a capture-group reference.
244
+ for (const [specifier, helperSource] of Object.entries(RUNTIME_HELPER_SOURCES)) {
245
+ const importRe = new RegExp(`import (\\w+) from "${specifier.replace(/[/]/g, '\\/')}";`, 'g');
246
+ out = out.replace(importRe, (_match, binding) => `const ${binding} = ${helperSource};`);
247
+ }
248
+ // Survivor check: a remaining `ajv/dist/runtime/*` import means Ajv
249
+ // emitted a helper we don't inline. Fail loud here (server-side,
250
+ // caught by tests / push) rather than shipping a module the iframe
251
+ // cannot load. Scoped to the `ajv/dist/runtime/` prefix — the only
252
+ // specifiers Ajv standalone emits — so an embedded contract-schema
253
+ // string can't false-trip it.
254
+ const leftover = /import\s+[\w*\s{},]+from\s*"(ajv\/dist\/runtime\/[^"]+)"/.exec(out);
255
+ if (leftover) {
256
+ throw new Error(`compileValidatorModule: emitted module has an un-inlined import of "${leftover[1]}". Add it to RUNTIME_HELPER_SOURCES.`);
257
+ }
258
+ return out;
259
+ }
260
+ /**
261
+ * Convert Ajv error objects into our {@link ContractViolation} shape.
262
+ *
263
+ * Path translation: Ajv `instancePath: '/todos/0/done'` →
264
+ * `field: 'todos[0].done'`. Numeric segments become bracket indices,
265
+ * named segments become dot-separated. Empty instancePath collapses
266
+ * to `''` (root-level error).
267
+ *
268
+ * Per-keyword mapping (see {@link mapOne}):
269
+ * - `additionalProperties` — extra-key violation; `field` includes
270
+ * the offending key, `expected: '<declared key>'`.
271
+ * - `required` — missing-key violation; `field` includes the
272
+ * missing key, `expected: 'present'`, `received: 'undefined'`.
273
+ * - `type` — type mismatch; `expected` is the JSON Schema type,
274
+ * `received` reads from the violating value.
275
+ * - `enum` / `const` — `expected` is the allowed value(s);
276
+ * `received` is the offending value.
277
+ * - `pattern` — `expected` is the regex; `received` is the
278
+ * offending string.
279
+ * - other keywords — fall through to Ajv's message verbatim.
280
+ */
281
+ export function mapAjvErrorsToViolations(errors, data) {
282
+ if (!errors)
283
+ return [];
284
+ return errors.map(err => mapOne(err, data));
285
+ }
286
+ /**
287
+ * Re-anchor a list of Ajv-mapped violations under a stable field
288
+ * prefix. Used by the four spec validators to lift Ajv's root-
289
+ * relative paths into the caller's namespace:
290
+ * - propsSpec: no prefix (paths already prop-relative).
291
+ * - actionSpec: `<actionName>.data`.
292
+ * - streamSpec: `<channelName>.payload`.
293
+ * - contextSpec: `<slotName>.value`.
294
+ *
295
+ * Empty `field` (root-level violation) collapses to the prefix
296
+ * itself; sub-fields dot-join.
297
+ */
298
+ export function prefixViolations(violations, prefix) {
299
+ if (!prefix)
300
+ return violations;
301
+ return violations.map(v => ({
302
+ ...v,
303
+ field: v.field ? `${prefix}.${v.field}` : prefix,
304
+ }));
305
+ }
306
+ function mapOne(err, root) {
307
+ const path = pathFromInstancePath(err.instancePath);
308
+ switch (err.keyword) {
309
+ case 'additionalProperties': {
310
+ const params = err.params;
311
+ const extra = params.additionalProperty ?? '<unknown>';
312
+ const fieldPath = path ? `${path}.${extra}` : extra;
313
+ const parentSchema = err.parentSchema;
314
+ const declaredKeys = parentSchema?.properties
315
+ ? Object.keys(parentSchema.properties)
316
+ : [];
317
+ const value = resolveAtPath(root, `${err.instancePath}/${extra}`);
318
+ const declaredHint = declaredKeys.length > 0
319
+ ? ` Declared keys: [${declaredKeys.join(', ')}].`
320
+ : ' Declared keys: [(none)].';
321
+ return {
322
+ field: fieldPath,
323
+ message: `Undeclared field '${extra}'${path ? ` at '${path}'` : ''}.${declaredHint}`,
324
+ expected: '<declared key>',
325
+ received: jsonTypeOf(value),
326
+ };
327
+ }
328
+ case 'required': {
329
+ const params = err.params;
330
+ const missing = params.missingProperty ?? '<unknown>';
331
+ const fieldPath = path ? `${path}.${missing}` : missing;
332
+ return {
333
+ field: fieldPath,
334
+ message: `Required field '${missing}' missing${path ? ` at '${path}'` : ''}`,
335
+ expected: 'present',
336
+ received: 'undefined',
337
+ };
338
+ }
339
+ case 'type': {
340
+ const params = err.params;
341
+ const expected = Array.isArray(params.type)
342
+ ? params.type.join('|')
343
+ : params.type ?? 'unknown';
344
+ const received = jsonTypeOf(resolveAtPath(root, err.instancePath));
345
+ return {
346
+ field: path,
347
+ message: err.message ?? `Type mismatch at '${path || '<root>'}'`,
348
+ expected,
349
+ received,
350
+ };
351
+ }
352
+ case 'enum': {
353
+ const params = err.params;
354
+ const allowed = params.allowedValues ?? [];
355
+ const value = resolveAtPath(root, err.instancePath);
356
+ return {
357
+ field: path,
358
+ message: err.message ?? `Enum mismatch at '${path || '<root>'}'`,
359
+ expected: allowed.map(v => JSON.stringify(v)).join('|'),
360
+ received: JSON.stringify(value),
361
+ };
362
+ }
363
+ case 'const': {
364
+ const params = err.params;
365
+ const value = resolveAtPath(root, err.instancePath);
366
+ return {
367
+ field: path,
368
+ message: err.message ?? `Const mismatch at '${path || '<root>'}'`,
369
+ expected: JSON.stringify(params.allowedValue),
370
+ received: JSON.stringify(value),
371
+ };
372
+ }
373
+ case 'pattern': {
374
+ const params = err.params;
375
+ const value = resolveAtPath(root, err.instancePath);
376
+ return {
377
+ field: path,
378
+ message: err.message ?? `Pattern mismatch at '${path || '<root>'}'`,
379
+ expected: params.pattern ?? '<pattern>',
380
+ received: typeof value === 'string' ? value : JSON.stringify(value),
381
+ };
382
+ }
383
+ default: {
384
+ return {
385
+ field: path,
386
+ message: err.message ?? `Validation failed${path ? ` at '${path}'` : ''} (${err.keyword})`,
387
+ };
388
+ }
389
+ }
390
+ }
391
+ /**
392
+ * Convert Ajv slash-style instance path to our bracket-dot field path.
393
+ *
394
+ * Examples:
395
+ * `''` → `''`
396
+ * `'/todos'` → `'todos'`
397
+ * `'/todos/0'` → `'todos[0]'`
398
+ * `'/todos/0/done'` → `'todos[0].done'`
399
+ * `'/users/alice/age'` → `'users.alice.age'`
400
+ *
401
+ * Numeric segments become bracket indices; everything else dot-joins.
402
+ * Ajv pre-decodes `~0`/`~1` JSON Pointer escapes, so a key with `/`
403
+ * still arrives slash-free.
404
+ */
405
+ function pathFromInstancePath(instancePath) {
406
+ if (!instancePath)
407
+ return '';
408
+ const parts = instancePath.slice(1).split('/');
409
+ let out = '';
410
+ for (const part of parts) {
411
+ if (/^\d+$/.test(part)) {
412
+ out += `[${part}]`;
413
+ }
414
+ else if (out === '') {
415
+ out = part;
416
+ }
417
+ else {
418
+ out += `.${part}`;
419
+ }
420
+ }
421
+ return out;
422
+ }
423
+ function resolveAtPath(root, instancePath) {
424
+ if (!instancePath)
425
+ return root;
426
+ const parts = instancePath.slice(1).split('/');
427
+ let cur = root;
428
+ for (const part of parts) {
429
+ if (cur === null || cur === undefined)
430
+ return undefined;
431
+ if (Array.isArray(cur)) {
432
+ const idx = Number(part);
433
+ if (!Number.isInteger(idx))
434
+ return undefined;
435
+ cur = cur[idx];
436
+ }
437
+ else if (typeof cur === 'object') {
438
+ cur = cur[part];
439
+ }
440
+ else {
441
+ return undefined;
442
+ }
443
+ }
444
+ return cur;
445
+ }
446
+ function jsonTypeOf(value) {
447
+ if (value === null)
448
+ return 'null';
449
+ if (Array.isArray(value))
450
+ return 'array';
451
+ return typeof value;
452
+ }
@@ -0,0 +1,3 @@
1
+ /** SHA-256 content hash (16-char hex) — canonical identity for a compiled UI. */
2
+ export declare function contentHash(compiledCode: string): string;
3
+ //# sourceMappingURL=content-hash.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content-hash.d.ts","sourceRoot":"","sources":["../../src/validation/content-hash.ts"],"names":[],"mappings":"AAmBA,iFAAiF;AACjF,wBAAgB,WAAW,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAExD"}
@@ -0,0 +1,21 @@
1
+ // packages/protocol/src/validation/content-hash.ts
2
+ //
3
+ // SHA-256 content hashing for compiled UI assets — the canonical identity
4
+ // function for cached + registered UIs.
5
+ //
6
+ // Why this lives in its own module (not `ui-security.ts`): `createHash`
7
+ // only ships in Node's `node:crypto` builtin. Re-exporting it from the
8
+ // protocol's root barrel drags `node:crypto` into every downstream
9
+ // bundler's module graph — including browser apps like Studio, where
10
+ // webpack refuses to resolve the `node:` scheme and the whole page fails
11
+ // to compile. Keeping this in a server-only subpath lets the root barrel
12
+ // stay browser-safe.
13
+ //
14
+ // Consumers (all server-side): `core/src/validation/ui-compiler.ts`,
15
+ // `cloud/amplify/functions/rest-api/cli-api/ui-register-handler.ts`.
16
+ // Import as `@ggui-ai/protocol/content-hash` — never from the barrel.
17
+ import { createHash } from 'node:crypto';
18
+ /** SHA-256 content hash (16-char hex) — canonical identity for a compiled UI. */
19
+ export function contentHash(compiledCode) {
20
+ return createHash('sha256').update(compiledCode).digest('hex').slice(0, 16);
21
+ }