veloce-ts 0.4.18 → 1.0.1
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 +124 -1
- package/MIGRATION.md +109 -0
- package/README.md +33 -3
- package/dist/cjs/src/adapters/hono.js +2 -2
- package/dist/cjs/src/adapters/hono.js.map +3 -3
- package/dist/cjs/src/auth/index.js +3 -16
- package/dist/cjs/src/auth/index.js.map +12 -16
- package/dist/cjs/src/graphql/index.js +14 -27
- package/dist/cjs/src/graphql/index.js.map +6 -7
- package/dist/cjs/src/index.js +49 -62
- package/dist/cjs/src/index.js.map +38 -57
- package/dist/cjs/src/middleware/index.js +8 -8
- package/dist/cjs/src/middleware/index.js.map +4 -4
- package/dist/cjs/src/plugins/index.js +41 -54
- package/dist/cjs/src/plugins/index.js.map +7 -8
- package/dist/cjs/src/testing/index.js +32 -45
- package/dist/cjs/src/testing/index.js.map +12 -37
- package/dist/cjs/src/validation/index.js +2 -2
- package/dist/cjs/src/validation/index.js.map +3 -3
- package/dist/cjs/src/websocket/index.js +2 -15
- package/dist/cjs/src/websocket/index.js.map +5 -6
- package/dist/esm/src/adapters/express.js +3 -3
- package/dist/esm/src/adapters/express.js.map +2 -2
- package/dist/esm/src/adapters/hono.js +2 -2
- package/dist/esm/src/adapters/hono.js.map +3 -3
- package/dist/esm/src/auth/index.js +3 -16
- package/dist/esm/src/auth/index.js.map +12 -16
- package/dist/esm/src/cli/index.js +32 -32
- package/dist/esm/src/cli/index.js.map +2 -2
- package/dist/esm/src/docs/index.js +3 -3
- package/dist/esm/src/docs/index.js.map +2 -2
- package/dist/esm/src/errors/index.js +7 -7
- package/dist/esm/src/errors/index.js.map +2 -2
- package/dist/esm/src/graphql/index.js +17 -30
- package/dist/esm/src/graphql/index.js.map +6 -7
- package/dist/esm/src/index.js +49 -62
- package/dist/esm/src/index.js.map +38 -57
- package/dist/esm/src/middleware/index.js +8 -8
- package/dist/esm/src/middleware/index.js.map +4 -4
- package/dist/esm/src/plugins/index.js +41 -54
- package/dist/esm/src/plugins/index.js.map +7 -8
- package/dist/esm/src/testing/index.js +32 -45
- package/dist/esm/src/testing/index.js.map +12 -37
- package/dist/esm/src/types/index.js +2 -2
- package/dist/esm/src/types/index.js.map +2 -2
- package/dist/esm/src/validation/index.js +2 -2
- package/dist/esm/src/validation/index.js.map +3 -3
- package/dist/esm/src/websocket/index.js +2 -15
- package/dist/esm/src/websocket/index.js.map +5 -6
- package/dist/types/auth/auth-plugin.d.ts.map +1 -1
- package/dist/types/auth/auth-service.d.ts.map +1 -1
- package/dist/types/auth/jwt-provider.d.ts +4 -4
- package/dist/types/auth/jwt-provider.d.ts.map +1 -1
- package/dist/types/auth/oauth-plugin.d.ts +2 -0
- package/dist/types/auth/oauth-plugin.d.ts.map +1 -1
- package/dist/types/auth/permission-plugin.d.ts.map +1 -1
- package/dist/types/auth/permissions.d.ts +12 -12
- package/dist/types/auth/rbac-plugin.d.ts.map +1 -1
- package/dist/types/auth/session.d.ts +2 -0
- package/dist/types/auth/session.d.ts.map +1 -1
- package/dist/types/cache/manager.d.ts +4 -0
- package/dist/types/cache/manager.d.ts.map +1 -1
- package/dist/types/core/application.d.ts.map +1 -1
- package/dist/types/core/compiled-metadata.d.ts.map +1 -1
- package/dist/types/core/metadata.d.ts.map +1 -1
- package/dist/types/core/router-compiler.d.ts +0 -1
- package/dist/types/core/router-compiler.d.ts.map +1 -1
- package/dist/types/dependencies/prisma.d.ts +40 -0
- package/dist/types/dependencies/prisma.d.ts.map +1 -0
- package/dist/types/dependencies/typeorm.d.ts +41 -0
- package/dist/types/dependencies/typeorm.d.ts.map +1 -0
- package/dist/types/graphql/plugin.d.ts +12 -0
- package/dist/types/graphql/plugin.d.ts.map +1 -1
- package/dist/types/graphql/schema-builder.d.ts +4 -5
- package/dist/types/graphql/schema-builder.d.ts.map +1 -1
- package/dist/types/index.d.ts +5 -4
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/middleware/rate-limit.d.ts.map +1 -1
- package/dist/types/orm/base-repository.d.ts.map +1 -1
- package/dist/types/orm/drizzle/plugin.d.ts +0 -22
- package/dist/types/orm/drizzle/plugin.d.ts.map +1 -1
- package/dist/types/orm/drizzle/repository.d.ts.map +1 -1
- package/dist/types/orm/drizzle/transaction-manager.d.ts.map +1 -1
- package/dist/types/orm/prisma/plugin.d.ts +1 -1
- package/dist/types/orm/prisma/repository.d.ts.map +1 -1
- package/dist/types/orm/prisma/types.d.ts +3 -0
- package/dist/types/orm/prisma/types.d.ts.map +1 -1
- package/dist/types/orm/transaction-plugin.d.ts +1 -1
- package/dist/types/orm/typeorm/decorators.d.ts +1 -1
- package/dist/types/orm/typeorm/plugin.d.ts +1 -1
- package/dist/types/orm/typeorm/repository.d.ts.map +1 -1
- package/dist/types/orm/typeorm/transaction-manager.d.ts.map +1 -1
- package/dist/types/validation/validator.d.ts.map +1 -1
- package/dist/types/websocket/plugin.d.ts +0 -4
- package/dist/types/websocket/plugin.d.ts.map +1 -1
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,126 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.0.1] - 2026-06-26
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- **GraphQL decorator pipeline wired up.** `@Resolver`, `@GQLQuery`, `@GQLMutation`, `@GQLSubscription`, and `@Arg` decorators now correctly connect to `GraphQLPlugin`. Previously the decorators wrote metadata using local symbols that `GraphQLSchemaBuilder` never read — the schema was always empty.
|
|
15
|
+
|
|
16
|
+
Pass resolver classes to the plugin via the new `resolvers` option:
|
|
17
|
+
```typescript
|
|
18
|
+
app.usePlugin(new GraphQLPlugin({ resolvers: [UserResolver, PostResolver] }));
|
|
19
|
+
```
|
|
20
|
+
Or register them with `app.include()` before calling `usePlugin()`:
|
|
21
|
+
```typescript
|
|
22
|
+
app.include(UserResolver);
|
|
23
|
+
app.usePlugin(new GraphQLPlugin());
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
- **`PermissionManager.revokePermission()` bug fixed.** When called without a `resourceId`, the filter condition `resourceId ? p.resourceId !== resourceId : true` always returned `true` (kept all records, removed nothing). Now correctly removes all permissions for the user+resource pair when no `resourceId` is specified.
|
|
27
|
+
|
|
28
|
+
- **`app.include()` now recognises `@Resolver` classes.** Previously silently ignored — now registers resolver metadata into `MetadataRegistry` so `GraphQLPlugin` can pick it up automatically.
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
|
|
32
|
+
- **89 new tests** (507 total, 0 failures) covering:
|
|
33
|
+
- GraphQL decorator metadata pipeline end-to-end
|
|
34
|
+
- `app.include()` + `resolvers: []` option merge
|
|
35
|
+
- `PermissionPlugin` construction, `PermissionManager` unit logic, management route auth guards
|
|
36
|
+
- `HealthCheckPlugin` `/health`, `/ready`, `/live` endpoints, custom check logic, `HealthCheckers` factory
|
|
37
|
+
- `OAuthPlugin` route behavior, `BaseOAuthProvider`/`GoogleOAuthProvider`/`GitHubOAuthProvider`, `OAuthStateManager`, `PKCEUtils`
|
|
38
|
+
- Logger (`createLogger`, `getLogger`, `initializeLogger`, `createChildLogger`, child context propagation)
|
|
39
|
+
|
|
40
|
+
## [1.0.0] - 2026-06-26
|
|
41
|
+
|
|
42
|
+
First stable release. Establishes a frozen public API with semver guarantees going forward.
|
|
43
|
+
|
|
44
|
+
### Breaking Changes
|
|
45
|
+
|
|
46
|
+
- **`FastAPITS` export removed.** Use `VeloceTS` or the `Veloce` alias instead.
|
|
47
|
+
```typescript
|
|
48
|
+
// Before
|
|
49
|
+
import { FastAPITS } from 'veloce-ts';
|
|
50
|
+
// After
|
|
51
|
+
import { Veloce } from 'veloce-ts';
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
- **`WebSocketPlugin` throws on Node.js at install time** instead of silently returning 501 at request time. If you were catching the 501 response, catch the constructor error instead. WebSocket is supported on Bun and Deno only.
|
|
55
|
+
```typescript
|
|
56
|
+
// Node.js — throws Error('WebSocketPlugin requires Bun or Deno runtime...')
|
|
57
|
+
app.usePlugin(new WebSocketPlugin({ ... }));
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
- **`@InjectDrizzleRepository` decorator removed.** It was a no-op stub. Remove any usages — the decorator did nothing at runtime.
|
|
61
|
+
|
|
62
|
+
### Added
|
|
63
|
+
|
|
64
|
+
- **`OAuthPlugin`, `PermissionPlugin`, `SessionPlugin`** now exported from the main `'veloce-ts'` barrel (previously only from `'veloce-ts/auth'`).
|
|
65
|
+
- **`registerPrisma(app, client)`** — thin DI helper for Prisma, mirrors `registerDrizzle`. Exports `PRISMA_TOKEN`.
|
|
66
|
+
- **`registerTypeORM(app, dataSource)`** — thin DI helper for TypeORM. Exports `TYPEORM_TOKEN`.
|
|
67
|
+
|
|
68
|
+
### Fixed
|
|
69
|
+
|
|
70
|
+
- **TypeScript errors** — 12 pre-existing errors resolved: route param casts in auth plugins, `PrismaDelegate` missing `createMany`, middleware spread type mismatch in `RouterCompiler`.
|
|
71
|
+
- **RouterCompiler** dead `compiledRoutes` Map removed — it was write-only and shared the same class-name collision bug that was already fixed in `MetadataCompiler`.
|
|
72
|
+
- **Branding** — "FastAPI-TS" replaced with "Veloce-TS" in all error messages and JSDoc across `src/adapters/hono.ts`, `src/orm/prisma/plugin.ts`, `src/orm/typeorm/plugin.ts`, `src/orm/typeorm/decorators.ts`, `src/orm/transaction-plugin.ts`.
|
|
73
|
+
|
|
74
|
+
### Test coverage
|
|
75
|
+
|
|
76
|
+
413 tests passing, 0 failures across 21 files. New files added:
|
|
77
|
+
- `tests/websocket.test.ts` — 11 tests (decorator registration, plugin construction on Bun, upgrade route, auth guards)
|
|
78
|
+
- `tests/graphql.test.ts` — 10 tests (plugin install, invalid JSON, GET/POST, playground, introspection)
|
|
79
|
+
|
|
80
|
+
## [0.4.18] - 2026-06-25
|
|
81
|
+
|
|
82
|
+
Full security & correctness audit across the framework core, auth layer, ORM integrations, and build pipeline. 375 tests passing, 0 failures.
|
|
83
|
+
|
|
84
|
+
### Fixed
|
|
85
|
+
|
|
86
|
+
**Auth / Security**
|
|
87
|
+
- **RBAC plugin:** all 7 management routes (`/roles`, `/users/:id/roles`, etc.) now correctly require `requireAuth`. Previously they were publicly accessible.
|
|
88
|
+
- **Permission plugin:** all 7 permission management routes now correctly require `requireAuth`. Same gap as RBAC.
|
|
89
|
+
- **JWT blacklist:** replaced the `Set<string>` with `Map<string, number>` (token → expiry timestamp). Provides O(1) lookup and enables `cleanupBlacklist()` to auto-purge expired entries without scanning the entire set.
|
|
90
|
+
- **OAuth plugin:** `login()` returned 401 on valid credentials due to an inverted conditional; fixed. PKCE code TTL was set to `Date.now()` (milliseconds) instead of `Date.now() / 1000` (seconds), causing immediate expiry.
|
|
91
|
+
- **Auth plugin:** removed 5 `console.log()` debug calls leaking tokens and user data to stdout in production builds.
|
|
92
|
+
- **Auth service:** `hashPassword()` was gated by an incorrect production guard that blocked legitimate use; removed.
|
|
93
|
+
|
|
94
|
+
**Core**
|
|
95
|
+
- **MetadataCompiler:** cache key changed from `"ClassName:methodName"` to a WeakMap-based numeric class identity (`targetId:methodName`). Fixes 36 test failures when running Bun's shared-process test runner: classes with the same name in different test files no longer share a cache slot, so each app instance compiles its own routes with its own DI/JWT context.
|
|
96
|
+
- **MetadataRegistry:** `getRouteMethods()` now walks the full prototype chain. Inherited route methods from a base controller were previously invisible to the router compiler.
|
|
97
|
+
- **Application:** `createAdapter()` is now `async` and properly `await`ed. `listen()` is awaited. Timeout middleware and cache configuration now propagate correctly to the adapter.
|
|
98
|
+
- **CacheManager:** added `static reset()` to clear all stores and registered names — required for test isolation between app instances in the same process.
|
|
99
|
+
|
|
100
|
+
**ORM**
|
|
101
|
+
- **Base repository:** `createMany()` now uses `Promise.all()` for parallel execution instead of sequential `await` in a loop.
|
|
102
|
+
- **Prisma repository:** fixed double insert in `create()` — the entity was being saved twice.
|
|
103
|
+
- **Drizzle transaction manager:** `commit()` was called inside `finally` block, running even after a successful commit. Moved to the normal path only.
|
|
104
|
+
- **TypeORM transaction manager:** `QueryRunner` was not released on error, leaking database connections.
|
|
105
|
+
- **Drizzle repository:** static `import` of `drizzle-orm` at module top level replaced with lazy `require()` via `getDrizzleOps()`. Fixes build failure when `drizzle-orm` is not installed (optional peer dep).
|
|
106
|
+
- **TypeORM repository:** same fix — static `import` of `typeorm` replaced with lazy `getTypeORMOps()`.
|
|
107
|
+
|
|
108
|
+
**Build**
|
|
109
|
+
- **`build.ts`:** added `external` array to both `Bun.build()` calls covering all optional peer deps (`drizzle-orm`, `typeorm`, `prisma`, `@prisma/client`, `reflect-metadata`, `graphql`, `ioredis`, `express`, `hono`). Without this, `bun run build` failed when any optional dep was absent.
|
|
110
|
+
|
|
111
|
+
**Other**
|
|
112
|
+
- **GraphQL plugin:** `JSON.parse()` on the `variables` field now wrapped in try-catch; previously threw an unhandled exception on malformed JSON.
|
|
113
|
+
- **Session store:** auto-cleanup interval registered on construction; SIGTERM/SIGINT handlers stop it on shutdown (prevents hanging processes).
|
|
114
|
+
- **Rate limit middleware:** SIGTERM/SIGINT cleanup for the cleanup interval — same pattern.
|
|
115
|
+
- **Validator:** removed dead `resultCache` WeakMap that accumulated entries without any code path reading from it.
|
|
116
|
+
|
|
117
|
+
### Added
|
|
118
|
+
|
|
119
|
+
**Tests**
|
|
120
|
+
- `tests/fixes.test.ts` — 45 integration tests covering all 24 corrected bugs above (JWT blacklist Map behavior, RBAC/Permission auth guards, CacheManager.reset(), prototype chain routing, parallel createMany, MetadataCompiler class identity, lazy ORM imports, GraphQL JSON parse errors, session cleanup, functional route timeout/cache).
|
|
121
|
+
- `tests/cache.test.ts` — 40 tests for `parseTTL`, `MemoryCacheStore` (CRUD, TTL expiry, LRU eviction, pattern deletion), and `CacheManager` (reset, named stores, generateKey, convenience functions).
|
|
122
|
+
- `tests/session.test.ts` — 17 tests for `MemorySessionStore` (CRUD, touch, expiry with real-time wait, multi-session independence).
|
|
123
|
+
|
|
124
|
+
**Total test count: 375 pass, 0 fail** (was 273 before this release).
|
|
125
|
+
|
|
126
|
+
**Benchmarks**
|
|
127
|
+
- `benchmarks/internal.bench.ts` — 14 in-process micro-benchmarks measuring: JWT sign/verify/decode, Map-based blacklist lookup (77 M ops/s), MetadataCompiler cache-miss vs cache-hit (5× faster on hit), Zod `safeParse` valid/invalid, CacheManager get/set, DIContainer singleton resolution, and full Hono stack dispatch (GET simple, GET with param, POST with Zod body).
|
|
128
|
+
- Results saved to `benchmarks/results/internal-2026-06-25.txt`.
|
|
129
|
+
|
|
10
130
|
## [0.4.10] - 2026-05-12
|
|
11
131
|
|
|
12
132
|
### Fixed
|
|
@@ -740,7 +860,10 @@ This release brings powerful performance optimization features to Veloce-TS:
|
|
|
740
860
|
- CLI tooling
|
|
741
861
|
- Testing utilities
|
|
742
862
|
|
|
743
|
-
[Unreleased]: https://github.com/AlfredoMejia3001/veloce-ts/compare/
|
|
863
|
+
[Unreleased]: https://github.com/AlfredoMejia3001/veloce-ts/compare/v1.0.0...HEAD
|
|
864
|
+
[1.0.0]: https://github.com/AlfredoMejia3001/veloce-ts/compare/v0.4.18...v1.0.0
|
|
865
|
+
[0.4.18]: https://github.com/AlfredoMejia3001/veloce-ts/compare/v0.4.10...v0.4.18
|
|
866
|
+
[0.4.10]: https://github.com/AlfredoMejia3001/veloce-ts/compare/v0.4.9...v0.4.10
|
|
744
867
|
[0.4.0]: https://github.com/AlfredoMejia3001/veloce-ts/compare/v0.3.3...v0.4.0
|
|
745
868
|
[0.3.3]: https://github.com/AlfredoMejia3001/veloce-ts/releases/tag/v0.3.3
|
|
746
869
|
[0.3.2]: https://github.com/AlfredoMejia3001/veloce-ts/releases/tag/v0.3.2
|
package/MIGRATION.md
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Migration Guide
|
|
2
|
+
|
|
3
|
+
## 0.x → 1.0.0
|
|
4
|
+
|
|
5
|
+
Three breaking changes. All have straightforward fixes.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
### 1. Replace `FastAPITS` with `Veloce`
|
|
10
|
+
|
|
11
|
+
`FastAPITS` is removed. It was deprecated in v0.4.1.
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
// Before
|
|
15
|
+
import { FastAPITS } from 'veloce-ts';
|
|
16
|
+
const app = new FastAPITS({ title: 'My API' });
|
|
17
|
+
|
|
18
|
+
// After
|
|
19
|
+
import { Veloce } from 'veloce-ts';
|
|
20
|
+
const app = new Veloce({ title: 'My API' });
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
`VeloceTS` is also valid if you prefer the full name:
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
import { VeloceTS } from 'veloce-ts';
|
|
27
|
+
const app = new VeloceTS({ title: 'My API' });
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
### 2. WebSocketPlugin throws on Node.js at startup
|
|
33
|
+
|
|
34
|
+
Previously `WebSocketPlugin` silently returned HTTP 501 on every upgrade request when running on Node.js. In 1.0.0 it throws at `app.usePlugin()` time.
|
|
35
|
+
|
|
36
|
+
**If you run on Bun or Deno** — no change needed. WebSocket works the same.
|
|
37
|
+
|
|
38
|
+
**If you run on Node.js** — remove `WebSocketPlugin` for now. Node.js WebSocket support is not yet implemented. Either:
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
// Option A: guard by runtime
|
|
42
|
+
const isBun = typeof Bun !== 'undefined';
|
|
43
|
+
if (isBun) {
|
|
44
|
+
app.usePlugin(new WebSocketPlugin({ ... }));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Option B: remove WebSocket until Node.js support lands
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
### 3. Remove `@InjectDrizzleRepository` usages
|
|
53
|
+
|
|
54
|
+
The decorator was a no-op stub that logged a warning and did nothing. Remove any imports or usages:
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
// Remove this — it never worked
|
|
58
|
+
import { InjectDrizzleRepository } from 'veloce-ts';
|
|
59
|
+
|
|
60
|
+
@InjectDrizzleRepository() // remove
|
|
61
|
+
userRepo: UserRepository;
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Use constructor injection with `@Depends` instead:
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
import { Controller, Get, Depends } from 'veloce-ts';
|
|
68
|
+
import { UserRepository } from './user.repository';
|
|
69
|
+
|
|
70
|
+
@Controller('/users')
|
|
71
|
+
class UserController {
|
|
72
|
+
@Get('/')
|
|
73
|
+
async list(@Depends(UserRepository) repo: UserRepository) {
|
|
74
|
+
return repo.findMany();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
### New in 1.0.0 — no action required
|
|
82
|
+
|
|
83
|
+
These additions are backwards-compatible:
|
|
84
|
+
|
|
85
|
+
- `OAuthPlugin`, `PermissionPlugin`, `SessionPlugin` now importable directly from `'veloce-ts'` (still also available from `'veloce-ts/auth'`)
|
|
86
|
+
- `registerPrisma(app, prismaClient)` — DI helper for Prisma
|
|
87
|
+
- `registerTypeORM(app, dataSource)` — DI helper for TypeORM
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
import { registerPrisma, registerTypeORM } from 'veloce-ts';
|
|
91
|
+
|
|
92
|
+
// Prisma
|
|
93
|
+
registerPrisma(app, prisma);
|
|
94
|
+
|
|
95
|
+
// TypeORM
|
|
96
|
+
registerTypeORM(app, dataSource);
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
### Semver from 1.0.0 onwards
|
|
102
|
+
|
|
103
|
+
Veloce-TS follows [Semantic Versioning](https://semver.org/) from this release:
|
|
104
|
+
|
|
105
|
+
- **Patch** (1.0.x) — bug fixes, no API changes
|
|
106
|
+
- **Minor** (1.x.0) — new features, backwards-compatible
|
|
107
|
+
- **Major** (x.0.0) — breaking changes with migration guide
|
|
108
|
+
|
|
109
|
+
Check [CHANGELOG.md](CHANGELOG.md) before upgrading.
|
package/README.md
CHANGED
|
@@ -83,6 +83,13 @@ Request tracing
|
|
|
83
83
|
- Timeout management
|
|
84
84
|
- Structured logging
|
|
85
85
|
|
|
86
|
+
### 🛡️ **Battle-tested** (v0.4.18)
|
|
87
|
+
Security & correctness audit
|
|
88
|
+
- 375 tests, 0 failures
|
|
89
|
+
- Auth routes secured (RBAC/Permissions)
|
|
90
|
+
- JWT blacklist O(1) Map lookup
|
|
91
|
+
- In-process benchmarks included
|
|
92
|
+
|
|
86
93
|
</td>
|
|
87
94
|
</tr>
|
|
88
95
|
</table>
|
|
@@ -236,7 +243,7 @@ app.listen(3000);
|
|
|
236
243
|
<summary><b>WebSocket Support</b></summary>
|
|
237
244
|
|
|
238
245
|
```typescript
|
|
239
|
-
import { WebSocket, OnConnect, OnMessage, OnDisconnect } from 'veloce-ts';
|
|
246
|
+
import { WebSocket, OnConnect, OnMessage, OnDisconnect, WebSocketConnection } from 'veloce-ts';
|
|
240
247
|
import { z } from 'zod';
|
|
241
248
|
|
|
242
249
|
const MessageSchema = z.object({
|
|
@@ -489,11 +496,32 @@ app.usePlugin(new OpenAPIPlugin({
|
|
|
489
496
|
docsPath: '/docs',
|
|
490
497
|
}));
|
|
491
498
|
|
|
492
|
-
// GraphQL support
|
|
499
|
+
// GraphQL support — pass resolver classes via the resolvers option
|
|
500
|
+
import { Resolver, GQLQuery, GQLMutation, Arg } from 'veloce-ts/graphql';
|
|
501
|
+
import { z } from 'zod';
|
|
502
|
+
|
|
503
|
+
@Resolver('user')
|
|
504
|
+
class UserResolver {
|
|
505
|
+
@GQLQuery('getUser')
|
|
506
|
+
async getUser(@Arg('id', z.string()) id: string) {
|
|
507
|
+
return { id, name: 'Alice' };
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
@GQLMutation('createUser')
|
|
511
|
+
async createUser(@Arg('name', z.string()) name: string) {
|
|
512
|
+
return { id: '1', name };
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
|
|
493
516
|
app.usePlugin(new GraphQLPlugin({
|
|
494
517
|
path: '/graphql',
|
|
495
518
|
playground: true,
|
|
519
|
+
resolvers: [UserResolver], // pass all @Resolver classes here
|
|
496
520
|
}));
|
|
521
|
+
|
|
522
|
+
// Alternatively, register via app.include() before usePlugin():
|
|
523
|
+
// app.include(UserResolver);
|
|
524
|
+
// app.usePlugin(new GraphQLPlugin({ playground: true }));
|
|
497
525
|
```
|
|
498
526
|
|
|
499
527
|
## 🌐 Multi-Runtime Support
|
|
@@ -520,7 +548,7 @@ These are **known gaps today** so you can choose Veloce-TS with clear expectatio
|
|
|
520
548
|
|
|
521
549
|
| Area | Limitation |
|
|
522
550
|
|------|------------|
|
|
523
|
-
| **WebSockets on Node.js** |
|
|
551
|
+
| **WebSockets on Node.js** | `WebSocketPlugin` throws at startup on Node.js with a clear error message directing you to Bun/Deno. WebSocket upgrades are supported on **Bun** and **Deno** only; Node.js support requires a `ws`/HTTP upgrade bridge not yet implemented. |
|
|
524
552
|
| **GraphQL** | The GraphQL plugin and decorators are **still maturing**. Treat them as **experimental** for production unless you have validated your use case. |
|
|
525
553
|
| **ORM integrations** | First-class **Drizzle** helpers exist for the DI container (`registerDrizzle`, `@InjectDB`). **Prisma** and **TypeORM** are **not yet** at the same level of documented, built-in integration—use them directly in your services today. |
|
|
526
554
|
| **Pre-1.0** | APIs may change between minor versions. Check the [CHANGELOG](CHANGELOG.md) before upgrading. |
|
|
@@ -534,6 +562,8 @@ Directional priorities—not a release calendar. Items may ship in a different o
|
|
|
534
562
|
3. **CLI scaffolding** — Lightweight **code generation** (e.g. controller + route + test skeleton), similar in spirit to Feathers’ `generate service`, kept maintainable and optional.
|
|
535
563
|
4. **GraphQL** — Improve stability and docs **after** REST, caching, WebSockets-on-Node, and ORM stories are in better shape (lower priority than the items above).
|
|
536
564
|
|
|
565
|
+
**Completed in v0.4.18:** Security audit of auth routes, JWT blacklist correctness, MetadataCompiler cache isolation fix, ORM lazy-loading for optional peer deps, 375-test suite, internal micro-benchmarks.
|
|
566
|
+
|
|
537
567
|
For the latest shipped changes, see [CHANGELOG](CHANGELOG.md). Discussion and proposals: [GitHub Discussions](https://github.com/AlfredoMejia3001/veloce-ts/discussions).
|
|
538
568
|
|
|
539
569
|
## 📖 Documentation
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var P=Object.create;var{getPrototypeOf:Q,defineProperty:H,getOwnPropertyNames:K,getOwnPropertyDescriptor:U}=Object,L=Object.prototype.hasOwnProperty;function M(x){return this[x]}var V,W,N=(x,z,A)=>{var D=x!=null&&typeof x==="object";if(D){var B=z?V??=new WeakMap:W??=new WeakMap,C=B.get(x);if(C)return C}A=x!=null?P(Q(x)):{};let E=z||!x||!x.__esModule?H(A,"default",{value:x,enumerable:!0}):A;for(let F of K(x))if(!L.call(E,F))H(E,F,{get:M.bind(x,F),enumerable:!0});if(D)B.set(x,E);return E},X=(x)=>{var z=(J??=new WeakMap).get(x),A;if(z)return z;if(z=H({},"__esModule",{value:!0}),x&&typeof x==="object"||typeof x==="function"){for(var D of K(x))if(!L.call(z,D))H(z,D,{get:M.bind(x,D),enumerable:!(A=U(x,D))||A.enumerable})}return J.set(x,z),z},J,R=(x,z)=>()=>(z||x((z={exports:{}}).exports,z),z.exports);var Y=(x)=>x;function Z(x,z){this[x]=Y.bind(null,z)}var $=(x,z)=>{for(var A in z)H(x,A,{get:z[A],enumerable:!0,configurable:!0,set:Z.bind(z,A)})};var T=(x,z)=>()=>(x&&(z=x(x=0)),z);var y={};$(y,{HonoAdapter:()=>O});module.exports=X(y);class O{hono;name="hono";runtime;constructor(x){this.hono=x;this.runtime=j()}listen(x,z){switch(this.runtime){case"bun":return this.listenBun(x,z);case"deno":return this.listenDeno(x,z);case"node":return this.listenNode(x,z);case"workerd":throw Error("Cloudflare Workers do not support listen(). Deploy using wrangler or export the handler with getHandler().");default:throw Error(`Unsupported runtime: ${this.runtime}.
|
|
1
|
+
var P=Object.create;var{getPrototypeOf:Q,defineProperty:H,getOwnPropertyNames:K,getOwnPropertyDescriptor:U}=Object,L=Object.prototype.hasOwnProperty;function M(x){return this[x]}var V,W,N=(x,z,A)=>{var D=x!=null&&typeof x==="object";if(D){var B=z?V??=new WeakMap:W??=new WeakMap,C=B.get(x);if(C)return C}A=x!=null?P(Q(x)):{};let E=z||!x||!x.__esModule?H(A,"default",{value:x,enumerable:!0}):A;for(let F of K(x))if(!L.call(E,F))H(E,F,{get:M.bind(x,F),enumerable:!0});if(D)B.set(x,E);return E},X=(x)=>{var z=(J??=new WeakMap).get(x),A;if(z)return z;if(z=H({},"__esModule",{value:!0}),x&&typeof x==="object"||typeof x==="function"){for(var D of K(x))if(!L.call(z,D))H(z,D,{get:M.bind(x,D),enumerable:!(A=U(x,D))||A.enumerable})}return J.set(x,z),z},J,R=(x,z)=>()=>(z||x((z={exports:{}}).exports,z),z.exports);var Y=(x)=>x;function Z(x,z){this[x]=Y.bind(null,z)}var $=(x,z)=>{for(var A in z)H(x,A,{get:z[A],enumerable:!0,configurable:!0,set:Z.bind(z,A)})};var T=(x,z)=>()=>(x&&(z=x(x=0)),z);var y={};$(y,{HonoAdapter:()=>O});module.exports=X(y);class O{hono;name="hono";runtime;constructor(x){this.hono=x;this.runtime=j()}listen(x,z){switch(this.runtime){case"bun":return this.listenBun(x,z);case"deno":return this.listenDeno(x,z);case"node":return this.listenNode(x,z);case"workerd":throw Error("Cloudflare Workers do not support listen(). Deploy using wrangler or export the handler with getHandler().");default:throw Error(`Unsupported runtime: ${this.runtime}. Veloce-TS supports Bun, Node.js, Deno, and Cloudflare Workers.`)}}getHandler(){return this.hono.fetch}getRuntime(){return this.runtime}listenBun(x,z){let A=this.hono,D=Bun.serve({port:x,fetch(B,C){return A.fetch(B,{server:C,upgrade:C.upgrade.bind(C)})},websocket:{open(B){let{manager:C,metadata:E}=B.data??{};if(!C||!E)return;let F=C.handleConnectionBun(B,E);B.data._connection=F},message(B,C){let{manager:E,metadata:F,_connection:G}=B.data??{};if(!E||!F||!G)return;E.handleMessageBun(C,G,F)},close(B,C,E){let{manager:F,metadata:G,_connection:I}=B.data??{};if(!F||!G||!I)return;F.handleDisconnectBun(I,G)},error(B,C){console.error("[WS] Bun WebSocket error:",C)}}});if(z)z();return{port:x,close:async()=>{D.stop()},...D}}listenDeno(x,z){let A=new AbortController;return Deno.serve({port:x,signal:A.signal,onListen:z},this.hono.fetch),{port:x,close:async()=>{A.abort()}}}listenNode(x,z){try{let{serve:A}=(()=>{throw new Error("Cannot require module "+"@hono/node-server");})(),D=A({fetch:this.hono.fetch,port:x},z);return{port:x,close:async()=>{return new Promise((B)=>{D.close(()=>B())})},...D}}catch(A){throw Error("Node.js adapter requires @hono/node-server package. Install it with: npm install @hono/node-server")}}}var j=()=>{if(typeof Bun<"u")return"bun";if(typeof Deno<"u")return"deno";if(typeof navigator<"u"&&navigator.userAgent==="Cloudflare-Workers")return"workerd";if(typeof process<"u"&&process.versions&&process.versions.node)return"node";return"unknown"};f();
|
|
2
2
|
|
|
3
|
-
//# debugId=
|
|
3
|
+
//# debugId=17A66C4D10DC90CF64756E2164756E21
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["..\\..\\src\\adapters\\hono.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"/**\r\n * @module veloce-ts/adapters/hono\r\n * @description {@link HonoAdapter}: detecta runtime (Bun/Node/Deno/Workers) y expone `listen` sobre la instancia Hono.\r\n */\r\nimport type { Hono } from 'hono';\r\nimport type { Adapter, ServerInstance } from './base';\r\n\r\n// Type declarations for runtime globals\r\ndeclare const Deno: any;\r\ndeclare const Bun: any;\r\n\r\n/**\r\n * Runtime detection utilities\r\n */\r\nconst detectRuntime = (): 'bun' | 'deno' | 'node' | 'workerd' | 'unknown' => {\r\n // Check for Bun\r\n if (typeof Bun !== 'undefined') {\r\n return 'bun';\r\n }\r\n\r\n // Check for Deno\r\n if (typeof Deno !== 'undefined') {\r\n return 'deno';\r\n }\r\n\r\n // Check for Cloudflare Workers\r\n if (typeof navigator !== 'undefined' && navigator.userAgent === 'Cloudflare-Workers') {\r\n return 'workerd';\r\n }\r\n\r\n // Check for Node.js\r\n if (typeof process !== 'undefined' && process.versions && process.versions.node) {\r\n return 'node';\r\n }\r\n\r\n return 'unknown';\r\n};\r\n\r\n/**\r\n * HonoAdapter - Adapts Hono.js to work across multiple runtimes\r\n * Automatically detects the runtime and uses the appropriate server implementation\r\n */\r\nexport class HonoAdapter implements Adapter {\r\n name = 'hono';\r\n private runtime: ReturnType<typeof detectRuntime>;\r\n\r\n constructor(private hono: Hono) {\r\n this.runtime = detectRuntime();\r\n }\r\n\r\n /**\r\n * Start the server on the specified port\r\n * Automatically uses the appropriate server for the detected runtime\r\n */\r\n listen(port: number, callback?: () => void): ServerInstance {\r\n switch (this.runtime) {\r\n case 'bun':\r\n return this.listenBun(port, callback);\r\n \r\n case 'deno':\r\n return this.listenDeno(port, callback);\r\n \r\n case 'node':\r\n return this.listenNode(port, callback);\r\n \r\n case 'workerd':\r\n throw new Error(\r\n 'Cloudflare Workers do not support listen(). Deploy using wrangler or export the handler with getHandler().'\r\n );\r\n \r\n default:\r\n throw new Error(\r\n `Unsupported runtime: ${this.runtime}.
|
|
5
|
+
"/**\r\n * @module veloce-ts/adapters/hono\r\n * @description {@link HonoAdapter}: detecta runtime (Bun/Node/Deno/Workers) y expone `listen` sobre la instancia Hono.\r\n */\r\nimport type { Hono } from 'hono';\r\nimport type { Adapter, ServerInstance } from './base';\r\n\r\n// Type declarations for runtime globals\r\ndeclare const Deno: any;\r\ndeclare const Bun: any;\r\n\r\n/**\r\n * Runtime detection utilities\r\n */\r\nconst detectRuntime = (): 'bun' | 'deno' | 'node' | 'workerd' | 'unknown' => {\r\n // Check for Bun\r\n if (typeof Bun !== 'undefined') {\r\n return 'bun';\r\n }\r\n\r\n // Check for Deno\r\n if (typeof Deno !== 'undefined') {\r\n return 'deno';\r\n }\r\n\r\n // Check for Cloudflare Workers\r\n if (typeof navigator !== 'undefined' && navigator.userAgent === 'Cloudflare-Workers') {\r\n return 'workerd';\r\n }\r\n\r\n // Check for Node.js\r\n if (typeof process !== 'undefined' && process.versions && process.versions.node) {\r\n return 'node';\r\n }\r\n\r\n return 'unknown';\r\n};\r\n\r\n/**\r\n * HonoAdapter - Adapts Hono.js to work across multiple runtimes\r\n * Automatically detects the runtime and uses the appropriate server implementation\r\n */\r\nexport class HonoAdapter implements Adapter {\r\n name = 'hono';\r\n private runtime: ReturnType<typeof detectRuntime>;\r\n\r\n constructor(private hono: Hono) {\r\n this.runtime = detectRuntime();\r\n }\r\n\r\n /**\r\n * Start the server on the specified port\r\n * Automatically uses the appropriate server for the detected runtime\r\n */\r\n listen(port: number, callback?: () => void): ServerInstance {\r\n switch (this.runtime) {\r\n case 'bun':\r\n return this.listenBun(port, callback);\r\n \r\n case 'deno':\r\n return this.listenDeno(port, callback);\r\n \r\n case 'node':\r\n return this.listenNode(port, callback);\r\n \r\n case 'workerd':\r\n throw new Error(\r\n 'Cloudflare Workers do not support listen(). Deploy using wrangler or export the handler with getHandler().'\r\n );\r\n \r\n default:\r\n throw new Error(\r\n `Unsupported runtime: ${this.runtime}. Veloce-TS supports Bun, Node.js, Deno, and Cloudflare Workers.`\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Get the Hono fetch handler\r\n * This can be used directly in serverless environments or for custom server setups\r\n */\r\n getHandler() {\r\n return this.hono.fetch;\r\n }\r\n\r\n /**\r\n * Get the detected runtime\r\n */\r\n getRuntime(): string {\r\n return this.runtime;\r\n }\r\n\r\n /**\r\n * Start server using Bun's native server\r\n * Passes the Bun server instance as `env` to Hono so WebSocketPlugin\r\n * can call server.upgrade() from within route handlers.\r\n */\r\n private listenBun(port: number, callback?: () => void): ServerInstance {\r\n const hono = this.hono;\r\n const server = Bun.serve({\r\n port,\r\n fetch(req: Request, bunServer: any) {\r\n // Pass the Bun server as `env` so c.env.upgrade() works in handlers,\r\n // and expose the server itself so `getConnInfo` (hono/bun) can read the\r\n // trusted peer IP — needed for IP-based rate limiting that must not\r\n // trust client-supplied X-Forwarded-For headers.\r\n return hono.fetch(req, { server: bunServer, upgrade: bunServer.upgrade.bind(bunServer) });\r\n },\r\n websocket: {\r\n open(ws: any) {\r\n const { manager, metadata } = ws.data ?? {};\r\n if (!manager || !metadata) return;\r\n const connection = manager.handleConnectionBun(ws, metadata);\r\n ws.data._connection = connection;\r\n },\r\n message(ws: any, message: string | Buffer) {\r\n const { manager, metadata, _connection } = ws.data ?? {};\r\n if (!manager || !metadata || !_connection) return;\r\n manager.handleMessageBun(message, _connection, metadata);\r\n },\r\n close(ws: any, code: number, reason: string) {\r\n const { manager, metadata, _connection } = ws.data ?? {};\r\n if (!manager || !metadata || !_connection) return;\r\n manager.handleDisconnectBun(_connection, metadata);\r\n },\r\n error(ws: any, error: Error) {\r\n console.error('[WS] Bun WebSocket error:', error);\r\n },\r\n },\r\n });\r\n\r\n if (callback) {\r\n callback();\r\n }\r\n\r\n return {\r\n port,\r\n close: async () => {\r\n server.stop();\r\n },\r\n ...server\r\n };\r\n }\r\n\r\n /**\r\n * Start server using Deno's native server\r\n */\r\n private listenDeno(port: number, callback?: () => void): ServerInstance {\r\n // Deno.serve returns a promise, so we handle it appropriately\r\n const ac = new AbortController();\r\n \r\n Deno.serve(\r\n {\r\n port,\r\n signal: ac.signal,\r\n onListen: callback\r\n },\r\n this.hono.fetch\r\n );\r\n\r\n // Return an object with a close method for consistency\r\n return {\r\n port,\r\n close: async () => {\r\n ac.abort();\r\n },\r\n };\r\n }\r\n\r\n /**\r\n * Start server using Node.js adapter\r\n * Requires @hono/node-server package\r\n */\r\n private listenNode(port: number, callback?: () => void): ServerInstance {\r\n try {\r\n // Dynamically import @hono/node-server\r\n // This is a peer dependency that users need to install for Node.js support\r\n const { serve } = require('@hono/node-server');\r\n \r\n const server = serve(\r\n {\r\n fetch: this.hono.fetch,\r\n port,\r\n },\r\n callback\r\n );\r\n\r\n return {\r\n port,\r\n close: async () => {\r\n return new Promise<void>((resolve) => {\r\n server.close(() => resolve());\r\n });\r\n },\r\n ...server\r\n };\r\n } catch (error) {\r\n throw new Error(\r\n 'Node.js adapter requires @hono/node-server package. Install it with: npm install @hono/node-server'\r\n );\r\n }\r\n }\r\n}\r\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": "ihCA0CO,MAAM,CAA+B,CAItB,KAHpB,KAAO,OACC,QAER,WAAW,CAAS,EAAY,CAAZ,YAClB,KAAK,QAAU,EAAc,EAO/B,MAAM,CAAC,EAAc,EAAuC,CAC1D,OAAQ,KAAK,aACN,MACH,OAAO,KAAK,UAAU,EAAM,CAAQ,MAEjC,OACH,OAAO,KAAK,WAAW,EAAM,CAAQ,MAElC,OACH,OAAO,KAAK,WAAW,EAAM,CAAQ,MAElC,UACH,MAAU,MACR,4GACF,UAGA,MAAU,MACR,wBAAwB,KAAK,
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "ihCA0CO,MAAM,CAA+B,CAItB,KAHpB,KAAO,OACC,QAER,WAAW,CAAS,EAAY,CAAZ,YAClB,KAAK,QAAU,EAAc,EAO/B,MAAM,CAAC,EAAc,EAAuC,CAC1D,OAAQ,KAAK,aACN,MACH,OAAO,KAAK,UAAU,EAAM,CAAQ,MAEjC,OACH,OAAO,KAAK,WAAW,EAAM,CAAQ,MAElC,OACH,OAAO,KAAK,WAAW,EAAM,CAAQ,MAElC,UACH,MAAU,MACR,4GACF,UAGA,MAAU,MACR,wBAAwB,KAAK,yEAC/B,GAQN,UAAU,EAAG,CACX,OAAO,KAAK,KAAK,MAMnB,UAAU,EAAW,CACnB,OAAO,KAAK,QAQN,SAAS,CAAC,EAAc,EAAuC,CACrE,IAAM,EAAO,KAAK,KACZ,EAAS,IAAI,MAAM,CACvB,OACA,KAAK,CAAC,EAAc,EAAgB,CAKlC,OAAO,EAAK,MAAM,EAAK,CAAE,OAAQ,EAAW,QAAS,EAAU,QAAQ,KAAK,CAAS,CAAE,CAAC,GAE1F,UAAW,CACT,IAAI,CAAC,EAAS,CACZ,IAAQ,UAAS,YAAa,EAAG,MAAQ,CAAC,EAC1C,GAAI,CAAC,GAAW,CAAC,EAAU,OAC3B,IAAM,EAAa,EAAQ,oBAAoB,EAAI,CAAQ,EAC3D,EAAG,KAAK,YAAc,GAExB,OAAO,CAAC,EAAS,EAA0B,CACzC,IAAQ,UAAS,WAAU,eAAgB,EAAG,MAAQ,CAAC,EACvD,GAAI,CAAC,GAAW,CAAC,GAAY,CAAC,EAAa,OAC3C,EAAQ,iBAAiB,EAAS,EAAa,CAAQ,GAEzD,KAAK,CAAC,EAAS,EAAc,EAAgB,CAC3C,IAAQ,UAAS,WAAU,eAAgB,EAAG,MAAQ,CAAC,EACvD,GAAI,CAAC,GAAW,CAAC,GAAY,CAAC,EAAa,OAC3C,EAAQ,oBAAoB,EAAa,CAAQ,GAEnD,KAAK,CAAC,EAAS,EAAc,CAC3B,QAAQ,MAAM,4BAA6B,CAAK,EAEpD,CACF,CAAC,EAED,GAAI,EACF,EAAS,EAGX,MAAO,CACL,OACA,MAAO,SAAY,CACjB,EAAO,KAAK,MAEX,CACL,EAMM,UAAU,CAAC,EAAc,EAAuC,CAEtE,IAAM,EAAK,IAAI,gBAYf,OAVA,KAAK,MACH,CACE,OACA,OAAQ,EAAG,OACX,SAAU,CACZ,EACA,KAAK,KAAK,KACZ,EAGO,CACL,OACA,MAAO,SAAY,CACjB,EAAG,MAAM,EAEb,EAOM,UAAU,CAAC,EAAc,EAAuC,CACtE,GAAI,CAGF,IAAQ,kFAEF,EAAS,EACb,CACE,MAAO,KAAK,KAAK,MACjB,MACF,EACA,CACF,EAEA,MAAO,CACL,OACA,MAAO,SAAY,CACjB,OAAO,IAAI,QAAc,CAAC,IAAY,CACpC,EAAO,MAAM,IAAM,EAAQ,CAAC,EAC7B,MAEA,CACL,EACA,MAAO,EAAO,CACd,MAAU,MACR,oGACF,GAGN,KA5LM,EAAgB,IAAuD,CAE3E,GAAI,OAAO,IAAQ,IACjB,MAAO,MAIT,GAAI,OAAO,KAAS,IAClB,MAAO,OAIT,GAAI,OAAO,UAAc,KAAe,UAAU,YAAc,qBAC9D,MAAO,UAIT,GAAI,OAAO,QAAY,KAAe,QAAQ,UAAY,QAAQ,SAAS,KACzE,MAAO,OAGT,MAAO",
|
|
8
|
+
"debugId": "17A66C4D10DC90CF64756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|