@hkdigital/lib-core 0.5.11 → 0.5.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -32,6 +32,9 @@ pnpm add @skeletonlabs/skeleton
32
32
  # UI icons
33
33
  pnpm add @steeze-ui/heroicons
34
34
 
35
+ # CSS processing (Tailwind and PostCSS)
36
+ pnpm add tailwindcss @tailwindcss/postcss postcss autoprefixer
37
+
35
38
  # Logging
36
39
  pnpm add pino pino-pretty
37
40
 
@@ -42,13 +45,13 @@ pnpm add jsonwebtoken
42
45
  pnpm add @eslint/js eslint-plugin-import
43
46
 
44
47
  # Image processing
45
- pnpm add vite-imagetools
48
+ pnpm add -D vite-imagetools
46
49
  ```
47
50
 
48
51
  **For other libraries**, install as dev dependencies and declare as peer dependencies:
49
52
  ```bash
50
53
  # Install as dev dependencies and peer dependencies
51
- pnpm add -D --save-peer @sveltejs/kit svelte svelte-preprocess runed valibot @skeletonlabs/skeleton @steeze-ui/heroicons pino pino-pretty jsonwebtoken @eslint/js eslint-plugin-import vite-imagetools
54
+ pnpm add -D --save-peer @sveltejs/kit svelte svelte-preprocess runed valibot @skeletonlabs/skeleton @steeze-ui/heroicons tailwindcss @tailwindcss/postcss postcss autoprefixer pino pino-pretty jsonwebtoken @eslint/js eslint-plugin-import vite-imagetools
52
55
  ```
53
56
 
54
57
  ### Design System & Configuration
@@ -135,7 +138,8 @@ This library includes a complete design system with Tailwind CSS integration. Ba
135
138
  export default config;
136
139
  ```
137
140
 
138
- 5. **TypeScript support for imagetools** - Add image import definitions to `app.d.ts`:
141
+ 5. **Image import type definitions** - Add imagetools type support to
142
+ `app.d.ts`:
139
143
  ```typescript
140
144
  // src/app.d.ts
141
145
  import '@hkdigital/lib-core/config/imagetools.d.ts';
@@ -179,6 +183,59 @@ This library includes a complete design system with Tailwind CSS integration. Ba
179
183
  import heroResponsive from '$lib/assets/hero.jpg?preset=photo&responsive';
180
184
  ```
181
185
 
186
+ 6. **(meta) folder setup** - Copy and configure PWA/favicon generation:
187
+
188
+ The library includes a complete `(meta)` folder with PWA configuration,
189
+ favicon generation, manifest.json, sitemap.xml, and robots.txt endpoints.
190
+
191
+ **Copy the folder to your project:**
192
+ ```bash
193
+ cp -r node_modules/@hkdigital/lib-core/src/routes/\(meta\) src/routes/
194
+ ```
195
+
196
+ **Customize for your app:**
197
+ - Replace `src/routes/(meta)/favicon.png` with your 512×512px image
198
+ - Edit `src/routes/(meta)/config.js`:
199
+ ```javascript
200
+ export const name = 'Your App Name';
201
+ export const shortName = 'App';
202
+ export const description = 'Your app description';
203
+ export const backgroundAndThemeColor = '#082962';
204
+
205
+ // Add your site routes for sitemap
206
+ export const siteRoutes = ['/', '/about', '/contact'];
207
+
208
+ // Configure robots.txt (prevent test sites from being indexed)
209
+ export const robotsConfig = {
210
+ allowedHosts: ['mysite.com', 'www.mysite.com'],
211
+ disallowedPaths: ['/admin/*'],
212
+ includeSitemap: true
213
+ };
214
+ ```
215
+
216
+ **Integrate into root layout:**
217
+ ```svelte
218
+ <!-- src/routes/+layout.svelte -->
219
+ <script>
220
+ import { Favicons, PWA } from './(meta)/index.js';
221
+ </script>
222
+
223
+ <Favicons />
224
+ <PWA />
225
+
226
+ {@render children()}
227
+ ```
228
+
229
+ **What you get:**
230
+ - Automatic favicon generation (16, 32, 192, 512px)
231
+ - Apple touch icons (120, 152, 167, 180px)
232
+ - Dynamic `/manifest.json` endpoint
233
+ - Dynamic `/sitemap.xml` endpoint
234
+ - Dynamic `/robots.txt` with host filtering
235
+
236
+ See [src/routes/(meta)/README.md](./src/routes/(meta)/README.md) for
237
+ complete documentation.
238
+
182
239
  ### Logging System
