@templatical/quality 0.7.3 → 0.8.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.
package/dist/index.d.ts CHANGED
@@ -4,43 +4,12 @@ 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
- }
7
+ export declare const ACCESSIBILITY_RULES: Rule[];
38
8
 
39
- export declare interface A11yThresholds {
40
- altMaxLength: number;
41
- minFontSize: number;
42
- allCapsMinLength: number;
43
- minTouchTargetPx: number;
9
+ /** Options consumed only by the accessibility linter. */
10
+ export declare interface AccessibilityLintOptions {
11
+ rules?: RuleOverrides;
12
+ thresholds?: Partial<LintThresholds>;
44
13
  }
45
14
 
46
15
  export declare interface AnchorInfo {
@@ -52,7 +21,9 @@ export declare interface AnchorInfo {
52
21
  hasImageWithAlt: boolean;
53
22
  }
54
23
 
55
- export declare const DEFAULT_THRESHOLDS: A11yThresholds;
24
+ export declare const DEFAULT_A11Y_THRESHOLDS: LintThresholds;
25
+
26
+ export declare const DEFAULT_NON_PRODUCTION_HOSTS: string[];
56
27
 
57
28
  export declare type Dictionary = typeof en;
58
29
 
@@ -81,25 +52,53 @@ declare const en: {
81
52
  * Templates use `{name}` placeholders, interpolated by `formatMessage`.
82
53
  */
83
54
  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;
55
+ "a11y.img-missing-alt": string;
56
+ "a11y.img-alt-is-filename": string;
57
+ "a11y.img-alt-too-long": string;
58
+ "a11y.img-decorative-needs-empty-alt": string;
59
+ "a11y.img-linked-no-context": string;
60
+ "a11y.heading-empty": string;
61
+ "a11y.heading-skip-level": string;
62
+ "a11y.heading-multiple-h1": string;
63
+ "a11y.link-empty": string;
64
+ "a11y.link-vague-text": string;
65
+ "a11y.link-href-empty": string;
66
+ "a11y.link-target-blank-no-rel": string;
67
+ "a11y.text-all-caps": string;
68
+ "a11y.text-low-contrast": string;
69
+ "a11y.text-too-small": string;
70
+ "a11y.button-vague-label": string;
71
+ "a11y.button-touch-target": string;
72
+ "a11y.button-low-contrast": string;
73
+ "a11y.missing-preheader": string;
74
+ };
75
+
76
+ /**
77
+ * English structure-rule messages. The source of truth — other locales
78
+ * annotate themselves `typeof en` so missing or extra keys fail typecheck.
79
+ *
80
+ * Templates use `{name}` placeholders, interpolated by `formatMessage`.
81
+ */
82
+ declare const en_3: {
83
+ "structure.duplicate-block-id": string;
84
+ "structure.section-column-mismatch": string;
85
+ "structure.nested-section": string;
86
+ "structure.empty-section": string;
87
+ "structure.empty-column": string;
88
+ };
89
+
90
+ /**
91
+ * English link-rule messages. The source of truth — other locales annotate
92
+ * themselves `typeof en` so missing or extra keys fail typecheck.
93
+ *
94
+ * Templates use `{name}` placeholders, interpolated by `formatLinkMessage`.
95
+ */
96
+ declare const en_4: {
97
+ "link.javascript-protocol": string;
98
+ "link.unsupported-protocol": string;
99
+ "link.malformed-mailto": string;
100
+ "link.malformed-tel": string;
101
+ "link.localhost-or-staging": string;
103
102
  };
104
103
 
105
104
  /**
@@ -115,6 +114,8 @@ export declare function extractAnchors(html: string): AnchorInfo[];
115
114
  */
116
115
  export declare function extractText(html: string): string;
117
116
 
117
+ export declare function formatLinkMessage(locale: string, ruleId: LinkRuleMessageId, params?: Record<string, string | number>): string;
118
+
118
119
  /**
119
120
  * Resolve a localized message for a rule. `params` interpolate `{name}`
120
121
  * placeholders. Falls back to English when the locale doesn't ship the
@@ -122,6 +123,8 @@ export declare function extractText(html: string): string;
122
123
  */
123
124
  export declare function formatMessage(locale: string, ruleId: RuleMessageId, params?: Record<string, string | number>): string;
124
125
 
126
+ export declare function formatStructureMessage(locale: string, ruleId: StructureRuleMessageId, params?: Record<string, string | number>): string;
127
+
125
128
  /**
126
129
  * WCAG 2.1 sRGB relative-luminance contrast.
127
130
  *
@@ -145,20 +148,116 @@ export declare function getContrastRatio(fg: string, bg: string): number;
145
148
  */
146
149
  export declare function getDictionary(_locale: string): Dictionary;
147
150
 
151
+ export declare function getLinkMessages(locale: string): LinkMessageMap;
152
+
148
153
  export declare function getMessages(locale: string): MessageMap;
149
154
 
155
+ export declare function getStructureMessages(locale: string): StructureMessageMap;
156
+
157
+ /**
158
+ * `true` when no linter would run for the given options — either the
159
+ * global `disabled` flag is set, or every per-tool key is `false`.
160
+ *
161
+ * The editor uses this to skip lazy-loading `@templatical/quality`, hide
162
+ * the Issues sidebar tab, and suppress inline canvas badges. Headless
163
+ * callers can use it to short-circuit before any linter call.
164
+ */
165
+ export declare function isLintFullyDisabled(options: LintOptions | undefined): boolean;
166
+
150
167
  export declare function isOpaqueHex(input: string | undefined | null): boolean;
151
168
 
152
- export declare function lintAccessibility(content: TemplateContent, options?: A11yOptions): A11yIssue[];
169
+ export declare const LINK_RULES: Rule[];
170
+
171
+ export declare type LinkMessageMap = typeof en_4;
172
+
173
+ export declare type LinkRuleMessageId = keyof LinkMessageMap;
174
+
175
+ /** Options consumed only by the links linter. */
176
+ export declare interface LinksLintOptions {
177
+ rules?: RuleOverrides;
178
+ /**
179
+ * Host patterns that should flag as "staging / non-production".
180
+ * Each entry is a glob-style pattern matched against the URL host.
181
+ * `*` matches any run of characters (including `.`), so `*.staging.*`
182
+ * matches `app.staging.example.com`.
183
+ *
184
+ * Default: ['localhost', '127.0.0.1', '0.0.0.0', '*.local',
185
+ * '*.staging.*', '*.dev.*']
186
+ */
187
+ nonProductionHosts?: string[];
188
+ }
189
+
190
+ export declare function lintAccessibility(content: TemplateContent, options?: LintOptions): LintIssue[];
191
+
192
+ export declare interface LintIssue {
193
+ /** Block id, or null for template-level issues. */
194
+ blockId: string | null;
195
+ ruleId: string;
196
+ severity: Exclude<Severity, "off">;
197
+ message: string;
198
+ fix?: LintPatch;
199
+ }
200
+
201
+ export declare function lintLinks(content: TemplateContent, options?: LintOptions): LintIssue[];
202
+
203
+ export declare interface LintOptions {
204
+ /**
205
+ * Fully disable linting. When true, the editor skips lazy-loading the
206
+ * package, hides the sidebar tab, and suppresses inline badges.
207
+ */
208
+ disabled?: boolean;
209
+ /** Locale for vague-text dictionaries and message text. Falls back to `en`. */
210
+ locale?: string;
211
+ /**
212
+ * Accessibility linter config. Set to `false` to disable the whole
213
+ * `lintAccessibility` linter without enumerating its rules.
214
+ */
215
+ accessibility?: false | AccessibilityLintOptions;
216
+ /**
217
+ * Structure linter config. Set to `false` to disable the whole
218
+ * `lintStructure` linter without enumerating its rules.
219
+ */
220
+ structure?: false | StructureLintOptions;
221
+ /**
222
+ * Links linter config. Set to `false` to disable the whole `lintLinks`
223
+ * linter without enumerating its rules.
224
+ */
225
+ links?: false | LinksLintOptions;
226
+ }
227
+
228
+ export declare interface LintPatch {
229
+ description: string;
230
+ apply: (ctx: LintPatchContext) => void;
231
+ }
232
+
233
+ export declare interface LintPatchContext {
234
+ updateBlock: (blockId: string, patch: Partial<Block>) => void;
235
+ updateSettings: (patch: Partial<TemplateSettings>) => void;
236
+ removeBlock: (blockId: string) => void;
237
+ }
238
+
239
+ export declare function lintStructure(content: TemplateContent, options?: LintOptions): LintIssue[];
240
+
241
+ export declare interface LintThresholds {
242
+ altMaxLength: number;
243
+ minFontSize: number;
244
+ allCapsMinLength: number;
245
+ minTouchTargetPx: number;
246
+ }
153
247
 
154
248
  export declare type MessageMap = typeof en_2;
155
249
 
156
250
  export declare function parseHex(input: string | undefined | null): Rgb | null;
157
251
 
252
+ export declare interface ResolvedLinksOptions {
253
+ nonProductionHosts: string[];
254
+ }
255
+
158
256
  export declare interface ResolvedOptions {
159
257
  locale: string;
160
- rules: Record<string, Severity>;
161
- thresholds: A11yThresholds;
258
+ rules: RuleOverrides;
259
+ thresholds: LintThresholds;
260
+ links: ResolvedLinksOptions;
162
261
  /** Returns the effective severity for a rule (override or default). */
163
262
  severity: (ruleId: string) => Severity;
164
263
  }
@@ -186,7 +285,7 @@ export declare interface RuleHit {
186
285
  blockId: string | null;
187
286
  /** Interpolation values for the rule's localized message template. */
188
287
  params?: Record<string, string | number>;
189
- fix?: A11yPatch;
288
+ fix?: LintPatch;
190
289
  }
191
290
 
192
291
  export declare type RuleMessageId = keyof MessageMap;
@@ -198,14 +297,44 @@ export declare interface RuleMeta {
198
297
  severity: Exclude<Severity, "off">;
199
298
  }
200
299
 
201
- export declare const RULES: Rule[];
300
+ /**
301
+ * Per-rule severity override. Set a rule to `'off'` to disable it.
302
+ * Keys are the full prefixed rule IDs (`a11y.*`, `structure.*`, `link.*`)
303
+ * so a value copied from `LintIssue.ruleId` pastes straight in.
304
+ */
305
+ export declare type RuleOverrides = Record<string, Severity>;
202
306
 
203
307
  export declare type Severity = "error" | "warning" | "info" | "off";
204
308
 
309
+ export declare const STRUCTURE_RULES: Rule[];
310
+
311
+ /** Options consumed only by the structure linter. */
312
+ export declare interface StructureLintOptions {
313
+ rules?: RuleOverrides;
314
+ }
315
+
316
+ export declare type StructureMessageMap = typeof en_3;
317
+
318
+ export declare type StructureRuleMessageId = keyof StructureMessageMap;
319
+
205
320
  export declare const SUPPORTED_DICTIONARY_LOCALES: string[];
206
321
 
322
+ export declare const SUPPORTED_LINK_MESSAGE_LOCALES: string[];
323
+
207
324
  export declare const SUPPORTED_MESSAGE_LOCALES: string[];
208
325
 
326
+ export declare const SUPPORTED_STRUCTURE_MESSAGE_LOCALES: string[];
327
+
328
+ export declare interface UrlOccurrence {
329
+ url: string;
330
+ blockId: string;
331
+ source: UrlSource;
332
+ /** Anchor text or block-derived label, if applicable. */
333
+ label?: string;
334
+ }
335
+
336
+ export declare type UrlSource = "anchor" | "button" | "image-link" | "video" | "menu-item" | "social-icon";
337
+
209
338
  export declare type Visitor = (block: Block, ctx: WalkContext) => void;
210
339
 
211
340
  /**
@@ -231,4 +360,20 @@ export declare interface WalkContext {
231
360
  resolvedBackgroundColor: string;
232
361
  }
233
362
 
363
+ /**
364
+ * Visit every URL-bearing field in the template tree.
365
+ *
366
+ * Sources covered:
367
+ * - anchor — `<a href>` inside `title.content`, `paragraph.content`,
368
+ * `html.content` (parsed via extractAnchors)
369
+ * - button — `button.url`
370
+ * - image-link — `image.linkUrl` (only when present + non-empty)
371
+ * - video — `video.url`
372
+ * - menu-item — `menu.items[i].url`
373
+ * - social-icon — `social.icons[i].url`
374
+ *
375
+ * Each rule iterates this list once and decides per occurrence.
376
+ */
377
+ export declare function walkUrls(content: TemplateContent_2): UrlOccurrence[];
378
+
234
379
  export { }