@rathnasgala2/schemas 3.8.0 → 3.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/THIRD_PARTY_LICENSES.json +1 -1
  2. package/diagnostics/diagnostic-map.json +277 -9
  3. package/docs/catalogs/schema-inventory.json +6 -6
  4. package/examples/valid/appearance/rules.json +105 -1
  5. package/examples/valid/build-input/rules.json +105 -1
  6. package/examples/valid/problem/rules.json +35 -0
  7. package/examples/valid/theme-contract/rules.json +276 -2
  8. package/fixtures/boundary/appearance/rules.json +105 -12
  9. package/fixtures/boundary/build-input/rules.json +105 -12
  10. package/fixtures/boundary/problem/rules.json +83 -0
  11. package/fixtures/boundary/theme-contract/rules.json +386 -2
  12. package/fixtures/invalid/appearance/REQUEST_FIELD_UNKNOWN/cases.json +33 -0
  13. package/fixtures/invalid/appearance/SCHEMA_OBJECT_TOO_LARGE/cases.json +0 -13
  14. package/fixtures/invalid/appearance/SCHEMA_PATTERN_INVALID/cases.json +68 -0
  15. package/fixtures/invalid/appearance/SCHEMA_REQUIRED_FIELD_MISSING/cases.json +66 -0
  16. package/fixtures/invalid/appearance/SCHEMA_TYPE_INVALID/cases.json +99 -0
  17. package/fixtures/invalid/build-input/REQUEST_FIELD_UNKNOWN/cases.json +33 -0
  18. package/fixtures/invalid/build-input/SCHEMA_PATTERN_INVALID/cases.json +66 -0
  19. package/fixtures/invalid/build-input/SCHEMA_REQUIRED_FIELD_MISSING/cases.json +66 -0
  20. package/fixtures/invalid/build-input/SCHEMA_TYPE_INVALID/cases.json +99 -0
  21. package/fixtures/invalid/problem/SCHEMA_ARRAY_TOO_LONG/cases.json +13 -0
  22. package/fixtures/invalid/problem/SCHEMA_ENUM_INVALID/cases.json +11 -0
  23. package/fixtures/invalid/problem/SCHEMA_STRING_TOO_LONG/cases.json +11 -0
  24. package/fixtures/invalid/problem/SCHEMA_STRING_TOO_SHORT/cases.json +11 -0
  25. package/fixtures/invalid/problem/SCHEMA_TYPE_INVALID/cases.json +29 -0
  26. package/fixtures/invalid/theme-contract/REQUEST_FIELD_UNKNOWN/cases.json +33 -0
  27. package/fixtures/invalid/theme-contract/SCHEMA_ARRAY_TOO_LONG/cases.json +165 -0
  28. package/fixtures/invalid/theme-contract/SCHEMA_CONSTANT_INVALID/cases.json +11 -11
  29. package/fixtures/invalid/theme-contract/SCHEMA_ENUM_INVALID/cases.json +88 -0
  30. package/fixtures/invalid/theme-contract/SCHEMA_REQUIRED_FIELD_MISSING/cases.json +93 -0
  31. package/fixtures/invalid/theme-contract/SCHEMA_TYPE_INVALID/cases.json +51 -0
  32. package/fixtures/manifest.json +2137 -37
  33. package/fixtures/unknown-field/appearance/rules.json +54 -1
  34. package/fixtures/unknown-field/build-input/rules.json +54 -1
  35. package/fixtures/unknown-field/problem/rules.json +8 -0
  36. package/fixtures/unknown-field/theme-contract/rules.json +70 -1
  37. package/generated/browser/validator-core.mjs +3 -3
  38. package/generated/typescript/contracts/appearance.d.ts +14 -1
  39. package/generated/typescript/contracts/build-input.d.ts +14 -1
  40. package/generated/typescript/contracts/problem.d.ts +9 -0
  41. package/generated/typescript/contracts/theme-contract.d.ts +51 -0
  42. package/openapi/http-catalog.json +2 -2
  43. package/openapi/openapi.yaml +97 -3
  44. package/package.json +10 -1
  45. package/parity/palette/golden-vectors.json +883 -0
  46. package/parity/palette/stress-corpus.json +521 -0
  47. package/parity/palette/theme-default.fixture.json +698 -0
  48. package/sbom.cdx.json +23 -23
  49. package/schemas/appearance.schema.json +30 -3
  50. package/schemas/build-input.schema.json +30 -3
  51. package/schemas/problem.schema.json +21 -1
  52. package/schemas/theme-contract.schema.json +185 -1
  53. package/src/palette.js +919 -0
  54. package/types/palette.d.ts +305 -0
  55. package/fixtures/invalid/build-input/SCHEMA_OBJECT_TOO_LARGE/cases.json +0 -15
