@fuaran-ui/conformance 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (301) hide show
  1. package/LICENSE +215 -0
  2. package/README.md +85 -0
  3. package/corpus/elicitation/elc-ans-accept-full.json +1 -0
  4. package/corpus/elicitation/elc-ans-accept-int-for-float.json +1 -0
  5. package/corpus/elicitation/elc-ans-accept-minimal.json +1 -0
  6. package/corpus/elicitation/elc-ans-reject-enum.json +1 -0
  7. package/corpus/elicitation/elc-ans-reject-float-range.json +1 -0
  8. package/corpus/elicitation/elc-ans-reject-int-range.json +1 -0
  9. package/corpus/elicitation/elc-ans-reject-missing-required.json +1 -0
  10. package/corpus/elicitation/elc-ans-reject-string-len.json +1 -0
  11. package/corpus/elicitation/elc-ans-reject-type-mismatch.json +1 -0
  12. package/corpus/elicitation/elc-ans-reject-undeclared.json +1 -0
  13. package/corpus/elicitation/elc-full.json +1 -0
  14. package/corpus/elicitation/elc-minimal.json +1 -0
  15. package/corpus/elicitation/elc-out-answered.json +1 -0
  16. package/corpus/elicitation/elc-out-declined.json +1 -0
  17. package/corpus/elicitation/elc-out-reject-empty-id.json +1 -0
  18. package/corpus/elicitation/elc-out-reject-missing-answer.json +1 -0
  19. package/corpus/elicitation/elc-out-reject-stray-key.json +1 -0
  20. package/corpus/elicitation/elc-out-reject-unknown-type.json +1 -0
  21. package/corpus/elicitation/elc-out-superseded-anon.json +1 -0
  22. package/corpus/elicitation/elc-out-superseded-by.json +1 -0
  23. package/corpus/elicitation/elc-out-timedout.json +1 -0
  24. package/corpus/elicitation/elc-reject-bad-space.json +1 -0
  25. package/corpus/elicitation/elc-reject-bad-timeout.json +1 -0
  26. package/corpus/elicitation/elc-reject-bad-version.json +1 -0
  27. package/corpus/elicitation/elc-reject-default-nonconformant.json +1 -0
  28. package/corpus/elicitation/elc-reject-duplicate-field.json +1 -0
  29. package/corpus/elicitation/elc-reject-empty-contract.json +1 -0
  30. package/corpus/elicitation/elc-reject-empty-id.json +1 -0
  31. package/corpus/elicitation/elc-reject-missing-version.json +1 -0
  32. package/corpus/elicitation/elc-reject-stray-key.json +1 -0
  33. package/corpus/elicitation/elc-reject-tree-decode.json +1 -0
  34. package/corpus/elicitation/elc-reject-unknown-node.json +1 -0
  35. package/corpus/envelope/envelope-foreign-major.json +1 -0
  36. package/corpus/envelope/envelope-foreign-namespace.json +1 -0
  37. package/corpus/envelope/envelope-known-current.json +1 -0
  38. package/corpus/envelope/envelope-unknown-behind.json +1 -0
  39. package/corpus/envelope/envelope-unknown-malformed-required-profile.json +1 -0
  40. package/corpus/envelope/envelope-unknown-required-profile.json +1 -0
  41. package/corpus/lenient/legacy-upgrade-card.expected.json +1 -0
  42. package/corpus/lenient/legacy-upgrade-card.json +1 -0
  43. package/corpus/lenient/legacy-upgrade-dashboard.expected.json +1 -0
  44. package/corpus/lenient/legacy-upgrade-dashboard.json +1 -0
  45. package/corpus/lenient/legacy-upgrade-gridlayout.expected.json +1 -0
  46. package/corpus/lenient/legacy-upgrade-gridlayout.json +1 -0
  47. package/corpus/lenient/legacy-upgrade-stack.expected.json +1 -0
  48. package/corpus/lenient/legacy-upgrade-stack.json +1 -0
  49. package/corpus/lenient/legacy-upgrade-table.expected.json +1 -0
  50. package/corpus/lenient/legacy-upgrade-table.json +1 -0
  51. package/corpus/lenient/lenient-022-lvr-emphasis-loud.expected.json +1 -0
  52. package/corpus/lenient/lenient-022-lvr-emphasis-loud.json +1 -0
  53. package/corpus/lenient/lenient-022-lvr-emphasis-normal.expected.json +1 -0
  54. package/corpus/lenient/lenient-022-lvr-emphasis-normal.json +1 -0
  55. package/corpus/lenient/lenient-460-alias-emphasis-muted.expected.json +1 -0
  56. package/corpus/lenient/lenient-460-alias-emphasis-muted.json +1 -0
  57. package/corpus/lenient/lenient-460-alias-emphasis-strong.expected.json +1 -0
  58. package/corpus/lenient/lenient-460-alias-emphasis-strong.json +1 -0
  59. package/corpus/lenient/lenient-460-alias-tone-danger.expected.json +1 -0
  60. package/corpus/lenient/lenient-460-alias-tone-danger.json +1 -0
  61. package/corpus/lenient/lenient-460-alias-tone-positive.expected.json +1 -0
  62. package/corpus/lenient/lenient-460-alias-tone-positive.json +1 -0
  63. package/corpus/lenient/lenient-460-explicit-default-column.expected.json +1 -0
  64. package/corpus/lenient/lenient-460-explicit-default-column.json +1 -0
  65. package/corpus/lenient/lenient-460-explicit-default-metric.expected.json +1 -0
  66. package/corpus/lenient/lenient-460-explicit-default-metric.json +1 -0
  67. package/corpus/lenient/lenient-460-explicit-default-style.expected.json +1 -0
  68. package/corpus/lenient/lenient-460-explicit-default-style.json +1 -0
  69. package/corpus/lenient/lenient-596-form-explicit-auto-state.expected.json +1 -0
  70. package/corpus/lenient/lenient-596-form-explicit-auto-state.json +1 -0
  71. package/corpus/lenient/lenient-alias-call-url.expected.json +1 -0
  72. package/corpus/lenient/lenient-alias-call-url.json +1 -0
  73. package/corpus/lenient/lenient-alias-card-title-metric-value.expected.json +1 -0
  74. package/corpus/lenient/lenient-alias-card-title-metric-value.json +1 -0
  75. package/corpus/lenient/lenient-alias-datagrid-data-column-type.expected.json +1 -0
  76. package/corpus/lenient/lenient-alias-datagrid-data-column-type.json +1 -0
  77. package/corpus/lenient/lenient-alias-form-field-name.expected.json +1 -0
  78. package/corpus/lenient/lenient-alias-form-field-name.json +1 -0
  79. package/corpus/lenient/lenient-alias-grid-columns-row.expected.json +1 -0
  80. package/corpus/lenient/lenient-alias-grid-columns-row.json +1 -0
  81. package/corpus/lenient/lenient-alias-navigate-href.expected.json +1 -0
  82. package/corpus/lenient/lenient-alias-navigate-href.json +1 -0
  83. package/corpus/lenient/lenient-alias-select-options-query-deps.expected.json +1 -0
  84. package/corpus/lenient/lenient-alias-select-options-query-deps.json +1 -0
  85. package/corpus/lenient/lenient-bare-text-button-label.expected.json +1 -0
  86. package/corpus/lenient/lenient-bare-text-button-label.json +1 -0
  87. package/corpus/lenient/lenient-bare-text-callout.expected.json +1 -0
  88. package/corpus/lenient/lenient-bare-text-callout.json +1 -0
  89. package/corpus/lenient/lenient-bare-text-heading.expected.json +1 -0
  90. package/corpus/lenient/lenient-bare-text-heading.json +1 -0
  91. package/corpus/lenient/lenient-bare-text-markdown.expected.json +1 -0
  92. package/corpus/lenient/lenient-bare-text-markdown.json +1 -0
  93. package/corpus/lenient/lenient-binding-bound-wrapper.expected.json +1 -0
  94. package/corpus/lenient/lenient-binding-bound-wrapper.json +1 -0
  95. package/corpus/lenient/lenient-emphasis-cross-vocab.expected.json +1 -0
  96. package/corpus/lenient/lenient-emphasis-cross-vocab.json +1 -0
  97. package/corpus/lenient/lenient-fact-explicit-defaults.expected.json +1 -0
  98. package/corpus/lenient/lenient-fact-explicit-defaults.json +1 -0
  99. package/corpus/lenient/lenient-null-static-options.expected.json +1 -0
  100. package/corpus/lenient/lenient-null-static-options.json +1 -0
  101. package/corpus/lenient/lenient-opaque-static-markers.expected.json +1 -0
  102. package/corpus/lenient/lenient-opaque-static-markers.json +1 -0
  103. package/corpus/lenient/lenient-opaque-static-options.expected.json +1 -0
  104. package/corpus/lenient/lenient-opaque-static-options.json +1 -0
  105. package/corpus/lenient/lenient-opaque-static-series.expected.json +1 -0
  106. package/corpus/lenient/lenient-opaque-static-series.json +1 -0
  107. package/corpus/lenient/lenient-opaque-static-values.expected.json +1 -0
  108. package/corpus/lenient/lenient-opaque-static-values.json +1 -0
  109. package/corpus/lenient/lenient-shape-binding-scalar-fraction.expected.json +1 -0
  110. package/corpus/lenient/lenient-shape-binding-scalar-fraction.json +1 -0
  111. package/corpus/lenient/lenient-shape-grid-no-cols.expected.json +1 -0
  112. package/corpus/lenient/lenient-shape-grid-no-cols.json +1 -0
  113. package/corpus/lenient/lenient-shape-grid-template-no-cols.expected.json +1 -0
  114. package/corpus/lenient/lenient-shape-grid-template-no-cols.json +1 -0
  115. package/corpus/lenient/lenient-shape-options-bare-strings.expected.json +1 -0
  116. package/corpus/lenient/lenient-shape-options-bare-strings.json +1 -0
  117. package/corpus/lenient/lenient-shape-params-map.expected.json +1 -0
  118. package/corpus/lenient/lenient-shape-params-map.json +1 -0
  119. package/corpus/lenient/lenient-shape-segmented-orientation-omitted.expected.json +1 -0
  120. package/corpus/lenient/lenient-shape-segmented-orientation-omitted.json +1 -0
  121. package/corpus/lenient/lenient-shape-static-envelope-plain-scalars.expected.json +1 -0
  122. package/corpus/lenient/lenient-shape-static-envelope-plain-scalars.json +1 -0
  123. package/corpus/lenient/lenient-transform-bare-columns.expected.json +1 -0
  124. package/corpus/lenient/lenient-transform-bare-columns.json +1 -0
  125. package/corpus/lenient/lenient-transform-epoch-timestamps.expected.json +1 -0
  126. package/corpus/lenient/lenient-transform-epoch-timestamps.json +1 -0
  127. package/corpus/lenient/lenient-transform-expr-spellings.expected.json +1 -0
  128. package/corpus/lenient/lenient-transform-expr-spellings.json +1 -0
  129. package/corpus/lenient/lenient-transform-flat-contains.expected.json +1 -0
  130. package/corpus/lenient/lenient-transform-flat-contains.json +1 -0
  131. package/corpus/lenient/lenient-transform-flat-filter.expected.json +1 -0
  132. package/corpus/lenient/lenient-transform-flat-filter.json +1 -0
  133. package/corpus/lenient/lenient-transform-flat-or.expected.json +1 -0
  134. package/corpus/lenient/lenient-transform-flat-or.json +1 -0
  135. package/corpus/lenient/lenient-transform-flat-scalar-fn.expected.json +1 -0
  136. package/corpus/lenient/lenient-transform-flat-scalar-fn.json +1 -0
  137. package/corpus/lenient/lenient-transform-schemaless.expected.json +1 -0
  138. package/corpus/lenient/lenient-transform-schemaless.json +1 -0
  139. package/corpus/lenient/lenient-transform-step-aliases.expected.json +1 -0
  140. package/corpus/lenient/lenient-transform-step-aliases.json +1 -0
  141. package/corpus/lenient/lenient-transform-values-only-columns.expected.json +1 -0
  142. package/corpus/lenient/lenient-transform-values-only-columns.json +1 -0
  143. package/corpus/lenient/lenient-window-cumsum-legacy.expected.json +1 -0
  144. package/corpus/lenient/lenient-window-cumsum-legacy.json +1 -0
  145. package/corpus/manifest.json +2000 -0
  146. package/corpus/nodes/badge-1.json +1 -0
  147. package/corpus/nodes/boundary-1.json +1 -0
  148. package/corpus/nodes/btn-1.json +1 -0
  149. package/corpus/nodes/btn-copy-link.json +1 -0
  150. package/corpus/nodes/btn-invoke.json +1 -0
  151. package/corpus/nodes/btn-read-workbook.json +1 -0
  152. package/corpus/nodes/call-into.json +1 -0
  153. package/corpus/nodes/callout-1.json +1 -0
  154. package/corpus/nodes/card-1.json +1 -0
  155. package/corpus/nodes/chart-1.json +1 -0
  156. package/corpus/nodes/code-1.json +1 -0
  157. package/corpus/nodes/composite-root.json +1 -0
  158. package/corpus/nodes/controls-closure.json +1 -0
  159. package/corpus/nodes/controls-declarative.json +1 -0
  160. package/corpus/nodes/custom-1.json +1 -0
  161. package/corpus/nodes/custom-bounded-1.json +1 -0
  162. package/corpus/nodes/custom-bounded-advisory.json +1 -0
  163. package/corpus/nodes/dash-empty.json +1 -0
  164. package/corpus/nodes/discl-1.json +1 -0
  165. package/corpus/nodes/drawing-1.json +1 -0
  166. package/corpus/nodes/drawing-empty.json +1 -0
  167. package/corpus/nodes/fact-1.json +1 -0
  168. package/corpus/nodes/filterable-static-dashboard.json +1 -0
  169. package/corpus/nodes/filters-1.json +1 -0
  170. package/corpus/nodes/filters-declarative.json +1 -0
  171. package/corpus/nodes/filters-segmented.json +1 -0
  172. package/corpus/nodes/form-1.json +1 -0
  173. package/corpus/nodes/form-date.json +1 -0
  174. package/corpus/nodes/form-declarative-minimal.json +1 -0
  175. package/corpus/nodes/form-declarative.json +1 -0
  176. package/corpus/nodes/form-local-1.json +1 -0
  177. package/corpus/nodes/form-local-debounce.json +1 -0
  178. package/corpus/nodes/form-ranged.json +1 -0
  179. package/corpus/nodes/form-segmented.json +1 -0
  180. package/corpus/nodes/format-bindings.json +1 -0
  181. package/corpus/nodes/frag-decl-1.json +1 -0
  182. package/corpus/nodes/frag-decl-param.json +1 -0
  183. package/corpus/nodes/frag-ref-1.json +1 -0
  184. package/corpus/nodes/frag-ref-args.json +1 -0
  185. package/corpus/nodes/glayout-1.json +1 -0
  186. package/corpus/nodes/glayout-tpl-autofit.json +1 -0
  187. package/corpus/nodes/glayout-tpl-fixed.json +1 -0
  188. package/corpus/nodes/glayout-tpl-ratio.json +1 -0
  189. package/corpus/nodes/grid-1.json +1 -0
  190. package/corpus/nodes/grid-field-named.json +1 -0
  191. package/corpus/nodes/grid-transform-param.json +1 -0
  192. package/corpus/nodes/grid-transform.json +1 -0
  193. package/corpus/nodes/heading-1.json +1 -0
  194. package/corpus/nodes/image-1.json +1 -0
  195. package/corpus/nodes/link-1.json +1 -0
  196. package/corpus/nodes/list-1.json +1 -0
  197. package/corpus/nodes/lvr-1.json +1 -0
  198. package/corpus/nodes/map-1.json +1 -0
  199. package/corpus/nodes/markdown-1.json +1 -0
  200. package/corpus/nodes/master-detail-preselected.json +1 -0
  201. package/corpus/nodes/math-1.json +1 -0
  202. package/corpus/nodes/metric-1.json +1 -0
  203. package/corpus/nodes/metric-float-17sig.json +1 -0
  204. package/corpus/nodes/metric-float-bigint.json +1 -0
  205. package/corpus/nodes/metric-float-exp-neg.json +1 -0
  206. package/corpus/nodes/metric-float-exp-pos.json +1 -0
  207. package/corpus/nodes/metric-invoke.json +1 -0
  208. package/corpus/nodes/modal-1.json +1 -0
  209. package/corpus/nodes/mount-1.json +1 -0
  210. package/corpus/nodes/mount-2.json +1 -0
  211. package/corpus/nodes/multiselect-1.json +1 -0
  212. package/corpus/nodes/progress-1.json +1 -0
  213. package/corpus/nodes/query-dependson.json +1 -0
  214. package/corpus/nodes/scalar-transform-composition.json +1 -0
  215. package/corpus/nodes/scroll-1.json +1 -0
  216. package/corpus/nodes/select-1.json +1 -0
  217. package/corpus/nodes/skel-1.json +1 -0
  218. package/corpus/nodes/spark-1.json +1 -0
  219. package/corpus/nodes/split-1.json +1 -0
  220. package/corpus/nodes/stack-1.json +1 -0
  221. package/corpus/nodes/step-1.json +1 -0
  222. package/corpus/nodes/style-role-voice-1.json +1 -0
  223. package/corpus/nodes/summary-1.json +1 -0
  224. package/corpus/nodes/switch-1.json +1 -0
  225. package/corpus/nodes/table-1.json +1 -0
  226. package/corpus/nodes/tabs-1.json +1 -0
  227. package/corpus/nodes/tabs-explicit-1.json +1 -0
  228. package/corpus/nodes/toast-1.json +1 -0
  229. package/corpus/nodes/upload-1.json +1 -0
  230. package/corpus/ops/op-batch.json +1 -0
  231. package/corpus/ops/op-editnode.json +1 -0
  232. package/corpus/ops/op-insertchild.json +1 -0
  233. package/corpus/ops/op-movenode.json +1 -0
  234. package/corpus/ops/op-removenode.json +1 -0
  235. package/corpus/ops/op-reorderchildren.json +1 -0
  236. package/corpus/ops/op-replacebinding.json +1 -0
  237. package/corpus/ops/op-replaceroot.json +1 -0
  238. package/corpus/ops/op-updateprop-nested-badfield.json +1 -0
  239. package/corpus/ops/op-updateprop-nested-badindex.json +1 -0
  240. package/corpus/ops/op-updateprop-nested-column0-label.json +1 -0
  241. package/corpus/ops/op-updateprop-nested-column1-label.json +1 -0
  242. package/corpus/ops/op-updateprop-nested-field0-required.json +1 -0
  243. package/corpus/ops/op-updateprop-nested-field1-required.json +1 -0
  244. package/corpus/ops/op-updateprop-nested-malformed.json +1 -0
  245. package/corpus/ops/op-updateprop-nested-object-value.json +1 -0
  246. package/corpus/ops/op-updateprop-nested-yfield0.json +1 -0
  247. package/corpus/ops/op-updateprop-nested-yfield1.json +1 -0
  248. package/corpus/ops/op-updateprop.json +1 -0
  249. package/corpus/ops/op-updatestate.json +1 -0
  250. package/corpus/ops/op-updatestyle (phase 147 role=eyebrow + voice=structural).json +1 -0
  251. package/corpus/ops/op-updatestyle.json +1 -0
  252. package/corpus/reject/reject-emptynodeid.json +1 -0
  253. package/corpus/reject/reject-invalid-empty.json +0 -0
  254. package/corpus/reject/reject-invalid-garbage.json +1 -0
  255. package/corpus/reject/reject-invalid-truncated.json +1 -0
  256. package/corpus/reject/reject-missing-binding-type.json +1 -0
  257. package/corpus/reject/reject-missing-custom-moduleid.json +1 -0
  258. package/corpus/reject/reject-missing-markdown-text.json +1 -0
  259. package/corpus/reject/reject-missing-metric-value.json +1 -0
  260. package/corpus/reject/reject-missing-mount-scopeid.json +1 -0
  261. package/corpus/reject/reject-missing-node-id.json +1 -0
  262. package/corpus/reject/reject-missing-node-kind.json +1 -0
  263. package/corpus/reject/reject-missing-switch-cases.json +1 -0
  264. package/corpus/reject/reject-missing-switch-default.json +1 -0
  265. package/corpus/reject/reject-missing-switch-statekey.json +1 -0
  266. package/corpus/reject/reject-null-action-aitool-args.json +1 -0
  267. package/corpus/reject/reject-null-action-notify-payload.json +1 -0
  268. package/corpus/reject/reject-null-action-setstate-value.json +1 -0
  269. package/corpus/reject/reject-null-custom-prop.json +1 -0
  270. package/corpus/reject/reject-null-i18n-arg.json +1 -0
  271. package/corpus/reject/reject-null-updateprop-value.json +1 -0
  272. package/corpus/reject/reject-op-insertchild-empty-childid.json +1 -0
  273. package/corpus/reject/reject-op-insertchild-missing-parentid.json +1 -0
  274. package/corpus/reject/reject-op-missing-type.json +1 -0
  275. package/corpus/reject/reject-op-reorderchildren-wrongtype.json +1 -0
  276. package/corpus/reject/reject-op-unknown-yeet.json +1 -0
  277. package/corpus/reject/reject-op-updateprop-missing-path.json +1 -0
  278. package/corpus/reject/reject-unknown-binding.json +1 -0
  279. package/corpus/reject/reject-unknown-drawing-curve-command.json +1 -0
  280. package/corpus/reject/reject-unknown-drawing-shape.json +1 -0
  281. package/corpus/reject/reject-unknown-mount-direction.json +1 -0
  282. package/corpus/reject/reject-unknown-textsource.json +1 -0
  283. package/corpus/reject/reject-unknown-tone.json +1 -0
  284. package/corpus/reject/reject-wrongnodekind-sparkler.json +1 -0
  285. package/corpus/reject/reject-wrongnodekind-widget.json +1 -0
  286. package/corpus/reject/reject-wrongtype-dashboard-children.json +1 -0
  287. package/corpus/reject/reject-wrongtype-discriminator.json +1 -0
  288. package/corpus/reject/reject-wrongtype-heading-level.json +1 -0
  289. package/corpus/reject/reject-wrongtype-id.json +1 -0
  290. package/corpus/reject/reject-wrongtype-kind.json +1 -0
  291. package/corpus/reject/reject-wrongtype-style-tone.json +1 -0
  292. package/corpus/schema.json +4396 -0
  293. package/dist/cli.js +612 -0
  294. package/dist/cli.js.map +1 -0
  295. package/dist/index.cjs +579 -0
  296. package/dist/index.cjs.map +1 -0
  297. package/dist/index.d.cts +159 -0
  298. package/dist/index.d.ts +159 -0
  299. package/dist/index.js +570 -0
  300. package/dist/index.js.map +1 -0
  301. package/package.json +59 -0
