@weatherboard/cms 0.0.0-stage → 0.2.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 (100) hide show
  1. package/README.md +70 -2
  2. package/dist/adapters/next/reader.d.cts +2 -0
  3. package/dist/adapters/next/reader.d.mts +2 -0
  4. package/dist/adapters/next/reader.d.ts +2 -0
  5. package/dist/adapters/sanity/adapter.d.cts +15 -0
  6. package/dist/adapters/sanity/adapter.d.mts +15 -0
  7. package/dist/adapters/sanity/adapter.d.ts +15 -0
  8. package/dist/contracts/query.d.cts +20 -0
  9. package/dist/contracts/query.d.mts +20 -0
  10. package/dist/contracts/query.d.ts +20 -0
  11. package/dist/contracts/text.d.cts +65 -0
  12. package/dist/contracts/text.d.mts +65 -0
  13. package/dist/contracts/text.d.ts +65 -0
  14. package/dist/guard.cjs +3 -0
  15. package/dist/guard.cjs.map +7 -0
  16. package/dist/guard.d.cts +2 -0
  17. package/dist/guard.d.mts +2 -0
  18. package/dist/guard.d.ts +2 -0
  19. package/dist/guard.mjs +3 -0
  20. package/dist/guard.mjs.map +7 -0
  21. package/dist/index.cjs +2 -0
  22. package/dist/index.cjs.map +7 -0
  23. package/dist/index.d.cts +4 -0
  24. package/dist/index.d.mts +4 -0
  25. package/dist/index.d.ts +4 -0
  26. package/dist/index.mjs +2 -0
  27. package/dist/index.mjs.map +7 -0
  28. package/dist/invalidation/route.d.cts +12 -0
  29. package/dist/invalidation/route.d.mts +12 -0
  30. package/dist/invalidation/route.d.ts +12 -0
  31. package/dist/next.cjs +2 -0
  32. package/dist/next.cjs.map +7 -0
  33. package/dist/next.d.cts +4 -0
  34. package/dist/next.d.mts +4 -0
  35. package/dist/next.d.ts +4 -0
  36. package/dist/next.mjs +2 -0
  37. package/dist/next.mjs.map +7 -0
  38. package/dist/portable/array.d.cts +2 -0
  39. package/dist/portable/array.d.mts +2 -0
  40. package/dist/portable/array.d.ts +2 -0
  41. package/dist/portable/fallback.d.cts +4 -0
  42. package/dist/portable/fallback.d.mts +4 -0
  43. package/dist/portable/fallback.d.ts +4 -0
  44. package/dist/portable/site-content.d.cts +220 -0
  45. package/dist/portable/site-content.d.mts +220 -0
  46. package/dist/portable/site-content.d.ts +220 -0
  47. package/dist/portable/webhook.d.cts +133 -0
  48. package/dist/portable/webhook.d.mts +133 -0
  49. package/dist/portable/webhook.d.ts +133 -0
  50. package/dist/runtime/reader.d.cts +51 -0
  51. package/dist/runtime/reader.d.mts +51 -0
  52. package/dist/runtime/reader.d.ts +51 -0
  53. package/dist/sanity.cjs +2 -0
  54. package/dist/sanity.cjs.map +7 -0
  55. package/dist/sanity.d.cts +3 -0
  56. package/dist/sanity.d.mts +3 -0
  57. package/dist/sanity.d.ts +3 -0
  58. package/dist/sanity.mjs +2 -0
  59. package/dist/sanity.mjs.map +7 -0
  60. package/dist/server.cjs +2 -0
  61. package/dist/server.cjs.map +7 -0
  62. package/dist/server.d.cts +3 -0
  63. package/dist/server.d.mts +3 -0
  64. package/dist/server.d.ts +3 -0
  65. package/dist/server.mjs +2 -0
  66. package/dist/server.mjs.map +7 -0
  67. package/dist/site-content.cjs +7 -0
  68. package/dist/site-content.cjs.map +7 -0
  69. package/dist/site-content.d.cts +4 -0
  70. package/dist/site-content.d.mts +4 -0
  71. package/dist/site-content.d.ts +4 -0
  72. package/dist/site-content.mjs +7 -0
  73. package/dist/site-content.mjs.map +7 -0
  74. package/dist/testing.cjs +2 -0
  75. package/dist/testing.cjs.map +7 -0
  76. package/dist/testing.d.cts +2 -0
  77. package/dist/testing.d.mts +2 -0
  78. package/dist/testing.d.ts +2 -0
  79. package/dist/testing.mjs +2 -0
  80. package/dist/testing.mjs.map +7 -0
  81. package/dist/tooling/guard.d.cts +13 -0
  82. package/dist/tooling/guard.d.mts +13 -0
  83. package/dist/tooling/guard.d.ts +13 -0
  84. package/dist/tooling/test-fixtures/break-proof.d.cts +1 -0
  85. package/dist/tooling/test-fixtures/break-proof.d.mts +1 -0
  86. package/dist/tooling/test-fixtures/break-proof.d.ts +1 -0
  87. package/dist/tooling/test-fixtures/consumer.d.cts +9 -0
  88. package/dist/tooling/test-fixtures/consumer.d.mts +9 -0
  89. package/dist/tooling/test-fixtures/consumer.d.ts +9 -0
  90. package/dist/tooling/testing.d.cts +10 -0
  91. package/dist/tooling/testing.d.mts +10 -0
  92. package/dist/tooling/testing.d.ts +10 -0
  93. package/dist/webhook.cjs +2 -0
  94. package/dist/webhook.cjs.map +7 -0
  95. package/dist/webhook.d.cts +2 -0
  96. package/dist/webhook.d.mts +2 -0
  97. package/dist/webhook.d.ts +2 -0
  98. package/dist/webhook.mjs +2 -0
  99. package/dist/webhook.mjs.map +7 -0
  100. package/package.json +142 -3