@@ -0,0 +1,305 @@
1
+ /**
2
+ * The number of colour literals (`#rrggbb` or `#rrggbbaa`) in a token
3
+ * value. `transparent` and `none` are not colours. A theme's `follow` list
4
+ * for that value, in that mode, has exactly this length.
5
+ *
6
+ * @param {string} value token value
7
+ * @returns {number} colour count
8
+ */
9
+ export function paletteColourCount(value: string): number;
10
+ /**
11
+ * Validate a theme's `palette` block against its own tokens: the profile,
12
+ * that every key is palette-drivable and appears at most once, that each
13
+ * list length equals the number of colours in the value of that mode, and
14
+ * that every entry is `accent`, `canvas`, `text` or `null`.
15
+ *
16
+ * @param {PaletteTheme} theme theme.json
17
+ * @returns {ThemePalette} the validated block
18
+ * @throws {PaletteError} `THEME_PALETTE_MISSING` or `THEME_PALETTE_INVALID`
19
+ */
20
+ export function validateThemePalette(theme: PaletteTheme): ThemePalette;
21
+ /**
22
+ * Validate the author's picked colours (`appearance.tokens`).
23
+ *
24
+ * @param {unknown} tokens candidate
25
+ * @returns {PaletteTokens} the same value, typed
26
+ * @throws {PaletteError} `APPEARANCE_PALETTE_INVALID`
27
+ */
28
+ export function validatePaletteTokens(tokens: unknown): PaletteTokens;
29
+ /**
30
+ * Derive every token's final value from a theme and the author's picks.
31
+ *
32
+ * Each colour bound to a seed whose effective value differs from the theme's
33
+ * own is moved in OKLCH (hue rotated by the hue difference, chroma scaled by
34
+ * the chroma ratio or set to the author's when the theme's seed is neutral,
35
+ * lightness through a piecewise-linear map), gamut-mapped by reducing chroma
36
+ * and rounded to 8 bits. A seed unchanged keeps the theme's literal exactly,
37
+ * and a colour equal to the theme's seed becomes the author's seed exactly.
38
+ * Where a contrast pair's foreground is bound (and not itself a seed), its
39
+ * lightness is then moved by the smallest step reaching the minimum plus
40
+ * 0.1; a failing pair with a theme-owned or seed foreground is not adjusted
41
+ * and is reported by `checkPalette`.
42
+ *
43
+ * @param {PaletteTheme} theme theme.json (needs `tokens` and `palette`)
44
+ * @param {PaletteTokens} tokens the author's `appearance.tokens` (`{}` = theme colours)
45
+ * @returns {DerivedPalette} the derived palette
46
+ * @throws {PaletteError} on a malformed theme palette or malformed picks
47
+ */
48
+ export function derivePalette(theme: PaletteTheme, tokens: PaletteTokens): DerivedPalette;
49
+ /**
50
+ * Check every palette pair on the derived final values, in one mode.
51
+ *
52
+ * @param {DerivedPalette} derived result of `derivePalette`
53
+ * @param {PaletteMode} mode `light` or `dark`
54
+ * @returns {PaletteCheck} per-pair results and the failures (`APPEARANCE_PALETTE_CONTRAST`)
55
+ */
56
+ export function checkPalette(derived: DerivedPalette, mode: PaletteMode): PaletteCheck;
57
+ /**
58
+ * Check the author's picks on their own, without a theme: Background
59
+ * lightness inside the band (OKLab L at least 0.90 in light, at most 0.30 in
60
+ * dark) and Text at least 4.5:1 on Background in each mode where both are
61
+ * set. The author's colours are never adjusted.
62
+ *
63
+ * @param {PaletteTokens} tokens the author's `appearance.tokens`
64
+ * @returns {{ ok: boolean, failures: PickedFailure[] }} outcome
65
+ */
66
+ export function checkPickedColours(tokens: PaletteTokens): {
67
+ ok: boolean;
68
+ failures: PickedFailure[];
69
+ };
70
+ /**
71
+ * Suggest the dark value for a light pick: the pick's hue and chroma at the
72
+ * OKLab lightness of the theme's own dark seed for that role, gamut-mapped.
73
+ *
74
+ * @param {PaletteTheme} theme theme.json (needs `tokens`)
75
+ * @param {PaletteRole} role the role being picked
76
+ * @param {string} lightHex the author's light pick, lowercase `#rrggbb`
77
+ * @returns {string} suggested dark `#rrggbb`
78
+ * @throws {PaletteError} `APPEARANCE_PALETTE_INVALID` for a malformed pick or role
79
+ */
80
+ export function suggestDarkSeed(theme: PaletteTheme, role: PaletteRole, lightHex: string): string;
81
+ /**
82
+ * Publication palette: the shared "follow and shift" algorithm.
83
+ *
84
+ * An author picks up to three colours (Accent, Background, Text), each as an
85
+ * explicit light and dark pair. A theme declares, in an optional `palette`
86
+ * block of its theme.json, which of those three seeds each colour inside
87
+ * each of 40 palette-drivable tokens follows. This module derives the final
88
+ * value of every token from the theme and the author's picks, and checks the
89
+ * result against the contrast pairs. The template (at render), theme-tooling
90
+ * (at theme release) and the App (while picking) all import it, so the three
91
+ * can never disagree.
92
+ *
93
+ * Pure browser-safe ESM: no Node built-ins, no I/O, no dependencies. All
94
+ * colour arithmetic is in OKLab/OKLCH with the standard sRGB transfer
95
+ * function; every result is rounded to 8 bits and written as lowercase hex.
96
+ *
97
+ * The 40 drivable keys, their value types and the per-type colour counts live
98
+ * here and nowhere else; the JSON Schema generators and the semantic
99
+ * validator import them from this module.
100
+ *
101
+ * @module
102
+ */
103
+ /** The `palette.profile` value a theme must carry. */
104
+ export const PALETTE_PROFILE: "gala-palette-follow-v1";
105
+ /** The three roles an author may pick, in canonical order. */
106
+ export const PALETTE_ROLES: readonly PaletteRole[];
107
+ /** The two colour modes, in canonical order. */
108
+ export const PALETTE_MODES: readonly PaletteMode[];
109
+ /** The seed token each role is read from, and the token that carries it. */
110
+ export const PALETTE_SEED_KEYS: Readonly<{
111
+ accent: "color-accent";
112
+ canvas: "color-canvas";
113
+ text: "color-text";
114
+ }>;
115
+ /**
116
+ * Every palette-drivable token key and its value type, in theme catalog
117
+ * order. A theme binds any subset; every other key is always theme-owned.
118
+ *
119
+ * @type {Readonly<Record<string, 'color' | 'paint' | 'border' | 'shadow'>>}
120
+ */
121
+ export const PALETTE_KEY_TYPES: Readonly<Record<string, "color" | "paint" | "border" | "shadow">>;
122
+ /** The 40 palette-drivable token keys, in theme catalog order. */
123
+ export const PALETTE_DRIVABLE_KEYS: readonly string[];
124
+ /**
125
+ * The most colours one value of each drivable type can hold under the token
126
+ * grammar: one colour, one border colour, three shadow layers, and three
127
+ * gradients of five stops. A value's exact count is `paletteColourCount`.
128
+ */
129
+ export const PALETTE_MAX_COLOURS_BY_TYPE: Readonly<{
130
+ color: 1;
131
+ border: 1;
132
+ shadow: 3;
133
+ paint: 15;
134
+ }>;
135
+ /**
136
+ * The contrast pairs every palette must clear on its final values, per
137
+ * mode: the 17 theme-tooling pairs plus the focus ring and the three status
138
+ * colours on the canvas. A pair flagged `skipWhenNotPlainColor` is skipped
139
+ * (never silently passed) when its background is `none`, translucent,
140
+ * transparent or an unreadable gradient.
141
+ */
142
+ export const PALETTE_PAIRS: readonly Readonly<{
143
+ label: `undefined on ${string}` | `undefined on ${number}` | `${string} on undefined` | `${string} on ${string}` | `${string} on ${number}` | `${string} on false` | `${string} on true` | `${number} on undefined` | `${number} on ${string}` | `${number} on ${number}` | `${number} on false` | `${number} on true` | `false on ${string}` | `false on ${number}` | `true on ${string}` | `true on ${number}`;
144
+ foreground: string;
145
+ background: string;
146
+ minimum: number;
147
+ skipWhenNotPlainColor: boolean;
148
+ }>[];
149
+ /** Limits of the Background role (OKLab lightness). */
150
+ export const PALETTE_CANVAS_BAND: Readonly<{
151
+ lightMin: 0.9;
152
+ darkMax: 0.3;
153
+ }>;
154
+ /** The margin above a pair's minimum that the readability clamp aims for. */
155
+ export const PALETTE_CLAMP_MARGIN: 0.1;
156
+ /**
157
+ * @typedef {'accent' | 'canvas' | 'text'} PaletteRole
158
+ * @typedef {'light' | 'dark'} PaletteMode
159
+ * @typedef {{ light: string, dark: string }} PalettePair
160
+ * @typedef {Partial<Record<PaletteRole, PalettePair>>} PaletteTokens
161
+ * @typedef {{ key: string, type: string, light: string, dark: string }} PaletteThemeToken
162
+ * @typedef {{
163
+ * key: string,
164
+ * light: (PaletteRole | null)[],
165
+ * dark: (PaletteRole | null)[],
166
+ * }} PaletteFollow
167
+ * @typedef {{ profile: string, follow: PaletteFollow[] }} ThemePalette
168
+ * @typedef {{ palette?: ThemePalette, tokens: PaletteThemeToken[] }} PaletteTheme
169
+ * @typedef {{
170
+ * key: string,
171
+ * mode: PaletteMode,
172
+ * from: string,
173
+ * to: string,
174
+ * pair: string,
175
+ * }} PaletteAdjustment
176
+ * @typedef {{
177
+ * profile: string,
178
+ * seeds: Record<PaletteMode, Record<PaletteRole, string>>,
179
+ * bound: string[],
180
+ * tokens: Record<string, PalettePair>,
181
+ * adjusted: PaletteAdjustment[],
182
+ * }} DerivedPalette
183
+ * @typedef {{
184
+ * label: string,
185
+ * foreground: string,
186
+ * background: string,
187
+ * minimum: number,
188
+ * status: 'pass' | 'fail' | 'skipped',
189
+ * ratio: number | null,
190
+ * reason?: string,
191
+ * worstStop?: string,
192
+ * }} PairResult
193
+ * @typedef {{
194
+ * code: 'APPEARANCE_PALETTE_CONTRAST',
195
+ * pair: string,
196
+ * mode: PaletteMode,
197
+ * ratio: number | null,
198
+ * minimum: number,
199
+ * foregroundKey: string,
200
+ * backgroundKey: string,
201
+ * reason?: string,
202
+ * }} PaletteFailure
203
+ * @typedef {{
204
+ * mode: PaletteMode,
205
+ * ok: boolean,
206
+ * results: PairResult[],
207
+ * failures: PaletteFailure[],
208
+ * }} PaletteCheck
209
+ * @typedef {{
210
+ * code: 'APPEARANCE_PALETTE_BAND' | 'APPEARANCE_PALETTE_TEXT_CONTRAST',
211
+ * mode: PaletteMode,
212
+ * role: PaletteRole,
213
+ * value: number,
214
+ * limit: number,
215
+ * }} PickedFailure
216
+ */
217
+ /**
218
+ * Error raised for a malformed theme palette block or malformed picked
219
+ * colours. `code` is one of `THEME_PALETTE_MISSING`, `THEME_PALETTE_INVALID`
220
+ * or `APPEARANCE_PALETTE_INVALID`.
221
+ */
222
+ export class PaletteError extends Error {
223
+ /**
224
+ * @param {string} code stable machine-readable code
225
+ * @param {string} detail human-readable detail
226
+ */
227
+ constructor(code: string, detail: string);
228
+ /** @type {string} */
229
+ code: string;
230
+ /** @type {string} */
231
+ detail: string;
232
+ }
233
+ export type PaletteRole = "accent" | "canvas" | "text";
234
+ export type PaletteMode = "light" | "dark";
235
+ export type PalettePair = {
236
+ light: string;
237
+ dark: string;
238
+ };
239
+ export type PaletteTokens = Partial<Record<PaletteRole, PalettePair>>;
240
+ export type PaletteThemeToken = {
241
+ key: string;
242
+ type: string;
243
+ light: string;
244
+ dark: string;
245
+ };
246
+ export type PaletteFollow = {
247
+ key: string;
248
+ light: (PaletteRole | null)[];
249
+ dark: (PaletteRole | null)[];
250
+ };
251
+ export type ThemePalette = {
252
+ profile: string;
253
+ follow: PaletteFollow[];
254
+ };
255
+ export type PaletteTheme = {
256
+ palette?: ThemePalette;
257
+ tokens: PaletteThemeToken[];
258
+ };
259
+ export type PaletteAdjustment = {
260
+ key: string;
261
+ mode: PaletteMode;
262
+ from: string;
263
+ to: string;
264
+ pair: string;
265
+ };
266
+ export type DerivedPalette = {
267
+ profile: string;
268
+ seeds: Record<PaletteMode, Record<PaletteRole, string>>;
269
+ bound: string[];
270
+ tokens: Record<string, PalettePair>;
271
+ adjusted: PaletteAdjustment[];
272
+ };
273
+ export type PairResult = {
274
+ label: string;
275
+ foreground: string;
276
+ background: string;
277
+ minimum: number;
278
+ status: "pass" | "fail" | "skipped";
279
+ ratio: number | null;
280
+ reason?: string;
281
+ worstStop?: string;
282
+ };
283
+ export type PaletteFailure = {
284
+ code: "APPEARANCE_PALETTE_CONTRAST";
285
+ pair: string;
286
+ mode: PaletteMode;
287
+ ratio: number | null;
288
+ minimum: number;
289
+ foregroundKey: string;
290
+ backgroundKey: string;
291
+ reason?: string;
292
+ };
293
+ export type PaletteCheck = {
294
+ mode: PaletteMode;
295
+ ok: boolean;
296
+ results: PairResult[];
297
+ failures: PaletteFailure[];
298
+ };
299
+ export type PickedFailure = {
300
+ code: "APPEARANCE_PALETTE_BAND" | "APPEARANCE_PALETTE_TEXT_CONTRAST";
301
+ mode: PaletteMode;
302
+ role: PaletteRole;
303
+ value: number;
304
+ limit: number;
305
+ };
@@ -1,15 +0,0 @@
1
- [
2
- {
3
- "caseId": "build-input-invalid-939226632d6b9645",
4
- "coveredRuleIds": ["build-input:#/$defs/semanticTokens:maxProperties"],
5
- "schemaId": "urn:gala:schema:build-input:2.0.0",
6
- "schemaPointer": "#/$defs/semanticTokens",
7
- "keyword": "maxProperties",
8
- "recipe": {
9
- "kind": "numbered-object",
10
- "schemaPointer": "#/$defs/semanticTokens",
11
- "count": 1
12
- },
13
- "expectedCodes": ["SCHEMA_OBJECT_TOO_LARGE"]
14
- }
15
- ]