@variance-authority/core 0.14.0 → 0.17.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 (147) hide show
  1. package/CHANGELOG.md +878 -0
  2. package/README.md +38 -17
  3. package/dist/attribute/component-hash.d.ts +14 -5
  4. package/dist/attribute/component-hash.js +14 -5
  5. package/dist/attribute/component-hash.js.map +1 -1
  6. package/dist/attribute/composition.d.ts +16 -0
  7. package/dist/attribute/composition.js +4 -6
  8. package/dist/attribute/composition.js.map +1 -1
  9. package/dist/attribute/index.d.ts +3 -1
  10. package/dist/attribute/index.js +2 -1
  11. package/dist/attribute/index.js.map +1 -1
  12. package/dist/attribute/movement.d.ts +1 -1
  13. package/dist/attribute/movement.js.map +1 -1
  14. package/dist/attribute/region.d.ts +47 -4
  15. package/dist/attribute/region.js +91 -22
  16. package/dist/attribute/region.js.map +1 -1
  17. package/dist/attribute/source.d.ts +10 -24
  18. package/dist/attribute/source.js +4 -38
  19. package/dist/attribute/source.js.map +1 -1
  20. package/dist/attribute/subject-pieces.d.ts +37 -0
  21. package/dist/attribute/subject-pieces.js +122 -0
  22. package/dist/attribute/subject-pieces.js.map +1 -0
  23. package/dist/compare/band.d.ts +8 -2
  24. package/dist/compare/band.js +7 -2
  25. package/dist/compare/band.js.map +1 -1
  26. package/dist/compare/diff/attribution.js +6 -4
  27. package/dist/compare/diff/attribution.js.map +1 -1
  28. package/dist/compare/diff/compare-nodes.js +9 -1
  29. package/dist/compare/diff/compare-nodes.js.map +1 -1
  30. package/dist/compare/diff/match.d.ts +2 -1
  31. package/dist/compare/diff/match.js +52 -0
  32. package/dist/compare/diff/match.js.map +1 -1
  33. package/dist/compare/parting.js +6 -4
  34. package/dist/compare/parting.js.map +1 -1
  35. package/dist/format/capture.d.ts +25 -0
  36. package/dist/format/capture.js +6 -0
  37. package/dist/format/capture.js.map +1 -1
  38. package/dist/format/document.d.ts +28 -5
  39. package/dist/format/document.js +8 -2
  40. package/dist/format/document.js.map +1 -1
  41. package/dist/format/drive.d.ts +191 -0
  42. package/dist/format/drive.js +343 -0
  43. package/dist/format/drive.js.map +1 -0
  44. package/dist/format/environment.d.ts +20 -2
  45. package/dist/format/environment.js +7 -0
  46. package/dist/format/environment.js.map +1 -1
  47. package/dist/format/index.d.ts +13 -3
  48. package/dist/format/index.js +8 -2
  49. package/dist/format/index.js.map +1 -1
  50. package/dist/format/intervention.d.ts +79 -0
  51. package/dist/format/intervention.js.map +1 -1
  52. package/dist/format/origin.d.ts +32 -0
  53. package/dist/format/origin.js +54 -0
  54. package/dist/format/origin.js.map +1 -0
  55. package/dist/format/packed.d.ts +32 -0
  56. package/dist/format/packed.js +23 -0
  57. package/dist/format/packed.js.map +1 -0
  58. package/dist/format/shape.d.ts +31 -0
  59. package/dist/format/shape.js +269 -0
  60. package/dist/format/shape.js.map +1 -0
  61. package/dist/format/snapshot.d.ts +2 -0
  62. package/dist/format/snapshot.js.map +1 -1
  63. package/dist/format/stabilize.d.ts +35 -6
  64. package/dist/format/stabilize.js +58 -103
  65. package/dist/format/stabilize.js.map +1 -1
  66. package/dist/format/steps.d.ts +44 -0
  67. package/dist/format/steps.js +67 -0
  68. package/dist/format/steps.js.map +1 -0
  69. package/dist/format/waits.d.ts +12 -0
  70. package/dist/format/waits.js +156 -0
  71. package/dist/format/waits.js.map +1 -0
  72. package/dist/format/width.d.ts +35 -0
  73. package/dist/format/width.js +38 -0
  74. package/dist/format/width.js.map +1 -0
  75. package/dist/index.d.ts +2 -1
  76. package/dist/index.js +2 -1
  77. package/dist/index.js.map +1 -1
  78. package/dist/judge/docket.js +1 -0
  79. package/dist/judge/docket.js.map +1 -1
  80. package/dist/judge/index.d.ts +1 -0
  81. package/dist/judge/index.js +1 -0
  82. package/dist/judge/index.js.map +1 -1
  83. package/dist/judge/restamp.d.ts +20 -0
  84. package/dist/judge/restamp.js +20 -0
  85. package/dist/judge/restamp.js.map +1 -0
  86. package/dist/nearest.d.ts +46 -0
  87. package/dist/nearest.js +102 -0
  88. package/dist/nearest.js.map +1 -0
  89. package/dist/relate/before.d.ts +19 -24
  90. package/dist/relate/before.js +37 -39
  91. package/dist/relate/before.js.map +1 -1
  92. package/dist/relate/beyond.d.ts +59 -0
  93. package/dist/relate/beyond.js +98 -0
  94. package/dist/relate/beyond.js.map +1 -0
  95. package/dist/relate/carried.d.ts +122 -0
  96. package/dist/relate/carried.js +279 -0
  97. package/dist/relate/carried.js.map +1 -0
  98. package/dist/relate/graph.d.ts +14 -12
  99. package/dist/relate/graph.js +12 -11
  100. package/dist/relate/graph.js.map +1 -1
  101. package/dist/relate/index.d.ts +3 -1
  102. package/dist/relate/index.js +3 -1
  103. package/dist/relate/index.js.map +1 -1
  104. package/dist/relate/merkle.js +2 -11
  105. package/dist/relate/merkle.js.map +1 -1
  106. package/dist/relate/narrow.d.ts +2 -2
  107. package/dist/relate/narrow.js +26 -73
  108. package/dist/relate/narrow.js.map +1 -1
  109. package/dist/relate/reach.d.ts +6 -6
  110. package/dist/relate/reach.js +10 -53
  111. package/dist/relate/reach.js.map +1 -1
  112. package/dist/relate/records.d.ts +6 -5
  113. package/dist/relate/records.js +3 -2
  114. package/dist/relate/records.js.map +1 -1
  115. package/dist/rules/index.d.ts +3 -3
  116. package/dist/rules/index.js +3 -3
  117. package/dist/rules/index.js.map +1 -1
  118. package/dist/rules/normalize/cascade.d.ts +12 -0
  119. package/dist/rules/normalize/cascade.js +34 -16
  120. package/dist/rules/normalize/cascade.js.map +1 -1
  121. package/dist/rules/normalize/fixture.d.ts +3 -0
  122. package/dist/rules/normalize/fixture.js +3 -1
  123. package/dist/rules/normalize/fixture.js.map +1 -1
  124. package/dist/rules/normalize/index.d.ts +2 -2
  125. package/dist/rules/normalize/index.js +9 -2
  126. package/dist/rules/normalize/index.js.map +1 -1
  127. package/dist/rules/normalize/value.d.ts +13 -2
  128. package/dist/rules/normalize/value.js +42 -8
  129. package/dist/rules/normalize/value.js.map +1 -1
  130. package/dist/rules/normalize/wrapper.js +5 -0
  131. package/dist/rules/normalize/wrapper.js.map +1 -1
  132. package/dist/rules/ruleset.d.ts +24 -4
  133. package/dist/rules/ruleset.js +24 -4
  134. package/dist/rules/ruleset.js.map +1 -1
  135. package/dist/segment/index.d.ts +20 -4
  136. package/dist/segment/index.js +28 -7
  137. package/dist/segment/index.js.map +1 -1
  138. package/dist/shard/index.d.ts +11 -0
  139. package/dist/shard/index.js +11 -0
  140. package/dist/shard/index.js.map +1 -0
  141. package/dist/shard/placement.d.ts +119 -0
  142. package/dist/shard/placement.js +156 -0
  143. package/dist/shard/placement.js.map +1 -0
  144. package/dist/share/index.d.ts +1 -1
  145. package/dist/share/index.js +1 -1
  146. package/dist/share/index.js.map +1 -1
  147. package/package.json +8 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drive.js","sourceRoot":"","sources":["../../src/format/drive.ts"],"names":[],"mappings":"AAQA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,0BAA0B,CAAC;AAEtD,MAAM,OAAO,GAAG,qEAAqE,CAAC;AAEtF;;;;;;;GAOG;AACH,MAAM,UAAU,SAAS,CAAC,GAAW;IACnC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;IAC9D,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CACb,qFAAqF;YACnF,oCAAoC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAC5D,CAAC;IACJ,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,aAAa,GAAiB;IACzC,EAAE,EAAE,gBAAgB;IACpB,KAAK,EAAE,OAAO;IACd,KAAK,EAAE,QAAQ;IACf,OAAO,EAAE,mBAAmB;IAC5B,OAAO,EAAE,2GAA2G;IACpH,MAAM,EAAE,EAAE,aAAa,EAAE,QAAQ,EAAE;CACpC,CAAC;AAEF,MAAM,QAAQ,GAAG,cAAc,CAAC;AAEhC;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,UAAU,aAAa,CAAC,GAAW;IACvC,MAAM,EAAE,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;IAC1B,OAAO;QACL,EAAE,EAAE,EAAE,KAAK,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,QAAQ,IAAI,IAAI,IAAI,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,EAAE;QAC1F,KAAK,EAAE,MAAM;QACb,KAAK,EAAE,QAAQ;QACf,OAAO,EAAE,OAAO;QAChB,OAAO,EAAE,mFAAmF;QAC5F,MAAM,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,EAAE;KAC1B,CAAC;AACJ,CAAC;AAED,sEAAsE;AACtE,MAAM,CAAC,MAAM,WAAW,GAAiB,aAAa,CAAC,WAAW,CAAC,CAAC;AAEpE;;;;;;GAMG;AACH,MAAM,UAAU,SAAS,CAAC,EAAU;IAClC,OAAO,EAAE,CAAC,UAAU,CAAC,GAAG,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAClG,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,UAAU,CAAC,MAAc;IACvC,4EAA4E;IAC5E,4EAA4E;IAC5E,4CAA4C;IAC5C,MAAM,IAAI,GAAG,IAAI,CAAC;IAClB,SAAS,MAAM,CAAC,GAAG,IAAe;QAChC,IAAI,IAAI,MAAM,KAAK,SAAS;YAAE,OAAO,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,CAAC;QACjE,OAAO,OAAO,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,MAAM,CAAC,CAAC;IAClF,CAAC;IACD,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACpC,MAAM,CAAC,gBAAgB,CAAC,MAAM,EAAE;QAC9B,SAAS,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,SAAS,EAAE;QACpC,IAAI,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE;QACvB,MAAM,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE;QACpB,GAAG,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE;KACjE,CAAC,CAAC;IACH,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,SAAS,EAAE,aAAa,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5G,MAAM,CAAC,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC;IAEjG,MAAM,MAAM,GAAG,IAAI,CAAC,cAAc,CAAC,SAAS,CAAC;IAC7C,MAAM,UAAU,GAAG,MAAM,CAAC,wBAAwB,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,GAAG,CAAC;IAC1E,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;QAC7B,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,QAAQ,EAAE;YACtC,YAAY,EAAE,IAAI;YAClB,GAAG;gBACD,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,IAAI,CAAqC,CAAC;gBACxE,OAAO,CAAC,IAAoB,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YAC7E,CAAC;SACF,CAAC,CAAC;IACL,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,CAAC,aAAa,CAAC;IACrC,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,eAAe,EAAE;QAC7C,YAAY,EAAE,IAAI;QAClB,QAAQ,EAAE,IAAI;QACd,KAAK,CAA4B,IAAoB;YACnD,OAAO,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAChE,CAAC;KACF,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,OAAO,GAAiB;IACnC,EAAE,EAAE,UAAU;IACd,KAAK,EAAE,OAAO;IACd,KAAK,EAAE,QAAQ;IACf,OAAO,EAAE,SAAS;IAClB,OAAO,EAAE,2EAA2E;IACpF,MAAM,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE;CAC/B,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,OAAO,GAAiB;IACnC,EAAE,EAAE,UAAU;IACd,KAAK,EAAE,OAAO;IACd,KAAK,EAAE,QAAQ;IACf,OAAO,EAAE,OAAO;IAChB,OAAO,EAAE,yEAAyE;IAClF,4EAA4E;IAC5E,6EAA6E;IAC7E,8EAA8E;IAC9E,8DAA8D;IAC9D,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;QACvB,MAAM,MAAM,CAAC,QAAQ,CACnB,GAAG,EAAE,CACH,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;YAC5B,MAAM,IAAI,GAAG,UAAoC,CAAC;YAClD,MAAM,OAAO,GAAG,GAAS,EAAE;gBACzB,IAAI,OAAO,GAAqC,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC;gBAC5E,OAAO,OAAO,EAAE,UAAU,EAAE,aAAa,IAAI,IAAI;oBAAE,OAAO,GAAG,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC;gBAC9F,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC;YACpB,CAAC,CAAC;YACF,OAAO,EAAE,CAAC;YACV,IAAI,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,aAAa,CAAC,KAAK,IAAI;gBAAE,OAAO,OAAO,EAAE,CAAC;YAC1E,IAAI,CAAC,qBAAqB,CAAC,GAAG,EAAE;gBAC9B,OAAO,EAAE,CAAC;gBACV,OAAO,EAAE,CAAC;YACZ,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CACL,CAAC;IACJ,CAAC;CACF,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,SAAS,GAA4B,CAAC,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,WAAW,CAAC,CAAC;AAEjG,MAAM,MAAM,GAAG,wBAAwB,CAAC;AAExC;;;;;;;;;;GAUG;AACH,MAAM,UAAU,UAAU,CACxB,MAA+B,EAC/B,QAA2C;IAE3C,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,YAAY,EAAE,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,aAAa,KAAK,SAAS,CAAC,CAAC;IAC/F,MAAM,QAAQ,GAAG;QACf,GAAG,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAO,CAAC,aAAc,EAAE,CAAC;QAC5E,GAAG,QAAQ;KACZ,CAAC;IACF,OAAO,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC;AACnE,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,MAA+B,EAC/B,MAAoB,EACpB,QAA2C;IAE3C,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,KAAK,MAAM,YAAY,IAAI,MAAM,EAAE,CAAC;QAClC,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,CAAC;QACnC,IAAI,MAAM,EAAE,OAAO,KAAK,SAAS,EAAE,CAAC;YAClC,MAAM,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;YAChC,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;QAChC,CAAC;aAAM,IAAI,MAAM,EAAE,aAAa,KAAK,SAAS,EAAE,CAAC;YAC/C,MAAM,UAAU,GAAG,QAAQ,CAAC,QAAQ,EAAE,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,aAAa,CAAC,CAAC;YACxE,MAAM,MAAM,CAAC,YAAY,CAAC,EAAE,aAAa,EAAE,UAAU,EAAE,CAAC,CAAC;YACzD,0EAA0E;YAC1E,kEAAkE;YAClE,IAAI,UAAU,KAAK,MAAM,CAAC,aAAa;gBAAE,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;QACzE,CAAC;aAAM,IAAI,MAAM,EAAE,KAAK,KAAK,SAAS,EAAE,CAAC;YACvC,MAAM,MAAM,CAAC,aAAa,CAAC,UAAU,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YACxD,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,QAAQ,CAAC,KAAa;IAC7B,IAAI,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,eAAe;QAAE,OAAO,KAAK,CAAC;IAClE,MAAM,IAAI,KAAK,CACb,KAAK,MAAM,qBAAqB,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,8BAA8B;QACjF,qCAAqC,CACxC,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,MAA+B,EAC/B,MAAmC;IAEnC,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,KAAK,MAAM,YAAY,IAAI,MAAM,EAAE,CAAC;QAClC,IAAI,YAAY,CAAC,MAAM,EAAE,OAAO,KAAK,SAAS,EAAE,CAAC;YAC/C,MAAM,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;YAChC,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,SAAS,CACvB,MAA+B,EAC/B,MAAyB;IAEzB,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACtG,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC,YAAY,EAAE,EAAE;QACrC,IAAI,YAAY,CAAC,MAAM,KAAK,SAAS,IAAI,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;YAAE,OAAO,CAAC,YAAY,CAAC,CAAC;QACjG,OAAO,YAAY,CAAC,MAAM,CAAC,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACvF,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import type {\n DriverTarget,\n Intervention,\n MotionPreference,\n PageFocusable,\n PageGlobals,\n} from './intervention.js';\n\n/**\n * The tricks a stylesheet cannot reach, and the one call each makes of a driver.\n *\n * Every other trick in [`stabilize.ts`](./stabilize.ts) is CSS, a screenshot\n * option or a script the page runs on itself. Three causes of a page that reads\n * two ways sit outside the document:\n *\n * - **the motion preference** — a page that honours `prefers-reduced-motion`\n * runs its script animation only when the browser says motion is welcome, and\n * a stylesheet cannot reach a `requestAnimationFrame` loop;\n * - **the clock** — a page that prints `new Date()` prints a new value every\n * run, and the date is read by the page's first script, before any stylesheet\n * or settle step exists, so it is replaced by a script the browser runs before\n * the page's own;\n * - **the pointer** — a hover ends when the pointer leaves, and nothing in the\n * page can move it.\n *\n * So each is declared as a {@link DriverState} and applied here, through the\n * structural {@link DriverTarget} a Playwright `Page` already is. A host that\n * owns the browser calls {@link driveBeforeLoad} before its first navigation\n * and hands the page the ids it applied. A host that shares the page with\n * somebody who moves the pointer, such as a test, also calls\n * {@link driveBeforeRead} before each subject is read. A host that does not — a page agent, a test runner's own\n * browser — drives nothing, and {@link forDriver} keeps those tricks out of its\n * recorded recipe, so the absence shows in the reading's ids and digest the way\n * a trick filtered by `needs` does.\n */\n\n/**\n * The instant a fixed clock reads unless a project names another.\n *\n * Noon UTC, so it is the same calendar date in every zone from UTC−11 to\n * UTC+11: a page that prints today's date prints one date whichever zone the\n * browser runs in. A date in the past, so a page that compares against \"now\"\n * to show something as expired does so on every run or on none.\n */\nexport const DEFAULT_NOW = '2024-06-15T12:00:00.000Z';\n\nconst INSTANT = /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}(:\\d{2}(\\.\\d+)?)?(Z|[+-]\\d{2}:\\d{2})$/;\n\n/**\n * The milliseconds an ISO 8601 instant names, or an error that says what an\n * instant is.\n *\n * The zone is required. `2024-06-15T12:00` with no zone is read in whatever\n * zone the machine runs in, so two machines would fix two clocks — the\n * variance this trick exists to remove.\n */\nexport function instantOf(now: string): number {\n const time = INSTANT.test(now) ? Date.parse(now) : Number.NaN;\n if (Number.isNaN(time)) {\n throw new Error(\n '`now` must be an ISO 8601 instant with its zone, such as \"2024-06-15T12:00:00Z\" or ' +\n `\"2024-06-15T14:00:00+02:00\", not ${JSON.stringify(now)}`,\n );\n }\n return time;\n}\n\n/**\n * `prefers-reduced-motion: reduce`, the switch a well-built page already reads.\n *\n * It reaches what `pin-animations` cannot: a script animation, which a\n * stylesheet has no handle on. It reaches only a page that honours the\n * preference, and that is the cost: the page is read as a user who asked for\n * less motion sees it, which can differ from what everybody else sees.\n */\nexport const reducedMotion: Intervention = {\n id: 'reduced-motion',\n trick: 'reset',\n needs: 'layout',\n governs: 'motion-preference',\n because: 'the browser reports prefers-reduced-motion: reduce, so a page that honours it stops its script animations',\n driver: { reducedMotion: 'reduce' },\n};\n\nconst CLOCK_ID = 'freeze-clock';\n\n/**\n * `Date` reads one instant; timers, animation frames and `performance` keep\n * running.\n *\n * An init script, {@link freezeDate}, run in every frame before the page's own\n * first script. It replaces `Date` and nothing else, because the thing that\n * prints two ways is the date a page shows, and the things that make a page\n * settle are timers and frames. Playwright's `clock.setFixedTime` was measured\n * against it and rejected: it installs a fake for `setTimeout`,\n * `requestAnimationFrame` and `performance.now` as well, and a frame's\n * timestamp no longer agrees with `document.timeline`, which drives every CSS\n * and Web Animations frame. Installing no clock also leaves `page.clock` to the\n * test that wants one.\n *\n * The id carries the instant when it is not {@link DEFAULT_NOW}, as\n * `freeze-clock@2031-01-02T03:04:05.000Z`, so the instant is in the recipe's\n * digest and so in the environment key: a baseline read at one instant and a\n * run read at another are compared, and the instant is named as the cause of\n * whatever changed.\n *\n * The cost: code that measures elapsed time with `Date.now()`, such as a\n * debounce, sees no time pass and never fires. `performance.now()` keeps\n * counting.\n */\nexport function freezeClockAt(now: string): Intervention {\n const at = instantOf(now);\n return {\n id: at === instantOf(DEFAULT_NOW) ? CLOCK_ID : `${CLOCK_ID}@${new Date(at).toISOString()}`,\n trick: 'hold',\n needs: 'layout',\n governs: 'clock',\n because: 'Date reads one fixed instant, so elapsed time measured with Date.now() reads zero',\n driver: { clock: { at } },\n };\n}\n\n/** {@link freezeClockAt} the default instant, {@link DEFAULT_NOW}. */\nexport const freezeClock: Intervention = freezeClockAt(DEFAULT_NOW);\n\n/**\n * The clock trick a `freeze-clock@<instant>` id names, or undefined for any\n * other id.\n *\n * The registry holds `freeze-clock` alone; an id with an instant in it is\n * resolved here, so a recipe that crossed into a page as ids keeps its instant.\n */\nexport function clockById(id: string): Intervention | undefined {\n return id.startsWith(`${CLOCK_ID}@`) ? freezeClockAt(id.slice(CLOCK_ID.length + 1)) : undefined;\n}\n\n/**\n * The init script {@link freezeClock} installs: `Date` answers `frozen`, and\n * nothing else changes.\n *\n * Runs in the page, so it closes over nothing. `new Date()`, `Date()` and\n * `Date.now()` read the instant; `new Date(value)`, `Date.UTC`, `Date.parse`\n * and every method on a date are the browser's own. `Intl.DateTimeFormat`'s\n * `format()` and `formatToParts()` with no date read the browser's clock rather\n * than `Date`, so they are given the instant too.\n */\nexport function freezeDate(frozen: number): void {\n // TODO: `Temporal.Now` and a worker's `Date` read the real clock. This runs\n // in each frame's window and replaces `Date` alone, so a page that prints a\n // date from either prints a second reading.\n const Real = Date;\n function Frozen(...args: unknown[]): unknown {\n if (new.target === undefined) return new Real(frozen).toString();\n return Reflect.construct(Real, args.length === 0 ? [frozen] : args, new.target);\n }\n Object.setPrototypeOf(Frozen, Real);\n Object.defineProperties(Frozen, {\n prototype: { value: Real.prototype },\n name: { value: 'Date' },\n length: { value: 7 },\n now: { value: () => frozen, writable: true, configurable: true },\n });\n Object.defineProperty(Real.prototype, 'constructor', { value: Frozen, writable: true, configurable: true });\n Object.defineProperty(globalThis, 'Date', { value: Frozen, writable: true, configurable: true });\n\n const format = Intl.DateTimeFormat.prototype;\n const formatting = Object.getOwnPropertyDescriptor(format, 'format')?.get;\n if (formatting !== undefined) {\n Object.defineProperty(format, 'format', {\n configurable: true,\n get(this: Intl.DateTimeFormat) {\n const bound = formatting.call(this) as (date?: Date | number) => string;\n return (date?: Date | number) => bound(date === undefined ? frozen : date);\n },\n });\n }\n const toParts = format.formatToParts;\n Object.defineProperty(format, 'formatToParts', {\n configurable: true,\n writable: true,\n value(this: Intl.DateTimeFormat, date?: Date | number) {\n return toParts.call(this, date === undefined ? frozen : date);\n },\n });\n}\n\n/**\n * The pointer moved off the page, so nothing is hovered.\n *\n * To `(-1, -1)` rather than to the page's corner: content at the top left is\n * under `(0, 0)` and stays hovered there. `pointer-events: none` is not a\n * substitute — measured, an element already hovered keeps `:hover` under it.\n *\n * Once per page, before load, where nothing but the host moves the pointer. A\n * move is acknowledged on the next frame, ~14 ms measured, so a host that alone\n * drives its page does not move it again. A host that shares the page with a\n * test does, before every reading: see {@link driveBeforeRead}.\n */\nexport const noHover: Intervention = {\n id: 'no-hover',\n trick: 'reset',\n needs: 'layout',\n governs: 'pointer',\n because: 'the pointer moved off the page, so a hover a test left behind is not read',\n driver: { pointer: 'outside' },\n};\n\n/**\n * Nothing focused, so a field a test filled or a page focused on load reads\n * as it does untouched.\n *\n * A settle step rather than a stylesheet: `:focus` and `:focus-visible` follow\n * the element that holds focus, and no rule can unmatch them. It blurs the\n * deepest focused element, walking through shadow roots, because a shadow host\n * is the document's `activeElement` for whatever its tree focused, and blurring\n * the host leaves the input inside it focused and outlined.\n *\n * The cost: a page evaluate per subject, and an animation frame as well when\n * the document holds an `[autofocus]`. A host that reads a page somebody goes\n * on using — a test, an agent's browser — gives focus back once the reading\n * is taken; a host that owns its page does not need to.\n */\nexport const noFocus: Intervention = {\n id: 'no-focus',\n trick: 'reset',\n needs: 'layout',\n governs: 'focus',\n because: 'focus released, so a field a test or the page focused is read unfocused',\n // Again in a frame callback when the document holds an `autofocus`, because\n // the browser honours one at the page's next rendering update: a blur before\n // it is undone, and a document written by `setContent` has had no update yet.\n // Only then, so a page without one pays no frame per subject.\n settle: async (target) => {\n await target.evaluate(\n () =>\n new Promise<void>((resolve) => {\n const view = globalThis as unknown as PageGlobals;\n const release = (): void => {\n let focused: PageFocusable | null | undefined = view.document.activeElement;\n while (focused?.shadowRoot?.activeElement != null) focused = focused.shadowRoot.activeElement;\n focused?.blur?.();\n };\n release();\n if (view.document.querySelector('[autofocus]') === null) return resolve();\n view.requestAnimationFrame(() => {\n release();\n resolve();\n });\n }),\n );\n },\n};\n\n/**\n * The tricks that put a page into the state nobody is touching, in every\n * default collection recipe whose tier can see them.\n *\n * A hover, a focus, a script animation and a printed date are each a second\n * reading of one subject, and each comes from something other than the code\n * under review. Three of them reach the page only through a driver, so a host\n * that cannot drive one leaves it out of the ids it records — see {@link forDriver}.\n */\nexport const UNTOUCHED: readonly Intervention[] = [noHover, noFocus, reducedMotion, freezeClock];\n\nconst MOTION = 'prefers-reduced-motion';\n\n/**\n * The media features a recipe's driver tricks make the browser report, merged\n * under the ones a project declared, or undefined when there are none.\n *\n * Collection resolves `@media` against declared features, not against the\n * browser, so a preference the driver emulated and nobody declared would be\n * read as unknown: both the `reduce` and the `no-preference` block applied, a\n * box the browser never drew. Declaring what the trick emulates gives the\n * preference one owner. A project's own value wins, and\n * {@link driveBeforeLoad} emulates that value, so the two still agree.\n */\nexport function featuresOf(\n recipe: readonly Intervention[],\n declared?: Readonly<Record<string, string>>,\n): Readonly<Record<string, string>> | undefined {\n const motion = recipe.find((intervention) => intervention.driver?.reducedMotion !== undefined);\n const features = {\n ...(motion === undefined ? {} : { [MOTION]: motion.driver!.reducedMotion! }),\n ...declared,\n };\n return Object.keys(features).length === 0 ? undefined : features;\n}\n\n/**\n * Apply what a recipe asks of the browser before the page loads.\n *\n * All three survive navigation, so a host calls this once per page, before its\n * first `goto` or `setContent`. Returns the ids applied, in recipe order. The\n * clock's instant is the trick's own, so a host that wants another instant\n * asks `recipeToDrive` for the recipe rather than telling this.\n *\n * `declared` is the media features the project declared. A declared\n * `prefers-reduced-motion` is what the motion trick emulates, so the browser\n * reports what the record says; see {@link featuresOf}.\n */\nexport async function driveBeforeLoad(\n recipe: readonly Intervention[],\n target: DriverTarget,\n declared?: Readonly<Record<string, string>>,\n): Promise<readonly string[]> {\n const applied: string[] = [];\n for (const intervention of recipe) {\n const driver = intervention.driver;\n if (driver?.pointer === 'outside') {\n await target.mouse.move(-1, -1);\n applied.push(intervention.id);\n } else if (driver?.reducedMotion !== undefined) {\n const preference = motionOf(declared?.[MOTION] ?? driver.reducedMotion);\n await target.emulateMedia({ reducedMotion: preference });\n // A declared `no-preference` is emulated and recorded among the features;\n // the trick's id would claim a reduced motion the page never had.\n if (preference === driver.reducedMotion) applied.push(intervention.id);\n } else if (driver?.clock !== undefined) {\n await target.addInitScript(freezeDate, driver.clock.at);\n applied.push(intervention.id);\n }\n }\n return applied;\n}\n\nfunction motionOf(value: string): MotionPreference {\n if (value === 'reduce' || value === 'no-preference') return value;\n throw new Error(\n `\\`${MOTION}\\` is declared as ${JSON.stringify(value)}, which no browser reports; ` +\n 'declare \"reduce\" or \"no-preference\"',\n );\n}\n\n/**\n * Take the pointer off the page again before a subject is read.\n *\n * For a host that shares the page: a test hovers a menu to open it and then\n * observes, so the pointer is wherever the test's last step left it. A host\n * that alone drives its page needs only {@link driveBeforeLoad}.\n *\n * Every reading pays the frame, hovered or not: measured in Chromium, a\n * Playwright hover restyles the element while `:hover` matches nothing from\n * script, so the page cannot say whether the move is needed. The pointer is\n * not put back afterwards, and the move fires `mouseleave`: a menu a script\n * opened on hover is closed for the test's next step.\n */\nexport async function driveBeforeRead(\n recipe: readonly Intervention[],\n target: Pick<DriverTarget, 'mouse'>,\n): Promise<readonly string[]> {\n const applied: string[] = [];\n for (const intervention of recipe) {\n if (intervention.driver?.pointer === 'outside') {\n await target.mouse.move(-1, -1);\n applied.push(intervention.id);\n }\n }\n return applied;\n}\n\n/**\n * The recipe as it holds on a page, given the driver tricks a host applied.\n *\n * A trick with no `driver` is kept. A trick with one is kept only when its id\n * is in `driven`: a host that could not drive it leaves it out of the ids and\n * the digest it records, rather than claiming a hold the page never had. The\n * clock is the one trick whose id the driver may change, because the instant is\n * the host's to choose: the recipe's clock is recorded as the one the host\n * fixed.\n */\nexport function forDriver(\n recipe: readonly Intervention[],\n driven: readonly string[],\n): readonly Intervention[] {\n const fixed = driven.map((id) => (id === freezeClock.id ? freezeClock : clockById(id))).find(Boolean);\n return recipe.flatMap((intervention) => {\n if (intervention.driver === undefined || driven.includes(intervention.id)) return [intervention];\n return intervention.driver.clock !== undefined && fixed !== undefined ? [fixed] : [];\n });\n}\n"]}
@@ -12,7 +12,7 @@ import type { ProfileId } from './profile.js';
12
12
  * treated as one. Leaving a field out is a correctness bug. When in doubt,
13
13
  * include it: over-invalidation costs CI minutes, under-invalidation costs trust.
14
14
  */
15
- export interface EnvironmentInputs {
15
+ export interface EnvironmentInputs extends PageRegion {
16
16
  /** Which collector, and therefore which dimensions exist at all (ADR-0002). */
17
17
  readonly profile: ProfileId;
18
18
  /** Engine identity. `jsdom@26.1.0`, `chromium@131.0.6778.33`. */
@@ -60,6 +60,24 @@ export interface EnvironmentInputs {
60
60
  */
61
61
  readonly stabilization?: Digest;
62
62
  }
63
+ /**
64
+ * The timezone a page's clock reads in and the language it formats for, as
65
+ * Playwright names them.
66
+ *
67
+ * A page that formats a date or a number without naming a timezone or a language
68
+ * of its own gets its browser's, so one build reads `12:30 AM` in one timezone
69
+ * and `4:30 PM` in another — different text, different widths, no edit anywhere.
70
+ * A collector that opened its page in a region it chose writes it to the
71
+ * environment key, the way the viewport carries its colour scheme. A field is
72
+ * absent where nobody chose it: a jsdom capture runs in its Node process, and an
73
+ * absent field says so rather than naming a timezone nobody set.
74
+ */
75
+ export interface PageRegion {
76
+ /** An IANA timezone, such as `Europe/Berlin`. */
77
+ readonly timezoneId?: string;
78
+ /** A BCP 47 language tag, such as `de-DE`: a run's base locale (spec 0008). */
79
+ readonly locale?: string;
80
+ }
63
81
  export interface Viewport {
64
82
  readonly width: number;
65
83
  readonly height: number;
@@ -111,7 +129,7 @@ export declare function environmentKey(inputs: EnvironmentInputs): EnvironmentKe
111
129
  * environments is a product feature, not a debugging aid.
112
130
  */
113
131
  export declare function diffEnvironments(before: EnvironmentInputs, after: EnvironmentInputs): readonly EnvironmentDelta[];
114
- export type EnvironmentField = 'profile' | 'engine' | 'ruleset' | 'allowlist' | 'viewport' | 'fonts' | 'conditions' | 'assets' | 'stabilization';
132
+ export type EnvironmentField = 'profile' | 'engine' | 'ruleset' | 'allowlist' | 'viewport' | 'fonts' | 'conditions' | 'assets' | 'stabilization' | 'timezoneId' | 'locale';
115
133
  export interface EnvironmentDelta {
116
134
  readonly field: EnvironmentField;
117
135
  readonly from: string;
@@ -12,6 +12,11 @@ export function environmentKey(inputs) {
12
12
  // the recipe's first job is to stop an animation from moving `transform`,
13
13
  // which is a value the semantic representation carries.
14
14
  stabilization: inputs.stabilization,
15
+ // Text is laid out, so a timezone or a language reaches the box tree, and both
16
+ // keys carry them. `undefined` is omitted from the canonical form, so a
17
+ // capture that chose neither keeps the key it had before either existed.
18
+ timezoneId: inputs.timezoneId,
19
+ locale: inputs.locale,
15
20
  };
16
21
  // Layout depends on the viewport's size and colour scheme; rasterization also
17
22
  // depends on its scale factor. Splitting the viewport is what makes the two
@@ -48,6 +53,8 @@ export function diffEnvironments(before, after) {
48
53
  scalar('conditions', before.conditions, after.conditions);
49
54
  scalar('assets', before.assets, after.assets);
50
55
  scalar('stabilization', before.stabilization, after.stabilization);
56
+ scalar('timezoneId', before.timezoneId, after.timezoneId);
57
+ scalar('locale', before.locale, after.locale);
51
58
  return deltas;
52
59
  }
53
60
  //# sourceMappingURL=environment.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"environment.js","sourceRoot":"","sources":["../../src/format/environment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAe,MAAM,WAAW,CAAC;AAqHrD,MAAM,UAAU,cAAc,CAAC,MAAyB;IACtD,MAAM,MAAM,GAAG;QACb,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,KAAK,EAAE,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE;QAC/B,UAAU,EAAE,EAAE,GAAG,MAAM,CAAC,UAAU,EAAE;QACpC,MAAM,EAAE,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE;QAC5B,4EAA4E;QAC5E,0EAA0E;QAC1E,wDAAwD;QACxD,aAAa,EAAE,MAAM,CAAC,aAAa;KACpC,CAAC;IAEF,8EAA8E;IAC9E,4EAA4E;IAC5E,2DAA2D;IAC3D,MAAM,EAAE,iBAAiB,EAAE,MAAM,EAAE,GAAG,cAAc,EAAE,GAAG,MAAM,CAAC,QAAQ,CAAC;IAEzE,OAAO;QACL,MAAM,EAAE,WAAW,CAAC,EAAE,GAAG,MAAM,EAAE,QAAQ,EAAE,EAAE,GAAG,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC;QACpE,cAAc,EAAE,WAAW,CAAC,EAAE,GAAG,MAAM,EAAE,QAAQ,EAAE,cAAc,EAAE,CAAC;QACpE,MAAM;KACP,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,gBAAgB,CAC9B,MAAyB,EACzB,KAAwB;IAExB,MAAM,MAAM,GAAuB,EAAE,CAAC;IAEtC,MAAM,MAAM,GAAG,CAAC,KAAuB,EAAE,CAAU,EAAE,CAAU,EAAQ,EAAE;QACvE,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;QAC/B,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;QAC7B,IAAI,IAAI,KAAK,EAAE;YAAE,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;IACpD,CAAC,CAAC;IAEF,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IACjD,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC9C,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IACjD,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,SAAS,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IACvD,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;IACpD,MAAM,CAAC,OAAO,EAAE,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IACnE,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;IAC1D,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC9C,MAAM,CAAC,eAAe,EAAE,MAAM,CAAC,aAAa,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC;IAEnE,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["import { digestValue, type Digest } from './hash.js';\nimport type { ProfileId } from './profile.js';\n\n/**\n * The environment key: a hash of every render input that is not code.\n *\n * The system's central guarantee is *same hash ⇒ same render*. That guarantee is\n * exactly as strong as this key's coverage, and no stronger — an uncovered input\n * (a font that silently changed, an asset served from a URL whose bytes moved)\n * produces two different renders under one key, which is a false `unchanged`.\n *\n * Adding a field here is a mass-invalidation event (spec §7.3) and must be\n * treated as one. Leaving a field out is a correctness bug. When in doubt,\n * include it: over-invalidation costs CI minutes, under-invalidation costs trust.\n */\nexport interface EnvironmentInputs {\n /** Which collector, and therefore which dimensions exist at all (ADR-0002). */\n readonly profile: ProfileId;\n\n /** Engine identity. `jsdom@26.1.0`, `chromium@131.0.6778.33`. */\n readonly engine: string;\n\n /**\n * Normalization ruleset version. Changing the rules changes every hash by\n * design, so the ruleset is a render input like any other.\n */\n readonly ruleset: string;\n\n /** Computed-style allowlist version. Widening it is also mass invalidation. */\n readonly allowlist: string;\n\n readonly viewport: Viewport;\n\n /**\n * Fonts available at render time, as `family/weight/style/contentHash`.\n * A font substitution changes metrics, and therefore geometry, without\n * changing a single byte of code.\n */\n readonly fonts: readonly string[];\n\n /**\n * Resolved media/feature conditions. ADR-0003 flattens `@media`, `@supports`,\n * and `@container` away from the CSS text, so the conditions they were\n * flattened against must live here or they vanish from the key entirely.\n */\n readonly conditions: Readonly<Record<string, string | boolean | number>>;\n\n /**\n * External assets keyed by request URL, valued by content hash. URLs are not\n * identities: the same `url(...)` can resolve to different bytes tomorrow.\n */\n readonly assets: Readonly<Record<string, string>>;\n\n /**\n * Identity of the stabilization recipe the page was held still with, from\n * `recipeDigest` — absent when the subject was observed untouched.\n *\n * A render input, and one of the more consequential ones: pinning animations\n * changes `transform` and `opacity` on every animated node, so a baseline\n * collected without it and a run collected with it disagree everywhere and\n * agree about nothing. Without this field that disagreement arrives as a\n * change with a component and a file attached — a confident, specific, wrong\n * answer. With it, the two are different baselines and never meet.\n *\n * Optional rather than required because *not stabilizing* is a real state a\n * caller may be in — a jsdom capture has nothing to hold still — and\n * `undefined` is omitted from the canonical form, so the untouched case hashes\n * as the absence it is rather than as an empty recipe.\n */\n readonly stabilization?: Digest;\n}\n\nexport interface Viewport {\n readonly width: number;\n readonly height: number;\n readonly deviceScaleFactor: number;\n /** `prefers-color-scheme`, and the reason a theme switch is not a code change. */\n readonly colorScheme: 'light' | 'dark';\n}\n\nexport interface EnvironmentKey {\n /**\n * Every render input, including the ones only pixels can see.\n *\n * The key a raster baseline must be stored under. Two machines agree on it only\n * if they agree on device pixel ratio as well as everything else — which is why\n * pixel-based tools end up inside a container.\n */\n readonly digest: Digest;\n\n /**\n * The inputs that can reach the *semantic* representation.\n *\n * This is the key that matters economically. A raster baseline is machine-bound\n * because rasterization is machine-bound, and the usual answer — put the whole\n * pipeline in Docker — pays that cost on every subject of every build in order\n * to stabilise the tier that decides almost nothing.\n *\n * The semantic representation is built from the box tree, and device pixel\n * ratio cannot reach a box tree: layout is in CSS pixels, and a 2× render lays\n * out identically. Measured, not assumed — a DPR change moves 3015 pixels and\n * leaves structure and style byte-identical (journal 0012).\n *\n * So the semantic key omits it, and one semantic baseline is valid on a retina\n * laptop, a non-retina CI runner, and a container alike. The container is then\n * needed only for the raster residue, which the tiering already makes rare.\n *\n * What is *not* omitted: fonts, engine, viewport size, ruleset, allowlist, and\n * the resolved conditions. Each of those genuinely changes the box tree, and\n * dropping one to make baselines more portable would buy portability with\n * false `unchanged` verdicts.\n */\n readonly semanticDigest: Digest;\n\n readonly inputs: EnvironmentInputs;\n}\n\nexport function environmentKey(inputs: EnvironmentInputs): EnvironmentKey {\n const shared = {\n profile: inputs.profile,\n engine: inputs.engine,\n ruleset: inputs.ruleset,\n allowlist: inputs.allowlist,\n fonts: [...inputs.fonts].sort(),\n conditions: { ...inputs.conditions },\n assets: { ...inputs.assets },\n // In `shared`, so it reaches the semantic key too. That is the whole point:\n // the recipe's first job is to stop an animation from moving `transform`,\n // which is a value the semantic representation carries.\n stabilization: inputs.stabilization,\n };\n\n // Layout depends on the viewport's size and colour scheme; rasterization also\n // depends on its scale factor. Splitting the viewport is what makes the two\n // keys differ, and it is the only difference between them.\n const { deviceScaleFactor: _scale, ...layoutViewport } = inputs.viewport;\n\n return {\n digest: digestValue({ ...shared, viewport: { ...inputs.viewport } }),\n semanticDigest: digestValue({ ...shared, viewport: layoutViewport }),\n inputs,\n };\n}\n\n/**\n * Explain why two environment keys differ, field by field.\n *\n * Mass invalidation is only survivable if the docket can collapse it to a single\n * named root — \"chromium 131→132\", \"ruleset v3\" — and offer one-action\n * re-baselining (spec §7.3). That collapse needs the specific field, so a diff of\n * environments is a product feature, not a debugging aid.\n */\nexport function diffEnvironments(\n before: EnvironmentInputs,\n after: EnvironmentInputs,\n): readonly EnvironmentDelta[] {\n const deltas: EnvironmentDelta[] = [];\n\n const scalar = (field: EnvironmentField, a: unknown, b: unknown): void => {\n const from = JSON.stringify(a);\n const to = JSON.stringify(b);\n if (from !== to) deltas.push({ field, from, to });\n };\n\n scalar('profile', before.profile, after.profile);\n scalar('engine', before.engine, after.engine);\n scalar('ruleset', before.ruleset, after.ruleset);\n scalar('allowlist', before.allowlist, after.allowlist);\n scalar('viewport', before.viewport, after.viewport);\n scalar('fonts', [...before.fonts].sort(), [...after.fonts].sort());\n scalar('conditions', before.conditions, after.conditions);\n scalar('assets', before.assets, after.assets);\n scalar('stabilization', before.stabilization, after.stabilization);\n\n return deltas;\n}\n\nexport type EnvironmentField =\n | 'profile'\n | 'engine'\n | 'ruleset'\n | 'allowlist'\n | 'viewport'\n | 'fonts'\n | 'conditions'\n | 'assets'\n | 'stabilization';\n\nexport interface EnvironmentDelta {\n readonly field: EnvironmentField;\n readonly from: string;\n readonly to: string;\n}\n"]}
1
+ {"version":3,"file":"environment.js","sourceRoot":"","sources":["../../src/format/environment.ts"],"names":[],"mappings":"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","sourcesContent":["import { digestValue, type Digest } from './hash.js';\nimport type { ProfileId } from './profile.js';\n\n/**\n * The environment key: a hash of every render input that is not code.\n *\n * The system's central guarantee is *same hash ⇒ same render*. That guarantee is\n * exactly as strong as this key's coverage, and no stronger — an uncovered input\n * (a font that silently changed, an asset served from a URL whose bytes moved)\n * produces two different renders under one key, which is a false `unchanged`.\n *\n * Adding a field here is a mass-invalidation event (spec §7.3) and must be\n * treated as one. Leaving a field out is a correctness bug. When in doubt,\n * include it: over-invalidation costs CI minutes, under-invalidation costs trust.\n */\nexport interface EnvironmentInputs extends PageRegion {\n /** Which collector, and therefore which dimensions exist at all (ADR-0002). */\n readonly profile: ProfileId;\n\n /** Engine identity. `jsdom@26.1.0`, `chromium@131.0.6778.33`. */\n readonly engine: string;\n\n /**\n * Normalization ruleset version. Changing the rules changes every hash by\n * design, so the ruleset is a render input like any other.\n */\n readonly ruleset: string;\n\n /** Computed-style allowlist version. Widening it is also mass invalidation. */\n readonly allowlist: string;\n\n readonly viewport: Viewport;\n\n /**\n * Fonts available at render time, as `family/weight/style/contentHash`.\n * A font substitution changes metrics, and therefore geometry, without\n * changing a single byte of code.\n */\n readonly fonts: readonly string[];\n\n /**\n * Resolved media/feature conditions. ADR-0003 flattens `@media`, `@supports`,\n * and `@container` away from the CSS text, so the conditions they were\n * flattened against must live here or they vanish from the key entirely.\n */\n readonly conditions: Readonly<Record<string, string | boolean | number>>;\n\n /**\n * External assets keyed by request URL, valued by content hash. URLs are not\n * identities: the same `url(...)` can resolve to different bytes tomorrow.\n */\n readonly assets: Readonly<Record<string, string>>;\n\n /**\n * Identity of the stabilization recipe the page was held still with, from\n * `recipeDigest` — absent when the subject was observed untouched.\n *\n * A render input, and one of the more consequential ones: pinning animations\n * changes `transform` and `opacity` on every animated node, so a baseline\n * collected without it and a run collected with it disagree everywhere and\n * agree about nothing. Without this field that disagreement arrives as a\n * change with a component and a file attached — a confident, specific, wrong\n * answer. With it, the two are different baselines and never meet.\n *\n * Optional rather than required because *not stabilizing* is a real state a\n * caller may be in — a jsdom capture has nothing to hold still — and\n * `undefined` is omitted from the canonical form, so the untouched case hashes\n * as the absence it is rather than as an empty recipe.\n */\n readonly stabilization?: Digest;\n}\n\n/**\n * The timezone a page's clock reads in and the language it formats for, as\n * Playwright names them.\n *\n * A page that formats a date or a number without naming a timezone or a language\n * of its own gets its browser's, so one build reads `12:30 AM` in one timezone\n * and `4:30 PM` in another — different text, different widths, no edit anywhere.\n * A collector that opened its page in a region it chose writes it to the\n * environment key, the way the viewport carries its colour scheme. A field is\n * absent where nobody chose it: a jsdom capture runs in its Node process, and an\n * absent field says so rather than naming a timezone nobody set.\n */\nexport interface PageRegion {\n /** An IANA timezone, such as `Europe/Berlin`. */\n readonly timezoneId?: string;\n\n /** A BCP 47 language tag, such as `de-DE`: a run's base locale (spec 0008). */\n readonly locale?: string;\n}\n\nexport interface Viewport {\n readonly width: number;\n readonly height: number;\n readonly deviceScaleFactor: number;\n /** `prefers-color-scheme`, and the reason a theme switch is not a code change. */\n readonly colorScheme: 'light' | 'dark';\n}\n\nexport interface EnvironmentKey {\n /**\n * Every render input, including the ones only pixels can see.\n *\n * The key a raster baseline must be stored under. Two machines agree on it only\n * if they agree on device pixel ratio as well as everything else — which is why\n * pixel-based tools end up inside a container.\n */\n readonly digest: Digest;\n\n /**\n * The inputs that can reach the *semantic* representation.\n *\n * This is the key that matters economically. A raster baseline is machine-bound\n * because rasterization is machine-bound, and the usual answer — put the whole\n * pipeline in Docker — pays that cost on every subject of every build in order\n * to stabilise the tier that decides almost nothing.\n *\n * The semantic representation is built from the box tree, and device pixel\n * ratio cannot reach a box tree: layout is in CSS pixels, and a 2× render lays\n * out identically. Measured, not assumed — a DPR change moves 3015 pixels and\n * leaves structure and style byte-identical (journal 0012).\n *\n * So the semantic key omits it, and one semantic baseline is valid on a retina\n * laptop, a non-retina CI runner, and a container alike. The container is then\n * needed only for the raster residue, which the tiering already makes rare.\n *\n * What is *not* omitted: fonts, engine, viewport size, ruleset, allowlist, and\n * the resolved conditions. Each of those genuinely changes the box tree, and\n * dropping one to make baselines more portable would buy portability with\n * false `unchanged` verdicts.\n */\n readonly semanticDigest: Digest;\n\n readonly inputs: EnvironmentInputs;\n}\n\nexport function environmentKey(inputs: EnvironmentInputs): EnvironmentKey {\n const shared = {\n profile: inputs.profile,\n engine: inputs.engine,\n ruleset: inputs.ruleset,\n allowlist: inputs.allowlist,\n fonts: [...inputs.fonts].sort(),\n conditions: { ...inputs.conditions },\n assets: { ...inputs.assets },\n // In `shared`, so it reaches the semantic key too. That is the whole point:\n // the recipe's first job is to stop an animation from moving `transform`,\n // which is a value the semantic representation carries.\n stabilization: inputs.stabilization,\n // Text is laid out, so a timezone or a language reaches the box tree, and both\n // keys carry them. `undefined` is omitted from the canonical form, so a\n // capture that chose neither keeps the key it had before either existed.\n timezoneId: inputs.timezoneId,\n locale: inputs.locale,\n };\n\n // Layout depends on the viewport's size and colour scheme; rasterization also\n // depends on its scale factor. Splitting the viewport is what makes the two\n // keys differ, and it is the only difference between them.\n const { deviceScaleFactor: _scale, ...layoutViewport } = inputs.viewport;\n\n return {\n digest: digestValue({ ...shared, viewport: { ...inputs.viewport } }),\n semanticDigest: digestValue({ ...shared, viewport: layoutViewport }),\n inputs,\n };\n}\n\n/**\n * Explain why two environment keys differ, field by field.\n *\n * Mass invalidation is only survivable if the docket can collapse it to a single\n * named root — \"chromium 131→132\", \"ruleset v3\" — and offer one-action\n * re-baselining (spec §7.3). That collapse needs the specific field, so a diff of\n * environments is a product feature, not a debugging aid.\n */\nexport function diffEnvironments(\n before: EnvironmentInputs,\n after: EnvironmentInputs,\n): readonly EnvironmentDelta[] {\n const deltas: EnvironmentDelta[] = [];\n\n const scalar = (field: EnvironmentField, a: unknown, b: unknown): void => {\n const from = JSON.stringify(a);\n const to = JSON.stringify(b);\n if (from !== to) deltas.push({ field, from, to });\n };\n\n scalar('profile', before.profile, after.profile);\n scalar('engine', before.engine, after.engine);\n scalar('ruleset', before.ruleset, after.ruleset);\n scalar('allowlist', before.allowlist, after.allowlist);\n scalar('viewport', before.viewport, after.viewport);\n scalar('fonts', [...before.fonts].sort(), [...after.fonts].sort());\n scalar('conditions', before.conditions, after.conditions);\n scalar('assets', before.assets, after.assets);\n scalar('stabilization', before.stabilization, after.stabilization);\n scalar('timezoneId', before.timezoneId, after.timezoneId);\n scalar('locale', before.locale, after.locale);\n\n return deltas;\n}\n\nexport type EnvironmentField =\n | 'profile'\n | 'engine'\n | 'ruleset'\n | 'allowlist'\n | 'viewport'\n | 'fonts'\n | 'conditions'\n | 'assets'\n | 'stabilization'\n | 'timezoneId'\n | 'locale';\n\nexport interface EnvironmentDelta {\n readonly field: EnvironmentField;\n readonly from: string;\n readonly to: string;\n}\n"]}
@@ -21,20 +21,30 @@ export type { AccessibilitySnapshot } from './accessibility.js';
21
21
  export { accessibilitySnapshot } from './accessibility.js';
22
22
  export type { ObservationProfile, ProfileId } from './profile.js';
23
23
  export { JSDOM_PROFILE, CHROMIUM_PROFILE, profileById, tierOfProfile } from './profile.js';
24
+ export { assetUrl, pageOrigin, withoutOrigin } from './origin.js';
24
25
  export type { Tier } from './tier.js';
25
26
  export { tierReaches } from './tier.js';
26
- export type { EnvironmentInputs, EnvironmentKey, EnvironmentDelta, EnvironmentField, Viewport, } from './environment.js';
27
+ export type { EnvironmentInputs, EnvironmentKey, EnvironmentDelta, EnvironmentField, PageRegion, Viewport, } from './environment.js';
27
28
  export { environmentKey, diffEnvironments } from './environment.js';
28
- export { INTERVENTIONS, SEMANTIC_RECIPE, COLLECT_RECIPE, LAYOUT_RECIPE, RASTER_RECIPE, holdAnimations, pinAnimations, hideCaret, hideScrollbars, hidePresentationalImages, waitForFonts, waitForImages, forTier, conflicts, interventionById, recipeOf, recipeCss, recipeScreenshot, recipeDigest, settleRecipe, describeRecipe, } from './stabilize.js';
29
+ export { INTERVENTIONS, SEMANTIC_RECIPE, COLLECT_RECIPE, DEFAULT_STABILIZE, LAYOUT_RECIPE, RASTER_RECIPE, holdAnimations, pinAnimations, hideCaret, hideScrollbars, hidePresentationalImages, waitForFonts, waitForImages, forTier, conflicts, interventionById, recipeOf, recipeToDrive, recipeCss, recipeScreenshot, recipeDigest, settleRecipe, describeRecipe, } from './stabilize.js';
29
30
  export type { Intervention, Recipe, Trick, ScreenshotOptions, SettleTarget, } from './stabilize.js';
31
+ export type { Step, StepClock, StepTotal } from './steps.js';
32
+ export { stepClock, timeOn, totalsOf } from './steps.js';
33
+ export { DEFAULT_NOW, driveBeforeLoad, driveBeforeRead, featuresOf, forDriver } from './drive.js';
34
+ export type { DriverState, DriverTarget, MotionPreference } from './intervention.js';
30
35
  export type { Provenance, OwnerFrame, SourceLocation, StackFrame } from './provenance.js';
31
36
  export { propsDigest, heldDigest, jsxSourceOf, relativizeSource, JSX_SOURCE } from './provenance.js';
32
37
  export type { Holding, HeldCell, HeldValue } from './holding.js';
33
38
  export type { Wiring } from './wiring.js';
34
39
  export { keyedByPosition } from './wiring.js';
35
40
  export type { RawCapture, RawNode, RawAria, MatchedRule, Declaration, Rect, SubjectRef, Diagnostic, } from './capture.js';
36
- export { mergeDiagnostics } from './capture.js';
41
+ export { intersectionArea, mergeDiagnostics } from './capture.js';
37
42
  export type { SemanticSnapshot, SemanticNode, NodePath, StyleProvenanceEntry, IgnoreSite, ComponentHash, } from './snapshot.js';
43
+ export { isRenderDocument, isSemanticSnapshot, isSubjectRef } from './shape.js';
38
44
  export { documentDigest, identityDigest, occupiesPixels, pictured, picture } from './document.js';
39
45
  export type { RenderDocument, RenderFrame, FrameElement, RenderResource, RenderIdentity, Raster, Pictured, } from './document.js';
46
+ export { keepsSnapshot, PACKED_SNAPSHOT_HEADER } from './packed.js';
47
+ export type { PackedSnapshot } from './packed.js';
48
+ export { atWidth, realizes, widenedFrom } from './width.js';
49
+ export type { Widenable } from './width.js';
40
50
  //# sourceMappingURL=index.d.ts.map
@@ -16,11 +16,17 @@ export { shapeValue, pointerShape, pointerToken, DROPPED, VALUE_RECIPE } from '.
16
16
  export { digestString, digestBytes, digestValue, digestCombine, digestOfSha256, fileNameFor, digestFileName, digestOfFileName } from './hash.js';
17
17
  export { accessibilitySnapshot } from './accessibility.js';
18
18
  export { JSDOM_PROFILE, CHROMIUM_PROFILE, profileById, tierOfProfile } from './profile.js';
19
+ export { assetUrl, pageOrigin, withoutOrigin } from './origin.js';
19
20
  export { tierReaches } from './tier.js';
20
21
  export { environmentKey, diffEnvironments } from './environment.js';
21
- export { INTERVENTIONS, SEMANTIC_RECIPE, COLLECT_RECIPE, LAYOUT_RECIPE, RASTER_RECIPE, holdAnimations, pinAnimations, hideCaret, hideScrollbars, hidePresentationalImages, waitForFonts, waitForImages, forTier, conflicts, interventionById, recipeOf, recipeCss, recipeScreenshot, recipeDigest, settleRecipe, describeRecipe, } from './stabilize.js';
22
+ export { INTERVENTIONS, SEMANTIC_RECIPE, COLLECT_RECIPE, DEFAULT_STABILIZE, LAYOUT_RECIPE, RASTER_RECIPE, holdAnimations, pinAnimations, hideCaret, hideScrollbars, hidePresentationalImages, waitForFonts, waitForImages, forTier, conflicts, interventionById, recipeOf, recipeToDrive, recipeCss, recipeScreenshot, recipeDigest, settleRecipe, describeRecipe, } from './stabilize.js';
23
+ export { stepClock, timeOn, totalsOf } from './steps.js';
24
+ export { DEFAULT_NOW, driveBeforeLoad, driveBeforeRead, featuresOf, forDriver } from './drive.js';
22
25
  export { propsDigest, heldDigest, jsxSourceOf, relativizeSource, JSX_SOURCE } from './provenance.js';
23
26
  export { keyedByPosition } from './wiring.js';
24
- export { mergeDiagnostics } from './capture.js';
27
+ export { intersectionArea, mergeDiagnostics } from './capture.js';
28
+ export { isRenderDocument, isSemanticSnapshot, isSubjectRef } from './shape.js';
25
29
  export { documentDigest, identityDigest, occupiesPixels, pictured, picture } from './document.js';
30
+ export { keepsSnapshot, PACKED_SNAPSHOT_HEADER } from './packed.js';
31
+ export { atWidth, realizes, widenedFrom } from './width.js';
26
32
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/format/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAKH,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAG/D,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,YAAY,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG3F,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,aAAa,EAAE,cAAc,EAAE,WAAW,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAGjJ,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAG3D,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAG3F,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AASxC,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEpE,OAAO,EACL,aAAa,EACb,eAAe,EACf,cAAc,EACd,aAAa,EACb,aAAa,EACb,cAAc,EACd,aAAa,EACb,SAAS,EACT,cAAc,EACd,wBAAwB,EACxB,YAAY,EACZ,aAAa,EACb,OAAO,EACP,SAAS,EACT,gBAAgB,EAChB,QAAQ,EACR,SAAS,EACT,gBAAgB,EAChB,YAAY,EACZ,YAAY,EACZ,cAAc,GACf,MAAM,gBAAgB,CAAC;AAUxB,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAGrG,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAY9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAWhD,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,cAAc,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC","sourcesContent":["/**\n * `@variance-authority/core/format` — what a subject *is*, as a value.\n *\n * The shapes every other group reads and none of them may redefine: a raw\n * capture as a collector hands it over, the normalized snapshot it becomes, the\n * document a renderer paints, the identity that addresses the result, and the\n * hashing that makes any of it comparable.\n *\n * A consumer writing their own collector needs exactly this and nothing else.\n * That is the entrypoint's reason to exist: the format is a contract other people\n * implement, and implementing it should not require reading the rules that\n * interpret it or the verdicts drawn from it.\n */\n\n// compass: variance-authority.normalization\n\nexport type { CanonicalValue } from './canonical.js';\nexport { canonicalize, canonicalNumber } from './canonical.js';\n\nexport type { CapturedValue, ShapeValueOptions, ValueShaping } from './value.js';\nexport { shapeValue, pointerShape, pointerToken, DROPPED, VALUE_RECIPE } from './value.js';\n\nexport type { Digest } from './hash.js';\nexport { digestString, digestBytes, digestValue, digestCombine, digestOfSha256, fileNameFor, digestFileName, digestOfFileName } from './hash.js';\n\nexport type { AccessibilitySnapshot } from './accessibility.js';\nexport { accessibilitySnapshot } from './accessibility.js';\n\nexport type { ObservationProfile, ProfileId } from './profile.js';\nexport { JSDOM_PROFILE, CHROMIUM_PROFILE, profileById, tierOfProfile } from './profile.js';\n\nexport type { Tier } from './tier.js';\nexport { tierReaches } from './tier.js';\n\nexport type {\n EnvironmentInputs,\n EnvironmentKey,\n EnvironmentDelta,\n EnvironmentField,\n Viewport,\n} from './environment.js';\nexport { environmentKey, diffEnvironments } from './environment.js';\n\nexport {\n INTERVENTIONS,\n SEMANTIC_RECIPE,\n COLLECT_RECIPE,\n LAYOUT_RECIPE,\n RASTER_RECIPE,\n holdAnimations,\n pinAnimations,\n hideCaret,\n hideScrollbars,\n hidePresentationalImages,\n waitForFonts,\n waitForImages,\n forTier,\n conflicts,\n interventionById,\n recipeOf,\n recipeCss,\n recipeScreenshot,\n recipeDigest,\n settleRecipe,\n describeRecipe,\n} from './stabilize.js';\nexport type {\n Intervention,\n Recipe,\n Trick,\n ScreenshotOptions,\n SettleTarget,\n} from './stabilize.js';\n\nexport type { Provenance, OwnerFrame, SourceLocation, StackFrame } from './provenance.js';\nexport { propsDigest, heldDigest, jsxSourceOf, relativizeSource, JSX_SOURCE } from './provenance.js';\nexport type { Holding, HeldCell, HeldValue } from './holding.js';\nexport type { Wiring } from './wiring.js';\nexport { keyedByPosition } from './wiring.js';\n\nexport type {\n RawCapture,\n RawNode,\n RawAria,\n MatchedRule,\n Declaration,\n Rect,\n SubjectRef,\n Diagnostic,\n} from './capture.js';\nexport { mergeDiagnostics } from './capture.js';\n\nexport type {\n SemanticSnapshot,\n SemanticNode,\n NodePath,\n StyleProvenanceEntry,\n IgnoreSite,\n ComponentHash,\n} from './snapshot.js';\n\nexport { documentDigest, identityDigest, occupiesPixels, pictured, picture } from './document.js';\nexport type {\n RenderDocument,\n RenderFrame,\n FrameElement,\n RenderResource,\n RenderIdentity,\n Raster,\n Pictured,\n} from './document.js';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/format/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAKH,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAG/D,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,YAAY,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG3F,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,aAAa,EAAE,cAAc,EAAE,WAAW,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAGjJ,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAG3D,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE3F,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAGlE,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAUxC,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEpE,OAAO,EACL,aAAa,EACb,eAAe,EACf,cAAc,EACd,iBAAiB,EACjB,aAAa,EACb,aAAa,EACb,cAAc,EACd,aAAa,EACb,SAAS,EACT,cAAc,EACd,wBAAwB,EACxB,YAAY,EACZ,aAAa,EACb,OAAO,EACP,SAAS,EACT,gBAAgB,EAChB,QAAQ,EACR,aAAa,EACb,SAAS,EACT,gBAAgB,EAChB,YAAY,EACZ,YAAY,EACZ,cAAc,GACf,MAAM,gBAAgB,CAAC;AASxB,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEzD,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,eAAe,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAIlG,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAGrG,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAY9C,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAUlE,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAEhF,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,cAAc,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAUlG,OAAO,EAAE,aAAa,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAEpE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC","sourcesContent":["/**\n * `@variance-authority/core/format` — what a subject *is*, as a value.\n *\n * The shapes every other group reads and none of them may redefine: a raw\n * capture as a collector hands it over, the normalized snapshot it becomes, the\n * document a renderer paints, the identity that addresses the result, and the\n * hashing that makes any of it comparable.\n *\n * A consumer writing their own collector needs exactly this and nothing else.\n * That is the entrypoint's reason to exist: the format is a contract other people\n * implement, and implementing it should not require reading the rules that\n * interpret it or the verdicts drawn from it.\n */\n\n// compass: variance-authority.normalization\n\nexport type { CanonicalValue } from './canonical.js';\nexport { canonicalize, canonicalNumber } from './canonical.js';\n\nexport type { CapturedValue, ShapeValueOptions, ValueShaping } from './value.js';\nexport { shapeValue, pointerShape, pointerToken, DROPPED, VALUE_RECIPE } from './value.js';\n\nexport type { Digest } from './hash.js';\nexport { digestString, digestBytes, digestValue, digestCombine, digestOfSha256, fileNameFor, digestFileName, digestOfFileName } from './hash.js';\n\nexport type { AccessibilitySnapshot } from './accessibility.js';\nexport { accessibilitySnapshot } from './accessibility.js';\n\nexport type { ObservationProfile, ProfileId } from './profile.js';\nexport { JSDOM_PROFILE, CHROMIUM_PROFILE, profileById, tierOfProfile } from './profile.js';\n\nexport { assetUrl, pageOrigin, withoutOrigin } from './origin.js';\n\nexport type { Tier } from './tier.js';\nexport { tierReaches } from './tier.js';\n\nexport type {\n EnvironmentInputs,\n EnvironmentKey,\n EnvironmentDelta,\n EnvironmentField,\n PageRegion,\n Viewport,\n} from './environment.js';\nexport { environmentKey, diffEnvironments } from './environment.js';\n\nexport {\n INTERVENTIONS,\n SEMANTIC_RECIPE,\n COLLECT_RECIPE,\n DEFAULT_STABILIZE,\n LAYOUT_RECIPE,\n RASTER_RECIPE,\n holdAnimations,\n pinAnimations,\n hideCaret,\n hideScrollbars,\n hidePresentationalImages,\n waitForFonts,\n waitForImages,\n forTier,\n conflicts,\n interventionById,\n recipeOf,\n recipeToDrive,\n recipeCss,\n recipeScreenshot,\n recipeDigest,\n settleRecipe,\n describeRecipe,\n} from './stabilize.js';\nexport type {\n Intervention,\n Recipe,\n Trick,\n ScreenshotOptions,\n SettleTarget,\n} from './stabilize.js';\nexport type { Step, StepClock, StepTotal } from './steps.js';\nexport { stepClock, timeOn, totalsOf } from './steps.js';\n\nexport { DEFAULT_NOW, driveBeforeLoad, driveBeforeRead, featuresOf, forDriver } from './drive.js';\nexport type { DriverState, DriverTarget, MotionPreference } from './intervention.js';\n\nexport type { Provenance, OwnerFrame, SourceLocation, StackFrame } from './provenance.js';\nexport { propsDigest, heldDigest, jsxSourceOf, relativizeSource, JSX_SOURCE } from './provenance.js';\nexport type { Holding, HeldCell, HeldValue } from './holding.js';\nexport type { Wiring } from './wiring.js';\nexport { keyedByPosition } from './wiring.js';\n\nexport type {\n RawCapture,\n RawNode,\n RawAria,\n MatchedRule,\n Declaration,\n Rect,\n SubjectRef,\n Diagnostic,\n} from './capture.js';\nexport { intersectionArea, mergeDiagnostics } from './capture.js';\n\nexport type {\n SemanticSnapshot,\n SemanticNode,\n NodePath,\n StyleProvenanceEntry,\n IgnoreSite,\n ComponentHash,\n} from './snapshot.js';\nexport { isRenderDocument, isSemanticSnapshot, isSubjectRef } from './shape.js';\n\nexport { documentDigest, identityDigest, occupiesPixels, pictured, picture } from './document.js';\nexport type {\n RenderDocument,\n RenderFrame,\n FrameElement,\n RenderResource,\n RenderIdentity,\n Raster,\n Pictured,\n} from './document.js';\nexport { keepsSnapshot, PACKED_SNAPSHOT_HEADER } from './packed.js';\nexport type { PackedSnapshot } from './packed.js';\nexport { atWidth, realizes, widenedFrom } from './width.js';\nexport type { Widenable } from './width.js';\n"]}
@@ -43,9 +43,21 @@ export interface SettleTarget {
43
43
  */
44
44
  export interface PageGlobals {
45
45
  readonly document: {
46
+ /** Read to find what a test left focused; a shadow host stands in for its tree. */
47
+ readonly activeElement?: PageFocusable | null;
48
+ /** Asked whether an `autofocus` is still to be honoured. */
49
+ querySelector(selectors: string): unknown;
46
50
  readonly fonts?: {
47
51
  readonly ready: Promise<unknown>;
52
+ [Symbol.iterator](): Iterator<{
53
+ readonly family: string;
54
+ readonly status: string;
55
+ }>;
48
56
  };
57
+ /** Read to name a web font by where it was asked for; a cross-origin sheet throws. */
58
+ readonly styleSheets: ArrayLike<{
59
+ readonly cssRules: ArrayLike<PageCssRule>;
60
+ }>;
49
61
  readonly images: ArrayLike<{
50
62
  readonly complete: boolean;
51
63
  readonly currentSrc: string;
@@ -58,6 +70,63 @@ export interface PageGlobals {
58
70
  };
59
71
  setTimeout(handler: () => void, ms: number): unknown;
60
72
  clearTimeout(handle: unknown): void;
73
+ requestAnimationFrame(callback: () => void): unknown;
74
+ }
75
+ /** An element as a focus reset reads it: its own shadow tree's focus, and a way to let go. */
76
+ export interface PageFocusable {
77
+ readonly shadowRoot?: {
78
+ readonly activeElement: PageFocusable | null;
79
+ } | null;
80
+ blur?(): void;
81
+ }
82
+ /** The two values of `prefers-reduced-motion` a browser can be told to report. */
83
+ export type MotionPreference = 'no-preference' | 'reduce';
84
+ /**
85
+ * What a trick asks of the browser driver, where neither a stylesheet nor a
86
+ * script in the page can reach.
87
+ *
88
+ * A media preference, the clock and the pointer belong to the browser rather
89
+ * than the document. A media query reads what the browser emulates, `Date` has
90
+ * to be replaced before the page's first script reads it, and a hover ends only
91
+ * when the pointer leaves. Declared as data rather than as a function, so a host
92
+ * with no driver — a page agent, a test runner's own browser — can tell which
93
+ * tricks it cannot honour, and the hosts that can drive agree on what each one
94
+ * means. [`drive.ts`](./drive.ts) applies it.
95
+ */
96
+ export interface DriverState {
97
+ /** `prefers-reduced-motion: reduce`, emulated before the page loads. */
98
+ readonly reducedMotion?: 'reduce';
99
+ /** `Date` reads one fixed instant, in milliseconds, from before the page's first script. */
100
+ readonly clock?: {
101
+ readonly at: number;
102
+ };
103
+ /** The pointer off the page, before the subject is read. */
104
+ readonly pointer?: 'outside';
105
+ }
106
+ /**
107
+ * The driver surface a {@link DriverState} is applied through.
108
+ *
109
+ * Structural, and the shape Playwright's `Page` already has, so no driver
110
+ * package leaks into `core`.
111
+ */
112
+ export interface DriverTarget {
113
+ emulateMedia(options: {
114
+ reducedMotion: MotionPreference;
115
+ }): Promise<void>;
116
+ addInitScript(script: (frozen: number) => void, arg: number): Promise<unknown>;
117
+ readonly mouse: {
118
+ move(x: number, y: number): Promise<void>;
119
+ };
120
+ }
121
+ /** A rule as a settle step reads it: its declarations, the rules it groups, and the sheet it imports. */
122
+ interface PageCssRule {
123
+ readonly style?: {
124
+ getPropertyValue(property: string): string;
125
+ };
126
+ readonly cssRules?: ArrayLike<PageCssRule>;
127
+ readonly styleSheet?: {
128
+ readonly cssRules: ArrayLike<PageCssRule>;
129
+ } | null;
61
130
  }
62
131
  export interface Intervention {
63
132
  /**
@@ -83,5 +152,15 @@ export interface Intervention {
83
152
  readonly css?: string;
84
153
  readonly screenshot?: ScreenshotOptions;
85
154
  readonly settle?: (target: SettleTarget) => Promise<void>;
155
+ /**
156
+ * What the browser driver must do for this trick to hold.
157
+ *
158
+ * A trick with a `driver` is in force only where a host drove it: the hosts
159
+ * that own the browser report the ids they applied, and the page drops the
160
+ * rest from its recipe, so a reading never records a hold it did not have.
161
+ * See `forDriver`.
162
+ */
163
+ readonly driver?: DriverState;
86
164
  }
165
+ export {};
87
166
  //# sourceMappingURL=intervention.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"intervention.js","sourceRoot":"","sources":["../../src/format/intervention.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG","sourcesContent":["/**\n * What an intervention is, as a shape.\n *\n * Beside [`stabilize.ts`](./stabilize.ts), which argues why the set is open and\n * then fills it. This file is the declaration that argument depends on: an id, a\n * tier, a property it claims, and the three ways it can reach a page. A trick\n * from another project is one of these and nothing more, so the shape is worth\n * reading without reading the tricks.\n */\n\nimport type { Tier } from './tier.js';\n\n/** What kind of move it is, for reading a recipe at a glance. */\nexport type Trick =\n /** Puts the page into a known state. */\n | 'reset'\n /** Stops something that would otherwise keep moving. */\n | 'hold'\n /** Blocks until something outside the page's control has landed. */\n | 'wait'\n /** Removes something that is present but must not be measured. */\n | 'support';\n\n/** Screenshot options a trick contributes. Narrow on purpose. */\nexport interface ScreenshotOptions {\n readonly animations?: 'disabled';\n readonly caret?: 'hide';\n}\n\n/** The page surface a `settle` step may use. Structural, so no driver leaks in. */\nexport interface SettleTarget {\n evaluate<T>(fn: () => T | Promise<T>): Promise<T>;\n}\n\n/**\n * The page's globals, as the little of them a settle step needs.\n *\n * A settle closure is the one place in `core` that reads a live document, and it\n * gets away with it because it never runs here: it is handed to\n * {@link SettleTarget.evaluate}, which ships it into a page. ADR-0001's\n * enforcement is `core`'s `tsconfig`, which has no `lib.dom` — so writing\n * `window.document.fonts` is a compile error and this shape is what remains.\n *\n * That is a stricter arrangement than the convention it replaced, not a\n * loophole. Everything a settle step may touch is enumerated here, in one\n * declaration a reader can check, and a trick that wants more of the DOM has to\n * widen it in public rather than reach for an ambient global.\n */\nexport interface PageGlobals {\n readonly document: {\n readonly fonts?: { readonly ready: Promise<unknown> };\n readonly images: ArrayLike<{\n readonly complete: boolean;\n readonly currentSrc: string;\n readonly src: string;\n loading: string;\n addEventListener(type: string, listener: () => void, options?: { once?: boolean }): void;\n }>;\n };\n setTimeout(handler: () => void, ms: number): unknown;\n clearTimeout(handle: unknown): void;\n}\n\nexport interface Intervention {\n /**\n * Stable identifier, and part of the recipe's digest.\n *\n * Renaming one is therefore a new identity, which is correct: a reader\n * comparing two runs has no way to know that `hold-animations` and\n * `pause-animations` were the same trick.\n */\n readonly id: string;\n readonly trick: Trick;\n readonly needs: Tier;\n /** What it does and what it costs, in one sentence a report can print. */\n readonly because: string;\n\n /**\n * The property this trick governs.\n *\n * Two tricks claiming one subject are in conflict — pinning animations in CSS\n * while asking the compositor to fast-forward them applies both, and the CSS\n * wins silently. See {@link conflicts}.\n */\n readonly governs: string;\n\n readonly css?: string;\n readonly screenshot?: ScreenshotOptions;\n readonly settle?: (target: SettleTarget) => Promise<void>;\n}\n"]}
1
+ {"version":3,"file":"intervention.js","sourceRoot":"","sources":["../../src/format/intervention.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG","sourcesContent":["/**\n * What an intervention is, as a shape.\n *\n * Beside [`stabilize.ts`](./stabilize.ts), which argues why the set is open and\n * then fills it. This file is the declaration that argument depends on: an id, a\n * tier, a property it claims, and the three ways it can reach a page. A trick\n * from another project is one of these and nothing more, so the shape is worth\n * reading without reading the tricks.\n */\n\nimport type { Tier } from './tier.js';\n\n/** What kind of move it is, for reading a recipe at a glance. */\nexport type Trick =\n /** Puts the page into a known state. */\n | 'reset'\n /** Stops something that would otherwise keep moving. */\n | 'hold'\n /** Blocks until something outside the page's control has landed. */\n | 'wait'\n /** Removes something that is present but must not be measured. */\n | 'support';\n\n/** Screenshot options a trick contributes. Narrow on purpose. */\nexport interface ScreenshotOptions {\n readonly animations?: 'disabled';\n readonly caret?: 'hide';\n}\n\n/** The page surface a `settle` step may use. Structural, so no driver leaks in. */\nexport interface SettleTarget {\n evaluate<T>(fn: () => T | Promise<T>): Promise<T>;\n}\n\n/**\n * The page's globals, as the little of them a settle step needs.\n *\n * A settle closure is the one place in `core` that reads a live document, and it\n * gets away with it because it never runs here: it is handed to\n * {@link SettleTarget.evaluate}, which ships it into a page. ADR-0001's\n * enforcement is `core`'s `tsconfig`, which has no `lib.dom` — so writing\n * `window.document.fonts` is a compile error and this shape is what remains.\n *\n * That is a stricter arrangement than the convention it replaced, not a\n * loophole. Everything a settle step may touch is enumerated here, in one\n * declaration a reader can check, and a trick that wants more of the DOM has to\n * widen it in public rather than reach for an ambient global.\n */\nexport interface PageGlobals {\n readonly document: {\n /** Read to find what a test left focused; a shadow host stands in for its tree. */\n readonly activeElement?: PageFocusable | null;\n /** Asked whether an `autofocus` is still to be honoured. */\n querySelector(selectors: string): unknown;\n readonly fonts?: {\n readonly ready: Promise<unknown>;\n [Symbol.iterator](): Iterator<{ readonly family: string; readonly status: string }>;\n };\n /** Read to name a web font by where it was asked for; a cross-origin sheet throws. */\n readonly styleSheets: ArrayLike<{ readonly cssRules: ArrayLike<PageCssRule> }>;\n readonly images: ArrayLike<{\n readonly complete: boolean;\n readonly currentSrc: string;\n readonly src: string;\n loading: string;\n addEventListener(type: string, listener: () => void, options?: { once?: boolean }): void;\n }>;\n };\n setTimeout(handler: () => void, ms: number): unknown;\n clearTimeout(handle: unknown): void;\n requestAnimationFrame(callback: () => void): unknown;\n}\n\n/** An element as a focus reset reads it: its own shadow tree's focus, and a way to let go. */\nexport interface PageFocusable {\n readonly shadowRoot?: { readonly activeElement: PageFocusable | null } | null;\n blur?(): void;\n}\n\n/** The two values of `prefers-reduced-motion` a browser can be told to report. */\nexport type MotionPreference = 'no-preference' | 'reduce';\n\n/**\n * What a trick asks of the browser driver, where neither a stylesheet nor a\n * script in the page can reach.\n *\n * A media preference, the clock and the pointer belong to the browser rather\n * than the document. A media query reads what the browser emulates, `Date` has\n * to be replaced before the page's first script reads it, and a hover ends only\n * when the pointer leaves. Declared as data rather than as a function, so a host\n * with no driver — a page agent, a test runner's own browser — can tell which\n * tricks it cannot honour, and the hosts that can drive agree on what each one\n * means. [`drive.ts`](./drive.ts) applies it.\n */\nexport interface DriverState {\n /** `prefers-reduced-motion: reduce`, emulated before the page loads. */\n readonly reducedMotion?: 'reduce';\n /** `Date` reads one fixed instant, in milliseconds, from before the page's first script. */\n readonly clock?: { readonly at: number };\n /** The pointer off the page, before the subject is read. */\n readonly pointer?: 'outside';\n}\n\n/**\n * The driver surface a {@link DriverState} is applied through.\n *\n * Structural, and the shape Playwright's `Page` already has, so no driver\n * package leaks into `core`.\n */\nexport interface DriverTarget {\n emulateMedia(options: { reducedMotion: MotionPreference }): Promise<void>;\n addInitScript(script: (frozen: number) => void, arg: number): Promise<unknown>;\n readonly mouse: { move(x: number, y: number): Promise<void> };\n}\n\n/** A rule as a settle step reads it: its declarations, the rules it groups, and the sheet it imports. */\ninterface PageCssRule {\n readonly style?: { getPropertyValue(property: string): string };\n readonly cssRules?: ArrayLike<PageCssRule>;\n readonly styleSheet?: { readonly cssRules: ArrayLike<PageCssRule> } | null;\n}\n\nexport interface Intervention {\n /**\n * Stable identifier, and part of the recipe's digest.\n *\n * Renaming one is therefore a new identity, which is correct: a reader\n * comparing two runs has no way to know that `hold-animations` and\n * `pause-animations` were the same trick.\n */\n readonly id: string;\n readonly trick: Trick;\n readonly needs: Tier;\n /** What it does and what it costs, in one sentence a report can print. */\n readonly because: string;\n\n /**\n * The property this trick governs.\n *\n * Two tricks claiming one subject are in conflict — pinning animations in CSS\n * while asking the compositor to fast-forward them applies both, and the CSS\n * wins silently. See {@link conflicts}.\n */\n readonly governs: string;\n\n readonly css?: string;\n readonly screenshot?: ScreenshotOptions;\n readonly settle?: (target: SettleTarget) => Promise<void>;\n\n /**\n * What the browser driver must do for this trick to hold.\n *\n * A trick with a `driver` is in force only where a host drove it: the hosts\n * that own the browser report the ids they applied, and the page drops the\n * rest from its recipe, so a reading never records a hold it did not have.\n * See `forDriver`.\n */\n readonly driver?: DriverState;\n}\n"]}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * The origin a page was served from, and a URL with that origin taken off.
3
+ *
4
+ * A collector serves a build on a free port, so the page's own origin is an
5
+ * accident of the run and never part of what was built. Every identity that
6
+ * holds a URL the page resolved — a `url()` in a computed value, a document's
7
+ * base and its resources — drops it through these two, so they agree on what
8
+ * counts as the page's own.
9
+ *
10
+ * `URL` answers, as it does for the engine that serialized `baseURI`. Only an
11
+ * `http` or `https` page has an origin to take off: `about:blank` and `file:`
12
+ * serialize theirs as `"null"`, and a string that does not parse has none.
13
+ */
14
+ export declare function pageOrigin(baseUrl: string | undefined): string | undefined;
15
+ /**
16
+ * `url` as a path when `origin` serves it, unchanged otherwise.
17
+ *
18
+ * Matched up to the slash that follows the authority, so `:400` never claims
19
+ * `:4001`, and a URL another origin serves keeps its origin.
20
+ */
21
+ export declare function withoutOrigin(url: string, origin: string | undefined): string;
22
+ /**
23
+ * The URL an asset key names, for looking up the bytes a document carries.
24
+ *
25
+ * The inverse of keying through {@link withoutOrigin}: a path the page's origin
26
+ * serves is resolved against the document's `baseUrl`, the URL the browser
27
+ * asked for and the resources are keyed by. An absolute key is returned as it
28
+ * is. A key that does not resolve, or a document with no base, answers the key
29
+ * unchanged, so the lookup that follows misses and says which key it missed.
30
+ */
31
+ export declare function assetUrl(key: string, baseUrl: string | undefined): string;
32
+ //# sourceMappingURL=origin.d.ts.map
@@ -0,0 +1,54 @@
1
+ /**
2
+ * The origin a page was served from, and a URL with that origin taken off.
3
+ *
4
+ * A collector serves a build on a free port, so the page's own origin is an
5
+ * accident of the run and never part of what was built. Every identity that
6
+ * holds a URL the page resolved — a `url()` in a computed value, a document's
7
+ * base and its resources — drops it through these two, so they agree on what
8
+ * counts as the page's own.
9
+ *
10
+ * `URL` answers, as it does for the engine that serialized `baseURI`. Only an
11
+ * `http` or `https` page has an origin to take off: `about:blank` and `file:`
12
+ * serialize theirs as `"null"`, and a string that does not parse has none.
13
+ */
14
+ export function pageOrigin(baseUrl) {
15
+ if (baseUrl === undefined)
16
+ return undefined;
17
+ let parsed;
18
+ try {
19
+ parsed = new URL(baseUrl);
20
+ }
21
+ catch {
22
+ return undefined;
23
+ }
24
+ return parsed.protocol === 'http:' || parsed.protocol === 'https:' ? parsed.origin : undefined;
25
+ }
26
+ /**
27
+ * `url` as a path when `origin` serves it, unchanged otherwise.
28
+ *
29
+ * Matched up to the slash that follows the authority, so `:400` never claims
30
+ * `:4001`, and a URL another origin serves keeps its origin.
31
+ */
32
+ export function withoutOrigin(url, origin) {
33
+ return origin !== undefined && url.startsWith(`${origin}/`) ? url.slice(origin.length) : url;
34
+ }
35
+ /**
36
+ * The URL an asset key names, for looking up the bytes a document carries.
37
+ *
38
+ * The inverse of keying through {@link withoutOrigin}: a path the page's origin
39
+ * serves is resolved against the document's `baseUrl`, the URL the browser
40
+ * asked for and the resources are keyed by. An absolute key is returned as it
41
+ * is. A key that does not resolve, or a document with no base, answers the key
42
+ * unchanged, so the lookup that follows misses and says which key it missed.
43
+ */
44
+ export function assetUrl(key, baseUrl) {
45
+ if (baseUrl === undefined)
46
+ return key;
47
+ try {
48
+ return new URL(key, baseUrl).toString();
49
+ }
50
+ catch {
51
+ return key;
52
+ }
53
+ }
54
+ //# sourceMappingURL=origin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"origin.js","sourceRoot":"","sources":["../../src/format/origin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,UAAU,CAAC,OAA2B;IACpD,IAAI,OAAO,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC5C,IAAI,MAA8D,CAAC;IACnE,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC;IAC5B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,MAAM,CAAC,QAAQ,KAAK,OAAO,IAAI,MAAM,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;AACjG,CAAC;AAOD;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,GAAW,EAAE,MAA0B;IACnE,OAAO,MAAM,KAAK,SAAS,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;AAC/F,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,QAAQ,CAAC,GAAW,EAAE,OAA2B;IAC/D,IAAI,OAAO,KAAK,SAAS;QAAE,OAAO,GAAG,CAAC;IACtC,IAAI,CAAC;QACH,OAAO,IAAI,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC1C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,GAAG,CAAC;IACb,CAAC;AACH,CAAC","sourcesContent":["/**\n * The origin a page was served from, and a URL with that origin taken off.\n *\n * A collector serves a build on a free port, so the page's own origin is an\n * accident of the run and never part of what was built. Every identity that\n * holds a URL the page resolved — a `url()` in a computed value, a document's\n * base and its resources — drops it through these two, so they agree on what\n * counts as the page's own.\n *\n * `URL` answers, as it does for the engine that serialized `baseURI`. Only an\n * `http` or `https` page has an origin to take off: `about:blank` and `file:`\n * serialize theirs as `\"null\"`, and a string that does not parse has none.\n */\nexport function pageOrigin(baseUrl: string | undefined): string | undefined {\n if (baseUrl === undefined) return undefined;\n let parsed: { readonly origin: string; readonly protocol: string };\n try {\n parsed = new URL(baseUrl);\n } catch {\n return undefined;\n }\n return parsed.protocol === 'http:' || parsed.protocol === 'https:' ? parsed.origin : undefined;\n}\n\n// `core` builds against ES2022 alone; every host it runs in has the WHATWG `URL`.\ndeclare const URL: {\n new (url: string, base?: string): { readonly origin: string; readonly protocol: string; toString(): string };\n};\n\n/**\n * `url` as a path when `origin` serves it, unchanged otherwise.\n *\n * Matched up to the slash that follows the authority, so `:400` never claims\n * `:4001`, and a URL another origin serves keeps its origin.\n */\nexport function withoutOrigin(url: string, origin: string | undefined): string {\n return origin !== undefined && url.startsWith(`${origin}/`) ? url.slice(origin.length) : url;\n}\n\n/**\n * The URL an asset key names, for looking up the bytes a document carries.\n *\n * The inverse of keying through {@link withoutOrigin}: a path the page's origin\n * serves is resolved against the document's `baseUrl`, the URL the browser\n * asked for and the resources are keyed by. An absolute key is returned as it\n * is. A key that does not resolve, or a document with no base, answers the key\n * unchanged, so the lookup that follows misses and says which key it missed.\n */\nexport function assetUrl(key: string, baseUrl: string | undefined): string {\n if (baseUrl === undefined) return key;\n try {\n return new URL(key, baseUrl).toString();\n } catch {\n return key;\n }\n}\n"]}
@@ -0,0 +1,32 @@
1
+ import type { SemanticSnapshot } from './snapshot.js';
2
+ /**
3
+ * A `SemanticSnapshot` as JSON, gzipped and in base64, after a header that
4
+ * names its format version.
5
+ *
6
+ * Text, so that it survives every hop a raster makes, as `Raster.bytes` does.
7
+ * `packSnapshot` and `unpackSnapshot` in `@variance-authority/observe` are the
8
+ * two ends; between them only {@link keepsSnapshot} reads it, and only its
9
+ * header.
10
+ */
11
+ export type PackedSnapshot = string;
12
+ /**
13
+ * The format version this code reads, typed from `SemanticSnapshot`, so that it
14
+ * stops compiling when the type moves. The header and `isSemanticSnapshot` both
15
+ * read it; `@variance-authority/core/format` does not export it.
16
+ */
17
+ export declare const FORMAT_VERSION: SemanticSnapshot['formatVersion'];
18
+ /** What a packed snapshot of the format this code reads opens with: its version, outside the gzip. */
19
+ export declare const PACKED_SNAPSHOT_HEADER: string;
20
+ /**
21
+ * Whether a raster kept a snapshot this code can read.
22
+ *
23
+ * Read off the header, without unpacking, so that every store gives the same
24
+ * answer, tribunal's inside a Worker with no `node:zlib` too. A snapshot of
25
+ * another format is one this code cannot read, and a baseline that keeps only
26
+ * that keeps none: `settle` renders it, for an accepting run to record one.
27
+ *
28
+ * The header cannot vouch for what follows it. Text damaged after the header
29
+ * is counted as kept here and reads as not recorded where it is unpacked.
30
+ */
31
+ export declare function keepsSnapshot(packed: PackedSnapshot | undefined): boolean;
32
+ //# sourceMappingURL=packed.d.ts.map
@@ -0,0 +1,23 @@
1
+ /**
2
+ * The format version this code reads, typed from `SemanticSnapshot`, so that it
3
+ * stops compiling when the type moves. The header and `isSemanticSnapshot` both
4
+ * read it; `@variance-authority/core/format` does not export it.
5
+ */
6
+ export const FORMAT_VERSION = 1;
7
+ /** What a packed snapshot of the format this code reads opens with: its version, outside the gzip. */
8
+ export const PACKED_SNAPSHOT_HEADER = `v${FORMAT_VERSION}:`;
9
+ /**
10
+ * Whether a raster kept a snapshot this code can read.
11
+ *
12
+ * Read off the header, without unpacking, so that every store gives the same
13
+ * answer, tribunal's inside a Worker with no `node:zlib` too. A snapshot of
14
+ * another format is one this code cannot read, and a baseline that keeps only
15
+ * that keeps none: `settle` renders it, for an accepting run to record one.
16
+ *
17
+ * The header cannot vouch for what follows it. Text damaged after the header
18
+ * is counted as kept here and reads as not recorded where it is unpacked.
19
+ */
20
+ export function keepsSnapshot(packed) {
21
+ return packed !== undefined && packed.startsWith(PACKED_SNAPSHOT_HEADER);
22
+ }
23
+ //# sourceMappingURL=packed.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"packed.js","sourceRoot":"","sources":["../../src/format/packed.ts"],"names":[],"mappings":"AAaA;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAsC,CAAC,CAAC;AAEnE,sGAAsG;AACtG,MAAM,CAAC,MAAM,sBAAsB,GAAG,IAAI,cAAc,GAAG,CAAC;AAE5D;;;;;;;;;;GAUG;AACH,MAAM,UAAU,aAAa,CAAC,MAAkC;IAC9D,OAAO,MAAM,KAAK,SAAS,IAAI,MAAM,CAAC,UAAU,CAAC,sBAAsB,CAAC,CAAC;AAC3E,CAAC","sourcesContent":["import type { SemanticSnapshot } from './snapshot.js';\n\n/**\n * A `SemanticSnapshot` as JSON, gzipped and in base64, after a header that\n * names its format version.\n *\n * Text, so that it survives every hop a raster makes, as `Raster.bytes` does.\n * `packSnapshot` and `unpackSnapshot` in `@variance-authority/observe` are the\n * two ends; between them only {@link keepsSnapshot} reads it, and only its\n * header.\n */\nexport type PackedSnapshot = string;\n\n/**\n * The format version this code reads, typed from `SemanticSnapshot`, so that it\n * stops compiling when the type moves. The header and `isSemanticSnapshot` both\n * read it; `@variance-authority/core/format` does not export it.\n */\nexport const FORMAT_VERSION: SemanticSnapshot['formatVersion'] = 1;\n\n/** What a packed snapshot of the format this code reads opens with: its version, outside the gzip. */\nexport const PACKED_SNAPSHOT_HEADER = `v${FORMAT_VERSION}:`;\n\n/**\n * Whether a raster kept a snapshot this code can read.\n *\n * Read off the header, without unpacking, so that every store gives the same\n * answer, tribunal's inside a Worker with no `node:zlib` too. A snapshot of\n * another format is one this code cannot read, and a baseline that keeps only\n * that keeps none: `settle` renders it, for an accepting run to record one.\n *\n * The header cannot vouch for what follows it. Text damaged after the header\n * is counted as kept here and reads as not recorded where it is unpacked.\n */\nexport function keepsSnapshot(packed: PackedSnapshot | undefined): boolean {\n return packed !== undefined && packed.startsWith(PACKED_SNAPSHOT_HEADER);\n}\n"]}
@@ -0,0 +1,31 @@
1
+ import type { SubjectRef } from './capture.js';
2
+ import type { RenderDocument } from './document.js';
3
+ import type { SemanticSnapshot } from './snapshot.js';
4
+ /**
5
+ * Whether a value read back from a file or a store is the format it claims.
6
+ *
7
+ * Read, not believed: a value parsed from JSON is `unknown` until one of these
8
+ * says otherwise, and each checks every field its type declares, so that there
9
+ * is one answer to what a snapshot, a document or a subject is. What a reader
10
+ * can re-derive, a digest against the bytes it names, is the reader's to check.
11
+ */
12
+ /**
13
+ * Whether a value is a `SemanticSnapshot`, every node down to the leaves.
14
+ *
15
+ * The readers walk all of it: the matcher pairs every child, and a node that is
16
+ * not one throws there instead of reading as a snapshot that is not recorded.
17
+ * A capture file and a packed snapshot are the two places one is read back.
18
+ * Whose snapshot it is, against the subject it was filed under, is the
19
+ * reader's question.
20
+ */
21
+ export declare function isSemanticSnapshot(value: unknown): value is SemanticSnapshot;
22
+ /**
23
+ * Whether a value is a `RenderDocument`, its resources' shape included.
24
+ *
25
+ * Shape only: that a resource's bytes are base64 and hash to its digest, and
26
+ * that a document is resource-closed, are what a reader that needs them checks.
27
+ */
28
+ export declare function isRenderDocument(value: unknown): value is RenderDocument;
29
+ /** Whether a value is a `SubjectRef`: an id, one of the four kinds, and an optional title. */
30
+ export declare function isSubjectRef(value: unknown): value is SubjectRef;
31
+ //# sourceMappingURL=shape.d.ts.map