@devslab/site-kit 0.14.1 → 0.16.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 (130) hide show
  1. package/README.ko.md +266 -3
  2. package/README.md +266 -3
  3. package/dist/chrome.d.ts +10 -0
  4. package/dist/chrome.d.ts.map +1 -1
  5. package/dist/consent.d.ts +36 -0
  6. package/dist/consent.d.ts.map +1 -0
  7. package/dist/index.d.ts +1 -0
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/solid.js +336 -135
  10. package/dist/solid.js.map +1 -1
  11. package/dist/solid.server.js +282 -25
  12. package/dist/solid.server.js.map +1 -1
  13. package/fonts/geist/LICENSE.txt +93 -0
  14. package/fonts/geist/geist-cyrillic-ext-wght-normal.woff2 +0 -0
  15. package/fonts/geist/geist-cyrillic-wght-normal.woff2 +0 -0
  16. package/fonts/geist/geist-latin-ext-wght-normal.woff2 +0 -0
  17. package/fonts/geist/geist-latin-wght-normal.woff2 +0 -0
  18. package/fonts/geist/geist-vietnamese-wght-normal.woff2 +0 -0
  19. package/fonts/geist-mono/LICENSE.txt +93 -0
  20. package/fonts/geist-mono/geist-mono-cyrillic-ext-wght-normal.woff2 +0 -0
  21. package/fonts/geist-mono/geist-mono-cyrillic-wght-normal.woff2 +0 -0
  22. package/fonts/geist-mono/geist-mono-latin-ext-wght-normal.woff2 +0 -0
  23. package/fonts/geist-mono/geist-mono-latin-wght-normal.woff2 +0 -0
  24. package/fonts/geist-mono/geist-mono-symbols2-wght-normal.woff2 +0 -0
  25. package/fonts/geist-mono/geist-mono-vietnamese-wght-normal.woff2 +0 -0
  26. package/fonts/manifest.json +674 -0
  27. package/fonts/pretendard/LICENSE.txt +94 -0
  28. package/fonts/pretendard/PretendardVariable.subset.0.woff2 +0 -0
  29. package/fonts/pretendard/PretendardVariable.subset.1.woff2 +0 -0
  30. package/fonts/pretendard/PretendardVariable.subset.10.woff2 +0 -0
  31. package/fonts/pretendard/PretendardVariable.subset.11.woff2 +0 -0
  32. package/fonts/pretendard/PretendardVariable.subset.12.woff2 +0 -0
  33. package/fonts/pretendard/PretendardVariable.subset.13.woff2 +0 -0
  34. package/fonts/pretendard/PretendardVariable.subset.14.woff2 +0 -0
  35. package/fonts/pretendard/PretendardVariable.subset.15.woff2 +0 -0
  36. package/fonts/pretendard/PretendardVariable.subset.16.woff2 +0 -0
  37. package/fonts/pretendard/PretendardVariable.subset.17.woff2 +0 -0
  38. package/fonts/pretendard/PretendardVariable.subset.18.woff2 +0 -0
  39. package/fonts/pretendard/PretendardVariable.subset.19.woff2 +0 -0
  40. package/fonts/pretendard/PretendardVariable.subset.2.woff2 +0 -0
  41. package/fonts/pretendard/PretendardVariable.subset.20.woff2 +0 -0
  42. package/fonts/pretendard/PretendardVariable.subset.21.woff2 +0 -0
  43. package/fonts/pretendard/PretendardVariable.subset.22.woff2 +0 -0
  44. package/fonts/pretendard/PretendardVariable.subset.23.woff2 +0 -0
  45. package/fonts/pretendard/PretendardVariable.subset.24.woff2 +0 -0
  46. package/fonts/pretendard/PretendardVariable.subset.25.woff2 +0 -0
  47. package/fonts/pretendard/PretendardVariable.subset.26.woff2 +0 -0
  48. package/fonts/pretendard/PretendardVariable.subset.27.woff2 +0 -0
  49. package/fonts/pretendard/PretendardVariable.subset.28.woff2 +0 -0
  50. package/fonts/pretendard/PretendardVariable.subset.29.woff2 +0 -0
  51. package/fonts/pretendard/PretendardVariable.subset.3.woff2 +0 -0
  52. package/fonts/pretendard/PretendardVariable.subset.30.woff2 +0 -0
  53. package/fonts/pretendard/PretendardVariable.subset.31.woff2 +0 -0
  54. package/fonts/pretendard/PretendardVariable.subset.32.woff2 +0 -0
  55. package/fonts/pretendard/PretendardVariable.subset.33.woff2 +0 -0
  56. package/fonts/pretendard/PretendardVariable.subset.34.woff2 +0 -0
  57. package/fonts/pretendard/PretendardVariable.subset.35.woff2 +0 -0
  58. package/fonts/pretendard/PretendardVariable.subset.36.woff2 +0 -0
  59. package/fonts/pretendard/PretendardVariable.subset.37.woff2 +0 -0
  60. package/fonts/pretendard/PretendardVariable.subset.38.woff2 +0 -0
  61. package/fonts/pretendard/PretendardVariable.subset.39.woff2 +0 -0
  62. package/fonts/pretendard/PretendardVariable.subset.4.woff2 +0 -0
  63. package/fonts/pretendard/PretendardVariable.subset.40.woff2 +0 -0
  64. package/fonts/pretendard/PretendardVariable.subset.41.woff2 +0 -0
  65. package/fonts/pretendard/PretendardVariable.subset.42.woff2 +0 -0
  66. package/fonts/pretendard/PretendardVariable.subset.43.woff2 +0 -0
  67. package/fonts/pretendard/PretendardVariable.subset.44.woff2 +0 -0
  68. package/fonts/pretendard/PretendardVariable.subset.45.woff2 +0 -0
  69. package/fonts/pretendard/PretendardVariable.subset.46.woff2 +0 -0
  70. package/fonts/pretendard/PretendardVariable.subset.47.woff2 +0 -0
  71. package/fonts/pretendard/PretendardVariable.subset.48.woff2 +0 -0
  72. package/fonts/pretendard/PretendardVariable.subset.49.woff2 +0 -0
  73. package/fonts/pretendard/PretendardVariable.subset.5.woff2 +0 -0
  74. package/fonts/pretendard/PretendardVariable.subset.50.woff2 +0 -0
  75. package/fonts/pretendard/PretendardVariable.subset.51.woff2 +0 -0
  76. package/fonts/pretendard/PretendardVariable.subset.52.woff2 +0 -0
  77. package/fonts/pretendard/PretendardVariable.subset.53.woff2 +0 -0
  78. package/fonts/pretendard/PretendardVariable.subset.54.woff2 +0 -0
  79. package/fonts/pretendard/PretendardVariable.subset.55.woff2 +0 -0
  80. package/fonts/pretendard/PretendardVariable.subset.56.woff2 +0 -0
  81. package/fonts/pretendard/PretendardVariable.subset.57.woff2 +0 -0
  82. package/fonts/pretendard/PretendardVariable.subset.58.woff2 +0 -0
  83. package/fonts/pretendard/PretendardVariable.subset.59.woff2 +0 -0
  84. package/fonts/pretendard/PretendardVariable.subset.6.woff2 +0 -0
  85. package/fonts/pretendard/PretendardVariable.subset.60.woff2 +0 -0
  86. package/fonts/pretendard/PretendardVariable.subset.61.woff2 +0 -0
  87. package/fonts/pretendard/PretendardVariable.subset.62.woff2 +0 -0
  88. package/fonts/pretendard/PretendardVariable.subset.63.woff2 +0 -0
  89. package/fonts/pretendard/PretendardVariable.subset.64.woff2 +0 -0
  90. package/fonts/pretendard/PretendardVariable.subset.65.woff2 +0 -0
  91. package/fonts/pretendard/PretendardVariable.subset.66.woff2 +0 -0
  92. package/fonts/pretendard/PretendardVariable.subset.67.woff2 +0 -0
  93. package/fonts/pretendard/PretendardVariable.subset.68.woff2 +0 -0
  94. package/fonts/pretendard/PretendardVariable.subset.69.woff2 +0 -0
  95. package/fonts/pretendard/PretendardVariable.subset.7.woff2 +0 -0
  96. package/fonts/pretendard/PretendardVariable.subset.70.woff2 +0 -0
  97. package/fonts/pretendard/PretendardVariable.subset.71.woff2 +0 -0
  98. package/fonts/pretendard/PretendardVariable.subset.72.woff2 +0 -0
  99. package/fonts/pretendard/PretendardVariable.subset.73.woff2 +0 -0
  100. package/fonts/pretendard/PretendardVariable.subset.74.woff2 +0 -0
  101. package/fonts/pretendard/PretendardVariable.subset.75.woff2 +0 -0
  102. package/fonts/pretendard/PretendardVariable.subset.76.woff2 +0 -0
  103. package/fonts/pretendard/PretendardVariable.subset.77.woff2 +0 -0
  104. package/fonts/pretendard/PretendardVariable.subset.78.woff2 +0 -0
  105. package/fonts/pretendard/PretendardVariable.subset.79.woff2 +0 -0
  106. package/fonts/pretendard/PretendardVariable.subset.8.woff2 +0 -0
  107. package/fonts/pretendard/PretendardVariable.subset.80.woff2 +0 -0
  108. package/fonts/pretendard/PretendardVariable.subset.81.woff2 +0 -0
  109. package/fonts/pretendard/PretendardVariable.subset.82.woff2 +0 -0
  110. package/fonts/pretendard/PretendardVariable.subset.83.woff2 +0 -0
  111. package/fonts/pretendard/PretendardVariable.subset.84.woff2 +0 -0
  112. package/fonts/pretendard/PretendardVariable.subset.85.woff2 +0 -0
  113. package/fonts/pretendard/PretendardVariable.subset.86.woff2 +0 -0
  114. package/fonts/pretendard/PretendardVariable.subset.87.woff2 +0 -0
  115. package/fonts/pretendard/PretendardVariable.subset.88.woff2 +0 -0
  116. package/fonts/pretendard/PretendardVariable.subset.89.woff2 +0 -0
  117. package/fonts/pretendard/PretendardVariable.subset.9.woff2 +0 -0
  118. package/fonts/pretendard/PretendardVariable.subset.90.woff2 +0 -0
  119. package/fonts/pretendard/PretendardVariable.subset.91.woff2 +0 -0
  120. package/fonts.css +937 -0
  121. package/package.json +10 -5
  122. package/src/core/consent.d.mts +160 -0
  123. package/src/core/consent.mjs +567 -0
  124. package/src/core/fonts.d.mts +5 -0
  125. package/src/core/fonts.mjs +39 -0
  126. package/src/core/index.d.mts +2 -0
  127. package/src/core/index.mjs +5 -0
  128. package/src/tanstack-start.d.mts +14 -2
  129. package/src/tanstack-start.mjs +39 -1
  130. package/styles.css +45 -2
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@devslab/site-kit",
3
- "version": "0.14.1",
3
+ "version": "0.16.0",
4
4
  "description": "Strict i18n, SEO/GEO, and Solid public-site shells for DevsLab products",
