@growth-labs/seo 0.1.5 → 0.2.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 (119) hide show
  1. package/README.md +142 -54
  2. package/dist/bindings.d.ts +127 -0
  3. package/dist/bindings.d.ts.map +1 -0
  4. package/dist/bindings.js +11 -0
  5. package/dist/bindings.js.map +1 -0
  6. package/dist/cron/prune-aeo-r2.d.ts +36 -0
  7. package/dist/cron/prune-aeo-r2.d.ts.map +1 -0
  8. package/dist/cron/prune-aeo-r2.js +94 -0
  9. package/dist/cron/prune-aeo-r2.js.map +1 -0
  10. package/dist/durable-objects/aeo-revalidation-coord.d.ts +69 -0
  11. package/dist/durable-objects/aeo-revalidation-coord.d.ts.map +1 -0
  12. package/dist/durable-objects/aeo-revalidation-coord.js +177 -0
  13. package/dist/durable-objects/aeo-revalidation-coord.js.map +1 -0
  14. package/dist/index.d.ts +1 -1
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +79 -12
  17. package/dist/index.js.map +1 -1
  18. package/dist/middleware/seo.d.ts +44 -4
  19. package/dist/middleware/seo.d.ts.map +1 -1
  20. package/dist/middleware/seo.js +237 -41
  21. package/dist/middleware/seo.js.map +1 -1
  22. package/dist/options.d.ts +1293 -6
  23. package/dist/options.d.ts.map +1 -1
  24. package/dist/options.js +238 -1
  25. package/dist/options.js.map +1 -1
  26. package/dist/routes/aeo-twin.d.ts +5 -0
  27. package/dist/routes/aeo-twin.d.ts.map +1 -0
  28. package/dist/routes/aeo-twin.js +108 -0
  29. package/dist/routes/aeo-twin.js.map +1 -0
  30. package/dist/routes/apple-news.d.ts +4 -0
  31. package/dist/routes/apple-news.d.ts.map +1 -0
  32. package/dist/routes/apple-news.js +28 -0
  33. package/dist/routes/apple-news.js.map +1 -0
  34. package/dist/routes/llms-full.d.ts +4 -0
  35. package/dist/routes/llms-full.d.ts.map +1 -0
  36. package/dist/routes/llms-full.js +29 -0
  37. package/dist/routes/llms-full.js.map +1 -0
  38. package/dist/routes/revalidate.d.ts +16 -0
  39. package/dist/routes/revalidate.d.ts.map +1 -0
  40. package/dist/routes/revalidate.js +243 -0
  41. package/dist/routes/revalidate.js.map +1 -0
  42. package/dist/routes/rss.d.ts.map +1 -1
  43. package/dist/routes/rss.js +4 -1
  44. package/dist/routes/rss.js.map +1 -1
  45. package/dist/routes/sitemap-markdown.d.ts +4 -0
  46. package/dist/routes/sitemap-markdown.d.ts.map +1 -0
  47. package/dist/routes/sitemap-markdown.js +32 -0
  48. package/dist/routes/sitemap-markdown.js.map +1 -0
  49. package/dist/types.d.ts +16 -2
  50. package/dist/types.d.ts.map +1 -1
  51. package/dist/utils/aeo-summary.d.ts +35 -0
  52. package/dist/utils/aeo-summary.d.ts.map +1 -0
  53. package/dist/utils/aeo-summary.js +141 -0
  54. package/dist/utils/aeo-summary.js.map +1 -0
  55. package/dist/utils/aeo-twin-emitter.d.ts +79 -0
  56. package/dist/utils/aeo-twin-emitter.d.ts.map +1 -0
  57. package/dist/utils/aeo-twin-emitter.js +99 -0
  58. package/dist/utils/aeo-twin-emitter.js.map +1 -0
  59. package/dist/utils/aeo.d.ts +62 -12
  60. package/dist/utils/aeo.d.ts.map +1 -1
  61. package/dist/utils/aeo.js +187 -26
  62. package/dist/utils/aeo.js.map +1 -1
  63. package/dist/utils/apple-news-anf.d.ts +38 -0
  64. package/dist/utils/apple-news-anf.d.ts.map +1 -0
  65. package/dist/utils/apple-news-anf.js +120 -0
  66. package/dist/utils/apple-news-anf.js.map +1 -0
  67. package/dist/utils/apple-news-rss.d.ts +31 -0
  68. package/dist/utils/apple-news-rss.d.ts.map +1 -0
  69. package/dist/utils/apple-news-rss.js +103 -0
  70. package/dist/utils/apple-news-rss.js.map +1 -0
  71. package/dist/utils/content-filter.d.ts +52 -0
  72. package/dist/utils/content-filter.d.ts.map +1 -0
  73. package/dist/utils/content-filter.js +75 -0
  74. package/dist/utils/content-filter.js.map +1 -0
  75. package/dist/utils/crawler-class.d.ts +39 -0
  76. package/dist/utils/crawler-class.d.ts.map +1 -0
  77. package/dist/utils/crawler-class.js +127 -0
  78. package/dist/utils/crawler-class.js.map +1 -0
  79. package/dist/utils/effective-auth.d.ts +28 -0
  80. package/dist/utils/effective-auth.d.ts.map +1 -0
  81. package/dist/utils/effective-auth.js +33 -0
  82. package/dist/utils/effective-auth.js.map +1 -0
  83. package/dist/utils/fcrdns.d.ts +73 -0
  84. package/dist/utils/fcrdns.d.ts.map +1 -0
  85. package/dist/utils/fcrdns.js +219 -0
  86. package/dist/utils/fcrdns.js.map +1 -0
  87. package/dist/utils/fresh-layer.d.ts +53 -0
  88. package/dist/utils/fresh-layer.d.ts.map +1 -0
  89. package/dist/utils/fresh-layer.js +147 -0
  90. package/dist/utils/fresh-layer.js.map +1 -0
  91. package/dist/utils/index.d.ts +14 -3
  92. package/dist/utils/index.d.ts.map +1 -1
  93. package/dist/utils/index.js +14 -3
  94. package/dist/utils/index.js.map +1 -1
  95. package/dist/utils/json-ld/article.d.ts +13 -1
  96. package/dist/utils/json-ld/article.d.ts.map +1 -1
  97. package/dist/utils/json-ld/article.js +37 -8
  98. package/dist/utils/json-ld/article.js.map +1 -1
  99. package/dist/utils/llms-full.d.ts +29 -0
  100. package/dist/utils/llms-full.d.ts.map +1 -0
  101. package/dist/utils/llms-full.js +67 -0
  102. package/dist/utils/llms-full.js.map +1 -0
  103. package/dist/utils/meta.d.ts +4 -1
  104. package/dist/utils/meta.d.ts.map +1 -1
  105. package/dist/utils/meta.js +25 -2
  106. package/dist/utils/meta.js.map +1 -1
  107. package/dist/utils/sitemap-markdown.d.ts +24 -0
  108. package/dist/utils/sitemap-markdown.d.ts.map +1 -0
  109. package/dist/utils/sitemap-markdown.js +57 -0
  110. package/dist/utils/sitemap-markdown.js.map +1 -0
  111. package/dist/utils/staleness.d.ts +27 -0
  112. package/dist/utils/staleness.d.ts.map +1 -0
  113. package/dist/utils/staleness.js +46 -0
  114. package/dist/utils/staleness.js.map +1 -0
  115. package/dist/utils/validation.d.ts +41 -0
  116. package/dist/utils/validation.d.ts.map +1 -1
  117. package/dist/utils/validation.js +78 -0
  118. package/dist/utils/validation.js.map +1 -1
  119. package/package.json +13 -1
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Compute the effective auth segment given a crawler class and a raw consumer auth segment.
3
+ *
4
+ * This function encodes the policy from spec "Effective auth segment" (lines
5
+ * "crawler class overrides member cookies"):
6
+ *
7
+ * - Verified search crawler → 'search-full' (regardless of cookies). Gets the
8
+ * sanctioned paywall-marked full body under Flexible Sampling.
9
+ * - LLM training crawler → caller should 403 before calling this; if it does reach
10
+ * here, we surface 'anon' defensively.
11
+ * - User-directed LLM agent → 'anon', ALWAYS. Even if member cookies are present.
12
+ * Load-bearing: ChatGPT-User / Claude-User forward responses to a third-party
13
+ * model that may retain them; we cannot verify the cookies belong to a paid
14
+ * subscriber, and leaking gated content to a third-party LLM defeats gating.
15
+ * - Anonymous → raw as-is.
16
+ *
17
+ * Consumers use `effectiveAuthSegment` (NOT raw `authSegment`) in cache keys so
18
+ * that cache segmentation reflects the crawler-class override.
19
+ */
20
+ export function computeEffectiveAuthSegment(crawlerClass, rawAuthSegment) {
21
+ switch (crawlerClass) {
22
+ case 'verifiedSearchCrawler':
23
+ return 'search-full';
24
+ case 'llmTrainingCrawler':
25
+ // Caller should reject with 403 before computing this; defensive fallback.
26
+ return 'anon';
27
+ case 'userDirectedLlmAgent':
28
+ return 'anon'; // Override: never 'member', even with cookies.
29
+ case 'anonymous':
30
+ return rawAuthSegment;
31
+ }
32
+ }
33
+ //# sourceMappingURL=effective-auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"effective-auth.js","sourceRoot":"","sources":["../../src/utils/effective-auth.ts"],"names":[],"mappings":"AASA;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,2BAA2B,CAC1C,YAA0B,EAC1B,cAA8B;IAE9B,QAAQ,YAAY,EAAE,CAAC;QACtB,KAAK,uBAAuB;YAC3B,OAAO,aAAa,CAAA;QACrB,KAAK,oBAAoB;YACxB,2EAA2E;YAC3E,OAAO,MAAM,CAAA;QACd,KAAK,sBAAsB;YAC1B,OAAO,MAAM,CAAA,CAAC,+CAA+C;QAC9D,KAAK,WAAW;YACf,OAAO,cAAc,CAAA;IACvB,CAAC;AACF,CAAC"}
@@ -0,0 +1,73 @@
1
+ export interface DnsAnswer {
2
+ name: string;
3
+ type: number;
4
+ TTL: number;
5
+ data: string;
6
+ }
7
+ export interface DnsResolver {
8
+ /**
9
+ * Perform a DoH-JSON-style DNS query.
10
+ *
11
+ * Returns the Answer array or null on failure. `type` is an RFC 1035
12
+ * numeric record type (12 = PTR, 1 = A, 28 = AAAA).
13
+ *
14
+ * Production implementation: {@link createDohResolver}. Tests inject a mock.
15
+ */
16
+ query(name: string, type: 'PTR' | 'A' | 'AAAA'): Promise<DnsAnswer[] | null>;
17
+ }
18
+ export interface FcrdnsVerifyInput {
19
+ clientIp: string;
20
+ trustedSuffixes: readonly string[];
21
+ }
22
+ export type FcrdnsVerifier = (input: FcrdnsVerifyInput) => Promise<boolean>;
23
+ /**
24
+ * Create an FCrDNS verifier. Returns a function that, given an IP and suffix list,
25
+ * returns true iff the IP's forward-confirmed reverse DNS lands on a trusted suffix.
26
+ *
27
+ * The returned verifier carries its own cache; create one per Worker isolate.
28
+ *
29
+ * @param resolver DNS resolver. Default in production: DoH against 1.1.1.1.
30
+ * Tests inject a mock.
31
+ * @param now time source (for testable TTL expiry). Default: Date.now.
32
+ */
33
+ export declare function createFcrdnsVerifier(resolver: DnsResolver, now?: () => number): FcrdnsVerifier;
34
+ /**
35
+ * Create a DoH-JSON resolver that queries Cloudflare's 1.1.1.1. One subrequest per
36
+ * DNS lookup. Not a singleton — resolver has no persistent state of its own.
37
+ *
38
+ * Docs: https://developers.cloudflare.com/1.1.1.1/encryption/dns-over-https/make-api-requests/dns-json/
39
+ */
40
+ export declare function createDohResolver(baseUrl?: string, fetchImpl?: typeof fetch): DnsResolver;
41
+ /**
42
+ * Convert an IP address to its in-addr.arpa / ip6.arpa PTR query name.
43
+ * Returns null for malformed input.
44
+ */
45
+ declare function reverseIpToArpa(ip: string): string | null;
46
+ /**
47
+ * Expand an IPv6 address to its full 32-hex-character form (colon-separated groups
48
+ * of 4). Handles "::" compression. Returns null for malformed input.
49
+ */
50
+ declare function expandIpv6(ip: string): string | null;
51
+ declare function matchesAnyTrustedSuffix(hostname: string, suffixes: readonly string[]): boolean;
52
+ /**
53
+ * Normalize an IP for comparison. IPv4 addresses pass through; IPv6 are canonicalized
54
+ * by expanding "::" and stripping leading zeros in each group.
55
+ */
56
+ declare function normalizeIp(ip: string): string;
57
+ declare class FcrdnsCache {
58
+ private readonly now;
59
+ private readonly entries;
60
+ constructor(now: () => number);
61
+ get(clientIp: string): boolean | undefined;
62
+ setPositive(clientIp: string, hostname: string, ttlMs: number): void;
63
+ setNegative(clientIp: string): void;
64
+ }
65
+ export declare const _internals: {
66
+ reverseIpToArpa: typeof reverseIpToArpa;
67
+ expandIpv6: typeof expandIpv6;
68
+ matchesAnyTrustedSuffix: typeof matchesAnyTrustedSuffix;
69
+ normalizeIp: typeof normalizeIp;
70
+ FcrdnsCache: typeof FcrdnsCache;
71
+ };
72
+ export {};
73
+ //# sourceMappingURL=fcrdns.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fcrdns.d.ts","sourceRoot":"","sources":["../../src/utils/fcrdns.ts"],"names":[],"mappings":"AAgBA,MAAM,WAAW,SAAS;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;CACZ;AAED,MAAM,WAAW,WAAW;IAC3B;;;;;;;OAOG;IACH,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,GAAG,GAAG,GAAG,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,GAAG,IAAI,CAAC,CAAA;CAC5E;AAID,MAAM,WAAW,iBAAiB;IACjC,QAAQ,EAAE,MAAM,CAAA;IAChB,eAAe,EAAE,SAAS,MAAM,EAAE,CAAA;CAClC;AAED,MAAM,MAAM,cAAc,GAAG,CAAC,KAAK,EAAE,iBAAiB,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;AAE3E;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CACnC,QAAQ,EAAE,WAAW,EACrB,GAAG,GAAE,MAAM,MAAiB,GAC1B,cAAc,CAiEhB;AAID;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAChC,OAAO,SAA8B,EACrC,SAAS,GAAE,OAAO,KAAa,GAC7B,WAAW,CAcb;AAID;;;GAGG;AACH,iBAAS,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAYlD;AAED;;;GAGG;AACH,iBAAS,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAiB7C;AAED,iBAAS,uBAAuB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAMvF;AAMD;;;GAGG;AACH,iBAAS,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CASvC;AAUD,cAAM,WAAW;IAGJ,OAAO,CAAC,QAAQ,CAAC,GAAG;IAFhC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgC;gBAE3B,GAAG,EAAE,MAAM,MAAM;IAE9C,GAAG,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS;IAU1C,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAQpE,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;CAMnC;AAID,eAAO,MAAM,UAAU;;;;;;CAMtB,CAAA"}
@@ -0,0 +1,219 @@
1
+ // Forward-confirmed reverse DNS verification. Used to classify incoming requests
2
+ // as verified search crawlers when Cloudflare Bot Management isn't available.
3
+ //
4
+ // Algorithm (spec "FCrDNS algorithm"):
5
+ // 1. PTR lookup on the client IP → hostname
6
+ // 2. Hostname must end with a trusted suffix, on a dot boundary
7
+ // 3. A/AAAA lookup on that hostname → IPs
8
+ // 4. Client IP must be in the result set
9
+ //
10
+ // Caching (spec "FCrDNS cache semantics"):
11
+ // - Positive: keyed by (clientIp, matchedHostname); TTL = min(10min, rDNS TTL, fDNS TTL)
12
+ // - Negative: 60s fixed TTL
13
+ // - Process-local only; do NOT persist across deploys
14
+ /**
15
+ * Create an FCrDNS verifier. Returns a function that, given an IP and suffix list,
16
+ * returns true iff the IP's forward-confirmed reverse DNS lands on a trusted suffix.
17
+ *
18
+ * The returned verifier carries its own cache; create one per Worker isolate.
19
+ *
20
+ * @param resolver DNS resolver. Default in production: DoH against 1.1.1.1.
21
+ * Tests inject a mock.
22
+ * @param now time source (for testable TTL expiry). Default: Date.now.
23
+ */
24
+ export function createFcrdnsVerifier(resolver, now = Date.now) {
25
+ const cache = new FcrdnsCache(now);
26
+ return async ({ clientIp, trustedSuffixes }) => {
27
+ // Cache check (positive or negative).
28
+ const cached = cache.get(clientIp);
29
+ if (cached !== undefined)
30
+ return cached;
31
+ try {
32
+ // 1. Reverse DNS lookup.
33
+ const ptrName = reverseIpToArpa(clientIp);
34
+ if (!ptrName) {
35
+ cache.setNegative(clientIp);
36
+ return false;
37
+ }
38
+ const ptrAnswers = await resolver.query(ptrName, 'PTR');
39
+ if (!ptrAnswers || ptrAnswers.length === 0) {
40
+ cache.setNegative(clientIp);
41
+ return false;
42
+ }
43
+ // 2. Suffix match on any PTR answer (dot-boundary, case-insensitive).
44
+ let matchedHostname = null;
45
+ let ptrTtl = Number.POSITIVE_INFINITY;
46
+ for (const answer of ptrAnswers) {
47
+ const hostname = stripTrailingDot(answer.data).toLowerCase();
48
+ if (matchesAnyTrustedSuffix(hostname, trustedSuffixes)) {
49
+ matchedHostname = hostname;
50
+ ptrTtl = Math.min(ptrTtl, answer.TTL);
51
+ break;
52
+ }
53
+ }
54
+ if (!matchedHostname) {
55
+ cache.setNegative(clientIp);
56
+ return false;
57
+ }
58
+ // 3. Forward DNS lookup. Try A first; if IPv6, also AAAA.
59
+ const isIpv6 = clientIp.includes(':');
60
+ const fwdType = isIpv6 ? 'AAAA' : 'A';
61
+ const fwdAnswers = await resolver.query(matchedHostname, fwdType);
62
+ if (!fwdAnswers || fwdAnswers.length === 0) {
63
+ cache.setNegative(clientIp);
64
+ return false;
65
+ }
66
+ // 4. Client IP must be in the result set.
67
+ const normalizedClient = normalizeIp(clientIp);
68
+ const matched = fwdAnswers.some((a) => normalizeIp(a.data) === normalizedClient);
69
+ if (!matched) {
70
+ cache.setNegative(clientIp);
71
+ return false;
72
+ }
73
+ // Positive: cache with TTL = min(10min, rDNS TTL, fDNS TTL).
74
+ const fwdTtl = fwdAnswers.reduce((min, a) => Math.min(min, a.TTL), Number.POSITIVE_INFINITY);
75
+ const ttlSeconds = Math.min(600, ptrTtl, fwdTtl);
76
+ cache.setPositive(clientIp, matchedHostname, ttlSeconds * 1000);
77
+ return true;
78
+ }
79
+ catch {
80
+ // Any error → fail closed (anonymous), short negative cache to avoid repeat hits.
81
+ cache.setNegative(clientIp);
82
+ return false;
83
+ }
84
+ };
85
+ }
86
+ // ─── DoH resolver (production default) ───
87
+ /**
88
+ * Create a DoH-JSON resolver that queries Cloudflare's 1.1.1.1. One subrequest per
89
+ * DNS lookup. Not a singleton — resolver has no persistent state of its own.
90
+ *
91
+ * Docs: https://developers.cloudflare.com/1.1.1.1/encryption/dns-over-https/make-api-requests/dns-json/
92
+ */
93
+ export function createDohResolver(baseUrl = 'https://1.1.1.1/dns-query', fetchImpl = fetch) {
94
+ return {
95
+ async query(name, type) {
96
+ const url = `${baseUrl}?name=${encodeURIComponent(name)}&type=${type}`;
97
+ const response = await fetchImpl(url, {
98
+ headers: { accept: 'application/dns-json' },
99
+ });
100
+ if (!response.ok)
101
+ return null;
102
+ const body = (await response.json());
103
+ // Status 0 = NOERROR. Anything else means no usable answer.
104
+ if (body.Status !== 0 || !body.Answer)
105
+ return null;
106
+ return body.Answer;
107
+ },
108
+ };
109
+ }
110
+ // ─── Internals ───
111
+ /**
112
+ * Convert an IP address to its in-addr.arpa / ip6.arpa PTR query name.
113
+ * Returns null for malformed input.
114
+ */
115
+ function reverseIpToArpa(ip) {
116
+ if (ip.includes(':')) {
117
+ // IPv6 — expand and nibble-reverse into ip6.arpa.
118
+ const expanded = expandIpv6(ip);
119
+ if (!expanded)
120
+ return null;
121
+ const nibbles = expanded.replace(/:/g, '').split('').reverse().join('.');
122
+ return `${nibbles}.ip6.arpa`;
123
+ }
124
+ // IPv4 — reverse octets into in-addr.arpa.
125
+ const octets = ip.split('.');
126
+ if (octets.length !== 4 || octets.some((o) => !/^\d+$/.test(o)))
127
+ return null;
128
+ return `${octets.reverse().join('.')}.in-addr.arpa`;
129
+ }
130
+ /**
131
+ * Expand an IPv6 address to its full 32-hex-character form (colon-separated groups
132
+ * of 4). Handles "::" compression. Returns null for malformed input.
133
+ */
134
+ function expandIpv6(ip) {
135
+ const doubleColon = ip.indexOf('::');
136
+ let parts;
137
+ if (doubleColon === -1) {
138
+ parts = ip.split(':');
139
+ }
140
+ else {
141
+ const head = ip.slice(0, doubleColon).split(':').filter(Boolean);
142
+ const tail = ip
143
+ .slice(doubleColon + 2)
144
+ .split(':')
145
+ .filter(Boolean);
146
+ const missing = 8 - head.length - tail.length;
147
+ if (missing < 0)
148
+ return null;
149
+ parts = [...head, ...Array(missing).fill('0000'), ...tail];
150
+ }
151
+ if (parts.length !== 8)
152
+ return null;
153
+ return parts.map((p) => p.padStart(4, '0')).join(':');
154
+ }
155
+ function matchesAnyTrustedSuffix(hostname, suffixes) {
156
+ const lowered = hostname.toLowerCase();
157
+ return suffixes.some((suffix) => {
158
+ const lsuffix = suffix.toLowerCase();
159
+ return lowered === lsuffix || lowered.endsWith(`.${lsuffix}`);
160
+ });
161
+ }
162
+ function stripTrailingDot(s) {
163
+ return s.endsWith('.') ? s.slice(0, -1) : s;
164
+ }
165
+ /**
166
+ * Normalize an IP for comparison. IPv4 addresses pass through; IPv6 are canonicalized
167
+ * by expanding "::" and stripping leading zeros in each group.
168
+ */
169
+ function normalizeIp(ip) {
170
+ if (!ip.includes(':'))
171
+ return ip; // IPv4
172
+ const expanded = expandIpv6(ip);
173
+ if (!expanded)
174
+ return ip;
175
+ // Strip leading zeros in each group for canonical comparison.
176
+ return expanded
177
+ .split(':')
178
+ .map((g) => g.replace(/^0+/, '') || '0')
179
+ .join(':');
180
+ }
181
+ class FcrdnsCache {
182
+ now;
183
+ entries = new Map();
184
+ constructor(now) {
185
+ this.now = now;
186
+ }
187
+ get(clientIp) {
188
+ const entry = this.entries.get(clientIp);
189
+ if (!entry)
190
+ return undefined;
191
+ if (entry.expiresAt <= this.now()) {
192
+ this.entries.delete(clientIp);
193
+ return undefined;
194
+ }
195
+ return entry.verified;
196
+ }
197
+ setPositive(clientIp, hostname, ttlMs) {
198
+ this.entries.set(clientIp, {
199
+ verified: true,
200
+ hostname,
201
+ expiresAt: this.now() + Math.max(1000, ttlMs),
202
+ });
203
+ }
204
+ setNegative(clientIp) {
205
+ this.entries.set(clientIp, {
206
+ verified: false,
207
+ expiresAt: this.now() + 60_000,
208
+ });
209
+ }
210
+ }
211
+ // ─── Exports for testing ───
212
+ export const _internals = {
213
+ reverseIpToArpa,
214
+ expandIpv6,
215
+ matchesAnyTrustedSuffix,
216
+ normalizeIp,
217
+ FcrdnsCache,
218
+ };
219
+ //# sourceMappingURL=fcrdns.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fcrdns.js","sourceRoot":"","sources":["../../src/utils/fcrdns.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ import type { KVNamespaceLike, R2BucketLike } from '../bindings.js';
2
+ export interface FreshLayerBinding {
3
+ type: 'r2' | 'kv';
4
+ impl: R2BucketLike | KVNamespaceLike;
5
+ deploymentId: string;
6
+ }
7
+ export interface FreshTwinReadResult {
8
+ body: string;
9
+ contentType: string;
10
+ lastModified?: Date;
11
+ }
12
+ /**
13
+ * Read a twin from the fresh layer for the current deployment. Returns null when
14
+ * no entry exists (cache miss — caller should fall back to Assets or contentProvider).
15
+ *
16
+ * Path is the URL-path form (e.g. '/article/midway.md'), not a full URL.
17
+ */
18
+ export declare function readFreshTwin(binding: FreshLayerBinding, path: string): Promise<FreshTwinReadResult | null>;
19
+ /**
20
+ * Write a twin to the fresh layer under the current deployment prefix. Called
21
+ * from the revalidation endpoint (POST /_seo/revalidate) and from the middleware
22
+ * fallthrough path after a successful on-demand render.
23
+ */
24
+ export declare function writeFreshTwin(binding: FreshLayerBinding, path: string, body: string, contentType?: string): Promise<void>;
25
+ /**
26
+ * Delete a twin (and its summary) from the fresh layer. Used by the unpublish
27
+ * path in /_seo/revalidate.
28
+ */
29
+ export declare function deleteFreshTwin(binding: FreshLayerBinding, path: string): Promise<void>;
30
+ /**
31
+ * List keys for a specific deployment prefix. Used by the prune cron to find
32
+ * and delete entries from old deployments.
33
+ */
34
+ export declare function listKeysByDeployment(binding: FreshLayerBinding, deploymentId: string): AsyncGenerator<string>;
35
+ /**
36
+ * List distinct deployment IDs currently present in the fresh layer. Used by
37
+ * the prune cron to find non-current deployments eligible for cleanup.
38
+ *
39
+ * Implementation note: neither R2 nor KV has a first-class "list top-level
40
+ * prefixes" API. We walk the top-level prefix `twin/` and parse the segment
41
+ * between the second and third slash. For sites with hundreds of old
42
+ * deployments this is O(N) in total key count — acceptable for the nightly
43
+ * cron, too expensive for the request path.
44
+ */
45
+ export declare function listDeploymentIds(binding: FreshLayerBinding): Promise<Set<string>>;
46
+ declare function buildKey(deploymentId: string, path: string): string;
47
+ declare function extractDeploymentId(key: string): string | null;
48
+ export declare const _internals: {
49
+ buildKey: typeof buildKey;
50
+ extractDeploymentId: typeof extractDeploymentId;
51
+ };
52
+ export {};
53
+ //# sourceMappingURL=fresh-layer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fresh-layer.d.ts","sourceRoot":"","sources":["../../src/utils/fresh-layer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AASnE,MAAM,WAAW,iBAAiB;IACjC,IAAI,EAAE,IAAI,GAAG,IAAI,CAAA;IACjB,IAAI,EAAE,YAAY,GAAG,eAAe,CAAA;IACpC,YAAY,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,mBAAmB;IACnC,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,CAAC,EAAE,IAAI,CAAA;CACnB;AAID;;;;;GAKG;AACH,wBAAsB,aAAa,CAClC,OAAO,EAAE,iBAAiB,EAC1B,IAAI,EAAE,MAAM,GACV,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAgBrC;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CACnC,OAAO,EAAE,iBAAiB,EAC1B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,WAAW,SAAiC,GAC1C,OAAO,CAAC,IAAI,CAAC,CASf;AAED;;;GAGG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,iBAAiB,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAS7F;AAED;;;GAGG;AACH,wBAAuB,oBAAoB,CAC1C,OAAO,EAAE,iBAAiB,EAC1B,YAAY,EAAE,MAAM,GAClB,cAAc,CAAC,MAAM,CAAC,CAqBxB;AAED;;;;;;;;;GASG;AACH,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CA4BxF;AAID,iBAAS,QAAQ,CAAC,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAG5D;AAED,iBAAS,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAMvD;AAED,eAAO,MAAM,UAAU;;;CAGtB,CAAA"}
@@ -0,0 +1,147 @@
1
+ // ─── Public API ───
2
+ /**
3
+ * Read a twin from the fresh layer for the current deployment. Returns null when
4
+ * no entry exists (cache miss — caller should fall back to Assets or contentProvider).
5
+ *
6
+ * Path is the URL-path form (e.g. '/article/midway.md'), not a full URL.
7
+ */
8
+ export async function readFreshTwin(binding, path) {
9
+ const key = buildKey(binding.deploymentId, path);
10
+ if (binding.type === 'r2') {
11
+ const r2 = binding.impl;
12
+ const obj = await r2.get(key);
13
+ if (!obj)
14
+ return null;
15
+ return {
16
+ body: await obj.text(),
17
+ contentType: obj.httpMetadata?.contentType ?? 'text/markdown; charset=utf-8',
18
+ lastModified: obj.uploaded,
19
+ };
20
+ }
21
+ const kv = binding.impl;
22
+ const body = await kv.get(key);
23
+ if (body === null)
24
+ return null;
25
+ return { body, contentType: 'text/markdown; charset=utf-8' };
26
+ }
27
+ /**
28
+ * Write a twin to the fresh layer under the current deployment prefix. Called
29
+ * from the revalidation endpoint (POST /_seo/revalidate) and from the middleware
30
+ * fallthrough path after a successful on-demand render.
31
+ */
32
+ export async function writeFreshTwin(binding, path, body, contentType = 'text/markdown; charset=utf-8') {
33
+ const key = buildKey(binding.deploymentId, path);
34
+ if (binding.type === 'r2') {
35
+ const r2 = binding.impl;
36
+ await r2.put(key, body, { httpMetadata: { contentType } });
37
+ return;
38
+ }
39
+ const kv = binding.impl;
40
+ await kv.put(key, body);
41
+ }
42
+ /**
43
+ * Delete a twin (and its summary) from the fresh layer. Used by the unpublish
44
+ * path in /_seo/revalidate.
45
+ */
46
+ export async function deleteFreshTwin(binding, path) {
47
+ const key = buildKey(binding.deploymentId, path);
48
+ if (binding.type === 'r2') {
49
+ const r2 = binding.impl;
50
+ await r2.delete(key);
51
+ return;
52
+ }
53
+ const kv = binding.impl;
54
+ await kv.delete(key);
55
+ }
56
+ /**
57
+ * List keys for a specific deployment prefix. Used by the prune cron to find
58
+ * and delete entries from old deployments.
59
+ */
60
+ export async function* listKeysByDeployment(binding, deploymentId) {
61
+ const prefix = `twin/${deploymentId}/`;
62
+ if (binding.type === 'r2') {
63
+ const r2 = binding.impl;
64
+ let cursor;
65
+ while (true) {
66
+ const result = await r2.list({ prefix, cursor });
67
+ for (const obj of result.objects)
68
+ yield obj.key;
69
+ if (!result.truncated)
70
+ break;
71
+ cursor = result.cursor;
72
+ }
73
+ return;
74
+ }
75
+ const kv = binding.impl;
76
+ let cursor;
77
+ while (true) {
78
+ const result = await kv.list({ prefix, cursor });
79
+ for (const entry of result.keys)
80
+ yield entry.name;
81
+ if (result.list_complete)
82
+ break;
83
+ cursor = result.cursor;
84
+ }
85
+ }
86
+ /**
87
+ * List distinct deployment IDs currently present in the fresh layer. Used by
88
+ * the prune cron to find non-current deployments eligible for cleanup.
89
+ *
90
+ * Implementation note: neither R2 nor KV has a first-class "list top-level
91
+ * prefixes" API. We walk the top-level prefix `twin/` and parse the segment
92
+ * between the second and third slash. For sites with hundreds of old
93
+ * deployments this is O(N) in total key count — acceptable for the nightly
94
+ * cron, too expensive for the request path.
95
+ */
96
+ export async function listDeploymentIds(binding) {
97
+ const deployments = new Set();
98
+ if (binding.type === 'r2') {
99
+ const r2 = binding.impl;
100
+ let cursor;
101
+ while (true) {
102
+ const result = await r2.list({ prefix: 'twin/', cursor });
103
+ for (const obj of result.objects) {
104
+ const id = extractDeploymentId(obj.key);
105
+ if (id)
106
+ deployments.add(id);
107
+ }
108
+ if (!result.truncated)
109
+ break;
110
+ cursor = result.cursor;
111
+ }
112
+ return deployments;
113
+ }
114
+ const kv = binding.impl;
115
+ let cursor;
116
+ while (true) {
117
+ const result = await kv.list({ prefix: 'twin/', cursor });
118
+ for (const entry of result.keys) {
119
+ const id = extractDeploymentId(entry.name);
120
+ if (id)
121
+ deployments.add(id);
122
+ }
123
+ if (result.list_complete)
124
+ break;
125
+ cursor = result.cursor;
126
+ }
127
+ return deployments;
128
+ }
129
+ // ─── Internals ───
130
+ function buildKey(deploymentId, path) {
131
+ const normalized = path.startsWith('/') ? path.slice(1) : path;
132
+ return `twin/${deploymentId}/${normalized}`;
133
+ }
134
+ function extractDeploymentId(key) {
135
+ // `twin/<id>/<rest>` — second segment.
136
+ const parts = key.split('/');
137
+ if (parts.length < 3)
138
+ return null;
139
+ if (parts[0] !== 'twin')
140
+ return null;
141
+ return parts[1] ?? null;
142
+ }
143
+ export const _internals = {
144
+ buildKey,
145
+ extractDeploymentId,
146
+ };
147
+ //# sourceMappingURL=fresh-layer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fresh-layer.js","sourceRoot":"","sources":["../../src/utils/fresh-layer.ts"],"names":[],"mappings":"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"}
@@ -1,12 +1,23 @@
1
- export { estimateTokenCount, generateAeoMarkdown } from './aeo.js';
2
- export { type DiscoverImage, getDiscoverImages } from './discover.js';
1
+ export { estimateTokenCount, type GenerateAeoMarkdownOptions, generateAeoMarkdown, } from './aeo.js';
2
+ export { type GenerateSummaryTwinOptions, type GenerateSummaryTwinResult, generateSummaryTwin, } from './aeo-summary.js';
3
+ export { type EmitAeoTwinsOptions, type EmitAeoTwinsResult, type EmittedTwin, emitAeoTwins, type RenderBody, } from './aeo-twin-emitter.js';
4
+ export { type AnfComponent, type AnfDocument, type GenerateAnfOptions, generateAppleNewsAnf, } from './apple-news-anf.js';
5
+ export { type ContentHtmlResolver, type GenerateAppleNewsRssOptions, generateAppleNewsRss, } from './apple-news-rss.js';
6
+ export * from './content-filter.js';
7
+ export { type ClassifyRequestInput, classifyRequest, VERIFIED_SEARCH_SUFFIXES, } from './crawler-class.js';
8
+ export { computeEffectiveAuthSegment, type RawAuthSegment } from './effective-auth.js';
9
+ export { createDohResolver, createFcrdnsVerifier, type DnsAnswer, type DnsResolver, type FcrdnsVerifier, type FcrdnsVerifyInput, } from './fcrdns.js';
10
+ export { deleteFreshTwin, type FreshLayerBinding, type FreshTwinReadResult, listDeploymentIds, listKeysByDeployment, readFreshTwin, writeFreshTwin, } from './fresh-layer.js';
3
11
  export { generateHreflang } from './hreflang.js';
