@domandigital/craft 0.10.0 → 0.12.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/CHANGELOG.md +35 -0
- package/CHARACTER.md +9 -1
- package/COPY.md +6 -2
- package/README.md +40 -0
- package/dist/{audit-EGDNWSQB.js → audit-5QWFLZ6L.js} +6 -3
- package/dist/audit.cjs +279 -16
- package/dist/audit.d.cts +81 -4
- package/dist/audit.d.ts +81 -4
- package/dist/audit.js +278 -16
- package/dist/{chunk-3NGWXZZE.js → chunk-AL6MRG6V.js} +40 -3
- package/dist/{chunk-2NQFIDLE.js → chunk-CDAJUS5U.js} +13 -11
- package/dist/{chunk-JKIDWHG3.js → chunk-J3QUROJ5.js} +299 -32
- package/dist/chunk-JYNXGCYZ.js +75 -0
- package/dist/{chunk-APO4BT2F.js → chunk-LHGY43LL.js} +11 -7
- package/dist/chunk-PU5G5DUC.js +206 -0
- package/dist/{chunk-QMYERCBB.js → chunk-PZ4MF2VV.js} +19 -7
- package/dist/{chunk-JWHC67ZS.js → chunk-S3NZAVU6.js} +121 -1
- package/dist/{chunk-TSKRBIOC.js → chunk-ULPI335S.js} +181 -13
- package/dist/chunk-ZKVJ7K6X.js +19 -0
- package/dist/cli-2DY3V3SF.js +17 -0
- package/dist/{cli-PRWTNFVP.js → cli-C3JLFXMG.js} +5 -4
- package/dist/cli-QM2AQQMF.js +19 -0
- package/dist/{cli-ADQQURNR.js → cli-ZB2VJ7CY.js} +25 -17
- package/dist/{cli-2WBGTBKD.js → cli-ZPJIWVEP.js} +15 -80
- package/dist/cli.js +2 -2
- package/dist/index.cjs +486 -9
- package/dist/index.d.cts +364 -6
- package/dist/index.d.ts +364 -6
- package/dist/index.js +462 -9
- package/licences.json +104 -0
- package/package.json +15 -7
- package/word-lists.json +1 -1
- package/dist/chunk-U2JTFQKO.js +0 -112
- package/dist/cli-EK3GSAOW.js +0 -15
- package/dist/cli-VQUNNIZM.js +0 -16
package/dist/index.cjs
CHANGED
|
@@ -23,10 +23,12 @@ __export(index_exports, {
|
|
|
23
23
|
AI_PHRASES: () => AI_PHRASES,
|
|
24
24
|
AI_VIOLET: () => AI_VIOLET,
|
|
25
25
|
AI_WORDS: () => AI_WORDS,
|
|
26
|
+
BASELINE_VERSION: () => BASELINE_VERSION,
|
|
26
27
|
BUZZWORDS: () => BUZZWORDS,
|
|
27
28
|
CATALOGUE: () => CATALOGUE,
|
|
28
29
|
CATALOGUE_VERSION: () => CATALOGUE_VERSION,
|
|
29
30
|
CHOICE_KEYS: () => CHOICE_KEYS,
|
|
31
|
+
DEFAULT_IGNORE: () => DEFAULT_IGNORE,
|
|
30
32
|
DENSITIES: () => DENSITIES,
|
|
31
33
|
DENSITY_NAMES: () => DENSITY_NAMES,
|
|
32
34
|
DIRECTION_VERSION: () => DIRECTION_VERSION,
|
|
@@ -35,12 +37,16 @@ __export(index_exports, {
|
|
|
35
37
|
DURATION_S: () => DURATION_S,
|
|
36
38
|
EASE: () => EASE,
|
|
37
39
|
EASE_TUPLE: () => EASE_TUPLE,
|
|
40
|
+
EDGE_STEP: () => EDGE_STEP,
|
|
38
41
|
ESTATE_VERSION: () => ESTATE_VERSION,
|
|
39
42
|
EXIT_RATIO: () => EXIT_RATIO,
|
|
40
43
|
FINGERPRINT_VERSION: () => FINGERPRINT_VERSION,
|
|
41
44
|
HOUSE: () => HOUSE,
|
|
42
45
|
HOUSE_BUDGET: () => HOUSE_BUDGET,
|
|
46
|
+
HOUSE_LOCKED: () => HOUSE_LOCKED,
|
|
43
47
|
HOUSE_TYPE: () => HOUSE_TYPE,
|
|
48
|
+
JSON_SCHEMA_VERSION: () => JSON_SCHEMA_VERSION,
|
|
49
|
+
LICENCES: () => LICENCES,
|
|
44
50
|
LIGHTNESS_CURVE: () => LIGHTNESS_CURVE,
|
|
45
51
|
MIN_DECIDED: () => MIN_DECIDED,
|
|
46
52
|
MIN_RUNS: () => MIN_RUNS,
|
|
@@ -50,6 +56,7 @@ __export(index_exports, {
|
|
|
50
56
|
PLAINER_WORDS: () => PLAINER_WORDS,
|
|
51
57
|
PROPOSED: () => PROPOSED,
|
|
52
58
|
RAMP_STEPS: () => RAMP_STEPS,
|
|
59
|
+
READABLE_SNAPSHOT_VERSIONS: () => READABLE_SNAPSHOT_VERSIONS,
|
|
53
60
|
REFLEX_FONTS_1: () => REFLEX_FONTS_1,
|
|
54
61
|
REFLEX_FONTS_2: () => REFLEX_FONTS_2,
|
|
55
62
|
RENDERED_PATHS: () => RENDERED_PATHS,
|
|
@@ -71,7 +78,9 @@ __export(index_exports, {
|
|
|
71
78
|
accentFork: () => accentFork,
|
|
72
79
|
addSite: () => addSite,
|
|
73
80
|
apcaContrast: () => apcaContrast,
|
|
81
|
+
applyBaseline: () => applyBaseline,
|
|
74
82
|
applyHouseGate: () => applyHouseGate,
|
|
83
|
+
applySeverity: () => applySeverity,
|
|
75
84
|
auditSnapshot: () => auditSnapshot,
|
|
76
85
|
catalogueTable: () => catalogueTable,
|
|
77
86
|
characterReport: () => characterReport,
|
|
@@ -82,6 +91,7 @@ __export(index_exports, {
|
|
|
82
91
|
compareFacts: () => compareFacts,
|
|
83
92
|
compareToEstate: () => compareToEstate,
|
|
84
93
|
craftTokens: () => craftTokens,
|
|
94
|
+
createBaseline: () => createBaseline,
|
|
85
95
|
deltaEOk: () => deltaEOk,
|
|
86
96
|
deltaEOkHex: () => deltaEOkHex,
|
|
87
97
|
densityCss: () => densityCss,
|
|
@@ -91,8 +101,10 @@ __export(index_exports, {
|
|
|
91
101
|
estatePairs: () => estatePairs,
|
|
92
102
|
exitDuration: () => exitDuration,
|
|
93
103
|
extractHtml: () => extractHtml,
|
|
104
|
+
faceLicence: () => faceLicence,
|
|
94
105
|
findClaims: () => findClaims,
|
|
95
106
|
findColours: () => findColours,
|
|
107
|
+
findingKey: () => findingKey,
|
|
96
108
|
fingerprint: () => fingerprint,
|
|
97
109
|
fingerprintDistance: () => fingerprintDistance,
|
|
98
110
|
fluidClamp: () => fluidClamp,
|
|
@@ -103,17 +115,22 @@ __export(index_exports, {
|
|
|
103
115
|
formatComparison: () => formatComparison,
|
|
104
116
|
formatHex: () => formatHex,
|
|
105
117
|
formatReport: () => formatReport,
|
|
118
|
+
globToRegExp: () => globToRegExp,
|
|
106
119
|
groundName: () => groundName,
|
|
107
120
|
harvest: () => harvest,
|
|
108
121
|
hexToOklch: () => hexToOklch,
|
|
122
|
+
holdHouseBlocks: () => holdHouseBlocks,
|
|
109
123
|
houseRule: () => houseRule,
|
|
110
124
|
hueName: () => hueName,
|
|
111
125
|
hueSwatch: () => hueSwatch,
|
|
126
|
+
ignoreMatcher: () => ignoreMatcher,
|
|
112
127
|
inP3Gamut: () => inP3Gamut,
|
|
113
128
|
inSrgbGamut: () => inSrgbGamut,
|
|
114
129
|
initDirection: () => initDirection,
|
|
115
130
|
isAiViolet: () => isAiViolet,
|
|
116
131
|
isCream: () => isCream,
|
|
132
|
+
layoutDistance: () => layoutDistance,
|
|
133
|
+
licencesJson: () => licencesJson,
|
|
117
134
|
makeSnapshot: () => makeSnapshot,
|
|
118
135
|
motionTokens: () => motionTokens,
|
|
119
136
|
namedSources: () => namedSources,
|
|
@@ -123,17 +140,21 @@ __export(index_exports, {
|
|
|
123
140
|
oklchToHex: () => oklchToHex,
|
|
124
141
|
oklchToRgb: () => oklchToRgb,
|
|
125
142
|
paletteFromPixels: () => paletteFromPixels,
|
|
143
|
+
parseBaseline: () => parseBaseline,
|
|
126
144
|
parseColour: () => parseColour,
|
|
145
|
+
parseCraftConfig: () => parseCraftConfig,
|
|
127
146
|
parseHex: () => parseHex,
|
|
128
147
|
propose: () => propose,
|
|
129
148
|
protectedFacts: () => protectedFacts,
|
|
130
149
|
ramp: () => ramp,
|
|
131
150
|
rampFromAnchors: () => rampFromAnchors,
|
|
151
|
+
readSnapshot: () => readSnapshot,
|
|
132
152
|
retrofitPlan: () => retrofitPlan,
|
|
133
153
|
runTell: () => runTell,
|
|
134
154
|
scanSource: () => scanSource,
|
|
135
155
|
sectionRhythm: () => sectionRhythm,
|
|
136
156
|
semantic: () => semantic,
|
|
157
|
+
severityExceptions: () => severityExceptions,
|
|
137
158
|
shapeName: () => shapeName,
|
|
138
159
|
sharedParts: () => sharedParts,
|
|
139
160
|
shouldAnimate: () => shouldAnimate,
|
|
@@ -142,10 +163,13 @@ __export(index_exports, {
|
|
|
142
163
|
tailwindV4Theme: () => tailwindV4Theme,
|
|
143
164
|
tellById: () => tellById,
|
|
144
165
|
toGamut: () => toGamut,
|
|
166
|
+
toJson: () => toJson,
|
|
167
|
+
toSarif: () => toSarif,
|
|
145
168
|
typeFeatureTokens: () => typeFeatureTokens,
|
|
146
169
|
typicality: () => typicality,
|
|
147
170
|
validateDirection: () => validateDirection,
|
|
148
171
|
visibleText: () => visibleText,
|
|
172
|
+
visualMeasures: () => visualMeasures,
|
|
149
173
|
wcagContrast: () => wcagContrast,
|
|
150
174
|
wordListsJson: () => wordListsJson
|
|
151
175
|
});
|
|
@@ -2895,6 +2919,7 @@ var REFLEX_FONTS_2 = [
|
|
|
2895
2919
|
"Plus Jakarta Sans",
|
|
2896
2920
|
"Outfit",
|
|
2897
2921
|
"Geist",
|
|
2922
|
+
"Cal Sans",
|
|
2898
2923
|
"Sora",
|
|
2899
2924
|
"Instrument Sans",
|
|
2900
2925
|
"Instrument Serif",
|
|
@@ -3116,7 +3141,7 @@ function iconTiles(file) {
|
|
|
3116
3141
|
return sized && has(unit, /^rounded-(?:md|lg|xl|2xl|full)$/) && has(unit, /^(?:items-center|place-items-center|place-content-center)$/) && has(unit, /^bg-/);
|
|
3117
3142
|
});
|
|
3118
3143
|
}
|
|
3119
|
-
var ICON_LIBRARY = /from\s+["'](?:lucide-react|@heroicons\/react[^"']*|react-icons[^"']*|@phosphor-icons\/react|@tabler\/icons-react|@radix-ui\/react-icons)["']/;
|
|
3144
|
+
var ICON_LIBRARY = /from\s+["'](?:lucide-react|@heroicons\/react[^"']*|react-icons[^"']*|@phosphor-icons\/react|@tabler\/icons-react|@radix-ui\/react-icons|@iconscout\/react-unicons[^"']*|@mui\/icons-material[^"']*|react-material-symbols[^"']*)["']/;
|
|
3120
3145
|
function iconTileStackHits(ctx) {
|
|
3121
3146
|
const hits = [];
|
|
3122
3147
|
for (const file of ctx.files) {
|
|
@@ -3263,6 +3288,12 @@ function shadcnHits(ctx) {
|
|
|
3263
3288
|
const detail = unused.length > 0 ? `; ${unused.length} imported nowhere` : "";
|
|
3264
3289
|
return [{ path: ui[0].path, offset: 0, message: `${ui.length} stock shadcn primitives${detail}` }];
|
|
3265
3290
|
}
|
|
3291
|
+
function shadcnCardHits(ctx) {
|
|
3292
|
+
return eachUnit(
|
|
3293
|
+
ctx,
|
|
3294
|
+
(unit) => has(unit, /^bg-card$/) && has(unit, /^text-card-foreground$/) && has(unit, /^border$/) && has(unit, /^rounded-(?:lg|xl)$/) && has(unit, /^shadow(?:-sm)?$/) ? "the registry Card recipe, unchanged" : null
|
|
3295
|
+
);
|
|
3296
|
+
}
|
|
3266
3297
|
function marqueeHits(ctx) {
|
|
3267
3298
|
const hits = [];
|
|
3268
3299
|
const pattern = /@keyframes\s+[\w-]*marquee|animate-(?:marquee|scroll-x|infinite-scroll)\b|<Marquee\b|["']react-fast-marquee["']|ui\/marquee["']|animation(?:-name)?\s*:\s*[^;]*marquee/i;
|
|
@@ -3480,6 +3511,23 @@ const inter = Inter({ subsets: ["latin"] });`),
|
|
|
3480
3511
|
pass: [f3("src/components/ui/button.tsx", `export function Button() { return null; }`)]
|
|
3481
3512
|
}
|
|
3482
3513
|
},
|
|
3514
|
+
{
|
|
3515
|
+
id: "shadcn-card-stock",
|
|
3516
|
+
name: "Stock shadcn Card",
|
|
3517
|
+
generation: 1,
|
|
3518
|
+
severity: "warn",
|
|
3519
|
+
surface: "source",
|
|
3520
|
+
why: "The registry Card (bg-card, a plain border, rounded-lg or rounded-xl, shadow-sm) is the container every generated page is built from. Left as shipped, every card on the site looks like every other shadcn site's.",
|
|
3521
|
+
fix: "Give the card this site's radius and shadow from the restraint budget, or drop the shadow and let a visible border carry it.",
|
|
3522
|
+
detect: shadcnCardHits,
|
|
3523
|
+
fixtures: {
|
|
3524
|
+
flag: [
|
|
3525
|
+
f3("components/ui/card.tsx", `className={cn("rounded-lg border bg-card text-card-foreground shadow-sm", className)}`),
|
|
3526
|
+
f3("components/ui/card.tsx", `className={cn("bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm", className)}`)
|
|
3527
|
+
],
|
|
3528
|
+
pass: [f3("components/ui/card.tsx", `className={cn("rounded-sm border-2 border-ink bg-card text-card-foreground", className)}`)]
|
|
3529
|
+
}
|
|
3530
|
+
},
|
|
3483
3531
|
{
|
|
3484
3532
|
id: "cream-palette",
|
|
3485
3533
|
name: "Cream ground",
|
|
@@ -3706,6 +3754,17 @@ var HOUSE = {
|
|
|
3706
3754
|
function houseRule(id, name = id) {
|
|
3707
3755
|
return HOUSE[id] ?? { tier: "review", label: name };
|
|
3708
3756
|
}
|
|
3757
|
+
var HOUSE_LOCKED = "the house blocking tier cannot be lowered by a repo. A genuine one-off takes copy-ok on its line; a rule that is wrong is changed in craft's house.ts and COPY.md";
|
|
3758
|
+
function holdHouseBlocks(exceptions, severity) {
|
|
3759
|
+
const blocks = (tell) => HOUSE[tell]?.tier === "block";
|
|
3760
|
+
const refusedSeverity = Object.entries(severity).filter(([tell, o]) => blocks(tell) && o.level !== "block").map(([tell]) => tell);
|
|
3761
|
+
return {
|
|
3762
|
+
exceptions: exceptions.filter((e) => !blocks(e.tell)),
|
|
3763
|
+
refused: exceptions.filter((e) => blocks(e.tell)),
|
|
3764
|
+
severity: Object.fromEntries(Object.entries(severity).filter(([tell]) => !refusedSeverity.includes(tell))),
|
|
3765
|
+
refusedSeverity
|
|
3766
|
+
};
|
|
3767
|
+
}
|
|
3709
3768
|
|
|
3710
3769
|
// src/character/parse.ts
|
|
3711
3770
|
var MARKUP = /\.(tsx|jsx|html?|astro|vue|svelte|mdx)$/i;
|
|
@@ -4039,7 +4098,7 @@ function extractStrings(file) {
|
|
|
4039
4098
|
}
|
|
4040
4099
|
|
|
4041
4100
|
// src/snapshot/types.ts
|
|
4042
|
-
var SNAPSHOT_VERSION =
|
|
4101
|
+
var SNAPSHOT_VERSION = 2;
|
|
4043
4102
|
|
|
4044
4103
|
// src/snapshot/fixture.ts
|
|
4045
4104
|
function makeSnapshot(patch = {}) {
|
|
@@ -4373,7 +4432,7 @@ var RENDERED_PATHS = {
|
|
|
4373
4432
|
};
|
|
4374
4433
|
|
|
4375
4434
|
// src/character/check.ts
|
|
4376
|
-
var CATALOGUE_VERSION = "2026.09.
|
|
4435
|
+
var CATALOGUE_VERSION = "2026.09.9";
|
|
4377
4436
|
var CATALOGUE = [...SOURCE_TELLS, ...COPY_TELLS, ...RESEARCH_TELLS, ...DENSITY_TELLS].map(
|
|
4378
4437
|
(t) => RENDERED_PATHS[t.id] ? { ...t, rendered: RENDERED_PATHS[t.id] } : t
|
|
4379
4438
|
);
|
|
@@ -4752,8 +4811,267 @@ function formatClaims(report2, label = "craft copy claims") {
|
|
|
4752
4811
|
return lines.join("\n");
|
|
4753
4812
|
}
|
|
4754
4813
|
|
|
4814
|
+
// src/character/baseline.ts
|
|
4815
|
+
var BASELINE_VERSION = 1;
|
|
4816
|
+
function fnv1a(text) {
|
|
4817
|
+
let hash = 2166136261;
|
|
4818
|
+
for (let i = 0; i < text.length; i += 1) {
|
|
4819
|
+
hash ^= text.charCodeAt(i);
|
|
4820
|
+
hash = Math.imul(hash, 16777619);
|
|
4821
|
+
}
|
|
4822
|
+
return (hash >>> 0).toString(16).padStart(8, "0");
|
|
4823
|
+
}
|
|
4824
|
+
var excerptHash = (f4) => fnv1a(f4.excerpt.replace(/\s+/g, " ").trim());
|
|
4825
|
+
var keyOf = (tell, path, excerpt) => `${tell}\0${path}\0${excerpt}`;
|
|
4826
|
+
function findingKey(f4) {
|
|
4827
|
+
return keyOf(f4.tell, f4.path, excerptHash(f4));
|
|
4828
|
+
}
|
|
4829
|
+
function createBaseline(report2) {
|
|
4830
|
+
const counts = /* @__PURE__ */ new Map();
|
|
4831
|
+
for (const f4 of report2.findings) {
|
|
4832
|
+
const excerpt = excerptHash(f4);
|
|
4833
|
+
const key = keyOf(f4.tell, f4.path, excerpt);
|
|
4834
|
+
const entry = counts.get(key);
|
|
4835
|
+
if (entry) entry.count += 1;
|
|
4836
|
+
else counts.set(key, { tell: f4.tell, path: f4.path, excerpt, count: 1 });
|
|
4837
|
+
}
|
|
4838
|
+
const entries = [...counts.values()].sort((a, b) => a.path.localeCompare(b.path) || a.tell.localeCompare(b.tell) || a.excerpt.localeCompare(b.excerpt));
|
|
4839
|
+
return { version: BASELINE_VERSION, catalogueVersion: report2.catalogueVersion, entries };
|
|
4840
|
+
}
|
|
4841
|
+
function parseBaseline(data, source = "baseline") {
|
|
4842
|
+
const b = data;
|
|
4843
|
+
if (!b || typeof b !== "object" || b.version !== BASELINE_VERSION || !Array.isArray(b.entries)) {
|
|
4844
|
+
throw new Error(`${source}: not a craft baseline (version ${BASELINE_VERSION})`);
|
|
4845
|
+
}
|
|
4846
|
+
for (const e of b.entries) {
|
|
4847
|
+
if (typeof e?.tell !== "string" || typeof e.path !== "string" || typeof e.excerpt !== "string" || !Number.isInteger(e.count) || e.count < 1) {
|
|
4848
|
+
throw new Error(`${source}: malformed entry ${JSON.stringify(e)}`);
|
|
4849
|
+
}
|
|
4850
|
+
}
|
|
4851
|
+
return b;
|
|
4852
|
+
}
|
|
4853
|
+
function recount(report2, findings) {
|
|
4854
|
+
const byGeneration = { 1: 0, 2: 0, 3: 0 };
|
|
4855
|
+
const byTell = {};
|
|
4856
|
+
for (const f4 of findings) {
|
|
4857
|
+
byGeneration[f4.generation] += 1;
|
|
4858
|
+
byTell[f4.tell] = (byTell[f4.tell] ?? 0) + 1;
|
|
4859
|
+
}
|
|
4860
|
+
return {
|
|
4861
|
+
...report2,
|
|
4862
|
+
findings,
|
|
4863
|
+
summary: { ...report2.summary, findings: findings.length, byGeneration, byTell, blocking: findings.filter((f4) => f4.severity === "block").length }
|
|
4864
|
+
};
|
|
4865
|
+
}
|
|
4866
|
+
function applyBaseline(report2, baseline) {
|
|
4867
|
+
const remaining = /* @__PURE__ */ new Map();
|
|
4868
|
+
for (const e of baseline.entries) {
|
|
4869
|
+
const key = keyOf(e.tell, e.path, e.excerpt);
|
|
4870
|
+
remaining.set(key, (remaining.get(key) ?? 0) + e.count);
|
|
4871
|
+
}
|
|
4872
|
+
const kept = [];
|
|
4873
|
+
let baselined = 0;
|
|
4874
|
+
for (const f4 of report2.findings) {
|
|
4875
|
+
const key = findingKey(f4);
|
|
4876
|
+
const left = remaining.get(key) ?? 0;
|
|
4877
|
+
if (left > 0) {
|
|
4878
|
+
remaining.set(key, left - 1);
|
|
4879
|
+
baselined += 1;
|
|
4880
|
+
} else kept.push(f4);
|
|
4881
|
+
}
|
|
4882
|
+
return { ...recount(report2, kept), baselined };
|
|
4883
|
+
}
|
|
4884
|
+
|
|
4885
|
+
// src/character/config.ts
|
|
4886
|
+
var DEFAULT_IGNORE = [".claude", ".agents", ".cursor"];
|
|
4887
|
+
var KEYS = /* @__PURE__ */ new Set(["$schema", "ignore", "copyPaths", "severity"]);
|
|
4888
|
+
var LEVELS = /* @__PURE__ */ new Set(["warn", "block", "off"]);
|
|
4889
|
+
var MIN_BECAUSE = 12;
|
|
4890
|
+
var isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
4891
|
+
var isStringArray = (v) => Array.isArray(v) && v.every((s) => typeof s === "string");
|
|
4892
|
+
function parseCraftConfig(data, knownTells, source = "craft.config.json") {
|
|
4893
|
+
if (!isRecord(data)) throw new Error(`${source}: expected an object`);
|
|
4894
|
+
for (const key of Object.keys(data)) if (!KEYS.has(key)) throw new Error(`${source}: unknown key "${key}"`);
|
|
4895
|
+
const ignore = data.ignore ?? [];
|
|
4896
|
+
if (!isStringArray(ignore)) throw new Error(`${source}: "ignore" must be an array of glob strings`);
|
|
4897
|
+
if (data.copyPaths !== void 0 && !isStringArray(data.copyPaths)) throw new Error(`${source}: "copyPaths" must be an array of paths`);
|
|
4898
|
+
const severity = {};
|
|
4899
|
+
const raw = data.severity ?? {};
|
|
4900
|
+
if (!isRecord(raw)) throw new Error(`${source}: "severity" must map a tell id to { level, because }`);
|
|
4901
|
+
for (const [tell, value] of Object.entries(raw)) {
|
|
4902
|
+
if (!knownTells.has(tell)) throw new Error(`${source}: severity names no tell called "${tell}"`);
|
|
4903
|
+
if (!isRecord(value) || !LEVELS.has(value.level)) {
|
|
4904
|
+
throw new Error(`${source}: severity.${tell} needs a level of "warn", "block" or "off"`);
|
|
4905
|
+
}
|
|
4906
|
+
if (typeof value.because !== "string" || value.because.trim().length < MIN_BECAUSE) {
|
|
4907
|
+
throw new Error(`${source}: severity.${tell} needs a because of at least a sentence`);
|
|
4908
|
+
}
|
|
4909
|
+
severity[tell] = { level: value.level, because: value.because };
|
|
4910
|
+
}
|
|
4911
|
+
return { ignore, severity, ...data.copyPaths ? { copyPaths: data.copyPaths } : {} };
|
|
4912
|
+
}
|
|
4913
|
+
function globToRegExp(glob) {
|
|
4914
|
+
const pattern = glob.replace(/^\.\//, "").replace(/\/$/, "").replace(/\/\*\*$/, "");
|
|
4915
|
+
let body = "";
|
|
4916
|
+
for (let i = 0; i < pattern.length; i += 1) {
|
|
4917
|
+
const c = pattern[i];
|
|
4918
|
+
if (c === "*" && pattern[i + 1] === "*") {
|
|
4919
|
+
const slash = pattern[i + 2] === "/";
|
|
4920
|
+
body += slash ? "(?:.*/)?" : ".*";
|
|
4921
|
+
i += slash ? 2 : 1;
|
|
4922
|
+
} else if (c === "*") body += "[^/]*";
|
|
4923
|
+
else if (c === "?") body += "[^/]";
|
|
4924
|
+
else body += c.replace(/[.+^${}()|[\]\\]/g, "\\$&");
|
|
4925
|
+
}
|
|
4926
|
+
const anywhere = !pattern.includes("/");
|
|
4927
|
+
return new RegExp(`^${anywhere ? "(?:.*/)?" : ""}${body}(?:/.*)?$`);
|
|
4928
|
+
}
|
|
4929
|
+
function ignoreMatcher(patterns) {
|
|
4930
|
+
const res = patterns.map(globToRegExp);
|
|
4931
|
+
return (path) => {
|
|
4932
|
+
const p = path.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
4933
|
+
return res.some((re) => re.test(p));
|
|
4934
|
+
};
|
|
4935
|
+
}
|
|
4936
|
+
function severityExceptions(config) {
|
|
4937
|
+
return Object.entries(config.severity).filter(([, o]) => o.level === "off").map(([tell, o]) => ({ tell, because: `craft.config.json: ${o.because}` }));
|
|
4938
|
+
}
|
|
4939
|
+
function applySeverity(report2, config) {
|
|
4940
|
+
const findings = report2.findings.map((f4) => {
|
|
4941
|
+
const o = config.severity[f4.tell];
|
|
4942
|
+
return o && o.level !== "off" ? { ...f4, severity: o.level } : f4;
|
|
4943
|
+
});
|
|
4944
|
+
return { ...report2, findings, summary: { ...report2.summary, blocking: findings.filter((f4) => f4.severity === "block").length } };
|
|
4945
|
+
}
|
|
4946
|
+
|
|
4947
|
+
// src/character/sarif.ts
|
|
4948
|
+
var isUrl = (path) => /^[a-z][a-z0-9+.-]*:\/\//i.test(path);
|
|
4949
|
+
function toSarif(report2, options) {
|
|
4950
|
+
const byId = new Map(options.tells.map((t) => [t.id, t]));
|
|
4951
|
+
const ids = [...new Set(report2.findings.map((f4) => f4.tell))].sort();
|
|
4952
|
+
const index = new Map(ids.map((id, i) => [id, i]));
|
|
4953
|
+
const rules = ids.map((id) => {
|
|
4954
|
+
const t = byId.get(id);
|
|
4955
|
+
return {
|
|
4956
|
+
id,
|
|
4957
|
+
name: t?.name ?? id,
|
|
4958
|
+
shortDescription: { text: t?.name ?? id },
|
|
4959
|
+
...t ? { fullDescription: { text: t.why }, help: { text: t.fix } } : {}
|
|
4960
|
+
};
|
|
4961
|
+
});
|
|
4962
|
+
const results = report2.findings.map((f4) => ({
|
|
4963
|
+
ruleId: f4.tell,
|
|
4964
|
+
ruleIndex: index.get(f4.tell),
|
|
4965
|
+
level: f4.severity === "block" ? "error" : "warning",
|
|
4966
|
+
message: { text: `${f4.message}. ${f4.fix}` },
|
|
4967
|
+
locations: [
|
|
4968
|
+
{
|
|
4969
|
+
physicalLocation: {
|
|
4970
|
+
artifactLocation: { uri: isUrl(f4.path) ? f4.path : f4.path.replace(/\\/g, "/") },
|
|
4971
|
+
...f4.line > 0 ? { region: { startLine: f4.line, snippet: { text: f4.excerpt } } } : {}
|
|
4972
|
+
}
|
|
4973
|
+
}
|
|
4974
|
+
],
|
|
4975
|
+
partialFingerprints: { "craftFinding/v1": fnv1a(findingKey(f4)) }
|
|
4976
|
+
}));
|
|
4977
|
+
return {
|
|
4978
|
+
$schema: "https://json.schemastore.org/sarif-2.1.0.json",
|
|
4979
|
+
version: "2.1.0",
|
|
4980
|
+
runs: [
|
|
4981
|
+
{
|
|
4982
|
+
tool: {
|
|
4983
|
+
driver: {
|
|
4984
|
+
name: "craft",
|
|
4985
|
+
version: report2.catalogueVersion,
|
|
4986
|
+
informationUri: options.informationUri ?? "https://www.npmjs.com/package/@domandigital/craft",
|
|
4987
|
+
rules
|
|
4988
|
+
}
|
|
4989
|
+
},
|
|
4990
|
+
results
|
|
4991
|
+
}
|
|
4992
|
+
]
|
|
4993
|
+
};
|
|
4994
|
+
}
|
|
4995
|
+
|
|
4996
|
+
// src/character/json.ts
|
|
4997
|
+
var JSON_SCHEMA_VERSION = 1;
|
|
4998
|
+
function toJson(data) {
|
|
4999
|
+
return JSON.stringify({ schemaVersion: JSON_SCHEMA_VERSION, ...data }, null, 2);
|
|
5000
|
+
}
|
|
5001
|
+
|
|
5002
|
+
// src/snapshot/migrate.ts
|
|
5003
|
+
var READABLE_SNAPSHOT_VERSIONS = [1, 2];
|
|
5004
|
+
function readSnapshot(data, source = "snapshot") {
|
|
5005
|
+
const v = data?.version;
|
|
5006
|
+
if (typeof data !== "object" || data === null || !READABLE_SNAPSHOT_VERSIONS.includes(v)) {
|
|
5007
|
+
throw new Error(`${source} is not a snapshot this craft reads (version ${String(v)}; reads ${READABLE_SNAPSHOT_VERSIONS.join(" and ")})`);
|
|
5008
|
+
}
|
|
5009
|
+
const snap = data;
|
|
5010
|
+
if (snap.version === 1) return { ...snap, version: SNAPSHOT_VERSION, migratedFrom: 1 };
|
|
5011
|
+
return snap;
|
|
5012
|
+
}
|
|
5013
|
+
|
|
5014
|
+
// src/snapshot/visual.ts
|
|
5015
|
+
var EDGE_STEP = 32;
|
|
5016
|
+
function visualMeasures(img, maxSamples = 1e6) {
|
|
5017
|
+
const { width: w, height: h, rgba } = img;
|
|
5018
|
+
if (w < 2 || h < 2 || rgba.length < w * h * 4) throw new Error(`visualMeasures: ${w}x${h} image with ${rgba.length} bytes`);
|
|
5019
|
+
const step = Math.max(1, Math.ceil(Math.sqrt(w * h / maxSamples)));
|
|
5020
|
+
const lum = (x, y) => {
|
|
5021
|
+
const i = (y * w + x) * 4;
|
|
5022
|
+
return 0.2126 * rgba[i] + 0.7152 * rgba[i + 1] + 0.0722 * rgba[i + 2];
|
|
5023
|
+
};
|
|
5024
|
+
let n = 0;
|
|
5025
|
+
let sumRg = 0;
|
|
5026
|
+
let sumYb = 0;
|
|
5027
|
+
let sumRg2 = 0;
|
|
5028
|
+
let sumYb2 = 0;
|
|
5029
|
+
let edges = 0;
|
|
5030
|
+
let edgeN = 0;
|
|
5031
|
+
let asym = 0;
|
|
5032
|
+
let asymN = 0;
|
|
5033
|
+
for (let y = 0; y < h; y += step) {
|
|
5034
|
+
for (let x = 0; x < w; x += step) {
|
|
5035
|
+
const i = (y * w + x) * 4;
|
|
5036
|
+
const r = rgba[i];
|
|
5037
|
+
const g = rgba[i + 1];
|
|
5038
|
+
const b = rgba[i + 2];
|
|
5039
|
+
const rg = r - g;
|
|
5040
|
+
const yb = 0.5 * (r + g) - b;
|
|
5041
|
+
sumRg += rg;
|
|
5042
|
+
sumYb += yb;
|
|
5043
|
+
sumRg2 += rg * rg;
|
|
5044
|
+
sumYb2 += yb * yb;
|
|
5045
|
+
n += 1;
|
|
5046
|
+
const l = lum(x, y);
|
|
5047
|
+
if (x + step < w && y + step < h) {
|
|
5048
|
+
if (Math.abs(lum(x + step, y) - l) + Math.abs(lum(x, y + step) - l) > EDGE_STEP) edges += 1;
|
|
5049
|
+
edgeN += 1;
|
|
5050
|
+
}
|
|
5051
|
+
if (x < w / 2) {
|
|
5052
|
+
asym += Math.abs(l - lum(w - 1 - x, y));
|
|
5053
|
+
asymN += 1;
|
|
5054
|
+
}
|
|
5055
|
+
}
|
|
5056
|
+
}
|
|
5057
|
+
const meanRg = sumRg / n;
|
|
5058
|
+
const meanYb = sumYb / n;
|
|
5059
|
+
const sdRg = Math.sqrt(Math.max(0, sumRg2 / n - meanRg * meanRg));
|
|
5060
|
+
const sdYb = Math.sqrt(Math.max(0, sumYb2 / n - meanYb * meanYb));
|
|
5061
|
+
const colourfulness = Math.sqrt(sdRg ** 2 + sdYb ** 2) + 0.3 * Math.sqrt(meanRg ** 2 + meanYb ** 2);
|
|
5062
|
+
const round2 = (v, d = 3) => Math.round(v * 10 ** d) / 10 ** d;
|
|
5063
|
+
return {
|
|
5064
|
+
colourfulness: round2(colourfulness, 1),
|
|
5065
|
+
edgeDensity: round2(edgeN ? edges / edgeN : 0),
|
|
5066
|
+
symmetry: round2(asymN ? 1 - asym / asymN / 255 : 1),
|
|
5067
|
+
width: w,
|
|
5068
|
+
height: h,
|
|
5069
|
+
sampled: n
|
|
5070
|
+
};
|
|
5071
|
+
}
|
|
5072
|
+
|
|
4755
5073
|
// src/fingerprint/index.ts
|
|
4756
|
-
var FINGERPRINT_VERSION =
|
|
5074
|
+
var FINGERPRINT_VERSION = 2;
|
|
4757
5075
|
var CHROMATIC = 0.03;
|
|
4758
5076
|
var MIN_ACCENT_AREA = 2e4;
|
|
4759
5077
|
var oklch = (value) => {
|
|
@@ -4808,23 +5126,36 @@ function fingerprint(s) {
|
|
|
4808
5126
|
roundness: median(s.controls.map((c) => Math.min(0.5, c.radiusPx / Math.max(1, c.heightPx)))),
|
|
4809
5127
|
motion: below ? Math.min(1, s.motion.hiddenSections / below) : 0,
|
|
4810
5128
|
effects: effects.sort(),
|
|
4811
|
-
layout: s.sections.map((x) => x.kind)
|
|
5129
|
+
layout: s.sections.map((x) => x.kind),
|
|
5130
|
+
...s.sections.length > 0 && s.sections.every((x) => x.role && x.geometry) ? { sections: s.sections.map((x) => ({ role: x.role, centred: x.geometry.centredShare, width: x.geometry.contentWidthRatio })) } : {}
|
|
4812
5131
|
};
|
|
4813
5132
|
}
|
|
4814
5133
|
var DISTANCE_WEIGHTS = { accent: 0.25, type: 0.25, ground: 0.1, shape: 0.1, motion: 0.1, effects: 0.1, layout: 0.1 };
|
|
4815
|
-
function levenshtein(a, b) {
|
|
5134
|
+
function levenshtein(a, b, cost = (x, y) => x === y ? 0 : 1) {
|
|
4816
5135
|
const row = Array.from({ length: b.length + 1 }, (_, i) => i);
|
|
4817
5136
|
for (let i = 1; i <= a.length; i += 1) {
|
|
4818
5137
|
let prev = row[0];
|
|
4819
5138
|
row[0] = i;
|
|
4820
5139
|
for (let j = 1; j <= b.length; j += 1) {
|
|
4821
5140
|
const cur = row[j];
|
|
4822
|
-
row[j] = Math.min(row[j] + 1, row[j - 1] + 1, prev + (a[i - 1]
|
|
5141
|
+
row[j] = Math.min(row[j] + 1, row[j - 1] + 1, prev + cost(a[i - 1], b[j - 1]));
|
|
4823
5142
|
prev = cur;
|
|
4824
5143
|
}
|
|
4825
5144
|
}
|
|
4826
5145
|
return row[b.length];
|
|
4827
5146
|
}
|
|
5147
|
+
function sectionCost(x, y) {
|
|
5148
|
+
if (x.role !== y.role) return 1;
|
|
5149
|
+
return Math.min(0.5, (Math.abs(x.centred - y.centred) + Math.abs(x.width - y.width)) / 2);
|
|
5150
|
+
}
|
|
5151
|
+
function layoutDistance(a, b) {
|
|
5152
|
+
if (a.sections && b.sections) {
|
|
5153
|
+
const longest2 = Math.max(a.sections.length, b.sections.length);
|
|
5154
|
+
return longest2 === 0 ? 0 : levenshtein(a.sections, b.sections, sectionCost) / longest2;
|
|
5155
|
+
}
|
|
5156
|
+
const longest = Math.max(a.layout.length, b.layout.length);
|
|
5157
|
+
return longest === 0 ? 0 : levenshtein(a.layout, b.layout) / longest;
|
|
5158
|
+
}
|
|
4828
5159
|
var clamp = (n) => Math.max(0, Math.min(1, n));
|
|
4829
5160
|
function faceDistance(a, b) {
|
|
4830
5161
|
if (a.family.toLowerCase() === b.family.toLowerCase()) return 0;
|
|
@@ -4839,8 +5170,7 @@ function fingerprintDistance(a, b) {
|
|
|
4839
5170
|
const union = /* @__PURE__ */ new Set([...a.effects, ...b.effects]);
|
|
4840
5171
|
const shared = a.effects.filter((e) => b.effects.includes(e)).length;
|
|
4841
5172
|
const effects = union.size === 0 ? 0 : 1 - shared / union.size;
|
|
4842
|
-
const
|
|
4843
|
-
const layout = longest === 0 ? 0 : levenshtein(a.layout, b.layout) / longest;
|
|
5173
|
+
const layout = layoutDistance(a, b);
|
|
4844
5174
|
const parts2 = { accent, ground, type, shape, motion, effects, layout };
|
|
4845
5175
|
const total = Object.keys(DISTANCE_WEIGHTS).reduce((sum, k) => sum + DISTANCE_WEIGHTS[k] * parts2[k], 0);
|
|
4846
5176
|
return { total: Math.round(total * 1e3) / 1e3, parts: parts2 };
|
|
@@ -4868,6 +5198,117 @@ var SOURCE_KINDS = [
|
|
|
4868
5198
|
];
|
|
4869
5199
|
var CHOICE_KEYS = ["accent", "ground", "display", "body", "shape", "motif", "signature"];
|
|
4870
5200
|
|
|
5201
|
+
// src/direction/licences.ts
|
|
5202
|
+
var LICENCES = [
|
|
5203
|
+
{
|
|
5204
|
+
name: "Basteleur",
|
|
5205
|
+
subject: "face",
|
|
5206
|
+
licence: "OFL-1.1",
|
|
5207
|
+
multiClient: "yes",
|
|
5208
|
+
checked: "2026-09-24",
|
|
5209
|
+
source: "https://velvetyne.fr/fonts/basteleur/",
|
|
5210
|
+
note: "Velvetyne, designed by Keussel. Keep the OFL notice with the font files."
|
|
5211
|
+
},
|
|
5212
|
+
{
|
|
5213
|
+
name: "Gulax",
|
|
5214
|
+
subject: "face",
|
|
5215
|
+
licence: "OFL-1.1",
|
|
5216
|
+
multiClient: "yes",
|
|
5217
|
+
checked: "2026-09-24",
|
|
5218
|
+
source: "https://velvetyne.fr/fonts/gulax/",
|
|
5219
|
+
note: "Velvetyne. Keep the OFL notice with the font files."
|
|
5220
|
+
},
|
|
5221
|
+
{
|
|
5222
|
+
name: "Le Murmure",
|
|
5223
|
+
subject: "face",
|
|
5224
|
+
licence: "OFL-1.1",
|
|
5225
|
+
multiClient: "yes",
|
|
5226
|
+
checked: "2026-09-24",
|
|
5227
|
+
source: "https://velvetyne.fr/fonts/le-murmure/",
|
|
5228
|
+
note: "Velvetyne. Keep the OFL notice with the font files."
|
|
5229
|
+
},
|
|
5230
|
+
{
|
|
5231
|
+
name: "Blaze Type",
|
|
5232
|
+
subject: "foundry",
|
|
5233
|
+
licence: "commercial",
|
|
5234
|
+
multiClient: "per-site",
|
|
5235
|
+
cap: 10,
|
|
5236
|
+
checked: "2026-09-24",
|
|
5237
|
+
source: "https://blazetype.eu/eula",
|
|
5238
|
+
note: "The EULA sells a professional licence for 1 to 10 websites, and lets an agency buy in a client's name at the listed price. Its /license page says one licence covers every domain; the EULA is the contract, so buy per client."
|
|
5239
|
+
},
|
|
5240
|
+
{
|
|
5241
|
+
name: "Power Type",
|
|
5242
|
+
subject: "foundry",
|
|
5243
|
+
licence: "commercial",
|
|
5244
|
+
multiClient: "unverified",
|
|
5245
|
+
checked: "2026-09-24",
|
|
5246
|
+
source: "https://power-type.com/eula",
|
|
5247
|
+
note: "Research on 2026-09-11 reported a Super License capped at 10 self-hosted domains. The EULA page did not load when checked; read it before relying on the cap."
|
|
5248
|
+
},
|
|
5249
|
+
{
|
|
5250
|
+
name: "Grilli Type",
|
|
5251
|
+
subject: "foundry",
|
|
5252
|
+
licence: "commercial",
|
|
5253
|
+
multiClient: "per-site",
|
|
5254
|
+
checked: "2026-09-24",
|
|
5255
|
+
source: "https://www.grillitype.com/information",
|
|
5256
|
+
note: "Web licences cover any number of domains controlled by the licensee, priced by monthly visitors, so a client site needs the client's own licence. Unlimited tiers start at $10,000 per style."
|
|
5257
|
+
},
|
|
5258
|
+
{
|
|
5259
|
+
name: "Phosphor",
|
|
5260
|
+
subject: "icons",
|
|
5261
|
+
licence: "MIT",
|
|
5262
|
+
multiClient: "yes",
|
|
5263
|
+
checked: "2026-09-24",
|
|
5264
|
+
source: "https://github.com/phosphor-icons/core/blob/main/LICENSE",
|
|
5265
|
+
note: "Six weights. The icon-tile tells treat it exactly as they treat Lucide: the library is not the tell, the tinted tile is."
|
|
5266
|
+
},
|
|
5267
|
+
{
|
|
5268
|
+
name: "Tabler Icons",
|
|
5269
|
+
subject: "icons",
|
|
5270
|
+
licence: "MIT",
|
|
5271
|
+
multiClient: "yes",
|
|
5272
|
+
checked: "2026-09-24",
|
|
5273
|
+
source: "https://github.com/tabler/tabler-icons/blob/main/LICENSE",
|
|
5274
|
+
note: "One stroke grid across the set."
|
|
5275
|
+
},
|
|
5276
|
+
{
|
|
5277
|
+
name: "Iconoir",
|
|
5278
|
+
subject: "icons",
|
|
5279
|
+
licence: "MIT",
|
|
5280
|
+
multiClient: "yes",
|
|
5281
|
+
checked: "2026-09-24",
|
|
5282
|
+
source: "https://github.com/iconoir-icons/iconoir/blob/main/LICENSE",
|
|
5283
|
+
note: "Smaller set, no paid tier."
|
|
5284
|
+
},
|
|
5285
|
+
{
|
|
5286
|
+
name: "Unicons",
|
|
5287
|
+
subject: "icons",
|
|
5288
|
+
licence: "IconScout Simple License",
|
|
5289
|
+
multiClient: "yes",
|
|
5290
|
+
checked: "2026-09-24",
|
|
5291
|
+
source: "https://github.com/Iconscout/unicons/blob/master/LICENSE",
|
|
5292
|
+
note: "IconScout's free set, personal and commercial use, attribution optional."
|
|
5293
|
+
},
|
|
5294
|
+
{
|
|
5295
|
+
name: "IconScout premium",
|
|
5296
|
+
subject: "icons",
|
|
5297
|
+
licence: "commercial",
|
|
5298
|
+
multiClient: "per-site",
|
|
5299
|
+
checked: "2026-09-24",
|
|
5300
|
+
source: "https://iconscout.com/licenses",
|
|
5301
|
+
note: "Each licence covers a single end product. Never in a tool, template or shared kit (create-site, craft, a house component library), even modified. Record the assets each site uses. Assets come from different contributors, so take one site's set from one contributor."
|
|
5302
|
+
}
|
|
5303
|
+
];
|
|
5304
|
+
function faceLicence(family) {
|
|
5305
|
+
const name = family.trim().toLowerCase();
|
|
5306
|
+
return LICENCES.find((l) => l.subject === "face" && l.name.toLowerCase() === name);
|
|
5307
|
+
}
|
|
5308
|
+
function licencesJson() {
|
|
5309
|
+
return JSON.stringify({ licences: LICENCES }, null, 2) + "\n";
|
|
5310
|
+
}
|
|
5311
|
+
|
|
4871
5312
|
// src/direction/validate.ts
|
|
4872
5313
|
var PREFERENCE = /\b(?:client (?:likes|liked|wants|wanted|asked|chose|prefers?)|(?:we|they|i) (?:like|liked|love|loved|prefer)|on[- ]trend|trendy|popular|what (?:everyone|competitors) (?:uses?|does)|brand (?:colou?r|font|guidelines?) (?:is|are|says?))\b/i;
|
|
4873
5314
|
var MOOD = /\b(?:modern|clean|fresh|professional|trustworthy|premium|elegant|sleek|timeless|minimal(?:ist)?|bold|vibrant|friendly|approachable|luxur(?:y|ious)|sophisticated|contemporary|stylish|warm|inviting|high[- ]end)\b/gi;
|
|
@@ -4991,6 +5432,18 @@ function validateDirection(input, ctx = {}) {
|
|
|
4991
5432
|
ok = false;
|
|
4992
5433
|
}
|
|
4993
5434
|
}
|
|
5435
|
+
if ((key === "display" || key === "body") && typeof c.value === "string" && c.value.trim()) {
|
|
5436
|
+
const family = normaliseFamily(c.value);
|
|
5437
|
+
const entry = faceLicence(family);
|
|
5438
|
+
if (!entry) {
|
|
5439
|
+
warn(`${at}.value`, `${family}: licence not in craft's register. Read the licensor's own terms for use on this client's site, then add it to src/direction/licences.ts.`);
|
|
5440
|
+
} else if (entry.multiClient === "unverified") {
|
|
5441
|
+
warn(`${at}.value`, `${family}: licence unverified (${entry.note})`);
|
|
5442
|
+
} else if (entry.multiClient === "capped" || entry.multiClient === "per-site") {
|
|
5443
|
+
const scope = entry.multiClient === "capped" ? `covers up to ${entry.cap} sites` : "needs a licence for this site, in the client's name";
|
|
5444
|
+
warn(`${at}.value`, `${family}: ${entry.licence} licence ${scope}. Record the purchase before launch.`);
|
|
5445
|
+
}
|
|
5446
|
+
}
|
|
4994
5447
|
const fp = ctx.fingerprint;
|
|
4995
5448
|
if (fp && typeof c.value === "string") {
|
|
4996
5449
|
if ((key === "accent" || key === "ground") && parseColour(c.value)) {
|
|
@@ -5647,10 +6100,12 @@ function retrofitPlan(report2, direction) {
|
|
|
5647
6100
|
AI_PHRASES,
|
|
5648
6101
|
AI_VIOLET,
|
|
5649
6102
|
AI_WORDS,
|
|
6103
|
+
BASELINE_VERSION,
|
|
5650
6104
|
BUZZWORDS,
|
|
5651
6105
|
CATALOGUE,
|
|
5652
6106
|
CATALOGUE_VERSION,
|
|
5653
6107
|
CHOICE_KEYS,
|
|
6108
|
+
DEFAULT_IGNORE,
|
|
5654
6109
|
DENSITIES,
|
|
5655
6110
|
DENSITY_NAMES,
|
|
5656
6111
|
DIRECTION_VERSION,
|
|
@@ -5659,12 +6114,16 @@ function retrofitPlan(report2, direction) {
|
|
|
5659
6114
|
DURATION_S,
|
|
5660
6115
|
EASE,
|
|
5661
6116
|
EASE_TUPLE,
|
|
6117
|
+
EDGE_STEP,
|
|
5662
6118
|
ESTATE_VERSION,
|
|
5663
6119
|
EXIT_RATIO,
|
|
5664
6120
|
FINGERPRINT_VERSION,
|
|
5665
6121
|
HOUSE,
|
|
5666
6122
|
HOUSE_BUDGET,
|
|
6123
|
+
HOUSE_LOCKED,
|
|
5667
6124
|
HOUSE_TYPE,
|
|
6125
|
+
JSON_SCHEMA_VERSION,
|
|
6126
|
+
LICENCES,
|
|
5668
6127
|
LIGHTNESS_CURVE,
|
|
5669
6128
|
MIN_DECIDED,
|
|
5670
6129
|
MIN_RUNS,
|
|
@@ -5674,6 +6133,7 @@ function retrofitPlan(report2, direction) {
|
|
|
5674
6133
|
PLAINER_WORDS,
|
|
5675
6134
|
PROPOSED,
|
|
5676
6135
|
RAMP_STEPS,
|
|
6136
|
+
READABLE_SNAPSHOT_VERSIONS,
|
|
5677
6137
|
REFLEX_FONTS_1,
|
|
5678
6138
|
REFLEX_FONTS_2,
|
|
5679
6139
|
RENDERED_PATHS,
|
|
@@ -5695,7 +6155,9 @@ function retrofitPlan(report2, direction) {
|
|
|
5695
6155
|
accentFork,
|
|
5696
6156
|
addSite,
|
|
5697
6157
|
apcaContrast,
|
|
6158
|
+
applyBaseline,
|
|
5698
6159
|
applyHouseGate,
|
|
6160
|
+
applySeverity,
|
|
5699
6161
|
auditSnapshot,
|
|
5700
6162
|
catalogueTable,
|
|
5701
6163
|
characterReport,
|
|
@@ -5706,6 +6168,7 @@ function retrofitPlan(report2, direction) {
|
|
|
5706
6168
|
compareFacts,
|
|
5707
6169
|
compareToEstate,
|
|
5708
6170
|
craftTokens,
|
|
6171
|
+
createBaseline,
|
|
5709
6172
|
deltaEOk,
|
|
5710
6173
|
deltaEOkHex,
|
|
5711
6174
|
densityCss,
|
|
@@ -5715,8 +6178,10 @@ function retrofitPlan(report2, direction) {
|
|
|
5715
6178
|
estatePairs,
|
|
5716
6179
|
exitDuration,
|
|
5717
6180
|
extractHtml,
|
|
6181
|
+
faceLicence,
|
|
5718
6182
|
findClaims,
|
|
5719
6183
|
findColours,
|
|
6184
|
+
findingKey,
|
|
5720
6185
|
fingerprint,
|
|
5721
6186
|
fingerprintDistance,
|
|
5722
6187
|
fluidClamp,
|
|
@@ -5727,17 +6192,22 @@ function retrofitPlan(report2, direction) {
|
|
|
5727
6192
|
formatComparison,
|
|
5728
6193
|
formatHex,
|
|
5729
6194
|
formatReport,
|
|
6195
|
+
globToRegExp,
|
|
5730
6196
|
groundName,
|
|
5731
6197
|
harvest,
|
|
5732
6198
|
hexToOklch,
|
|
6199
|
+
holdHouseBlocks,
|
|
5733
6200
|
houseRule,
|
|
5734
6201
|
hueName,
|
|
5735
6202
|
hueSwatch,
|
|
6203
|
+
ignoreMatcher,
|
|
5736
6204
|
inP3Gamut,
|
|
5737
6205
|
inSrgbGamut,
|
|
5738
6206
|
initDirection,
|
|
5739
6207
|
isAiViolet,
|
|
5740
6208
|
isCream,
|
|
6209
|
+
layoutDistance,
|
|
6210
|
+
licencesJson,
|
|
5741
6211
|
makeSnapshot,
|
|
5742
6212
|
motionTokens,
|
|
5743
6213
|
namedSources,
|
|
@@ -5747,17 +6217,21 @@ function retrofitPlan(report2, direction) {
|
|
|
5747
6217
|
oklchToHex,
|
|
5748
6218
|
oklchToRgb,
|
|
5749
6219
|
paletteFromPixels,
|
|
6220
|
+
parseBaseline,
|
|
5750
6221
|
parseColour,
|
|
6222
|
+
parseCraftConfig,
|
|
5751
6223
|
parseHex,
|
|
5752
6224
|
propose,
|
|
5753
6225
|
protectedFacts,
|
|
5754
6226
|
ramp,
|
|
5755
6227
|
rampFromAnchors,
|
|
6228
|
+
readSnapshot,
|
|
5756
6229
|
retrofitPlan,
|
|
5757
6230
|
runTell,
|
|
5758
6231
|
scanSource,
|
|
5759
6232
|
sectionRhythm,
|
|
5760
6233
|
semantic,
|
|
6234
|
+
severityExceptions,
|
|
5761
6235
|
shapeName,
|
|
5762
6236
|
sharedParts,
|
|
5763
6237
|
shouldAnimate,
|
|
@@ -5766,10 +6240,13 @@ function retrofitPlan(report2, direction) {
|
|
|
5766
6240
|
tailwindV4Theme,
|
|
5767
6241
|
tellById,
|
|
5768
6242
|
toGamut,
|
|
6243
|
+
toJson,
|
|
6244
|
+
toSarif,
|
|
5769
6245
|
typeFeatureTokens,
|
|
5770
6246
|
typicality,
|
|
5771
6247
|
validateDirection,
|
|
5772
6248
|
visibleText,
|
|
6249
|
+
visualMeasures,
|
|
5773
6250
|
wcagContrast,
|
|
5774
6251
|
wordListsJson
|
|
5775
6252
|
});
|