@jjlmoya/utils-developer 1.2.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 (228) hide show
  1. package/package.json +64 -0
  2. package/src/category/i18n/en.ts +34 -0
  3. package/src/category/i18n/es.ts +34 -0
  4. package/src/category/i18n/fr.ts +34 -0
  5. package/src/category/index.ts +31 -0
  6. package/src/category/seo.astro +15 -0
  7. package/src/components/PreviewNavSidebar.astro +116 -0
  8. package/src/components/PreviewToolbar.astro +143 -0
  9. package/src/data.ts +11 -0
  10. package/src/env.d.ts +5 -0
  11. package/src/index.ts +78 -0
  12. package/src/layouts/PreviewLayout.astro +117 -0
  13. package/src/pages/[locale]/[slug].astro +146 -0
  14. package/src/pages/[locale].astro +251 -0
  15. package/src/pages/index.astro +4 -0
  16. package/src/tests/css_specificity_logic.test.ts +39 -0
  17. package/src/tests/faq_count.test.ts +19 -0
  18. package/src/tests/locale_completeness.test.ts +42 -0
  19. package/src/tests/mocks/astro_mock.js +2 -0
  20. package/src/tests/no_h1_in_components.test.ts +48 -0
  21. package/src/tests/schemas_fulfillment.test.ts +23 -0
  22. package/src/tests/seo_length.test.ts +22 -0
  23. package/src/tests/title_quality.test.ts +55 -0
  24. package/src/tests/tool_validation.test.ts +17 -0
  25. package/src/tool/aspectRatio/bibliography.astro +14 -0
  26. package/src/tool/aspectRatio/component.astro +468 -0
  27. package/src/tool/aspectRatio/i18n/en.ts +177 -0
  28. package/src/tool/aspectRatio/i18n/es.ts +177 -0
  29. package/src/tool/aspectRatio/i18n/fr.ts +177 -0
  30. package/src/tool/aspectRatio/index.ts +34 -0
  31. package/src/tool/aspectRatio/seo.astro +14 -0
  32. package/src/tool/aspectRatio/ui.ts +12 -0
  33. package/src/tool/calculadoraTiempoDatos/bibliography.astro +14 -0
  34. package/src/tool/calculadoraTiempoDatos/component.astro +829 -0
  35. package/src/tool/calculadoraTiempoDatos/i18n/en.ts +150 -0
  36. package/src/tool/calculadoraTiempoDatos/i18n/es.ts +163 -0
  37. package/src/tool/calculadoraTiempoDatos/i18n/fr.ts +150 -0
  38. package/src/tool/calculadoraTiempoDatos/index.ts +33 -0
  39. package/src/tool/calculadoraTiempoDatos/seo.astro +14 -0
  40. package/src/tool/calculadoraTiempoDatos/ui.ts +24 -0
  41. package/src/tool/colorConverter/bibliography.astro +14 -0
  42. package/src/tool/colorConverter/component.astro +504 -0
  43. package/src/tool/colorConverter/i18n/en.ts +126 -0
  44. package/src/tool/colorConverter/i18n/es.ts +126 -0
  45. package/src/tool/colorConverter/i18n/fr.ts +126 -0
  46. package/src/tool/colorConverter/index.ts +33 -0
  47. package/src/tool/colorConverter/seo.astro +14 -0
  48. package/src/tool/colorConverter/ui.ts +16 -0
  49. package/src/tool/conversorExcelCsvHtml/bibliography.astro +14 -0
  50. package/src/tool/conversorExcelCsvHtml/component.astro +595 -0
  51. package/src/tool/conversorExcelCsvHtml/i18n/en.ts +129 -0
  52. package/src/tool/conversorExcelCsvHtml/i18n/es.ts +129 -0
  53. package/src/tool/conversorExcelCsvHtml/i18n/fr.ts +129 -0
  54. package/src/tool/conversorExcelCsvHtml/index.ts +33 -0
  55. package/src/tool/conversorExcelCsvHtml/seo.astro +14 -0
  56. package/src/tool/conversorExcelCsvHtml/ui.ts +14 -0
  57. package/src/tool/cronGenerator/bibliography.astro +14 -0
  58. package/src/tool/cronGenerator/component.astro +667 -0
  59. package/src/tool/cronGenerator/i18n/en.ts +191 -0
  60. package/src/tool/cronGenerator/i18n/es.ts +191 -0
  61. package/src/tool/cronGenerator/i18n/fr.ts +191 -0
  62. package/src/tool/cronGenerator/index.ts +34 -0
  63. package/src/tool/cronGenerator/seo.astro +14 -0
  64. package/src/tool/cronGenerator/ui.ts +45 -0
  65. package/src/tool/cssSpecificityCalculator/bibliography.astro +14 -0
  66. package/src/tool/cssSpecificityCalculator/component.astro +292 -0
  67. package/src/tool/cssSpecificityCalculator/i18n/en.ts +168 -0
  68. package/src/tool/cssSpecificityCalculator/i18n/es.ts +168 -0
  69. package/src/tool/cssSpecificityCalculator/i18n/fr.ts +168 -0
  70. package/src/tool/cssSpecificityCalculator/index.ts +34 -0
  71. package/src/tool/cssSpecificityCalculator/logic.ts +100 -0
  72. package/src/tool/cssSpecificityCalculator/seo.astro +14 -0
  73. package/src/tool/cssSpecificityCalculator/ui-controller.ts +57 -0
  74. package/src/tool/cssSpecificityCalculator/ui.ts +11 -0
  75. package/src/tool/cssToInlineConverter/bibliography.astro +14 -0
  76. package/src/tool/cssToInlineConverter/component.astro +308 -0
  77. package/src/tool/cssToInlineConverter/i18n/en.ts +160 -0
  78. package/src/tool/cssToInlineConverter/i18n/es.ts +160 -0
  79. package/src/tool/cssToInlineConverter/i18n/fr.ts +160 -0
  80. package/src/tool/cssToInlineConverter/index.ts +34 -0
  81. package/src/tool/cssToInlineConverter/seo.astro +14 -0
  82. package/src/tool/cssToInlineConverter/ui.ts +12 -0
  83. package/src/tool/duplicateCssRemover/bibliography.astro +14 -0
  84. package/src/tool/duplicateCssRemover/component.astro +353 -0
  85. package/src/tool/duplicateCssRemover/i18n/en.ts +167 -0
  86. package/src/tool/duplicateCssRemover/i18n/es.ts +167 -0
  87. package/src/tool/duplicateCssRemover/i18n/fr.ts +167 -0
  88. package/src/tool/duplicateCssRemover/index.ts +34 -0
  89. package/src/tool/duplicateCssRemover/seo.astro +14 -0
  90. package/src/tool/duplicateCssRemover/ui.ts +12 -0
  91. package/src/tool/generadorSecurityTxt/bibliography.astro +14 -0
  92. package/src/tool/generadorSecurityTxt/component.astro +473 -0
  93. package/src/tool/generadorSecurityTxt/i18n/en.ts +173 -0
  94. package/src/tool/generadorSecurityTxt/i18n/es.ts +173 -0
  95. package/src/tool/generadorSecurityTxt/i18n/fr.ts +173 -0
  96. package/src/tool/generadorSecurityTxt/index.ts +33 -0
  97. package/src/tool/generadorSecurityTxt/seo.astro +14 -0
  98. package/src/tool/generadorSecurityTxt/ui.ts +27 -0
  99. package/src/tool/hashGenerator/bibliography.astro +14 -0
  100. package/src/tool/hashGenerator/component.astro +367 -0
  101. package/src/tool/hashGenerator/i18n/en.ts +118 -0
  102. package/src/tool/hashGenerator/i18n/es.ts +118 -0
  103. package/src/tool/hashGenerator/i18n/fr.ts +118 -0
  104. package/src/tool/hashGenerator/index.ts +33 -0
  105. package/src/tool/hashGenerator/md5.ts +138 -0
  106. package/src/tool/hashGenerator/seo.astro +14 -0
  107. package/src/tool/hashGenerator/ui.ts +11 -0
  108. package/src/tool/inspectorCertificadosSsl/bibliography.astro +14 -0
  109. package/src/tool/inspectorCertificadosSsl/component.astro +580 -0
  110. package/src/tool/inspectorCertificadosSsl/i18n/en.ts +214 -0
  111. package/src/tool/inspectorCertificadosSsl/i18n/es.ts +214 -0
  112. package/src/tool/inspectorCertificadosSsl/i18n/fr.ts +214 -0
  113. package/src/tool/inspectorCertificadosSsl/index.ts +33 -0
  114. package/src/tool/inspectorCertificadosSsl/seo.astro +14 -0
  115. package/src/tool/inspectorCertificadosSsl/ui.ts +29 -0
  116. package/src/tool/jsonFormatter/bibliography.astro +14 -0
  117. package/src/tool/jsonFormatter/component.astro +590 -0
  118. package/src/tool/jsonFormatter/i18n/en.ts +156 -0
  119. package/src/tool/jsonFormatter/i18n/es.ts +156 -0
  120. package/src/tool/jsonFormatter/i18n/fr.ts +156 -0
  121. package/src/tool/jsonFormatter/index.ts +34 -0
  122. package/src/tool/jsonFormatter/seo.astro +14 -0
  123. package/src/tool/jsonFormatter/ui.ts +15 -0
  124. package/src/tool/keycode/bibliography.astro +14 -0
  125. package/src/tool/keycode/component.astro +540 -0
  126. package/src/tool/keycode/i18n/en.ts +160 -0
  127. package/src/tool/keycode/i18n/es.ts +160 -0
  128. package/src/tool/keycode/i18n/fr.ts +160 -0
  129. package/src/tool/keycode/index.ts +34 -0
  130. package/src/tool/keycode/seo.astro +14 -0
  131. package/src/tool/keycode/ui.ts +10 -0
  132. package/src/tool/llmCostCalculator/bibliography.astro +14 -0
  133. package/src/tool/llmCostCalculator/component.astro +488 -0
  134. package/src/tool/llmCostCalculator/data.ts +99 -0
  135. package/src/tool/llmCostCalculator/i18n/en.ts +165 -0
  136. package/src/tool/llmCostCalculator/i18n/es.ts +165 -0
  137. package/src/tool/llmCostCalculator/i18n/fr.ts +165 -0
  138. package/src/tool/llmCostCalculator/index.ts +34 -0
  139. package/src/tool/llmCostCalculator/seo.astro +14 -0
  140. package/src/tool/llmCostCalculator/ui.ts +11 -0
  141. package/src/tool/mobileMockupGenerator/app.ts +614 -0
  142. package/src/tool/mobileMockupGenerator/bibliography.astro +14 -0
  143. package/src/tool/mobileMockupGenerator/component.astro +1044 -0
  144. package/src/tool/mobileMockupGenerator/constants.ts +48 -0
  145. package/src/tool/mobileMockupGenerator/i18n/en.ts +121 -0
  146. package/src/tool/mobileMockupGenerator/i18n/es.ts +121 -0
  147. package/src/tool/mobileMockupGenerator/i18n/fr.ts +121 -0
  148. package/src/tool/mobileMockupGenerator/index.ts +33 -0
  149. package/src/tool/mobileMockupGenerator/rendering.ts +189 -0
  150. package/src/tool/mobileMockupGenerator/seo.astro +14 -0
  151. package/src/tool/mobileMockupGenerator/store.ts +145 -0
  152. package/src/tool/mobileMockupGenerator/types.ts +41 -0
  153. package/src/tool/mobileMockupGenerator/ui.ts +48 -0
  154. package/src/tool/musicalTypography/bibliography.astro +14 -0
  155. package/src/tool/musicalTypography/component.astro +480 -0
  156. package/src/tool/musicalTypography/i18n/en.ts +179 -0
  157. package/src/tool/musicalTypography/i18n/es.ts +179 -0
  158. package/src/tool/musicalTypography/i18n/fr.ts +179 -0
  159. package/src/tool/musicalTypography/index.ts +33 -0
  160. package/src/tool/musicalTypography/seo.astro +14 -0
  161. package/src/tool/musicalTypography/ui.ts +25 -0
  162. package/src/tool/placeholderGenerator/bibliography.astro +14 -0
  163. package/src/tool/placeholderGenerator/component.astro +499 -0
  164. package/src/tool/placeholderGenerator/i18n/en.ts +176 -0
  165. package/src/tool/placeholderGenerator/i18n/es.ts +176 -0
  166. package/src/tool/placeholderGenerator/i18n/fr.ts +176 -0
  167. package/src/tool/placeholderGenerator/index.ts +34 -0
  168. package/src/tool/placeholderGenerator/seo.astro +14 -0
  169. package/src/tool/placeholderGenerator/ui.ts +14 -0
  170. package/src/tool/promptLibrary/bibliography.astro +14 -0
  171. package/src/tool/promptLibrary/component.astro +1131 -0
  172. package/src/tool/promptLibrary/controller.ts +49 -0
  173. package/src/tool/promptLibrary/i18n/en.ts +131 -0
  174. package/src/tool/promptLibrary/i18n/es.ts +131 -0
  175. package/src/tool/promptLibrary/i18n/fr.ts +126 -0
  176. package/src/tool/promptLibrary/index.ts +33 -0
  177. package/src/tool/promptLibrary/seo.astro +14 -0
  178. package/src/tool/promptLibrary/ui.ts +29 -0
  179. package/src/tool/readabilityCalculator/bibliography.astro +14 -0
  180. package/src/tool/readabilityCalculator/component.astro +534 -0
  181. package/src/tool/readabilityCalculator/i18n/en.ts +133 -0
  182. package/src/tool/readabilityCalculator/i18n/es.ts +133 -0
  183. package/src/tool/readabilityCalculator/i18n/fr.ts +133 -0
  184. package/src/tool/readabilityCalculator/index.ts +33 -0
  185. package/src/tool/readabilityCalculator/seo.astro +14 -0
  186. package/src/tool/readabilityCalculator/ui.ts +27 -0
  187. package/src/tool/svgSanitizer/bibliography.astro +14 -0
  188. package/src/tool/svgSanitizer/component.astro +554 -0
  189. package/src/tool/svgSanitizer/i18n/en.ts +125 -0
  190. package/src/tool/svgSanitizer/i18n/es.ts +125 -0
  191. package/src/tool/svgSanitizer/i18n/fr.ts +125 -0
  192. package/src/tool/svgSanitizer/index.ts +33 -0
  193. package/src/tool/svgSanitizer/seo.astro +14 -0
  194. package/src/tool/svgSanitizer/ui.ts +18 -0
  195. package/src/tool/svgToCss/bibliography.astro +14 -0
  196. package/src/tool/svgToCss/component.astro +381 -0
  197. package/src/tool/svgToCss/i18n/en.ts +174 -0
  198. package/src/tool/svgToCss/i18n/es.ts +174 -0
  199. package/src/tool/svgToCss/i18n/fr.ts +174 -0
  200. package/src/tool/svgToCss/index.ts +34 -0
  201. package/src/tool/svgToCss/seo.astro +14 -0
  202. package/src/tool/svgToCss/ui.ts +13 -0
  203. package/src/tool/urlCleaner/bibliography.astro +14 -0
  204. package/src/tool/urlCleaner/component.astro +458 -0
  205. package/src/tool/urlCleaner/i18n/en.ts +116 -0
  206. package/src/tool/urlCleaner/i18n/es.ts +116 -0
  207. package/src/tool/urlCleaner/i18n/fr.ts +116 -0
  208. package/src/tool/urlCleaner/index.ts +33 -0
  209. package/src/tool/urlCleaner/seo.astro +14 -0
  210. package/src/tool/urlCleaner/ui.ts +12 -0
  211. package/src/tool/urlEncoderDecoder/bibliography.astro +14 -0
  212. package/src/tool/urlEncoderDecoder/component.astro +291 -0
  213. package/src/tool/urlEncoderDecoder/i18n/en.ts +162 -0
  214. package/src/tool/urlEncoderDecoder/i18n/es.ts +162 -0
  215. package/src/tool/urlEncoderDecoder/i18n/fr.ts +162 -0
  216. package/src/tool/urlEncoderDecoder/index.ts +34 -0
  217. package/src/tool/urlEncoderDecoder/seo.astro +14 -0
  218. package/src/tool/urlEncoderDecoder/ui.ts +11 -0
  219. package/src/tool/utmGenerator/bibliography.astro +14 -0
  220. package/src/tool/utmGenerator/component.astro +270 -0
  221. package/src/tool/utmGenerator/i18n/en.ts +117 -0
  222. package/src/tool/utmGenerator/i18n/es.ts +117 -0
  223. package/src/tool/utmGenerator/i18n/fr.ts +117 -0
  224. package/src/tool/utmGenerator/index.ts +33 -0
  225. package/src/tool/utmGenerator/seo.astro +14 -0
  226. package/src/tool/utmGenerator/ui.ts +13 -0
  227. package/src/tools.ts +27 -0
  228. package/src/types.ts +72 -0
