shelfware 0.0.1 → 0.1.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 (206) hide show
  1. package/.output/nitro.json +15 -0
  2. package/.output/public/_nuxt/6IstVYrV.js +1 -0
  3. package/.output/public/_nuxt/9C5WUlLE.js +36 -0
  4. package/.output/public/_nuxt/BDNMzG2s.js +1 -0
  5. package/.output/public/_nuxt/BW3xJdEx.js +1 -0
  6. package/.output/public/_nuxt/BXXtFil5.js +1 -0
  7. package/.output/public/_nuxt/BZuUK3cr.js +1 -0
  8. package/.output/public/_nuxt/BeLyMmd4.js +1 -0
  9. package/.output/public/_nuxt/C_VgD9ZI.js +3 -0
  10. package/.output/public/_nuxt/DB5Vxbte.js +2 -0
  11. package/.output/public/_nuxt/DMg0j7gv.js +14 -0
  12. package/.output/public/_nuxt/builds/latest.json +1 -0
  13. package/.output/public/_nuxt/builds/meta/e0d2b6a3-3a30-44b7-8c79-a56f66400005.json +1 -0
  14. package/.output/public/_nuxt/entry.b3_DJTEt.css +2 -0
  15. package/.output/public/_nuxt/error-404.CHsn7XF-.css +1 -0
  16. package/.output/public/_nuxt/error-500.Bawx2bLr.css +1 -0
  17. package/.output/server/chunks/_/api-handler.mjs +44 -0
  18. package/.output/server/chunks/_/batch.mjs +27 -0
  19. package/.output/server/chunks/_/catalog.mjs +1115 -0
  20. package/.output/server/chunks/_/detail.mjs +16 -0
  21. package/.output/server/chunks/_/error-500.mjs +19 -0
  22. package/.output/server/chunks/_/quarantine.mjs +159 -0
  23. package/.output/server/chunks/nitro/nitro.mjs +5399 -0
  24. package/.output/server/chunks/routes/api/_..._.mjs +16 -0
  25. package/.output/server/chunks/routes/api/health.get.mjs +13 -0
  26. package/.output/server/chunks/routes/api/skills/_id/file.get.mjs +25 -0
  27. package/.output/server/chunks/routes/api/skills/_id_.get.mjs +25 -0
  28. package/.output/server/chunks/routes/api/skills/_id_.put.mjs +74 -0
  29. package/.output/server/chunks/routes/api/skills/delete.post.mjs +48 -0
  30. package/.output/server/chunks/routes/api/skills/quarantine.post.mjs +26 -0
  31. package/.output/server/chunks/routes/api/skills/restore.post.mjs +26 -0
  32. package/.output/server/chunks/routes/api/skills.get.mjs +41 -0
  33. package/.output/server/chunks/routes/renderer.mjs +341 -0
  34. package/.output/server/chunks/virtual/_virtual_spa-template.mjs +3 -0
  35. package/.output/server/chunks/virtual/precomputed.mjs +3 -0
  36. package/.output/server/index.mjs +9 -0
  37. package/.output/server/node_modules/@babel/parser/lib/index.js +14611 -0
  38. package/.output/server/node_modules/@babel/parser/package.json +50 -0
  39. package/.output/server/node_modules/@vue/compiler-core/dist/compiler-core.cjs.prod.js +6801 -0
  40. package/.output/server/node_modules/@vue/compiler-core/package.json +58 -0
  41. package/.output/server/node_modules/@vue/compiler-dom/dist/compiler-dom.cjs.prod.js +692 -0
  42. package/.output/server/node_modules/@vue/compiler-dom/package.json +57 -0
  43. package/.output/server/node_modules/@vue/compiler-ssr/dist/compiler-ssr.cjs.js +1420 -0
  44. package/.output/server/node_modules/@vue/compiler-ssr/package.json +34 -0
  45. package/.output/server/node_modules/@vue/reactivity/dist/reactivity.cjs.prod.js +1903 -0
  46. package/.output/server/node_modules/@vue/reactivity/package.json +55 -0
  47. package/.output/server/node_modules/@vue/runtime-core/dist/runtime-core.cjs.prod.js +6980 -0
  48. package/.output/server/node_modules/@vue/runtime-core/package.json +52 -0
  49. package/.output/server/node_modules/@vue/runtime-dom/dist/runtime-dom.cjs.prod.js +1867 -0
  50. package/.output/server/node_modules/@vue/runtime-dom/package.json +60 -0
  51. package/.output/server/node_modules/@vue/server-renderer/dist/server-renderer.cjs.prod.js +904 -0
  52. package/.output/server/node_modules/@vue/server-renderer/package.json +53 -0
  53. package/.output/server/node_modules/@vue/shared/dist/shared.cjs.prod.js +636 -0
  54. package/.output/server/node_modules/@vue/shared/package.json +47 -0
  55. package/.output/server/node_modules/devalue/index.js +17 -0
  56. package/.output/server/node_modules/devalue/package.json +39 -0
  57. package/.output/server/node_modules/devalue/src/base64.js +60 -0
  58. package/.output/server/node_modules/devalue/src/constants.js +12 -0
  59. package/.output/server/node_modules/devalue/src/operations.js +193 -0
  60. package/.output/server/node_modules/devalue/src/parse.js +274 -0
  61. package/.output/server/node_modules/devalue/src/stringify.js +428 -0
  62. package/.output/server/node_modules/devalue/src/uneval.js +606 -0
  63. package/.output/server/node_modules/devalue/src/utils.js +185 -0
  64. package/.output/server/node_modules/entities/dist/commonjs/decode-codepoint.js +77 -0
  65. package/.output/server/node_modules/entities/dist/commonjs/decode.js +568 -0
  66. package/.output/server/node_modules/entities/dist/commonjs/generated/decode-data-html.js +7 -0
  67. package/.output/server/node_modules/entities/dist/commonjs/generated/decode-data-xml.js +7 -0
  68. package/.output/server/node_modules/entities/dist/commonjs/internal/bin-trie-flags.js +21 -0
  69. package/.output/server/node_modules/entities/dist/commonjs/internal/decode-shared.js +31 -0
  70. package/.output/server/node_modules/entities/dist/commonjs/package.json +3 -0
  71. package/.output/server/node_modules/entities/package.json +120 -0
  72. package/.output/server/node_modules/estree-walker/dist/umd/estree-walker.js +344 -0
  73. package/.output/server/node_modules/estree-walker/package.json +37 -0
  74. package/.output/server/node_modules/hookable/dist/index.mjs +257 -0
  75. package/.output/server/node_modules/hookable/package.json +53 -0
  76. package/.output/server/node_modules/nostics/dist/formatters/ansi.mjs +19 -0
  77. package/.output/server/node_modules/nostics/dist/index.mjs +187 -0
  78. package/.output/server/node_modules/nostics/package.json +125 -0
  79. package/.output/server/node_modules/source-map-js/lib/array-set.js +121 -0
  80. package/.output/server/node_modules/source-map-js/lib/base64-vlq.js +140 -0
  81. package/.output/server/node_modules/source-map-js/lib/base64.js +67 -0
  82. package/.output/server/node_modules/source-map-js/lib/binary-search.js +111 -0
  83. package/.output/server/node_modules/source-map-js/lib/mapping-list.js +79 -0
  84. package/.output/server/node_modules/source-map-js/lib/quick-sort.js +132 -0
  85. package/.output/server/node_modules/source-map-js/lib/source-map-consumer.js +1188 -0
  86. package/.output/server/node_modules/source-map-js/lib/source-map-generator.js +444 -0
  87. package/.output/server/node_modules/source-map-js/lib/source-node.js +413 -0
  88. package/.output/server/node_modules/source-map-js/lib/util.js +594 -0
  89. package/.output/server/node_modules/source-map-js/package.json +71 -0
  90. package/.output/server/node_modules/source-map-js/source-map.js +8 -0
  91. package/.output/server/node_modules/ufo/dist/index.mjs +645 -0
  92. package/.output/server/node_modules/ufo/package.json +48 -0
  93. package/.output/server/node_modules/unhead/dist/client.mjs +22 -0
  94. package/.output/server/node_modules/unhead/dist/legacy.mjs +33 -0
  95. package/.output/server/node_modules/unhead/dist/minify.mjs +183 -0
  96. package/.output/server/node_modules/unhead/dist/parser.mjs +518 -0
  97. package/.output/server/node_modules/unhead/dist/plugins.mjs +720 -0
  98. package/.output/server/node_modules/unhead/dist/server.mjs +99 -0
  99. package/.output/server/node_modules/unhead/dist/shared/unhead.-hZVKou0.mjs +400 -0
  100. package/.output/server/node_modules/unhead/dist/shared/unhead.BEmVMkeA.mjs +673 -0
  101. package/.output/server/node_modules/unhead/dist/shared/unhead.BGFxPGPQ.mjs +47 -0
  102. package/.output/server/node_modules/unhead/dist/shared/unhead.BQWSw36o.mjs +192 -0
  103. package/.output/server/node_modules/unhead/dist/shared/unhead.Bf_fPVYA.mjs +6 -0
  104. package/.output/server/node_modules/unhead/dist/shared/unhead.Bjbp1C8D.mjs +224 -0
  105. package/.output/server/node_modules/unhead/dist/shared/unhead.Bm4Y6XQI.mjs +17 -0
  106. package/.output/server/node_modules/unhead/dist/shared/unhead.C5Bksi2B.mjs +49 -0
  107. package/.output/server/node_modules/unhead/dist/shared/unhead.CGPOfp5O.mjs +32 -0
  108. package/.output/server/node_modules/unhead/dist/shared/unhead.CHEy9ana.mjs +7 -0
  109. package/.output/server/node_modules/unhead/dist/shared/unhead.D7HkBzZn.mjs +59 -0
  110. package/.output/server/node_modules/unhead/dist/shared/unhead.Dssd7L5I.mjs +196 -0
  111. package/.output/server/node_modules/unhead/dist/shared/unhead.jEy9BmRq.mjs +264 -0
  112. package/.output/server/node_modules/unhead/dist/shared/unhead.oZ7pkTV1.mjs +19 -0
  113. package/.output/server/node_modules/unhead/dist/shared/unhead.tQnRaXfX.mjs +240 -0
  114. package/.output/server/node_modules/unhead/dist/stream/client.mjs +33 -0
  115. package/.output/server/node_modules/unhead/dist/stream/iife.mjs +2 -0
  116. package/.output/server/node_modules/unhead/dist/stream/server.mjs +445 -0
  117. package/.output/server/node_modules/unhead/dist/utils.mjs +7 -0
  118. package/.output/server/node_modules/unhead/package.json +179 -0
  119. package/.output/server/node_modules/vue/dist/vue.cjs.js +80 -0
  120. package/.output/server/node_modules/vue/dist/vue.cjs.prod.js +66 -0
  121. package/.output/server/node_modules/vue/index.js +7 -0
  122. package/.output/server/node_modules/vue/index.mjs +1 -0
  123. package/.output/server/node_modules/vue/package.json +112 -0
  124. package/.output/server/node_modules/vue/server-renderer/index.mjs +1 -0
  125. package/.output/server/node_modules/vue-bundle-renderer/dist/runtime.mjs +278 -0
  126. package/.output/server/node_modules/vue-bundle-renderer/package.json +54 -0
  127. package/.output/server/node_modules/yaml/dist/compose/compose-collection.js +90 -0
  128. package/.output/server/node_modules/yaml/dist/compose/compose-doc.js +45 -0
  129. package/.output/server/node_modules/yaml/dist/compose/compose-node.js +112 -0
  130. package/.output/server/node_modules/yaml/dist/compose/compose-scalar.js +88 -0
  131. package/.output/server/node_modules/yaml/dist/compose/composer.js +224 -0
  132. package/.output/server/node_modules/yaml/dist/compose/resolve-block-map.js +117 -0
  133. package/.output/server/node_modules/yaml/dist/compose/resolve-block-scalar.js +200 -0
  134. package/.output/server/node_modules/yaml/dist/compose/resolve-block-seq.js +51 -0
  135. package/.output/server/node_modules/yaml/dist/compose/resolve-end.js +39 -0
  136. package/.output/server/node_modules/yaml/dist/compose/resolve-flow-collection.js +209 -0
  137. package/.output/server/node_modules/yaml/dist/compose/resolve-flow-scalar.js +227 -0
  138. package/.output/server/node_modules/yaml/dist/compose/resolve-props.js +148 -0
  139. package/.output/server/node_modules/yaml/dist/compose/util-contains-newline.js +36 -0
  140. package/.output/server/node_modules/yaml/dist/compose/util-empty-scalar-position.js +28 -0
  141. package/.output/server/node_modules/yaml/dist/compose/util-flow-indent-check.js +17 -0
  142. package/.output/server/node_modules/yaml/dist/compose/util-map-includes.js +15 -0
  143. package/.output/server/node_modules/yaml/dist/doc/Document.js +337 -0
  144. package/.output/server/node_modules/yaml/dist/doc/anchors.js +76 -0
  145. package/.output/server/node_modules/yaml/dist/doc/applyReviver.js +57 -0
  146. package/.output/server/node_modules/yaml/dist/doc/createNode.js +90 -0
  147. package/.output/server/node_modules/yaml/dist/doc/directives.js +178 -0
  148. package/.output/server/node_modules/yaml/dist/errors.js +62 -0
  149. package/.output/server/node_modules/yaml/dist/index.js +50 -0
  150. package/.output/server/node_modules/yaml/dist/log.js +19 -0
  151. package/.output/server/node_modules/yaml/dist/nodes/Alias.js +118 -0
  152. package/.output/server/node_modules/yaml/dist/nodes/Collection.js +151 -0
  153. package/.output/server/node_modules/yaml/dist/nodes/Node.js +40 -0
  154. package/.output/server/node_modules/yaml/dist/nodes/Pair.js +39 -0
  155. package/.output/server/node_modules/yaml/dist/nodes/Scalar.js +27 -0
  156. package/.output/server/node_modules/yaml/dist/nodes/YAMLMap.js +147 -0
  157. package/.output/server/node_modules/yaml/dist/nodes/YAMLSeq.js +115 -0
  158. package/.output/server/node_modules/yaml/dist/nodes/addPairToJSMap.js +65 -0
  159. package/.output/server/node_modules/yaml/dist/nodes/identity.js +53 -0
  160. package/.output/server/node_modules/yaml/dist/nodes/toJS.js +39 -0
  161. package/.output/server/node_modules/yaml/dist/parse/cst-scalar.js +218 -0
  162. package/.output/server/node_modules/yaml/dist/parse/cst-stringify.js +63 -0
  163. package/.output/server/node_modules/yaml/dist/parse/cst-visit.js +99 -0
  164. package/.output/server/node_modules/yaml/dist/parse/cst.js +112 -0
  165. package/.output/server/node_modules/yaml/dist/parse/lexer.js +723 -0
  166. package/.output/server/node_modules/yaml/dist/parse/line-counter.js +41 -0
  167. package/.output/server/node_modules/yaml/dist/parse/parser.js +980 -0
  168. package/.output/server/node_modules/yaml/dist/public-api.js +107 -0
  169. package/.output/server/node_modules/yaml/dist/schema/Schema.js +39 -0
  170. package/.output/server/node_modules/yaml/dist/schema/common/map.js +19 -0
  171. package/.output/server/node_modules/yaml/dist/schema/common/null.js +17 -0
  172. package/.output/server/node_modules/yaml/dist/schema/common/seq.js +19 -0
  173. package/.output/server/node_modules/yaml/dist/schema/common/string.js +16 -0
  174. package/.output/server/node_modules/yaml/dist/schema/core/bool.js +21 -0
  175. package/.output/server/node_modules/yaml/dist/schema/core/float.js +47 -0
  176. package/.output/server/node_modules/yaml/dist/schema/core/int.js +42 -0
  177. package/.output/server/node_modules/yaml/dist/schema/core/schema.js +25 -0
  178. package/.output/server/node_modules/yaml/dist/schema/json/schema.js +64 -0
  179. package/.output/server/node_modules/yaml/dist/schema/tags.js +99 -0
  180. package/.output/server/node_modules/yaml/dist/schema/yaml-1.1/binary.js +70 -0
  181. package/.output/server/node_modules/yaml/dist/schema/yaml-1.1/bool.js +29 -0
  182. package/.output/server/node_modules/yaml/dist/schema/yaml-1.1/float.js +50 -0
  183. package/.output/server/node_modules/yaml/dist/schema/yaml-1.1/int.js +76 -0
  184. package/.output/server/node_modules/yaml/dist/schema/yaml-1.1/merge.js +71 -0
  185. package/.output/server/node_modules/yaml/dist/schema/yaml-1.1/omap.js +77 -0
  186. package/.output/server/node_modules/yaml/dist/schema/yaml-1.1/pairs.js +82 -0
  187. package/.output/server/node_modules/yaml/dist/schema/yaml-1.1/schema.js +41 -0
  188. package/.output/server/node_modules/yaml/dist/schema/yaml-1.1/set.js +96 -0
  189. package/.output/server/node_modules/yaml/dist/schema/yaml-1.1/timestamp.js +105 -0
  190. package/.output/server/node_modules/yaml/dist/stringify/foldFlowLines.js +151 -0
  191. package/.output/server/node_modules/yaml/dist/stringify/stringify.js +132 -0
  192. package/.output/server/node_modules/yaml/dist/stringify/stringifyCollection.js +155 -0
  193. package/.output/server/node_modules/yaml/dist/stringify/stringifyComment.js +24 -0
  194. package/.output/server/node_modules/yaml/dist/stringify/stringifyDocument.js +87 -0
  195. package/.output/server/node_modules/yaml/dist/stringify/stringifyNumber.js +27 -0
  196. package/.output/server/node_modules/yaml/dist/stringify/stringifyPair.js +152 -0
  197. package/.output/server/node_modules/yaml/dist/stringify/stringifyString.js +338 -0
  198. package/.output/server/node_modules/yaml/dist/visit.js +236 -0
  199. package/.output/server/node_modules/yaml/package.json +97 -0
  200. package/.output/server/package.json +28 -0
  201. package/LICENSE +21 -0
  202. package/README.md +57 -1
  203. package/bin/launch.mjs +216 -0
  204. package/bin/shelfware.mjs +11 -0
  205. package/package.json +64 -5
  206. package/cli.mjs +0 -3
