@warpgogol/werkstatt-shared 0.14.0 → 0.14.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.
Files changed (117) hide show
  1. package/README.md +26 -16
  2. package/README.uk.md +24 -14
  3. package/package.json +7 -1
  4. package/src/checks/result-helpers.ts +21 -4
  5. package/src/checks/tests/remediation-auto-populate.test.ts +83 -0
  6. package/src/integration/vitest.config.ts +7 -0
  7. package/src/ontology/manifest.ts +19 -0
  8. package/src/ontology/schemas/system/tests/growth.test.ts +22 -0
  9. package/src/ontology/schemas/system/tests/integrations.test.ts +12 -0
  10. package/src/ontology/schemas/system/tests/manifest.test.ts +22 -0
  11. package/src/ontology/schemas/system/tests/page-output.test.ts +13 -0
  12. package/src/ontology/schemas/system/tests/release.test.ts +22 -0
  13. package/src/ontology/schemas/system/tests/text.test.ts +18 -0
  14. package/src/ontology/schemas/system/tests/verification.test.ts +12 -0
  15. package/src/ontology/schemas/tests/biome.test.ts +19 -0
  16. package/src/ontology/schemas/tests/capability.test.ts +22 -0
  17. package/src/ontology/schemas/tests/constellation.test.ts +22 -0
  18. package/src/ontology/schemas/tests/dns-records.test.ts +13 -0
  19. package/src/ontology/schemas/tests/manifest-resolver.test.ts +13 -0
  20. package/src/ontology/schemas/tests/page-entry.test.ts +22 -0
  21. package/src/ontology/schemas/tests/section-archetype.test.ts +12 -0
  22. package/src/ontology/schemas/tests/site-family.test.ts +18 -0
  23. package/src/ontology/schemas/tests/system.test.ts +20 -0
  24. package/src/share/agent/tests/api-catalog.test.ts +8 -0
  25. package/src/share/agent/tests/ard-catalog.test.ts +8 -0
  26. package/src/share/agent/tests/capability.test.ts +14 -0
  27. package/src/share/agent/tests/fleet-catalog.test.ts +15 -0
  28. package/src/share/agent/tests/knowledge.test.ts +20 -0
  29. package/src/share/agent/tests/manifest.test.ts +9 -0
  30. package/src/share/agent/tests/mcp-card.test.ts +8 -0
  31. package/src/share/agent/tests/omit-empty.test.ts +33 -0
  32. package/src/share/agent/tests/openapi.test.ts +8 -0
  33. package/src/share/agent/tests/proof.test.ts +15 -0
  34. package/src/share/knowledge/tests/claim.test.ts +11 -0
  35. package/src/share/knowledge/tests/derivation.test.ts +30 -0
  36. package/src/share/knowledge/tests/freshness.test.ts +18 -0
  37. package/src/share/knowledge/tests/ledger.test.ts +35 -0
  38. package/src/share/knowledge/tests/plan.test.ts +19 -0
  39. package/src/share/knowledge/tests/source.test.ts +19 -0
  40. package/src/share/remediation/remediation-catalog.ts +196 -0
  41. package/src/share/schemas/tests/claim-records.test.ts +52 -0
  42. package/src/share/schemas/tests/claims.test.ts +23 -0
  43. package/src/share/schemas/tests/effects.test.ts +116 -0
  44. package/src/share/schemas/tests/features.test.ts +61 -0
  45. package/src/share/schemas/tests/horizontal-align.test.ts +16 -0
  46. package/src/share/schemas/tests/icon-color.test.ts +16 -0
  47. package/src/share/schemas/tests/live-photo.test.ts +40 -0
  48. package/src/share/schemas/tests/material-credit.test.ts +51 -0
  49. package/src/share/schemas/tests/media.test.ts +26 -0
  50. package/src/share/schemas/tests/navigation.test.ts +58 -0
  51. package/src/share/schemas/tests/page-base.test.ts +19 -0
  52. package/src/share/schemas/tests/participant.test.ts +26 -0
  53. package/src/share/schemas/tests/person.test.ts +12 -0
  54. package/src/share/schemas/tests/print.test.ts +30 -0
  55. package/src/share/schemas/tests/section-background.test.ts +31 -0
  56. package/src/share/schemas/tests/section-body.test.ts +16 -0
  57. package/src/share/schemas/tests/section-cards.test.ts +17 -0
  58. package/src/share/schemas/tests/section-cta.test.ts +41 -0
  59. package/src/share/schemas/tests/section-header.test.ts +31 -0
  60. package/src/share/schemas/tests/section-image.test.ts +24 -0
  61. package/src/share/schemas/tests/section-motion.test.ts +29 -0
  62. package/src/share/schemas/tests/section-shell.test.ts +42 -0
  63. package/src/share/schemas/tests/section-stats.test.ts +17 -0
  64. package/src/share/schemas/tests/site-background.test.ts +33 -0
  65. package/src/share/schemas/tests/standard-list-item.test.ts +16 -0
  66. package/src/share/scripts/tests/external-link-qr.test.ts +8 -0
  67. package/src/share/scripts/tests/external-links.test.ts +25 -0
  68. package/src/share/scripts/tests/gsap-counter.test.ts +8 -0
  69. package/src/share/scripts/tests/gsap-mountain-journey.test.ts +8 -0
  70. package/src/share/scripts/tests/gsap-parallax.test.ts +8 -0
  71. package/src/share/scripts/tests/gsap-reveal.test.ts +8 -0
  72. package/src/share/scripts/tests/gsap-stagger.test.ts +8 -0
  73. package/src/share/scripts/tests/inline-number-animation.test.ts +8 -0
  74. package/src/share/scripts/tests/lenis.test.ts +8 -0
  75. package/src/share/scripts/tests/live-photos.test.ts +8 -0
  76. package/src/share/scripts/tests/lordicon.test.ts +8 -0
  77. package/src/share/scripts/tests/orchestrator.test.ts +8 -0
  78. package/src/share/scripts/tests/scheduler.test.ts +23 -0
  79. package/src/share/semantic/jsonld/tests/article.test.ts +89 -0
  80. package/src/share/semantic/jsonld/tests/breadcrumb.test.ts +62 -0
  81. package/src/share/semantic/jsonld/tests/collection-list.test.ts +39 -0
  82. package/src/share/semantic/jsonld/tests/faq.test.ts +60 -0
  83. package/src/share/semantic/jsonld/tests/initiative.test.ts +56 -0
  84. package/src/share/semantic/jsonld/tests/organization.test.ts +123 -0
  85. package/src/share/semantic/jsonld/tests/person.test.ts +89 -0
  86. package/src/share/semantic/jsonld/tests/service.test.ts +65 -0
  87. package/src/share/semantic/jsonld/tests/shared.test.ts +41 -0
  88. package/src/share/semantic/jsonld/tests/video.test.ts +84 -0
  89. package/src/share/semantic/jsonld/tests/webpage.test.ts +93 -0
  90. package/src/share/semantic/jsonld/tests/website.test.ts +27 -0
  91. package/src/share/semantic/jsonld/types.ts +1 -0
  92. package/src/share/semantic/models.ts +1 -0
  93. package/src/share/semantic/tests/ai.test.ts +55 -0
  94. package/src/share/semantic/tests/block-extraction.test.ts +34 -0
  95. package/src/share/semantic/tests/breadcrumbs.test.ts +127 -0
  96. package/src/share/semantic/tests/build-page.test.ts +122 -0
  97. package/src/share/semantic/tests/business-projection.test.ts +188 -0
  98. package/src/share/semantic/tests/derived-prices-loader.test.ts +46 -0
  99. package/src/share/semantic/tests/extract.test.ts +126 -0
  100. package/src/share/semantic/tests/feed.test.ts +113 -0
  101. package/src/share/semantic/tests/helpers.ts +32 -0
  102. package/src/share/semantic/tests/ids.test.ts +118 -0
  103. package/src/share/semantic/tests/image-sitemap.test.ts +109 -0
  104. package/src/share/semantic/tests/jsonld.test.ts +61 -0
  105. package/src/share/semantic/tests/llms-policy.test.ts +60 -0
  106. package/src/share/semantic/tests/llms.test.ts +118 -0
  107. package/src/share/semantic/tests/markdown-hygiene.test.ts +72 -0
  108. package/src/share/semantic/tests/markdown-page.test.ts +97 -0
  109. package/src/share/semantic/tests/markdown-twin-provenance.test.ts +152 -0
  110. package/src/share/semantic/tests/organization-profile.test.ts +107 -0
  111. package/src/share/semantic/tests/output-projection.test.ts +77 -0
  112. package/src/share/semantic/tests/page-markdown.test.ts +98 -0
  113. package/src/share/semantic/tests/page-utils.test.ts +88 -0
  114. package/src/share/semantic/tests/price-marker-resolver.test.ts +88 -0
  115. package/src/share/semantic/tests/robots.test.ts +70 -0
  116. package/src/share/semantic/tests/update-stamp.test.ts +102 -0
  117. package/src/share/utility-registry.yaml +14 -0
