@cortejojicoy/admin-kit 0.1.8

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 kukux
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.
package/README.md ADDED
@@ -0,0 +1,347 @@
1
+ # @cortejojicoy/admin-kit
2
+
3
+ A pluggable Next.js 16 admin panel kit. Drop in a config, get an authenticated dashboard with a customizable sidebar, JWT/OAuth/custom auth, and modules.
4
+
5
+ - **Next.js 16 / React 19** — Works with both App Router and Pages Router.
6
+ - **Pluggable auth** — JWT by default; switch to OAuth or supply a custom provider.
7
+ - **Config-driven sidebar** — Sections, items, badges, roles, and per-user filtering.
8
+ - **Modules** — Self-contained feature packs that contribute nav, providers, and dashboard widgets.
9
+ - **Edge-safe middleware** — Token verification at the edge via Web Crypto.
10
+ - **Theme tokens** — Override colors and radii via CSS variables.
11
+
12
+ ## Install
13
+
14
+ ```bash
15
+ pnpm add @cortejojicoy/admin-kit
16
+ # or
17
+ npm install @cortejojicoy/admin-kit
18
+ ```
19
+
20
+ Peer deps (must be installed in the consumer): `next@>=16`, `react@>=19`, `react-dom@>=19`.
21
+
22
+ ## 1. Define your config
23
+
24
+ ```ts
25
+ // admin.config.ts
26
+ import { defineAdminConfig } from '@cortejojicoy/admin-kit'
27
+
28
+ export const adminConfig = defineAdminConfig({
29
+ app: { name: 'Acme Admin' },
30
+ auth: {
31
+ provider: 'jwt',
32
+ jwt: {
33
+ endpoints: {
34
+ login: '/api/auth/login',
35
+ me: '/api/auth/me',
36
+ logout: '/api/auth/logout',
37
+ },
38
+ secret: process.env.JWT_SECRET, // only read on the server / in middleware
39
+ cookieName: 'acme_token',
40
+ },
41
+ loginPage: { path: '/login', title: 'Sign in to Acme' },
42
+ publicRoutes: ['/login', '/api/auth'],
43
+ },
44
+ navigation: {
45
+ sections: [
46
+ {
47
+ label: 'Workspace',
48
+ items: [
49
+ { label: 'Dashboard', href: '/' },
50
+ { label: 'Users', href: '/users', roles: ['admin'] },
51
+ ],
52
+ },
53
+ ],
54
+ },
55
+ })
56
+ ```
57
+
58
+ ## 2. App Router
59
+
60
+ ```tsx
61
+ // app/layout.tsx
62
+ import { AdminProvider, AdminLayout, AppLink, useAppPathname } from '@cortejojicoy/admin-kit/client'
63
+ import { adminConfig } from '@/admin.config'
64
+ import { resolveConfig } from '@cortejojicoy/admin-kit'
65
+
66
+ const resolved = resolveConfig(adminConfig)
67
+
68
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
69
+ return (
70
+ <html>
71
+ <body>
72
+ <AdminProvider config={adminConfig}>
73
+ <Shell>{children}</Shell>
74
+ </AdminProvider>
75
+ </body>
76
+ </html>
77
+ )
78
+ }
79
+
80
+ function Shell({ children }: { children: React.ReactNode }) {
81
+ const pathname = useAppPathname()
82
+ return (
83
+ <AdminLayout config={resolved} Link={AppLink} currentPath={pathname}>
84
+ {children}
85
+ </AdminLayout>
86
+ )
87
+ }
88
+ ```
89
+
90
+ ## 3. Pages Router
91
+
92
+ ```tsx
93
+ // pages/_app.tsx
94
+ import { AdminProvider } from '@cortejojicoy/admin-kit/client'
95
+ import { adminConfig } from '@/admin.config'
96
+
97
+ export default function App({ Component, pageProps }) {
98
+ return (
99
+ <AdminProvider config={adminConfig}>
100
+ <Component {...pageProps} />
101
+ </AdminProvider>
102
+ )
103
+ }
104
+
105
+ // pages/dashboard.tsx
106
+ import { withAdminLayout } from '@cortejojicoy/admin-kit/client'
107
+ import { resolveConfig } from '@cortejojicoy/admin-kit'
108
+ import { adminConfig } from '@/admin.config'
109
+
110
+ function DashboardPage() { return <div>Hello</div> }
111
+ export default withAdminLayout(DashboardPage, { config: resolveConfig(adminConfig), title: 'Dashboard' })
112
+ ```
113
+
114
+ ## 4. Edge middleware
115
+
116
+ ```ts
117
+ // middleware.ts (consumer root)
118
+ import { createAdminMiddleware } from '@cortejojicoy/admin-kit/middleware'
119
+ import { adminConfig } from './admin.config'
120
+
121
+ export default createAdminMiddleware(adminConfig)
122
+ export const config = { matcher: ['/((?!_next|api/auth|favicon).*)'] }
123
+ ```
124
+
125
+ ## 5. Server-side session
126
+
127
+ ```ts
128
+ // app/api/me/route.ts
129
+ import { getServerSession } from '@cortejojicoy/admin-kit/server'
130
+ import { adminConfig } from '@/admin.config'
131
+
132
+ export async function GET(req: Request) {
133
+ const session = await getServerSession(adminConfig, req)
134
+ return Response.json({ user: session?.user ?? null })
135
+ }
136
+ ```
137
+
138
+ ## Sub-path exports
139
+
140
+ | Sub-path | Purpose |
141
+ | --------------------------------- | ------------------------------------------------------ |
142
+ | `@cortejojicoy/admin-kit` | Config helpers + types (safe everywhere) |
143
+ | `@cortejojicoy/admin-kit/client` | React components & hooks (client components) |
144
+ | `@cortejojicoy/admin-kit/server` | `getServerSession`, `verifyJWT`, cookie helpers |
145
+ | `@cortejojicoy/admin-kit/middleware` | `createAdminMiddleware` (edge-safe) |
146
+
147
+ ## Modules
148
+
149
+ ```ts
150
+ import type { AdminModule } from '@cortejojicoy/admin-kit'
151
+
152
+ export const billingModule: AdminModule = {
153
+ id: 'billing',
154
+ navSections: [{
155
+ label: 'Billing',
156
+ items: [{ label: 'Invoices', href: '/billing/invoices' }],
157
+ }],
158
+ enabled: ({ user }) => user?.roles?.includes('admin') ?? false,
159
+ }
160
+
161
+ // then add to config.modules
162
+ ```
163
+
164
+ ## Versioning
165
+
166
+ We follow [Semantic Versioning](https://semver.org/) — the `MAJOR.MINOR.PATCH` triplet:
167
+
168
+ ```
169
+ 1.4.2
170
+ │ │ └── PATCH — bug fix, no API change (consumers auto-update safely)
171
+ │ └──── MINOR — new feature, backwards compat (consumers auto-update safely)
172
+ └────── MAJOR — breaking change (consumers must opt in)
173
+ ```
174
+
175
+ What counts as which:
176
+
177
+ | Change | Bump |
178
+ | ------------------------------------------------------- | ------- |
179
+ | Fix a sidebar render bug, JWT cookie parsing fix | PATCH |
180
+ | Add a new optional prop, add a new module slot | MINOR |
181
+ | Add a new sub-path export (no behavior change) | MINOR |
182
+ | Rename a public prop, remove an export, drop Next 16 | MAJOR |
183
+ | Change the shape of `AdminConfig.auth` | MAJOR |
184
+ | Change a default that consumers depended on | MAJOR |
185
+
186
+ Document every change in `CHANGELOG.md` under `## [Unreleased]` before
187
+ cutting a release — the release script promotes that section to the new
188
+ version automatically.
189
+
190
+ ## Releasing
191
+
192
+ The recommended release path is GitHub Actions Trusted Publishing. Local
193
+ commands create and push the release commit/tag; CI performs the npm publish
194
+ without a long-lived `NPM_TOKEN` or local 2FA prompt.
195
+
196
+ **Option A — guarded script (recommended):**
197
+
198
+ ```bash
199
+ ./scripts/release.sh patch # 0.1.0 -> 0.1.1
200
+ ./scripts/release.sh minor # 0.1.0 -> 0.2.0
201
+ ./scripts/release.sh major # 0.1.0 -> 1.0.0
202
+ ```
203
+
204
+ It refuses to release from a dirty tree or a non-`main` branch, runs
205
+ typecheck + build, bumps the version, updates the CHANGELOG, commits,
206
+ tags, pushes, then the pushed tag is published by CI.
207
+
208
+ **Option B — raw npm scripts + CI publish:**
209
+
210
+ ```bash
211
+ npm run release:patch
212
+ npm run release:minor
213
+ npm run release:major
214
+ ```
215
+
216
+ These wire into npm's `version` lifecycle:
217
+
218
+ | Hook | What runs |
219
+ | --------------- | ------------------------------------------------ |
220
+ | `preversion` | typecheck + build |
221
+ | `version` | promote `[Unreleased]` → new version in CHANGELOG |
222
+ | `postversion` | `git push --follow-tags` |
223
+ | `prepublishOnly` | clean + build before `npm publish` |
224
+
225
+ ## Publishing to npm
226
+
227
+ `@cortejojicoy/admin-kit` is a **scoped** package, so publishes use
228
+ `--access public`. The normal path is CI Trusted Publishing:
229
+
230
+ ```bash
231
+ # local release
232
+ ./scripts/release.sh patch
233
+
234
+ # CI then runs
235
+ npm publish --access public --provenance
236
+ ```
237
+
238
+ For a one-off local publish, npm accounts with publish 2FA need an OTP:
239
+
240
+ ```bash
241
+ NPM_OTP=<current-6-digit-code> npm run publish:npm:local
242
+ ```
243
+
244
+ > "packagist" is the PHP/Composer registry — not relevant here. The JavaScript
245
+ > ecosystem publishes to **npm** (https://www.npmjs.com). The same tarball
246
+ > can also be mirrored to GitHub Packages by changing `publishConfig.registry`.
247
+
248
+ ### CI/CD (GitHub Actions)
249
+
250
+ The workflow lives at [.github/workflows/ci.yml](.github/workflows/ci.yml). It's **push-driven**: every commit to `main` auto-publishes a new version.
251
+
252
+ | Trigger | What happens |
253
+ | ------------------------------------------------ | ----------------------------------------------------------- |
254
+ | `push` to `main` (normal commit) | `verify` + `release-on-push` → bump + tag + publish to npm |
255
+ | `push` to `main` (release commit, has `[skip ci]`) | nothing — loop guard |
256
+ | `pull_request` to `main` | `verify` only — never publishes |
257
+ | `push` of a `v*.*.*` tag | `verify` + `publish-on-tag` → publish that tag's version |
258
+ | `workflow_dispatch` (manual) | `verify` + `release-on-push` with chosen bump |
259
+
260
+ **Setup (one-time):**
261
+
262
+ 1. **npmjs.com → Trusted Publishers** (under your org `@cortejojicoy` or directly on the package settings once it exists).
263
+ Add a new GitHub Actions trusted publisher:
264
+
265
+ | Field | Value |
266
+ | --- | --- |
267
+ | Package | `@cortejojicoy/admin-kit` |
268
+ | Organization/user | (your GitHub username/org owning the repo) |
269
+ | Repository | `admin-kit` |
270
+ | Workflow filename | `ci.yml` |
271
+ | Environment | (leave blank) |
272
+
273
+ This replaces the long-lived `NPM_TOKEN` — the workflow gets a short-lived OIDC token at publish time. No secret in GitHub needed.
274
+
275
+ 2. **GitHub repo → Settings → Actions → General → Workflow permissions** → enable **Read and write** (so CI can push the bump commit and tag).
276
+
277
+ That's it. No `NPM_TOKEN` secret, no 2FA bypass. The package will publish with a **verified provenance attestation** (the green "Verified" badge on the npm page), proving it was built from the exact commit shown.
278
+
279
+ ### How the bump size is chosen
280
+
281
+ For each push to `main`, CI reads the head commit message and picks a bump level. Precedence (first match wins):
282
+
283
+ 1. **`workflow_dispatch` input** — the dropdown you pick in the UI overrides everything.
284
+ 2. **Explicit tag in the message:** `[major]`, `[minor]`, or `[patch]` anywhere in the commit.
285
+ 3. **Conventional commits:**
286
+ - `feat!: ...` or contains `BREAKING CHANGE` → **major**
287
+ - `feat: ...` / `feat(scope): ...` → **minor**
288
+ 4. **Default** → **patch**
289
+
290
+ Examples:
291
+
292
+ | Commit message | Bump |
293
+ | ------------------------------------------- | ------ |
294
+ | `fix: race in sidebar collapse` | patch |
295
+ | `chore: bump deps` | patch |
296
+ | `feat: add OAuth callback handler` | minor |
297
+ | `feat(auth): GitHub provider` | minor |
298
+ | `feat!: drop AdminProvider props` | major |
299
+ | `refactor: rewrite layout [minor]` | minor (override) |
300
+ | `docs: tweak readme [skip release]` | **no release** |
301
+
302
+ ### Opting out per-commit
303
+
304
+ Include `[skip release]` anywhere in the commit message and that push won't publish. Use it for README/docs/CI tweaks that shouldn't mint a version.
305
+
306
+ ### The other two paths (kept as escape hatches)
307
+
308
+ - **Manual tag** — `git tag v0.2.0 && git push origin v0.2.0`. CI syncs `package.json` to the tag and publishes. Useful if you bumped locally with [`./scripts/release.sh`](scripts/release.sh) or want to ship an exact version.
309
+ - **Manual dispatch** — Actions tab → `ci` → **Run workflow** → pick the bump. Has a `dry_run` toggle for rehearsals.
310
+
311
+ In all three paths the **git tag is the source of truth**; `package.json` version in `main` can lag behind the latest npm version when CI publishes from a tag without committing back.
312
+
313
+ ### Loop prevention
314
+
315
+ CI's release commit message is `chore(release): vX.Y.Z [skip ci]`. GitHub honors `[skip ci]` and won't re-trigger the workflow. As a belt-and-suspenders measure, the `release-on-push` job also has an `if:` that skips messages starting with `chore(release):` — so even if `[skip ci]` is stripped somehow, the loop still can't form.
316
+
317
+ ## What gets pushed where
318
+
319
+ Two separate allowlists control this — don't conflate them.
320
+
321
+ **Git (`.gitignore`)** — what we *commit* to the repo:
322
+
323
+ | Tracked in git | Ignored in git |
324
+ | --------------------------- | --------------------------------------------------- |
325
+ | `src/**` | `node_modules/`, `dist/`, `.next/`, `.cache/` |
326
+ | `package.json`, `tsconfig.json`, `tsup.config.ts` | `*.log`, `.env`, `.env.*` (except `.env.example`) |
327
+ | `scripts/**`, `examples/**` (source only) | `examples/*/node_modules`, `examples/*/.next` |
328
+ | `README.md`, `CHANGELOG.md`, `LICENSE` | `coverage/`, `.eslintcache`, `*.tsbuildinfo` |
329
+
330
+ `dist/` is intentionally **not** committed — it's built fresh in CI and shipped
331
+ to npm only.
332
+
333
+ **npm (`files` in `package.json`)** — what we *publish* to the registry:
334
+
335
+ ```json
336
+ "files": ["dist", "README.md", "LICENSE"]
337
+ ```
338
+
339
+ That's a whitelist, so consumers only download `dist/`, the README, and the
340
+ LICENSE. Source `.ts` files, tests, scripts, and configs **never** go to npm.
341
+ We don't use `.npmignore` (the `files` field is single-source-of-truth — using
342
+ both invites drift). Run `npm pack --dry-run` to preview the exact tarball
343
+ contents before publishing.
344
+
345
+ ## License
346
+
347
+ MIT