package/README.md CHANGED
@@ -1,3 +1,71 @@
1
- # Temporary Holding Version
1
+ # @weatherboard/cms
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Published content contracts and server readers. The pure root and
4
+ `@weatherboard/cms/site-content` expose `siteHeroQuery`, `parseSiteHero` and the
5
+ hero types without a provider SDK or environment access. Server reads use
6
+ `@weatherboard/cms/server` or `/next`, with the `/sanity` adapter; `/webhook`
7
+ provides portable signature verification. `/testing` and `/guard` are tooling.
8
+
9
+ ## Site hero
10
+
11
+ The singleton `siteHero` keeps its existing heading (1–80 characters, single
12
+ line, no trailing full stop) and intro (1–320 characters). Both trim whitespace,
13
+ count Unicode code points and refuse an em dash.
14
+
15
+ The additive fields in 0.2.0 are:
16
+
17
+ - `layout`: `"columns"` (Side by side) or `"rows"` (Stacked), exported as
18
+ `SITE_HERO_LAYOUTS`. Missing or unknown CMS values resolve to `"columns"`,
19
+ including when the local fallback selects rows.
20
+ - `taglines`: `{ key: string, text: string }[]`. Keys are site-defined lowercase
21
+ identifiers, 1–32 characters, starting with a letter; remaining characters
22
+ may be letters, digits, short dashes or underscores. Duplicate keys refuse
23
+ the whole field. Text uses the heading copy rules and is limited to
24
+ `SITE_HERO_TAGLINE_MAX_LENGTH` (provisionally 36 Unicode code points).
25
+ An empty array deliberately clears taglines; the site can continue using
26
+ heading. A missing or invalid array uses the reviewed local array, or `[]`
27
+ for a legacy fallback.
28
+
29
+ `SiteHero` accepts existing heading/intro-only fallbacks. `parseSiteHero` returns
30
+ normalized `ParsedSiteHero` data with both new fields present. Invalid CMS fields
31
+ fall back individually, preserving valid siblings. Invalid local fallbacks throw
32
+ before network I/O. A missing whole document uses the normalized local hero.
33
+
34
+ A defaulted CMS layout records `SITE_HERO_LAYOUT_WARNING`, whose value is
35
+ `siteHero.layout.defaulted`, in the resolution/read result's `warnings`, with
36
+ `layout` in `fallbackFields`. The server reader includes it in its existing safe
37
+ `cms.partial-fallback` diagnostic (or `cms.fallback` for a whole fallback).
38
+ Supply `onDiagnostic` to log diagnostics; without it the reader logs safe JSON
39
+ through `console.warn`. Diagnostics contain paths and warning names, never copy.
40
+
41
+ ```ts
42
+ import { parseSiteHero } from '@weatherboard/cms/site-content';
43
+
44
+ const fallback = { heading: 'Fresh air at home', intro: 'Comfort through the year' };
45
+ const resolved = parseSiteHero({
46
+ heading: 'Fresh air at home',
47
+ intro: 'Comfort through the year',
48
+ layout: 'rows',
49
+ taglines: [{ key: 'spring', text: 'Welcome the fresh air' }],
50
+ }, fallback);
51
+ ```
52
+
53
+ ## Schema and release delivery
54
+
55
+ `@weatherboard/sanity-schema-base` derives the hero schema from these rules:
56
+ Layout is a radio with initial value columns; taglines use the same validator
57
+ for item fields and duplicate keys. Schema validation is advisory; Content Lake
58
+ API writes still need boundary validation.
59
+
60
+ The schema source and its committed TypeGen outputs are local artifacts. Updating
61
+ this package does not mutate or deploy a dataset schema. Existing operator/admin
62
+ schema consumers must take the updated shared schema and rebuild their schema
63
+ bundle; generated client schemas require their existing reviewed upgrade path.
64
+ The dashboard's later editor slice must add the picker/tagline controls and use
65
+ these validators before publishing. New field values require a separately
66
+ authorized content write; existing documents remain readable with defaults.
67
+
68
+ 0.2.0 is an additive minor under the pilot semver policy. CI publishes CMS after
69
+ merge through the registered release workflow; clients then need an exact package
70
+ bump and deploy. The CI publish and trusted-publisher proof are release evidence
71
+ that this source change alone cannot supply.
@@ -0,0 +1,2 @@
1
+ import { createCmsReader } from "../../runtime/reader.cjs";
2
+ export declare const createNextCmsReader: typeof createCmsReader;
@@ -0,0 +1,2 @@
1
+ import { createCmsReader } from "../../runtime/reader.mjs";
2
+ export declare const createNextCmsReader: typeof createCmsReader;
@@ -0,0 +1,2 @@
1
+ import { createCmsReader } from "../../runtime/reader.js";
2
+ export declare const createNextCmsReader: typeof createCmsReader;
@@ -0,0 +1,15 @@
1
+ import type { CmsAdapter, CmsFetch } from "../../runtime/reader.cjs";
2
+ export type SanityBinding = {
3
+ readonly projectId: string;
4
+ readonly dataset: string;
5
+ readonly apiVersion: string;
6
+ };
7
+ export type SanityAdapterOptions = {
8
+ readonly binding: SanityBinding;
9
+ readonly readToken?: string;
10
+ readonly fetchImpl?: CmsFetch;
11
+ readonly timeoutMs?: number;
12
+ };
13
+ /** Applies policy via fetch's Next metadata. Standard fetch has no persistent cache or HTML invalidation. */
14
+ export declare function createSanityAdapter(options: SanityAdapterOptions): CmsAdapter;
15
+ export declare function sanityBindingFromEnv(env: Readonly<Record<string, string | undefined>>): SanityBinding;
@@ -0,0 +1,15 @@
1
+ import type { CmsAdapter, CmsFetch } from "../../runtime/reader.mjs";
2
+ export type SanityBinding = {
3
+ readonly projectId: string;
4
+ readonly dataset: string;
5
+ readonly apiVersion: string;
6
+ };
7
+ export type SanityAdapterOptions = {
8
+ readonly binding: SanityBinding;
9
+ readonly readToken?: string;
10
+ readonly fetchImpl?: CmsFetch;
11
+ readonly timeoutMs?: number;
12
+ };
13
+ /** Applies policy via fetch's Next metadata. Standard fetch has no persistent cache or HTML invalidation. */
14
+ export declare function createSanityAdapter(options: SanityAdapterOptions): CmsAdapter;
15
+ export declare function sanityBindingFromEnv(env: Readonly<Record<string, string | undefined>>): SanityBinding;
@@ -0,0 +1,15 @@
1
+ import type { CmsAdapter, CmsFetch } from "../../runtime/reader.js";
2
+ export type SanityBinding = {
3
+ readonly projectId: string;
4
+ readonly dataset: string;
5
+ readonly apiVersion: string;
6
+ };
7
+ export type SanityAdapterOptions = {
8
+ readonly binding: SanityBinding;
9
+ readonly readToken?: string;
10
+ readonly fetchImpl?: CmsFetch;
11
+ readonly timeoutMs?: number;
12
+ };
13
+ /** Applies policy via fetch's Next metadata. Standard fetch has no persistent cache or HTML invalidation. */
14
+ export declare function createSanityAdapter(options: SanityAdapterOptions): CmsAdapter;
15
+ export declare function sanityBindingFromEnv(env: Readonly<Record<string, string | undefined>>): SanityBinding;
@@ -0,0 +1,20 @@
1
+ /** A-1822 S1 owns this implementation. S0 registers the public signatures. */
2
+ export type CmsParams = Readonly<Record<string, string | number | boolean | null>>;
3
+ export type CmsDependencies = {
4
+ readonly types: readonly string[];
5
+ readonly ids?: readonly string[];
6
+ };
7
+ export type CmsResolution<T> = {
8
+ readonly data: T;
9
+ readonly fallbackFields: readonly string[];
10
+ readonly warnings?: readonly "siteHero.layout.defaulted"[];
11
+ };
12
+ export type CmsQuery<T> = {
13
+ readonly name: string;
14
+ readonly query: string;
15
+ readonly params: CmsParams;
16
+ readonly dependencies: CmsDependencies;
17
+ readonly resolve: (raw: unknown, fallback: T) => CmsResolution<T>;
18
+ };
19
+ export declare function defineCmsQuery<T>(query: CmsQuery<T>): CmsQuery<T>;
20
+ export declare function cmsTags(scope: string, dependencies: CmsDependencies): readonly string[];
@@ -0,0 +1,20 @@
1
+ /** A-1822 S1 owns this implementation. S0 registers the public signatures. */
2
+ export type CmsParams = Readonly<Record<string, string | number | boolean | null>>;
3
+ export type CmsDependencies = {
4
+ readonly types: readonly string[];
5
+ readonly ids?: readonly string[];
6
+ };
7
+ export type CmsResolution<T> = {
8
+ readonly data: T;
9
+ readonly fallbackFields: readonly string[];
10
+ readonly warnings?: readonly "siteHero.layout.defaulted"[];
11
+ };
12
+ export type CmsQuery<T> = {
13
+ readonly name: string;
14
+ readonly query: string;
15
+ readonly params: CmsParams;
16
+ readonly dependencies: CmsDependencies;
17
+ readonly resolve: (raw: unknown, fallback: T) => CmsResolution<T>;
18
+ };
19
+ export declare function defineCmsQuery<T>(query: CmsQuery<T>): CmsQuery<T>;
20
+ export declare function cmsTags(scope: string, dependencies: CmsDependencies): readonly string[];
@@ -0,0 +1,20 @@
1
+ /** A-1822 S1 owns this implementation. S0 registers the public signatures. */
2
+ export type CmsParams = Readonly<Record<string, string | number | boolean | null>>;
3
+ export type CmsDependencies = {
4
+ readonly types: readonly string[];
5
+ readonly ids?: readonly string[];
6
+ };
7
+ export type CmsResolution<T> = {
8
+ readonly data: T;
9
+ readonly fallbackFields: readonly string[];
10
+ readonly warnings?: readonly "siteHero.layout.defaulted"[];
11
+ };
12
+ export type CmsQuery<T> = {
13
+ readonly name: string;
14
+ readonly query: string;
15
+ readonly params: CmsParams;
16
+ readonly dependencies: CmsDependencies;
17
+ readonly resolve: (raw: unknown, fallback: T) => CmsResolution<T>;
18
+ };
19
+ export declare function defineCmsQuery<T>(query: CmsQuery<T>): CmsQuery<T>;
20
+ export declare function cmsTags(scope: string, dependencies: CmsDependencies): readonly string[];
@@ -0,0 +1,65 @@
1
+ import { type CmsQuery, type CmsResolution } from "./query.cjs";
2
+ export declare const SITE_HERO_LAYOUTS: readonly ["columns", "rows"];
3
+ export declare const SITE_HERO_TAGLINE_MAX_LENGTH = 36;
4
+ export declare const SITE_HERO_LAYOUT_WARNING: "siteHero.layout.defaulted";
5
+ export type SiteHeroLayout = typeof SITE_HERO_LAYOUTS[number];
6
+ export type SiteHeroTagline = {
7
+ readonly key: string;
8
+ readonly text: string;
9
+ };
10
+ /** Optional additions preserve existing heading/intro-only consumer fallbacks. */
11
+ export type SiteHero = {
12
+ readonly heading: string;
13
+ readonly intro: string;
14
+ readonly layout?: SiteHeroLayout;
15
+ readonly taglines?: readonly SiteHeroTagline[];
16
+ };
17
+ export type ParsedSiteHero = SiteHero & {
18
+ readonly layout: SiteHeroLayout;
19
+ readonly taglines: readonly SiteHeroTagline[];
20
+ };
21
+ export type TextFieldDefinition = {
22
+ readonly name: string;
23
+ readonly label: string;
24
+ readonly noun: {
25
+ readonly article: "a" | "an";
26
+ readonly word: string;
27
+ };
28
+ readonly kind: "single-line" | "multi-line";
29
+ readonly required: boolean;
30
+ readonly minLength: number;
31
+ readonly maxLength: number;
32
+ readonly noTrailingFullStop?: boolean;
33
+ };
34
+ export type TextDocumentDefinition = {
35
+ readonly key: string;
36
+ readonly documentId: string;
37
+ readonly documentType: string;
38
+ readonly fields: readonly TextFieldDefinition[];
39
+ readonly copy: {
40
+ readonly currentTitle: string;
41
+ readonly editTitle: string;
42
+ readonly empty: string;
43
+ };
44
+ };
45
+ export type TextValidation = {
46
+ readonly ok: true;
47
+ readonly value: Readonly<Record<string, string>>;
48
+ } | {
49
+ readonly ok: false;
50
+ readonly errors: Readonly<Record<string, string>>;
51
+ };
52
+ export declare const CMS_CONTRACT_VERSION = 1;
53
+ export declare const SITE_HERO: TextDocumentDefinition;
54
+ export declare function validateTextDocument(definition: TextDocumentDefinition, raw: Readonly<Record<string, unknown>>): TextValidation;
55
+ /** Shared with the schema adapter; no provider metadata is returned. */
56
+ export declare function validateSiteHeroTaglines(raw: unknown): {
57
+ readonly ok: true;
58
+ readonly value: readonly SiteHeroTagline[];
59
+ } | {
60
+ readonly ok: false;
61
+ readonly error: string;
62
+ };
63
+ /** Strict per-field resolution: an invalid list never partially changes keyed copy. */
64
+ export declare function parseSiteHero(raw: unknown, fallback: SiteHero): CmsResolution<ParsedSiteHero>;
65
+ export declare function siteHeroQuery(): CmsQuery<SiteHero>;
@@ -0,0 +1,65 @@
1
+ import { type CmsQuery, type CmsResolution } from "./query.mjs";
2
+ export declare const SITE_HERO_LAYOUTS: readonly ["columns", "rows"];
3
+ export declare const SITE_HERO_TAGLINE_MAX_LENGTH = 36;
4
+ export declare const SITE_HERO_LAYOUT_WARNING: "siteHero.layout.defaulted";
5
+ export type SiteHeroLayout = typeof SITE_HERO_LAYOUTS[number];
6
+ export type SiteHeroTagline = {
7
+ readonly key: string;
8
+ readonly text: string;
9
+ };
10
+ /** Optional additions preserve existing heading/intro-only consumer fallbacks. */
11
+ export type SiteHero = {
12
+ readonly heading: string;
13
+ readonly intro: string;
14
+ readonly layout?: SiteHeroLayout;
15
+ readonly taglines?: readonly SiteHeroTagline[];
16
+ };
17
+ export type ParsedSiteHero = SiteHero & {
18
+ readonly layout: SiteHeroLayout;
19
+ readonly taglines: readonly SiteHeroTagline[];
20
+ };
21
+ export type TextFieldDefinition = {
22
+ readonly name: string;
23
+ readonly label: string;
24
+ readonly noun: {
25
+ readonly article: "a" | "an";
26
+ readonly word: string;
27
+ };
28
+ readonly kind: "single-line" | "multi-line";
29
+ readonly required: boolean;
30
+ readonly minLength: number;
31
+ readonly maxLength: number;
32
+ readonly noTrailingFullStop?: boolean;
33
+ };
34
+ export type TextDocumentDefinition = {
35
+ readonly key: string;
36
+ readonly documentId: string;
37
+ readonly documentType: string;
38
+ readonly fields: readonly TextFieldDefinition[];
39
+ readonly copy: {
40
+ readonly currentTitle: string;
41
+ readonly editTitle: string;
42
+ readonly empty: string;
43
+ };
44
+ };
45
+ export type TextValidation = {
46
+ readonly ok: true;
47
+ readonly value: Readonly<Record<string, string>>;
48
+ } | {
49
+ readonly ok: false;
50
+ readonly errors: Readonly<Record<string, string>>;
51
+ };
52
+ export declare const CMS_CONTRACT_VERSION = 1;
53
+ export declare const SITE_HERO: TextDocumentDefinition;
54
+ export declare function validateTextDocument(definition: TextDocumentDefinition, raw: Readonly<Record<string, unknown>>): TextValidation;
55
+ /** Shared with the schema adapter; no provider metadata is returned. */
56
+ export declare function validateSiteHeroTaglines(raw: unknown): {
57
+ readonly ok: true;
58
+ readonly value: readonly SiteHeroTagline[];
59
+ } | {
60
+ readonly ok: false;
61
+ readonly error: string;
62
+ };
63
+ /** Strict per-field resolution: an invalid list never partially changes keyed copy. */
64
+ export declare function parseSiteHero(raw: unknown, fallback: SiteHero): CmsResolution<ParsedSiteHero>;
65
+ export declare function siteHeroQuery(): CmsQuery<SiteHero>;
@@ -0,0 +1,65 @@
1
+ import { type CmsQuery, type CmsResolution } from "./query.js";
2
+ export declare const SITE_HERO_LAYOUTS: readonly ["columns", "rows"];
3
+ export declare const SITE_HERO_TAGLINE_MAX_LENGTH = 36;
4
+ export declare const SITE_HERO_LAYOUT_WARNING: "siteHero.layout.defaulted";
5
+ export type SiteHeroLayout = typeof SITE_HERO_LAYOUTS[number];
6
+ export type SiteHeroTagline = {
7
+ readonly key: string;
8
+ readonly text: string;
9
+ };
10
+ /** Optional additions preserve existing heading/intro-only consumer fallbacks. */
11
+ export type SiteHero = {
12
+ readonly heading: string;
13
+ readonly intro: string;
14
+ readonly layout?: SiteHeroLayout;
15
+ readonly taglines?: readonly SiteHeroTagline[];
16
+ };
17
+ export type ParsedSiteHero = SiteHero & {
18
+ readonly layout: SiteHeroLayout;
19
+ readonly taglines: readonly SiteHeroTagline[];
20
+ };
21
+ export type TextFieldDefinition = {
22
+ readonly name: string;
23
+ readonly label: string;
24
+ readonly noun: {
25
+ readonly article: "a" | "an";
26
+ readonly word: string;
27
+ };
28
+ readonly kind: "single-line" | "multi-line";
29
+ readonly required: boolean;
30
+ readonly minLength: number;
31
+ readonly maxLength: number;
32
+ readonly noTrailingFullStop?: boolean;
33
+ };
34
+ export type TextDocumentDefinition = {
35
+ readonly key: string;
36
+ readonly documentId: string;
37
+ readonly documentType: string;
38
+ readonly fields: readonly TextFieldDefinition[];
39
+ readonly copy: {
40
+ readonly currentTitle: string;
41
+ readonly editTitle: string;
42
+ readonly empty: string;
43
+ };
44
+ };
45
+ export type TextValidation = {
46
+ readonly ok: true;
47
+ readonly value: Readonly<Record<string, string>>;
48
+ } | {
49
+ readonly ok: false;
50
+ readonly errors: Readonly<Record<string, string>>;
51
+ };
52
+ export declare const CMS_CONTRACT_VERSION = 1;
53
+ export declare const SITE_HERO: TextDocumentDefinition;
54
+ export declare function validateTextDocument(definition: TextDocumentDefinition, raw: Readonly<Record<string, unknown>>): TextValidation;
55
+ /** Shared with the schema adapter; no provider metadata is returned. */
56
+ export declare function validateSiteHeroTaglines(raw: unknown): {
57
+ readonly ok: true;
58
+ readonly value: readonly SiteHeroTagline[];
59
+ } | {
60
+ readonly ok: false;
61
+ readonly error: string;
62
+ };
63
+ /** Strict per-field resolution: an invalid list never partially changes keyed copy. */
64
+ export declare function parseSiteHero(raw: unknown, fallback: SiteHero): CmsResolution<ParsedSiteHero>;
65
+ export declare function siteHeroQuery(): CmsQuery<SiteHero>;
package/dist/guard.cjs ADDED
@@ -0,0 +1,3 @@
1
+ "use strict";var I=Object.create;var w=Object.defineProperty;var B=Object.getOwnPropertyDescriptor;var M=Object.getOwnPropertyNames;var A=Object.getPrototypeOf,z=Object.prototype.hasOwnProperty;var v=(i,n)=>{for(var s in n)w(i,s,{get:n[s],enumerable:!0})},D=(i,n,s,f)=>{if(n&&typeof n=="object"||typeof n=="function")for(let p of M(n))!z.call(i,p)&&p!==s&&w(i,p,{get:()=>n[p],enumerable:!(f=B(n,p))||f.enumerable});return i};var q=(i,n,s)=>(s=i!=null?I(A(i)):{},D(n||!i||!i.__esModule?w(s,"default",{value:i,enumerable:!0}):s,i)),R=i=>D(w({},"__esModule",{value:!0}),i);var $={};v($,{findCmsBoundaryViolations:()=>N});module.exports=R($);var t=q(require("typescript"),1),P=["next-sanity","@sanity/client","@sanity/webhook"],V=/\.api(?:cdn)?\.sanity\.io|\/data\/query\/|\/query(?:[/?#]|$)/i;function S(i){return t.default.isParenthesizedExpression(i)||t.default.isAsExpression(i)||t.default.isSatisfiesExpression(i)||t.default.isTypeAssertionExpression(i)||t.default.isNonNullExpression(i)?S(i.expression):t.default.isBinaryExpression(i)&&i.operatorToken.kind===t.default.SyntaxKind.CommaToken?S(i.right):i}function d(i){if(i=S(i),t.default.isStringLiteralLike(i))return i.text;if(t.default.isBinaryExpression(i)&&i.operatorToken.kind===t.default.SyntaxKind.PlusToken){let n=d(i.left),s=d(i.right);return n===void 0||s===void 0?void 0:n+s}if(t.default.isTemplateExpression(i))return i.head.text+i.templateSpans.map(n=>(d(n.expression)??"\0")+n.literal.text).join("");if(t.default.isNewExpression(i)&&t.default.isIdentifier(i.expression)&&["URL","Request"].includes(i.expression.text)&&i.arguments?.[0])return d(i.arguments[0])}function k(i,n){for(let s=i;s;s=s.parent){let f=s.bindings.get(n);if(f)return f}}function F(i,n){if(i=S(i),t.default.isIdentifier(i)){let s=n&&k(n,i.text);return s?s.aliasDepth===void 0?void 0:"fetch":i.text}if(t.default.isPropertyAccessExpression(i)){let s=F(i.expression,n);return s?`${s}.${i.name.text}`:void 0}if(t.default.isElementAccessExpression(i)&&i.argumentExpression){let s=F(i.expression,n),f=d(i.argumentExpression);return s&&f?`${s}.${f}`:void 0}}function j(i){let n={kind:"source",bindings:new Map},s=new Map([[i,n]]),f=[],p=(e,o)=>{let r=e.bindings.get(o);if(r)return r;let l={};return e.bindings.set(o,l),l},c=(e,o)=>{if(t.default.isIdentifier(o))p(e,o.text);else for(let r of o.elements)t.default.isOmittedExpression(r)||c(e,r.name)},g=(e,o)=>{if(o.parent.flags&t.default.NodeFlags.BlockScoped)return e;let r=e;for(;r.kind==="block"&&r.parent;)r=r.parent;return r},m=(e,o)=>{let r=e.importClause;if(!r)return;r.name&&p(o,r.name.text);let l=r.namedBindings;if(l&&t.default.isNamespaceImport(l))p(o,l.name.text);else if(l)for(let a of l.elements)p(o,a.name.text)},L=e=>{let o=e.propertyName??e.name;return(t.default.isIdentifier(o)||t.default.isStringLiteralLike(o))&&o.text==="fetch"},x=(e,o)=>{let r=o;if(t.default.isFunctionDeclaration(e)&&e.name||t.default.isClassDeclaration(e)&&e.name||t.default.isEnumDeclaration(e)?p(r,e.name.text):t.default.isImportDeclaration(e)?m(e,r):t.default.isImportEqualsDeclaration(e)&&p(r,e.name.text),t.default.isFunctionLike(e)){r={parent:o,kind:"function",bindings:new Map},s.set(e,r),t.default.isFunctionExpression(e)&&e.name&&p(r,e.name.text);for(let l of e.parameters)c(r,l.name)}else if(t.default.isCatchClause(e))r={parent:o,kind:"block",bindings:new Map},s.set(e,r),e.variableDeclaration&&c(r,e.variableDeclaration.name);else if(t.default.isBlock(e))r={parent:o,kind:"block",bindings:new Map},s.set(e,r);else if(t.default.isForStatement(e)||t.default.isForInStatement(e)||t.default.isForOfStatement(e)){let l=e.initializer;l&&t.default.isVariableDeclarationList(l)&&l.flags&t.default.NodeFlags.BlockScoped&&(r={parent:o,kind:"block",bindings:new Map},s.set(e,r))}if(t.default.isVariableDeclaration(e)){let l=g(r,e);if(c(l,e.name),e.initializer&&t.default.isIdentifier(e.name))f.push({binding:p(l,e.name.text),initializer:e.initializer,scope:r,globalThisFetch:!1});else if(e.initializer&&t.default.isObjectBindingPattern(e.name))for(let a of e.name.elements)!L(a)||!t.default.isIdentifier(a.name)||f.push({binding:p(l,a.name.text),initializer:e.initializer,scope:r,globalThisFetch:!0})}t.default.forEachChild(e,l=>x(l,r))};t.default.forEachChild(i,e=>x(e,n));let b=(e,o,r)=>(e=S(e),t.default.isIdentifier(e)&&r.includes(e.text)&&!k(o,e.text)),E=(e,o)=>{if(e=S(e),t.default.isIdentifier(e)){let r=k(o,e.text);return r?r.aliasDepth:e.text==="fetch"?-1:void 0}if(t.default.isPropertyAccessExpression(e)&&e.name.text==="fetch"&&b(e.expression,o,["globalThis","window"])||t.default.isElementAccessExpression(e)&&e.argumentExpression&&d(e.argumentExpression)==="fetch"&&b(e.expression,o,["globalThis","window"]))return-1};for(let e of f)(e.globalThisFetch?b(e.initializer,e.scope,["globalThis"]):E(e.initializer,e.scope)===-1)&&(e.binding.aliasDepth=0);for(let e of f)e.binding.aliasDepth===void 0&&!e.globalThisFetch&&E(e.initializer,e.scope)===0&&(e.binding.aliasDepth=1);return{scopes:s,sourceScope:n}}function T(i){return typeof i=="string"&&i.length>0&&!i.startsWith("/")&&!/[\\\0*?{}]/.test(i)&&i.split("/").every(n=>n!==""&&n!=="."&&n!=="..")}function N(i){if(!Array.isArray(i.files)||i.files.length===0)throw new TypeError("CMS guard requires source files");let n=new Set;for(let c of i.files){if(!c||!T(c.path)||typeof c.text!="string"||n.has(c.path))throw new TypeError("CMS guard requires unique readable source paths");n.add(c.path)}let s=i.allowedProviderFiles??[];if(!Array.isArray(s)||new Set(s).size!==s.length||s.some(c=>!T(c)||!n.has(c)))throw new TypeError("CMS guard requires unique exact provider exemptions present in the scan");let f=new Set,p=[];for(let c of i.files){let g=c.path.split(".").at(-1)?.toLowerCase();if(!["ts","tsx","mts","cts","js","jsx","mjs","cjs","astro"].includes(g??""))throw new TypeError("CMS guard does not support this source syntax");let m=c.text;if(g==="astro"){let a=/^(?:\uFEFF)?---[^\S\r\n]*\r?\n((?:[^\n]*\n)*?)---[^\S\r\n]*(?:\r?\n|$)/.exec(m);if(m.replace(/^\uFEFF/,"").startsWith("---")&&!a)throw new TypeError("CMS guard cannot parse Astro frontmatter");m=a?`
2
+ `+a[1]:""}let L=g==="tsx"||g==="jsx"?t.default.ScriptKind.TSX:t.default.ScriptKind.TS,x=t.default.createSourceFile(c.path,m,t.default.ScriptTarget.Latest,!0,L);if(x.parseDiagnostics.length)throw new TypeError("CMS guard cannot parse source");let{scopes:E,sourceScope:e}=j(x),o=(a,h)=>{if(s.includes(c.path)){f.add(c.path);return}p.push({path:c.path,line:x.getLineAndCharacterOfPosition(a.getStart(x)).line+1,rule:h})},r=(a,h)=>{let y=h&&d(h);y!==void 0&&P.some(u=>y===u||y.startsWith(`${u}/`))&&o(a,"forbidden-provider-import")},l=(a,h)=>{let y=E.get(a)??h;if(t.default.isImportDeclaration(a)||t.default.isExportDeclaration(a))r(a,a.moduleSpecifier);else if(t.default.isImportEqualsDeclaration(a)&&t.default.isExternalModuleReference(a.moduleReference))r(a,a.moduleReference.expression);else if(t.default.isImportTypeNode(a)&&t.default.isLiteralTypeNode(a.argument))r(a,a.argument.literal);else if(t.default.isCallExpression(a)){let u=F(a.expression,y);if((a.expression.kind===t.default.SyntaxKind.ImportKeyword||u==="require"||u==="require.resolve")&&r(a,a.arguments[0]),u==="fetch"||u?.endsWith(".fetch")){let C=a.arguments[0]&&d(a.arguments[0]);C!==void 0&&V.test(C)&&o(a,"direct-provider-read")}}t.default.forEachChild(a,u=>l(u,y))};l(x,e)}if(s.some(c=>!f.has(c)))throw new TypeError("CMS guard has an unused provider exemption");return p}0&&(module.exports={findCmsBoundaryViolations});
3
+ //# sourceMappingURL=guard.cjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/guard.ts", "../src/tooling/guard.ts"],
4
+ "sourcesContent": ["export { findCmsBoundaryViolations } from \"./tooling/guard.js\";\nexport type { CmsSourceFile, CmsGuardViolation } from \"./tooling/guard.js\";\n", "import ts from \"typescript\";\n\nexport type CmsSourceFile = { readonly path: string; readonly text: string };\nexport type CmsGuardViolation = { readonly path: string; readonly line: number; readonly rule: string };\n\nconst PROVIDERS = [\"next-sanity\", \"@sanity/client\", \"@sanity/webhook\"];\nconst RAW_READ = /\\.api(?:cdn)?\\.sanity\\.io|\\/data\\/query\\/|\\/query(?:[/?#]|$)/i;\n\n// These expressions preserve their operand (or the final comma operand).\n// Inspect the underlying syntax without evaluating consumer code.\nfunction unwrap(node: ts.Node): ts.Node {\n if (ts.isParenthesizedExpression(node) || ts.isAsExpression(node)\n || ts.isSatisfiesExpression(node) || ts.isTypeAssertionExpression(node)\n || ts.isNonNullExpression(node)) return unwrap(node.expression);\n if (ts.isBinaryExpression(node) && node.operatorToken.kind === ts.SyntaxKind.CommaToken) return unwrap(node.right);\n return node;\n}\n\n// Evaluate ordinary literal expressions, without executing consumer code.\nfunction literal(node: ts.Node): string | undefined {\n node = unwrap(node);\n if (ts.isStringLiteralLike(node)) return node.text;\n if (ts.isBinaryExpression(node) && node.operatorToken.kind === ts.SyntaxKind.PlusToken) {\n const left = literal(node.left);\n const right = literal(node.right);\n return left === undefined || right === undefined ? undefined : left + right;\n }\n if (ts.isTemplateExpression(node)) {\n return node.head.text + node.templateSpans.map((span) =>\n (literal(span.expression) ?? \"\\0\") + span.literal.text).join(\"\");\n }\n if (ts.isNewExpression(node) && ts.isIdentifier(node.expression)\n && [\"URL\", \"Request\"].includes(node.expression.text) && node.arguments?.[0]) return literal(node.arguments[0]);\n return undefined;\n}\n\ntype LexicalBinding = { aliasDepth?: 0 | 1 };\ntype LexicalScope = {\n readonly parent?: LexicalScope;\n readonly kind: \"source\" | \"function\" | \"block\";\n readonly bindings: Map<string, LexicalBinding>;\n};\n\nfunction lookupBinding(scope: LexicalScope, name: string): LexicalBinding | undefined {\n for (let current: LexicalScope | undefined = scope; current; current = current.parent) {\n const binding = current.bindings.get(name);\n if (binding) return binding;\n }\n return undefined;\n}\n\nfunction callName(node: ts.Node, scope?: LexicalScope): string | undefined {\n node = unwrap(node);\n if (ts.isIdentifier(node)) {\n const binding = scope && lookupBinding(scope, node.text);\n if (binding) return binding.aliasDepth === undefined ? undefined : \"fetch\";\n return node.text;\n }\n if (ts.isPropertyAccessExpression(node)) {\n const owner = callName(node.expression, scope);\n return owner ? `${owner}.${node.name.text}` : undefined;\n }\n if (ts.isElementAccessExpression(node) && node.argumentExpression) {\n const owner = callName(node.expression, scope);\n const key = literal(node.argumentExpression);\n return owner && key ? `${owner}.${key}` : undefined;\n }\n return undefined;\n}\n\nfunction lexicalScopes(source: ts.SourceFile): {\n readonly scopes: ReadonlyMap<ts.Node, LexicalScope>;\n readonly sourceScope: LexicalScope;\n} {\n const sourceScope: LexicalScope = { kind: \"source\", bindings: new Map() };\n const scopes = new Map<ts.Node, LexicalScope>([[source, sourceScope]]);\n const candidates: Array<{\n readonly binding: LexicalBinding;\n readonly initializer: ts.Expression;\n readonly scope: LexicalScope;\n readonly globalThisFetch: boolean;\n }> = [];\n const bind = (scope: LexicalScope, name: string) => {\n const existing = scope.bindings.get(name);\n if (existing) return existing;\n const binding: LexicalBinding = {};\n scope.bindings.set(name, binding);\n return binding;\n };\n const bindName = (scope: LexicalScope, name: ts.BindingName) => {\n if (ts.isIdentifier(name)) bind(scope, name.text);\n else for (const element of name.elements) if (!ts.isOmittedExpression(element)) bindName(scope, element.name);\n };\n const variableScope = (scope: LexicalScope, declaration: ts.VariableDeclaration) => {\n if (declaration.parent.flags & ts.NodeFlags.BlockScoped) return scope;\n let current = scope;\n while (current.kind === \"block\" && current.parent) current = current.parent;\n return current;\n };\n const bindImports = (node: ts.ImportDeclaration, scope: LexicalScope) => {\n const clause = node.importClause;\n if (!clause) return;\n if (clause.name) bind(scope, clause.name.text);\n const imports = clause.namedBindings;\n if (imports && ts.isNamespaceImport(imports)) bind(scope, imports.name.text);\n else if (imports) for (const element of imports.elements) bind(scope, element.name.text);\n };\n const destructuredFetch = (element: ts.BindingElement) => {\n const property = element.propertyName ?? element.name;\n return (ts.isIdentifier(property) || ts.isStringLiteralLike(property)) && property.text === \"fetch\";\n };\n\n const collect = (node: ts.Node, incoming: LexicalScope) => {\n let scope = incoming;\n if (ts.isFunctionDeclaration(node) && node.name) bind(scope, node.name.text);\n else if (ts.isClassDeclaration(node) && node.name) bind(scope, node.name.text);\n else if (ts.isEnumDeclaration(node)) bind(scope, node.name.text);\n else if (ts.isImportDeclaration(node)) bindImports(node, scope);\n else if (ts.isImportEqualsDeclaration(node)) bind(scope, node.name.text);\n\n if (ts.isFunctionLike(node)) {\n scope = { parent: incoming, kind: \"function\", bindings: new Map() };\n scopes.set(node, scope);\n if (ts.isFunctionExpression(node) && node.name) bind(scope, node.name.text);\n for (const parameter of node.parameters) bindName(scope, parameter.name);\n } else if (ts.isCatchClause(node)) {\n scope = { parent: incoming, kind: \"block\", bindings: new Map() };\n scopes.set(node, scope);\n if (node.variableDeclaration) bindName(scope, node.variableDeclaration.name);\n } else if (ts.isBlock(node)) {\n scope = { parent: incoming, kind: \"block\", bindings: new Map() };\n scopes.set(node, scope);\n } else if (ts.isForStatement(node) || ts.isForInStatement(node) || ts.isForOfStatement(node)) {\n const initializer = node.initializer;\n if (initializer && ts.isVariableDeclarationList(initializer)\n && initializer.flags & ts.NodeFlags.BlockScoped) {\n scope = { parent: incoming, kind: \"block\", bindings: new Map() };\n scopes.set(node, scope);\n }\n }\n\n if (ts.isVariableDeclaration(node)) {\n const target = variableScope(scope, node);\n bindName(target, node.name);\n if (node.initializer && ts.isIdentifier(node.name)) {\n candidates.push({\n binding: bind(target, node.name.text), initializer: node.initializer, scope,\n globalThisFetch: false,\n });\n } else if (node.initializer && ts.isObjectBindingPattern(node.name)) {\n for (const element of node.name.elements) {\n if (!destructuredFetch(element) || !ts.isIdentifier(element.name)) continue;\n candidates.push({\n binding: bind(target, element.name.text), initializer: node.initializer, scope,\n globalThisFetch: true,\n });\n }\n }\n }\n ts.forEachChild(node, (child) => collect(child, scope));\n };\n ts.forEachChild(source, (node) => collect(node, sourceScope));\n\n const isUnboundGlobal = (node: ts.Node, scope: LexicalScope, names: readonly string[]) => {\n node = unwrap(node);\n return ts.isIdentifier(node) && names.includes(node.text) && !lookupBinding(scope, node.text);\n };\n const fetchReferenceDepth = (node: ts.Node, scope: LexicalScope): -1 | 0 | 1 | undefined => {\n node = unwrap(node);\n if (ts.isIdentifier(node)) {\n const binding = lookupBinding(scope, node.text);\n if (binding) return binding.aliasDepth;\n return node.text === \"fetch\" ? -1 : undefined;\n }\n if (ts.isPropertyAccessExpression(node) && node.name.text === \"fetch\"\n && isUnboundGlobal(node.expression, scope, [\"globalThis\", \"window\"])) return -1;\n if (ts.isElementAccessExpression(node) && node.argumentExpression\n && literal(node.argumentExpression) === \"fetch\"\n && isUnboundGlobal(node.expression, scope, [\"globalThis\", \"window\"])) return -1;\n return undefined;\n };\n\n // Classify direct aliases first, then exactly one alias-to-alias hop. Binding\n // collection happens before classification so nearer non-fetch declarations\n // and parameters correctly shadow aliases in an outer scope.\n for (const candidate of candidates) {\n const direct = candidate.globalThisFetch\n ? isUnboundGlobal(candidate.initializer, candidate.scope, [\"globalThis\"])\n : fetchReferenceDepth(candidate.initializer, candidate.scope) === -1;\n if (direct) candidate.binding.aliasDepth = 0;\n }\n for (const candidate of candidates) {\n if (candidate.binding.aliasDepth === undefined && !candidate.globalThisFetch\n && fetchReferenceDepth(candidate.initializer, candidate.scope) === 0) candidate.binding.aliasDepth = 1;\n }\n return { scopes, sourceScope };\n}\n\nfunction exactPath(path: unknown): path is string {\n return typeof path === \"string\" && path.length > 0 && !path.startsWith(\"/\")\n && !/[\\\\\\0*?{}]/.test(path)\n && path.split(\"/\").every((part) => part !== \"\" && part !== \".\" && part !== \"..\");\n}\n\nexport function findCmsBoundaryViolations(options: {\n files: readonly CmsSourceFile[];\n allowedProviderFiles?: readonly string[];\n}): readonly CmsGuardViolation[] {\n if (!Array.isArray(options.files) || options.files.length === 0) throw new TypeError(\"CMS guard requires source files\");\n const paths = new Set<string>();\n for (const file of options.files) {\n if (!file || !exactPath(file.path) || typeof file.text !== \"string\" || paths.has(file.path)) {\n throw new TypeError(\"CMS guard requires unique readable source paths\");\n }\n paths.add(file.path);\n }\n const allowed = options.allowedProviderFiles ?? [];\n if (!Array.isArray(allowed) || new Set(allowed).size !== allowed.length\n || allowed.some((path) => !exactPath(path) || !paths.has(path))) {\n throw new TypeError(\"CMS guard requires unique exact provider exemptions present in the scan\");\n }\n const used = new Set<string>();\n const violations: CmsGuardViolation[] = [];\n for (const file of options.files) {\n const extension = file.path.split(\".\").at(-1)?.toLowerCase();\n if (![\"ts\", \"tsx\", \"mts\", \"cts\", \"js\", \"jsx\", \"mjs\", \"cjs\", \"astro\"].includes(extension ?? \"\")) {\n throw new TypeError(\"CMS guard does not support this source syntax\");\n }\n let text = file.text;\n if (extension === \"astro\") {\n // Keep line positions from the complete file, but parse only frontmatter.\n const frontmatter = /^(?:\\uFEFF)?---[^\\S\\r\\n]*\\r?\\n((?:[^\\n]*\\n)*?)---[^\\S\\r\\n]*(?:\\r?\\n|$)/.exec(text);\n if (text.replace(/^\\uFEFF/, \"\").startsWith(\"---\") && !frontmatter) {\n throw new TypeError(\"CMS guard cannot parse Astro frontmatter\");\n }\n text = frontmatter ? \"\\n\" + frontmatter[1] : \"\";\n }\n const kind = extension === \"tsx\" || extension === \"jsx\" ? ts.ScriptKind.TSX : ts.ScriptKind.TS;\n const source = ts.createSourceFile(file.path, text, ts.ScriptTarget.Latest, true, kind);\n const diagnostics = (source as ts.SourceFile & { parseDiagnostics: readonly ts.Diagnostic[] }).parseDiagnostics;\n if (diagnostics.length) throw new TypeError(\"CMS guard cannot parse source\");\n // Plan section 6 covers literal/template fetch URLs and ordinary lexical\n // aliases, without resolving computed transports or computed URL values.\n const { scopes, sourceScope } = lexicalScopes(source);\n const report = (node: ts.Node, rule: string) => {\n if (allowed.includes(file.path)) { used.add(file.path); return; }\n violations.push({ path: file.path, line: source.getLineAndCharacterOfPosition(node.getStart(source)).line + 1, rule });\n };\n const checkImport = (node: ts.Node, argument: ts.Node | undefined) => {\n const specifier = argument && literal(argument);\n if (specifier !== undefined && PROVIDERS.some((name) => specifier === name || specifier.startsWith(`${name}/`))) {\n report(node, \"forbidden-provider-import\");\n }\n };\n const visit = (node: ts.Node, incoming: LexicalScope) => {\n const scope = scopes.get(node) ?? incoming;\n if (ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) checkImport(node, node.moduleSpecifier);\n else if (ts.isImportEqualsDeclaration(node) && ts.isExternalModuleReference(node.moduleReference)) {\n checkImport(node, node.moduleReference.expression);\n } else if (ts.isImportTypeNode(node) && ts.isLiteralTypeNode(node.argument)) checkImport(node, node.argument.literal);\n else if (ts.isCallExpression(node)) {\n const name = callName(node.expression, scope);\n if (node.expression.kind === ts.SyntaxKind.ImportKeyword || name === \"require\" || name === \"require.resolve\") {\n checkImport(node, node.arguments[0]);\n }\n if (name === \"fetch\" || name?.endsWith(\".fetch\")) {\n const url = node.arguments[0] && literal(node.arguments[0]);\n if (url !== undefined && RAW_READ.test(url)) report(node, \"direct-provider-read\");\n }\n }\n ts.forEachChild(node, (child) => visit(child, scope));\n };\n visit(source, sourceScope);\n }\n if (allowed.some((path) => !used.has(path))) throw new TypeError(\"CMS guard has an unused provider exemption\");\n return violations;\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["guard_exports", "__export", "findCmsBoundaryViolations", "__toCommonJS", "import_typescript", "PROVIDERS", "RAW_READ", "unwrap", "node", "ts", "literal", "left", "right", "span", "lookupBinding", "scope", "name", "current", "binding", "callName", "owner", "key", "lexicalScopes", "source", "sourceScope", "scopes", "candidates", "bind", "existing", "bindName", "element", "variableScope", "declaration", "bindImports", "clause", "imports", "destructuredFetch", "property", "collect", "incoming", "parameter", "initializer", "target", "child", "isUnboundGlobal", "names", "fetchReferenceDepth", "candidate", "exactPath", "path", "part", "findCmsBoundaryViolations", "options", "paths", "file", "allowed", "used", "violations", "extension", "text", "frontmatter", "kind", "report", "rule", "checkImport", "argument", "specifier", "visit", "url"]
7
+ }
@@ -0,0 +1,2 @@
1
+ export { findCmsBoundaryViolations } from "./tooling/guard.cjs";
2
+ export type { CmsSourceFile, CmsGuardViolation } from "./tooling/guard.cjs";
@@ -0,0 +1,2 @@
1
+ export { findCmsBoundaryViolations } from "./tooling/guard.mjs";
2
+ export type { CmsSourceFile, CmsGuardViolation } from "./tooling/guard.mjs";
@@ -0,0 +1,2 @@
1
+ export { findCmsBoundaryViolations } from "./tooling/guard.js";
2
+ export type { CmsSourceFile, CmsGuardViolation } from "./tooling/guard.js";
package/dist/guard.mjs ADDED
@@ -0,0 +1,3 @@
1
+ import i from"typescript";var D=["next-sanity","@sanity/client","@sanity/webhook"],T=/\.api(?:cdn)?\.sanity\.io|\/data\/query\/|\/query(?:[/?#]|$)/i;function S(t){return i.isParenthesizedExpression(t)||i.isAsExpression(t)||i.isSatisfiesExpression(t)||i.isTypeAssertionExpression(t)||i.isNonNullExpression(t)?S(t.expression):i.isBinaryExpression(t)&&t.operatorToken.kind===i.SyntaxKind.CommaToken?S(t.right):t}function d(t){if(t=S(t),i.isStringLiteralLike(t))return t.text;if(i.isBinaryExpression(t)&&t.operatorToken.kind===i.SyntaxKind.PlusToken){let o=d(t.left),a=d(t.right);return o===void 0||a===void 0?void 0:o+a}if(i.isTemplateExpression(t))return t.head.text+t.templateSpans.map(o=>(d(o.expression)??"\0")+o.literal.text).join("");if(i.isNewExpression(t)&&i.isIdentifier(t.expression)&&["URL","Request"].includes(t.expression.text)&&t.arguments?.[0])return d(t.arguments[0])}function L(t,o){for(let a=t;a;a=a.parent){let f=a.bindings.get(o);if(f)return f}}function k(t,o){if(t=S(t),i.isIdentifier(t)){let a=o&&L(o,t.text);return a?a.aliasDepth===void 0?void 0:"fetch":t.text}if(i.isPropertyAccessExpression(t)){let a=k(t.expression,o);return a?`${a}.${t.name.text}`:void 0}if(i.isElementAccessExpression(t)&&t.argumentExpression){let a=k(t.expression,o),f=d(t.argumentExpression);return a&&f?`${a}.${f}`:void 0}}function N(t){let o={kind:"source",bindings:new Map},a=new Map([[t,o]]),f=[],p=(e,n)=>{let r=e.bindings.get(n);if(r)return r;let l={};return e.bindings.set(n,l),l},c=(e,n)=>{if(i.isIdentifier(n))p(e,n.text);else for(let r of n.elements)i.isOmittedExpression(r)||c(e,r.name)},g=(e,n)=>{if(n.parent.flags&i.NodeFlags.BlockScoped)return e;let r=e;for(;r.kind==="block"&&r.parent;)r=r.parent;return r},m=(e,n)=>{let r=e.importClause;if(!r)return;r.name&&p(n,r.name.text);let l=r.namedBindings;if(l&&i.isNamespaceImport(l))p(n,l.name.text);else if(l)for(let s of l.elements)p(n,s.name.text)},w=e=>{let n=e.propertyName??e.name;return(i.isIdentifier(n)||i.isStringLiteralLike(n))&&n.text==="fetch"},x=(e,n)=>{let r=n;if(i.isFunctionDeclaration(e)&&e.name||i.isClassDeclaration(e)&&e.name||i.isEnumDeclaration(e)?p(r,e.name.text):i.isImportDeclaration(e)?m(e,r):i.isImportEqualsDeclaration(e)&&p(r,e.name.text),i.isFunctionLike(e)){r={parent:n,kind:"function",bindings:new Map},a.set(e,r),i.isFunctionExpression(e)&&e.name&&p(r,e.name.text);for(let l of e.parameters)c(r,l.name)}else if(i.isCatchClause(e))r={parent:n,kind:"block",bindings:new Map},a.set(e,r),e.variableDeclaration&&c(r,e.variableDeclaration.name);else if(i.isBlock(e))r={parent:n,kind:"block",bindings:new Map},a.set(e,r);else if(i.isForStatement(e)||i.isForInStatement(e)||i.isForOfStatement(e)){let l=e.initializer;l&&i.isVariableDeclarationList(l)&&l.flags&i.NodeFlags.BlockScoped&&(r={parent:n,kind:"block",bindings:new Map},a.set(e,r))}if(i.isVariableDeclaration(e)){let l=g(r,e);if(c(l,e.name),e.initializer&&i.isIdentifier(e.name))f.push({binding:p(l,e.name.text),initializer:e.initializer,scope:r,globalThisFetch:!1});else if(e.initializer&&i.isObjectBindingPattern(e.name))for(let s of e.name.elements)!w(s)||!i.isIdentifier(s.name)||f.push({binding:p(l,s.name.text),initializer:e.initializer,scope:r,globalThisFetch:!0})}i.forEachChild(e,l=>x(l,r))};i.forEachChild(t,e=>x(e,o));let b=(e,n,r)=>(e=S(e),i.isIdentifier(e)&&r.includes(e.text)&&!L(n,e.text)),E=(e,n)=>{if(e=S(e),i.isIdentifier(e)){let r=L(n,e.text);return r?r.aliasDepth:e.text==="fetch"?-1:void 0}if(i.isPropertyAccessExpression(e)&&e.name.text==="fetch"&&b(e.expression,n,["globalThis","window"])||i.isElementAccessExpression(e)&&e.argumentExpression&&d(e.argumentExpression)==="fetch"&&b(e.expression,n,["globalThis","window"]))return-1};for(let e of f)(e.globalThisFetch?b(e.initializer,e.scope,["globalThis"]):E(e.initializer,e.scope)===-1)&&(e.binding.aliasDepth=0);for(let e of f)e.binding.aliasDepth===void 0&&!e.globalThisFetch&&E(e.initializer,e.scope)===0&&(e.binding.aliasDepth=1);return{scopes:a,sourceScope:o}}function C(t){return typeof t=="string"&&t.length>0&&!t.startsWith("/")&&!/[\\\0*?{}]/.test(t)&&t.split("/").every(o=>o!==""&&o!=="."&&o!=="..")}function I(t){if(!Array.isArray(t.files)||t.files.length===0)throw new TypeError("CMS guard requires source files");let o=new Set;for(let c of t.files){if(!c||!C(c.path)||typeof c.text!="string"||o.has(c.path))throw new TypeError("CMS guard requires unique readable source paths");o.add(c.path)}let a=t.allowedProviderFiles??[];if(!Array.isArray(a)||new Set(a).size!==a.length||a.some(c=>!C(c)||!o.has(c)))throw new TypeError("CMS guard requires unique exact provider exemptions present in the scan");let f=new Set,p=[];for(let c of t.files){let g=c.path.split(".").at(-1)?.toLowerCase();if(!["ts","tsx","mts","cts","js","jsx","mjs","cjs","astro"].includes(g??""))throw new TypeError("CMS guard does not support this source syntax");let m=c.text;if(g==="astro"){let s=/^(?:\uFEFF)?---[^\S\r\n]*\r?\n((?:[^\n]*\n)*?)---[^\S\r\n]*(?:\r?\n|$)/.exec(m);if(m.replace(/^\uFEFF/,"").startsWith("---")&&!s)throw new TypeError("CMS guard cannot parse Astro frontmatter");m=s?`
2
+ `+s[1]:""}let w=g==="tsx"||g==="jsx"?i.ScriptKind.TSX:i.ScriptKind.TS,x=i.createSourceFile(c.path,m,i.ScriptTarget.Latest,!0,w);if(x.parseDiagnostics.length)throw new TypeError("CMS guard cannot parse source");let{scopes:E,sourceScope:e}=N(x),n=(s,h)=>{if(a.includes(c.path)){f.add(c.path);return}p.push({path:c.path,line:x.getLineAndCharacterOfPosition(s.getStart(x)).line+1,rule:h})},r=(s,h)=>{let y=h&&d(h);y!==void 0&&D.some(u=>y===u||y.startsWith(`${u}/`))&&n(s,"forbidden-provider-import")},l=(s,h)=>{let y=E.get(s)??h;if(i.isImportDeclaration(s)||i.isExportDeclaration(s))r(s,s.moduleSpecifier);else if(i.isImportEqualsDeclaration(s)&&i.isExternalModuleReference(s.moduleReference))r(s,s.moduleReference.expression);else if(i.isImportTypeNode(s)&&i.isLiteralTypeNode(s.argument))r(s,s.argument.literal);else if(i.isCallExpression(s)){let u=k(s.expression,y);if((s.expression.kind===i.SyntaxKind.ImportKeyword||u==="require"||u==="require.resolve")&&r(s,s.arguments[0]),u==="fetch"||u?.endsWith(".fetch")){let F=s.arguments[0]&&d(s.arguments[0]);F!==void 0&&T.test(F)&&n(s,"direct-provider-read")}}i.forEachChild(s,u=>l(u,y))};l(x,e)}if(a.some(c=>!f.has(c)))throw new TypeError("CMS guard has an unused provider exemption");return p}export{I as findCmsBoundaryViolations};
3
+ //# sourceMappingURL=guard.mjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/tooling/guard.ts"],
4
+ "sourcesContent": ["import ts from \"typescript\";\n\nexport type CmsSourceFile = { readonly path: string; readonly text: string };\nexport type CmsGuardViolation = { readonly path: string; readonly line: number; readonly rule: string };\n\nconst PROVIDERS = [\"next-sanity\", \"@sanity/client\", \"@sanity/webhook\"];\nconst RAW_READ = /\\.api(?:cdn)?\\.sanity\\.io|\\/data\\/query\\/|\\/query(?:[/?#]|$)/i;\n\n// These expressions preserve their operand (or the final comma operand).\n// Inspect the underlying syntax without evaluating consumer code.\nfunction unwrap(node: ts.Node): ts.Node {\n if (ts.isParenthesizedExpression(node) || ts.isAsExpression(node)\n || ts.isSatisfiesExpression(node) || ts.isTypeAssertionExpression(node)\n || ts.isNonNullExpression(node)) return unwrap(node.expression);\n if (ts.isBinaryExpression(node) && node.operatorToken.kind === ts.SyntaxKind.CommaToken) return unwrap(node.right);\n return node;\n}\n\n// Evaluate ordinary literal expressions, without executing consumer code.\nfunction literal(node: ts.Node): string | undefined {\n node = unwrap(node);\n if (ts.isStringLiteralLike(node)) return node.text;\n if (ts.isBinaryExpression(node) && node.operatorToken.kind === ts.SyntaxKind.PlusToken) {\n const left = literal(node.left);\n const right = literal(node.right);\n return left === undefined || right === undefined ? undefined : left + right;\n }\n if (ts.isTemplateExpression(node)) {\n return node.head.text + node.templateSpans.map((span) =>\n (literal(span.expression) ?? \"\\0\") + span.literal.text).join(\"\");\n }\n if (ts.isNewExpression(node) && ts.isIdentifier(node.expression)\n && [\"URL\", \"Request\"].includes(node.expression.text) && node.arguments?.[0]) return literal(node.arguments[0]);\n return undefined;\n}\n\ntype LexicalBinding = { aliasDepth?: 0 | 1 };\ntype LexicalScope = {\n readonly parent?: LexicalScope;\n readonly kind: \"source\" | \"function\" | \"block\";\n readonly bindings: Map<string, LexicalBinding>;\n};\n\nfunction lookupBinding(scope: LexicalScope, name: string): LexicalBinding | undefined {\n for (let current: LexicalScope | undefined = scope; current; current = current.parent) {\n const binding = current.bindings.get(name);\n if (binding) return binding;\n }\n return undefined;\n}\n\nfunction callName(node: ts.Node, scope?: LexicalScope): string | undefined {\n node = unwrap(node);\n if (ts.isIdentifier(node)) {\n const binding = scope && lookupBinding(scope, node.text);\n if (binding) return binding.aliasDepth === undefined ? undefined : \"fetch\";\n return node.text;\n }\n if (ts.isPropertyAccessExpression(node)) {\n const owner = callName(node.expression, scope);\n return owner ? `${owner}.${node.name.text}` : undefined;\n }\n if (ts.isElementAccessExpression(node) && node.argumentExpression) {\n const owner = callName(node.expression, scope);\n const key = literal(node.argumentExpression);\n return owner && key ? `${owner}.${key}` : undefined;\n }\n return undefined;\n}\n\nfunction lexicalScopes(source: ts.SourceFile): {\n readonly scopes: ReadonlyMap<ts.Node, LexicalScope>;\n readonly sourceScope: LexicalScope;\n} {\n const sourceScope: LexicalScope = { kind: \"source\", bindings: new Map() };\n const scopes = new Map<ts.Node, LexicalScope>([[source, sourceScope]]);\n const candidates: Array<{\n readonly binding: LexicalBinding;\n readonly initializer: ts.Expression;\n readonly scope: LexicalScope;\n readonly globalThisFetch: boolean;\n }> = [];\n const bind = (scope: LexicalScope, name: string) => {\n const existing = scope.bindings.get(name);\n if (existing) return existing;\n const binding: LexicalBinding = {};\n scope.bindings.set(name, binding);\n return binding;\n };\n const bindName = (scope: LexicalScope, name: ts.BindingName) => {\n if (ts.isIdentifier(name)) bind(scope, name.text);\n else for (const element of name.elements) if (!ts.isOmittedExpression(element)) bindName(scope, element.name);\n };\n const variableScope = (scope: LexicalScope, declaration: ts.VariableDeclaration) => {\n if (declaration.parent.flags & ts.NodeFlags.BlockScoped) return scope;\n let current = scope;\n while (current.kind === \"block\" && current.parent) current = current.parent;\n return current;\n };\n const bindImports = (node: ts.ImportDeclaration, scope: LexicalScope) => {\n const clause = node.importClause;\n if (!clause) return;\n if (clause.name) bind(scope, clause.name.text);\n const imports = clause.namedBindings;\n if (imports && ts.isNamespaceImport(imports)) bind(scope, imports.name.text);\n else if (imports) for (const element of imports.elements) bind(scope, element.name.text);\n };\n const destructuredFetch = (element: ts.BindingElement) => {\n const property = element.propertyName ?? element.name;\n return (ts.isIdentifier(property) || ts.isStringLiteralLike(property)) && property.text === \"fetch\";\n };\n\n const collect = (node: ts.Node, incoming: LexicalScope) => {\n let scope = incoming;\n if (ts.isFunctionDeclaration(node) && node.name) bind(scope, node.name.text);\n else if (ts.isClassDeclaration(node) && node.name) bind(scope, node.name.text);\n else if (ts.isEnumDeclaration(node)) bind(scope, node.name.text);\n else if (ts.isImportDeclaration(node)) bindImports(node, scope);\n else if (ts.isImportEqualsDeclaration(node)) bind(scope, node.name.text);\n\n if (ts.isFunctionLike(node)) {\n scope = { parent: incoming, kind: \"function\", bindings: new Map() };\n scopes.set(node, scope);\n if (ts.isFunctionExpression(node) && node.name) bind(scope, node.name.text);\n for (const parameter of node.parameters) bindName(scope, parameter.name);\n } else if (ts.isCatchClause(node)) {\n scope = { parent: incoming, kind: \"block\", bindings: new Map() };\n scopes.set(node, scope);\n if (node.variableDeclaration) bindName(scope, node.variableDeclaration.name);\n } else if (ts.isBlock(node)) {\n scope = { parent: incoming, kind: \"block\", bindings: new Map() };\n scopes.set(node, scope);\n } else if (ts.isForStatement(node) || ts.isForInStatement(node) || ts.isForOfStatement(node)) {\n const initializer = node.initializer;\n if (initializer && ts.isVariableDeclarationList(initializer)\n && initializer.flags & ts.NodeFlags.BlockScoped) {\n scope = { parent: incoming, kind: \"block\", bindings: new Map() };\n scopes.set(node, scope);\n }\n }\n\n if (ts.isVariableDeclaration(node)) {\n const target = variableScope(scope, node);\n bindName(target, node.name);\n if (node.initializer && ts.isIdentifier(node.name)) {\n candidates.push({\n binding: bind(target, node.name.text), initializer: node.initializer, scope,\n globalThisFetch: false,\n });\n } else if (node.initializer && ts.isObjectBindingPattern(node.name)) {\n for (const element of node.name.elements) {\n if (!destructuredFetch(element) || !ts.isIdentifier(element.name)) continue;\n candidates.push({\n binding: bind(target, element.name.text), initializer: node.initializer, scope,\n globalThisFetch: true,\n });\n }\n }\n }\n ts.forEachChild(node, (child) => collect(child, scope));\n };\n ts.forEachChild(source, (node) => collect(node, sourceScope));\n\n const isUnboundGlobal = (node: ts.Node, scope: LexicalScope, names: readonly string[]) => {\n node = unwrap(node);\n return ts.isIdentifier(node) && names.includes(node.text) && !lookupBinding(scope, node.text);\n };\n const fetchReferenceDepth = (node: ts.Node, scope: LexicalScope): -1 | 0 | 1 | undefined => {\n node = unwrap(node);\n if (ts.isIdentifier(node)) {\n const binding = lookupBinding(scope, node.text);\n if (binding) return binding.aliasDepth;\n return node.text === \"fetch\" ? -1 : undefined;\n }\n if (ts.isPropertyAccessExpression(node) && node.name.text === \"fetch\"\n && isUnboundGlobal(node.expression, scope, [\"globalThis\", \"window\"])) return -1;\n if (ts.isElementAccessExpression(node) && node.argumentExpression\n && literal(node.argumentExpression) === \"fetch\"\n && isUnboundGlobal(node.expression, scope, [\"globalThis\", \"window\"])) return -1;\n return undefined;\n };\n\n // Classify direct aliases first, then exactly one alias-to-alias hop. Binding\n // collection happens before classification so nearer non-fetch declarations\n // and parameters correctly shadow aliases in an outer scope.\n for (const candidate of candidates) {\n const direct = candidate.globalThisFetch\n ? isUnboundGlobal(candidate.initializer, candidate.scope, [\"globalThis\"])\n : fetchReferenceDepth(candidate.initializer, candidate.scope) === -1;\n if (direct) candidate.binding.aliasDepth = 0;\n }\n for (const candidate of candidates) {\n if (candidate.binding.aliasDepth === undefined && !candidate.globalThisFetch\n && fetchReferenceDepth(candidate.initializer, candidate.scope) === 0) candidate.binding.aliasDepth = 1;\n }\n return { scopes, sourceScope };\n}\n\nfunction exactPath(path: unknown): path is string {\n return typeof path === \"string\" && path.length > 0 && !path.startsWith(\"/\")\n && !/[\\\\\\0*?{}]/.test(path)\n && path.split(\"/\").every((part) => part !== \"\" && part !== \".\" && part !== \"..\");\n}\n\nexport function findCmsBoundaryViolations(options: {\n files: readonly CmsSourceFile[];\n allowedProviderFiles?: readonly string[];\n}): readonly CmsGuardViolation[] {\n if (!Array.isArray(options.files) || options.files.length === 0) throw new TypeError(\"CMS guard requires source files\");\n const paths = new Set<string>();\n for (const file of options.files) {\n if (!file || !exactPath(file.path) || typeof file.text !== \"string\" || paths.has(file.path)) {\n throw new TypeError(\"CMS guard requires unique readable source paths\");\n }\n paths.add(file.path);\n }\n const allowed = options.allowedProviderFiles ?? [];\n if (!Array.isArray(allowed) || new Set(allowed).size !== allowed.length\n || allowed.some((path) => !exactPath(path) || !paths.has(path))) {\n throw new TypeError(\"CMS guard requires unique exact provider exemptions present in the scan\");\n }\n const used = new Set<string>();\n const violations: CmsGuardViolation[] = [];\n for (const file of options.files) {\n const extension = file.path.split(\".\").at(-1)?.toLowerCase();\n if (![\"ts\", \"tsx\", \"mts\", \"cts\", \"js\", \"jsx\", \"mjs\", \"cjs\", \"astro\"].includes(extension ?? \"\")) {\n throw new TypeError(\"CMS guard does not support this source syntax\");\n }\n let text = file.text;\n if (extension === \"astro\") {\n // Keep line positions from the complete file, but parse only frontmatter.\n const frontmatter = /^(?:\\uFEFF)?---[^\\S\\r\\n]*\\r?\\n((?:[^\\n]*\\n)*?)---[^\\S\\r\\n]*(?:\\r?\\n|$)/.exec(text);\n if (text.replace(/^\\uFEFF/, \"\").startsWith(\"---\") && !frontmatter) {\n throw new TypeError(\"CMS guard cannot parse Astro frontmatter\");\n }\n text = frontmatter ? \"\\n\" + frontmatter[1] : \"\";\n }\n const kind = extension === \"tsx\" || extension === \"jsx\" ? ts.ScriptKind.TSX : ts.ScriptKind.TS;\n const source = ts.createSourceFile(file.path, text, ts.ScriptTarget.Latest, true, kind);\n const diagnostics = (source as ts.SourceFile & { parseDiagnostics: readonly ts.Diagnostic[] }).parseDiagnostics;\n if (diagnostics.length) throw new TypeError(\"CMS guard cannot parse source\");\n // Plan section 6 covers literal/template fetch URLs and ordinary lexical\n // aliases, without resolving computed transports or computed URL values.\n const { scopes, sourceScope } = lexicalScopes(source);\n const report = (node: ts.Node, rule: string) => {\n if (allowed.includes(file.path)) { used.add(file.path); return; }\n violations.push({ path: file.path, line: source.getLineAndCharacterOfPosition(node.getStart(source)).line + 1, rule });\n };\n const checkImport = (node: ts.Node, argument: ts.Node | undefined) => {\n const specifier = argument && literal(argument);\n if (specifier !== undefined && PROVIDERS.some((name) => specifier === name || specifier.startsWith(`${name}/`))) {\n report(node, \"forbidden-provider-import\");\n }\n };\n const visit = (node: ts.Node, incoming: LexicalScope) => {\n const scope = scopes.get(node) ?? incoming;\n if (ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) checkImport(node, node.moduleSpecifier);\n else if (ts.isImportEqualsDeclaration(node) && ts.isExternalModuleReference(node.moduleReference)) {\n checkImport(node, node.moduleReference.expression);\n } else if (ts.isImportTypeNode(node) && ts.isLiteralTypeNode(node.argument)) checkImport(node, node.argument.literal);\n else if (ts.isCallExpression(node)) {\n const name = callName(node.expression, scope);\n if (node.expression.kind === ts.SyntaxKind.ImportKeyword || name === \"require\" || name === \"require.resolve\") {\n checkImport(node, node.arguments[0]);\n }\n if (name === \"fetch\" || name?.endsWith(\".fetch\")) {\n const url = node.arguments[0] && literal(node.arguments[0]);\n if (url !== undefined && RAW_READ.test(url)) report(node, \"direct-provider-read\");\n }\n }\n ts.forEachChild(node, (child) => visit(child, scope));\n };\n visit(source, sourceScope);\n }\n if (allowed.some((path) => !used.has(path))) throw new TypeError(\"CMS guard has an unused provider exemption\");\n return violations;\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["ts", "PROVIDERS", "RAW_READ", "unwrap", "node", "literal", "left", "right", "span", "lookupBinding", "scope", "name", "current", "binding", "callName", "owner", "key", "lexicalScopes", "source", "sourceScope", "scopes", "candidates", "bind", "existing", "bindName", "element", "variableScope", "declaration", "bindImports", "clause", "imports", "destructuredFetch", "property", "collect", "incoming", "parameter", "initializer", "target", "child", "isUnboundGlobal", "names", "fetchReferenceDepth", "candidate", "exactPath", "path", "part", "findCmsBoundaryViolations", "options", "paths", "file", "allowed", "used", "violations", "extension", "text", "frontmatter", "kind", "report", "rule", "checkImport", "argument", "specifier", "visit", "url"]
7
+ }
package/dist/index.cjs ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";var f=Object.defineProperty;var C=Object.getOwnPropertyDescriptor;var R=Object.getOwnPropertyNames;var v=Object.prototype.hasOwnProperty;var _=(e,t)=>{for(var o in t)f(e,o,{get:t[o],enumerable:!0})},A=(e,t,o,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of R(t))!v.call(e,n)&&n!==o&&f(e,n,{get:()=>t[n],enumerable:!(r=C(t,n))||r.enumerable});return e};var I=e=>A(f({},"__esModule",{value:!0}),e);var z={};_(z,{CMS_CONTRACT_VERSION:()=>k,SITE_HERO:()=>l,SITE_HERO_LAYOUTS:()=>m,SITE_HERO_LAYOUT_WARNING:()=>h,SITE_HERO_TAGLINE_MAX_LENGTH:()=>T,cmsTags:()=>H,defineCmsQuery:()=>c,parseSiteHero:()=>S,siteHeroQuery:()=>E,validateSiteHeroTaglines:()=>p,validateTextDocument:()=>y});module.exports=I(z);var L=/^[A-Za-z][A-Za-z0-9_]{0,63}$/,j=/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/,D=/^[a-z0-9]{6,12}\.[a-z][a-z0-9_-]{0,63}$/;function x(e){if(!e||!Array.isArray(e.types)||e.types.length===0)throw new TypeError("CMS dependencies require document types");let t=[...e.types];for(let r of t)if(typeof r!="string"||!L.test(r))throw new TypeError("Invalid CMS document type");if(e.ids!==void 0&&!Array.isArray(e.ids))throw new TypeError("Invalid CMS document IDs");let o=e.ids===void 0?void 0:[...e.ids];for(let r of o??[])if(typeof r!="string"||!j.test(r)||/^(drafts|versions)\./.test(r))throw new TypeError("Invalid CMS document ID");if(new Set(t).size+new Set(o).size>128)throw new TypeError("Too many CMS tags");return Object.freeze({types:Object.freeze(t),...o===void 0?{}:{ids:Object.freeze(o)}})}function c(e){if(!e||typeof e.name!="string"||!/^[A-Za-z][A-Za-z0-9_.-]{0,127}$/.test(e.name)||typeof e.query!="string"||e.query.trim()===""||typeof e.resolve!="function")throw new TypeError("Invalid CMS query configuration");let t=e.params;if(!t||typeof t!="object"||Object.getPrototypeOf(t)!==Object.prototype&&Object.getPrototypeOf(t)!==null)throw new TypeError("CMS params must be plain JSON scalars");let o={};for(let r of Reflect.ownKeys(t)){let n=Object.getOwnPropertyDescriptor(t,r);if(typeof r!="string"||!n||!Object.hasOwn(n,"value")||!n.enumerable)throw new TypeError("CMS params must be plain JSON scalars");let i=n.value;if(!(i===null||typeof i=="string"||typeof i=="boolean"||typeof i=="number"&&Number.isFinite(i)))throw new TypeError("CMS params must be plain JSON scalars");Object.defineProperty(o,r,{value:i,enumerable:!0})}return Object.freeze({name:e.name,query:e.query,params:Object.freeze(o),dependencies:x(e.dependencies),resolve:e.resolve})}function H(e,t){if(typeof e!="string"||e.length>77||!D.test(e))throw new TypeError("Invalid CMS scope");let o=x(t),r=[...[...new Set(o.types)].sort().map(n=>`cms:${e}:type:${n}`),...[...new Set(o.ids??[])].sort().map(n=>`cms:${e}:id:${n}`)];return Object.freeze(r)}var m=["columns","rows"],T=36,h="siteHero.layout.defaulted",k=1,l={key:"hero",documentId:"siteHero",documentType:"siteHero",fields:[{name:"heading",label:"Heading",noun:{article:"a",word:"heading"},kind:"single-line",required:!0,minLength:1,maxLength:80,noTrailingFullStop:!0},{name:"intro",label:"Introduction",noun:{article:"an",word:"introduction"},kind:"multi-line",required:!0,minLength:1,maxLength:320}],copy:{currentTitle:"On your site now",editTitle:"Edit and publish",empty:"Your site has no hero text yet. Whatever you publish below becomes its first version."}};function $(e,t){let{article:o,word:r}=e.noun;if(t.length===0)return e.required?`Enter ${o} ${r}.`:null;let n=[...t].length;return n<e.minLength?`Make the ${r} at least ${String(e.minLength)} characters. It is ${String(n)} now.`:n>e.maxLength?`Keep the ${r} to ${String(e.maxLength)} characters or fewer. It is ${String(n)} now.`:t.includes("\u2014")?`The ${r} contains a long dash. Use a comma, a colon or a short dash instead.`:e.kind==="single-line"&&/[\r\n]/.test(t)?`The ${r} must be a single line.`:e.noTrailingFullStop===!0&&t.endsWith(".")?`${r.charAt(0).toUpperCase()}${r.slice(1)}s on your site do not end with a full stop. Remove the last full stop.`:null}function y(e,t){let o={},r={};for(let n of e.fields){let i=Object.hasOwn(t,n.name)?t[n.name]:void 0,a=typeof i=="string"?i.trim():"",s=$(n,a);Object.defineProperty(s===null?o:r,n.name,{value:s??a,enumerable:!0})}return Object.keys(r).length>0?{ok:!1,errors:r}:{ok:!0,value:o}}function p(e){if(!Array.isArray(e))return{ok:!1,error:"Enter a list of taglines."};let t=l.fields[0];if(!t)throw new TypeError("Missing siteHero heading definition");let o={...t,name:"text",noun:{article:"a",word:"tagline"},maxLength:T},r=[],n=new Set;for(let i of e){if(!i||typeof i!="object"||Array.isArray(i))return{ok:!1,error:"Enter a key and text for each tagline."};let a=i,s=Object.hasOwn(a,"key")?a.key:void 0;if(typeof s!="string"||!/^[a-z][a-z0-9_-]{0,31}$/.test(s))return{ok:!1,error:"Use a lowercase key of 1 to 32 letters, numbers, short dashes or underscores, starting with a letter."};if(n.has(s))return{ok:!1,error:"Use each tagline key only once."};let d=y({...l,fields:[o]},a);if(!d.ok)return{ok:!1,error:d.errors.text??"Invalid tagline."};n.add(s),r.push({key:s,text:d.value.text??""})}return{ok:!0,value:r}}function O(e){return m.some(t=>e===t)}function S(e,t){let o=y(l,t),r=p(t.taglines??[]);if(!o.ok||!r.ok||t.layout!==void 0&&!O(t.layout))throw new TypeError("Invalid siteHero fallback");let n={heading:o.value.heading??"",intro:o.value.intro??"",layout:t.layout??"columns",taglines:r.value};if(e===null||typeof e!="object"||Array.isArray(e))return{data:n,fallbackFields:["$"]};let i=e,a=y(l,i),s=a.ok?[]:Object.keys(a.errors);for(let u of l.fields)if(!s.includes(u.name)){let b=a.ok?a.value[u.name]:i[u.name];typeof b=="string"&&(u.name==="heading"||u.name==="intro")&&(n[u.name]=b.trim())}let d=Object.hasOwn(i,"layout")?i.layout:void 0,g=[];O(d)?n.layout=d:(n.layout="columns",s.push("layout"),g.push(h));let w=p(Object.hasOwn(i,"taglines")?i.taglines:void 0);return w.ok?n.taglines=w.value:s.push("taglines"),{data:n,fallbackFields:s.length===l.fields.length+2?["$"]:s,...g.length>0?{warnings:g}:{}}}function E(){return c({name:"siteHero.v1",query:'*[_id == $id && _type == "siteHero"][0]{heading,intro,layout,taglines}',params:{id:"siteHero"},dependencies:{types:["siteHero"],ids:["siteHero"]},resolve:S})}0&&(module.exports={CMS_CONTRACT_VERSION,SITE_HERO,SITE_HERO_LAYOUTS,SITE_HERO_LAYOUT_WARNING,SITE_HERO_TAGLINE_MAX_LENGTH,cmsTags,defineCmsQuery,parseSiteHero,siteHeroQuery,validateSiteHeroTaglines,validateTextDocument});
2
+ //# sourceMappingURL=index.cjs.map