@jomae/catalog 0.9.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. package/dist/baseline.d.ts +41 -2
  2. package/dist/baseline.d.ts.map +1 -1
  3. package/dist/baseline.js +52 -10
  4. package/dist/baseline.js.map +1 -1
  5. package/dist/detect.d.ts +26 -0
  6. package/dist/detect.d.ts.map +1 -1
  7. package/dist/detect.js +27 -1
  8. package/dist/detect.js.map +1 -1
  9. package/dist/generated/baseline-history.d.ts.map +1 -1
  10. package/dist/generated/baseline-history.js +16 -4
  11. package/dist/generated/baseline-history.js.map +1 -1
  12. package/dist/generated/baseline.d.ts.map +1 -1
  13. package/dist/generated/baseline.js +301 -37
  14. package/dist/generated/baseline.js.map +1 -1
  15. package/dist/generated/guides.d.ts.map +1 -1
  16. package/dist/generated/guides.js +22 -3
  17. package/dist/generated/guides.js.map +1 -1
  18. package/dist/generated/sizes.d.ts.map +1 -1
  19. package/dist/generated/sizes.js +284 -29
  20. package/dist/generated/sizes.js.map +1 -1
  21. package/dist/generated/support-claims.d.ts.map +1 -1
  22. package/dist/generated/support-claims.js +28 -3
  23. package/dist/generated/support-claims.js.map +1 -1
  24. package/dist/index.d.ts +3 -2
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +3 -2
  27. package/dist/index.js.map +1 -1
  28. package/dist/rules/color-functions.d.ts.map +1 -1
  29. package/dist/rules/color-functions.js +4 -0
  30. package/dist/rules/color-functions.js.map +1 -1
  31. package/dist/rules/container-queries.d.ts.map +1 -1
  32. package/dist/rules/container-queries.js +1 -0
  33. package/dist/rules/container-queries.js.map +1 -1
  34. package/dist/rules/cookie-store.d.ts +3 -0
  35. package/dist/rules/cookie-store.d.ts.map +1 -0
  36. package/dist/rules/cookie-store.js +38 -0
  37. package/dist/rules/cookie-store.js.map +1 -0
  38. package/dist/rules/css-has.d.ts +3 -0
  39. package/dist/rules/css-has.d.ts.map +1 -0
  40. package/dist/rules/css-has.js +35 -0
  41. package/dist/rules/css-has.js.map +1 -0
  42. package/dist/rules/css-nesting.d.ts +3 -0
  43. package/dist/rules/css-nesting.d.ts.map +1 -0
  44. package/dist/rules/css-nesting.js +35 -0
  45. package/dist/rules/css-nesting.js.map +1 -0
  46. package/dist/rules/date-format.d.ts +3 -0
  47. package/dist/rules/date-format.d.ts.map +1 -0
  48. package/dist/rules/date-format.js +33 -0
  49. package/dist/rules/date-format.js.map +1 -0
  50. package/dist/rules/diacritics.d.ts +3 -0
  51. package/dist/rules/diacritics.d.ts.map +1 -0
  52. package/dist/rules/diacritics.js +34 -0
  53. package/dist/rules/diacritics.js.map +1 -0
  54. package/dist/rules/display-names.d.ts.map +1 -1
  55. package/dist/rules/display-names.js +4 -0
  56. package/dist/rules/display-names.js.map +1 -1
  57. package/dist/rules/drag-and-drop.d.ts.map +1 -1
  58. package/dist/rules/drag-and-drop.js +4 -0
  59. package/dist/rules/drag-and-drop.js.map +1 -1
  60. package/dist/rules/fetch.d.ts.map +1 -1
  61. package/dist/rules/fetch.js +4 -0
  62. package/dist/rules/fetch.js.map +1 -1
  63. package/dist/rules/field-sizing.d.ts.map +1 -1
  64. package/dist/rules/field-sizing.js +4 -0
  65. package/dist/rules/field-sizing.js.map +1 -1
  66. package/dist/rules/file-download.d.ts +3 -0
  67. package/dist/rules/file-download.d.ts.map +1 -0
  68. package/dist/rules/file-download.js +40 -0
  69. package/dist/rules/file-download.js.map +1 -0
  70. package/dist/rules/file-drop.d.ts +3 -0
  71. package/dist/rules/file-drop.d.ts.map +1 -0
  72. package/dist/rules/file-drop.js +40 -0
  73. package/dist/rules/file-drop.js.map +1 -0
  74. package/dist/rules/font-loading.d.ts +3 -0
  75. package/dist/rules/font-loading.d.ts.map +1 -0
  76. package/dist/rules/font-loading.js +33 -0
  77. package/dist/rules/font-loading.js.map +1 -0
  78. package/dist/rules/form-validation.d.ts +3 -0
  79. package/dist/rules/form-validation.d.ts.map +1 -0
  80. package/dist/rules/form-validation.js +40 -0
  81. package/dist/rules/form-validation.js.map +1 -0
  82. package/dist/rules/index.d.ts.map +1 -1
  83. package/dist/rules/index.js +34 -0
  84. package/dist/rules/index.js.map +1 -1
  85. package/dist/rules/inert.d.ts.map +1 -1
  86. package/dist/rules/inert.js +2 -0
  87. package/dist/rules/inert.js.map +1 -1
  88. package/dist/rules/jquery.d.ts +3 -0
  89. package/dist/rules/jquery.d.ts.map +1 -0
  90. package/dist/rules/jquery.js +37 -0
  91. package/dist/rules/jquery.js.map +1 -0
  92. package/dist/rules/light-dark.d.ts +3 -0
  93. package/dist/rules/light-dark.d.ts.map +1 -0
  94. package/dist/rules/light-dark.js +37 -0
  95. package/dist/rules/light-dark.js.map +1 -0
  96. package/dist/rules/list-format.d.ts.map +1 -1
  97. package/dist/rules/list-format.js +3 -0
  98. package/dist/rules/list-format.js.map +1 -1
  99. package/dist/rules/match-media.d.ts +3 -0
  100. package/dist/rules/match-media.d.ts.map +1 -0
  101. package/dist/rules/match-media.js +36 -0
  102. package/dist/rules/match-media.js.map +1 -0
  103. package/dist/rules/number-format.d.ts.map +1 -1
  104. package/dist/rules/number-format.js +10 -1
  105. package/dist/rules/number-format.js.map +1 -1
  106. package/dist/rules/passkeys.d.ts +3 -0
  107. package/dist/rules/passkeys.d.ts.map +1 -0
  108. package/dist/rules/passkeys.js +44 -0
  109. package/dist/rules/passkeys.js.map +1 -0
  110. package/dist/rules/plural-rules.d.ts +3 -0
  111. package/dist/rules/plural-rules.d.ts.map +1 -0
  112. package/dist/rules/plural-rules.js +33 -0
  113. package/dist/rules/plural-rules.js.map +1 -0
  114. package/dist/rules/pointer-events.d.ts +3 -0
  115. package/dist/rules/pointer-events.d.ts.map +1 -0
  116. package/dist/rules/pointer-events.js +43 -0
  117. package/dist/rules/pointer-events.js.map +1 -0
  118. package/dist/rules/progress-indicator.d.ts +3 -0
  119. package/dist/rules/progress-indicator.d.ts.map +1 -0
  120. package/dist/rules/progress-indicator.js +39 -0
  121. package/dist/rules/progress-indicator.js.map +1 -0
  122. package/dist/rules/screen-wake-lock.d.ts.map +1 -1
  123. package/dist/rules/screen-wake-lock.js +4 -0
  124. package/dist/rules/screen-wake-lock.js.map +1 -1
  125. package/dist/rules/scroll-into-view.d.ts +3 -0
  126. package/dist/rules/scroll-into-view.d.ts.map +1 -0
  127. package/dist/rules/scroll-into-view.js +38 -0
  128. package/dist/rules/scroll-into-view.js.map +1 -0
  129. package/dist/rules/segmenter.d.ts.map +1 -1
  130. package/dist/rules/segmenter.js +4 -0
  131. package/dist/rules/segmenter.js.map +1 -1
  132. package/dist/rules/select.d.ts.map +1 -1
  133. package/dist/rules/select.js +1 -0
  134. package/dist/rules/select.js.map +1 -1
  135. package/dist/rules/server-sent-events.d.ts.map +1 -1
  136. package/dist/rules/server-sent-events.js +4 -0
  137. package/dist/rules/server-sent-events.js.map +1 -1
  138. package/dist/rules/speculation-rules.d.ts +3 -0
  139. package/dist/rules/speculation-rules.d.ts.map +1 -0
  140. package/dist/rules/speculation-rules.js +35 -0
  141. package/dist/rules/speculation-rules.js.map +1 -0
  142. package/dist/rules/temporal.d.ts +3 -0
  143. package/dist/rules/temporal.d.ts.map +1 -0
  144. package/dist/rules/temporal.js +34 -0
  145. package/dist/rules/temporal.js.map +1 -0
  146. package/dist/rules/url-pattern.d.ts.map +1 -1
  147. package/dist/rules/url-pattern.js +4 -0
  148. package/dist/rules/url-pattern.js.map +1 -1
  149. package/dist/rules/web-animations.d.ts.map +1 -1
  150. package/dist/rules/web-animations.js +4 -0
  151. package/dist/rules/web-animations.js.map +1 -1
  152. package/dist/rules/web-crypto.d.ts.map +1 -1
  153. package/dist/rules/web-crypto.js +4 -0
  154. package/dist/rules/web-crypto.js.map +1 -1
  155. package/dist/rules/web-locks.d.ts.map +1 -1
  156. package/dist/rules/web-locks.js +4 -0
  157. package/dist/rules/web-locks.js.map +1 -1
  158. package/dist/tier-diff.d.ts +55 -0
  159. package/dist/tier-diff.d.ts.map +1 -0
  160. package/dist/tier-diff.js +61 -0
  161. package/dist/tier-diff.js.map +1 -0
  162. package/package.json +1 -1