4
12
  export * from './json-ld/index.js';
5
13
  export { generateLlmsTxt } from './llms.js';
14
+ export { type GenerateLlmsFullOptions, generateLlmsFull } from './llms-full.js';
6
15
  export { applyTrailingSlash, generateCanonical, generateMeta, type OgVariant } from './meta.js';
7
16
  export { generatePodcastFeed } from './podcast.js';
8
17
  export { generateRobotsTxt } from './robots.js';
9
18
  export { generateRssFeed } from './rss.js';
10
19
  export { generateArticleSitemap, generatePagesSitemap, generateProductSitemap, generateSitemapIndex, generateVideoSitemap, type SitemapEntry, } from './sitemap.js';
11
- export { type PageValidationOptions, type ValidationResult, validateJsonLd, validatePage, } from './validation.js';
20
+ export { generateMarkdownSitemap } from './sitemap-markdown.js';
21
+ export { checkStaleness, computeContentHash, type StalenessDriftRecord, } from './staleness.js';
22
+ export { type HreflangReciprocityIssue, type PageValidationOptions, type PrerenderGuardIssue, type ValidationResult, validateHreflangReciprocity, validateJsonLd, validatePage, validatePrerenderedGatedRoutes, } from './validation.js';
12
23
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAA;AAClE,OAAO,EAAE,KAAK,aAAa,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AACrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAChD,cAAc,oBAAoB,CAAA;AAClC,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAC3C,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,YAAY,EAAE,KAAK,SAAS,EAAE,MAAM,WAAW,CAAA;AAC/F,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAA;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAC1C,OAAO,EACN,sBAAsB,EACtB,oBAAoB,EACpB,sBAAsB,EACtB,oBAAoB,EACpB,oBAAoB,EACpB,KAAK,YAAY,GACjB,MAAM,cAAc,CAAA;AACrB,OAAO,EACN,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EACrB,cAAc,EACd,YAAY,GACZ,MAAM,iBAAiB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,kBAAkB,EAClB,KAAK,0BAA0B,EAC/B,mBAAmB,GACnB,MAAM,UAAU,CAAA;AACjB,OAAO,EACN,KAAK,0BAA0B,EAC/B,KAAK,yBAAyB,EAC9B,mBAAmB,GACnB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EACN,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,WAAW,EAChB,YAAY,EACZ,KAAK,UAAU,GACf,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EACN,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,oBAAoB,GACpB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EACN,KAAK,mBAAmB,EACxB,KAAK,2BAA2B,EAChC,oBAAoB,GACpB,MAAM,qBAAqB,CAAA;AAC5B,cAAc,qBAAqB,CAAA;AACnC,OAAO,EACN,KAAK,oBAAoB,EACzB,eAAe,EACf,wBAAwB,GACxB,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EAAE,2BAA2B,EAAE,KAAK,cAAc,EAAE,MAAM,qBAAqB,CAAA;AACtF,OAAO,EACN,iBAAiB,EACjB,oBAAoB,EACpB,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,iBAAiB,GACtB,MAAM,aAAa,CAAA;AACpB,OAAO,EACN,eAAe,EACf,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EACxB,iBAAiB,EACjB,oBAAoB,EACpB,aAAa,EACb,cAAc,GACd,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAChD,cAAc,oBAAoB,CAAA;AAClC,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAC3C,OAAO,EAAE,KAAK,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAC/E,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,YAAY,EAAE,KAAK,SAAS,EAAE,MAAM,WAAW,CAAA;AAC/F,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAA;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAC1C,OAAO,EACN,sBAAsB,EACtB,oBAAoB,EACpB,sBAAsB,EACtB,oBAAoB,EACpB,oBAAoB,EACpB,KAAK,YAAY,GACjB,MAAM,cAAc,CAAA;AACrB,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAA;AAC/D,OAAO,EACN,cAAc,EACd,kBAAkB,EAClB,KAAK,oBAAoB,GACzB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EACN,KAAK,wBAAwB,EAC7B,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,EACrB,2BAA2B,EAC3B,cAAc,EACd,YAAY,EACZ,8BAA8B,GAC9B,MAAM,iBAAiB,CAAA"}
@@ -1,12 +1,23 @@
1
- export { estimateTokenCount, generateAeoMarkdown } from './aeo.js';
2
- export { getDiscoverImages } from './discover.js';
1
+ export { estimateTokenCount, generateAeoMarkdown, } from './aeo.js';
2
+ export { generateSummaryTwin, } from './aeo-summary.js';
3
+ export { emitAeoTwins, } from './aeo-twin-emitter.js';
4
+ export { generateAppleNewsAnf, } from './apple-news-anf.js';
5
+ export { generateAppleNewsRss, } from './apple-news-rss.js';
6
+ export * from './content-filter.js';
7
+ export { classifyRequest, VERIFIED_SEARCH_SUFFIXES, } from './crawler-class.js';
8
+ export { computeEffectiveAuthSegment } from './effective-auth.js';
9
+ export { createDohResolver, createFcrdnsVerifier, } from './fcrdns.js';
10
+ export { deleteFreshTwin, listDeploymentIds, listKeysByDeployment, readFreshTwin, writeFreshTwin, } from './fresh-layer.js';
3
11
  export { generateHreflang } from './hreflang.js';