5
5
  "type": "module",
6
6
  "license": "SEE LICENSE IN LICENSE",
7
7
  "sideEffects": [
8
8
  "./styles.css",
9
- "./site-sections.css"
9
+ "./site-sections.css",
10
+ "./fonts.css"
10
11
  ],
11
12
  "repository": {
12
13
  "type": "git",
@@ -21,11 +22,13 @@
21
22
  "files": [
22
23
  "dist",
23
24
  "flags",
25
+ "fonts",
24
26
  "src/core",
25
27
  "src/tanstack-start.mjs",
26
28
  "src/tanstack-start.d.mts",
27
29
  "styles.css",
28
30
  "site-sections.css",
31
+ "fonts.css",
29
32
  "README.md",
30
33
  "README.ko.md"
31
34
  ],
@@ -57,10 +60,12 @@
57
60
  "import": "./src/tanstack-start.mjs"
58
61
  },
59
62
  "./styles.css": "./styles.css",
60
- "./site-sections.css": "./site-sections.css"
63
+ "./site-sections.css": "./site-sections.css",
64
+ "./fonts.css": "./fonts.css",
65
+ "./fonts/*": "./fonts/*"
61
66
  },
62
67
  "dependencies": {
63
- "@devslab/dds-solid": "0.14.1"
68
+ "@devslab/dds-solid": "0.16.0"
64
69
  },
