katanakit-js 2.1.5
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/CHANGELOG.md +96 -0
- package/LICENSE +22 -0
- package/README.md +446 -0
- package/dist/adapters/astro/astro.service.d.ts +18 -0
- package/dist/adapters/astro/astro.service.d.ts.map +1 -0
- package/dist/adapters/astro/astro.service.js +83 -0
- package/dist/adapters/astro/astro.service.js.map +1 -0
- package/dist/adapters/astro/index.d.ts +4 -0
- package/dist/adapters/astro/index.d.ts.map +1 -0
- package/dist/adapters/astro/index.js +4 -0
- package/dist/adapters/astro/index.js.map +1 -0
- package/dist/adapters/astro/rss.service.d.ts +115 -0
- package/dist/adapters/astro/rss.service.d.ts.map +1 -0
- package/dist/adapters/astro/rss.service.js +231 -0
- package/dist/adapters/astro/rss.service.js.map +1 -0
- package/dist/adapters/express/app.d.ts +4 -0
- package/dist/adapters/express/app.d.ts.map +1 -0
- package/dist/adapters/express/app.js +4 -0
- package/dist/adapters/express/app.js.map +1 -0
- package/dist/adapters/express/index.d.ts +5 -0
- package/dist/adapters/express/index.d.ts.map +1 -0
- package/dist/adapters/express/index.js +5 -0
- package/dist/adapters/express/index.js.map +1 -0
- package/dist/adapters/express/main.d.ts +2 -0
- package/dist/adapters/express/main.d.ts.map +1 -0
- package/dist/adapters/express/main.js +3 -0
- package/dist/adapters/express/main.js.map +1 -0
- package/dist/adapters/express/products.controller.d.ts +10 -0
- package/dist/adapters/express/products.controller.d.ts.map +1 -0
- package/dist/adapters/express/products.controller.js +53 -0
- package/dist/adapters/express/products.controller.js.map +1 -0
- package/dist/adapters/express/router.d.ts +3 -0
- package/dist/adapters/express/router.d.ts.map +1 -0
- package/dist/adapters/express/router.js +14 -0
- package/dist/adapters/express/router.js.map +1 -0
- package/dist/adapters/express/server.d.ts +15 -0
- package/dist/adapters/express/server.d.ts.map +1 -0
- package/dist/adapters/express/server.js +59 -0
- package/dist/adapters/express/server.js.map +1 -0
- package/dist/adapters/index.d.ts +2 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +2 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/nuxt/index.d.ts +2 -0
- package/dist/adapters/nuxt/index.d.ts.map +1 -0
- package/dist/adapters/nuxt/index.js +2 -0
- package/dist/adapters/nuxt/index.js.map +1 -0
- package/dist/adapters/nuxt/nuxt.service.d.ts +83 -0
- package/dist/adapters/nuxt/nuxt.service.d.ts.map +1 -0
- package/dist/adapters/nuxt/nuxt.service.js +104 -0
- package/dist/adapters/nuxt/nuxt.service.js.map +1 -0
- package/dist/config/index.d.ts +3 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +3 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/seo.service.d.ts +72 -0
- package/dist/config/seo.service.d.ts.map +1 -0
- package/dist/config/seo.service.js +190 -0
- package/dist/config/seo.service.js.map +1 -0
- package/dist/config/site.config.d.ts +89 -0
- package/dist/config/site.config.d.ts.map +1 -0
- package/dist/config/site.config.js +59 -0
- package/dist/config/site.config.js.map +1 -0
- package/dist/core/index.d.ts +13 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +13 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/services/dates.service.d.ts +24 -0
- package/dist/core/services/dates.service.d.ts.map +1 -0
- package/dist/core/services/dates.service.js +64 -0
- package/dist/core/services/dates.service.js.map +1 -0
- package/dist/core/services/error.service.d.ts +25 -0
- package/dist/core/services/error.service.d.ts.map +1 -0
- package/dist/core/services/error.service.js +39 -0
- package/dist/core/services/error.service.js.map +1 -0
- package/dist/core/services/formatter.service.d.ts +36 -0
- package/dist/core/services/formatter.service.d.ts.map +1 -0
- package/dist/core/services/formatter.service.js +64 -0
- package/dist/core/services/formatter.service.js.map +1 -0
- package/dist/core/services/generator.service.d.ts +34 -0
- package/dist/core/services/generator.service.d.ts.map +1 -0
- package/dist/core/services/generator.service.js +83 -0
- package/dist/core/services/generator.service.js.map +1 -0
- package/dist/core/services/geometry.service.d.ts +45 -0
- package/dist/core/services/geometry.service.d.ts.map +1 -0
- package/dist/core/services/geometry.service.js +100 -0
- package/dist/core/services/geometry.service.js.map +1 -0
- package/dist/core/services/http.service.d.ts +22 -0
- package/dist/core/services/http.service.d.ts.map +1 -0
- package/dist/core/services/http.service.js +124 -0
- package/dist/core/services/http.service.js.map +1 -0
- package/dist/core/services/logger.service.d.ts +36 -0
- package/dist/core/services/logger.service.d.ts.map +1 -0
- package/dist/core/services/logger.service.js +60 -0
- package/dist/core/services/logger.service.js.map +1 -0
- package/dist/core/services/reactive.service.d.ts +22 -0
- package/dist/core/services/reactive.service.d.ts.map +1 -0
- package/dist/core/services/reactive.service.js +167 -0
- package/dist/core/services/reactive.service.js.map +1 -0
- package/dist/core/services/timing.service.d.ts +23 -0
- package/dist/core/services/timing.service.d.ts.map +1 -0
- package/dist/core/services/timing.service.js +217 -0
- package/dist/core/services/timing.service.js.map +1 -0
- package/dist/core/services/utils.service.d.ts +44 -0
- package/dist/core/services/utils.service.d.ts.map +1 -0
- package/dist/core/services/utils.service.js +143 -0
- package/dist/core/services/utils.service.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/infrastructure/dom/dom.service.d.ts +49 -0
- package/dist/infrastructure/dom/dom.service.d.ts.map +1 -0
- package/dist/infrastructure/dom/dom.service.js +133 -0
- package/dist/infrastructure/dom/dom.service.js.map +1 -0
- package/dist/infrastructure/index.d.ts +11 -0
- package/dist/infrastructure/index.d.ts.map +1 -0
- package/dist/infrastructure/index.js +11 -0
- package/dist/infrastructure/index.js.map +1 -0
- package/dist/infrastructure/observer/observer.service.d.ts +34 -0
- package/dist/infrastructure/observer/observer.service.d.ts.map +1 -0
- package/dist/infrastructure/observer/observer.service.js +145 -0
- package/dist/infrastructure/observer/observer.service.js.map +1 -0
- package/dist/infrastructure/sensors/sensors.service.d.ts +25 -0
- package/dist/infrastructure/sensors/sensors.service.d.ts.map +1 -0
- package/dist/infrastructure/sensors/sensors.service.js +133 -0
- package/dist/infrastructure/sensors/sensors.service.js.map +1 -0
- package/dist/infrastructure/storage/storage.service.d.ts +48 -0
- package/dist/infrastructure/storage/storage.service.d.ts.map +1 -0
- package/dist/infrastructure/storage/storage.service.js +108 -0
- package/dist/infrastructure/storage/storage.service.js.map +1 -0
- package/dist/infrastructure/theme/theme.service.d.ts +30 -0
- package/dist/infrastructure/theme/theme.service.d.ts.map +1 -0
- package/dist/infrastructure/theme/theme.service.js +104 -0
- package/dist/infrastructure/theme/theme.service.js.map +1 -0
- package/dist/infrastructure/viewport/viewport.service.d.ts +38 -0
- package/dist/infrastructure/viewport/viewport.service.d.ts.map +1 -0
- package/dist/infrastructure/viewport/viewport.service.js +145 -0
- package/dist/infrastructure/viewport/viewport.service.js.map +1 -0
- package/dist/infrastructure/worker/worker.service.d.ts +42 -0
- package/dist/infrastructure/worker/worker.service.d.ts.map +1 -0
- package/dist/infrastructure/worker/worker.service.js +151 -0
- package/dist/infrastructure/worker/worker.service.js.map +1 -0
- package/dist/prisma/db.d.ts +4 -0
- package/dist/prisma/db.d.ts.map +1 -0
- package/dist/prisma/db.js +12 -0
- package/dist/prisma/db.js.map +1 -0
- package/dist/prisma/schema.json +411 -0
- package/dist/types/index.d.ts +436 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/package.json +117 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [2.1.4] - Unreleased
|
|
9
|
+
|
|
10
|
+
Current development version. This is the first release published under the
|
|
11
|
+
**`katanakit-js`** npm name (previous development releases were published as
|
|
12
|
+
`katanakit-dev`).
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- **Nuxt adapter** — new `katanakit-js/adapters/nuxt` subpath exporting three
|
|
17
|
+
pure helpers that bridge KatanaKit Safe Results to Nuxt/Nitro server routes:
|
|
18
|
+
`useUnwrap(result, context?)`, `useSafeResponse(result)` and
|
|
19
|
+
`useEventResponse(event, result)`. They avoid a hard dependency on `h3`
|
|
20
|
+
(which ships with Nuxt).
|
|
21
|
+
- **Site config and SEO module** — new `src/config/` layer with
|
|
22
|
+
`siteConfig`/`SiteConfig` and pure SEO helpers (`useGenerateMetaTags`,
|
|
23
|
+
`useHeadTags`, `useTitle`, `useRssHeadLink`), re-exported from the main
|
|
24
|
+
barrel. Includes HTML/attribute escaping and JSON-LD breakout protection.
|
|
25
|
+
- **`publish:*` scripts** — `publish:patch`, `publish:minor`, `publish:major`
|
|
26
|
+
and `publish:beta` (tagged `beta`) with a `prepublishOnly` gate that runs
|
|
27
|
+
Biome, tests and the build before publishing.
|
|
28
|
+
- New Vitest suites for RSS, Nuxt and SEO services (57 tests across 7 files).
|
|
29
|
+
|
|
30
|
+
### Changed
|
|
31
|
+
|
|
32
|
+
- **Renamed the npm package** from `katanakit-dev` to **`katanakit-js`**
|
|
33
|
+
(version bumped to 2.1.4). The `exports` map now exposes the root entry plus
|
|
34
|
+
two framework subpaths: `katanakit-js/adapters/express` and
|
|
35
|
+
`katanakit-js/adapters/nuxt`.
|
|
36
|
+
- **Restructured dependencies** — only `@js-temporal/polyfill` remains a
|
|
37
|
+
runtime dependency; `express`, `cors`, `dotenv` and `@prisma/orm-postgres`
|
|
38
|
+
are now **optional peer dependencies** installed only by consumers that use
|
|
39
|
+
the Express adapter or the Prisma layer.
|
|
40
|
+
- Upgraded the toolchain: Biome to v2 (migrated `biome.json` schema), Vitest to
|
|
41
|
+
v5, TypeScript to v7; Node engine floor raised to `>=22.18.0`.
|
|
42
|
+
- Hardened the Express reference adapter: CORS defaults to the comma-separated
|
|
43
|
+
`CORS_ORIGINS` allow-list (localhost fallback), `x-powered-by` is disabled
|
|
44
|
+
and JSON/urlencoded bodies are capped at 100 kb.
|
|
45
|
+
|
|
46
|
+
### Fixed
|
|
47
|
+
|
|
48
|
+
- **Pure ESM `.js` exports** — all relative imports in `src/` now use explicit
|
|
49
|
+
`.js` extensions (`module: nodenext`), so Node's ESM loader and bundlers
|
|
50
|
+
resolve the emitted `dist/` correctly.
|
|
51
|
+
- **`WorkerService` pool correlation** — `useRunPool` now correlates responses
|
|
52
|
+
by echoing a `taskId` from the worker blob, fixing concurrent race
|
|
53
|
+
conditions.
|
|
54
|
+
- **`useEncrypt` salt** — generates a random 128-bit salt when none is provided
|
|
55
|
+
instead of relying on a fixed default.
|
|
56
|
+
- **SEO JSON-LD escaping** — `<`, `>` and `&` are escaped inside the
|
|
57
|
+
`application/ld+json` script to prevent `</script>` breakout.
|
|
58
|
+
|
|
59
|
+
## [2.0.0] - 2026-09-03
|
|
60
|
+
|
|
61
|
+
Developed and published as `katanakit-dev`.
|
|
62
|
+
|
|
63
|
+
### Changed
|
|
64
|
+
|
|
65
|
+
- **Reorganized the project with hexagonal architecture** into `types/`,
|
|
66
|
+
`core/services/` (ports + pure services), `infrastructure/` (browser/runtime
|
|
67
|
+
adapters), `adapters/` (Astro + Express) and `prisma/`, replacing the previous
|
|
68
|
+
flat `helpers/`/`api/` layout.
|
|
69
|
+
- Exposed the public API through barrel files (`src/index.ts` and per-layer
|
|
70
|
+
`index.ts`).
|
|
71
|
+
|
|
72
|
+
### Added
|
|
73
|
+
|
|
74
|
+
- Added `biome.json` and `vitest.config.ts`; wired `typecheck`, `build`, `test`,
|
|
75
|
+
`lint`, `format` and `check` scripts.
|
|
76
|
+
- Added Vitest unit tests for the HTTP client, logger, storage, geometry, errors
|
|
77
|
+
and reactive signals.
|
|
78
|
+
- Added English documentation (`README.md`, `CONTRIBUTING.md`, `docs/`,
|
|
79
|
+
`SECURITY.md`).
|
|
80
|
+
|
|
81
|
+
### Fixed
|
|
82
|
+
|
|
83
|
+
- Fixed TypeScript compile errors: broken imports, broken singleton guards
|
|
84
|
+
(`TimingService` and `ViewportService`), the `FIND_ENTRY` scope bug in
|
|
85
|
+
`AstroService`, and Express `Request`/`Response` typing.
|
|
86
|
+
- Removed all side effects on import (top-level `fetch` to dummyjson.com,
|
|
87
|
+
`console.log` calls, storage writes and timers).
|
|
88
|
+
- Fixed the Express server: `listen` now uses the configured port/host, the user
|
|
89
|
+
router is mounted, and handler `(req, res)` argument order is correct.
|
|
90
|
+
- Fixed the logger call sites (level is now the first argument of `useLog`).
|
|
91
|
+
|
|
92
|
+
### Removed
|
|
93
|
+
|
|
94
|
+
- Removed duplicate type definitions (unified in `src/types/`), dead code, empty
|
|
95
|
+
files and the broken signals draft.
|
|
96
|
+
- Moved demos to `examples/` and dropped the obsolete `wiki/` documentation.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 senseikatana - Sergio Jurado
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
# KatanaKit (`katanakit-js`)
|
|
2
|
+
|
|
3
|
+
KatanaKit is a sharp, framework-agnostic **TypeScript service toolkit** organized
|
|
4
|
+
with **hexagonal architecture** and built on proven design patterns (Singleton,
|
|
5
|
+
Observer, Factory, Strategy, Facade and Adapter).
|
|
6
|
+
|
|
7
|
+
It runs in the browser, in Node.js (>= 22.18) and in Bun, ships with an Astro
|
|
8
|
+
`getStaticPaths` adapter, an RSS 2.0 generator, a site-config/SEO module, an
|
|
9
|
+
optional Express server reference and a Nuxt (Nitro/H3) adapter. Importing a
|
|
10
|
+
module never triggers side effects, and every async operation returns a **Safe
|
|
11
|
+
Result** instead of throwing.
|
|
12
|
+
|
|
13
|
+
- **Version:** 2.1.4
|
|
14
|
+
- **npm package:** `katanakit-js`
|
|
15
|
+
- **Repository:** [senseikatana/katanakit](https://github.com/senseikatana/katanakit)
|
|
16
|
+
(development happens on the `dev` branch)
|
|
17
|
+
- **License:** MIT
|
|
18
|
+
|
|
19
|
+
## Why
|
|
20
|
+
|
|
21
|
+
Modern frontend and full-stack apps keep reimplementing the same plumbing:
|
|
22
|
+
fetching an API safely, logging, persisting to storage, querying the DOM,
|
|
23
|
+
reacting to state changes, generating slugs, formatting dates and money,
|
|
24
|
+
building RSS/SEO output. KatanaKit provides all of that as a single, typed and
|
|
25
|
+
tree-shakeable set of `use*` services with a consistent API: one `useInit` for
|
|
26
|
+
your APIs, one `useFetch` that returns a Safe Result, one way to do logging,
|
|
27
|
+
storage and DOM across every framework.
|
|
28
|
+
|
|
29
|
+
## Features
|
|
30
|
+
|
|
31
|
+
- **Safe URL construction** — builds URLs from a JSON-defined API registry using
|
|
32
|
+
the native `URL` API and `encodeURIComponent`; only `http:`/`https:` schemes
|
|
33
|
+
are allowed (prevents `javascript:` URLs and SSRF).
|
|
34
|
+
- **Safe Result** — every `useFetch` returns a discriminated union
|
|
35
|
+
`{ data, error, url, status, ok }` (Astro Actions style) instead of throwing
|
|
36
|
+
on HTTP errors.
|
|
37
|
+
- **Hexagonal architecture** — a pure `core` layer (services), an
|
|
38
|
+
`infrastructure` layer (browser/runtime adapters), an `adapters` layer
|
|
39
|
+
(Astro, Express, Nuxt), a `config` layer (site config + SEO) and a single
|
|
40
|
+
source of truth for contracts and types in `src/types/`.
|
|
41
|
+
- **Design patterns** — Singleton facades, Strategy (logger, storage,
|
|
42
|
+
crypto/UUID), Factory (errors, debounce/throttle), Observer (signals,
|
|
43
|
+
IntersectionObserver, media queries), Facade and Adapter, all exposed as
|
|
44
|
+
`use*` methods (like React hooks) with safe destructured exports.
|
|
45
|
+
- **Consistent `use*` API** — every public method is prefixed with `use`
|
|
46
|
+
(`useInit`, `useFetch`, `useLog`, `useCreateSignal`, ...); the only exception
|
|
47
|
+
is `getInstance()`.
|
|
48
|
+
- **Pure ESM** — relative imports use explicit `.js` extensions and
|
|
49
|
+
`module: nodenext`; bundlers and Node resolve the package cleanly.
|
|
50
|
+
- **Zero side effects on import** — importing a module never triggers network
|
|
51
|
+
calls, timers, storage writes or DOM mutations.
|
|
52
|
+
- **SSR-safe** — browser-only adapters guard or fall back gracefully
|
|
53
|
+
(in-memory storage, main-thread worker execution) when `window` is
|
|
54
|
+
unavailable.
|
|
55
|
+
- **Optional heavy dependencies** — only `@js-temporal/polyfill` is a runtime
|
|
56
|
+
dependency; `express`, `cors`, `dotenv` and `@prisma/orm-postgres` are
|
|
57
|
+
optional peer dependencies.
|
|
58
|
+
- **Fully typed** — strict TypeScript with generated `.d.ts` declarations.
|
|
59
|
+
|
|
60
|
+
## Installation
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
npm install katanakit-js
|
|
64
|
+
# or
|
|
65
|
+
bun add katanakit-js
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Requirements: **Node.js >= 22.18.0** (pure ESM package). TypeScript consumers
|
|
69
|
+
should use `moduleResolution: "nodenext"` (or `"bundler"`).
|
|
70
|
+
|
|
71
|
+
### Framework adapters
|
|
72
|
+
|
|
73
|
+
The root import is framework-agnostic. Framework-specific entry points are
|
|
74
|
+
published as package **subpaths** of the same `katanakit-js` package:
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
// Nuxt / Nitro adapter helpers (no extra dependency; h3 ships with Nuxt)
|
|
78
|
+
import { useUnwrap } from "katanakit-js/adapters/nuxt";
|
|
79
|
+
|
|
80
|
+
// Express reference server (requires express as a peer dependency)
|
|
81
|
+
import { ServerExpress } from "katanakit-js/adapters/express";
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
`express`, `cors`, `dotenv` and `@prisma/orm-postgres` are **optional peer
|
|
85
|
+
dependencies** — install them only if you use the Express adapter or the Prisma
|
|
86
|
+
layer.
|
|
87
|
+
|
|
88
|
+
### Building from source
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
git clone https://github.com/senseikatana/katanakit.git
|
|
92
|
+
cd katanakit
|
|
93
|
+
bun install
|
|
94
|
+
bun run build # outputs to dist/
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Quick Start
|
|
98
|
+
|
|
99
|
+
Everything below is importable from the main barrel `katanakit-js`.
|
|
100
|
+
|
|
101
|
+
### HTTP client
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
import { useInit, useGet, usePost, useBuildUrl } from "katanakit-js";
|
|
105
|
+
|
|
106
|
+
// 1. Register your APIs once.
|
|
107
|
+
useInit({
|
|
108
|
+
pokeapi: {
|
|
109
|
+
baseUri: "https://pokeapi.co/api/v2",
|
|
110
|
+
endpoints: {
|
|
111
|
+
pokemonById: "/pokemon/:id/",
|
|
112
|
+
pokemons: "/pokemons/",
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// 2. Build a safe URL (path params encoded, query params merged).
|
|
118
|
+
const url = useBuildUrl("pokeapi", "pokemonById", {
|
|
119
|
+
params: { id: "pikachu" },
|
|
120
|
+
});
|
|
121
|
+
// => "https://pokeapi.co/api/v2/pokemon/pikachu/"
|
|
122
|
+
|
|
123
|
+
// 3. Fetch with a Safe Result (no throwing on HTTP errors).
|
|
124
|
+
const result = await useGet<{ name: string }>("pokeapi", "pokemonById", {
|
|
125
|
+
params: { id: 25 },
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
if (result.ok) {
|
|
129
|
+
console.log(result.data.name);
|
|
130
|
+
} else {
|
|
131
|
+
console.error(result.error.message, result.error.status);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// 4. POST JSON.
|
|
135
|
+
await usePost("pokeapi", "pokemons", { name: "charmander" });
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Logger, storage and DOM
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
import {
|
|
142
|
+
useLog,
|
|
143
|
+
useSetStorage,
|
|
144
|
+
useGetStorage,
|
|
145
|
+
useRemoveStorage,
|
|
146
|
+
useAddClass,
|
|
147
|
+
useGetRoot,
|
|
148
|
+
} from "katanakit-js";
|
|
149
|
+
|
|
150
|
+
useLog("Hello", { user: "John" }); // info level
|
|
151
|
+
useLog("error", "Something failed", { code: 500 }); // error level
|
|
152
|
+
|
|
153
|
+
useSetStorage("theme", "dark");
|
|
154
|
+
const theme = useGetStorage<string>("theme"); // "dark"
|
|
155
|
+
useRemoveStorage("theme");
|
|
156
|
+
|
|
157
|
+
useAddClass(useGetRoot()!, "dark-mode");
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Storage is SSR-safe: when `window` is unavailable an in-memory fallback is used
|
|
161
|
+
automatically, so imports never crash in Node/Bun.
|
|
162
|
+
|
|
163
|
+
## Astro adapter
|
|
164
|
+
|
|
165
|
+
KatanaKit converts arbitrary collections into the payload Astro's
|
|
166
|
+
`getStaticPaths` expects, wrapped in a Safe Result.
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
// src/pages/blog/[slug].astro
|
|
170
|
+
import { AstroService } from "katanakit-js";
|
|
171
|
+
|
|
172
|
+
export async function getStaticPaths() {
|
|
173
|
+
const { useGetStaticPaths } = AstroService.getInstance();
|
|
174
|
+
|
|
175
|
+
return useGetStaticPaths(getCollection, "blog", {
|
|
176
|
+
param: "slug",
|
|
177
|
+
valueFrom: (entry) => entry.slug ?? entry.id,
|
|
178
|
+
propsFrom: (entry) => entry.data,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Also available: `usePathsFrom`, `useFindEntry`, `useGeneratePagination`,
|
|
184
|
+
`usePathsFromValues` and `useExtractUniqueValues`. See the
|
|
185
|
+
[API Reference](docs/API-Reference.md#astro--astroservice).
|
|
186
|
+
|
|
187
|
+
## RSS feeds for Astro
|
|
188
|
+
|
|
189
|
+
`RssService` generates pure RSS 2.0 XML with **no external dependencies**
|
|
190
|
+
(`@astrojs/rss` is not required).
|
|
191
|
+
|
|
192
|
+
```ts
|
|
193
|
+
// src/pages/rss.xml.ts
|
|
194
|
+
import { RssService } from "katanakit-js";
|
|
195
|
+
import { getCollection } from "astro:content";
|
|
196
|
+
|
|
197
|
+
const { useCreateRssEndpoint } = RssService.getInstance();
|
|
198
|
+
|
|
199
|
+
export const GET = useCreateRssEndpoint({
|
|
200
|
+
title: "My Blog",
|
|
201
|
+
description: "Posts about TypeScript",
|
|
202
|
+
site: "https://example.com",
|
|
203
|
+
items: async () => {
|
|
204
|
+
const posts = await getCollection("blog");
|
|
205
|
+
return posts.map((post) => ({
|
|
206
|
+
title: post.data.title,
|
|
207
|
+
pubDate: post.data.date,
|
|
208
|
+
link: `/blog/${post.slug}/`,
|
|
209
|
+
description: post.data.description,
|
|
210
|
+
}));
|
|
211
|
+
},
|
|
212
|
+
});
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Add the feed `<link>` tag to your layout's `<head>`:
|
|
216
|
+
|
|
217
|
+
```astro
|
|
218
|
+
---
|
|
219
|
+
import { useRssLinkTag } from "katanakit-js";
|
|
220
|
+
---
|
|
221
|
+
<head>
|
|
222
|
+
<Fragment set:html={useRssLinkTag({ title: "My Blog" })} />
|
|
223
|
+
</head>
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
`useCreateRssEndpointFromConfig(siteConfig, items)` builds the same endpoint
|
|
227
|
+
from your `SiteConfig`. See the [API Reference](docs/API-Reference.md#rss--rssservice).
|
|
228
|
+
|
|
229
|
+
## Site config and SEO
|
|
230
|
+
|
|
231
|
+
Centralize your site metadata in a typed `SiteConfig` and derive every `<head>`
|
|
232
|
+
tag from it.
|
|
233
|
+
|
|
234
|
+
```ts
|
|
235
|
+
// src/config/site.config.ts
|
|
236
|
+
import { type SiteConfig } from "katanakit-js";
|
|
237
|
+
|
|
238
|
+
export const siteConfig: SiteConfig = {
|
|
239
|
+
site: "https://myblog.com",
|
|
240
|
+
title: "My Blog",
|
|
241
|
+
description: "A blog about TypeScript and Astro",
|
|
242
|
+
lang: "en",
|
|
243
|
+
author: "John Doe",
|
|
244
|
+
ogImage: "/og-default.png",
|
|
245
|
+
twitter: "johndoe",
|
|
246
|
+
rss: { enabled: true, path: "/rss.xml", limit: 20 },
|
|
247
|
+
seo: { noindex: false, canonical: true, openGraph: true, twitterCard: true, jsonLd: true },
|
|
248
|
+
nav: [
|
|
249
|
+
{ label: "Home", href: "/" },
|
|
250
|
+
{ label: "Blog", href: "/blog" },
|
|
251
|
+
{ label: "GitHub", href: "https://github.com/senseikatana/katanakit", external: true },
|
|
252
|
+
],
|
|
253
|
+
};
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
Then inject complete meta tags (title, description, canonical, Open Graph,
|
|
257
|
+
Twitter Card and JSON-LD) in your layout:
|
|
258
|
+
|
|
259
|
+
```astro
|
|
260
|
+
---
|
|
261
|
+
// src/layouts/BaseLayout.astro
|
|
262
|
+
import { siteConfig } from "@/config/site.config";
|
|
263
|
+
import { useHeadTags } from "katanakit-js";
|
|
264
|
+
|
|
265
|
+
interface Props {
|
|
266
|
+
title: string;
|
|
267
|
+
description?: string;
|
|
268
|
+
ogType?: "website" | "article";
|
|
269
|
+
publishedTime?: string;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const { title, description, ogType, publishedTime } = Astro.props;
|
|
273
|
+
const headTags = useHeadTags(siteConfig, {
|
|
274
|
+
title,
|
|
275
|
+
description,
|
|
276
|
+
url: new URL(Astro.url.pathname, siteConfig.site).href,
|
|
277
|
+
ogType,
|
|
278
|
+
publishedTime,
|
|
279
|
+
});
|
|
280
|
+
---
|
|
281
|
+
<html lang={siteConfig.lang}>
|
|
282
|
+
<head>
|
|
283
|
+
<meta charset="utf-8" />
|
|
284
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
285
|
+
<Fragment set:html={headTags} />
|
|
286
|
+
</head>
|
|
287
|
+
<body>
|
|
288
|
+
<slot />
|
|
289
|
+
</body>
|
|
290
|
+
</html>
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
The RSS endpoint consumes the same config:
|
|
294
|
+
|
|
295
|
+
```ts
|
|
296
|
+
// src/pages/rss.xml.ts
|
|
297
|
+
import { RssService } from "katanakit-js";
|
|
298
|
+
import { siteConfig } from "@/config/site.config";
|
|
299
|
+
import { getCollection } from "astro:content";
|
|
300
|
+
|
|
301
|
+
const { useCreateRssEndpointFromConfig } = RssService.getInstance();
|
|
302
|
+
|
|
303
|
+
export const GET = useCreateRssEndpointFromConfig(siteConfig, async () => {
|
|
304
|
+
const posts = await getCollection("blog");
|
|
305
|
+
return posts.map((post) => ({
|
|
306
|
+
title: post.data.title,
|
|
307
|
+
pubDate: post.data.date,
|
|
308
|
+
link: `/blog/${post.slug}/`,
|
|
309
|
+
description: post.data.description,
|
|
310
|
+
}));
|
|
311
|
+
});
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
## Nuxt adapter
|
|
315
|
+
|
|
316
|
+
New in 2.1.4. Three pure helper functions bridge KatanaKit's Safe Results to
|
|
317
|
+
Nuxt/Nitro server routes. They are **not** singleton services — just exported
|
|
318
|
+
functions — and they avoid a hard dependency on `h3` (it ships with Nuxt).
|
|
319
|
+
|
|
320
|
+
```ts
|
|
321
|
+
import { useInit, useGet } from "katanakit-js";
|
|
322
|
+
import { useUnwrap } from "katanakit-js/adapters/nuxt";
|
|
323
|
+
|
|
324
|
+
// server/plugins/api.ts — register your APIs once
|
|
325
|
+
useInit({
|
|
326
|
+
pokeapi: {
|
|
327
|
+
baseUri: "https://pokeapi.co/api/v2",
|
|
328
|
+
endpoints: { pokemonById: "/pokemon/:id/" },
|
|
329
|
+
},
|
|
330
|
+
});
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
```ts
|
|
334
|
+
// server/api/pokemon/[id].ts
|
|
335
|
+
import { useGet } from "katanakit-js";
|
|
336
|
+
import { useUnwrap } from "katanakit-js/adapters/nuxt";
|
|
337
|
+
|
|
338
|
+
export default defineEventHandler(async (event) => {
|
|
339
|
+
const id = getRouterParam(event, "id");
|
|
340
|
+
const result = await useGet("pokeapi", "pokemonById", { params: { id } });
|
|
341
|
+
return useUnwrap(result, `Pokemon ${id}`); // returns data, or throws an H3-compatible error
|
|
342
|
+
});
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
The other helpers are `useSafeResponse(result)` (shape the result as a clean
|
|
346
|
+
JSON response object) and `useEventResponse(event, result)` (set the H3 event
|
|
347
|
+
status code and return data/error). See the
|
|
348
|
+
[API Reference](docs/API-Reference.md#nuxt-adapter--katanakit-jsadaptersnuxt).
|
|
349
|
+
|
|
350
|
+
## Express server (optional)
|
|
351
|
+
|
|
352
|
+
The Express reference adapter is exposed only through its own subpath so it
|
|
353
|
+
never bloats the main bundle:
|
|
354
|
+
|
|
355
|
+
```ts
|
|
356
|
+
import { ServerExpress } from "katanakit-js/adapters/express";
|
|
357
|
+
|
|
358
|
+
ServerExpress.getInstance().useStart(); // http://localhost:3000
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
It expects `express` (and `cors`/`dotenv` if used) to be installed in your
|
|
362
|
+
project. A `ProductController` and a demo `router` are also exported.
|
|
363
|
+
|
|
364
|
+
## Services at a glance
|
|
365
|
+
|
|
366
|
+
| Area | Service / module | Highlights |
|
|
367
|
+
| --------- | --------------------------- | ------------------------------------------------------------------- |
|
|
368
|
+
| HTTP | `FetchApiManager` | `useInit`, `useGetApis`, `useBuildUrl`, `useFetch`, `useGet`, `usePost`, `usePut`, `useDelete` |
|
|
369
|
+
| Logging | `LoggerService` | `useLog`, `useError`, `useClear`, `useTable`, pluggable `LogStrategy` |
|
|
370
|
+
| Storage | `StorageService` | `useGetStorage`, `useSetStorage`, `useRemoveStorage`, `useClearStorage` (SSR-safe) |
|
|
371
|
+
| DOM | `DomService` | `useQuerySelector`, `useAddClass`, `useOn`, `useSetText`, ... |
|
|
372
|
+
| Reactive | `ReactiveService` | `useCreateSignal`, `useCreateEffect`, `useCreateMemo`, `useCreateToggle`, `useCreateStorageSignal`, `useCreateDebouncedSignal`, `useCreateBatch` |
|
|
373
|
+
| Format | `FormatterService` | `useFormatNumber`, `useFormatCurrency`, `useJsonStringify`, `useUpperCase`, `useCapitalize`, ... |
|
|
374
|
+
| Convert | `ConverterService` | `useToCelsius`, `useToFahrenheit`, `useToMiles`, `useToKilos`, ... |
|
|
375
|
+
| Errors | `ErrorFactoryService` | `useBadRequest`, `useUnauthorized`, `useForbidden`, `useNotFound`, `useInternal`, `useCustom` |
|
|
376
|
+
| Generate | `GeneratorService` | `useUuid`, `useSlugify`, `useNumericId`, `useToken`, `useEncrypt` |
|
|
377
|
+
| Dates | `DatesService` | `useFormat`, `useNow`, `useAddDays`, `useIsBefore`, `useLastDayOfMonth`, ... (Temporal) |
|
|
378
|
+
| Geometry | `GeometryUtils` | `area`, `perimeter`, `volume` static helpers (`useCircle`, `useSphere`, ...) |
|
|
379
|
+
| Timing | `TimingService` | `useDelay`, `useSetTimeout`, `useInterval`, `useDebounce`, `useThrottle`, `useRepeat`, `useRace` |
|
|
380
|
+
| Utils | `DataUtils` / `SystemUtils` | `useUnique`, `useGroupBy`, `useDeepClone`, `useSleep`, `useRetry`, `useRound`, ... |
|
|
381
|
+
| Viewport | `ViewportService` | scroll, fullscreen, visibility, `usePrefersReducedMotion` |
|
|
382
|
+
| Sensors | `SensorsUtils` | `useGetGeolocation`, `useGetMediaStream`, `useVibrate`, `useGetBattery`, ... |
|
|
383
|
+
| Observer | `ObserverService` | `useCreate`, `useObserve`, `useObserveAll`, `useDisconnect` |
|
|
384
|
+
| LazyLoad | `LazyLoaderService` | `useInit`, `useStop`, `useStopAll` for `img[data-src]` |
|
|
385
|
+
| Worker | `WorkerService` | `useRun`, `useCreatePool`, `useRunPool`, `useTerminate` |
|
|
386
|
+
| Theme | `ThemeService` | `useInitTheme`, `useSetThemeMode`, `useToggleTheme`, `useResetTheme` |
|
|
387
|
+
| Astro | `AstroService` | `useGetStaticPaths`, `usePathsFrom`, `useGeneratePagination`, ... |
|
|
388
|
+
| RSS | `RssService` | `useGenerateRss`, `useRssLinkTag`, `useCreateRssEndpoint`, `useCreateRssEndpointFromConfig` |
|
|
389
|
+
| SEO | `useHeadTags` and friends | `useGenerateMetaTags`, `useTitle`, `useRssHeadLink` + typed `SiteConfig` |
|
|
390
|
+
| Config | `siteConfig` / `SiteConfig` | single source for site metadata, RSS and SEO defaults |
|
|
391
|
+
| Nuxt | `katanakit-js/adapters/nuxt`| `useUnwrap`, `useSafeResponse`, `useEventResponse` (pure functions) |
|
|
392
|
+
| Server | `katanakit-js/adapters/express` | `ServerExpress`, `router`, `ProductController` (optional reference) |
|
|
393
|
+
|
|
394
|
+
The Astro, RSS, SEO and config modules plus all core/infrastructure services
|
|
395
|
+
are re-exported from the main barrel. The Nuxt and Express adapters are only
|
|
396
|
+
available through their subpaths.
|
|
397
|
+
|
|
398
|
+
## Project structure (hexagonal)
|
|
399
|
+
|
|
400
|
+
```
|
|
401
|
+
katanakit/
|
|
402
|
+
├── package.json # name: katanakit-js, ESM, exports map
|
|
403
|
+
├── prisma.config.ts # Prisma ORM config (contract + connection)
|
|
404
|
+
├── src/
|
|
405
|
+
│ ├── index.ts # main barrel (public API)
|
|
406
|
+
│ ├── types/ # single source of truth: contracts & types
|
|
407
|
+
│ ├── core/
|
|
408
|
+
│ │ └── services/ # pure services (no I/O): logger, http, ...
|
|
409
|
+
│ ├── infrastructure/ # browser/runtime adapters
|
|
410
|
+
│ │ ├── dom/ storage/ viewport/ sensors/
|
|
411
|
+
│ │ └── observer/ worker/ theme/
|
|
412
|
+
│ ├── adapters/ # framework adapters
|
|
413
|
+
│ │ ├── astro/ # AstroService + RssService
|
|
414
|
+
│ │ ├── express/ # ServerExpress reference (subpath export)
|
|
415
|
+
│ │ └── nuxt/ # Nuxt helpers (subpath export)
|
|
416
|
+
│ ├── config/ # siteConfig (SiteConfig) + SEO helpers
|
|
417
|
+
│ └── prisma/ # Prisma schema, contract types and db client
|
|
418
|
+
├── tests/ # Vitest suite (57 tests across 7 files)
|
|
419
|
+
├── examples/ # runnable demos
|
|
420
|
+
├── docs/ # Getting Started, Architecture, API Reference, Roadmap
|
|
421
|
+
├── CONTRIBUTING.md
|
|
422
|
+
└── SECURITY.md
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
All internal source imports are relative and carry an explicit `.js` extension
|
|
426
|
+
(pure ESM, `module: nodenext`). The `@/` alias (mapped to `src/`) is configured
|
|
427
|
+
for the test suite and the examples.
|
|
428
|
+
|
|
429
|
+
## Documentation
|
|
430
|
+
|
|
431
|
+
- [Getting Started](docs/Getting-Started.md)
|
|
432
|
+
- [Architecture](docs/Architecture.md)
|
|
433
|
+
- [API Reference](docs/API-Reference.md)
|
|
434
|
+
- [Roadmap](docs/Roadmap.md)
|
|
435
|
+
- [Security](SECURITY.md)
|
|
436
|
+
- [Changelog](CHANGELOG.md)
|
|
437
|
+
|
|
438
|
+
## Contributing
|
|
439
|
+
|
|
440
|
+
Contributions are welcome. Please read [CONTRIBUTING.md](CONTRIBUTING.md) for
|
|
441
|
+
the development contract (hexagonal layering, `use*` convention, `.js` ESM
|
|
442
|
+
imports, tests, scripts) before opening a pull request.
|
|
443
|
+
|
|
444
|
+
## License
|
|
445
|
+
|
|
446
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { AstroPath, AstroServiceResult, CollectionEntryLike, IAstroService, PaginationProps, PathsOptions } from "../../types/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* Astro adapter (Facade + Adapter + Singleton). Converts arbitrary collections
|
|
4
|
+
* into the `getStaticPaths` format Astro expects, wrapped in a Safe Result.
|
|
5
|
+
*/
|
|
6
|
+
export declare class AstroService implements IAstroService {
|
|
7
|
+
private static instance;
|
|
8
|
+
private constructor();
|
|
9
|
+
static getInstance(): AstroService;
|
|
10
|
+
usePathsFrom: <T, TParam extends string = "slug", TProps = T>(items: T[], options?: PathsOptions<T, TParam, TProps>) => AstroPath<TParam, TProps>[];
|
|
11
|
+
useGetStaticPaths: <TData = unknown, TParam extends string = "slug", TProps = CollectionEntryLike<TData>>(getCollectionFn: (collection: string) => Promise<CollectionEntryLike<TData>[]>, collectionName: string, options?: PathsOptions<CollectionEntryLike<TData>, TParam, TProps>) => Promise<AstroServiceResult<AstroPath<TParam, TProps>[]>>;
|
|
12
|
+
useFindEntry: <T>(items: T[], value: string, keyFrom?: (item: T) => string | number) => T | null;
|
|
13
|
+
useGeneratePagination: <T, TParam extends string = "page">(items: T[], pageSize?: number, param?: TParam) => AstroPath<TParam, PaginationProps<T>>[];
|
|
14
|
+
usePathsFromValues: <TParam extends string = "slug">(values: (string | number)[], param?: TParam) => AstroPath<TParam, string | number>[];
|
|
15
|
+
useExtractUniqueValues: <T, V>(items: T[], keyFrom: (item: T) => V | V[]) => V[];
|
|
16
|
+
}
|
|
17
|
+
export declare const usePathsFrom: <T, TParam extends string = "slug", TProps = T>(items: T[], options?: PathsOptions<T, TParam, TProps>) => AstroPath<TParam, TProps>[], useGetStaticPaths: <TData = unknown, TParam extends string = "slug", TProps = CollectionEntryLike<TData>>(getCollectionFn: (collection: string) => Promise<CollectionEntryLike<TData>[]>, collectionName: string, options?: PathsOptions<CollectionEntryLike<TData>, TParam, TProps>) => Promise<AstroServiceResult<AstroPath<TParam, TProps>[]>>, useFindEntry: <T>(items: T[], value: string, keyFrom?: ((item: T) => string | number) | undefined) => T | null, useGeneratePagination: <T, TParam extends string = "page">(items: T[], pageSize?: number, param?: TParam) => AstroPath<TParam, PaginationProps<T>>[], usePathsFromValues: <TParam extends string = "slug">(values: (string | number)[], param?: TParam) => AstroPath<TParam, string | number>[], useExtractUniqueValues: <T, V>(items: T[], keyFrom: (item: T) => V | V[]) => V[];
|
|
18
|
+
//# sourceMappingURL=astro.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"astro.service.d.ts","sourceRoot":"","sources":["../../../src/adapters/astro/astro.service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,SAAS,EACT,kBAAkB,EAClB,mBAAmB,EACnB,aAAa,EACb,eAAe,EACf,YAAY,EACZ,MAAM,sBAAsB,CAAC;AAE9B;;;GAGG;AACH,qBAAa,YAAa,YAAW,aAAa;IACjD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAe;IAEtC,OAAO,eAAiB;IAExB,OAAc,WAAW,IAAI,YAAY,CAKxC;IAEM,YAAY,GAAI,CAAC,EAAE,MAAM,SAAS,MAAM,GAAG,MAAM,EAAE,MAAM,GAAG,CAAC,SAC5D,CAAC,EAAE,YACD,YAAY,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,KACtC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAkB5B;IAEK,iBAAiB,GACvB,KAAK,GAAG,OAAO,EACf,MAAM,SAAS,MAAM,GAAG,MAAM,EAC9B,MAAM,GAAG,mBAAmB,CAAC,KAAK,CAAC,mBAElB,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,mBAAmB,CAAC,KAAK,CAAC,EAAE,CAAC,kBAC9D,MAAM,YACb,YAAY,CAAC,mBAAmB,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,KAC/D,OAAO,CAAC,kBAAkB,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC,CAoBzD;IAEK,YAAY,GAAI,CAAC,SAChB,CAAC,EAAE,SACH,MAAM,YACH,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,GAAG,MAAM,KACpC,CAAC,GAAG,IAAI,CAQT;IAEK,qBAAqB,GAAI,CAAC,EAAE,MAAM,SAAS,MAAM,GAAG,MAAM,SACzD,CAAC,EAAE,6BAEH,MAAM,KACX,SAAS,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC,CAAC,CAAC,EAAE,CAgBxC;IAEK,kBAAkB,GAAI,MAAM,SAAS,MAAM,GAAG,MAAM,UAClD,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,UACpB,MAAM,KACX,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,EAAE,CAKrC;IAEK,sBAAsB,GAAI,CAAC,EAAE,CAAC,SAAS,CAAC,EAAE,WAAW,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,KAAG,CAAC,EAAE,CAGpF;CACF;AAGD,eAAO,MACN,YAAY,GA3GW,CAAC,EAAE,MAAM,SAAS,MAAM,WAAW,MAAM,6FA4GhE,iBAAiB,GApFhB,KAAK,YACL,MAAM,SAAS,MAAM,WACrB,MAAM,6DAEwB,MAAM,4DACpB,MAAM,mIAgFvB,YAAY,GAxDW,CAAC,qBAEhB,MAAM,0BACU,MAAM,GAAG,MAAM,4BAsDvC,qBAAqB,GA3CW,CAAC,EAAE,MAAM,SAAS,MAAM,sGA4CxD,kBAAkB,GAtBW,MAAM,SAAS,MAAM,mBACzC,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,2DAsB5B,sBAAsB,GAbW,CAAC,EAAE,CAAC,mDAcM,CAAC"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Astro adapter (Facade + Adapter + Singleton). Converts arbitrary collections
|
|
3
|
+
* into the `getStaticPaths` format Astro expects, wrapped in a Safe Result.
|
|
4
|
+
*/
|
|
5
|
+
export class AstroService {
|
|
6
|
+
static instance;
|
|
7
|
+
constructor() { }
|
|
8
|
+
static getInstance() {
|
|
9
|
+
if (!AstroService.instance) {
|
|
10
|
+
AstroService.instance = new AstroService();
|
|
11
|
+
}
|
|
12
|
+
return AstroService.instance;
|
|
13
|
+
}
|
|
14
|
+
usePathsFrom = (items, options = {}) => {
|
|
15
|
+
const { param = "slug", valueFrom = (item) => {
|
|
16
|
+
const record = item;
|
|
17
|
+
return record?.slug ?? record?.id ?? "";
|
|
18
|
+
}, propsFrom = (item) => item, paramsFrom, } = options;
|
|
19
|
+
return items.map((item) => ({
|
|
20
|
+
params: (paramsFrom ? paramsFrom(item) : { [param]: String(valueFrom(item)) }),
|
|
21
|
+
props: propsFrom(item),
|
|
22
|
+
}));
|
|
23
|
+
};
|
|
24
|
+
useGetStaticPaths = async (getCollectionFn, collectionName, options = {}) => {
|
|
25
|
+
try {
|
|
26
|
+
const entries = await getCollectionFn(collectionName);
|
|
27
|
+
const paths = this.usePathsFrom(entries, options);
|
|
28
|
+
return {
|
|
29
|
+
data: paths,
|
|
30
|
+
error: null,
|
|
31
|
+
ok: true,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
return {
|
|
36
|
+
data: null,
|
|
37
|
+
error: {
|
|
38
|
+
message: `Error generating routes for collection "${collectionName}"`,
|
|
39
|
+
collectionName,
|
|
40
|
+
details: error instanceof Error ? error.message : String(error),
|
|
41
|
+
},
|
|
42
|
+
ok: false,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
useFindEntry = (items, value, keyFrom) => {
|
|
47
|
+
const getKey = keyFrom ??
|
|
48
|
+
((item) => {
|
|
49
|
+
const record = item;
|
|
50
|
+
return record?.slug ?? record?.id ?? "";
|
|
51
|
+
});
|
|
52
|
+
return items.find((item) => String(getKey(item)) === value) ?? null;
|
|
53
|
+
};
|
|
54
|
+
useGeneratePagination = (items, pageSize = 10, param = "page") => {
|
|
55
|
+
const totalPages = Math.max(1, Math.ceil(items.length / pageSize));
|
|
56
|
+
return Array.from({ length: totalPages }, (_, i) => {
|
|
57
|
+
const currentPage = i + 1;
|
|
58
|
+
return {
|
|
59
|
+
params: {
|
|
60
|
+
[param]: currentPage === 1 ? undefined : String(currentPage),
|
|
61
|
+
},
|
|
62
|
+
props: {
|
|
63
|
+
items: items.slice(i * pageSize, (i + 1) * pageSize),
|
|
64
|
+
currentPage,
|
|
65
|
+
totalPages,
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
});
|
|
69
|
+
};
|
|
70
|
+
usePathsFromValues = (values, param = "slug") => {
|
|
71
|
+
return values.map((value) => ({
|
|
72
|
+
params: { [param]: String(value) },
|
|
73
|
+
props: value,
|
|
74
|
+
}));
|
|
75
|
+
};
|
|
76
|
+
useExtractUniqueValues = (items, keyFrom) => {
|
|
77
|
+
const values = items.flatMap(keyFrom);
|
|
78
|
+
return [...new Set(values)];
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
// Singleton instance and destructured exports.
|
|
82
|
+
export const { usePathsFrom, useGetStaticPaths, useFindEntry, useGeneratePagination, usePathsFromValues, useExtractUniqueValues, } = AstroService.getInstance();
|
|
83
|
+
//# sourceMappingURL=astro.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"astro.service.js","sourceRoot":"","sources":["../../../src/adapters/astro/astro.service.ts"],"names":[],"mappings":"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"}
|