4
12
  export * from './json-ld/index.js';
5
13
  export { generateLlmsTxt } from './llms.js';
14
+ export { generateLlmsFull } from './llms-full.js';
6
15
  export { applyTrailingSlash, generateCanonical, generateMeta } from './meta.js';
7
16
  export { generatePodcastFeed } from './podcast.js';
8
17
  export { generateRobotsTxt } from './robots.js';
9
18
  export { generateRssFeed } from './rss.js';
10
19
  export { generateArticleSitemap, generatePagesSitemap, generateProductSitemap, generateSitemapIndex, generateVideoSitemap, } from './sitemap.js';
11
- export { validateJsonLd, validatePage, } from './validation.js';
20
+ export { generateMarkdownSitemap } from './sitemap-markdown.js';
21
+ export { checkStaleness, computeContentHash, } from './staleness.js';
22
+ export { validateHreflangReciprocity, validateJsonLd, validatePage, validatePrerenderedGatedRoutes, } from './validation.js';
12
23
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAA;AAClE,OAAO,EAAsB,iBAAiB,EAAE,MAAM,eAAe,CAAA;AACrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAChD,cAAc,oBAAoB,CAAA;AAClC,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAC3C,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,YAAY,EAAkB,MAAM,WAAW,CAAA;AAC/F,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAA;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAC1C,OAAO,EACN,sBAAsB,EACtB,oBAAoB,EACpB,sBAAsB,EACtB,oBAAoB,EACpB,oBAAoB,GAEpB,MAAM,cAAc,CAAA;AACrB,OAAO,EAGN,cAAc,EACd,YAAY,GACZ,MAAM,iBAAiB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,kBAAkB,EAElB,mBAAmB,GACnB,MAAM,UAAU,CAAA;AACjB,OAAO,EAGN,mBAAmB,GACnB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAIN,YAAY,GAEZ,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EAIN,oBAAoB,GACpB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAGN,oBAAoB,GACpB,MAAM,qBAAqB,CAAA;AAC5B,cAAc,qBAAqB,CAAA;AACnC,OAAO,EAEN,eAAe,EACf,wBAAwB,GACxB,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EAAE,2BAA2B,EAAuB,MAAM,qBAAqB,CAAA;AACtF,OAAO,EACN,iBAAiB,EACjB,oBAAoB,GAKpB,MAAM,aAAa,CAAA;AACpB,OAAO,EACN,eAAe,EAGf,iBAAiB,EACjB,oBAAoB,EACpB,aAAa,EACb,cAAc,GACd,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAChD,cAAc,oBAAoB,CAAA;AAClC,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAC3C,OAAO,EAAgC,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAC/E,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,YAAY,EAAkB,MAAM,WAAW,CAAA;AAC/F,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAA;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAC1C,OAAO,EACN,sBAAsB,EACtB,oBAAoB,EACpB,sBAAsB,EACtB,oBAAoB,EACpB,oBAAoB,GAEpB,MAAM,cAAc,CAAA;AACrB,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAA;AAC/D,OAAO,EACN,cAAc,EACd,kBAAkB,GAElB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAKN,2BAA2B,EAC3B,cAAc,EACd,YAAY,EACZ,8BAA8B,GAC9B,MAAM,iBAAiB,CAAA"}