@templatical/quality 0.7.2 → 0.8.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.
package/dist/index.d.ts CHANGED
@@ -4,44 +4,7 @@ import { TemplateContent } from '../../../types/src/index.ts';
4
4
  import { TemplateContent as TemplateContent_2 } from '../../types/src/index.ts';
5
5
  import { TemplateSettings } from '../../types/src/index.ts';
6
6
 
7
- export declare interface A11yIssue {
8
- /** Block id, or null for template-level issues. */
9
- blockId: string | null;
10
- ruleId: string;
11
- severity: Exclude<Severity, "off">;
12
- message: string;
13
- fix?: A11yPatch;
14
- }
15
-
16
- export declare interface A11yOptions {
17
- /**
18
- * Fully disable linting. When true, the editor skips lazy-loading the
19
- * package, hides the sidebar tab, and suppresses inline badges.
20
- */
21
- disabled?: boolean;
22
- /** Locale for vague-text dictionaries and message text. Falls back to `en`. */
23
- locale?: string;
24
- /** Per-rule severity override. Set to `'off'` to disable a specific rule. */
25
- rules?: Record<string, Severity>;
26
- thresholds?: Partial<A11yThresholds>;
27
- }
28
-
29
- export declare interface A11yPatch {
30
- description: string;
31
- apply: (ctx: A11yPatchContext) => void;
32
- }
33
-
34
- export declare interface A11yPatchContext {
35
- updateBlock: (blockId: string, patch: Partial<Block>) => void;
36
- updateSettings: (patch: Partial<TemplateSettings>) => void;
37
- }
38
-
39
- export declare interface A11yThresholds {
40
- altMaxLength: number;
41
- minFontSize: number;
42
- allCapsMinLength: number;
43
- minTouchTargetPx: number;
44
- }
7
+ export declare const ACCESSIBILITY_RULES: Rule[];
45
8
 
