@domandigital/craft 0.8.1 → 0.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.
- package/CHANGELOG.md +13 -0
- package/CHARACTER.md +2 -1
- package/COPY.md +12 -2
- package/README.md +6 -0
- package/dist/{audit-C62GULQR.js → audit-EGDNWSQB.js} +3 -3
- package/dist/audit.cjs +67 -13
- package/dist/audit.js +67 -13
- package/dist/{chunk-2VLN6JLN.js → chunk-2NQFIDLE.js} +5 -5
- package/dist/{chunk-OBVWDPQQ.js → chunk-3NGWXZZE.js} +67 -13
- package/dist/{chunk-XEH4GK5K.js → chunk-APO4BT2F.js} +4 -4
- package/dist/{chunk-X6PHP22P.js → chunk-JKIDWHG3.js} +133 -7
- package/dist/{chunk-JNFHQGKR.js → chunk-JWHC67ZS.js} +1 -1
- package/dist/{chunk-DWZY5MWD.js → chunk-QMYERCBB.js} +1 -1
- package/dist/{chunk-UVBVYM5D.js → chunk-TSKRBIOC.js} +2 -2
- package/dist/{chunk-6LPMM752.js → chunk-U2JTFQKO.js} +5 -5
- package/dist/{cli-BWERSVPW.js → cli-2WBGTBKD.js} +4 -4
- package/dist/{cli-UMQNCHAQ.js → cli-ADQQURNR.js} +9 -9
- package/dist/cli-EK3GSAOW.js +15 -0
- package/dist/{cli-C5RLL7AW.js → cli-PRWTNFVP.js} +4 -4
- package/dist/cli-VQUNNIZM.js +16 -0
- package/dist/cli.js +2 -2
- package/dist/index.cjs +187 -13
- package/dist/index.d.cts +43 -2
- package/dist/index.d.ts +43 -2
- package/dist/index.js +184 -13
- package/package.json +1 -1
- package/word-lists.json +1 -1
- package/dist/cli-D6VYGCOE.js +0 -16
- package/dist/cli-I4TQMCUP.js +0 -15
package/dist/index.js
CHANGED
|
@@ -1782,10 +1782,14 @@ var COPY_TELLS = [
|
|
|
1782
1782
|
surface: "copy",
|
|
1783
1783
|
why: "Three fragments in a row ('Fast. Friendly. Local.' or 'No fuss. No jargon. Just results.') is the second wave's favourite rhythm.",
|
|
1784
1784
|
fix: "Write one sentence that says which of the three is true, and how you know.",
|
|
1785
|
-
|
|
1785
|
+
// The fragments share one paragraph: "You don't. Again.", a blank line, then "It's frustrating." is two beats, not three.
|
|
1786
|
+
detect: phrases([/(?:\b[A-Z][\w'’-]*(?:[ \t]+[\w'’-]+){0,2}[.!](?:[ \t]+|[ \t]*\n(?![ \t]*\n)[ \t]*)){2}[A-Z][\w'’-]*(?:[ \t]+[\w'’-]+){0,2}[.!](?=\s|$)/], quoted),
|
|
1786
1787
|
fixtures: {
|
|
1787
|
-
flag: [f("content/home.md", "No fuss. No jargon. Just results.")],
|
|
1788
|
-
pass: [
|
|
1788
|
+
flag: [f("content/home.md", "No fuss. No jargon. Just results."), f("content/home.md", "No fuss.\nNo jargon.\nJust results.")],
|
|
1789
|
+
pass: [
|
|
1790
|
+
f("content/home.md", "We fix boilers the same day, and we tell you the price before we start."),
|
|
1791
|
+
f("content/guide.md", "Your competitor appears in the top three. You don't. Again.\n\nIt's frustrating.")
|
|
1792
|
+
]
|
|
1789
1793
|
}
|
|
1790
1794
|
},
|
|
1791
1795
|
{
|
|
@@ -1804,6 +1808,14 @@ var COPY_TELLS = [
|
|
|
1804
1808
|
}
|
|
1805
1809
|
];
|
|
1806
1810
|
var ING_TAIL = /,\s+(?:ensuring|highlighting|underscoring|showcasing|reflecting|demonstrating|emphasi[sz]ing|fostering|cementing|solidifying|reinforcing|symboli[sz]ing|signalling|signaling)\b[^.!?\n]{0,80}/i;
|
|
1811
|
+
var PROMPT_CONTEXT = [
|
|
1812
|
+
/\b(?:the|this|that)\s+(?:attached|uploaded|provided)\s+(?:(?:Perplexity|ChatGPT|Claude|Gemini|Copilot)\s+)?(?:research|research file|research pack|source file|sources)\b/i,
|
|
1813
|
+
/\b(?:attached|uploaded)\s+(?:Perplexity|ChatGPT|Claude|Gemini|Copilot)\b/i,
|
|
1814
|
+
/\bthe\s+(?:attached|uploaded|provided)\s+brief\s+(?:asks|says|wants|requests|suggests|mentions)\b/i
|
|
1815
|
+
];
|
|
1816
|
+
function gerundList(block, _match, index) {
|
|
1817
|
+
return /(?:^|[(,;:]\s*)\w+ing\b[^,.;()!?]{0,40}$/.test(block.text.slice(Math.max(0, index - 60), index));
|
|
1818
|
+
}
|
|
1807
1819
|
var VAGUE_ATTRIBUTION = [
|
|
1808
1820
|
/\b(?:studies|research|experts|scientists|surveys|statistics|the data)\s+(?:show|shows|suggest|suggests|agree|say|says|confirm|confirms|prove|proves|indicate|indicates)\b/i,
|
|
1809
1821
|
/\b(?:many|most)\s+(?:experts|people|customers|professionals|homeowners)\s+(?:agree|say|believe)\b/i,
|
|
@@ -1889,8 +1901,10 @@ function inlineLabelHits(ctx) {
|
|
|
1889
1901
|
run = [];
|
|
1890
1902
|
};
|
|
1891
1903
|
for (const line of lines) {
|
|
1892
|
-
const m = /^\s*
|
|
1893
|
-
|
|
1904
|
+
const m = /^\s*[-*+]\s+\*\*([^*]{1,40}?):?\*\*:?\s+(\S.*)$/.exec(line);
|
|
1905
|
+
const data = m && /[\d£$€%`]|https?:|^\*\*/.test(m[2]);
|
|
1906
|
+
const named = m && /\S[A-Z]|[./]/.test(m[1]);
|
|
1907
|
+
if (m && !data && !named && m[2].trim().split(/\s+/).length <= 6) run.push(offset);
|
|
1894
1908
|
else flush();
|
|
1895
1909
|
offset += line.length + 1;
|
|
1896
1910
|
}
|
|
@@ -1928,6 +1942,29 @@ var RESEARCH_TELLS = [
|
|
|
1928
1942
|
]
|
|
1929
1943
|
}
|
|
1930
1944
|
},
|
|
1945
|
+
{
|
|
1946
|
+
id: "prompt-context",
|
|
1947
|
+
name: "Prompt context",
|
|
1948
|
+
generation: 1,
|
|
1949
|
+
severity: "warn",
|
|
1950
|
+
surface: "copy",
|
|
1951
|
+
why: "'This article uses the attached Perplexity research file as the primary source', 'the attached research is right to say': the model talking about what it was given. A reader of a web page has no attachment. Found 38 times across five live DD articles in 2026-09, none caught by any other tell.",
|
|
1952
|
+
fix: "Delete the reference to the research or brief. Where it carried a claim, name the real source or cut the claim.",
|
|
1953
|
+
detect: phrases(PROMPT_CONTEXT, quoted),
|
|
1954
|
+
fixtures: {
|
|
1955
|
+
flag: [
|
|
1956
|
+
f("content/guide.md", "This article uses the attached Perplexity research file as the primary source."),
|
|
1957
|
+
f("content/guide.md", "The attached research is right to say these platforms suit early-stage businesses."),
|
|
1958
|
+
f("content/guide.md", "That is exactly how the uploaded research frames the category."),
|
|
1959
|
+
f("content/guide.md", "The attached brief asks for the point that most tradespeople miss.")
|
|
1960
|
+
],
|
|
1961
|
+
pass: [
|
|
1962
|
+
f("content/guide.md", "We research every quote before we send it."),
|
|
1963
|
+
f("app/email.tsx", `export const Invoice = () => <p>Your invoice is attached.</p>;`),
|
|
1964
|
+
f("content/guide.md", "BrightLocal's research shows that reviews drive 20% of local ranking.")
|
|
1965
|
+
]
|
|
1966
|
+
}
|
|
1967
|
+
},
|
|
1931
1968
|
{
|
|
1932
1969
|
id: "placeholder",
|
|
1933
1970
|
name: "Unfilled placeholder",
|
|
@@ -1980,7 +2017,11 @@ var RESEARCH_TELLS = [
|
|
|
1980
2017
|
pass: [
|
|
1981
2018
|
f("content/home.md", "- Pages load in under a second\n- Card details never touch our server\n- Support by phone until 8pm\n"),
|
|
1982
2019
|
f("content/home.md", "- **Speed:** Faster pages\n- **Security:** Safer data\n"),
|
|
1983
|
-
f("content/guide.md", "- **GDC**: The statutory regulator for all dental professionals.\n- **GCC**: The statutory regulator for chiropractors in the UK.\n- **CQC**: The independent regulator of health and social care in England.\n")
|
|
2020
|
+
f("content/guide.md", "- **GDC**: The statutory regulator for all dental professionals.\n- **GCC**: The statutory regulator for chiropractors in the UK.\n- **CQC**: The independent regulator of health and social care in England.\n"),
|
|
2021
|
+
f("content/guide.md", "- **Per page**: \xA3100-\xA3200 per page\n- **Hourly**: \xA350-\xA375/hour\n- **Day rate**: \xA3350-\xA3500\n"),
|
|
2022
|
+
f("content/guide.md", "- **Law Society Find a Solicitor:** For solicitors\n- **AccountingWEB / ICAEW directories:** For accountants\n- **NHS.uk:** For NHS services\n"),
|
|
2023
|
+
f("content/guide.md", "1. **Visit PageSpeed Insights** and paste the address\n2. **Enter their URL** and run it\n3. **Note their scores** for later\n"),
|
|
2024
|
+
f("content/guide.md", "- **Structural problems:** **REBUILD.**\n- **Messaging issues:** **REFRESH.**\n- **Neither:** **LEAVE IT.**\n")
|
|
1984
2025
|
]
|
|
1985
2026
|
}
|
|
1986
2027
|
},
|
|
@@ -1992,7 +2033,7 @@ var RESEARCH_TELLS = [
|
|
|
1992
2033
|
surface: "copy",
|
|
1993
2034
|
why: "'..., ensuring peace of mind', '..., highlighting our commitment': a participle tacked on the end that claims significance and names no mechanism. One of the most common shapes in the Wikipedia guide to AI writing.",
|
|
1994
2035
|
fix: "Cut the tail, or replace it with the mechanism: 'so you can drop the car off before work'.",
|
|
1995
|
-
detect: phrases([ING_TAIL], (m) => quoted(m.replace(/^,\s*/, ""))),
|
|
2036
|
+
detect: phrases([ING_TAIL], (m) => quoted(m.replace(/^,\s*/, "")), void 0, gerundList),
|
|
1996
2037
|
fixtures: {
|
|
1997
2038
|
flag: [
|
|
1998
2039
|
f("content/home.md", "Every engineer is Gas Safe registered, ensuring complete peace of mind."),
|
|
@@ -2000,7 +2041,8 @@ var RESEARCH_TELLS = [
|
|
|
2000
2041
|
],
|
|
2001
2042
|
pass: [
|
|
2002
2043
|
f("content/home.md", "We open at 7am, so you can drop the car off before work."),
|
|
2003
|
-
f("content/home.md", "Engineers arrive by 9, carrying the parts for most boilers.")
|
|
2044
|
+
f("content/home.md", "Engineers arrive by 9, carrying the parts for most boilers."),
|
|
2045
|
+
f("content/guide.md", "Many items (claiming your GBP, ensuring NAP consistency, writing title tags) are straightforward.")
|
|
2004
2046
|
]
|
|
2005
2047
|
}
|
|
2006
2048
|
},
|
|
@@ -2250,6 +2292,7 @@ function repeatHits(ctx) {
|
|
|
2250
2292
|
for (const s of sentences(doc2)) {
|
|
2251
2293
|
const words2 = normal(s.text);
|
|
2252
2294
|
if (words2.length < RUN || !/[.!?]["'’”)]*$/.test(s.text)) continue;
|
|
2295
|
+
if (/^\s*\[\^[\w-]+\]:/.test(s.text)) continue;
|
|
2253
2296
|
const key = words2.join(" ");
|
|
2254
2297
|
const earlier = whole.get(key);
|
|
2255
2298
|
if (earlier) {
|
|
@@ -2447,7 +2490,15 @@ var DENSITY_TELLS = [
|
|
|
2447
2490
|
f2("content/proposal.md", doc("We check every valve before the engineer leaves the house.\n\nMore here.\n\nWe check every valve before the engineer leaves the house.")),
|
|
2448
2491
|
f2("content/proposal.md", doc("We check every valve before the engineer leaves the house.\n\nBy then we check every valve before the engineer leaves the site."))
|
|
2449
2492
|
],
|
|
2450
|
-
pass: [
|
|
2493
|
+
pass: [
|
|
2494
|
+
f2("content/proposal.md", doc("We check every valve before the engineer leaves the house.\n\nThe engineer leaves once the customer has signed the sheet.")),
|
|
2495
|
+
f2(
|
|
2496
|
+
"content/guide.md",
|
|
2497
|
+
doc(
|
|
2498
|
+
"The Independent Healthcare Providers Network Private Patient Insight Report 2024 found that private demand rose.[^1]\n\n[^1]: Independent Healthcare Providers Network, Private Patient Insight Report 2024, published online."
|
|
2499
|
+
)
|
|
2500
|
+
)
|
|
2501
|
+
]
|
|
2451
2502
|
}
|
|
2452
2503
|
},
|
|
2453
2504
|
{
|
|
@@ -3469,6 +3520,9 @@ var HOUSE = {
|
|
|
3469
3520
|
"plain-english": { tier: "block", label: 'use "properly explained"' },
|
|
3470
3521
|
buzzword: { tier: "block", label: "forbidden phrase" },
|
|
3471
3522
|
"negative-reassurance": { tier: "block", label: "negative reassurance, say what they get" },
|
|
3523
|
+
// Evidence of a pasted chat reply, not a matter of style. Moved from review
|
|
3524
|
+
// on 2026-09-24 after its estate hits were read (ROADMAP.md): 15 of 15 real.
|
|
3525
|
+
"chatbot-residue": { tier: "block", label: "chatbot residue. A chat reply was pasted in: remove it, and source or cut the claim it backed" },
|
|
3472
3526
|
"review-phrase": { tier: "review", label: "review tier" },
|
|
3473
3527
|
"ai-vocabulary": { tier: "review", label: "AI vocabulary" },
|
|
3474
3528
|
"stock-phrase": { tier: "review", label: "stock phrase" },
|
|
@@ -3480,10 +3534,10 @@ var HOUSE = {
|
|
|
3480
3534
|
"vague-attribution": { tier: "review", label: "unnamed source" },
|
|
3481
3535
|
"closing-summary": { tier: "review", label: "closing summary" },
|
|
3482
3536
|
"false-range": { tier: "review", label: "false range" },
|
|
3483
|
-
// The first candidate for block: evidence of a pasted chat reply, not a
|
|
3484
|
-
// matter of style. Blocks once its estate hits have been read (ROADMAP.md).
|
|
3485
|
-
"chatbot-residue": { tier: "review", label: "chatbot residue" },
|
|
3486
3537
|
"placeholder": { tier: "review", label: "unfilled placeholder" },
|
|
3538
|
+
// Every estate hit so far is real (38 in five DD articles, 2026-09-24), but
|
|
3539
|
+
// a proposal can honestly say "the attached research". Review until measured there.
|
|
3540
|
+
"prompt-context": { tier: "review", label: "prompt context: the model naming its own inputs" },
|
|
3487
3541
|
"question-reveal": { tier: "review", label: "staged reveal" },
|
|
3488
3542
|
"inline-label-list": { tier: "review", label: "bold-label bullets" },
|
|
3489
3543
|
"phrase-density": { tier: "review", label: "density" },
|
|
@@ -4166,7 +4220,7 @@ var RENDERED_PATHS = {
|
|
|
4166
4220
|
};
|
|
4167
4221
|
|
|
4168
4222
|
// src/character/check.ts
|
|
4169
|
-
var CATALOGUE_VERSION = "2026.09.
|
|
4223
|
+
var CATALOGUE_VERSION = "2026.09.8";
|
|
4170
4224
|
var CATALOGUE = [...SOURCE_TELLS, ...COPY_TELLS, ...RESEARCH_TELLS, ...DENSITY_TELLS].map(
|
|
4171
4225
|
(t) => RENDERED_PATHS[t.id] ? { ...t, rendered: RENDERED_PATHS[t.id] } : t
|
|
4172
4226
|
);
|
|
@@ -4431,6 +4485,120 @@ function formatComparison(result, beforePath, afterPath) {
|
|
|
4431
4485
|
return lines.join("\n");
|
|
4432
4486
|
}
|
|
4433
4487
|
|
|
4488
|
+
// src/character/claims.ts
|
|
4489
|
+
var CLAIM_KINDS = /* @__PURE__ */ new Set(["money", "number", "date"]);
|
|
4490
|
+
var PROPER = "[A-Z][\\w&.\u2019'-]*(?:\\s+(?:of|and|&|the|for)?\\s*[A-Z][\\w&.\u2019'-]*)*";
|
|
4491
|
+
var NOT_A_SOURCE = /^(?:We|I|It|This|That|These|Those|They|He|She|You|Our|Your|The|A|An|Each|Every|Most|Many|Some|One|Research|Studies|Experts|Data|Analysis|If|When|So|But|And|Or|In|On|At|From|For|By|As|Since|After|Before|Across|Over|Under|Its|Their|External|Source|UK|US|EU|Sometimes|Public|Recent|Industry|Independent|What|How|Why|Who|Where|Businesses|Customers|Clients|Patients|Consumers|People|Users|Online)$/;
|
|
4492
|
+
var NOUN = "study|survey|report|research|analysis|benchmark|benchmarks|guidelines|docs|documentation|help centre|help center|pricing page|figures|statistics|case study";
|
|
4493
|
+
var OWNED_NOUN = "guide|audit|support|pricing|data|site|website|blog";
|
|
4494
|
+
var VERB = "found|finds|reports?|reported|says|said|states?|stated|estimates?|estimated|recommends?|publishes|published|advertises|advertised|shows|showed|claims";
|
|
4495
|
+
var ATTRIBUTION = [
|
|
4496
|
+
new RegExp(`\\b[Aa]ccording to\\s+(?:the\\s+)?(${PROPER})`, "g"),
|
|
4497
|
+
new RegExp(`\\b(?:${NOUN})\\s+(?:by|from|commissioned by)\\s+(?:the\\s+)?(${PROPER})`, "g"),
|
|
4498
|
+
// "Akamai’s 2017 retail study", "the 2020 Deloitte study", "Bark’s UK guide", "Wix’s own developer guidelines".
|
|
4499
|
+
new RegExp(`\\b(${PROPER})(?:(?:\u2019s|'s)\\s+(?:own\\s+)?(?:\\d{4}\\s+)?(?:[A-Za-z]+\\s+)?(?:${NOUN}|${OWNED_NOUN})|\\s+(?:\\d{4}\\s+)?(?:[a-z]+\\s+)?(?:${NOUN}))\\b`, "g"),
|
|
4500
|
+
// "Google also reported", "Shopify UK currently advertises".
|
|
4501
|
+
new RegExp(`\\b(${PROPER})\\s+(?:also\\s+|now\\s+|currently\\s+|still\\s+)?(?:${VERB})\\b`, "g"),
|
|
4502
|
+
/\((?:[Ss]ources?:\s*|via\s+(?=[A-Z]))([^)]+)\)/g,
|
|
4503
|
+
/^\s*(?:source|sources)\s*:\s*(.+)$/gim
|
|
4504
|
+
];
|
|
4505
|
+
var LINK2 = /\[([^\]]+)\]\((https?:\/\/[^)\s]+)\)|\bhttps?:\/\/[^\s<>"')\]]+/g;
|
|
4506
|
+
function cleanName(raw) {
|
|
4507
|
+
const words2 = raw.trim().replace(/[.,;:]+$/, "").split(/\s+/);
|
|
4508
|
+
while (words2.length && NOT_A_SOURCE.test(words2[0])) words2.shift();
|
|
4509
|
+
return words2.join(" ").replace(/(?:’s|'s)(?=\s|$).*$/, "").trim();
|
|
4510
|
+
}
|
|
4511
|
+
var IMPERATIVE = /^(?:Add|Use|Check|Make|Get|Set|Include|Create|Update|List|Keep|Ask|Write|Track|Test|Claim|Build|Link|Submit|Verify|Review|Fix|Remove|Upload|Respond|Reply|Choose|Pick|Start|Run|Open|Install|Connect|Enable|Turn|Put|Give|Send|Book|Call|Read|See|Try|Compare|Aim|Target|Avoid|Post|Share|Mark|Tag)$/;
|
|
4512
|
+
var atStart = (sentence, index) => /^[\s*_"“”’(>#|-]*(?:\d{1,3}[.)]\s*)?[\s*_]*(?:[\w ]+:\**\s*)?$/.test(sentence.slice(0, index)) || /\|\s*\**$/.test(sentence.slice(0, index));
|
|
4513
|
+
function namedSources(sentence) {
|
|
4514
|
+
const found = /* @__PURE__ */ new Set();
|
|
4515
|
+
ATTRIBUTION.forEach((pattern, i) => {
|
|
4516
|
+
for (const m of sentence.matchAll(pattern)) {
|
|
4517
|
+
const raw = m[1].trim();
|
|
4518
|
+
const word = !/\s/.test(raw) && !/[’']s$/.test(m[0].split(/\s+/)[0]);
|
|
4519
|
+
if (i === 2 && word && atStart(sentence, m.index ?? 0)) continue;
|
|
4520
|
+
if (IMPERATIVE.test(raw.split(/\s+/)[0]) && atStart(sentence, m.index ?? 0)) continue;
|
|
4521
|
+
const name = cleanName(raw);
|
|
4522
|
+
if (!name || name.length < 2 || !/\s/.test(name) && /[a-z]ing$/.test(name)) continue;
|
|
4523
|
+
found.add(name);
|
|
4524
|
+
}
|
|
4525
|
+
});
|
|
4526
|
+
for (const m of sentence.matchAll(LINK2)) found.add(m[2] ?? m[0]);
|
|
4527
|
+
return [...found];
|
|
4528
|
+
}
|
|
4529
|
+
var FOOTNOTE_REF = /\[\^([\w-]+)\](?!:)/g;
|
|
4530
|
+
var FOOTNOTE_DEF = /^[ \t]*\[\^([\w-]+)\]:[ \t]*(.+)$/gm;
|
|
4531
|
+
var ASSUMPTION = /^(?:\*\*)?(?:assume|suppose|imagine|say|let's say|for example, say|picture)\b/i;
|
|
4532
|
+
var LEAD = /^\s*(?:#{1,6}\s+|>\s*|[-*+]\s+|\d+[.)]\s+)*/;
|
|
4533
|
+
function sentences2(text) {
|
|
4534
|
+
const out = [];
|
|
4535
|
+
let start = 0;
|
|
4536
|
+
let paragraph = 0;
|
|
4537
|
+
const push = (end) => {
|
|
4538
|
+
const raw = text.slice(start, end);
|
|
4539
|
+
const lead = raw.length - raw.trimStart().length;
|
|
4540
|
+
if (raw.trim()) out.push({ text: raw.trim(), offset: start + lead, paragraph });
|
|
4541
|
+
};
|
|
4542
|
+
for (const m of text.matchAll(/(?<!(?:^|\n)[ \t]*\d{1,3})[.!?](?:\[\^[\w-]+\])*(?=["”’)]?[ \t]+["“(*]*[A-Z0-9£$€])|\n+/g)) {
|
|
4543
|
+
const end = (m.index ?? 0) + m[0].length;
|
|
4544
|
+
push(end);
|
|
4545
|
+
start = end;
|
|
4546
|
+
if (/\n\s*\n/.test(m[0]) || /^\s*(?:#|\|)/.test(text.slice(end))) paragraph += 1;
|
|
4547
|
+
}
|
|
4548
|
+
push(text.length);
|
|
4549
|
+
return out;
|
|
4550
|
+
}
|
|
4551
|
+
var MAX = 180;
|
|
4552
|
+
var trim = (s) => s.length > MAX ? `${s.slice(0, MAX - 1).trimEnd()}\u2026` : s;
|
|
4553
|
+
function findClaims(files) {
|
|
4554
|
+
const claims = [];
|
|
4555
|
+
for (const file of files) {
|
|
4556
|
+
for (const block of extractCopy(file)) {
|
|
4557
|
+
const notes = new Map([...block.text.matchAll(FOOTNOTE_DEF)].map((m) => [m[1], m[2].trim()]));
|
|
4558
|
+
let carried;
|
|
4559
|
+
for (const s of sentences2(block.text)) {
|
|
4560
|
+
if (/^\s*#{1,6}\s/.test(s.text) || /^\s*\[\^[\w-]+\]:/.test(s.text)) continue;
|
|
4561
|
+
const refs = [...s.text.matchAll(FOOTNOTE_REF)].map((m) => notes.get(m[1]) ?? `footnote ${m[1]}`);
|
|
4562
|
+
const text = s.text.replace(FOOTNOTE_REF, "");
|
|
4563
|
+
const body = text.replace(LEAD, "");
|
|
4564
|
+
const own = [...namedSources(text), ...refs];
|
|
4565
|
+
if (own.length) carried = { paragraph: s.paragraph, sources: own };
|
|
4566
|
+
if (ASSUMPTION.test(body)) continue;
|
|
4567
|
+
const facts = protectedFacts(body).filter((f4) => CLAIM_KINDS.has(f4.kind));
|
|
4568
|
+
if (facts.length === 0 && own.length === 0) continue;
|
|
4569
|
+
const nearby = own.length === 0 && carried?.paragraph === s.paragraph ? carried.sources : void 0;
|
|
4570
|
+
const at = block.offset + s.offset;
|
|
4571
|
+
claims.push({
|
|
4572
|
+
path: file.path,
|
|
4573
|
+
line: file.text.slice(0, at).split("\n").length,
|
|
4574
|
+
sentence: trim(text.replace(/\s+/g, " ")),
|
|
4575
|
+
facts,
|
|
4576
|
+
sources: own,
|
|
4577
|
+
...nearby ? { nearby } : {}
|
|
4578
|
+
});
|
|
4579
|
+
}
|
|
4580
|
+
}
|
|
4581
|
+
}
|
|
4582
|
+
return { files: files.length, claims, unsourced: claims.filter((c) => c.sources.length === 0).length };
|
|
4583
|
+
}
|
|
4584
|
+
function formatClaims(report2, label = "craft copy claims") {
|
|
4585
|
+
const { claims, unsourced } = report2;
|
|
4586
|
+
if (claims.length === 0) return `${label}: no prices, figures, dates or named sources in ${report2.files} file${report2.files === 1 ? "" : "s"}.`;
|
|
4587
|
+
const lines = [
|
|
4588
|
+
`${label}: ${claims.length} claim${claims.length === 1 ? "" : "s"} to check, ${unsourced} with no source named.`,
|
|
4589
|
+
" Check each against its primary source before it ships. Sourced means a source is named, not that it agrees."
|
|
4590
|
+
];
|
|
4591
|
+
for (const c of claims) {
|
|
4592
|
+
const what = [
|
|
4593
|
+
...c.facts.map((f4) => f4.value),
|
|
4594
|
+
...c.sources.map((s) => `source: ${s}`),
|
|
4595
|
+
...c.nearby ? [`${c.nearby.join(", ")} named earlier in the paragraph: does it cover this?`] : []
|
|
4596
|
+
].join(" \xB7 ");
|
|
4597
|
+
lines.push("", ` ${c.path}:${c.line} ${c.sources.length ? "sourced " : "UNSOURCED"} ${what}`, ` ${c.sentence}`);
|
|
4598
|
+
}
|
|
4599
|
+
return lines.join("\n");
|
|
4600
|
+
}
|
|
4601
|
+
|
|
4434
4602
|
// src/fingerprint/index.ts
|
|
4435
4603
|
var FINGERPRINT_VERSION = 1;
|
|
4436
4604
|
var CHROMATIC = 0.03;
|
|
@@ -5393,6 +5561,7 @@ export {
|
|
|
5393
5561
|
estatePairs,
|
|
5394
5562
|
exitDuration,
|
|
5395
5563
|
extractHtml,
|
|
5564
|
+
findClaims,
|
|
5396
5565
|
findColours,
|
|
5397
5566
|
fingerprint,
|
|
5398
5567
|
fingerprintDistance,
|
|
@@ -5400,6 +5569,7 @@ export {
|
|
|
5400
5569
|
fluidSpace,
|
|
5401
5570
|
fluidType,
|
|
5402
5571
|
fontClass,
|
|
5572
|
+
formatClaims,
|
|
5403
5573
|
formatComparison,
|
|
5404
5574
|
formatHex,
|
|
5405
5575
|
formatReport,
|
|
@@ -5416,6 +5586,7 @@ export {
|
|
|
5416
5586
|
isCream,
|
|
5417
5587
|
makeSnapshot,
|
|
5418
5588
|
motionTokens,
|
|
5589
|
+
namedSources,
|
|
5419
5590
|
nearest,
|
|
5420
5591
|
normaliseFamily,
|
|
5421
5592
|
nullPrompt,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@domandigital/craft",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "The Doman Digital house craft standard as numbers: OKLCh colour ramps that snap to existing anchors, derived semantic tokens with measured WCAG and APCA contrast, one shared motion vocabulary for CSS and JS, and a base stylesheet. Zero dependencies.",
|
|
6
6
|
"license": "Apache-2.0",
|
package/word-lists.json
CHANGED
package/dist/cli-D6VYGCOE.js
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
formatMatches,
|
|
3
|
-
formatPairs,
|
|
4
|
-
loadEstate,
|
|
5
|
-
runEstate
|
|
6
|
-
} from "./chunk-2VLN6JLN.js";
|
|
7
|
-
import "./chunk-XEH4GK5K.js";
|
|
8
|
-
import "./chunk-X6PHP22P.js";
|
|
9
|
-
import "./chunk-DWZY5MWD.js";
|
|
10
|
-
import "./chunk-OBVWDPQQ.js";
|
|
11
|
-
export {
|
|
12
|
-
formatMatches,
|
|
13
|
-
formatPairs,
|
|
14
|
-
loadEstate,
|
|
15
|
-
runEstate
|
|
16
|
-
};
|
package/dist/cli-I4TQMCUP.js
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
describeFingerprint,
|
|
3
|
-
merge,
|
|
4
|
-
runAudit
|
|
5
|
-
} from "./chunk-6LPMM752.js";
|
|
6
|
-
import "./chunk-UVBVYM5D.js";
|
|
7
|
-
import "./chunk-XEH4GK5K.js";
|
|
8
|
-
import "./chunk-X6PHP22P.js";
|
|
9
|
-
import "./chunk-DWZY5MWD.js";
|
|
10
|
-
import "./chunk-OBVWDPQQ.js";
|
|
11
|
-
export {
|
|
12
|
-
describeFingerprint,
|
|
13
|
-
merge,
|
|
14
|
-
runAudit
|
|
15
|
-
};
|