@bloomneo/appkit 1.2.9 → 1.5.2
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/AGENTS.md +195 -0
- package/CHANGELOG.md +253 -0
- package/README.md +147 -799
- package/bin/commands/generate.js +7 -7
- package/cookbook/README.md +26 -0
- package/cookbook/api-key-service.ts +106 -0
- package/cookbook/auth-protected-crud.ts +112 -0
- package/cookbook/file-upload-pipeline.ts +113 -0
- package/cookbook/multi-tenant-saas.ts +87 -0
- package/cookbook/real-time-chat.ts +121 -0
- package/dist/auth/auth.d.ts +21 -4
- package/dist/auth/auth.d.ts.map +1 -1
- package/dist/auth/auth.js +56 -44
- package/dist/auth/auth.js.map +1 -1
- package/dist/auth/defaults.d.ts +1 -1
- package/dist/auth/defaults.js +35 -35
- package/dist/cache/cache.d.ts +29 -6
- package/dist/cache/cache.d.ts.map +1 -1
- package/dist/cache/cache.js +72 -44
- package/dist/cache/cache.js.map +1 -1
- package/dist/cache/defaults.js +29 -29
- package/dist/cache/index.d.ts +19 -10
- package/dist/cache/index.d.ts.map +1 -1
- package/dist/cache/index.js +21 -18
- package/dist/cache/index.js.map +1 -1
- package/dist/config/defaults.d.ts +1 -1
- package/dist/config/defaults.js +11 -11
- package/dist/config/index.d.ts +3 -3
- package/dist/config/index.js +4 -4
- package/dist/database/adapters/mongoose.d.ts +4 -4
- package/dist/database/adapters/mongoose.js +7 -7
- package/dist/database/adapters/prisma.d.ts +4 -4
- package/dist/database/adapters/prisma.js +7 -7
- package/dist/database/defaults.d.ts +1 -1
- package/dist/database/defaults.js +4 -4
- package/dist/database/index.js +2 -2
- package/dist/database/index.js.map +1 -1
- package/dist/email/defaults.js +26 -26
- package/dist/email/index.js +7 -7
- package/dist/email/strategies/resend.js +1 -1
- package/dist/error/defaults.d.ts +1 -1
- package/dist/error/defaults.js +13 -13
- package/dist/error/error.d.ts +12 -0
- package/dist/error/error.d.ts.map +1 -1
- package/dist/error/error.js +19 -0
- package/dist/error/error.js.map +1 -1
- package/dist/error/index.d.ts +14 -3
- package/dist/error/index.d.ts.map +1 -1
- package/dist/error/index.js +14 -3
- package/dist/error/index.js.map +1 -1
- package/dist/event/defaults.js +35 -35
- package/dist/event/index.js +7 -7
- package/dist/logger/defaults.d.ts +1 -1
- package/dist/logger/defaults.js +40 -40
- package/dist/logger/index.d.ts +1 -0
- package/dist/logger/index.d.ts.map +1 -1
- package/dist/logger/index.js.map +1 -1
- package/dist/logger/logger.d.ts +8 -0
- package/dist/logger/logger.d.ts.map +1 -1
- package/dist/logger/logger.js +13 -3
- package/dist/logger/logger.js.map +1 -1
- package/dist/logger/transports/console.js +2 -2
- package/dist/logger/transports/http.d.ts +1 -1
- package/dist/logger/transports/http.js +2 -2
- package/dist/logger/transports/webhook.d.ts +1 -1
- package/dist/logger/transports/webhook.js +3 -3
- package/dist/queue/defaults.d.ts +2 -2
- package/dist/queue/defaults.js +38 -38
- package/dist/security/defaults.d.ts +1 -1
- package/dist/security/defaults.js +30 -30
- package/dist/security/index.d.ts +1 -1
- package/dist/security/index.js +3 -3
- package/dist/security/security.d.ts +1 -1
- package/dist/security/security.js +4 -4
- package/dist/storage/defaults.js +26 -26
- package/dist/storage/index.js +3 -3
- package/dist/util/defaults.d.ts +1 -1
- package/dist/util/defaults.js +41 -41
- package/dist/util/env.d.ts +35 -0
- package/dist/util/env.d.ts.map +1 -0
- package/dist/util/env.js +50 -0
- package/dist/util/env.js.map +1 -0
- package/dist/util/errors.d.ts +52 -0
- package/dist/util/errors.d.ts.map +1 -0
- package/dist/util/errors.js +82 -0
- package/dist/util/errors.js.map +1 -0
- package/dist/util/util.js +1 -1
- package/examples/.env.example +80 -0
- package/examples/README.md +16 -0
- package/examples/auth.ts +228 -0
- package/examples/cache.ts +36 -0
- package/examples/config.ts +45 -0
- package/examples/database.ts +69 -0
- package/examples/email.ts +53 -0
- package/examples/error.ts +50 -0
- package/examples/event.ts +42 -0
- package/examples/logger.ts +41 -0
- package/examples/queue.ts +58 -0
- package/examples/security.ts +46 -0
- package/examples/storage.ts +44 -0
- package/examples/util.ts +47 -0
- package/llms.txt +591 -0
- package/package.json +19 -10
- package/src/auth/README.md +850 -0
- package/src/cache/README.md +756 -0
- package/src/config/README.md +604 -0
- package/src/database/README.md +818 -0
- package/src/email/README.md +759 -0
- package/src/error/README.md +660 -0
- package/src/event/README.md +729 -0
- package/src/logger/README.md +435 -0
- package/src/queue/README.md +851 -0
- package/src/security/README.md +612 -0
- package/src/storage/README.md +1008 -0
- package/src/util/README.md +955 -0
- package/bin/templates/backend/docs/APPKIT_CLI.md +0 -507
- package/bin/templates/backend/docs/APPKIT_COMMENTS_GUIDELINES.md +0 -61
- package/bin/templates/backend/docs/APPKIT_LLM_GUIDE.md +0 -2539
package/AGENTS.md
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# AGENTS.md — @bloomneo/appkit
|
|
2
|
+
|
|
3
|
+
> Agent instructions for `@bloomneo/appkit`. This is the **rules** file.
|
|
4
|
+
> For the **full API reference**, read [`llms.txt`](./llms.txt) in the same directory.
|
|
5
|
+
> Both files ship with the package and are accessible via
|
|
6
|
+
> `node_modules/@bloomneo/appkit/AGENTS.md` and `node_modules/@bloomneo/appkit/llms.txt`.
|
|
7
|
+
|
|
8
|
+
## What this package is
|
|
9
|
+
|
|
10
|
+
`@bloomneo/appkit` is a Node.js backend toolkit with **12 integrated modules**
|
|
11
|
+
that share one canonical pattern: every module exports a `xxxClass` namespace
|
|
12
|
+
object with a `.get()` factory. There is exactly one way to obtain each module
|
|
13
|
+
and exactly one way to use it.
|
|
14
|
+
|
|
15
|
+
Use it for: Express/Fastify backends, JWT auth, multi-tenant database,
|
|
16
|
+
Redis cache, S3 storage, background queues, email, structured logging,
|
|
17
|
+
error handling. **Don't use it for:** frontend code, CLI tools, real-time
|
|
18
|
+
WebSocket-as-primary-feature, non-Node environments.
|
|
19
|
+
|
|
20
|
+
## The one rule that matters most
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
const auth = authClass.get(); // ALWAYS .get(), NEVER `new AuthClass()`
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Every module follows this pattern. There are no exceptions. If you find
|
|
27
|
+
yourself writing `new SomethingClass()` in code that imports from
|
|
28
|
+
`@bloomneo/appkit`, you're doing it wrong.
|
|
29
|
+
|
|
30
|
+
## Canonical imports — pick one and stay consistent
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
// Option A — flat (preferred for general code):
|
|
34
|
+
import { authClass, databaseClass, errorClass, loggerClass } from '@bloomneo/appkit';
|
|
35
|
+
|
|
36
|
+
// Option B — subpath (preferred when only one module is needed):
|
|
37
|
+
import { authClass } from '@bloomneo/appkit/auth';
|
|
38
|
+
import { databaseClass } from '@bloomneo/appkit/database';
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Both work. The subpath form is unusual for AppKit (most users want
|
|
42
|
+
multiple modules in the same file) but it tree-shakes slightly better.
|
|
43
|
+
**Don't mix the two styles in the same file.**
|
|
44
|
+
|
|
45
|
+
## The 12 modules at a glance
|
|
46
|
+
|
|
47
|
+
| Module | Import | Purpose |
|
|
48
|
+
|---|---|---|
|
|
49
|
+
| `authClass` | `from '@bloomneo/appkit/auth'` | JWT tokens, role.level permissions, middleware |
|
|
50
|
+
| `databaseClass` | `from '@bloomneo/appkit/database'` | Prisma/Mongoose with multi-tenant filtering |
|
|
51
|
+
| `securityClass` | `from '@bloomneo/appkit/security'` | CSRF, rate limiting, encryption, sanitization |
|
|
52
|
+
| `errorClass` | `from '@bloomneo/appkit/error'` | HTTP errors with semantic types |
|
|
53
|
+
| `cacheClass` | `from '@bloomneo/appkit/cache'` | Memory → Redis auto-scaling |
|
|
54
|
+
| `storageClass` | `from '@bloomneo/appkit/storage'` | Local → S3/R2 auto-scaling |
|
|
55
|
+
| `queueClass` | `from '@bloomneo/appkit/queue'` | Memory → Redis → DB scaling |
|
|
56
|
+
| `emailClass` | `from '@bloomneo/appkit/email'` | Console → SMTP → Resend |
|
|
57
|
+
| `eventClass` | `from '@bloomneo/appkit/event'` | Memory → Redis pub/sub |
|
|
58
|
+
| `loggerClass` | `from '@bloomneo/appkit/logger'` | Multi-transport, auto-scaling |
|
|
59
|
+
| `configClass` | `from '@bloomneo/appkit/config'` | Environment-driven config |
|
|
60
|
+
| `utilClass` | `from '@bloomneo/appkit/util'` | Safe property access, debounce, chunk |
|
|
61
|
+
|
|
62
|
+
For full method signatures and examples, read `llms.txt` in this same directory.
|
|
63
|
+
|
|
64
|
+
## Environment variables
|
|
65
|
+
|
|
66
|
+
AppKit reads env vars with the `BLOOM_*` prefix. There is **no** backwards
|
|
67
|
+
compatibility for the legacy `VOILA_*` prefix from `@voilajsx/appkit` —
|
|
68
|
+
that was removed entirely in 1.5.2.
|
|
69
|
+
|
|
70
|
+
Required for production:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
BLOOM_AUTH_SECRET=<min 32 chars> # JWT signing key
|
|
74
|
+
DATABASE_URL=postgresql://... # any Prisma-supported URL
|
|
75
|
+
BLOOM_SECURITY_CSRF_SECRET=<min 32 chars> # CSRF protection
|
|
76
|
+
BLOOM_SECURITY_ENCRYPTION_KEY=<64 hex> # AES-256-GCM key
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Optional (auto-scaling kicks in when set):
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
REDIS_URL=redis://... # → distributed cache + queue
|
|
83
|
+
AWS_S3_BUCKET=... # → cloud storage
|
|
84
|
+
RESEND_API_KEY=re_... # → professional email
|
|
85
|
+
BLOOM_DB_TENANT=auto # → multi-tenant mode
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## When generating code with AppKit
|
|
89
|
+
|
|
90
|
+
### Always
|
|
91
|
+
|
|
92
|
+
- **Use `xxxClass.get()`** to obtain a module instance. Cache the result at
|
|
93
|
+
module scope, not inside request handlers.
|
|
94
|
+
- **Wrap async route handlers** in `error.asyncRoute(...)` so thrown errors
|
|
95
|
+
flow into the centralized error middleware.
|
|
96
|
+
- **Use semantic error types**: `error.badRequest('...')`, `error.unauthorized('...')`,
|
|
97
|
+
`error.notFound('...')`, etc. — never `throw new Error(...)` in routes.
|
|
98
|
+
- **Mount `error.handleErrors()` middleware last** in the Express stack.
|
|
99
|
+
- **Use `auth.requireLoginToken()` and `auth.requireUserRoles(['admin.tenant'])`** as
|
|
100
|
+
middleware, not custom token-checking code. Chain in that order:
|
|
101
|
+
`requireLoginToken()` first, then `requireUserRoles([...])` — never standalone,
|
|
102
|
+
never reversed.
|
|
103
|
+
- **Use `cache.getOrSet(key, fetcher, ttl)`** instead of manual cache-check-then-fetch.
|
|
104
|
+
- **Use `logger.get('component-name')`** so logs are tagged.
|
|
105
|
+
|
|
106
|
+
### Never
|
|
107
|
+
|
|
108
|
+
- **Never write your own JWT helper.** `auth.generateLoginToken()`,
|
|
109
|
+
`auth.generateApiToken()`, and `auth.verifyToken()` cover every case.
|
|
110
|
+
(`signToken` is a private internal — don't reach for it.)
|
|
111
|
+
- **Never instantiate Prisma directly.** `databaseClass.get()` returns the
|
|
112
|
+
shared, tenant-aware client.
|
|
113
|
+
- **Never hand-roll rate limiting.** Use `security.requests(maxRequests, windowMs)`.
|
|
114
|
+
- **Never write a custom file-upload-to-S3 wrapper.** `storage.put()` /
|
|
115
|
+
`storage.get()` / `storage.url()` handle local + S3 + R2 with the same API.
|
|
116
|
+
- **Never read `process.env.X` directly** in business code. Go through
|
|
117
|
+
`config.get('section.key')` so the value is validated and typed.
|
|
118
|
+
- **Never `throw new Error(...)` in a route handler.** Use `error.badRequest(...)`,
|
|
119
|
+
`error.unauthorized(...)`, etc. — they include the right HTTP status code.
|
|
120
|
+
|
|
121
|
+
## Canonical pattern — protected endpoint with database + logger
|
|
122
|
+
|
|
123
|
+
This is the shape ~80% of AppKit endpoints follow. Generate this as the
|
|
124
|
+
default starting point unless told otherwise:
|
|
125
|
+
|
|
126
|
+
```ts
|
|
127
|
+
import { authClass, databaseClass, errorClass, loggerClass } from '@bloomneo/appkit';
|
|
128
|
+
|
|
129
|
+
const auth = authClass.get();
|
|
130
|
+
const database = await databaseClass.get();
|
|
131
|
+
const error = errorClass.get();
|
|
132
|
+
const logger = loggerClass.get('users');
|
|
133
|
+
|
|
134
|
+
app.post(
|
|
135
|
+
'/api/users',
|
|
136
|
+
auth.requireLoginToken(), // 1. authenticate the user
|
|
137
|
+
auth.requireUserRoles(['admin.tenant']), // 2. check the role (always chained)
|
|
138
|
+
error.asyncRoute(async (req, res) => {
|
|
139
|
+
if (!req.body?.email) {
|
|
140
|
+
throw error.badRequest('Email required');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const newUser = await database.user.create({ data: req.body });
|
|
144
|
+
logger.info('User created', { userId: newUser.id });
|
|
145
|
+
|
|
146
|
+
res.json({ user: newUser });
|
|
147
|
+
})
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
// Last middleware in the stack — handles every thrown semantic error.
|
|
151
|
+
app.use(error.handleErrors());
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
**Critical chaining rules for the auth middleware:**
|
|
155
|
+
- `requireLoginToken()` MUST come first (it sets `req.user` for downstream).
|
|
156
|
+
- `requireUserRoles([...])` is for role-based access on USER routes — chain it
|
|
157
|
+
AFTER `requireLoginToken()`. Never use it standalone or with API tokens.
|
|
158
|
+
- `requireApiToken()` is for SERVICE routes (webhooks, integrations). Use it
|
|
159
|
+
alone. Never chain `requireUserRoles` after `requireApiToken` — API tokens
|
|
160
|
+
don't have user roles.
|
|
161
|
+
|
|
162
|
+
## CLI
|
|
163
|
+
|
|
164
|
+
`@bloomneo/appkit` ships a CLI for scaffolding backend projects:
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
appkit generate app myproject # full backend scaffold
|
|
168
|
+
cd myproject && npm run dev:api # → http://localhost:3000
|
|
169
|
+
|
|
170
|
+
appkit generate feature product # basic feature (route + service + types)
|
|
171
|
+
appkit generate feature order --db # database-enabled feature
|
|
172
|
+
appkit generate feature user # full auth system with 9-role hierarchy
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
For a downstream consumer building with `@bloomneo/bloom`, the bloom CLI
|
|
176
|
+
handles scaffolding instead — appkit's CLI is for users who only want the
|
|
177
|
+
backend, no frontend.
|
|
178
|
+
|
|
179
|
+
## Migration notes
|
|
180
|
+
|
|
181
|
+
- This package was previously published as `@voilajsx/appkit` (frozen at
|
|
182
|
+
1.2.8). Run a project-wide find-and-replace of `@voilajsx/appkit` →
|
|
183
|
+
`@bloomneo/appkit`. The API is identical.
|
|
184
|
+
- **BREAKING (1.5.2):** the legacy `VOILA_*` env var prefix is gone.
|
|
185
|
+
Rename every `VOILA_FOO` in your `.env` files to `BLOOM_FOO`. There
|
|
186
|
+
is no fallback, no deprecation warning, no compatibility shim — the
|
|
187
|
+
rebrand is a clean break and consumers upgrading from earlier versions
|
|
188
|
+
must rename in one go.
|
|
189
|
+
|
|
190
|
+
## Where to look next
|
|
191
|
+
|
|
192
|
+
- **Full API reference**: [`llms.txt`](./llms.txt) (in this directory)
|
|
193
|
+
- **Module source code**: `node_modules/@bloomneo/appkit/dist/` (TypeScript types)
|
|
194
|
+
- **CHANGELOG**: [`CHANGELOG.md`](./CHANGELOG.md) for release history
|
|
195
|
+
- **Issues**: https://github.com/bloomneo/appkit/issues
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to AppKit will be documented in this file.
|
|
4
|
+
|
|
5
|
+
## [1.5.2] - 2026-04-11
|
|
6
|
+
|
|
7
|
+
### Behavior fix — `auth.can()` permission resolution
|
|
8
|
+
|
|
9
|
+
**Breaking semantic change in the auth module.** The `permissions` field on
|
|
10
|
+
the JWT payload now correctly **replaces** the role's default permissions
|
|
11
|
+
instead of supplementing them. This matches AWS IAM, Casbin, OPA, Auth0
|
|
12
|
+
RBAC, and every mainstream permission system: explicit permissions are the
|
|
13
|
+
truth, defaults are the fallback.
|
|
14
|
+
|
|
15
|
+
**Old (buggy) behavior:**
|
|
16
|
+
```ts
|
|
17
|
+
const user = auth.generateLoginToken({
|
|
18
|
+
userId: 1, role: 'admin', level: 'tenant',
|
|
19
|
+
permissions: ['view:own'], // expected: user is restricted to view:own
|
|
20
|
+
});
|
|
21
|
+
auth.can(user, 'manage:tenant'); // returned TRUE (additive — bug)
|
|
22
|
+
// because admin.tenant defaults included it
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**New (fixed) behavior:**
|
|
26
|
+
```ts
|
|
27
|
+
const user = auth.generateLoginToken({
|
|
28
|
+
userId: 1, role: 'admin', level: 'tenant',
|
|
29
|
+
permissions: ['view:own'], // explicit permissions REPLACE role defaults
|
|
30
|
+
});
|
|
31
|
+
auth.can(user, 'manage:tenant'); // now FALSE — defaults are not consulted
|
|
32
|
+
auth.can(user, 'view:own'); // TRUE — exact match
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**Resolution rule:**
|
|
36
|
+
- If `user.permissions` is present (any array, including `[]`), it is the
|
|
37
|
+
COMPLETE permission set. Role defaults are NOT consulted.
|
|
38
|
+
- If `user.permissions` is absent, the role.level's default permissions
|
|
39
|
+
from the configured RolePermissionConfig apply.
|
|
40
|
+
|
|
41
|
+
**Action inheritance still works** within whichever set is in scope:
|
|
42
|
+
`manage:scope` grants `view`, `create`, `edit`, `delete` for that scope.
|
|
43
|
+
**No upward inheritance**: `edit:scope` does NOT grant `manage:scope`.
|
|
44
|
+
|
|
45
|
+
**Why this is a fix, not a feature:**
|
|
46
|
+
- The original JSDoc on `can()` said `auth.hasRole('edit:tenant', 'manage:tenant') → FALSE`
|
|
47
|
+
meaning the author intended no upward inheritance. The implementation
|
|
48
|
+
silently bypassed this via the additive fallback. The fix aligns the code
|
|
49
|
+
with its own documented intent.
|
|
50
|
+
- Documentation alone could not fix the consumer trap because the bug was
|
|
51
|
+
silent — devs writing `permissions: ['view:own']` thought they were
|
|
52
|
+
restricting the user, but the user could still do `manage:tenant`. That's
|
|
53
|
+
the worst class of security bug.
|
|
54
|
+
- No deployed consumers were on `@bloomneo/appkit@1.5.1` at the time of this
|
|
55
|
+
fix, so the breaking change has zero blast radius.
|
|
56
|
+
|
|
57
|
+
**Migration:** anyone who was relying on the additive behavior (passing
|
|
58
|
+
`permissions: [...]` expecting it to extend role defaults) needs to either:
|
|
59
|
+
1. Remove the explicit `permissions` array entirely (defaults will apply)
|
|
60
|
+
2. Add the role's defaults to the explicit array manually if you want the union
|
|
61
|
+
|
|
62
|
+
**Tests:** `src/auth/auth.test.ts` now has 8 `can()` tests covering
|
|
63
|
+
explicit replacement, no upward inheritance, empty-array downgrade, and
|
|
64
|
+
no-explicit-permissions fallback. 55/55 vitest passing.
|
|
65
|
+
|
|
66
|
+
### error / logger / database / config module audit
|
|
67
|
+
|
|
68
|
+
- **error**: Added `tooMany()` (429) and `internal()` (500 alias for `serverError()`) as real
|
|
69
|
+
methods on `ErrorClass` and as shortcuts on `errorClass`. Both were previously in example
|
|
70
|
+
comments only — examples referenced them as if callable.
|
|
71
|
+
- **logger**: Added `fatal(message, meta?)` to the `Logger` interface and `LoggerClass`.
|
|
72
|
+
Delegates to `error()` with `{ fatal: true }` in meta. Was in `examples/logger.ts` line 24
|
|
73
|
+
but not in the interface — would throw "not a function" at runtime.
|
|
74
|
+
- **config**: Fixed `examples/config.ts` — `config.isDevelopment()` / `config.isProduction()`
|
|
75
|
+
do NOT exist on the `ConfigClass` instance; they live on `configClass` (the module-level
|
|
76
|
+
object). Fixed to `configClass.isDevelopment()` with a comment explaining the distinction.
|
|
77
|
+
Also fixed `config.getNumber()` / `config.getBoolean()` — neither exist on `ConfigClass`;
|
|
78
|
+
examples now use `Number(config.get(...))` / `config.get(...) === 'true'`.
|
|
79
|
+
- **database**: `disconnect()` error prefix changed to `[@bloomneo/appkit/database]` for
|
|
80
|
+
consistency with other modules.
|
|
81
|
+
- Added test files: `src/error/error.test.ts` (31 tests), `src/logger/logger.test.ts`
|
|
82
|
+
(25 tests), `src/config/config.test.ts` (31 tests), `src/database/database.test.ts`
|
|
83
|
+
(9 tests). **Total: 216/216 vitest passing.**
|
|
84
|
+
|
|
85
|
+
### Cache module audit
|
|
86
|
+
|
|
87
|
+
- Fixed `examples/cache.ts`: `cache.del()` → `cache.delete()` (wrong name), removed
|
|
88
|
+
`cache.has()` (internal `CacheStrategy` method, not on the public `Cache` interface)
|
|
89
|
+
- Fixed `src/cache/README.md` testing section: `cacheClass.clear()` (disconnects all
|
|
90
|
+
instances) → `cacheClass.flushAll()` (clears cached data — the right call between tests)
|
|
91
|
+
- Added `src/cache/cache.test.ts` (49 vitest tests, full public API coverage, drift-check
|
|
92
|
+
section asserting hallucinated method names `del` and `has` don't exist on the public
|
|
93
|
+
`Cache` interface)
|
|
94
|
+
- Score block added to `src/cache/README.md`: **75.3/100 🟡 Solid** (no cap).
|
|
95
|
+
- Added `CacheError` class (exported from `@bloomneo/appkit/cache`) — all cache
|
|
96
|
+
operations now throw `CacheError` instead of swallowing errors via `console.error`.
|
|
97
|
+
Use `instanceof CacheError` to distinguish infrastructure failures from your own
|
|
98
|
+
errors and decide whether to fall back or re-throw. Error codes: `CACHE_GET_FAILED`,
|
|
99
|
+
`CACHE_SET_FAILED`, `CACHE_DELETE_FAILED`, `CACHE_CLEAR_FAILED`, `CACHE_CONNECT_FAILED`,
|
|
100
|
+
`CACHE_INVALID_KEY`, `CACHE_INVALID_VALUE`.
|
|
101
|
+
- `cacheClass.clear()` renamed to `cacheClass.disconnectAll()` — eliminates the naming
|
|
102
|
+
collision with `cache.clear()` (which clears data in a namespace). The two methods had
|
|
103
|
+
opposite effects under the same name.
|
|
104
|
+
- Added generics to `Cache` interface: `get<T>()`, `set<T>()`, `getOrSet<T>()` — no more
|
|
105
|
+
`any` casts when working with typed values.
|
|
106
|
+
- All error messages use `[@bloomneo/appkit/cache]` prefix (consistent with auth module).
|
|
107
|
+
|
|
108
|
+
### Other fixes (auth-only revamp)
|
|
109
|
+
|
|
110
|
+
- Added `src/auth/auth.test.ts` (55 tests, full public API coverage,
|
|
111
|
+
drift-check section asserting hallucinated method names don't exist)
|
|
112
|
+
- Added `vitest.setup.ts` for env var bootstrapping before module init
|
|
113
|
+
- Updated `vitest.config.js` to wire the setup file
|
|
114
|
+
- Fixed README hero example: `auth.requireRole` → `auth.requireLoginToken()` +
|
|
115
|
+
`auth.requireUserRoles([...])` (was hallucinated)
|
|
116
|
+
- Fixed AGENTS.md `auth.requireLogin()` / `requireRole()` / `signToken()` →
|
|
117
|
+
real method names
|
|
118
|
+
- Fixed llms.txt auth section: rewrote 12 method signatures, role hierarchy,
|
|
119
|
+
middleware chaining rules, and 2 worked examples to match runtime
|
|
120
|
+
- Fixed src/auth/README.md `service.webhook` / `api.external` examples to use
|
|
121
|
+
valid `admin.system` role.level (the old examples threw at runtime)
|
|
122
|
+
- Fixed `examples/auth.ts` to demonstrate all 12 public methods (added
|
|
123
|
+
`verifyTokenManually` + `permissionCheckHandler`)
|
|
124
|
+
- Improved auth.ts runtime errors: now `[@bloomneo/appkit/auth] message + DOCS_URL#anchor`
|
|
125
|
+
format so devs and AI agents can self-correct from the error alone
|
|
126
|
+
- Added `AGENT_DEV_SCORING_ALGORITHM.md` at repo root: 15-dimension rubric
|
|
127
|
+
for scoring AI-agent + dev friendliness, applied to the auth module first
|
|
128
|
+
- Added agent-dev friendliness score block to `src/auth/README.md`
|
|
129
|
+
(current: 83.6/100 uncapped, 50/100 capped due to broken cookbook files
|
|
130
|
+
pending repair in a follow-up release)
|
|
131
|
+
- Added "Which case is your app?" decision tree to `src/auth/README.md`
|
|
132
|
+
mapping the 9-level default hierarchy to three real-world app shapes:
|
|
133
|
+
Case 1 (admin + users, ~50% of apps), Case 2 (admin + orgs + users, ~30%),
|
|
134
|
+
Case 3 (admin + orgs + tenants, ~20%). Establishes a 3-role core
|
|
135
|
+
(`user.basic` + `moderator.manage` + `admin.system`) shared by all cases,
|
|
136
|
+
with pricing tiers (`user.pro`, `user.max`) and admin level scoping
|
|
137
|
+
(`admin.tenant`, `admin.org`) marked optional. Multi-tenancy is reframed
|
|
138
|
+
as a database concern (`BLOOM_DB_TENANT=auto`), not an auth concern.
|
|
139
|
+
Lifts the auth README's Reading Order score 9 → 10 and Learning Curve 7 → 9.
|
|
140
|
+
|
|
141
|
+
### security / util / queue / storage / email / event module audit
|
|
142
|
+
|
|
143
|
+
- **security**: Fixed `examples/security.ts` — removed hallucinated `csrf()`, `requireCsrf()`,
|
|
144
|
+
`email()`, `url()`. Real CSRF method is `forms()` (single middleware handles both injection
|
|
145
|
+
and validation). Added `html()` and `escape()` examples. Noted that email/URL validation
|
|
146
|
+
should use zod or validator.js.
|
|
147
|
+
- **util**: Fixed `examples/util.ts` — removed `util.set()`, `util.omit()`, `util.throttle()`,
|
|
148
|
+
`util.retry()` (none exist). Added real methods: `util.unique()`, `util.clamp()`,
|
|
149
|
+
`util.truncate()`. Added note that `util.get()` is read-only (no `set()`), `util.pick()` is
|
|
150
|
+
the correct "exclude keys" approach (no `omit()`).
|
|
151
|
+
- **queue**: Fixed `examples/queue.ts` — `retries: 3` → `attempts: 3` in `JobOptions`.
|
|
152
|
+
`queue.schedule('name', '0 3 * * *', {})` (cron-style, wrong) → `queue.schedule('name', {}, delayMs)`
|
|
153
|
+
(delay in milliseconds — there is no built-in cron scheduler; use node-cron to call `queue.add()`).
|
|
154
|
+
- **storage**: Fixed `examples/storage.ts` — `storage.has(key)` → `storage.exists(key)`.
|
|
155
|
+
- **email**: Fixed `examples/email.ts` — `email.send({ template, data })` → `email.sendTemplate(name, data)`.
|
|
156
|
+
Added note that `EmailData` has no `template` or `data` fields.
|
|
157
|
+
- **event**: `examples/event.ts` was already correct — no changes.
|
|
158
|
+
- Added test files for all 6 modules: `src/security/security.test.ts`, `src/util/util.test.ts`,
|
|
159
|
+
`src/queue/queue.test.ts`, `src/storage/storage.test.ts`, `src/email/email.test.ts`,
|
|
160
|
+
`src/event/event.test.ts`. Each includes a drift-check section asserting hallucinated
|
|
161
|
+
method names do not exist at runtime.
|
|
162
|
+
|
|
163
|
+
### Cookbook fixes
|
|
164
|
+
|
|
165
|
+
All 5 cookbook files corrected — the two root hallucinations that had propagated everywhere:
|
|
166
|
+
- `auth.requireLogin()` → `auth.requireLoginToken()` (5 occurrences across 4 files)
|
|
167
|
+
- `auth.requireRole('admin.tenant')` → `auth.requireUserRoles(['admin.tenant'])` (6 occurrences)
|
|
168
|
+
- `cache.del()` → `cache.delete()` (`multi-tenant-saas.ts`)
|
|
169
|
+
- `{ retries: 3 }` → `{ attempts: 3 }` + updated inline comment (`file-upload-pipeline.ts`)
|
|
170
|
+
|
|
171
|
+
### llms.txt and AGENTS.md
|
|
172
|
+
|
|
173
|
+
- Added `AGENTS.md` (new file): concise agent rules — always/never lists, canonical patterns,
|
|
174
|
+
CLI reference, migration notes. Ships with the package for consumption by AI coding agents.
|
|
175
|
+
- Fixed `llms.txt` — 7 sections with stale or hallucinated API:
|
|
176
|
+
- Security: corrected to `forms()`, `html()`, `escape()`; removed `csrf()`, `requireCsrf()`,
|
|
177
|
+
`email()`, `url()`
|
|
178
|
+
- Cache: `del()` → `delete()`; removed `has()` with null-check pattern documented
|
|
179
|
+
- Storage: `del()` → `delete()`; `has()` → `exists()`; added `list()`, `copy()`
|
|
180
|
+
- Queue: `retries` → `attempts`; `schedule(name, cron, data)` → `schedule(name, data, delayMs)`
|
|
181
|
+
- Email: removed `template`/`data` from `EmailData`; added `sendTemplate()` signature
|
|
182
|
+
- Util: removed `set()`, `omit()`, `throttle()`, `retry()`; added `isEmpty()`, `unique()`,
|
|
183
|
+
`clamp()`, `truncate()`
|
|
184
|
+
- Config: split instance methods (`get`, `has`, `getRequired`, `getMany`, `getAll`) vs
|
|
185
|
+
module-level helpers (`configClass.isDevelopment()` / `isProduction()` / `isTest()`);
|
|
186
|
+
documented `getNumber()`/`getBoolean()` pattern via `Number()` / `=== 'true'`
|
|
187
|
+
|
|
188
|
+
### VOILA_* env var prefix removed (breaking change)
|
|
189
|
+
|
|
190
|
+
The legacy `VOILA_*` env var prefix is gone entirely. Rename in your `.env` files:
|
|
191
|
+
- `VOILA_AUTH_SECRET` → `BLOOM_AUTH_SECRET`
|
|
192
|
+
- `VOILA_SECURITY_CSRF_SECRET` → `BLOOM_SECURITY_CSRF_SECRET`
|
|
193
|
+
- `VOILA_SECURITY_ENCRYPTION_KEY` → `BLOOM_SECURITY_ENCRYPTION_KEY`
|
|
194
|
+
- And so on for all other `VOILA_*` vars.
|
|
195
|
+
|
|
196
|
+
There is no fallback, no deprecation warning, no compatibility shim.
|
|
197
|
+
|
|
198
|
+
## [1.5.1] - 2026-04-11
|
|
199
|
+
|
|
200
|
+
> **Note on version jump.** Previous releases of `@bloomneo/appkit` were `1.2.9`
|
|
201
|
+
> and earlier (and the package was previously published as `@voilajsx/appkit`
|
|
202
|
+
> at `1.2.8`). This release jumps to `1.5.1` to align with the
|
|
203
|
+
> bloomneo trio (`@bloomneo/uikit@1.5.1`, `@bloomneo/appkit@1.5.1`,
|
|
204
|
+
> `@bloomneo/bloom@1.5.1`) so consumers can install matched versions in one
|
|
205
|
+
> step. **No breaking changes** between 1.2.9 and 1.5.1 — every export, every
|
|
206
|
+
> method, every default behavior is identical. The version bump is purely for
|
|
207
|
+
> trio alignment.
|
|
208
|
+
|
|
209
|
+
### Fixed
|
|
210
|
+
|
|
211
|
+
- **Stale `[VoilaJSX AppKit]` brand strings in runtime warnings.** The 1.2.9
|
|
212
|
+
rebrand updated the package metadata, README, and documentation but missed
|
|
213
|
+
~70 hardcoded brand strings inside the source files (warning messages, log
|
|
214
|
+
prefixes, HTTP `User-Agent` headers, JSDoc comments). Smoke testing surfaced
|
|
215
|
+
these as `[VoilaJSX AppKit] Environment variable …` warnings printed to
|
|
216
|
+
consumer terminals. All cleaned up:
|
|
217
|
+
- `[VoilaJSX AppKit]` → `[Bloomneo AppKit]` (runtime warning prefix in
|
|
218
|
+
`cache/defaults.ts`, `util/defaults.ts`, `config/defaults.ts`)
|
|
219
|
+
- `[VoilaJSX Utils]` → `[Bloomneo Utils]` (in `util/util.ts`)
|
|
220
|
+
- HTTP `User-Agent: VoilaJSX-AppKit-Logging/1.0.0` → `Bloomneo-AppKit-Logging/1.0.0`
|
|
221
|
+
(in `logger/transports/http.ts` and `logger/transports/webhook.ts`)
|
|
222
|
+
- HTTP `User-Agent: VoilaJSX-AppKit-Email/1.0.0` → `Bloomneo-AppKit-Email/1.0.0`
|
|
223
|
+
(in `email/strategies/resend.ts`)
|
|
224
|
+
- Webhook footer string `VoilaJSX AppKit Logging` → `Bloomneo AppKit Logging`
|
|
225
|
+
(in `logger/transports/webhook.ts`)
|
|
226
|
+
- JSDoc references to `VoilaJSX framework`, `VoilaJSX standard`,
|
|
227
|
+
`VoilaJSX app discovery`, `VoilaJSX structure`, `VoilaJSX startup` →
|
|
228
|
+
all renamed to `Bloomneo` equivalents
|
|
229
|
+
- Module README license footers `MIT © [VoilaJSX]` → `MIT © [Bloomneo]`
|
|
230
|
+
|
|
231
|
+
### Not changed (in 1.5.1 — see 1.5.2 for the env var rename)
|
|
232
|
+
|
|
233
|
+
- **`VOILA_*` environment variable prefix was unchanged in 1.5.1.** At the
|
|
234
|
+
time, AppKit still read `VOILA_AUTH_SECRET`, `VOILA_DB_URL`, etc. Renaming
|
|
235
|
+
the prefix was deferred from this release. The prefix was treated as a
|
|
236
|
+
schema convention, not a brand mention.
|
|
237
|
+
- **The `VOILA_*` prefix was removed entirely in the next release (1.5.2).**
|
|
238
|
+
See the 1.5.2 entry below for migration instructions.
|
|
239
|
+
|
|
240
|
+
### Verification
|
|
241
|
+
|
|
242
|
+
- Final grep sweep: 0 `VoilaJSX` references in `src/`
|
|
243
|
+
- `npm run build` (tsc): green, all 11 sub-modules compile
|
|
244
|
+
- `npm pack --dry-run`: tarball name updated to `bloomneo-appkit-1.5.1.tgz`
|
|
245
|
+
|
|
246
|
+
## [1.2.9] - 2026-04-10
|
|
247
|
+
|
|
248
|
+
Republish under the `@bloomneo` scope (was `@voilajsx/appkit`). API,
|
|
249
|
+
behavior, and types are identical to `@voilajsx/appkit@1.2.8`. The
|
|
250
|
+
`@voilajsx` npm account was lost; this release migrates the package to
|
|
251
|
+
the new `@bloomneo` namespace. Run `npm install @bloomneo/appkit` and
|
|
252
|
+
do a project-wide find-and-replace of `@voilajsx/appkit` →
|
|
253
|
+
`@bloomneo/appkit` to migrate.
|