46
9
  export declare interface AnchorInfo {
47
10
  href: string;
@@ -52,7 +15,9 @@ export declare interface AnchorInfo {
52
15
  hasImageWithAlt: boolean;
53
16
  }
54
17
 
55
- export declare const DEFAULT_THRESHOLDS: A11yThresholds;
18
+ export declare const DEFAULT_A11Y_THRESHOLDS: LintThresholds;
19
+
20
+ export declare const DEFAULT_NON_PRODUCTION_HOSTS: string[];
56
21
 
57
22
  export declare type Dictionary = typeof en;
58
23
 
@@ -81,25 +46,53 @@ declare const en: {
81
46
  * Templates use `{name}` placeholders, interpolated by `formatMessage`.
82
47
  */
83
48
  declare const en_2: {
84
- "img-missing-alt": string;
85
- "img-alt-is-filename": string;
86
- "img-alt-too-long": string;
87
- "img-decorative-needs-empty-alt": string;
88
- "img-linked-no-context": string;
89
- "heading-empty": string;
90
- "heading-skip-level": string;
91
- "heading-multiple-h1": string;
92
- "link-empty": string;
93
- "link-vague-text": string;
94
- "link-href-empty": string;
95
- "link-target-blank-no-rel": string;
96
- "text-all-caps": string;
97
- "text-low-contrast": string;
98
- "text-too-small": string;
99
- "button-vague-label": string;
100
- "button-touch-target": string;
101
- "button-low-contrast": string;
102
- "missing-preheader": string;
49
+ "a11y.img-missing-alt": string;
50
+ "a11y.img-alt-is-filename": string;
51
+ "a11y.img-alt-too-long": string;
52
+ "a11y.img-decorative-needs-empty-alt": string;
53
+ "a11y.img-linked-no-context": string;
54
+ "a11y.heading-empty": string;
55
+ "a11y.heading-skip-level": string;
56
+ "a11y.heading-multiple-h1": string;
57
+ "a11y.link-empty": string;
58
+ "a11y.link-vague-text": string;
59
+ "a11y.link-href-empty": string;
60
+ "a11y.link-target-blank-no-rel": string;
61
+ "a11y.text-all-caps": string;
62
+ "a11y.text-low-contrast": string;
63
+ "a11y.text-too-small": string;
64
+ "a11y.button-vague-label": string;
65
+ "a11y.button-touch-target": string;
66
+ "a11y.button-low-contrast": string;
67
+ "a11y.missing-preheader": string;
68
+ };
69
+
70
+ /**
71
+ * English structure-rule messages. The source of truth — other locales
72
+ * annotate themselves `typeof en` so missing or extra keys fail typecheck.
73
+ *
74
+ * Templates use `{name}` placeholders, interpolated by `formatMessage`.
75
+ */
76
+ declare const en_3: {
77
+ "structure.duplicate-block-id": string;
78
+ "structure.section-column-mismatch": string;
79
+ "structure.nested-section": string;
80
+ "structure.empty-section": string;
81
+ "structure.empty-column": string;
82
+ };
83
+
84
+ /**
85
+ * English link-rule messages. The source of truth — other locales annotate
86
+ * themselves `typeof en` so missing or extra keys fail typecheck.
87
+ *
88
+ * Templates use `{name}` placeholders, interpolated by `formatLinkMessage`.
89
+ */
90
+ declare const en_4: {
91
+ "link.javascript-protocol": string;
92
+ "link.unsupported-protocol": string;
93
+ "link.malformed-mailto": string;
94
+ "link.malformed-tel": string;
95
+ "link.localhost-or-staging": string;
103
96
  };
104
97
 
105
98
  /**
@@ -115,6 +108,8 @@ export declare function extractAnchors(html: string): AnchorInfo[];
115
108
  */
116
109
  export declare function extractText(html: string): string;
117
110
 
111
+ export declare function formatLinkMessage(locale: string, ruleId: LinkRuleMessageId, params?: Record<string, string | number>): string;
112
+
118
113
  /**
119
114
  * Resolve a localized message for a rule. `params` interpolate `{name}`
120
115
  * placeholders. Falls back to English when the locale doesn't ship the
@@ -122,6 +117,8 @@ export declare function extractText(html: string): string;
122
117
  */
123
118
  export declare function formatMessage(locale: string, ruleId: RuleMessageId, params?: Record<string, string | number>): string;
124
119
 
120
+ export declare function formatStructureMessage(locale: string, ruleId: StructureRuleMessageId, params?: Record<string, string | number>): string;
121
+
125
122
  /**
126
123
  * WCAG 2.1 sRGB relative-luminance contrast.
127
124
  *
@@ -145,20 +142,94 @@ export declare function getContrastRatio(fg: string, bg: string): number;
145
142
  */
146
143
  export declare function getDictionary(_locale: string): Dictionary;
147
144
 
145
+ export declare function getLinkMessages(locale: string): LinkMessageMap;
146
+
148
147
  export declare function getMessages(locale: string): MessageMap;
149
148
 
149
+ export declare function getStructureMessages(locale: string): StructureMessageMap;
150
+
150
151
  export declare function isOpaqueHex(input: string | undefined | null): boolean;
151
152
 
152
- export declare function lintAccessibility(content: TemplateContent, options?: A11yOptions): A11yIssue[];
153
+ export declare const LINK_RULES: Rule[];
154
+
155
+ export declare type LinkMessageMap = typeof en_4;
156
+
157
+ export declare type LinkRuleMessageId = keyof LinkMessageMap;
158
+
159
+ export declare function lintAccessibility(content: TemplateContent, options?: LintOptions): LintIssue[];
160
+
161
+ export declare interface LintIssue {
162
+ /** Block id, or null for template-level issues. */
163
+ blockId: string | null;
164
+ ruleId: string;
165
+ severity: Exclude<Severity, "off">;
166
+ message: string;
167
+ fix?: LintPatch;
168
+ }
169
+
170
+ export declare function lintLinks(content: TemplateContent, options?: LintOptions): LintIssue[];
171
+
172
+ export declare interface LintLinksOptions {
173
+ /**
174
+ * Host patterns that should flag as "staging / non-production".
175
+ * Each entry is a glob-style pattern matched against the URL host.
176
+ * `*` matches any run of characters (including `.`), so `*.staging.*`
177
+ * matches `app.staging.example.com`.
178
+ *
179
+ * Default: ['localhost', '127.0.0.1', '0.0.0.0', '*.local',
180
+ * '*.staging.*', '*.dev.*']
181
+ */
182
+ nonProductionHosts?: string[];
183
+ }
184
+
185
+ export declare interface LintOptions {
186
+ /**
187
+ * Fully disable linting. When true, the editor skips lazy-loading the
188
+ * package, hides the sidebar tab, and suppresses inline badges.
189
+ */
190
+ disabled?: boolean;
191
+ /** Locale for vague-text dictionaries and message text. Falls back to `en`. */
192
+ locale?: string;
193
+ /** Per-rule severity override. Set to `'off'` to disable a specific rule. */
194
+ rules?: Record<string, Severity>;
195
+ thresholds?: Partial<LintThresholds>;
196
+ /** Per-linter knobs. Only `lintLinks` reads `links` today. */
197
+ links?: LintLinksOptions;
198
+ }
199
+
200
+ export declare interface LintPatch {
201
+ description: string;
202
+ apply: (ctx: LintPatchContext) => void;
203
+ }
204
+
205
+ export declare interface LintPatchContext {
206
+ updateBlock: (blockId: string, patch: Partial<Block>) => void;
207
+ updateSettings: (patch: Partial<TemplateSettings>) => void;
208
+ removeBlock: (blockId: string) => void;
209
+ }
210
+
211
+ export declare function lintStructure(content: TemplateContent, options?: LintOptions): LintIssue[];
212
+
213
+ export declare interface LintThresholds {
214
+ altMaxLength: number;
215
+ minFontSize: number;
216
+ allCapsMinLength: number;
217
+ minTouchTargetPx: number;
218
+ }
153
219
 
154
220
  export declare type MessageMap = typeof en_2;
155
221
 
156
222
  export declare function parseHex(input: string | undefined | null): Rgb | null;
157
223
 
224
+ export declare interface ResolvedLinksOptions {
225
+ nonProductionHosts: string[];
226
+ }
227
+
158
228
  export declare interface ResolvedOptions {
159
229
  locale: string;
160
230
  rules: Record<string, Severity>;
161
- thresholds: A11yThresholds;
231
+ thresholds: LintThresholds;
232
+ links: ResolvedLinksOptions;
162
233
  /** Returns the effective severity for a rule (override or default). */
163
234
  severity: (ruleId: string) => Severity;
164
235
  }
@@ -186,7 +257,7 @@ export declare interface RuleHit {
186
257
  blockId: string | null;
187
258
  /** Interpolation values for the rule's localized message template. */
188
259
  params?: Record<string, string | number>;
189
- fix?: A11yPatch;
260
+ fix?: LintPatch;
190
261
  }
191
262
 
192
263
  export declare type RuleMessageId = keyof MessageMap;
@@ -198,14 +269,32 @@ export declare interface RuleMeta {
198
269
  severity: Exclude<Severity, "off">;
199
270
  }
200
271
 
201
- export declare const RULES: Rule[];
202
-
203
272
  export declare type Severity = "error" | "warning" | "info" | "off";
204
273
 
274
+ export declare const STRUCTURE_RULES: Rule[];
275
+
276
+ export declare type StructureMessageMap = typeof en_3;
277
+
278
+ export declare type StructureRuleMessageId = keyof StructureMessageMap;
279
+
205
280
  export declare const SUPPORTED_DICTIONARY_LOCALES: string[];
206
281
 
282
+ export declare const SUPPORTED_LINK_MESSAGE_LOCALES: string[];
283
+
207
284
  export declare const SUPPORTED_MESSAGE_LOCALES: string[];
208
285
 
286
+ export declare const SUPPORTED_STRUCTURE_MESSAGE_LOCALES: string[];
287
+
288
+ export declare interface UrlOccurrence {
289
+ url: string;
290
+ blockId: string;
291
+ source: UrlSource;
292
+ /** Anchor text or block-derived label, if applicable. */
293
+ label?: string;
294
+ }
295
+
296
+ export declare type UrlSource = "anchor" | "button" | "image-link" | "video" | "menu-item" | "social-icon";
297
+
209
298
  export declare type Visitor = (block: Block, ctx: WalkContext) => void;
210
299
 
211
300
  /**
@@ -231,4 +320,20 @@ export declare interface WalkContext {
231
320
  resolvedBackgroundColor: string;
232
321
  }
233
322
 
323
+ /**
324
+ * Visit every URL-bearing field in the template tree.
325
+ *
326
+ * Sources covered:
327
+ * - anchor — `<a href>` inside `title.content`, `paragraph.content`,
328
+ * `html.content` (parsed via extractAnchors)
329
+ * - button — `button.url`
330
+ * - image-link — `image.linkUrl` (only when present + non-empty)
331
+ * - video — `video.url`
332
+ * - menu-item — `menu.items[i].url`
333
+ * - social-icon — `social.icons[i].url`
334
+ *
335
+ * Each rule iterates this list once and decides per occurrence.
336
+ */
337
+ export declare function walkUrls(content: TemplateContent_2): UrlOccurrence[];
338
+
234
339
  export { }