package/dist/index.cjs ADDED
@@ -0,0 +1,579 @@
1
+ 'use strict';
2
+
3
+ var crypto = require('crypto');
4
+ var fs = require('fs');
5
+ var path = require('path');
6
+ var url = require('url');
7
+ var _2020_js = require('ajv/dist/2020.js');
8
+
9
+ // ../../node_modules/.pnpm/tsup@8.5.1_postcss@8.5.15_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js
10
+ var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href;
11
+ var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
12
+ var bundledRoot = () => (
13
+ // dist/index.js → ../corpus (tsup `shims: true` provides import.meta.url under CJS).
14
+ url.fileURLToPath(new URL("../corpus/", importMetaUrl))
15
+ );
16
+ var loadCorpus = (options) => {
17
+ const external = options?.corpusRoot;
18
+ const root = external ?? bundledRoot();
19
+ const read = (relPath) => fs.readFileSync(path.join(root, relPath), "utf8");
20
+ const manifestText = read("manifest.json");
21
+ const manifest = JSON.parse(manifestText);
22
+ const hash = crypto.createHash("sha256");
23
+ hash.update(manifestText);
24
+ hash.update(read(manifest.schema));
25
+ for (const f of manifest.fixtures) {
26
+ hash.update(read(f.inputFile));
27
+ if (f.expectedFile !== void 0 && f.expectedFile !== f.inputFile)
28
+ hash.update(read(f.expectedFile));
29
+ }
30
+ return {
31
+ root,
32
+ source: external === void 0 ? "bundled" : "external",
33
+ manifestVersion: manifest.version,
34
+ fixtures: manifest.fixtures,
35
+ digest: hash.digest("hex"),
36
+ schema: JSON.parse(read(manifest.schema)),
37
+ read
38
+ };
39
+ };
40
+
41
+ // src/report.ts
42
+ var computeVerdict = (legs) => {
43
+ if (legs.some((l) => l.status === "fail")) return "non-conformant";
44
+ if (legs.some((l) => l.mandatory && l.status === "skipped")) return "partially-conformant";
45
+ return "conformant";
46
+ };
47
+ var statusGlyph = { pass: "\u2713", fail: "\u2717", skipped: "\u2013" };
48
+ var formatReport = (report) => {
49
+ const lines = [];
50
+ const impl = report.implementation.version === void 0 ? report.implementation.name : `${report.implementation.name} ${report.implementation.version}`;
51
+ lines.push(`Fuaran wire-format conformance report`);
52
+ lines.push(` kit ${report.kit.name}@${report.kit.version}`);
53
+ lines.push(` implementation ${impl}`);
54
+ lines.push(
55
+ ` corpus v${report.corpus.manifestVersion} (${report.corpus.fixtureCount} fixtures, ${report.corpus.source})`
56
+ );
57
+ lines.push(` corpus digest sha256:${report.corpus.digest}`);
58
+ lines.push(` generated ${report.generatedAt}`);
59
+ lines.push("");
60
+ for (const leg of report.legs) {
61
+ const tier = leg.mandatory ? "mandatory" : "optional ";
62
+ const counts = leg.status === "skipped" ? leg.skipReason ?? "skipped" : `${leg.fixturesPassed}/${leg.fixturesTotal}`;
63
+ lines.push(` ${statusGlyph[leg.status]} ${leg.leg.padEnd(20)} ${tier} ${counts}`);
64
+ for (const f of leg.failures) {
65
+ lines.push(` ${f.fixtureId}: ${f.summary}`);
66
+ if (f.detail !== void 0)
67
+ for (const detailLine of f.detail.split("\n")) lines.push(` ${detailLine}`);
68
+ }
69
+ }
70
+ lines.push("");
71
+ lines.push(` verdict: ${report.verdict.toUpperCase()}`);
72
+ if (report.verdict === "partially-conformant") {
73
+ const skipped = report.legs.filter((l) => l.mandatory && l.status === "skipped").map((l) => l.leg);
74
+ lines.push(` (mandatory legs not attempted: ${skipped.join(", ")})`);
75
+ }
76
+ lines.push(
77
+ ` Certification is per corpus version \u2014 re-certify when the corpus advances (WIRE_FORMAT.md \xA711).`
78
+ );
79
+ return lines.join("\n");
80
+ };
81
+ var KIT_NAME = "@fuaran-ui/conformance";
82
+ var KIT_VERSION = "0.1.0";
83
+ var canon = (s) => s.replace(/\r?\n$/, "");
84
+ var firstDiff = (expected, actual) => {
85
+ const n = Math.min(expected.length, actual.length);
86
+ let i = 0;
87
+ while (i < n && expected[i] === actual[i]) i++;
88
+ const from = Math.max(0, i - 30);
89
+ const win = (s) => JSON.stringify(s.slice(from, i + 30));
90
+ return `byte offset ${i} (length canonical=${expected.length}, host=${actual.length})
91
+ canonical \u2026${win(expected)}\u2026
92
+ host \u2026${win(actual)}\u2026`;
93
+ };
94
+ var messageOf = (err) => err instanceof Error ? err.message : String(err);
95
+ var newAcc = () => ({ total: 0, passed: 0, failures: [] });
96
+ var toLeg = (leg, mandatory, acc, skipReason) => acc === void 0 ? {
97
+ leg,
98
+ mandatory,
99
+ status: "skipped",
100
+ ...skipReason === void 0 ? {} : { skipReason },
101
+ fixturesTotal: 0,
102
+ fixturesPassed: 0,
103
+ failures: []
104
+ } : {
105
+ leg,
106
+ mandatory,
107
+ status: acc.failures.length === 0 ? "pass" : "fail",
108
+ fixturesTotal: acc.total,
109
+ fixturesPassed: acc.passed,
110
+ failures: acc.failures
111
+ };
112
+ var runConformance = (adapter, options) => {
113
+ const corpus = loadCorpus(
114
+ options.corpusRoot === void 0 ? void 0 : { corpusRoot: options.corpusRoot }
115
+ );
116
+ const ajv = new _2020_js.Ajv2020({ strict: false, allErrors: true });
117
+ const schemaValid = ajv.compile(corpus.schema);
118
+ const accepts = (decoder) => corpus.fixtures.filter(
119
+ (f) => f.decoder === decoder && (f.kind === "node-round-trip" || f.kind === "op-round-trip")
120
+ );
121
+ const rejects = (decoder) => corpus.fixtures.filter((f) => f.decoder === decoder && f.kind === "reject");
122
+ const lenients = (decoder) => corpus.fixtures.filter((f) => f.decoder === decoder && f.kind === "lenient-accept");
123
+ const accs = /* @__PURE__ */ new Map();
124
+ const acc = (leg) => {
125
+ let a = accs.get(leg);
126
+ if (a === void 0) {
127
+ a = newAcc();
128
+ accs.set(leg, a);
129
+ }
130
+ return a;
131
+ };
132
+ const runSide = (decoder, decode, encode) => {
133
+ const decodeLeg = decoder === "node" ? "node-decode" : "op-decode";
134
+ const byteLeg = decoder === "node" ? "node-byte-identity" : "op-byte-identity";
135
+ const rejectLeg = decoder === "node" ? "node-reject" : "op-reject";
136
+ if (decode === void 0) return;
137
+ for (const f of accepts(decoder)) {
138
+ const input = corpus.read(f.inputFile);
139
+ const d = acc(decodeLeg);
140
+ d.total++;
141
+ let decoded;
142
+ try {
143
+ decoded = decode(input);
144
+ } catch (err) {
145
+ d.failures.push({
146
+ fixtureId: f.id,
147
+ summary: "decoder THREW on an accept fixture (a conformant decoder never throws)",
148
+ detail: messageOf(err)
149
+ });
150
+ continue;
151
+ }
152
+ if (!decoded.ok) {
153
+ d.failures.push({
154
+ fixtureId: f.id,
155
+ summary: "decoder rejected an accept fixture",
156
+ detail: `${decoded.error.code} at ${decoded.error.path}${decoded.error.message === void 0 ? "" : ` \u2014 ${decoded.error.message}`}`
157
+ });
158
+ continue;
159
+ }
160
+ d.passed++;
161
+ if (encode === void 0) continue;
162
+ const b = acc(byteLeg);
163
+ b.total++;
164
+ const expected = canon(corpus.read(f.expectedFile ?? f.inputFile));
165
+ let actual;
166
+ try {
167
+ actual = encode(decoded.value);
168
+ } catch (err) {
169
+ b.failures.push({
170
+ fixtureId: f.id,
171
+ summary: "encoder THREW re-encoding a decoded accept fixture",
172
+ detail: messageOf(err)
173
+ });
174
+ continue;
175
+ }
176
+ if (actual !== expected) {
177
+ b.failures.push({
178
+ fixtureId: f.id,
179
+ summary: "encoder output diverges from the canonical form",
180
+ detail: firstDiff(expected, actual)
181
+ });
182
+ continue;
183
+ }
184
+ b.passed++;
185
+ const s = acc("schema-validation");
186
+ s.total++;
187
+ if (schemaValid(JSON.parse(actual))) {
188
+ s.passed++;
189
+ } else {
190
+ s.failures.push({
191
+ fixtureId: f.id,
192
+ summary: "encoder output fails the canonical JSON Schema (schema.json)",
193
+ detail: (schemaValid.errors ?? []).map((e) => `${e.instancePath === "" ? "$" : e.instancePath} ${e.message ?? ""}`).join("\n")
194
+ });
195
+ }
196
+ }
197
+ if (encode !== void 0) {
198
+ for (const f of lenients(decoder)) {
199
+ const input = corpus.read(f.inputFile);
200
+ const l = acc("lenient-accept");
201
+ l.total++;
202
+ let decoded;
203
+ try {
204
+ decoded = decode(input);
205
+ } catch (err) {
206
+ l.failures.push({
207
+ fixtureId: f.id,
208
+ summary: "decoder THREW on a \xA716 shorthand (a conformant decoder accepts it)",
209
+ detail: messageOf(err)
210
+ });
211
+ continue;
212
+ }
213
+ if (!decoded.ok) {
214
+ l.failures.push({
215
+ fixtureId: f.id,
216
+ summary: "decoder rejected a \xA716 shorthand (MUST accept \u2014 WIRE_FORMAT \xA716)",
217
+ detail: `${decoded.error.code} at ${decoded.error.path}${decoded.error.message === void 0 ? "" : ` \u2014 ${decoded.error.message}`}`
218
+ });
219
+ continue;
220
+ }
221
+ let actual;
222
+ try {
223
+ actual = encode(decoded.value);
224
+ } catch (err) {
225
+ l.failures.push({
226
+ fixtureId: f.id,
227
+ summary: "encoder THREW re-encoding a decoded \xA716 shorthand",
228
+ detail: messageOf(err)
229
+ });
230
+ continue;
231
+ }
232
+ const expected = canon(corpus.read(f.expectedFile ?? f.inputFile));
233
+ if (actual !== expected) {
234
+ l.failures.push({
235
+ fixtureId: f.id,
236
+ summary: "shorthand normalisation diverges from the verbose canonical form",
237
+ detail: firstDiff(expected, actual)
238
+ });
239
+ continue;
240
+ }
241
+ l.passed++;
242
+ }
243
+ }
244
+ for (const f of rejects(decoder)) {
245
+ const input = corpus.read(f.inputFile);
246
+ const r = acc(rejectLeg);
247
+ r.total++;
248
+ let decoded;
249
+ try {
250
+ decoded = decode(input);
251
+ } catch (err) {
252
+ r.failures.push({
253
+ fixtureId: f.id,
254
+ summary: "decoder THREW on a reject fixture (must return a structured error)",
255
+ detail: messageOf(err)
256
+ });
257
+ continue;
258
+ }
259
+ if (decoded.ok) {
260
+ r.failures.push({
261
+ fixtureId: f.id,
262
+ summary: "decoder ACCEPTED a reject fixture",
263
+ detail: `expected ${f.expectedErrorCode} at ${f.expectedPath}`
264
+ });
265
+ continue;
266
+ }
267
+ if (decoded.error.code !== f.expectedErrorCode) {
268
+ r.failures.push({
269
+ fixtureId: f.id,
270
+ summary: "decode-error CODE diverges from the canonical error",
271
+ detail: `expected ${f.expectedErrorCode}, got ${decoded.error.code} (path ${decoded.error.path})`
272
+ });
273
+ continue;
274
+ }
275
+ if (!decoded.error.path.startsWith(f.expectedPath ?? "$")) {
276
+ r.failures.push({
277
+ fixtureId: f.id,
278
+ summary: "decode-error PATH diverges from the canonical error",
279
+ detail: `expected prefix ${f.expectedPath}, got ${decoded.error.path} (code ${decoded.error.code})`
280
+ });
281
+ continue;
282
+ }
283
+ r.passed++;
284
+ }
285
+ };
286
+ runSide("node", adapter.decodeNode, adapter.encodeNode);
287
+ runSide("op", adapter.decodeOp, adapter.encodeOp);
288
+ const negotiateEnvelope = adapter.negotiateEnvelope;
289
+ if (negotiateEnvelope !== void 0) {
290
+ const byKind = (kind) => corpus.fixtures.filter((f) => f.kind === kind);
291
+ for (const f of byKind("envelope-round-trip")) {
292
+ const a = acc("envelope-round-trip");
293
+ a.total++;
294
+ const input = corpus.read(f.inputFile);
295
+ let r;
296
+ try {
297
+ r = negotiateEnvelope(input);
298
+ } catch (err) {
299
+ a.failures.push({
300
+ fixtureId: f.id,
301
+ summary: "negotiateEnvelope THREW on a round-trip fixture (must return a structured result)",
302
+ detail: messageOf(err)
303
+ });
304
+ continue;
305
+ }
306
+ if (!r.ok) {
307
+ a.failures.push({
308
+ fixtureId: f.id,
309
+ summary: "negotiateEnvelope refused a round-trip fixture (Current/Behind must be tolerated)",
310
+ detail: `${r.error.code} at ${r.error.path}`
311
+ });
312
+ continue;
313
+ }
314
+ const expected = canon(corpus.read(f.expectedFile ?? f.inputFile));
315
+ if (typeof r.value !== "string" || r.value !== expected) {
316
+ a.failures.push({
317
+ fixtureId: f.id,
318
+ summary: "envelope re-encode diverges from the canonical form (must-ignore-but-preserve)",
319
+ detail: typeof r.value === "string" ? firstDiff(expected, r.value) : "value was not a string"
320
+ });
321
+ continue;
322
+ }
323
+ a.passed++;
324
+ }
325
+ for (const f of byKind("envelope-reject")) {
326
+ const a = acc("envelope-reject");
327
+ a.total++;
328
+ const input = corpus.read(f.inputFile);
329
+ let r;
330
+ try {
331
+ r = negotiateEnvelope(input);
332
+ } catch (err) {
333
+ a.failures.push({
334
+ fixtureId: f.id,
335
+ summary: "negotiateEnvelope THREW on a reject fixture (must return a structured error)",
336
+ detail: messageOf(err)
337
+ });
338
+ continue;
339
+ }
340
+ if (r.ok) {
341
+ a.failures.push({
342
+ fixtureId: f.id,
343
+ summary: "negotiateEnvelope ACCEPTED a Foreign artifact (must hard-refuse)",
344
+ detail: `expected ${f.expectedErrorCode} at ${f.expectedPath}`
345
+ });
346
+ continue;
347
+ }
348
+ if (r.error.code !== f.expectedErrorCode) {
349
+ a.failures.push({
350
+ fixtureId: f.id,
351
+ summary: "refusal CODE diverges from the canonical error",
352
+ detail: `expected ${f.expectedErrorCode}, got ${r.error.code} (path ${r.error.path})`
353
+ });
354
+ continue;
355
+ }
356
+ if (!r.error.path.startsWith(f.expectedPath ?? "$")) {
357
+ a.failures.push({
358
+ fixtureId: f.id,
359
+ summary: "refusal PATH diverges from the canonical error",
360
+ detail: `expected prefix ${f.expectedPath}, got ${r.error.path}`
361
+ });
362
+ continue;
363
+ }
364
+ a.passed++;
365
+ }
366
+ }
367
+ const roundTripElicitation = adapter.roundTripElicitation;
368
+ if (roundTripElicitation !== void 0) {
369
+ const elDecoder = (f) => f.decoder === "elicitation-outcome" ? "elicitation-outcome" : "elicitation";
370
+ for (const f of corpus.fixtures.filter((x) => x.kind === "elicitation-round-trip")) {
371
+ const a = acc("elicitation-round-trip");
372
+ a.total++;
373
+ const input = corpus.read(f.inputFile);
374
+ let r;
375
+ try {
376
+ r = roundTripElicitation(elDecoder(f), input);
377
+ } catch (err) {
378
+ a.failures.push({
379
+ fixtureId: f.id,
380
+ summary: "roundTripElicitation THREW on a round-trip fixture (must return a structured result)",
381
+ detail: messageOf(err)
382
+ });
383
+ continue;
384
+ }
385
+ if (!r.ok) {
386
+ a.failures.push({
387
+ fixtureId: f.id,
388
+ summary: "roundTripElicitation refused a round-trip fixture",
389
+ detail: `${r.error.code} at ${r.error.path}`
390
+ });
391
+ continue;
392
+ }
393
+ const expected = canon(corpus.read(f.expectedFile ?? f.inputFile));
394
+ if (typeof r.value !== "string" || r.value !== expected) {
395
+ a.failures.push({
396
+ fixtureId: f.id,
397
+ summary: "elicitation re-encode diverges from the canonical form",
398
+ detail: typeof r.value === "string" ? firstDiff(expected, r.value) : "value was not a string"
399
+ });
400
+ continue;
401
+ }
402
+ a.passed++;
403
+ }
404
+ for (const f of corpus.fixtures.filter((x) => x.kind === "elicitation-reject")) {
405
+ const a = acc("elicitation-reject");
406
+ a.total++;
407
+ const input = corpus.read(f.inputFile);
408
+ let r;
409
+ try {
410
+ r = roundTripElicitation(elDecoder(f), input);
411
+ } catch (err) {
412
+ a.failures.push({
413
+ fixtureId: f.id,
414
+ summary: "roundTripElicitation THREW on a reject fixture (must return a structured error)",
415
+ detail: messageOf(err)
416
+ });
417
+ continue;
418
+ }
419
+ if (r.ok) {
420
+ a.failures.push({
421
+ fixtureId: f.id,
422
+ summary: "roundTripElicitation ACCEPTED a reject fixture",
423
+ detail: `expected ${f.expectedErrorCode} at ${f.expectedPath}`
424
+ });
425
+ continue;
426
+ }
427
+ if (r.error.code !== f.expectedErrorCode) {
428
+ a.failures.push({
429
+ fixtureId: f.id,
430
+ summary: "refusal CODE diverges from the canonical error",
431
+ detail: `expected ${f.expectedErrorCode}, got ${r.error.code} (path ${r.error.path})`
432
+ });
433
+ continue;
434
+ }
435
+ if (!r.error.path.startsWith(f.expectedPath ?? "$")) {
436
+ a.failures.push({
437
+ fixtureId: f.id,
438
+ summary: "refusal PATH diverges from the canonical error",
439
+ detail: `expected prefix ${f.expectedPath}, got ${r.error.path}`
440
+ });
441
+ continue;
442
+ }
443
+ a.passed++;
444
+ }
445
+ }
446
+ const validateAnswerDocument = adapter.validateAnswerDocument;
447
+ if (validateAnswerDocument !== void 0) {
448
+ for (const f of corpus.fixtures.filter(
449
+ (x) => x.kind === "elicitation-answer-accept" || x.kind === "elicitation-answer-reject"
450
+ )) {
451
+ const a = acc("elicitation-answer");
452
+ a.total++;
453
+ const input = corpus.read(f.inputFile);
454
+ let r;
455
+ try {
456
+ r = validateAnswerDocument(input);
457
+ } catch (err) {
458
+ a.failures.push({
459
+ fixtureId: f.id,
460
+ summary: "validateAnswerDocument THREW (must return a structured result)",
461
+ detail: messageOf(err)
462
+ });
463
+ continue;
464
+ }
465
+ if (f.kind === "elicitation-answer-accept") {
466
+ if (!r.ok) {
467
+ a.failures.push({
468
+ fixtureId: f.id,
469
+ summary: "validateAnswerDocument refused a conforming answer",
470
+ detail: `${r.error.code} at ${r.error.path}`
471
+ });
472
+ continue;
473
+ }
474
+ a.passed++;
475
+ continue;
476
+ }
477
+ if (r.ok) {
478
+ a.failures.push({
479
+ fixtureId: f.id,
480
+ summary: "validateAnswerDocument ACCEPTED a nonconforming answer",
481
+ detail: `expected ${f.expectedErrorCode} at ${f.expectedPath}`
482
+ });
483
+ continue;
484
+ }
485
+ if (r.error.code !== f.expectedErrorCode) {
486
+ a.failures.push({
487
+ fixtureId: f.id,
488
+ summary: "refusal CODE diverges from the canonical error",
489
+ detail: `expected ${f.expectedErrorCode}, got ${r.error.code} (path ${r.error.path})`
490
+ });
491
+ continue;
492
+ }
493
+ if (!r.error.path.startsWith(f.expectedPath ?? "$")) {
494
+ a.failures.push({
495
+ fixtureId: f.id,
496
+ summary: "refusal PATH diverges from the canonical error",
497
+ detail: `expected prefix ${f.expectedPath}, got ${r.error.path}`
498
+ });
499
+ continue;
500
+ }
501
+ a.passed++;
502
+ }
503
+ }
504
+ const need = (hooks) => `adapter does not provide ${hooks.join(" + ")}`;
505
+ const hasSchemaInput = adapter.encodeNode !== void 0 || adapter.encodeOp !== void 0;
506
+ const legs = [
507
+ toLeg("node-decode", true, accs.get("node-decode"), need(["decodeNode"])),
508
+ toLeg(
509
+ "node-byte-identity",
510
+ true,
511
+ accs.get("node-byte-identity"),
512
+ need(["decodeNode", "encodeNode"])
513
+ ),
514
+ toLeg("node-reject", true, accs.get("node-reject"), need(["decodeNode"])),
515
+ toLeg("op-decode", true, accs.get("op-decode"), need(["decodeOp"])),
516
+ toLeg("op-byte-identity", true, accs.get("op-byte-identity"), need(["decodeOp", "encodeOp"])),
517
+ toLeg("op-reject", true, accs.get("op-reject"), need(["decodeOp"])),
518
+ toLeg(
519
+ "lenient-accept",
520
+ true,
521
+ accs.get("lenient-accept"),
522
+ need(["a decode + encode hook pair (node and/or op)"])
523
+ ),
524
+ toLeg(
525
+ "envelope-round-trip",
526
+ true,
527
+ accs.get("envelope-round-trip"),
528
+ need(["negotiateEnvelope"])
529
+ ),
530
+ toLeg("envelope-reject", true, accs.get("envelope-reject"), need(["negotiateEnvelope"])),
531
+ toLeg(
532
+ "elicitation-round-trip",
533
+ true,
534
+ accs.get("elicitation-round-trip"),
535
+ need(["roundTripElicitation"])
536
+ ),
537
+ toLeg(
538
+ "elicitation-reject",
539
+ true,
540
+ accs.get("elicitation-reject"),
541
+ need(["roundTripElicitation"])
542
+ ),
543
+ toLeg(
544
+ "elicitation-answer",
545
+ true,
546
+ accs.get("elicitation-answer"),
547
+ need(["validateAnswerDocument"])
548
+ ),
549
+ toLeg(
550
+ "schema-validation",
551
+ true,
552
+ accs.get("schema-validation"),
553
+ hasSchemaInput ? need(["a decode hook for the encode hooks provided"]) : need(["encodeNode and/or encodeOp"])
554
+ ),
555
+ toLeg("apply", false, void 0, "corpus v1 ships no apply fixtures \u2014 leg reserved")
556
+ ];
557
+ return {
558
+ kit: { name: KIT_NAME, version: KIT_VERSION },
559
+ implementation: options.implementation,
560
+ corpus: {
561
+ manifestVersion: corpus.manifestVersion,
562
+ digest: corpus.digest,
563
+ fixtureCount: corpus.fixtures.length,
564
+ source: corpus.source
565
+ },
566
+ legs,
567
+ verdict: computeVerdict(legs),
568
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString()
569
+ };
570
+ };
571
+
572
+ exports.KIT_NAME = KIT_NAME;
573
+ exports.KIT_VERSION = KIT_VERSION;
574
+ exports.computeVerdict = computeVerdict;
575
+ exports.formatReport = formatReport;
576
+ exports.loadCorpus = loadCorpus;
577
+ exports.runConformance = runConformance;
578
+ //# sourceMappingURL=index.cjs.map
579
+ //# sourceMappingURL=index.cjs.map