183
240
 
184
241
  The library includes a comprehensive logging system that provides:
@@ -220,34 +277,112 @@ pnpm run upgrade:all # Update all packages
220
277
  pnpm run publish:npm # Version bump and publish to npm
221
278
  ```
222
279
 
223
- ### Import JS, Svelte files and Typedefs
280
+ ### Import Patterns and Export Structure
224
281
 
225
- Main folders in lib have an index.js, but may also have a more specific export file e.g. http.js or cache.js (@see $lib/network).
282
+ **Public exports use domain-specific files matching folder names:**
226
283
 
227
- Main folders (should) have a typedef.js that can be used in JSdoc comments.
284
+ ```js
285
+ // Standard pattern: folder → matching .js export file
286
+ import { httpGet, httpPost } from '@hkdigital/lib-core/network/http.js';
287
+ import { CacheManager } from '@hkdigital/lib-core/network/cache.js';
288
+ import { LCHAR } from '@hkdigital/lib-core/constants/regexp.js';
289
+ import { Button } from '@hkdigital/lib-core/ui/primitives.js';
290
+ ```
291
+
292
+ **Pattern:**
293
+ - Folder `$lib/network/http/` → Export file `$lib/network/http.js`
294
+ - Folder `$lib/network/cache/` → Export file `$lib/network/cache.js`
295
+ - Folder `$lib/constants/regexp/` → Export file `$lib/constants/regexp.js`
296
+
297
+ **Rare exception - index.js for aggregated APIs:**
228
298
 
229
299
  ```js
230
- // Import Latin char constant for use in regular expressions
231
- import { LCHAR } from '@hkdigital/lib-core/constants/regexp/index.js';
300
+ // Only used for large subsystems with public-facing aggregated APIs
301
+ import { designTokens } from '@hkdigital/lib-core/design/index.js';
232
302
  ```
233
303
 
304
+ **TypeDefs for JSDoc:**
305
+
234
306
  ```js
235
307
  /**
236
- * @param {import('@hkdigital/lib-core/network/typedef.js').JsonGetOptions} JsonGetOptions
308
+ * @param {import('@hkdigital/lib-core/network/typedef.js').HttpGetOptions}
309
+ * options
237
310
  */
311
+ function fetchData(options) {
312
+ // ...
313
+ }
238
314
  ```
239
315
 
240
- ### Import CSS
316
+ ### CSS Architecture (app.css)
241
317
 
242
- Vite should include postcss-import, but the only solution to get it working for now is to use a relative path to the node_modules folder.
318
+ **CRITICAL**: Your `src/app.css` must include the complete design system
319
+ architecture. Incomplete imports will cause build failures.
243
320
 
244
- For example:
321
+ **Required structure:**
245
322
 
246
323
  ```css
247
324
  /* src/app.css */