@@ -0,0 +1,88 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import {
3
+ AMOUNT_MARKER_RE,
4
+ OFFERING_URI_PREFIX,
5
+ PRICE_MARKER_RE,
6
+ formatSourcePrice,
7
+ resolveAmountMarkersForSemantic,
8
+ resolvePriceMarkersForSemantic,
9
+ type DerivedPriceEntry,
10
+ } from "../price-marker-resolver.ts";
11
+
12
+ describe("constants", () => {
13
+ it("OFFERING_URI_PREFIX is warpgogol offerings URL", () => {
14
+ expect(OFFERING_URI_PREFIX).toBe("https://warpgogol.com/id/offerings/");
15
+ });
16
+
17
+ it("PRICE_MARKER_RE matches price markers", () => {
18
+ const text = "{price:hosting:monthly}";
19
+ PRICE_MARKER_RE.lastIndex = 0;
20
+ const match = PRICE_MARKER_RE.exec(text);
21
+ expect(match).not.toBeNull();
22
+ expect(match![1]).toBe("hosting");
23
+ expect(match![2]).toBe("monthly");
24
+ });
25
+ });
26
+
27
+ describe("formatSourcePrice", () => {
28
+ it("formats EUR amount with narrowSymbol for de", () => {
29
+ const result = formatSourcePrice("10", "de");
30
+ expect(result).toContain("10");
31
+ expect(result).toContain("€");
32
+ });
33
+
34
+ it("returns 0 € for non-numeric amount", () => {
35
+ expect(formatSourcePrice("not-a-number", "de")).toBe("0\u00A0€");
36
+ });
37
+ });
38
+
39
+ describe("resolvePriceMarkersForSemantic", () => {
40
+ const derivedPrices: Record<string, DerivedPriceEntry[]> = {
41
+ [`${OFFERING_URI_PREFIX}hosting`]: [
42
+ {
43
+ chargeRef: "monthly",
44
+ targetCurrency: "USD",
45
+ amount: { value: "15", currency: "USD" },
46
+ trace: { source: { amount: "10", currency: "EUR" }, rate: { value: "0.9", pair: "EUR/USD" } },
47
+ },
48
+ ],
49
+ };
50
+
51
+ it("resolves price marker to source currency", () => {
52
+ const result = resolvePriceMarkersForSemantic(
53
+ "Price: {price:hosting:monthly}",
54
+ "de",
55
+ derivedPrices,
56
+ );
57
+ expect(result).toContain("10");
58
+ expect(result).toContain("€");
59
+ expect(result).not.toContain("{price:");
60
+ });
61
+
62
+ it("returns 0 € for unknown offering", () => {
63
+ const result = resolvePriceMarkersForSemantic(
64
+ "Price: {price:unknown:charge}",
65
+ "de",
66
+ derivedPrices,
67
+ );
68
+ expect(result).toContain("0");
69
+ expect(result).toContain("€");
70
+ });
71
+
72
+ it("returns text unchanged when no markers", () => {
73
+ expect(resolvePriceMarkersForSemantic("No markers", "de", derivedPrices)).toBe("No markers");
74
+ });
75
+ });
76
+
77
+ describe("resolveAmountMarkersForSemantic", () => {
78
+ it("resolves amount marker to EUR", () => {
79
+ const result = resolveAmountMarkersForSemantic("Threshold: {amount:50}", "de");
80
+ expect(result).toContain("50");
81
+ expect(result).toContain("€");
82
+ expect(result).not.toContain("{amount:");
83
+ });
84
+
85
+ it("returns text unchanged when no markers", () => {
86
+ expect(resolveAmountMarkersForSemantic("No markers", "de")).toBe("No markers");
87
+ });
88
+ });
@@ -0,0 +1,70 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { buildRobotsTxt } from "../robots.ts";
3
+
4
+ describe("buildRobotsTxt", () => {
5
+ it("generates default allow policy", () => {
6
+ const result = buildRobotsTxt({});
7
+ expect(result).toContain("User-agent: *");
8
+ expect(result).not.toContain("Disallow: /");
9
+ expect(result).toContain("Allow: /robots.txt");
10
+ expect(result).toContain("Sitemap: /sitemap.xml");
11
+ });
12
+
13
+ it("generates disallow-all for defaultPolicy: disallow", () => {
14
+ const result = buildRobotsTxt({ defaultPolicy: "disallow" });
15
+ expect(result).toContain("Disallow: /");
16
+ });
17
+
18
+ it("generates specific disallow paths", () => {
19
+ const result = buildRobotsTxt({ disallowedPaths: ["/admin", "/private"] });
20
+ expect(result).toContain("Disallow: /admin");
21
+ expect(result).toContain("Disallow: /private");
22
+ expect(result).not.toContain("Disallow: /\n");
23
+ });
24
+
25
+ it("generates allow paths in allow mode", () => {
26
+ const result = buildRobotsTxt({ allowedPaths: ["/public"] });
27
+ expect(result).toContain("Allow: /public");
28
+ });
29
+
30
+ it("emits Content-Signal directive when provided", () => {
31
+ const result = buildRobotsTxt({ contentSignal: ["ai-train", "search"] });
32
+ expect(result).toContain("Content-Signal: ai-train, search");
33
+ });
34
+
35
+ it("emits crawler blocklist", () => {
36
+ const result = buildRobotsTxt({ crawlerBlocklist: ["BadBot", "ScraperBot"] });
37
+ expect(result).toContain("User-agent: BadBot");
38
+ expect(result).toContain("User-agent: ScraperBot");
39
+ expect(result).toContain("Disallow: /");
40
+ });
41
+
42
+ it("emits crawler allowlist", () => {
43
+ const result = buildRobotsTxt({ crawlerAllowlist: ["FriendlyBot"] });
44
+ expect(result).toContain("User-agent: FriendlyBot");
45
+ expect(result).toContain("Allow: /");
46
+ });
47
+
48
+ it("emits custom per-user-agent rules", () => {
49
+ const result = buildRobotsTxt({
50
+ customRules: [{ userAgent: "GoogleBot", disallow: ["/no-google"], allow: ["/yes-google"] }],
51
+ });
52
+ expect(result).toContain("User-agent: GoogleBot");
53
+ expect(result).toContain("Disallow: /no-google");
54
+ expect(result).toContain("Allow: /yes-google");
55
+ });
56
+
57
+ it("uses custom sitemap URL", () => {
58
+ const result = buildRobotsTxt({ sitemap: "https://example.com/sitemap.xml" });
59
+ expect(result).toContain("Sitemap: https://example.com/sitemap.xml");
60
+ });
61
+
62
+ it("always allows auxiliary discovery files", () => {
63
+ const result = buildRobotsTxt({ defaultPolicy: "disallow" });
64
+ expect(result).toContain("Allow: /robots.txt");
65
+ expect(result).toContain("Allow: /llms.txt");
66
+ expect(result).toContain("Allow: /llms-full.txt");
67
+ expect(result).toContain("Allow: /sitemap.xml");
68
+ expect(result).toContain("Allow: /sitemap-content.xml");
69
+ });
70
+ });
@@ -0,0 +1,102 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import {
3
+ isValidStampDate,
4
+ resolveAuthoredStamp,
5
+ resolveCklStamp,
6
+ resolvePageUpdateStamp,
7
+ } from "../update-stamp.ts";
8
+
9
+ describe("isValidStampDate", () => {
10
+ it("accepts YYYY-MM-DD", () => {
11
+ expect(isValidStampDate("2024-01-15")).toBe(true);
12
+ });
13
+
14
+ it("rejects non-string", () => {
15
+ expect(isValidStampDate(20240115)).toBe(false);
16
+ });
17
+
18
+ it("rejects wrong format", () => {
19
+ expect(isValidStampDate("2024/01/15")).toBe(false);
20
+ expect(isValidStampDate("January 15, 2024")).toBe(false);
21
+ });
22
+ });
23
+
24
+ describe("resolveAuthoredStamp", () => {
25
+ it("uses output.sitemap.lastmod first", () => {
26
+ const result = resolveAuthoredStamp({
27
+ output: { sitemap: { lastmod: "2024-06-01" } },
28
+ });
29
+ expect(result?.date).toBe("2024-06-01");
30
+ expect(result?.source).toBe("authored-system-output");
31
+ });
32
+
33
+ it("uses article.updatedAt when no sitemap lastmod", () => {
34
+ const result = resolveAuthoredStamp({
35
+ article: { updatedAt: "2024-05-01" },
36
+ });
37
+ expect(result?.date).toBe("2024-05-01");
38
+ expect(result?.source).toBe("authored-page-frontmatter");
39
+ });
40
+
41
+ it("uses article.publishedAt when no updatedAt", () => {
42
+ const result = resolveAuthoredStamp({
43
+ article: { publishedAt: "2024-01-01" },
44
+ });
45
+ expect(result?.date).toBe("2024-01-01");
46
+ });
47
+
48
+ it("returns undefined when no valid dates", () => {
49
+ expect(resolveAuthoredStamp({})).toBeUndefined();
50
+ });
51
+
52
+ it("ignores invalid date formats", () => {
53
+ expect(resolveAuthoredStamp({ article: { updatedAt: "not-a-date" } })).toBeUndefined();
54
+ });
55
+ });
56
+
57
+ describe("resolveCklStamp", () => {
58
+ it("returns latest valid date", () => {
59
+ const result = resolveCklStamp(["2024-01-01", "2024-06-15", "2024-03-01"], ["ledger"]);
60
+ expect(result?.date).toBe("2024-06-15");
61
+ expect(result?.source).toBe("ckl-claim-ledger");
62
+ });
63
+
64
+ it("returns undefined when no valid dates", () => {
65
+ expect(resolveCklStamp(["invalid"], ["ledger"])).toBeUndefined();
66
+ });
67
+ });
68
+
69
+ describe("resolvePageUpdateStamp", () => {
70
+ it("prefers authored stamp over CKL", () => {
71
+ const result = resolvePageUpdateStamp({
72
+ pageId: "p1",
73
+ lang: "de",
74
+ pageEntry: { output: { sitemap: { lastmod: "2024-06-01" } } },
75
+ cklLedgerDates: ["2024-01-01"],
76
+ });
77
+ expect(result.stamp?.date).toBe("2024-06-01");
78
+ expect(result.stamp?.source).toBe("authored-system-output");
79
+ });
80
+
81
+ it("falls back to CKL when no authored stamp", () => {
82
+ const result = resolvePageUpdateStamp({
83
+ pageId: "p1",
84
+ lang: "de",
85
+ pageEntry: {},
86
+ cklLedgerDates: ["2024-03-01"],
87
+ cklInputs: ["ledger"],
88
+ });
89
+ expect(result.stamp?.date).toBe("2024-03-01");
90
+ expect(result.stamp?.source).toBe("ckl-claim-ledger");
91
+ });
92
+
93
+ it("returns missingReason when no sources available", () => {
94
+ const result = resolvePageUpdateStamp({
95
+ pageId: "p1",
96
+ lang: "de",
97
+ pageEntry: {},
98
+ });
99
+ expect(result.stamp).toBeUndefined();
100
+ expect(result.missingReason).toBe("no-content-source");
101
+ });
102
+ });
@@ -34,3 +34,17 @@ utilities:
34
34
  allowlist:
35
35
  - path: packages/werkstatt-shared/src/share/routes/
36
36
  reason: Canonical placeholder route filter — owns all template detection logic (RFC-0917)
37
+ - id: remediation-catalog
38
+ canonicalPath: packages/werkstatt-shared/src/share/remediation/
39
+ forbiddenImports: []
40
+ functionNames:
41
+ - lookupRemediation
42
+ - REMEDIATION_CATALOG
43
+ patterns: []
44
+ allowlist:
45
+ - path: packages/werkstatt-shared/src/share/remediation/
46
+ reason: Canonical remediation catalog — owns all ruleId-to-remediation-pattern mappings (RFC-1027)
47
+ - path: packages/werkstatt-shared/src/checks/result-helpers.ts
48
+ reason: Consumes lookupRemediation to auto-populate Diagnostic.remediation (RFC-1027)
49
+ - path: packages/werkstatt-engine/src/remediation/
50
+ reason: Engine remediation module reads REMEDIATION_CATALOG for catalog.generate and hint.validate (RFC-1027)