65
70
  "peerDependencies": {
66
71
  "@tanstack/solid-router": "1.170.30",
@@ -102,7 +107,7 @@
102
107
  ],
103
108
  "scripts": {
104
109
  "build": "vite build && vite build --config vite.server.config.ts && tsc -p tsconfig.build.json --emitDeclarationOnly",
105
- "check": "node scripts/build-flags.mjs --check && tsc -p tsconfig.json --noEmit && tsc -p fixtures/bundle-probe/tsconfig.json && node scripts/check-client-bundle.mjs",
110
+ "check": "node scripts/build-flags.mjs --check && node scripts/build-fonts.mjs --check && tsc -p tsconfig.json --noEmit && tsc -p fixtures/bundle-probe/tsconfig.json && node scripts/check-client-bundle.mjs",
106
111
  "test": "vitest run --config vitest.config.ts && vitest run --config vitest.ssr.config.ts",
107
112
  "test:a11y": "vitest run --config vitest.config.ts src/solid/__tests__/a11y.test.tsx",
108
113
  "test:worker": "wrangler deploy --dry-run --config fixtures/worker/wrangler.jsonc --outdir dist-worker"
@@ -0,0 +1,160 @@
1
+ /** Default consent cookie name: `site_consent`. */
2
+ export declare const CONSENT_COOKIE_NAME: string;
3
+ /** Twelve months, in seconds. */
4
+ export declare const CONSENT_MAX_AGE_SECONDS: number;
5
+ /** Letters, digits, `.`, `_`, `-`; starts with a letter or digit; at most 32 characters. */
6
+ export declare const CONSENT_POLICY_VERSION_PATTERN: RegExp;
7
+ /** 32 lowercase hex characters. */
8
+ export declare const CONSENT_ANONYMOUS_ID_PATTERN: RegExp;
9
+ export type ConsentAction = "grant" | "deny" | "withdraw" | "update";
10
+ export declare const CONSENT_ACTIONS: readonly ConsentAction[];
11
+ export type ConsentModeSignal = "ad_personalization" | "ad_storage" | "ad_user_data" | "analytics_storage";
12
+ /** Consent Mode v2 defaults: all four signals denied. */
13
+ export declare const CONSENT_MODE_DEFAULTS: Readonly<Record<ConsentModeSignal, "denied">>;
14
+ /** `_ga`, `_ga_<stream>`, `_gid`, `_gat`, `_gat_<…>`. */
15
+ export declare const GA_COOKIE_PATTERN: RegExp;
16
+ export declare const CONSENT_RECORD_MAX_BYTES: number;
17
+ /** `{ "Cache-Control": "private, no-store", Vary: "Cookie" }` — send on every response whose HTML was rendered from the consent cookie, so a shared cache never serves one visitor's granted head (with the Tag Manager loader) to another. */
18
+ export declare const CONSENT_RESPONSE_HEADERS: Readonly<{ "Cache-Control": "private, no-store"; Vary: "Cookie" }>;
19
+
20
+ /** What the consent cookie holds: policy version, analytics (1 granted / 0 not), unix seconds, anonymous id. */
21
+ export interface ConsentState { readonly v: string; readonly a: 0 | 1; readonly t: number; readonly id: string }
22
+
23
+ /** Strict parse of a cookie value; `null` for anything malformed. */
24
+ export declare function parseConsentCookie(value: string | null | undefined): ConsentState | null;
25
+ /** The cookie value for a state. Throws RangeError for a malformed state. */
26
+ export declare function formatConsentCookie(state: ConsentState): string;
27
+ export interface ConsentCookieOptions { name?: string; domain?: string; maxAgeSeconds?: number }
28
+ /** `name=value; Path=/; Max-Age=…; SameSite=Lax; Secure` (+ `Domain` if given), for `document.cookie` or `Set-Cookie`. */
29
+ export declare function serializeConsentCookie(state: ConsentState, options?: ConsentCookieOptions): string;
30
+
31
+ export interface ConsentReadOptions {
32
+ policyVersion: string;
33
+ cookieName?: string;
34
+ /** Milliseconds since the epoch; defaults to Date.now(). */
35
+ now?: number;
36
+ maxAgeSeconds?: number;
37
+ }
38
+ /** The decision for the current policy version in a Cookie header or `document.cookie`; `null` (ask) when absent, malformed, expired or for another version. */
39
+ export declare function readConsentCookie(cookieHeader: string | null | undefined, options: ConsentReadOptions): ConsentState | null;
40
+ /** True when the Cookie header holds a current, unexpired analytics grant. */
41
+ export declare function consentCookieGrantsAnalytics(cookieHeader: string | null | undefined, options: ConsentReadOptions): boolean;
42
+ /** True when `state` grants analytics for `policyVersion` and is unexpired. */
43
+ export declare function analyticsConsented(state: ConsentState | null | undefined, policyVersion: string, options?: { now?: number; maxAgeSeconds?: number }): boolean;
44
+
45
+ /**
46
+ * Body of the head `<script>` (no tag, no nonce). Not granted: Consent Mode defaults only — no Google host in the string.
47
+ * Granted with `gtm`: defaults, an `analytics_storage` grant, then Google's Tag Manager loader unless gtm.js is already on the page.
48
+ */
49
+ export declare function consentHeadScript(options?: { granted?: boolean; gtm?: string | undefined }): string;
50
+
51
+ /** Path before any query or fragment, starting with "/", at most 512 characters; anything else becomes "/". */
52
+ export declare function normalizeConsentPath(path: string | null | undefined): string;
53
+
54
+ /** The record a browser sends after each decision. The server adds subject, IP, user agent and its own receive time. */
55
+ export interface ConsentRecord {
56
+ readonly policyVersion: string;
57
+ readonly analytics: boolean;
58
+ readonly action: ConsentAction;
59
+ readonly anonymousId: string;
60
+ /** Unix seconds — the `t` written into the cookie. */
61
+ readonly decidedAt: number;
62
+ readonly source: "web";
63
+ readonly path: string;
64
+ }
65
+ export interface ConsentRecordParseOptions {
66
+ /** The current version, or a list while records made under an older one may still arrive. */
67
+ policyVersion: string | readonly string[];
68
+ now?: number;
69
+ /** How old a `decidedAt` may be (seconds). Default 31 days — queued records are resent on the next visit. */
70
+ maxAgeSeconds?: number;
71
+ }
72
+ /** Strict server-side validation of a POSTed record (JSON text or object); `null` when anything is off. */
73
+ export declare function parseConsentRecord(input: unknown, options: ConsentRecordParseOptions): ConsentRecord | null;
74
+ type HeaderSource = { headers: { get(name: string): string | null } } | { get(name: string): string | null } | Record<string, string | string[] | undefined>;
75
+ /** `Origin` equals `expectedOrigin`, or — without `Origin` — `Sec-Fetch-Site: same-origin`. */
76
+ export declare function isSameOriginRequest(request: HeaderSource, expectedOrigin: string): boolean;
77
+
78
+ export interface ConsentRecordError extends Error { retryable: boolean }
79
+ /** An `onChange` that POSTs the record as JSON to a same-origin path. Rejects with `retryable` set (network, 429, 5xx → true). */
80
+ export declare function postConsentRecord(endpoint: string, options?: { fetch?: typeof fetch }): (record: ConsentRecord) => Promise<void>;
81
+
82
+ export type ConsentEvent =
83
+ | { type: "change"; state: ConsentState; record: ConsentRecord }
84
+ | { type: "open-settings" }
85
+ | { type: "dismiss" };
86
+
87
+ export interface ConsentManagerOptions {
88
+ policyVersion: string;
89
+ /** Tag Manager container to load once analytics is granted. Omit to manage consent without Tag Manager. */
90
+ gtm?: string;
91
+ cookieName?: string;
92
+ /** Share the decision across subdomains. Default: host-only. */
93
+ cookieDomain?: string;
94
+ maxAgeSeconds?: number;
95
+ /**
96
+ * GA4 measurement ids (`G-…`) the container sends to, switched off with `ga-disable-<id>` on withdrawal.
97
+ * Required (non-empty) when `gtm` is given: it is the only switch that stops a GA4 tag Tag Manager has
98
+ * already initialised. Throws RangeError otherwise.
99
+ */
100
+ measurementIds?: readonly string[];
101
+ /** Called after every decision — POST it to your backend (see postConsentRecord). A rejection with `retryable !== false` is kept and resent on the next start(). */
102
+ onChange?: (record: ConsentRecord) => void | Promise<void>;
103
+ /** Redacts the page path before it goes into a record (a path that carries a secret). */
104
+ recordPath?: (pathname: string) => string;
105
+ /** Nonce for the gtm.js element. Default: a `csp-nonce` meta, else the first `[nonce]` element. */
106
+ nonce?: string;
107
+ /** For tests. */
108
+ window?: Window;
109
+ now?: () => number;
110
+ }
111
+
112
+ export interface ConsentManager {
113
+ readonly policyVersion: string;
114
+ /** Applies the stored decision to this page. Idempotent; a no-op outside a browser. */
115
+ start(): void;
116
+ /** The current-version, unexpired decision (from the cookie, or the one made on this page if the browser refused the cookie), or `null`. */
117
+ state(): ConsentState | null;
118
+ analyticsGranted(): boolean;
119
+ needsDecision(): boolean;
120
+ acceptAll(): ConsentRecord;
121
+ rejectAll(): ConsentRecord;
122
+ save(choice: { analytics: boolean }): ConsentRecord;
123
+ withdraw(): ConsentRecord;
124
+ /** Asks the mounted banner to open its settings dialog. */
125
+ openSettings(): void;
126
+ /** Hides the banner for this page view without a decision. */
127
+ dismiss(): void;
128
+ dismissed(): boolean;
129
+ subscribe(listener: (event: ConsentEvent) => void): () => void;
130
+ /** Delegated click handler for `[data-consent-settings]` triggers. Returns a remover. */
131
+ bindTriggers(root?: Document | Element): () => void;
132
+ /** Resolves once records sent so far are delivered or queued. */
133
+ settled(): Promise<void>;
134
+ }
135
+ export declare function createConsentManager(options: ConsentManagerOptions): ConsentManager;
136
+
137
+ export interface ConsentMessages {
138
+ regionLabel: string;
139
+ title: string;
140
+ body: string;
141
+ privacyLink: string;
142
+ acceptAll: string;
143
+ rejectAll: string;
144
+ settings: string;
145
+ dismiss: string;
146
+ settingsTitle: string;
147
+ settingsIntro: string;
148
+ necessaryTitle: string;
149
+ necessaryBody: string;
150
+ necessaryStatus: string;
151
+ analyticsTitle: string;
152
+ analyticsBody: string;
153
+ analyticsSwitch: string;
154
+ save: string;
155
+ close: string;
156
+ saved: string;
157
+ trigger: string;
158
+ }
159
+ export declare const CONSENT_MESSAGES_KO: Readonly<ConsentMessages>;
160
+ export declare const CONSENT_MESSAGES_EN: Readonly<ConsentMessages>;