@@ -0,0 +1,473 @@
1
+ ---
2
+ import { Icon } from 'astro-icon/components';
3
+ import type { KnownLocale } from '../../types';
4
+ import type { GeneradorSecurityTxtUI } from './ui';
5
+
6
+ interface Props {
7
+ locale?: KnownLocale;
8
+ ui?: Record<string, unknown>;
9
+ }
10
+
11
+ const { ui } = Astro.props;
12
+ const t = (ui ?? {}) as GeneradorSecurityTxtUI;
13
+ ---
14
+
15
+ <div id="gst-root" data-i18n={JSON.stringify(t)} class="gst-root">
16
+ <div class="gst-container">
17
+ <div class="gst-sections">
18
+ <div class="gst-section-group gst-glass-card">
19
+ <div class="gst-group-title">
20
+ <Icon name="mdi:security" class="gst-icon" />
21
+ {t.titleBasicFields}
22
+ </div>
23
+
24
+ <div class="gst-input-field">
25
+ <label for="contact">
26
+ <Icon name="mdi:email-outline" class="gst-icon-small" />
27
+ {t.labelContact}
28
+ </label>
29
+ <input
30
+ type="text"
31
+ id="contact"
32
+ class="gst-premium-input"
33
+ placeholder={t.placeholderContact}
34
+ />
35
+ <div class="gst-field-tip">{t.contactTip}</div>
36
+ </div>
37
+
38
+ <div class="gst-input-field">
39
+ <label for="expires">
40
+ <Icon name="mdi:calendar-clock" class="gst-icon-small" />
41
+ {t.labelExpires}
42
+ </label>
43
+ <input type="date" id="expires" class="gst-premium-input" required />
44
+ <div class="gst-field-tip">{t.expiresTip}</div>
45
+ </div>
46
+ </div>
47
+
48
+ <div class="gst-section-group gst-glass-card">
49
+ <div class="gst-group-title">
50
+ <Icon name="mdi:plus-circle-outline" class="gst-icon" />
51
+ {t.titleOptionalFields}
52
+ </div>
53
+
54
+ <div class="gst-input-field">
55
+ <label for="encryption">
56
+ <Icon name="mdi:key-variant" class="gst-icon-small" />
57
+ {t.labelEncryption}
58
+ </label>
59
+ <input
60
+ type="text"
61
+ id="encryption"
62
+ class="gst-premium-input"
63
+ placeholder={t.placeholderEncryption}
64
+ />
65
+ <div class="gst-field-tip">{t.encryptionTip}</div>
66
+ </div>
67
+
68
+ <div class="gst-input-field">
69
+ <label for="policy">
70
+ <Icon name="mdi:file-document-outline" class="gst-icon-small" />
71
+ {t.labelPolicy}
72
+ </label>
73
+ <input
74
+ type="text"
75
+ id="policy"
76
+ class="gst-premium-input"
77
+ placeholder={t.placeholderPolicy}
78
+ />
79
+ <div class="gst-field-tip">{t.policyTip}</div>
80
+ </div>
81
+
82
+ <div class="gst-input-field">
83
+ <label for="acknowledgments">
84
+ <Icon name="mdi:trophy-outline" class="gst-icon-small" />
85
+ {t.labelAcknowledgments}
86
+ </label>
87
+ <input
88
+ type="text"
89
+ id="acknowledgments"
90
+ class="gst-premium-input"
91
+ placeholder={t.placeholderAcknowledgments}
92
+ />
93
+ <div class="gst-field-tip">{t.acknowledgmentsTip}</div>
94
+ </div>
95
+
96
+ <div class="gst-input-field">
97
+ <label for="hiring">
98
+ <Icon name="mdi:briefcase-outline" class="gst-icon-small" />
99
+ {t.labelHiring}
100
+ </label>
101
+ <input
102
+ type="text"
103
+ id="hiring"
104
+ class="gst-premium-input"
105
+ placeholder={t.placeholderHiring}
106
+ />
107
+ <div class="gst-field-tip">{t.hiringTip}</div>
108
+ </div>
109
+ </div>
110
+ </div>
111
+
112
+ <div class="gst-glass-card">
113
+ <div class="gst-result-header">
114
+ <div class="gst-result-title">{t.resultTitle}</div>
115
+ <div class="gst-actions">
116
+ <button id="btnCopy" class="gst-btn-secondary">
117
+ <Icon name="mdi:content-copy" class="gst-icon-small" />
118
+ {t.btnCopy}
119
+ </button>
120
+ <button id="btnDownload" class="gst-btn-primary">
121
+ <Icon name="mdi:download" class="gst-icon-small" />
122
+ {t.btnDownload}
123
+ </button>
124
+ </div>
125
+ </div>
126
+
127
+ <div class="gst-code-display" id="result-display">
128
+ <pre id="outputCode">{t.generatingMessage}</pre>
129
+ </div>
130
+ </div>
131
+ </div>
132
+ </div>
133
+
134
+ <script>
135
+ const contactInput = document.getElementById('contact') as HTMLInputElement;
136
+ const expiresInput = document.getElementById('expires') as HTMLInputElement;
137
+ const encryptionInput = document.getElementById('encryption') as HTMLInputElement;
138
+ const policyInput = document.getElementById('policy') as HTMLInputElement;
139
+ const acknowledgmentsInput = document.getElementById('acknowledgments') as HTMLInputElement;
140
+ const hiringInput = document.getElementById('hiring') as HTMLInputElement;
141
+ const outputCode = document.getElementById('outputCode') as HTMLPreElement;
142
+ const btnCopy = document.getElementById('btnCopy') as HTMLButtonElement;
143
+ const btnDownload = document.getElementById('btnDownload') as HTMLButtonElement;
144
+
145
+ function initDefaultExpiry(): void {
146
+ const today = new Date();
147
+ const sixMonthsLater = new Date(today.setMonth(today.getMonth() + 6));
148
+ expiresInput.value = sixMonthsLater.toISOString().split('T')[0];
149
+ }
150
+
151
+ function getUIData(): Record<string, string> {
152
+ return JSON.parse(document.getElementById('gst-root')?.dataset.i18n ?? '{}');
153
+ }
154
+
155
+ function formatExpiryDate(dateString: string): string {
156
+ if (!dateString) return '';
157
+ const expDate = new Date(dateString);
158
+ return expDate.toISOString();
159
+ }
160
+
161
+ function generateSecurityTxt(): string {
162
+ const lines: string[] = [];
163
+
164
+ if (contactInput.value) {
165
+ lines.push(`Contact: ${contactInput.value}`);
166
+ } else {
167
+ lines.push('# Contact: (Requerido)');
168
+ }
169
+
170
+ if (expiresInput.value) {
171
+ lines.push(`Expires: ${formatExpiryDate(expiresInput.value)}`);
172
+ }
173
+
174
+ if (encryptionInput.value) {
175
+ lines.push(`Encryption: ${encryptionInput.value}`);
176
+ }
177
+
178
+ if (policyInput.value) {
179
+ lines.push(`Policy: ${policyInput.value}`);
180
+ }
181
+
182
+ if (acknowledgmentsInput.value) {
183
+ lines.push(`Acknowledgments: ${acknowledgmentsInput.value}`);
184
+ }
185
+
186
+ if (hiringInput.value) {
187
+ lines.push(`Hiring: ${hiringInput.value}`);
188
+ }
189
+
190
+ const uiData = getUIData();
191
+ lines.push(`\n# ${uiData.comment || 'Generado'}`);
192
+
193
+ return lines.join('\n');
194
+ }
195
+
196
+ function updateOutput(): void {
197
+ outputCode.textContent = generateSecurityTxt();
198
+ }
199
+
200
+ function copyToClipboard(): void {
201
+ const text = outputCode.textContent || '';
202
+ navigator.clipboard.writeText(text).then(() => {
203
+ const originalHTML = btnCopy.innerHTML;
204
+ const uiData = getUIData();
205
+ btnCopy.innerHTML = '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="20 6 9 17 4 12"></polyline></svg> ' + (uiData.copiedMessage || 'Copied');
206
+ setTimeout(() => {
207
+ btnCopy.innerHTML = originalHTML;
208
+ }, 2000);
209
+ });
210
+ }
211
+
212
+ function downloadFile(): void {
213
+ const text = outputCode.textContent || '';
214
+ const blob = new Blob([text], { type: 'text/plain' });
215
+ const url = window.URL.createObjectURL(blob);
216
+ const a = document.createElement('a');
217
+ a.href = url;
218
+ a.download = 'security.txt';
219
+ a.click();
220
+ window.URL.revokeObjectURL(url);
221
+ }
222
+
223
+ initDefaultExpiry();
224
+ updateOutput();
225
+
226
+ const inputs = [
227
+ contactInput,
228
+ expiresInput,
229
+ encryptionInput,
230
+ policyInput,
231
+ acknowledgmentsInput,
232
+ hiringInput,
233
+ ];
234
+
235
+ inputs.forEach((input) => {
236
+ input.addEventListener('input', updateOutput);
237
+ });
238
+
239
+ btnCopy.addEventListener('click', copyToClipboard);
240
+ btnDownload.addEventListener('click', downloadFile);
241
+ </script>
242
+
243
+ <style>
244
+ .gst-root {
245
+ width: 100%;
246
+ }
247
+
248
+ .gst-container {
249
+ max-width: 900px;
250
+ margin: 0 auto;
251
+ display: flex;
252
+ flex-direction: column;
253
+ gap: 1.5rem;
254
+ padding-bottom: 2rem;
255
+ }
256
+
257
+ .gst-glass-card {
258
+ background: rgba(255, 255, 255, 0.7);
259
+ backdrop-filter: blur(12px);
260
+ border: 1px solid rgba(255, 255, 255, 0.3);
261
+ border-radius: 1.5rem;
262
+ padding: 1.5rem;
263
+ box-shadow: 0 8px 32px rgba(31, 38, 135, 0.07);
264
+ }
265
+
266
+ :global(.theme-dark) .gst-glass-card {
267
+ background: rgba(20, 20, 25, 0.7);
268
+ border: 1px solid rgba(255, 255, 255, 0.05);
269
+ }
270
+
271
+ .gst-sections {
272
+ display: grid;
273
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
274
+ gap: 1.5rem;
275
+ }
276
+
277
+ .gst-section-group {
278
+ display: flex;
279
+ flex-direction: column;
280
+ gap: 1.25rem;
281
+ }
282
+
283
+ .gst-group-title {
284
+ font-size: 0.875rem;
285
+ font-weight: 700;
286
+ color: rgb(100, 116, 139);
287
+ text-transform: uppercase;
288
+ letter-spacing: 0.05em;
289
+ display: flex;
290
+ align-items: center;
291
+ gap: 0.5rem;
292
+ margin-bottom: 0.5rem;
293
+ }
294
+
295
+ :global(.theme-dark) .gst-group-title {
296
+ color: rgb(148, 163, 184);
297
+ }
298
+
299
+ .gst-icon {
300
+ width: 1rem;
301
+ height: 1rem;
302
+ color: var(--gst-theme-color, #4f46e5);
303
+ }
304
+
305
+ .gst-input-field {
306
+ display: flex;
307
+ flex-direction: column;
308
+ gap: 0.5rem;
309
+ }
310
+
311
+ .gst-input-field label {
312
+ font-size: 0.875rem;
313
+ font-weight: 600;
314
+ color: rgb(51, 65, 85);
315
+ display: flex;
316
+ align-items: center;
317
+ gap: 0.5rem;
318
+ }
319
+
320
+ :global(.theme-dark) .gst-input-field label {
321
+ color: rgb(203, 213, 225);
322
+ }
323
+
324
+ .gst-icon-small {
325
+ width: 0.875rem;
326
+ height: 0.875rem;
327
+ }
328
+
329
+ .gst-premium-input {
330
+ background: white;
331
+ border: 1px solid rgb(226, 232, 240);
332
+ border-radius: 0.75rem;
333
+ padding: 0.75rem 1rem;
334
+ font-size: 1rem;
335
+ outline: none;
336
+ transition: all 0.2s ease;
337
+ width: 100%;
338
+ box-sizing: border-box;
339
+ }
340
+
341
+ :global(.theme-dark) .gst-premium-input {
342
+ background: rgb(15, 23, 42);
343
+ border-color: rgb(51, 65, 85);
344
+ color: white;
345
+ }
346
+
347
+ .gst-premium-input:focus {
348
+ border-color: var(--gst-theme-color, #4f46e5);
349
+ box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.1);
350
+ }
351
+
352
+ .gst-field-tip {
353
+ font-size: 0.75rem;
354
+ color: rgb(100, 116, 139);
355
+ }
356
+
357
+ :global(.theme-dark) .gst-field-tip {
358
+ color: rgb(148, 163, 184);
359
+ }
360
+
361
+ .gst-result-header {
362
+ display: flex;
363
+ justify-content: space-between;
364
+ align-items: center;
365
+ margin-bottom: 1rem;
366
+ flex-wrap: wrap;
367
+ gap: 1rem;
368
+ }
369
+
370
+ .gst-result-title {
371
+ font-size: 1.125rem;
372
+ font-weight: 700;
373
+ color: rgb(30, 41, 59);
374
+ }
375
+
376
+ :global(.theme-dark) .gst-result-title {
377
+ color: white;
378
+ }
379
+
380
+ .gst-code-display {
381
+ background: rgba(0, 0, 0, 0.03);
382
+ padding: 1.25rem;
383
+ border-radius: 1rem;
384
+ font-size: 0.9rem;
385
+ line-height: 1.5;
386
+ color: rgb(51, 65, 85);
387
+ min-height: 200px;
388
+ max-height: 400px;
389
+ overflow-y: auto;
390
+ border: 1px solid rgba(0, 0, 0, 0.05);
391
+ }
392
+
393
+ :global(.theme-dark) .gst-code-display {
394
+ background: rgba(255, 255, 255, 0.03);
395
+ color: rgb(203, 213, 225);
396
+ border-color: rgba(255, 255, 255, 0.05);
397
+ }
398
+
399
+ .gst-code-display pre {
400
+ margin: 0;
401
+ white-space: pre-wrap;
402
+ word-break: break-all;
403
+ }
404
+
405
+ .gst-actions {
406
+ display: flex;
407
+ gap: 0.75rem;
408
+ }
409
+
410
+ .gst-btn-primary {
411
+ background: var(--gst-theme-color, #4f46e5);
412
+ color: white;
413
+ border: none;
414
+ border-radius: 0.75rem;
415
+ padding: 0.6rem 1.25rem;
416
+ font-weight: 600;
417
+ cursor: pointer;
418
+ display: flex;
419
+ align-items: center;
420
+ gap: 0.5rem;
421
+ transition: all 0.2s ease;
422
+ }
423
+
424
+ .gst-btn-primary:hover {
425
+ opacity: 0.9;
426
+ transform: translateY(-1px);
427
+ }
428
+
429
+ .gst-btn-secondary {
430
+ background: white;
431
+ color: rgb(30, 41, 59);
432
+ border: 1px solid rgb(226, 232, 240);
433
+ border-radius: 0.75rem;
434
+ padding: 0.6rem 1.25rem;
435
+ font-weight: 600;
436
+ cursor: pointer;
437
+ display: flex;
438
+ align-items: center;
439
+ gap: 0.5rem;
440
+ transition: all 0.2s ease;
441
+ }
442
+
443
+ :global(.theme-dark) .gst-btn-secondary {
444
+ background: rgb(30, 41, 59);
445
+ color: white;
446
+ border-color: rgb(51, 65, 85);
447
+ }
448
+
449
+ .gst-btn-secondary:hover {
450
+ background: rgb(248, 250, 252);
451
+ }
452
+
453
+ :global(.theme-dark) .gst-btn-secondary:hover {
454
+ background: rgb(51, 65, 85);
455
+ }
456
+
457
+ @media (max-width: 640px) {
458
+ .gst-result-header {
459
+ flex-direction: column;
460
+ align-items: stretch;
461
+ }
462
+
463
+ .gst-actions {
464
+ flex-direction: column;
465
+ width: 100%;
466
+ }
467
+
468
+ .gst-actions button {
469
+ width: 100%;
470
+ justify-content: center;
471
+ }
472
+ }
473
+ </style>
@@ -0,0 +1,173 @@
1
+ import type { WithContext, FAQPage, HowToThing, SoftwareApplication } from 'schema-dts';
2
+ import type { ToolLocaleContent } from '../../../types';
3
+ import type { GeneradorSecurityTxtUI } from '../ui';
4
+
5
+ const slug = 'security-txt-generator-rfc-9116';
6
+ const title = 'Security.txt Generator RFC 9116';
7
+ const description = 'Create your professional security.txt file to facilitate vulnerability reporting and comply with international web security standards.';
8
+
9
+ const faqData = [
10
+ {
11
+ question: 'What is the security.txt file?',
12
+ answer: 'It is a standard (RFC 9116) that allows websites to define how security researchers should contact them to responsibly report vulnerabilities.',
13
+ },
14
+ {
15
+ question: 'Where should the security.txt file be placed?',
16
+ answer: 'The recommended standard location is in the /.well-known/ folder of your domain, resulting in the URL https://example.com/.well-known/security.txt.',
17
+ },
18
+ {
19
+ question: 'Why is the expiration date mandatory?',
20
+ answer: 'To ensure that contact information does not become outdated. If the file does not have a valid expiration date, researchers may not trust that communication channels remain active.',
21
+ },
22
+ {
23
+ question: 'What fields are required in a security.txt?',
24
+ answer: 'According to RFC 9116, the required fields are "Contact" (with an email address or URL) and "Expires" (with a future date in ISO 8601 format).',
25
+ },
26
+ ];
27
+
28
+ const howToData = [
29
+ { name: 'Fill in the fields', text: 'Complete your email address or contact URL and select an expiration date.' },
30
+ { name: 'Add optional fields', text: 'Include additional information such as your PGP key, security policy or job board.' },
31
+ { name: 'Download or copy the file', text: 'Click on "Download .txt" or copy the content and save it as security.txt.' },
32
+ { name: 'Upload to server', text: 'Place the file in the /.well-known/ folder of your domain.' },
33
+ ];
34
+
35
+ const faqSchema: WithContext<FAQPage> = {
36
+ '@context': 'https://schema.org',
37
+ '@type': 'FAQPage',
38
+ mainEntity: faqData.map((item) => ({
39
+ '@type': 'Question',
40
+ name: item.question,
41
+ acceptedAnswer: { '@type': 'Answer', text: item.answer },
42
+ })),
43
+ };
44
+
45
+ const howToSchema: WithContext<HowToThing> = {
46
+ '@context': 'https://schema.org',
47
+ '@type': 'HowTo',
48
+ name: title,
49
+ description,
50
+ step: howToData.map((s, i) => ({ '@type': 'HowToStep', position: i + 1, name: s.name, text: s.text })),
51
+ };
52
+
53
+ const appSchema: WithContext<SoftwareApplication> = {
54
+ '@context': 'https://schema.org',
55
+ '@type': 'SoftwareApplication',
56
+ name: title,
57
+ description,
58
+ applicationCategory: 'UtilityApplication',
59
+ operatingSystem: 'All',
60
+ offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' },
61
+ inLanguage: 'en',
62
+ };
63
+
64
+ const ui: GeneradorSecurityTxtUI = {
65
+ titleBasicFields: 'Required Fields',
66
+ labelContact: 'Contact (Email or URL)',
67
+ placeholderContact: 'mailto:security@example.com or https://example.com/contact',
68
+ contactTip: 'Address where vulnerability reports should be sent.',
69
+ labelExpires: 'Expiration Date',
70
+ expiresTip: 'Should not exceed one year in the future.',
71
+ titleOptionalFields: 'Optional Fields',
72
+ labelEncryption: 'Public Key',
73
+ placeholderEncryption: 'https://example.com/pgp-key.txt',
74
+ encryptionTip: 'Link to your PGP key for encrypted reports.',
75
+ labelPolicy: 'Security Policy',
76
+ placeholderPolicy: 'https://example.com/security/policy/',
77
+ policyTip: 'Page detailing how to handle vulnerabilities.',
78
+ labelAcknowledgments: 'Acknowledgments',
79
+ placeholderAcknowledgments: 'https://example.com/security/hall-of-fame/',
80
+ acknowledgmentsTip: 'Page thanking security researchers.',
81
+ labelHiring: 'Job Board',
82
+ placeholderHiring: 'https://example.com/jobs',
83
+ hiringTip: 'Link to security job openings.',
84
+ resultTitle: 'Preview (security.txt)',
85
+ btnCopy: 'Copy Code',
86
+ btnDownload: 'Download .txt',
87
+ copiedMessage: 'Copied',
88
+ generatingMessage: 'Generating security.txt file...',
89
+ comment: 'Generated',
90
+ };
91
+
92
+ export const content: ToolLocaleContent<GeneradorSecurityTxtUI> = {
93
+ slug,
94
+ title,
95
+ description,
96
+ ui,
97
+ faqTitle: 'Frequently Asked Questions',
98
+ faq: faqData,
99
+ bibliographyTitle: 'Technical Resources on Security.txt',
100
+ bibliography: [
101
+ { name: 'RFC 9116: A File Format to Aid in Security Vulnerability Disclosure', url: 'https://datatracker.ietf.org/doc/html/rfc9116' },
102
+ { name: 'Security.txt Official Website', url: 'https://securitytxt.org/' },
103
+ { name: 'OWASP: Vulnerability Disclosure Cheat Sheet', url: 'https://cheatsheetseries.owasp.org/cheatsheets/Vulnerability_Disclosure_Cheat_Sheet.html' },
104
+ ],
105
+ howTo: howToData,
106
+ schemas: [faqSchema, howToSchema, appSchema],
107
+ seo: [
108
+ { type: 'title', text: 'What is a Security.txt file and why do you need to generate one?', level: 2 },
109
+ {
110
+ type: 'paragraph',
111
+ html: 'In today\'s cybersecurity landscape, transparency and communication are essential. If you\'re a system administrator, web developer, or digital business owner, you\'re probably already familiar with text files that help machines understand your website, like <code>robots.txt</code> or <code>ads.txt</code>. However, there is a lesser-known but vital standard for your platform\'s integrity: <strong>Security.txt</strong>, defined by <strong>RFC 9116</strong>.',
112
+ },
113
+ {
114
+ type: 'paragraph',
115
+ html: 'The purpose of <strong>generating a security.txt file</strong> is to provide security researchers with a standardized way to contact your website\'s administrators when they discover a vulnerability. Without this file, an ethical hacker who finds a flaw in your system might not know who to report it to, which often results in the information being lost, published without notice, or exploited by malicious actors.',
116
+ },
117
+ { type: 'title', text: 'How to create and install the Security.txt file following RFC 9116', level: 2 },
118
+ {
119
+ type: 'paragraph',
120
+ html: 'The <strong>security researcher contact standard</strong> dictates that this file must reside in a specific location on your server: the <code>/.well-known/</code> folder. Therefore, the final path is usually <code>https://yourdomain.com/.well-known/security.txt</code>. Although it is also allowed to place it in the root (<code>/security.txt</code>), the first option is preferred by automatic scanning tools.',
121
+ },
122
+ { type: 'title', text: 'Required fields you cannot miss', level: 3 },
123
+ {
124
+ type: 'paragraph',
125
+ html: 'When <strong>obtaining your security.txt code</strong>, you must ensure it includes at least two critical elements:',
126
+ },
127
+ {
128
+ type: 'list',
129
+ items: [
130
+ '<strong>Contact:</strong> The email address or URL of a form where reports should be sent. Must start with <code>mailto:</code> or <code>https://</code>.',
131
+ '<strong>Expires:</strong> A date in ISO 8601 format that indicates when the file\'s information is no longer valid. It is recommended not to set a date more than one year away.',
132
+ ],
133
+ },
134
+ { type: 'code', text: 'Contact: mailto:security@yourcompany.com\nExpires: 2025-12-31T23:59:59.000Z' },
135
+ { type: 'title', text: 'Optional fields for advanced security', level: 3 },
136
+ {
137
+ type: 'paragraph',
138
+ html: 'For sites seeking more robust <strong>website protection</strong>, the standard offers additional fields:',
139
+ },
140
+ {
141
+ type: 'list',
142
+ items: [
143
+ '<strong>Encryption:</strong> A link to your PGP public key so researchers can send you encrypted and secure information.',
144
+ '<strong>Policy:</strong> A link to your security policy page where you explain the responsible disclosure process.',
145
+ '<strong>Acknowledgments:</strong> A link to your "Hall of Fame" or appreciation wall for researchers.',
146
+ '<strong>Hiring:</strong> A link to your cybersecurity job openings.',
147
+ ],
148
+ },
149
+ { type: 'title', text: 'Benefits of using our free Security.txt generator', level: 2 },
150
+ {
151
+ type: 'paragraph',
152
+ html: 'Many people wonder <strong>how to get a website\'s security contact</strong> quickly. By using our tool, you ensure strict compliance with RFC 9116 format without having to read complex technical documents.',
153
+ },
154
+ {
155
+ type: 'paragraph',
156
+ html: 'Using a generator saves you common syntax errors. For example, forgetting the <code>mailto:</code> prefix or incorrectly formatting the timezone in the expiration date can cause researchers\' automated tools to ignore your file.',
157
+ },
158
+ { type: 'title', text: 'Impact on SEO and web reputation', level: 3 },
159
+ {
160
+ type: 'paragraph',
161
+ html: 'Although the <code>security.txt</code> file is not a direct ranking factor in Google like page speed or HTTPS, it does have an indirect impact. A website that manages vulnerabilities well avoids noisy hacks (defacement, spam injections) that ruin SEO in hours. Additionally, many corporate security rating platforms (such as SecurityScorecard or BitSight) award extra points to domains that implement this standard.',
162
+ },
163
+ { type: 'title', text: 'Conclusion: A simple step for a more secure web', level: 2 },
164
+ {
165
+ type: 'paragraph',
166
+ html: 'In summary, <strong>downloading security.txt</strong> and uploading it to your server is one of the fastest and most effective security maintenance tasks you can perform today. You make it easy for the "good guys", discourage the curious, and show the world that you take user privacy and data seriously.',
167
+ },
168
+ {
169
+ type: 'paragraph',
170
+ html: 'Don\'t wait for a security breach to regret not providing a communication channel. Use our <strong>online security.txt generator</strong> now and keep your digital ecosystem under control.',
171
+ },
172
+ ],
173
+ };