@@ -0,0 +1,36 @@
1
+ export const matchMedia = {
2
+ id: "match-media",
3
+ title: "Reacting to a breakpoint",
4
+ category: "layout",
5
+ replaces: ["react-responsive", "react-media", "use-media", "enquire.js"],
6
+ featureIds: ["matchmedia"],
7
+ native: "matchMedia()",
8
+ human: {
9
+ explainer: "These libraries wrap one method. matchMedia() takes the same query string a stylesheet would, reports whether it matches now, and fires an event when that changes, with no resize listener and no width arithmetic. Most uses of them are styling rather than logic, and a media or container query in CSS handles those without JavaScript running at all.",
10
+ snippet: `const wide = matchMedia("(min-width: 48rem)");
11
+
12
+ function apply(event) {
13
+ // Same shape for the initial read and every later change.
14
+ setColumns(event.matches ? 3 : 1);
15
+ }
16
+
17
+ apply(wide);
18
+ wide.addEventListener("change", apply);`,
19
+ mdnUrl: "https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia",
20
+ },
21
+ agent: {
22
+ when: "JavaScript needs to know whether a media query currently matches",
23
+ unless: [
24
+ "You server-render and need the first paint to be right. matchMedia does not exist on the server and the client's first read happens after hydration, so a wrong first frame or a mismatch warning is the failure mode these libraries put real work into avoiding.",
25
+ "You are changing appearance rather than behaviour. A media query or a container query does that in CSS, which is fewer moving parts than either the library or this API.",
26
+ "You query an element's size rather than the viewport's. matchMedia only answers about the viewport and the device, so a component that responds to its own box needs a container query or ResizeObserver.",
27
+ "The library's other features are in use, such as react-responsive's device-property shorthands or its context-based overriding for tests.",
28
+ ],
29
+ snippet: 'matchMedia("(min-width: 48rem)").matches;',
30
+ handRolled: [
31
+ "a resize listener comparing window.innerWidth against a breakpoint number kept in JavaScript",
32
+ "an isMobile flag held in state and recomputed from innerWidth inside a debounced resize handler",
33
+ ],
34
+ },
35
+ };
36
+ //# sourceMappingURL=match-media.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"match-media.js","sourceRoot":"","sources":["../../src/rules/match-media.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,UAAU,GAAS;IAC9B,EAAE,EAAE,aAAa;IACjB,KAAK,EAAE,0BAA0B;IACjC,QAAQ,EAAE,QAAQ;IAClB,QAAQ,EAAE,CAAC,kBAAkB,EAAE,aAAa,EAAE,WAAW,EAAE,YAAY,CAAC;IACxE,UAAU,EAAE,CAAC,YAAY,CAAC;IAC1B,MAAM,EAAE,cAAc;IACtB,KAAK,EAAE;QACL,SAAS,EACP,8VAA8V;QAChW,OAAO,EAAE;;;;;;;;wCAQ2B;QACpC,MAAM,EACJ,oEAAoE;KACvE;IACD,KAAK,EAAE;QACL,IAAI,EAAE,kEAAkE;QACxE,MAAM,EAAE;YACN,oQAAoQ;YACpQ,0KAA0K;YAC1K,2MAA2M;YAC3M,2IAA2I;SAC5I;QACD,OAAO,EAAE,2CAA2C;QACpD,UAAU,EAAE;YACV,8FAA8F;YAC9F,iGAAiG;SAClG;KACF;CACF,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"number-format.d.ts","sourceRoot":"","sources":["../../src/rules/number-format.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEzC,eAAO,MAAM,YAAY,EAAE,IAgC1B,CAAC"}
1
+ {"version":3,"file":"number-format.d.ts","sourceRoot":"","sources":["../../src/rules/number-format.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEzC,eAAO,MAAM,YAAY,EAAE,IAyC1B,CAAC"}
@@ -2,7 +2,14 @@ export const numberFormat = {
2
2
  id: "number-format",
3
3
  title: "Locale-aware number and currency formatting",
4
4
  category: "formatting",
5
- replaces: ["numeral", "accounting", "currency.js", "format-number"],
5
+ replaces: [
6
+ "numeral",
7
+ "accounting",
8
+ "currency.js",
9
+ "format-number",
10
+ "pretty-bytes",
11
+ "filesize",
12
+ ],
6
13
  featureIds: ["intl"],
7
14
  native: "Intl.NumberFormat",
8
15
  human: {
@@ -19,6 +26,8 @@ export const numberFormat = {
19
26
  'You format units or compact notation. style: "unit" and notation: "compact" arrived long after the rest of Intl.NumberFormat, in Chrome {{chrome:javascript.builtins.Intl.NumberFormat.NumberFormat.options_parameter.options_unit_parameter}}, Firefox {{firefox:javascript.builtins.Intl.NumberFormat.NumberFormat.options_parameter.options_unit_parameter}} and Safari {{safari:javascript.builtins.Intl.NumberFormat.NumberFormat.options_parameter.options_unit_parameter}}, so an older target does not get them.',
20
27
  "You need safe decimal arithmetic, such as adding money values without floating-point rounding errors. currency.js and accounting.js do that math for you; Intl.NumberFormat only formats a number you already computed correctly.",
21
28
  "You need to parse a formatted string back into a number. Intl.NumberFormat is format-only; these libraries often provide the reverse direction too.",
29
+ 'You format byte sizes and need the unit chosen for you. Intl.NumberFormat formats a magnitude you already picked, so "kilobyte" on 1200 prints 1,200 kB rather than 1.2 MB, and dividing down to the right unit first is the part pretty-bytes and filesize actually do.',
30
+ "You need binary units. The sanctioned unit list has no kibibyte, mebibyte or gibibyte, and asking for one throws a RangeError rather than falling back, so anything reporting KiB or MiB keeps its library.",
22
31
  "You need a custom format pattern, such as a specific abbreviation style, that the options Intl.NumberFormat exposes cannot express.",
23
32
  ],
24
33
  snippet: 'new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" }).format(value);',
@@ -1 +1 @@
1
- {"version":3,"file":"number-format.js","sourceRoot":"","sources":["../../src/rules/number-format.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,YAAY,GAAS;IAChC,EAAE,EAAE,eAAe;IACnB,KAAK,EAAE,6CAA6C;IACpD,QAAQ,EAAE,YAAY;IACtB,QAAQ,EAAE,CAAC,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,eAAe,CAAC;IACnE,UAAU,EAAE,CAAC,MAAM,CAAC;IACpB,MAAM,EAAE,mBAAmB;IAC3B,KAAK,EAAE;QACL,SAAS,EACP,wZAAwZ;QAC1Z,OAAO,EAAE;;;kCAGqB;QAC9B,MAAM,EACJ,oGAAoG;KACvG;IACD,KAAK,EAAE;QACL,IAAI,EAAE,yGAAyG;QAC/G,MAAM,EAAE;YACN,0fAA0f;YAC1f,mOAAmO;YACnO,qJAAqJ;YACrJ,qIAAqI;SACtI;QACD,OAAO,EACL,uFAAuF;QACzF,UAAU,EAAE;YACV,oEAAoE;YACpE,kFAAkF;SACnF;KACF;CACF,CAAC"}
1
+ {"version":3,"file":"number-format.js","sourceRoot":"","sources":["../../src/rules/number-format.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,YAAY,GAAS;IAChC,EAAE,EAAE,eAAe;IACnB,KAAK,EAAE,6CAA6C;IACpD,QAAQ,EAAE,YAAY;IACtB,QAAQ,EAAE;QACR,SAAS;QACT,YAAY;QACZ,aAAa;QACb,eAAe;QACf,cAAc;QACd,UAAU;KACX;IACD,UAAU,EAAE,CAAC,MAAM,CAAC;IACpB,MAAM,EAAE,mBAAmB;IAC3B,KAAK,EAAE;QACL,SAAS,EACP,wZAAwZ;QAC1Z,OAAO,EAAE;;;kCAGqB;QAC9B,MAAM,EACJ,oGAAoG;KACvG;IACD,KAAK,EAAE;QACL,IAAI,EAAE,yGAAyG;QAC/G,MAAM,EAAE;YACN,0fAA0f;YAC1f,mOAAmO;YACnO,qJAAqJ;YACrJ,0QAA0Q;YAC1Q,6MAA6M;YAC7M,qIAAqI;SACtI;QACD,OAAO,EACL,uFAAuF;QACzF,UAAU,EAAE;YACV,oEAAoE;YACpE,kFAAkF;SACnF;KACF;CACF,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Rule } from "../schema.ts";
2
+ export declare const passkeys: Rule;
3
+ //# sourceMappingURL=passkeys.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"passkeys.d.ts","sourceRoot":"","sources":["../../src/rules/passkeys.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEzC,eAAO,MAAM,QAAQ,EAAE,IA6CtB,CAAC"}
@@ -0,0 +1,44 @@
1
+ export const passkeys = {
2
+ id: "passkeys",
3
+ title: "Passkeys in the browser",
4
+ category: "device-apis",
5
+ replaces: ["@simplewebauthn/browser"],
6
+ featureIds: [],
7
+ manualBaseline: {
8
+ status: "newly",
9
+ verifiedOn: "2026-09-20",
10
+ note: "web-features has no ID for the JSON helpers this rule turns on, and they sit inside the 'webauthn' feature's own compat data rather than beside it. Deriving from that ID would badge this widely available on the ceremony's dates, Safari {{safari:webauthn}}, while the parseCreationOptionsFromJSON and toJSON calls that remove the need for a wrapper arrived in Safari {{safari:api.PublicKeyCredential.parseCreationOptionsFromJSON_static}}, Chrome {{chrome:api.PublicKeyCredential.parseCreationOptionsFromJSON_static}} and Firefox {{firefox:api.PublicKeyCredential.parseCreationOptionsFromJSON_static}}. Newly available on the helpers' own dates: the last engine shipped them in March 2025.",
11
+ },
12
+ native: "navigator.credentials with PublicKeyCredential",
13
+ human: {
14
+ explainer: "The browser half of a passkey flow is two calls: create() to register a credential and get() to sign a challenge. The reason a wrapper existed is that both used to need every challenge and id field converted to and from base64url by hand. parseCreationOptionsFromJSON and toJSON now do that conversion in the browser, so the server's JSON goes in and the response comes back out in the same shape.",
15
+ snippet: `// The server sends JSON; the browser parses it into the
16
+ // ArrayBuffer-shaped options the call expects.
17
+ const options = PublicKeyCredential.parseCreationOptionsFromJSON(fromServer);
18
+ const credential = await navigator.credentials.create({
19
+ publicKey: options,
20
+ });
21
+
22
+ // And back to JSON for the response, without hand-rolled base64url.
23
+ await fetch("/register", {
24
+ method: "POST",
25
+ body: JSON.stringify(credential.toJSON()),
26
+ });`,
27
+ mdnUrl: "https://developer.mozilla.org/en-US/docs/Web/API/Web_Authentication_API",
28
+ },
29
+ agent: {
30
+ when: "registering or authenticating a passkey from the browser",
31
+ unless: [
32
+ "Your support target reaches below Chrome {{chrome:api.PublicKeyCredential.parseCreationOptionsFromJSON_static}}, Firefox {{firefox:api.PublicKeyCredential.parseCreationOptionsFromJSON_static}} or Safari {{safari:api.PublicKeyCredential.parseCreationOptionsFromJSON_static}}. The ceremony itself is years older than those, so the credential calls work while the JSON helpers that make the wrapper unnecessary do not, and the base64url conversion comes back.",
33
+ "You use the library's server package as well. @simplewebauthn/server generates and verifies the challenge, and the platform has no counterpart for that: this covers the browser side only.",
34
+ "You rely on the library's browser-capability helpers, such as its checks for a platform authenticator or for conditional UI, which are feature detection it has already written.",
35
+ "You need the same code path on a browser where conditional mediation behaves differently, which the library smooths over and the raw call does not.",
36
+ ],
37
+ snippet: "const credential = await navigator.credentials.get({ publicKey: options });",
38
+ handRolled: [
39
+ "base64url encode and decode helpers written next to a credentials.create call to convert challenge and id fields",
40
+ "a Uint8Array built by looping over atob output to turn a server challenge into bytes",
41
+ ],
42
+ },
43
+ };
44
+ //# sourceMappingURL=passkeys.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"passkeys.js","sourceRoot":"","sources":["../../src/rules/passkeys.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,QAAQ,GAAS;IAC5B,EAAE,EAAE,UAAU;IACd,KAAK,EAAE,yBAAyB;IAChC,QAAQ,EAAE,aAAa;IACvB,QAAQ,EAAE,CAAC,yBAAyB,CAAC;IACrC,UAAU,EAAE,EAAE;IACd,cAAc,EAAE;QACd,MAAM,EAAE,OAAO;QACf,UAAU,EAAE,YAAY;QACxB,IAAI,EAAE,irBAAirB;KACxrB;IACD,MAAM,EAAE,gDAAgD;IACxD,KAAK,EAAE;QACL,SAAS,EACP,+YAA+Y;QACjZ,OAAO,EAAE;;;;;;;;;;;IAWT;QACA,MAAM,EACJ,yEAAyE;KAC5E;IACD,KAAK,EAAE;QACL,IAAI,EAAE,0DAA0D;QAChE,MAAM,EAAE;YACN,0cAA0c;YAC1c,6LAA6L;YAC7L,kLAAkL;YAClL,qJAAqJ;SACtJ;QACD,OAAO,EACL,6EAA6E;QAC/E,UAAU,EAAE;YACV,kHAAkH;YAClH,sFAAsF;SACvF;KACF;CACF,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Rule } from "../schema.ts";
2
+ export declare const pluralRules: Rule;
3
+ //# sourceMappingURL=plural-rules.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plural-rules.d.ts","sourceRoot":"","sources":["../../src/rules/plural-rules.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEzC,eAAO,MAAM,WAAW,EAAE,IAkCzB,CAAC"}
@@ -0,0 +1,33 @@
1
+ export const pluralRules = {
2
+ id: "plural-rules",
3
+ title: "Choosing a plural form",
4
+ category: "formatting",
5
+ replaces: ["pluralize"],
6
+ featureIds: ["intl-plural-rules"],
7
+ native: "Intl.PluralRules",
8
+ human: {
9
+ explainer: 'Picking between "1 file" and "2 files" by testing count === 1 is only correct in English, and barely. Russian needs one form for 3 and another for 5, and Polish and Arabic have more. Intl.PluralRules answers which category a number falls into for a locale, so the branching stops being a guess. It returns the category rather than the word, which is the part worth knowing before reaching for it.',
10
+ snippet: `const pr = new Intl.PluralRules("en-US");
11
+ const forms = { one: "file", other: "files" };
12
+ \`\${count} \${forms[pr.select(count)]}\`; // "1 file", "2 files"
13
+
14
+ // Ordinals are the same API with a different type.
15
+ new Intl.PluralRules("en-US", { type: "ordinal" }).select(22); // "two"`,
16
+ mdnUrl: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/PluralRules",
17
+ },
18
+ agent: {
19
+ when: "choosing between singular and plural wording for a count",
20
+ unless: [
21
+ 'You need the plural word itself. Intl.PluralRules returns a category such as "one" or "other" and never inflects anything, so "person" to "people" and "index" to "indices" still need pluralize or a table of your own. This is the difference that decides most cases.',
22
+ "You pluralise arbitrary nouns you do not control, such as user-supplied or database-driven labels. A category is only useful when you already hold both forms, which you cannot if the noun is unknown at build time.",
23
+ "You singularise as well as pluralise. pluralize goes both directions and this API goes neither.",
24
+ "Your strings already run through an ICU message catalogue. It has plural selection built in and doing it twice is how the two disagree.",
25
+ ],
26
+ snippet: 'new Intl.PluralRules(locale).select(count); // "one" | "other" | ...',
27
+ handRolled: [
28
+ 'a count === 1 ternary picking between two words, or a bare + "s" appended to a noun',
29
+ "a lookup of irregular plurals kept next to the component that renders them",
30
+ ],
31
+ },
32
+ };
33
+ //# sourceMappingURL=plural-rules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plural-rules.js","sourceRoot":"","sources":["../../src/rules/plural-rules.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,WAAW,GAAS;IAC/B,EAAE,EAAE,cAAc;IAClB,KAAK,EAAE,wBAAwB;IAC/B,QAAQ,EAAE,YAAY;IACtB,QAAQ,EAAE,CAAC,WAAW,CAAC;IACvB,UAAU,EAAE,CAAC,mBAAmB,CAAC;IACjC,MAAM,EAAE,kBAAkB;IAC1B,KAAK,EAAE;QACL,SAAS,EACP,8YAA8Y;QAChZ,OAAO,EAAE;;;;;wEAK2D;QACpE,MAAM,EACJ,mGAAmG;KACtG;IACD,KAAK,EAAE;QACL,IAAI,EAAE,0DAA0D;QAChE,MAAM,EAAE;YACN,0QAA0Q;YAC1Q,uNAAuN;YACvN,iGAAiG;YACjG,yIAAyI;SAC1I;QACD,OAAO,EACL,sEAAsE;QACxE,UAAU,EAAE;YACV,qFAAqF;YACrF,4EAA4E;SAC7E;KACF;CACF,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Rule } from "../schema.ts";
2
+ export declare const pointerEvents: Rule;
3
+ //# sourceMappingURL=pointer-events.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pointer-events.d.ts","sourceRoot":"","sources":["../../src/rules/pointer-events.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEzC,eAAO,MAAM,aAAa,EAAE,IA2C3B,CAAC"}
@@ -0,0 +1,43 @@
1
+ export const pointerEvents = {
2
+ id: "pointer-events",
3
+ title: "Touch and pointer gestures",
4
+ category: "device-apis",
5
+ replaces: ["hammerjs", "react-swipeable", "swiped-events"],
6
+ featureIds: ["pointer-events-api"],
7
+ native: "pointer events with touch-action",
8
+ human: {
9
+ explainer: "hammerjs was written when mouse and touch were separate event streams that had to be normalised, and it has not been maintained since 2016. Pointer events deliver both through one set of handlers, with pointerId to tell fingers apart and setPointerCapture so a drag keeps reporting after the pointer leaves the element. A swipe is a pointerdown coordinate, a pointerup coordinate, and a threshold.",
10
+ snippet: `let startX = 0;
11
+
12
+ el.addEventListener("pointerdown", (event) => {
13
+ startX = event.clientX;
14
+ // Keeps events coming even if the pointer leaves the element.
15
+ el.setPointerCapture(event.pointerId);
16
+ });
17
+
18
+ el.addEventListener("pointerup", (event) => {
19
+ const dx = event.clientX - startX;
20
+ if (Math.abs(dx) > 50) onSwipe(dx > 0 ? "right" : "left");
21
+ });
22
+
23
+ // Tell the browser which direction you are handling, so it keeps
24
+ // vertical scrolling for itself. In CSS: touch-action: pan-y.
25
+ el.style.touchAction = "pan-y";`,
26
+ mdnUrl: "https://developer.mozilla.org/en-US/docs/Web/API/Pointer_events",
27
+ },
28
+ agent: {
29
+ when: "handling swipes, drags or other pointer gestures",
30
+ unless: [
31
+ "You need multi-touch recognisers such as pinch to zoom or two-finger rotate. Tracking several pointerIds and deriving the transform between them is real geometry, and it is most of what hammerjs is.",
32
+ "You need velocity, direction and threshold tuning that already feels right. A naive distance check fires on a slow drag that was never meant as a swipe, and the tuned version is the library's actual value.",
33
+ "You rely on hammerjs recognisers with conflict resolution between them, such as pan and swipe on the same element, where the ordering is configured rather than written.",
34
+ "Your support target reaches below Chrome {{chrome:pointer-events-api}}, Firefox {{firefox:pointer-events-api}} or Safari {{safari:pointer-events-api}}, which came to pointer events considerably later than the others.",
35
+ ],
36
+ snippet: 'el.addEventListener("pointerdown", (e) => el.setPointerCapture(e.pointerId));',
37
+ handRolled: [
38
+ "paired touchstart and touchend listeners reading changedTouches[0].clientX to work out a swipe direction",
39
+ "separate mousedown and touchstart branches doing the same thing, written because the two event streams disagree",
40
+ ],
41
+ },
42
+ };
43
+ //# sourceMappingURL=pointer-events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pointer-events.js","sourceRoot":"","sources":["../../src/rules/pointer-events.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,aAAa,GAAS;IACjC,EAAE,EAAE,gBAAgB;IACpB,KAAK,EAAE,4BAA4B;IACnC,QAAQ,EAAE,aAAa;IACvB,QAAQ,EAAE,CAAC,UAAU,EAAE,iBAAiB,EAAE,eAAe,CAAC;IAC1D,UAAU,EAAE,CAAC,oBAAoB,CAAC;IAClC,MAAM,EAAE,kCAAkC;IAC1C,KAAK,EAAE;QACL,SAAS,EACP,+YAA+Y;QACjZ,OAAO,EAAE;;;;;;;;;;;;;;;gCAemB;QAC5B,MAAM,EAAE,iEAAiE;KAC1E;IACD,KAAK,EAAE;QACL,IAAI,EAAE,kDAAkD;QACxD,MAAM,EAAE;YACN,wMAAwM;YACxM,+MAA+M;YAC/M,0KAA0K;YAC1K,0NAA0N;SAC3N;QACD,OAAO,EACL,+EAA+E;QACjF,UAAU,EAAE;YACV,0GAA0G;YAC1G,iHAAiH;SAClH;KACF;CACF,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Rule } from "../schema.ts";
2
+ export declare const progressIndicator: Rule;
3
+ //# sourceMappingURL=progress-indicator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"progress-indicator.d.ts","sourceRoot":"","sources":["../../src/rules/progress-indicator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEzC,eAAO,MAAM,iBAAiB,EAAE,IAuC/B,CAAC"}
@@ -0,0 +1,39 @@
1
+ export const progressIndicator = {
2
+ id: "progress-indicator",
3
+ title: "Spinners and progress bars",
4
+ category: "forms",
5
+ replaces: ["react-spinners", "spinkit", "react-loader-spinner", "ldrs"],
6
+ featureIds: ["progress"],
7
+ native: "<progress>",
8
+ human: {
9
+ explainer: "A progress element with a value draws a determinate bar, and the same element with the value removed draws the indeterminate one, which is the browser's own spinner. It carries the right role and reports its value to assistive technology without any aria being written by hand, which is the part the div-and-keyframes version usually gets wrong. Styling it is CSS on the element and its two pseudo-elements.",
10
+ snippet: `<!-- Determinate. -->
11
+ <progress value="0.7">70%</progress>
12
+
13
+ <!-- Indeterminate: no value attribute at all. -->
14
+ <progress></progress>
15
+
16
+ <style>
17
+ progress { inline-size: 12rem; block-size: 0.5rem; }
18
+ /* The track and the filled part, in WebKit and Blink. */
19
+ progress::-webkit-progress-bar { background: #eee; }
20
+ progress::-webkit-progress-value { background: currentColor; }
21
+ </style>`,
22
+ mdnUrl: "https://developer.mozilla.org/en-US/docs/Web/HTML/Element/progress",
23
+ },
24
+ agent: {
25
+ when: "showing that work is happening, or how far along it is",
26
+ unless: [
27
+ "You need a circular ring. The element draws a bar, and the pseudo-elements that style it are not a shape you can bend into an arc, so a ring is still SVG or a conic-gradient on your own markup.",
28
+ "The design is a branded multi-part animation, such as bouncing dots or a pulsing logo. Those are several moving elements and a progress element is one.",
29
+ "You need a page-level loading bar tied to route changes, which is what nprogress is: the visual is trivial and the router integration and its timing are the library.",
30
+ "Your styling has to match across engines exactly. The pseudo-elements differ between Blink, WebKit and Gecko, and accent-color only recolours the default rendering rather than restyling it.",
31
+ ],
32
+ snippet: "<progress></progress> <!-- indeterminate -->",
33
+ handRolled: [
34
+ "a div with a CSS keyframe rotation and a border-radius, standing in for a spinner, usually with no role or aria-label",
35
+ "a wrapper div whose inline width is set to a percentage string to draw a filled bar",
36
+ ],
37
+ },
38
+ };
39
+ //# sourceMappingURL=progress-indicator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"progress-indicator.js","sourceRoot":"","sources":["../../src/rules/progress-indicator.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,iBAAiB,GAAS;IACrC,EAAE,EAAE,oBAAoB;IACxB,KAAK,EAAE,4BAA4B;IACnC,QAAQ,EAAE,OAAO;IACjB,QAAQ,EAAE,CAAC,gBAAgB,EAAE,SAAS,EAAE,sBAAsB,EAAE,MAAM,CAAC;IACvE,UAAU,EAAE,CAAC,UAAU,CAAC;IACxB,MAAM,EAAE,YAAY;IACpB,KAAK,EAAE;QACL,SAAS,EACP,yZAAyZ;QAC3Z,OAAO,EAAE;;;;;;;;;;;SAWJ;QACL,MAAM,EACJ,oEAAoE;KACvE;IACD,KAAK,EAAE;QACL,IAAI,EAAE,wDAAwD;QAC9D,MAAM,EAAE;YACN,mMAAmM;YACnM,yJAAyJ;YACzJ,uKAAuK;YACvK,+LAA+L;SAChM;QACD,OAAO,EAAE,8CAA8C;QACvD,UAAU,EAAE;YACV,uHAAuH;YACvH,qFAAqF;SACtF;KACF;CACF,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"screen-wake-lock.d.ts","sourceRoot":"","sources":["../../src/rules/screen-wake-lock.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEzC,eAAO,MAAM,cAAc,EAAE,IAyB5B,CAAC"}
1
+ {"version":3,"file":"screen-wake-lock.d.ts","sourceRoot":"","sources":["../../src/rules/screen-wake-lock.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEzC,eAAO,MAAM,cAAc,EAAE,IA6B5B,CAAC"}
@@ -20,6 +20,10 @@ await lock.release();`,
20
20
  "You need this in a non-secure (non-HTTPS) context, where the API isn't available at all.",
21
21
  ],
22
22
  snippet: `const lock = await navigator.wakeLock.request("screen");`,
23
+ handRolled: [
24
+ "a muted looping video kept playing off-screen so the display does not sleep",
25
+ "a no-op setInterval added in the hope the page keeps counting as active",
26
+ ],
23
27
  },
24
28
  };
25
29
  //# sourceMappingURL=screen-wake-lock.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"screen-wake-lock.js","sourceRoot":"","sources":["../../src/rules/screen-wake-lock.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,cAAc,GAAS;IAClC,EAAE,EAAE,kBAAkB;IACtB,KAAK,EAAE,0BAA0B;IACjC,QAAQ,EAAE,aAAa;IACvB,QAAQ,EAAE,CAAC,YAAY,EAAE,qBAAqB,CAAC;IAC/C,UAAU,EAAE,CAAC,kBAAkB,CAAC;IAChC,MAAM,EAAE,sCAAsC;IAC9C,KAAK,EAAE;QACL,SAAS,EACP,0RAA0R;QAC5R,OAAO,EAAE;;sBAES;QAClB,MAAM,EACJ,uEAAuE;KAC1E;IACD,KAAK,EAAE;QACL,IAAI,EAAE,4GAA4G;QAClH,MAAM,EAAE;YACN,sNAAsN;YACtN,2MAA2M;YAC3M,0FAA0F;SAC3F;QACD,OAAO,EAAE,0DAA0D;KACpE;CACF,CAAC"}
1
+ {"version":3,"file":"screen-wake-lock.js","sourceRoot":"","sources":["../../src/rules/screen-wake-lock.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,cAAc,GAAS;IAClC,EAAE,EAAE,kBAAkB;IACtB,KAAK,EAAE,0BAA0B;IACjC,QAAQ,EAAE,aAAa;IACvB,QAAQ,EAAE,CAAC,YAAY,EAAE,qBAAqB,CAAC;IAC/C,UAAU,EAAE,CAAC,kBAAkB,CAAC;IAChC,MAAM,EAAE,sCAAsC;IAC9C,KAAK,EAAE;QACL,SAAS,EACP,0RAA0R;QAC5R,OAAO,EAAE;;sBAES;QAClB,MAAM,EACJ,uEAAuE;KAC1E;IACD,KAAK,EAAE;QACL,IAAI,EAAE,4GAA4G;QAClH,MAAM,EAAE;YACN,sNAAsN;YACtN,2MAA2M;YAC3M,0FAA0F;SAC3F;QACD,OAAO,EAAE,0DAA0D;QACnE,UAAU,EAAE;YACV,6EAA6E;YAC7E,yEAAyE;SAC1E;KACF;CACF,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Rule } from "../schema.ts";
2
+ export declare const scrollIntoView: Rule;
3
+ //# sourceMappingURL=scroll-into-view.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scroll-into-view.d.ts","sourceRoot":"","sources":["../../src/rules/scroll-into-view.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEzC,eAAO,MAAM,cAAc,EAAE,IAsC5B,CAAC"}
@@ -0,0 +1,38 @@
1
+ export const scrollIntoView = {
2
+ id: "scroll-into-view",
3
+ title: "Scrolling an element into view",
4
+ category: "scrolling",
5
+ replaces: [
6
+ "scroll-into-view-if-needed",
7
+ "compute-scroll-into-view",
8
+ "smooth-scroll-into-view-if-needed",
9
+ ],
10
+ featureIds: ["scroll-into-view"],
11
+ native: 'scrollIntoView({ block: "nearest" })',
12
+ human: {
13
+ explainer: 'These packages exist for one behaviour: scroll only if the element is not already visible, and scroll as little as possible when it is not. That is what block: "nearest" does, in every engine. The default is block: "start", which always scrolls and is why the native method got a reputation for being too blunt, so the option is the whole difference.',
14
+ snippet: `// Scrolls only as far as it has to, and not at all if the
15
+ // option is already visible. Ideal for keyboard navigation.
16
+ option.scrollIntoView({
17
+ block: "nearest",
18
+ inline: "nearest",
19
+ behavior: "smooth",
20
+ });`,
21
+ mdnUrl: "https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView",
22
+ },
23
+ agent: {
24
+ when: "bringing an element into view, typically a highlighted option or a focused row",
25
+ unless: [
26
+ "You need the scroll offset computed without applying it. That is what compute-scroll-into-view is for on its own, and a virtualised list that batches its own scrolling genuinely needs the number rather than the movement.",
27
+ 'You need a specific duration or easing. behavior: "smooth" exposes neither, and the browser picks both.',
28
+ "You must scroll several containers to a coordinated position. The native method walks its own ancestors on its own terms, so a scroll that has to stay in step across panes needs the offsets.",
29
+ "Your support target reaches below Chrome {{chrome:api.Element.scrollIntoView.options_parameter}}, Firefox {{firefox:api.Element.scrollIntoView.options_parameter}} or Safari {{safari:api.Element.scrollIntoView.options_parameter}}. The method is far older than that, so scrollIntoView() existing is not proof the options object is read.",
30
+ ],
31
+ snippet: 'el.scrollIntoView({ block: "nearest" });',
32
+ handRolled: [
33
+ "getBoundingClientRect on an element and its scroll container, compared to decide whether to set scrollTop",
34
+ "a scrollTop assignment computed from offsetTop minus the container's height, to centre a highlighted item",
35
+ ],
36
+ },
37
+ };
38
+ //# sourceMappingURL=scroll-into-view.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scroll-into-view.js","sourceRoot":"","sources":["../../src/rules/scroll-into-view.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,cAAc,GAAS;IAClC,EAAE,EAAE,kBAAkB;IACtB,KAAK,EAAE,gCAAgC;IACvC,QAAQ,EAAE,WAAW;IACrB,QAAQ,EAAE;QACR,4BAA4B;QAC5B,0BAA0B;QAC1B,mCAAmC;KACpC;IACD,UAAU,EAAE,CAAC,kBAAkB,CAAC;IAChC,MAAM,EAAE,sCAAsC;IAC9C,KAAK,EAAE;QACL,SAAS,EACP,gWAAgW;QAClW,OAAO,EAAE;;;;;;IAMT;QACA,MAAM,EACJ,yEAAyE;KAC5E;IACD,KAAK,EAAE;QACL,IAAI,EAAE,gFAAgF;QACtF,MAAM,EAAE;YACN,8NAA8N;YAC9N,yGAAyG;YACzG,gMAAgM;YAChM,gVAAgV;SACjV;QACD,OAAO,EAAE,0CAA0C;QACnD,UAAU,EAAE;YACV,2GAA2G;YAC3G,2GAA2G;SAC5G;KACF;CACF,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"segmenter.d.ts","sourceRoot":"","sources":["../../src/rules/segmenter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEzC,eAAO,MAAM,SAAS,EAAE,IAyCvB,CAAC"}
1
+ {"version":3,"file":"segmenter.d.ts","sourceRoot":"","sources":["../../src/rules/segmenter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEzC,eAAO,MAAM,SAAS,EAAE,IA6CvB,CAAC"}
@@ -36,6 +36,10 @@ const words = new Intl.Segmenter("en", { granularity: "word" });
36
36
  ],
37
37
  snippet: `const seg = new Intl.Segmenter("en", { granularity: "grapheme" });
38
38
  const count = [...seg.segment(text)].length;`,
39
+ handRolled: [
40
+ 'str.length or split("") used to count characters, which counts an emoji or a combining accent as several',
41
+ "a regex over surrogate pairs to split a string into what the author is calling characters",
42
+ ],
39
43
  },
40
44
  };
41
45
  //# sourceMappingURL=segmenter.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"segmenter.js","sourceRoot":"","sources":["../../src/rules/segmenter.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,SAAS,GAAS;IAC7B,EAAE,EAAE,gBAAgB;IACpB,KAAK,EAAE,qCAAqC;IAC5C,QAAQ,EAAE,YAAY;IACtB,QAAQ,EAAE;QACR,WAAW;QACX,mBAAmB;QACnB,eAAe;QACf,cAAc;QACd,iBAAiB;KAClB;IACD,UAAU,EAAE,CAAC,gBAAgB,CAAC;IAC9B,MAAM,EAAE,gBAAgB;IACxB,KAAK,EAAE;QACL,SAAS,EACP,2cAA2c;QAC7c,OAAO,EAAE;;;;;;;;;sBASS;QAClB,MAAM,EACJ,iGAAiG;KACpG;IACD,KAAK,EAAE;QACL,IAAI,EAAE,sFAAsF;QAC5F,MAAM,EAAE;YACN,sLAAsL;YACtL,6JAA6J;YAC7J,gLAAgL;YAChL,2HAA2H;YAC3H,+MAA+M;SAChN;QACD,OAAO,EAAE;6CACgC;KAC1C;CACF,CAAC"}
1
+ {"version":3,"file":"segmenter.js","sourceRoot":"","sources":["../../src/rules/segmenter.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,SAAS,GAAS;IAC7B,EAAE,EAAE,gBAAgB;IACpB,KAAK,EAAE,qCAAqC;IAC5C,QAAQ,EAAE,YAAY;IACtB,QAAQ,EAAE;QACR,WAAW;QACX,mBAAmB;QACnB,eAAe;QACf,cAAc;QACd,iBAAiB;KAClB;IACD,UAAU,EAAE,CAAC,gBAAgB,CAAC;IAC9B,MAAM,EAAE,gBAAgB;IACxB,KAAK,EAAE;QACL,SAAS,EACP,2cAA2c;QAC7c,OAAO,EAAE;;;;;;;;;sBASS;QAClB,MAAM,EACJ,iGAAiG;KACpG;IACD,KAAK,EAAE;QACL,IAAI,EAAE,sFAAsF;QAC5F,MAAM,EAAE;YACN,sLAAsL;YACtL,6JAA6J;YAC7J,gLAAgL;YAChL,2HAA2H;YAC3H,+MAA+M;SAChN;QACD,OAAO,EAAE;6CACgC;QACzC,UAAU,EAAE;YACV,0GAA0G;YAC1G,2FAA2F;SAC5F;KACF;CACF,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"select.d.ts","sourceRoot":"","sources":["../../src/rules/select.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEzC,eAAO,MAAM,MAAM,EAAE,IAyEpB,CAAC"}
1
+ {"version":3,"file":"select.d.ts","sourceRoot":"","sources":["../../src/rules/select.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEzC,eAAO,MAAM,MAAM,EAAE,IA0EpB,CAAC"}
@@ -68,6 +68,7 @@ export const select = {
68
68
  "animated-select-picker",
69
69
  "branded-select-styling",
70
70
  "custom-select-picker-layouts",
71
+ "rich-media-picker",
71
72
  "select-menu-interaction",
72
73
  ],
73
74
  };
@@ -1 +1 @@
1
- {"version":3,"file":"select.js","sourceRoot":"","sources":["../../src/rules/select.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,MAAM,GAAS;IAC1B,EAAE,EAAE,qBAAqB;IACzB,KAAK,EAAE,qBAAqB;IAC5B,QAAQ,EAAE,OAAO;IACjB,QAAQ,EAAE;QACR,cAAc;QACd,YAAY;QACZ,SAAS;QACT,YAAY;QACZ,YAAY;QACZ,iBAAiB;QACjB,sBAAsB;QACtB,eAAe;KAChB;IACD,UAAU,EAAE,CAAC,qBAAqB,CAAC;IACnC,MAAM,EAAE,qCAAqC;IAC7C,KAAK,EAAE;QACL,SAAS,EACP,+YAA+Y;QACjZ,OAAO,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;SA0BJ;QACL,MAAM,EAAE,6DAA6D;KACtE;IACD,KAAK,EAAE;QACL,IAAI,EAAE,4EAA4E;QAClF,MAAM,EAAE;YACN,0LAA0L;YAC1L,qFAAqF;YACrF,gLAAgL;YAChL,qEAAqE;SACtE;QACD,OAAO,EAAE;;;;;;SAMJ;QACL,UAAU,EAAE;YACV,8FAA8F;SAC/F;KACF;IACD,MAAM,EAAE;QACN,wBAAwB;QACxB,wBAAwB;QACxB,8BAA8B;QAC9B,yBAAyB;KAC1B;CACF,CAAC"}
1
+ {"version":3,"file":"select.js","sourceRoot":"","sources":["../../src/rules/select.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,MAAM,GAAS;IAC1B,EAAE,EAAE,qBAAqB;IACzB,KAAK,EAAE,qBAAqB;IAC5B,QAAQ,EAAE,OAAO;IACjB,QAAQ,EAAE;QACR,cAAc;QACd,YAAY;QACZ,SAAS;QACT,YAAY;QACZ,YAAY;QACZ,iBAAiB;QACjB,sBAAsB;QACtB,eAAe;KAChB;IACD,UAAU,EAAE,CAAC,qBAAqB,CAAC;IACnC,MAAM,EAAE,qCAAqC;IAC7C,KAAK,EAAE;QACL,SAAS,EACP,+YAA+Y;QACjZ,OAAO,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;SA0BJ;QACL,MAAM,EAAE,6DAA6D;KACtE;IACD,KAAK,EAAE;QACL,IAAI,EAAE,4EAA4E;QAClF,MAAM,EAAE;YACN,0LAA0L;YAC1L,qFAAqF;YACrF,gLAAgL;YAChL,qEAAqE;SACtE;QACD,OAAO,EAAE;;;;;;SAMJ;QACL,UAAU,EAAE;YACV,8FAA8F;SAC/F;KACF;IACD,MAAM,EAAE;QACN,wBAAwB;QACxB,wBAAwB;QACxB,8BAA8B;QAC9B,mBAAmB;QACnB,yBAAyB;KAC1B;CACF,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"server-sent-events.d.ts","sourceRoot":"","sources":["../../src/rules/server-sent-events.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEzC,eAAO,MAAM,gBAAgB,EAAE,IAqC9B,CAAC"}
1
+ {"version":3,"file":"server-sent-events.d.ts","sourceRoot":"","sources":["../../src/rules/server-sent-events.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEzC,eAAO,MAAM,gBAAgB,EAAE,IAyC9B,CAAC"}
@@ -33,6 +33,10 @@ source.addEventListener("error", () => {
33
33
  snippet: `const source = new EventSource("/api/updates");
34
34
  source.addEventListener("price", (e) => update(JSON.parse(e.data)));
35
35
  source.close();`,
36
+ handRolled: [
37
+ "a setInterval or a self-rescheduling setTimeout re-fetching an endpoint to notice changes, with the delay tuned by hand",
38
+ "a long poll: a fetch the server holds open, re-issued from its own then handler",
39
+ ],
36
40
  },
37
41
  };
38
42
  //# sourceMappingURL=server-sent-events.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"server-sent-events.js","sourceRoot":"","sources":["../../src/rules/server-sent-events.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,gBAAgB,GAAS;IACpC,EAAE,EAAE,oBAAoB;IACxB,KAAK,EAAE,oBAAoB;IAC3B,QAAQ,EAAE,YAAY;IACtB,QAAQ,EAAE,CAAC,aAAa,EAAE,uBAAuB,CAAC;IAClD,UAAU,EAAE,CAAC,oBAAoB,CAAC;IAClC,MAAM,EAAE,aAAa;IACrB,KAAK,EAAE;QACL,SAAS,EACP,mWAAmW;QACrW,OAAO,EAAE;;;;;;;;;;;IAWT;QACA,MAAM,EAAE,8DAA8D;KACvE;IACD,KAAK,EAAE;QACL,IAAI,EAAE,0DAA0D;QAChE,MAAM,EAAE;YACN,kLAAkL;YAClL,oJAAoJ;YACpJ,gKAAgK;YAChK,+JAA+J;YAC/J,8JAA8J;SAC/J;QACD,OAAO,EAAE;;gBAEG;KACb;CACF,CAAC"}
1
+ {"version":3,"file":"server-sent-events.js","sourceRoot":"","sources":["../../src/rules/server-sent-events.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,gBAAgB,GAAS;IACpC,EAAE,EAAE,oBAAoB;IACxB,KAAK,EAAE,oBAAoB;IAC3B,QAAQ,EAAE,YAAY;IACtB,QAAQ,EAAE,CAAC,aAAa,EAAE,uBAAuB,CAAC;IAClD,UAAU,EAAE,CAAC,oBAAoB,CAAC;IAClC,MAAM,EAAE,aAAa;IACrB,KAAK,EAAE;QACL,SAAS,EACP,mWAAmW;QACrW,OAAO,EAAE;;;;;;;;;;;IAWT;QACA,MAAM,EAAE,8DAA8D;KACvE;IACD,KAAK,EAAE;QACL,IAAI,EAAE,0DAA0D;QAChE,MAAM,EAAE;YACN,kLAAkL;YAClL,oJAAoJ;YACpJ,gKAAgK;YAChK,+JAA+J;YAC/J,8JAA8J;SAC/J;QACD,OAAO,EAAE;;gBAEG;QACZ,UAAU,EAAE;YACV,yHAAyH;YACzH,iFAAiF;SAClF;KACF;CACF,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Rule } from "../schema.ts";
2
+ export declare const speculationRules: Rule;
3
+ //# sourceMappingURL=speculation-rules.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"speculation-rules.d.ts","sourceRoot":"","sources":["../../src/rules/speculation-rules.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEzC,eAAO,MAAM,gBAAgB,EAAE,IAoC9B,CAAC"}
@@ -0,0 +1,35 @@
1
+ export const speculationRules = {
2
+ id: "speculation-rules",
3
+ title: "Prefetching the next page",
4
+ category: "async-data",
5
+ replaces: ["quicklink", "instant.page"],
6
+ featureIds: ["speculation-rules"],
7
+ native: "speculation rules",
8
+ human: {
9
+ explainer: "These scripts watch for links entering the viewport or being hovered, then inject a prefetch tag. Speculation rules say the same thing declaratively in a script tag the browser reads: which links to speculate on, and how eagerly. The browser handles the heuristics, and prerender goes further than any library can, rendering the next page so the click is instant. Chromium only for now.",
10
+ snippet: `<script type="speculationrules">
11
+ {
12
+ "prerender": [{
13
+ "where": { "href_matches": "/articles/*" },
14
+ "eagerness": "moderate"
15
+ }]
16
+ }
17
+ </script>`,
18
+ mdnUrl: "https://developer.mozilla.org/en-US/docs/Web/API/Speculation_Rules_API",
19
+ },
20
+ agent: {
21
+ when: "prefetching or prerendering the page a visitor is likely to open next",
22
+ unless: [
23
+ "You ship to Firefox or Safari. Only Chromium has this, from Chrome {{chrome:speculation-rules}}, so the library is still what serves everyone else and this is an enhancement rather than a swap.",
24
+ "Prerendering would run analytics or side effects for a page nobody visited. A prerendered document runs its scripts, so anything counting a view has to check document.prerendering and wait, which is work the library never made you do.",
25
+ "The next URL is decided in JavaScript rather than present as a link, such as a router that builds the target from state. Rules match on the document's own hrefs.",
26
+ "Your pages are personalised or expensive to render, where speculating wrongly costs the server more than the click saves the visitor.",
27
+ ],
28
+ snippet: '<script type="speculationrules">{"prefetch":[{"where":{"href_matches":"/*"}}]}</script>',
29
+ handRolled: [
30
+ "an IntersectionObserver over anchors that injects a <link rel=prefetch> when one scrolls into view",
31
+ "a mouseover or touchstart listener on links that starts fetching the target before the click lands",
32
+ ],
33
+ },
34
+ };
35
+ //# sourceMappingURL=speculation-rules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"speculation-rules.js","sourceRoot":"","sources":["../../src/rules/speculation-rules.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,gBAAgB,GAAS;IACpC,EAAE,EAAE,mBAAmB;IACvB,KAAK,EAAE,2BAA2B;IAClC,QAAQ,EAAE,YAAY;IACtB,QAAQ,EAAE,CAAC,WAAW,EAAE,cAAc,CAAC;IACvC,UAAU,EAAE,CAAC,mBAAmB,CAAC;IACjC,MAAM,EAAE,mBAAmB;IAC3B,KAAK,EAAE;QACL,SAAS,EACP,oYAAoY;QACtY,OAAO,EAAE;;;;;;;UAOH;QACN,MAAM,EACJ,wEAAwE;KAC3E;IACD,KAAK,EAAE;QACL,IAAI,EAAE,uEAAuE;QAC7E,MAAM,EAAE;YACN,mMAAmM;YACnM,4OAA4O;YAC5O,mKAAmK;YACnK,uIAAuI;SACxI;QACD,OAAO,EACL,yFAAyF;QAC3F,UAAU,EAAE;YACV,oGAAoG;YACpG,oGAAoG;SACrG;KACF;CACF,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Rule } from "../schema.ts";
2
+ export declare const temporal: Rule;
3
+ //# sourceMappingURL=temporal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"temporal.d.ts","sourceRoot":"","sources":["../../src/rules/temporal.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEzC,eAAO,MAAM,QAAQ,EAAE,IAmCtB,CAAC"}
@@ -0,0 +1,34 @@
1
+ export const temporal = {
2
+ id: "temporal",
3
+ title: "Date arithmetic and time zones",
4
+ category: "formatting",
5
+ replaces: ["moment-timezone", "spacetime", "@js-joda/core", "js-joda"],
6
+ featureIds: ["temporal"],
7
+ native: "Temporal",
8
+ human: {
9
+ explainer: "These libraries exist because Date cannot do the job: it has no concept of a time zone beyond the host's, and adding a day across a daylight saving boundary gives the wrong answer. Temporal is the replacement, with ZonedDateTime carrying an IANA zone and arithmetic that respects it. It is not in every engine yet, so this is a plan rather than a deletion, and the honest move today is the official polyfill.",
10
+ snippet: `const meeting = Temporal.ZonedDateTime.from(
11
+ "2026-03-08T01:30:00-05:00[America/New_York]",
12
+ );
13
+
14
+ // Adds a calendar day, not 24 hours, so the DST change is handled.
15
+ meeting.add({ days: 1 }).toString();`,
16
+ mdnUrl: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal",
17
+ },
18
+ agent: {
19
+ when: "doing date arithmetic or working with named time zones",
20
+ unless: [
21
+ "You ship to Safari, or to any engine below Chrome {{chrome:temporal}} or Firefox {{firefox:temporal}}. Temporal is not available everywhere yet, so today this means adding @js-temporal/polyfill rather than removing a dependency, and that polyfill is larger than most of the libraries it would replace.",
22
+ "You only format dates for display. Intl.DateTimeFormat already does that everywhere, and the date-format rule covers it. Temporal is for the arithmetic.",
23
+ "You parse loose or non-ISO input. Temporal is deliberately strict and throws on anything ambiguous, which is the opposite of what moment was liked for.",
24
+ "You depend on moment-timezone's bundled zone database at a pinned version, for example to reproduce a historical calculation. Temporal reads the zone data the engine ships, which moves under you.",
25
+ "The codebase is on moment and the migration is the work. Two date libraries in one bundle is worse than one, so a partial migration costs more than either end state.",
26
+ ],
27
+ snippet: 'Temporal.ZonedDateTime.from("2026-03-08T01:30:00-05:00[America/New_York]").add({ days: 1 });',
28
+ handRolled: [
29
+ "a date rebuilt through UTC offsets with getTimezoneOffset, to move a timestamp into another zone",
30
+ "adding 86400000 milliseconds to a timestamp to mean tomorrow, which is an hour out on the two days a year the offset changes",
31
+ ],
32
+ },
33
+ };
34
+ //# sourceMappingURL=temporal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"temporal.js","sourceRoot":"","sources":["../../src/rules/temporal.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,QAAQ,GAAS;IAC5B,EAAE,EAAE,UAAU;IACd,KAAK,EAAE,gCAAgC;IACvC,QAAQ,EAAE,YAAY;IACtB,QAAQ,EAAE,CAAC,iBAAiB,EAAE,WAAW,EAAE,eAAe,EAAE,SAAS,CAAC;IACtE,UAAU,EAAE,CAAC,UAAU,CAAC;IACxB,MAAM,EAAE,UAAU;IAClB,KAAK,EAAE;QACL,SAAS,EACP,0ZAA0Z;QAC5Z,OAAO,EAAE;;;;;qCAKwB;QACjC,MAAM,EACJ,2FAA2F;KAC9F;IACD,KAAK,EAAE;QACL,IAAI,EAAE,wDAAwD;QAC9D,MAAM,EAAE;YACN,+SAA+S;YAC/S,0JAA0J;YAC1J,yJAAyJ;YACzJ,qMAAqM;YACrM,uKAAuK;SACxK;QACD,OAAO,EACL,8FAA8F;QAChG,UAAU,EAAE;YACV,kGAAkG;YAClG,8HAA8H;SAC/H;KACF;CACF,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"url-pattern.d.ts","sourceRoot":"","sources":["../../src/rules/url-pattern.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEzC,eAAO,MAAM,UAAU,EAAE,IAmCxB,CAAC"}
1
+ {"version":3,"file":"url-pattern.d.ts","sourceRoot":"","sources":["../../src/rules/url-pattern.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEzC,eAAO,MAAM,UAAU,EAAE,IAuCxB,CAAC"}
@@ -31,6 +31,10 @@ new URLPattern({ hostname: ":tenant.example.com" })
31
31
  snippet: `const pattern = new URLPattern({ pathname: "/users/:id" });
32
32
  const match = pattern.exec(url);
33
33
  const id = match?.pathname.groups.id;`,
34
+ handRolled: [
35
+ "a hand-written regex per route with named capture groups, and a loop testing each against the path",
36
+ 'pathname.split("/") compared segment by segment to work out which route matched and pull out an id',
37
+ ],
34
38
  },
35
39
  };
36
40
  //# sourceMappingURL=url-pattern.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"url-pattern.js","sourceRoot":"","sources":["../../src/rules/url-pattern.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,UAAU,GAAS;IAC9B,EAAE,EAAE,YAAY;IAChB,KAAK,EAAE,uBAAuB;IAC9B,QAAQ,EAAE,YAAY;IACtB,QAAQ,EAAE,CAAC,gBAAgB,EAAE,aAAa,EAAE,cAAc,CAAC;IAC3D,UAAU,EAAE,CAAC,YAAY,CAAC;IAC1B,MAAM,EAAE,YAAY;IACpB,KAAK,EAAE;QACL,SAAS,EACP,8XAA8X;QAChY,OAAO,EAAE;;;;;;;;;;0CAU6B;QACtC,MAAM,EAAE,6DAA6D;KACtE;IACD,KAAK,EAAE;QACL,IAAI,EAAE,yEAAyE;QAC/E,MAAM,EAAE;YACN,4LAA4L;YAC5L,8LAA8L;YAC9L,2OAA2O;YAC3O,oHAAoH;SACrH;QACD,OAAO,EAAE;;sCAEyB;KACnC;CACF,CAAC"}
1
+ {"version":3,"file":"url-pattern.js","sourceRoot":"","sources":["../../src/rules/url-pattern.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,UAAU,GAAS;IAC9B,EAAE,EAAE,YAAY;IAChB,KAAK,EAAE,uBAAuB;IAC9B,QAAQ,EAAE,YAAY;IACtB,QAAQ,EAAE,CAAC,gBAAgB,EAAE,aAAa,EAAE,cAAc,CAAC;IAC3D,UAAU,EAAE,CAAC,YAAY,CAAC;IAC1B,MAAM,EAAE,YAAY;IACpB,KAAK,EAAE;QACL,SAAS,EACP,8XAA8X;QAChY,OAAO,EAAE;;;;;;;;;;0CAU6B;QACtC,MAAM,EAAE,6DAA6D;KACtE;IACD,KAAK,EAAE;QACL,IAAI,EAAE,yEAAyE;QAC/E,MAAM,EAAE;YACN,4LAA4L;YAC5L,8LAA8L;YAC9L,2OAA2O;YAC3O,oHAAoH;SACrH;QACD,OAAO,EAAE;;sCAEyB;QAClC,UAAU,EAAE;YACV,oGAAoG;YACpG,oGAAoG;SACrG;KACF;CACF,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"web-animations.d.ts","sourceRoot":"","sources":["../../src/rules/web-animations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEzC,eAAO,MAAM,aAAa,EAAE,IAwC3B,CAAC"}
1
+ {"version":3,"file":"web-animations.d.ts","sourceRoot":"","sources":["../../src/rules/web-animations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEzC,eAAO,MAAM,aAAa,EAAE,IA4C3B,CAAC"}
@@ -35,6 +35,10 @@ animation.reverse();`,
35
35
  { duration: 240, fill: "both" },
36
36
  );
37
37
  await animation.finished;`,
38
+ handRolled: [
39
+ "a requestAnimationFrame loop interpolating a style property between two values, with its own easing function",
40
+ "a chain of setTimeout calls stepping an element's style to fake a keyframe sequence",
41
+ ],
38
42
  },
39
43
  };
40
44
  //# sourceMappingURL=web-animations.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"web-animations.js","sourceRoot":"","sources":["../../src/rules/web-animations.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,aAAa,GAAS;IACjC,EAAE,EAAE,gBAAgB;IACpB,KAAK,EAAE,2BAA2B;IAClC,QAAQ,EAAE,WAAW;IACrB,QAAQ,EAAE,CAAC,SAAS,EAAE,WAAW,EAAE,kBAAkB,CAAC;IACtD,UAAU,EAAE,CAAC,gBAAgB,CAAC;IAC9B,MAAM,EAAE,mBAAmB;IAC3B,KAAK,EAAE;QACL,SAAS,EACP,8aAA8a;QAChb,OAAO,EAAE;;;;;;;;;;;qBAWQ;QACjB,MAAM,EACJ,qEAAqE;KACxE;IACD,KAAK,EAAE;QACL,IAAI,EAAE,kFAAkF;QACxF,MAAM,EAAE;YACN,mOAAmO;YACnO,gNAAgN;YAChN,6MAA6M;YAC7M,oKAAoK;YACpK,kIAAkI;SACnI;QACD,OAAO,EAAE;;;;0BAIa;KACvB;CACF,CAAC"}
1
+ {"version":3,"file":"web-animations.js","sourceRoot":"","sources":["../../src/rules/web-animations.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,aAAa,GAAS;IACjC,EAAE,EAAE,gBAAgB;IACpB,KAAK,EAAE,2BAA2B;IAClC,QAAQ,EAAE,WAAW;IACrB,QAAQ,EAAE,CAAC,SAAS,EAAE,WAAW,EAAE,kBAAkB,CAAC;IACtD,UAAU,EAAE,CAAC,gBAAgB,CAAC;IAC9B,MAAM,EAAE,mBAAmB;IAC3B,KAAK,EAAE;QACL,SAAS,EACP,8aAA8a;QAChb,OAAO,EAAE;;;;;;;;;;;qBAWQ;QACjB,MAAM,EACJ,qEAAqE;KACxE;IACD,KAAK,EAAE;QACL,IAAI,EAAE,kFAAkF;QACxF,MAAM,EAAE;YACN,mOAAmO;YACnO,gNAAgN;YAChN,6MAA6M;YAC7M,oKAAoK;YACpK,kIAAkI;SACnI;QACD,OAAO,EAAE;;;;0BAIa;QACtB,UAAU,EAAE;YACV,8GAA8G;YAC9G,qFAAqF;SACtF;KACF;CACF,CAAC"}