248
- @import '../node_modules/@hkdigital/lib-core/dist/css/utilities.css';
325
+
326
+ /* 1. CSS Layers - Controls style precedence */
327
+ @layer theme, base, utilities, components;
328
+
329
+ /* 2. Tailwind CSS Core */
330
+ @import 'tailwindcss';
331
+
332
+ /* 3. HKdigital Design System Theme (ALL required for colors to work) */
333
+ @import '../node_modules/@hkdigital/lib-core/dist/design/themes/hkdev/theme.css' layer(theme);
334
+ @import '../node_modules/@hkdigital/lib-core/dist/design/themes/hkdev/globals.css';
335
+ @import '../node_modules/@hkdigital/lib-core/dist/design/themes/hkdev/components.css' layer(components);
336
+ @import '../node_modules/@hkdigital/lib-core/dist/design/themes/hkdev/responsive.css';
337
+
338
+ /* 4. Skeleton UI Framework */
339
+ @import '@skeletonlabs/skeleton' source('../node_modules/@skeletonlabs/skeleton-svelte/dist');
340
+ @import '@skeletonlabs/skeleton/optional/presets' source('../node_modules/@skeletonlabs/skeleton-svelte/dist');
341
+
342
+ /* 5. Tailwind Configuration Reference */
343
+ @config "../tailwind.config.js";
344
+
345
+ /* 6. Optional: Additional utilities */
346
+ /*@import '../node_modules/@hkdigital/lib-core/dist/css/utilities.css';*/
249
347
  ```
250
348
 
349
+ **Why all theme files are required:**
350
+ - Missing any theme file will cause "Cannot apply unknown utility class"
351
+ errors
352
+ - Classes like `bg-surface-100`, `text-primary-500` won't work without
353
+ complete theme
354
+ - All four theme files (theme.css, globals.css, components.css,
355
+ responsive.css) are needed
356
+
357
+ See [src/lib/design/README.md](./src/lib/design/README.md) for complete
358
+ CSS architecture documentation.
359
+
360
+ ### Critical: data-theme Attribute
361
+
362
+ **IMPORTANT**: Add `data-theme="hkdev"` to your `<body>` element in
363
+ `src/app.html`. Without this, theme colors will not work correctly.
364
+
365
+ ```html
366
+ <!-- src/app.html -->
367
+ <!doctype html>
368
+ <html lang="en">
369
+ <head>
370
+ <meta charset="utf-8" />
371
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
372
+ %sveltekit.head%
373
+ </head>
374
+ <body data-theme="hkdev" data-sveltekit-preload-data="hover">
375
+ <div>%sveltekit.body%</div>
376
+ </body>
377
+ </html>
378
+ ```
379
+
380
+ **Why this is required:**
381
+ - Theme CSS custom properties are scoped to `[data-theme='hkdev']`
382
+ - Without this attribute, colors fall back to browser defaults
383
+ - **Symptom**: Colors like `bg-primary-500` show grey instead of magenta
384
+ - **Solution**: Add `data-theme="hkdev"` to `<body>` element
385
+
251
386
  ## Building the library
252
387
 
253
388
  To build your library:
@@ -0,0 +1,37 @@
1
+ /** @typedef {import('./typedef.js').RobotsConfig} RobotsConfig */
2
+ /**
3
+ * Check if hostname matches allowed hosts pattern
4
+ *
5
+ * @param {string} hostname - Hostname to check (e.g., test.mysite.com)
6
+ * @param {string[] | '*' | undefined} allowedHosts - Allowed host patterns
7
+ *
8
+ * @returns {boolean} True if host is allowed
9
+ */
10
+ export function isHostAllowed(hostname: string, allowedHosts: string[] | "*" | undefined): boolean;
11
+ /**
12
+ * Generate robots.txt with sitemap reference
13
+ *
14
+ * NOTE: If deployed behind a reverse proxy (nginx, Cloudflare, etc.),
15
+ * ensure your adapter is configured to trust proxy headers for correct
16
+ * origin detection:
17
+ *
18
+ * // svelte.config.js
19
+ * export default {
20
+ * kit: {
21
+ * adapter: adapter({
22
+ * // Trust X-Forwarded-* headers from proxy
23
+ * trustProxy: true
24
+ * })
25
+ * }
26
+ * };
27
+ *
28
+ * Without this, url.origin may be http://localhost instead of your
29
+ * actual domain, and the sitemap directive will be omitted.
30
+ *
31
+ * @param {URL} url - Request URL object
32
+ * @param {RobotsConfig} [config] - Robots configuration object
33
+ *
34
+ * @returns {string} robots.txt content
35
+ */
36
+ export function generateRobotsTxt(url: URL, config?: RobotsConfig): string;
37
+ export type RobotsConfig = import("./typedef.js").RobotsConfig;
@@ -0,0 +1,83 @@
1
+ /** @typedef {import('./typedef.js').RobotsConfig} RobotsConfig */
2
+
3
+ /**
4
+ * Check if hostname matches allowed hosts pattern
5
+ *
6
+ * @param {string} hostname - Hostname to check (e.g., test.mysite.com)
7
+ * @param {string[] | '*' | undefined} allowedHosts - Allowed host patterns
8
+ *
9
+ * @returns {boolean} True if host is allowed
10
+ */
11
+ export function isHostAllowed(hostname, allowedHosts) {
12
+ // If not configured or set to '*', allow all hosts
13
+ if (!allowedHosts || allowedHosts === '*') {
14
+ return true;
15
+ }
16
+
17
+ if (typeof allowedHosts === 'string') {
18
+ allowedHosts = [allowedHosts];
19
+ }
20
+
21
+ // Check if hostname matches any allowed pattern
22
+ return allowedHosts.some((pattern) => {
23
+ // Convert wildcard pattern to regex
24
+ // Example: *.mysite.com -> ^.*\.mysite\.com$
25
+ const regexPattern = pattern
26
+ .replace(/\./g, '\\.') // Escape dots
27
+ .replace(/\*/g, '.*'); // * becomes .*
28
+
29
+ const regex = new RegExp(`^${regexPattern}$`, 'i');
30
+ return regex.test(hostname);
31
+ });
32
+ }
33
+
34
+ /**
35
+ * Generate robots.txt with sitemap reference
36
+ *
37
+ * NOTE: If deployed behind a reverse proxy (nginx, Cloudflare, etc.),
38
+ * ensure your adapter is configured to trust proxy headers for correct
39
+ * origin detection:
40
+ *
41
+ * // svelte.config.js
42
+ * export default {
43
+ * kit: {
44
+ * adapter: adapter({
45
+ * // Trust X-Forwarded-* headers from proxy
46
+ * trustProxy: true
47
+ * })
48
+ * }
49
+ * };
50
+ *
51
+ * Without this, url.origin may be http://localhost instead of your
52
+ * actual domain, and the sitemap directive will be omitted.
53
+ *
54
+ * @param {URL} url - Request URL object
55
+ * @param {RobotsConfig} [config] - Robots configuration object
56
+ *
57
+ * @returns {string} robots.txt content
58
+ */
59
+ export function generateRobotsTxt(url, config = {}) {
60
+ const hostAllowed = isHostAllowed(url.hostname, config.allowedHosts);
61
+
62
+ // Block entire site if host is not allowed
63
+ if (!hostAllowed) {
64
+ return 'User-agent: *\nDisallow: /';
65
+ }
66
+
67
+ // Allow site, but add specific path blocks
68
+ let content = 'User-agent: *\nAllow: /';
69
+
70
+ // Add disallowed paths
71
+ if (config.disallowedPaths && config.disallowedPaths.length > 0) {
72
+ config.disallowedPaths.forEach((path) => {
73
+ content += `\nDisallow: ${path}`;
74
+ });
75
+ }
76
+
77
+ // Add sitemap reference if enabled and origin is available
78
+ if (url.origin && config.includeSitemap !== false) {
79
+ content += `\nSitemap: ${url.origin}/sitemap.xml`;
80
+ }
81
+
82
+ return content;
83
+ }
@@ -0,0 +1,17 @@
1
+ declare const _default: {};
2
+ export default _default;
3
+ export type RobotsConfig = {
4
+ /**
5
+ * Allowed host patterns. Use '*' or omit to allow all hosts.
6
+ * Supports wildcards (e.g., '*.example.com')
7
+ */
8
+ allowedHosts?: string[] | "*" | undefined;
9
+ /**
10
+ * Paths to block from indexing (e.g., '/admin', '/api/*')
11
+ */
12
+ disallowedPaths?: string[] | undefined;
13
+ /**
14
+ * Include sitemap reference in robots.txt (default: true)
15
+ */
16
+ includeSitemap?: boolean | undefined;
17
+ };
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @typedef {Object} RobotsConfig
3
+ * @property {string[] | '*'} [allowedHosts]
4
+ * Allowed host patterns. Use '*' or omit to allow all hosts.
5
+ * Supports wildcards (e.g., '*.example.com')
6
+ * @property {string[]} [disallowedPaths]
7
+ * Paths to block from indexing (e.g., '/admin', '/api/*')
8
+ * @property {boolean} [includeSitemap]
9
+ * Include sitemap reference in robots.txt (default: true)
10
+ */
11
+
12
+ export default {};
@@ -0,0 +1 @@
1
+ export { generateRobotsTxt, isHostAllowed } from "./robots/index.js";
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Public exports for robots.txt utilities
3
+ */
4
+
5
+ export { generateRobotsTxt, isHostAllowed } from './robots/index.js';
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Generate sitemap XML
3
+ *
4
+ * @param {string} origin - Base URL (e.g., https://example.com)
5
+ * @param {SitemapRoute[]} [routes=[]] - Array of routes
6
+ *
7
+ * @returns {string} XML sitemap
8
+ */
9
+ export function generateSitemap(origin: string, routes?: SitemapRoute[]): string;
10
+ export type SitemapRoute = import("./typedef.js").SitemapRoute;
11
+ export type SitemapRouteObject = import("./typedef.js").SitemapRouteObject;
@@ -0,0 +1,63 @@
1
+ // @see https://www.sitemaps.org/protocol.html
2
+
3
+ /** @typedef {import('./typedef.js').SitemapRoute} SitemapRoute */
4
+ /** @typedef {import('./typedef.js').SitemapRouteObject} SitemapRouteObject */
5
+
6
+ /**
7
+ * Normalize route to full route object with defaults
8
+ *
9
+ * @param {import('./typedef.js').SitemapRoute} route - Route path string or route object
10
+ *
11
+ * @returns {SitemapRouteObject} Normalized route object
12
+ */
13
+ function normalizeRoute(route) {
14
+ // Handle simple string format
15
+ if (typeof route === 'string') {
16
+ return {
17
+ path: route,
18
+ priority: route === '/' ? 1.0 : 0.8,
19
+ changefreq: route === '/' ? 'daily' : 'weekly'
20
+ };
21
+ }
22
+
23
+ // Handle object format with defaults
24
+ return {
25
+ priority: 0.8,
26
+ changefreq: 'weekly',
27
+ ...route
28
+ };
29
+ }
30
+
31
+ /**
32
+ * Generate sitemap XML
33
+ *
34
+ * @param {string} origin - Base URL (e.g., https://example.com)
35
+ * @param {SitemapRoute[]} [routes=[]] - Array of routes
36
+ *
37
+ * @returns {string} XML sitemap
38
+ */
39
+ export function generateSitemap(origin, routes = []) {
40
+ // Ensure root path is always included (failsafe)
41
+ const hasRoot = routes.some((route) => {
42
+ const path = typeof route === 'string' ? route : route.path;
43
+ return path === '/';
44
+ });
45
+
46
+ const normalizedRoutes = hasRoot ? routes : ['/', ...routes];
47
+
48
+ const urls = normalizedRoutes
49
+ .map(normalizeRoute)
50
+ .map(
51
+ (route) => `
52
+ <url>
53
+ <loc>${origin}${route.path}</loc>
54
+ <changefreq>${route.changefreq}</changefreq>
55
+ <priority>${route.priority}</priority>
56
+ </url>`
57
+ )
58
+ .join('');
59
+
60
+ return `<?xml version="1.0" encoding="UTF-8"?>
61
+ <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">${urls}
62
+ </urlset>`;
63
+ }
@@ -0,0 +1,20 @@
1
+ declare const _default: {};
2
+ export default _default;
3
+ export type SitemapRouteObject = {
4
+ /**
5
+ * - Route path (e.g., '/about')
6
+ */
7
+ path: string;
8
+ /**
9
+ * - Priority (0.0 to 1.0)
10
+ */
11
+ priority?: number | undefined;
12
+ /**
13
+ * - Change frequency
14
+ */
15
+ changefreq?: "hourly" | "daily" | "weekly" | "always" | "monthly" | "yearly" | "never" | undefined;
16
+ };
17
+ /**
18
+ * Route can be a simple string path or an object with details
19
+ */
20
+ export type SitemapRoute = string | SitemapRouteObject;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * @typedef {Object} SitemapRouteObject
3
+ * @property {string} path - Route path (e.g., '/about')
4
+ * @property {number} [priority] - Priority (0.0 to 1.0)
5
+ * @property {'always'|'hourly'|'daily'|'weekly'|'monthly'|'yearly'|'never'}
6
+ * [changefreq] - Change frequency
7
+ */
8
+
9
+ /**
10
+ * @typedef {string | SitemapRouteObject} SitemapRoute
11
+ * Route can be a simple string path or an object with details
12
+ */
13
+
14
+ export default {};
@@ -0,0 +1 @@
1
+ export { generateSitemap } from "./sitemap/index.js";
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Public exports for sitemap utilities
3
+ */
4
+
5
+ export { generateSitemap } from './sitemap/index.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hkdigital/lib-core",
3
- "version": "0.5.11",
3
+ "version": "0.5.13",
4
4
  "author": {
5
5
  "name": "HKdigital",
6
6
  "url": "https://hkdigital.nl"