@@ -0,0 +1,673 @@
1
+ const URL_META_KEYS = /* @__PURE__ */ new Set([
2
+ "og:url",
3
+ "og:image",
4
+ "og:image:url",
5
+ "og:image:secure_url",
6
+ "og:video",
7
+ "og:video:url",
8
+ "og:video:secure_url",
9
+ "og:audio",
10
+ "og:audio:url",
11
+ "og:audio:secure_url",
12
+ "twitter:image",
13
+ "twitter:image:src",
14
+ "twitter:player",
15
+ "twitter:player:stream",
16
+ "payment:success_url"
17
+ ]);
18
+ const KNOWN_META_PROPERTIES = /* @__PURE__ */ new Set([
19
+ "article:author",
20
+ "article:expiration_time",
21
+ "article:modified_time",
22
+ "article:published_time",
23
+ "article:section",
24
+ "article:tag",
25
+ "book:author",
26
+ "book:isbn",
27
+ "book:release_date",
28
+ "book:tag",
29
+ "fb:app_id",
30
+ "og:audio",
31
+ "og:audio:secure_url",
32
+ "og:audio:type",
33
+ "og:audio:url",
34
+ "og:description",
35
+ "og:determiner",
36
+ "og:image",
37
+ "og:image:alt",
38
+ "og:image:height",
39
+ "og:image:secure_url",
40
+ "og:image:type",
41
+ "og:image:url",
42
+ "og:image:width",
43
+ "og:locale",
44
+ "og:locale:alternate",
45
+ "og:site_name",
46
+ "og:title",
47
+ "og:type",
48
+ "og:url",
49
+ "og:video",
50
+ "og:video:alt",
51
+ "og:video:duration",
52
+ "og:video:height",
53
+ "og:video:secure_url",
54
+ "og:video:type",
55
+ "og:video:url",
56
+ "og:video:width",
57
+ "payment:amount",
58
+ "payment:currency",
59
+ "payment:description",
60
+ "payment:expires_at",
61
+ "payment:id",
62
+ "payment:status",
63
+ "payment:success_url",
64
+ "profile:first_name",
65
+ "profile:gender",
66
+ "profile:last_name",
67
+ "profile:username"
68
+ ]);
69
+ const KNOWN_META_NAMES = /* @__PURE__ */ new Set([
70
+ "apple-itunes-app",
71
+ "apple-mobile-web-app-capable",
72
+ "apple-mobile-web-app-status-bar-style",
73
+ "apple-mobile-web-app-title",
74
+ "application-name",
75
+ "author",
76
+ "color-scheme",
77
+ "creator",
78
+ "description",
79
+ "fb:app_id",
80
+ "fediverse:creator",
81
+ "format-detection",
82
+ "generator",
83
+ "google-site-verification",
84
+ "google",
85
+ "googlebot",
86
+ "keywords",
87
+ "mobile-web-app-capable",
88
+ "msapplication-config",
89
+ "msapplication-tilecolor",
90
+ "msapplication-tileimage",
91
+ "publisher",
92
+ "rating",
93
+ "referrer",
94
+ "robots",
95
+ "theme-color",
96
+ "viewport",
97
+ "twitter:app:id:googleplay",
98
+ "twitter:app:id:ipad",
99
+ "twitter:app:id:iphone",
100
+ "twitter:app:name:googleplay",
101
+ "twitter:app:name:ipad",
102
+ "twitter:app:name:iphone",
103
+ "twitter:app:url:googleplay",
104
+ "twitter:app:url:ipad",
105
+ "twitter:app:url:iphone",
106
+ "twitter:card",
107
+ "twitter:creator",
108
+ "twitter:creator:id",
109
+ "twitter:data:1",
110
+ "twitter:data:2",
111
+ "twitter:description",
112
+ "twitter:image",
113
+ "twitter:image:alt",
114
+ "twitter:label:1",
115
+ "twitter:label:2",
116
+ "twitter:player",
117
+ "twitter:player:height",
118
+ "twitter:player:stream",
119
+ "twitter:player:width",
120
+ "twitter:site",
121
+ "twitter:site:id",
122
+ "twitter:title"
123
+ ]);
124
+ const TAG_PRIORITY_ALIASES = ["critical", "high", "low"];
125
+ const DEPRECATED_PROPS = {
126
+ children: { replacement: "innerHTML", ruleId: "deprecated-prop-children" },
127
+ hid: { replacement: "key", ruleId: "deprecated-prop-hid-vmid" },
128
+ vmid: { replacement: "key", ruleId: "deprecated-prop-hid-vmid" },
129
+ body: { replacement: "tagPosition: 'bodyClose'", ruleId: "deprecated-prop-body" }
130
+ };
131
+
132
+ function levenshtein(a, b) {
133
+ const m = a.length;
134
+ const n = b.length;
135
+ const d = Array.from({ length: n + 1 }, (_, i) => i);
136
+ for (let i = 1; i <= m; i++) {
137
+ let prev = i - 1;
138
+ d[0] = i;
139
+ for (let j = 1; j <= n; j++) {
140
+ const tmp = d[j];
141
+ d[j] = a[i - 1] === b[j - 1] ? prev : 1 + Math.min(prev, d[j], d[j - 1]);
142
+ prev = tmp;
143
+ }
144
+ }
145
+ return d[n];
146
+ }
147
+ function findClosestMatch(value, knownSet) {
148
+ const threshold = value.length <= 8 ? 2 : 3;
149
+ let best;
150
+ let bestDist = threshold + 1;
151
+ for (const known of knownSet) {
152
+ if (Math.abs(known.length - value.length) > threshold)
153
+ continue;
154
+ const dist = levenshtein(value, known);
155
+ if (dist < bestDist) {
156
+ bestDist = dist;
157
+ best = known;
158
+ }
159
+ }
160
+ return best;
161
+ }
162
+
163
+ const emptyMetaContent = (tag) => {
164
+ if (tag.tagType !== "meta")
165
+ return [];
166
+ if (!tag.keys.has("content"))
167
+ return [];
168
+ if (tag.props.content !== "")
169
+ return [];
170
+ const key = typeof tag.props.name === "string" && tag.props.name || typeof tag.props.property === "string" && tag.props.property || "meta";
171
+ const diag = {
172
+ ruleId: "empty-meta-content",
173
+ message: `Meta tag "${key}" has empty content.`,
174
+ at: { kind: "prop", key: "content" }
175
+ };
176
+ return [diag];
177
+ };
178
+
179
+ const RESOLVABLE_OBJECT = ["boolean", "function", "null", "object"];
180
+ const RESOLVABLE_TAG_LIST = ["array", "boolean", "function", "null"];
181
+ const RESOLVABLE_TITLE = ["boolean", "function", "null", "number", "object", "string"];
182
+ const ATTRIBUTE_SCALAR = ["boolean", "function", "null", "number", "string"];
183
+ const ATTRIBUTE_STRUCTURED = ["array", "boolean", "function", "null", "number", "object", "string"];
184
+ const INPUT_SHAPE_FIELDS = {
185
+ base: {
186
+ canonical: "base",
187
+ contexts: { head: RESOLVABLE_OBJECT }
188
+ },
189
+ bodyattrs: {
190
+ canonical: "bodyAttrs",
191
+ contexts: { head: RESOLVABLE_OBJECT }
192
+ },
193
+ class: {
194
+ canonical: "class",
195
+ contexts: {
196
+ bodyAttrs: ATTRIBUTE_STRUCTURED,
197
+ htmlAttrs: ATTRIBUTE_STRUCTURED
198
+ }
199
+ },
200
+ htmlattrs: {
201
+ canonical: "htmlAttrs",
202
+ contexts: { head: RESOLVABLE_OBJECT }
203
+ },
204
+ link: {
205
+ canonical: "link",
206
+ contexts: { head: RESOLVABLE_TAG_LIST }
207
+ },
208
+ meta: {
209
+ canonical: "meta",
210
+ contexts: { head: RESOLVABLE_TAG_LIST }
211
+ },
212
+ noscript: {
213
+ canonical: "noscript",
214
+ contexts: { head: RESOLVABLE_TAG_LIST }
215
+ },
216
+ script: {
217
+ canonical: "script",
218
+ contexts: { head: RESOLVABLE_TAG_LIST }
219
+ },
220
+ style: {
221
+ canonical: "style",
222
+ contexts: {
223
+ bodyAttrs: ATTRIBUTE_STRUCTURED,
224
+ head: RESOLVABLE_TAG_LIST,
225
+ htmlAttrs: ATTRIBUTE_STRUCTURED
226
+ }
227
+ },
228
+ templateparams: {
229
+ canonical: "templateParams",
230
+ contexts: { head: ["object"] }
231
+ },
232
+ title: {
233
+ canonical: "title",
234
+ contexts: {
235
+ bodyAttrs: ATTRIBUTE_SCALAR,
236
+ head: RESOLVABLE_TITLE,
237
+ htmlAttrs: ATTRIBUTE_SCALAR,
238
+ seoMeta: RESOLVABLE_TITLE
239
+ }
240
+ },
241
+ titletemplate: {
242
+ canonical: "titleTemplate",
243
+ contexts: {
244
+ head: ["function", "null", "object", "string"],
245
+ seoMeta: ["function", "null", "object", "string"]
246
+ }
247
+ }
248
+ };
249
+ function includes(kinds, kind) {
250
+ return kinds?.includes(kind) === true;
251
+ }
252
+ function formatKinds(kinds) {
253
+ return kinds.join(", ");
254
+ }
255
+ function invalidKnownHeadField(input, key, shape, kind) {
256
+ const expected = shape.contexts[input.context];
257
+ if (includes(expected, kind))
258
+ return void 0;
259
+ if ((input.context === "htmlAttrs" || input.context === "bodyAttrs") && kind === "null")
260
+ return void 0;
261
+ if (input.context === "head") {
262
+ return {
263
+ ruleId: "invalid-input-shape",
264
+ message: `"${shape.canonical}" in a head input must be one of: ${formatKinds(expected || [])}. Received ${kind}.`,
265
+ at: { kind: "prop-value", key }
266
+ };
267
+ }
268
+ if (includes(shape.contexts.head, kind)) {
269
+ return {
270
+ ruleId: "invalid-input-shape",
271
+ message: `"${shape.canonical}" has a head input shape but appears in ${input.context}. Move it to the top-level head input.`,
272
+ at: { kind: "prop-value", key }
273
+ };
274
+ }
275
+ }
276
+ function invalidAttributeField(input, key, kind) {
277
+ if (includes(ATTRIBUTE_SCALAR, kind))
278
+ return void 0;
279
+ return {
280
+ ruleId: "invalid-input-shape",
281
+ message: `"${key}" in ${input.context} must resolve to a scalar attribute value. Received ${kind}.`,
282
+ at: { kind: "prop-value", key }
283
+ };
284
+ }
285
+ function validateInputShape(input) {
286
+ const diagnostics = [];
287
+ const isAttributes = input.context === "htmlAttrs" || input.context === "bodyAttrs";
288
+ for (const key of input.keys) {
289
+ const kind = input.valueKinds.get(key) ?? "unknown";
290
+ if (kind === "unknown")
291
+ continue;
292
+ const shape = INPUT_SHAPE_FIELDS[key.toLowerCase()];
293
+ const knownDiagnostic = shape ? invalidKnownHeadField(input, key, shape, kind) : void 0;
294
+ if (knownDiagnostic) {
295
+ diagnostics.push(knownDiagnostic);
296
+ continue;
297
+ }
298
+ if (isAttributes && !shape?.contexts[input.context]) {
299
+ const attributeDiagnostic = invalidAttributeField(input, key, kind);
300
+ if (attributeDiagnostic)
301
+ diagnostics.push(attributeDiagnostic);
302
+ }
303
+ }
304
+ return diagnostics;
305
+ }
306
+
307
+ const noDeprecatedProps = (tag) => {
308
+ const out = [];
309
+ for (const key of tag.keys) {
310
+ if (!(key in DEPRECATED_PROPS))
311
+ continue;
312
+ const { replacement } = DEPRECATED_PROPS[key];
313
+ if (key === "body") {
314
+ if (tag.props.body !== true)
315
+ continue;
316
+ const fix2 = tag.keys.has("tagPosition") ? void 0 : { type: "replace-prop", key: "body", newSource: `tagPosition: 'bodyClose'` };
317
+ out.push({
318
+ ruleId: "deprecated-prop-body",
319
+ message: `"body" was removed in v3 of unhead. Use "${replacement}" instead.`,
320
+ at: { kind: "prop", key },
321
+ fix: fix2
322
+ });
323
+ continue;
324
+ }
325
+ const newKey = key === "children" ? "innerHTML" : "key";
326
+ const fix = tag.keys.has(newKey) ? void 0 : { type: "rename-prop", key, newKey };
327
+ out.push({
328
+ ruleId: key === "children" ? "deprecated-prop-children" : "deprecated-prop-hid-vmid",
329
+ message: `"${key}" was removed in v3 of unhead. Use "${replacement}" instead.`,
330
+ at: { kind: "prop", key },
331
+ fix
332
+ });
333
+ }
334
+ return out;
335
+ };
336
+
337
+ const HTML_CHARS_RE = /[<>]/;
338
+ const noHtmlInTitle = (input) => {
339
+ const title = input.props.title;
340
+ if (typeof title !== "string" || !HTML_CHARS_RE.test(title))
341
+ return [];
342
+ const diag = {
343
+ ruleId: "html-in-title",
344
+ message: `Title contains HTML characters which will be escaped, not rendered: "${title}".`,
345
+ at: { kind: "prop-value", key: "title" }
346
+ };
347
+ return [diag];
348
+ };
349
+
350
+ const OG_PREFIX_RE = /^(?:og|article|book|profile|fb):/;
351
+ const noUnknownMeta = (tag) => {
352
+ if (tag.tagType !== "meta")
353
+ return [];
354
+ const out = [];
355
+ const property = tag.props.property;
356
+ if (typeof property === "string" && !KNOWN_META_PROPERTIES.has(property) && OG_PREFIX_RE.test(property)) {
357
+ const suggestion = findClosestMatch(property, KNOWN_META_PROPERTIES);
358
+ if (suggestion) {
359
+ out.push({
360
+ ruleId: "possible-typo",
361
+ message: `Unknown meta property "${property}". Did you mean "${suggestion}"?`,
362
+ at: { kind: "prop-value", key: "property" },
363
+ fix: { type: "replace-prop-value", key: "property", newSource: `'${suggestion}'` }
364
+ });
365
+ }
366
+ }
367
+ const name = tag.props.name;
368
+ if (typeof name === "string") {
369
+ const lower = name.toLowerCase();
370
+ if (!KNOWN_META_NAMES.has(lower) && (lower.startsWith("twitter:") || lower.startsWith("fediverse:") || !lower.includes(":"))) {
371
+ const suggestion = findClosestMatch(lower, KNOWN_META_NAMES);
372
+ if (suggestion) {
373
+ out.push({
374
+ ruleId: "possible-typo",
375
+ message: `Unknown meta name "${name}". Did you mean "${suggestion}"?`,
376
+ at: { kind: "prop-value", key: "name" },
377
+ fix: { type: "replace-prop-value", key: "name", newSource: `'${suggestion}'` }
378
+ });
379
+ }
380
+ }
381
+ }
382
+ return out;
383
+ };
384
+
385
+ function isAbsolute(url) {
386
+ return url.startsWith("http://") || url.startsWith("https://");
387
+ }
388
+ const nonAbsoluteCanonical = (tag) => {
389
+ if (tag.tagType !== "link")
390
+ return [];
391
+ if (tag.props.rel !== "canonical")
392
+ return [];
393
+ const href = tag.props.href;
394
+ if (typeof href !== "string")
395
+ return [];
396
+ if (isAbsolute(href))
397
+ return [];
398
+ const diag = {
399
+ ruleId: "non-absolute-canonical",
400
+ message: `Canonical URL should be absolute, received "${href}".`,
401
+ at: { kind: "prop-value", key: "href" }
402
+ };
403
+ return [diag];
404
+ };
405
+
406
+ const ALIASES = ["critical", "high", "low"];
407
+ const numericTagPriority = (tag) => {
408
+ const value = tag.props.tagPriority;
409
+ if (typeof value !== "number")
410
+ return [];
411
+ const diag = {
412
+ ruleId: "numeric-tag-priority",
413
+ message: `Numeric tagPriority (${value}) is brittle. Prefer an alias ('critical' | 'high' | 'low') or 'before:<key>' / 'after:<key>'.`,
414
+ at: { kind: "prop-value", key: "tagPriority" },
415
+ suggestions: ALIASES.map((alias) => ({
416
+ message: `Replace with '${alias}'`,
417
+ fix: { type: "replace-prop-value", key: "tagPriority", newSource: `'${alias}'` }
418
+ }))
419
+ };
420
+ return [diag];
421
+ };
422
+
423
+ const TAG_TO_HELPER = {
424
+ link: "defineLink",
425
+ script: "defineScript"
426
+ };
427
+ const preferDefineHelpers = (tag, ctx) => {
428
+ if (!tag.inArray)
429
+ return [];
430
+ const helper = TAG_TO_HELPER[tag.tagType];
431
+ if (!helper)
432
+ return [];
433
+ const localName = ctx?.importedHelpers?.get(helper);
434
+ const imported = localName !== void 0;
435
+ const fix = { type: "wrap-tag", wrapWith: localName ?? helper };
436
+ const diag = {
437
+ ruleId: "prefer-define-helpers",
438
+ message: `Wrap this ${tag.tagType} entry in \`${helper}()\` so unhead can narrow its type.`,
439
+ fix: imported ? fix : void 0,
440
+ suggestions: imported ? void 0 : [{ message: `Wrap in \`${helper}()\` (you may need to import it).`, fix: { type: "wrap-tag", wrapWith: helper } }]
441
+ };
442
+ return [diag];
443
+ };
444
+
445
+ const preloadMissingAs = (tag) => {
446
+ if (tag.tagType !== "link")
447
+ return [];
448
+ if (tag.props.rel !== "preload")
449
+ return [];
450
+ if (tag.keys.has("as"))
451
+ return [];
452
+ const diag = {
453
+ ruleId: "preload-missing-as",
454
+ message: 'Preload link is missing the required "as" attribute.'
455
+ };
456
+ return [diag];
457
+ };
458
+ const preloadFontCrossorigin = (tag) => {
459
+ if (tag.tagType !== "link")
460
+ return [];
461
+ if (tag.props.rel !== "preload")
462
+ return [];
463
+ if (tag.props.as !== "font")
464
+ return [];
465
+ if (tag.keys.has("crossorigin"))
466
+ return [];
467
+ const diag = {
468
+ ruleId: "preload-font-crossorigin",
469
+ message: 'Font preload requires "crossorigin" \u2014 without it the font will be fetched twice.',
470
+ fix: { type: "insert-after-prop", afterKey: "as", insert: `, crossorigin: 'anonymous'` }
471
+ };
472
+ return [diag];
473
+ };
474
+
475
+ const robotsConflict = (tag) => {
476
+ if (tag.tagType !== "meta")
477
+ return [];
478
+ if (tag.props.name !== "robots")
479
+ return [];
480
+ const content = tag.props.content;
481
+ if (typeof content !== "string")
482
+ return [];
483
+ const directives = content.toLowerCase().split(",").map((d) => d.trim());
484
+ const out = [];
485
+ if (directives.includes("index") && directives.includes("noindex")) {
486
+ out.push({
487
+ ruleId: "robots-conflict",
488
+ message: 'Robots meta has conflicting "index" and "noindex" directives.'
489
+ });
490
+ }
491
+ if (directives.includes("follow") && directives.includes("nofollow")) {
492
+ out.push({
493
+ ruleId: "robots-conflict",
494
+ message: 'Robots meta has conflicting "follow" and "nofollow" directives.'
495
+ });
496
+ }
497
+ return out;
498
+ };
499
+
500
+ const deferOnModuleScript = (tag) => {
501
+ if (tag.tagType !== "script")
502
+ return [];
503
+ if (tag.props.type !== "module")
504
+ return [];
505
+ if (tag.props.defer !== true)
506
+ return [];
507
+ const diag = {
508
+ ruleId: "defer-on-module-script",
509
+ message: '"defer" is redundant on module scripts. Modules are deferred by default.',
510
+ at: { kind: "prop", key: "defer" },
511
+ fix: { type: "remove-prop", key: "defer" }
512
+ };
513
+ return [diag];
514
+ };
515
+ const scriptSrcWithContent = (tag) => {
516
+ if (tag.tagType !== "script")
517
+ return [];
518
+ if (typeof tag.props.src !== "string")
519
+ return [];
520
+ const hasInner = tag.keys.has("innerHTML") && tag.props.innerHTML !== "" || tag.keys.has("textContent") && tag.props.textContent !== "";
521
+ if (!hasInner)
522
+ return [];
523
+ const diag = {
524
+ ruleId: "script-src-with-content",
525
+ message: 'Script has both "src" and inline content. The browser will ignore the inline content.'
526
+ };
527
+ return [diag];
528
+ };
529
+
530
+ const NUMERIC_RE = /^\d+$/;
531
+ const twitterHandleMissingAt = (tag) => {
532
+ if (tag.tagType !== "meta")
533
+ return [];
534
+ const name = tag.props.name;
535
+ if (name !== "twitter:site" && name !== "twitter:creator")
536
+ return [];
537
+ const content = tag.props.content;
538
+ if (typeof content !== "string")
539
+ return [];
540
+ if (content.startsWith("@") || NUMERIC_RE.test(content))
541
+ return [];
542
+ const fixedSource = JSON.stringify(`@${content}`);
543
+ const diag = {
544
+ ruleId: "twitter-handle-missing-at",
545
+ message: `${name} should start with "@", received "${content}".`,
546
+ at: { kind: "prop-value", key: "content" },
547
+ fix: { type: "replace-prop-value", key: "content", newSource: fixedSource }
548
+ };
549
+ return [diag];
550
+ };
551
+
552
+ const USER_SCALABLE_NO_RE = /user-scalable\s*=\s*(?:no|0|false)(?:[\s,;]|$)/i;
553
+ const MAX_SCALE_RE = /maximum-scale\s*=\s*1(?:\.\d+)?(?:[\s,;]|$)/i;
554
+ const viewportUserScalable = (tag) => {
555
+ if (tag.tagType !== "meta")
556
+ return [];
557
+ if (tag.props.name !== "viewport")
558
+ return [];
559
+ const content = tag.props.content;
560
+ if (typeof content !== "string")
561
+ return [];
562
+ const out = [];
563
+ if (USER_SCALABLE_NO_RE.test(content)) {
564
+ out.push({
565
+ ruleId: "viewport-user-scalable",
566
+ message: 'viewport "user-scalable=no" prevents zooming and harms accessibility.'
567
+ });
568
+ }
569
+ if (MAX_SCALE_RE.test(content)) {
570
+ out.push({
571
+ ruleId: "viewport-user-scalable",
572
+ message: 'viewport "maximum-scale=1" limits zooming and may harm accessibility.'
573
+ });
574
+ }
575
+ return out;
576
+ };
577
+
578
+ const TAG_TYPES = /* @__PURE__ */ new Set(["meta", "link", "script", "noscript", "style"]);
579
+ function valueKind(value) {
580
+ if (value === null)
581
+ return "null";
582
+ if (Array.isArray(value))
583
+ return "array";
584
+ const kind = typeof value;
585
+ if (kind === "boolean" || kind === "function" || kind === "number" || kind === "object" || kind === "string")
586
+ return kind;
587
+ return "unknown";
588
+ }
589
+ function inputShapeFromRuntime(context, input) {
590
+ const keys = /* @__PURE__ */ new Set();
591
+ const valueKinds = /* @__PURE__ */ new Map();
592
+ for (const [key, value] of Object.entries(input)) {
593
+ keys.add(key);
594
+ valueKinds.set(key, valueKind(value));
595
+ }
596
+ return { context, keys, valueKinds };
597
+ }
598
+ function tagInputFromRuntime(tag) {
599
+ if (!TAG_TYPES.has(tag.tag))
600
+ return void 0;
601
+ const props = {};
602
+ const keys = /* @__PURE__ */ new Set();
603
+ for (const [k, v] of Object.entries(tag.props)) {
604
+ keys.add(k);
605
+ if (v == null) {
606
+ if (tag.tag === "meta" && k === "content")
607
+ props[k] = "";
608
+ continue;
609
+ }
610
+ if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") {
611
+ if (tag.tag === "meta" && k === "name" && typeof v === "string")
612
+ props[k] = v.toLowerCase();
613
+ else
614
+ props[k] = v;
615
+ } else {
616
+ props[k] = String(v);
617
+ }
618
+ }
619
+ if (tag.tag === "script" || tag.tag === "style" || tag.tag === "noscript") {
620
+ if (tag.innerHTML != null && tag.innerHTML !== "") {
621
+ keys.add("innerHTML");
622
+ }
623
+ if (tag.textContent != null && tag.textContent !== "") {
624
+ keys.add("textContent");
625
+ }
626
+ }
627
+ if (tag.tagPriority != null) {
628
+ keys.add("tagPriority");
629
+ if (typeof tag.tagPriority === "string" || typeof tag.tagPriority === "number")
630
+ props.tagPriority = tag.tagPriority;
631
+ }
632
+ return {
633
+ tagType: tag.tag,
634
+ props,
635
+ keys
636
+ };
637
+ }
638
+ function titleInputFromRuntime(titleTag) {
639
+ if (titleTag.tag !== "title")
640
+ return void 0;
641
+ const text = titleTag.textContent ?? titleTag.innerHTML ?? "";
642
+ return {
643
+ callee: "runtime",
644
+ props: { title: text },
645
+ keys: /* @__PURE__ */ new Set(["title"])
646
+ };
647
+ }
648
+
649
+ const tagPredicates = {
650
+ "defer-on-module-script": deferOnModuleScript,
651
+ "empty-meta-content": emptyMetaContent,
652
+ "no-deprecated-props": noDeprecatedProps,
653
+ "no-unknown-meta": noUnknownMeta,
654
+ "non-absolute-canonical": nonAbsoluteCanonical,
655
+ "numeric-tag-priority": numericTagPriority,
656
+ "preload-font-crossorigin": preloadFontCrossorigin,
657
+ "preload-missing-as": preloadMissingAs,
658
+ "robots-conflict": robotsConflict,
659
+ "script-src-with-content": scriptSrcWithContent,
660
+ "twitter-handle-missing-at": twitterHandleMissingAt,
661
+ "viewport-user-scalable": viewportUserScalable
662
+ };
663
+ const inputShapePredicates = {
664
+ "invalid-input-shape": validateInputShape
665
+ };
666
+ const migrationTagPredicates = {
667
+ "prefer-define-helpers": preferDefineHelpers
668
+ };
669
+ const headInputPredicates = {
670
+ "no-html-in-title": noHtmlInTitle
671
+ };
672
+
673
+ export { DEPRECATED_PROPS as D, INPUT_SHAPE_FIELDS as I, KNOWN_META_NAMES as K, TAG_PRIORITY_ALIASES as T, URL_META_KEYS as U, KNOWN_META_PROPERTIES as a, inputShapePredicates as b, noHtmlInTitle as c, deferOnModuleScript as d, emptyMetaContent as e, findClosestMatch as f, noUnknownMeta as g, headInputPredicates as h, inputShapeFromRuntime as i, nonAbsoluteCanonical as j, numericTagPriority as k, levenshtein as l, migrationTagPredicates as m, noDeprecatedProps as n, preloadFontCrossorigin as o, preferDefineHelpers as p, preloadMissingAs as q, robotsConflict as r, scriptSrcWithContent as s, tagInputFromRuntime as t, tagPredicates as u, titleInputFromRuntime as v, twitterHandleMissingAt as w, validateInputShape as x, viewportUserScalable as y };