@spfn/core 0.1.0-alpha.7 → 0.1.0-alpha.72

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.
Files changed (59) hide show
  1. package/README.md +168 -195
  2. package/dist/auto-loader-JFaZ9gON.d.ts +80 -0
  3. package/dist/cache/index.d.ts +211 -0
  4. package/dist/cache/index.js +992 -0
  5. package/dist/cache/index.js.map +1 -0
  6. package/dist/client/index.d.ts +92 -92
  7. package/dist/client/index.js +80 -85
  8. package/dist/client/index.js.map +1 -1
  9. package/dist/codegen/generators/index.d.ts +19 -0
  10. package/dist/codegen/generators/index.js +1491 -0
  11. package/dist/codegen/generators/index.js.map +1 -0
  12. package/dist/codegen/index.d.ts +76 -60
  13. package/dist/codegen/index.js +1479 -736
  14. package/dist/codegen/index.js.map +1 -1
  15. package/dist/database-errors-BNNmLTJE.d.ts +86 -0
  16. package/dist/db/index.d.ts +844 -44
  17. package/dist/db/index.js +1262 -1309
  18. package/dist/db/index.js.map +1 -1
  19. package/dist/env/index.d.ts +508 -0
  20. package/dist/env/index.js +1106 -0
  21. package/dist/env/index.js.map +1 -0
  22. package/dist/error-handler-wjLL3v-a.d.ts +44 -0
  23. package/dist/errors/index.d.ts +136 -0
  24. package/dist/errors/index.js +172 -0
  25. package/dist/errors/index.js.map +1 -0
  26. package/dist/index-DHiAqhKv.d.ts +101 -0
  27. package/dist/index.d.ts +3 -374
  28. package/dist/index.js +2394 -2176
  29. package/dist/index.js.map +1 -1
  30. package/dist/logger/index.d.ts +94 -0
  31. package/dist/logger/index.js +774 -0
  32. package/dist/logger/index.js.map +1 -0
  33. package/dist/middleware/index.d.ts +33 -0
  34. package/dist/middleware/index.js +890 -0
  35. package/dist/middleware/index.js.map +1 -0
  36. package/dist/route/index.d.ts +21 -53
  37. package/dist/route/index.js +1234 -219
  38. package/dist/route/index.js.map +1 -1
  39. package/dist/server/index.d.ts +18 -0
  40. package/dist/server/index.js +2390 -2058
  41. package/dist/server/index.js.map +1 -1
  42. package/dist/types-Dzggq1Yb.d.ts +170 -0
  43. package/package.json +59 -15
  44. package/dist/auto-loader-C44TcLmM.d.ts +0 -125
  45. package/dist/bind-pssq1NRT.d.ts +0 -34
  46. package/dist/postgres-errors-CY_Es8EJ.d.ts +0 -1703
  47. package/dist/scripts/index.d.ts +0 -24
  48. package/dist/scripts/index.js +0 -1201
  49. package/dist/scripts/index.js.map +0 -1
  50. package/dist/scripts/templates/api-index.template.txt +0 -10
  51. package/dist/scripts/templates/api-tag.template.txt +0 -11
  52. package/dist/scripts/templates/contract.template.txt +0 -87
  53. package/dist/scripts/templates/entity-type.template.txt +0 -31
  54. package/dist/scripts/templates/entity.template.txt +0 -19
  55. package/dist/scripts/templates/index.template.txt +0 -10
  56. package/dist/scripts/templates/repository.template.txt +0 -37
  57. package/dist/scripts/templates/routes-id.template.txt +0 -59
  58. package/dist/scripts/templates/routes-index.template.txt +0 -44
  59. package/dist/types-SlzTr8ZO.d.ts +0 -143
package/README.md CHANGED
@@ -58,7 +58,7 @@ export const getUsersContract = {
58
58
  // src/server/routes/users/index.ts
59
59
  import { createApp } from '@spfn/core/route';
60
60
  import { getUsersContract } from './contract.js';
61
- import { getRepository } from '@spfn/core/db';
61
+ import { findMany } from '@spfn/core/db';
62
62
  import { users } from '../../entities/users.js';
63
63
 
64
64
  const app = createApp();
@@ -66,14 +66,11 @@ const app = createApp();
66
66
  app.bind(getUsersContract, async (c) => {
67
67
  const { page = 1, limit = 10 } = c.query;
68
68
 
69
- // Get repository singleton - automatically cached
70
- const repo = getRepository(users);
69
+ // Use helper function directly - no Repository needed
70
+ const offset = (page - 1) * limit;
71
+ const result = await findMany(users, { limit, offset });
71
72
 
72
- const result = await repo.findPage({
73
- pagination: { page, limit }
74
- });
75
-
76
- return c.json(result);
73
+ return c.json({ users: result, total: result.length });
77
74
  });
78
75
 
79
76
  export default app;
@@ -88,7 +85,7 @@ npm run spfn:dev
88
85
 
89
86
  ## Architecture Pattern
90
87
 
91
- SPFN follows a **layered architecture** that separates concerns and keeps code maintainable:
88
+ Superfunction follows a **layered architecture** that separates concerns and keeps code maintainable:
92
89
 
93
90
  ```
94
91
  ┌─────────────────────────────────────────┐
@@ -102,14 +99,14 @@ SPFN follows a **layered architecture** that separates concerns and keeps code m
102
99
  │ Service Layer │ Business logic
103
100
  │ - Orchestrate operations │
104
101
  │ - Implement business rules │
105
- │ - Use repositories
102
+ │ - Use helper functions or custom logic
106
103
  └──────────────┬──────────────────────────┘
107
104
 
108
105
  ┌──────────────▼──────────────────────────┐
109
- Repository Layer Data access
110
- │ - CRUD operations
111
- │ - Custom queries
112
- │ - Extend base Repository
106
+ Data Access Layer Database operations
107
+ │ - Use helper functions (findOne, etc)
108
+ │ - Custom queries with Drizzle
109
+ │ - Domain-specific wrappers
113
110
  └──────────────┬──────────────────────────┘
114
111
 
115
112
  ┌──────────────▼──────────────────────────┐
@@ -144,134 +141,35 @@ export type Post = typeof posts.$inferSelect;
144
141
  export type NewPost = typeof posts.$inferInsert;
145
142
  ```
146
143
 
147
- **2. Repository Layer** - Data access with custom methods
144
+ **2. Data Access Layer** - Helper functions with domain-specific wrappers
148
145
 
149
146
  ```typescript
150
147
  // src/server/repositories/posts.repository.ts
151
- import { eq } from 'drizzle-orm';
152
- import { Repository } from '@spfn/core/db';
153
- import { posts } from '../entities';
154
- import type { Post } from '../entities';
155
-
156
- export class PostRepository extends Repository<typeof posts>
157
- {
158
- async findBySlug(slug: string): Promise<Post | null>
159
- {
160
- return this.findOne(eq(this.table.slug, slug));
161
- }
162
-
163
- async findPublished(): Promise<Post[]>
164
- {
165
- const results = await this.db
166
- .select()
167
- .from(this.table)
168
- .where(eq(this.table.status, 'published'))
169
- .orderBy(this.table.createdAt);
170
-
171
- return results;
172
- }
173
- }
174
- ```
175
-
176
- **3. Service Layer** - Business logic (Function-based pattern)
148
+ import { findOne, findMany, create as createHelper } from '@spfn/core/db';
149
+ import { eq, desc } from 'drizzle-orm';
150
+ import { posts, type Post, type NewPost } from '../entities/posts';
177
151
 
178
- ```typescript
179
- // src/server/services/posts.ts
180
- import { getRepository } from '@spfn/core/db';
181
- import { ValidationError, DatabaseError, NotFoundError } from '@spfn/core';
182
- import { posts } from '../entities';
183
- import { PostRepository } from '../repositories/posts.repository';
184
- import type { NewPost, Post } from '../entities';
185
-
186
- /**
187
- * Create a new post
188
- */
189
- export async function createPost(data: {
190
- title: string;
191
- content: string;
192
- }): Promise<Post> {
193
- try {
194
- // Get repository singleton
195
- const repo = getRepository(posts, PostRepository);
196
-
197
- // Business logic: Generate slug from title
198
- const slug = generateSlug(data.title);
199
-
200
- // Validation: Check if slug already exists
201
- const existing = await repo.findBySlug(slug);
202
- if (existing) {
203
- throw new ValidationError('Post with this title already exists', {
204
- fields: [{
205
- path: '/title',
206
- message: 'A post with this title already exists',
207
- value: data.title
208
- }]
209
- });
210
- }
211
-
212
- // Create post
213
- return await repo.save({
214
- ...data,
215
- slug,
216
- status: 'draft',
217
- });
218
- } catch (error) {
219
- // Re-throw ValidationError as-is
220
- if (error instanceof ValidationError) {
221
- throw error;
222
- }
223
-
224
- // Wrap unexpected errors
225
- throw new DatabaseError('Failed to create post', 500, {
226
- originalError: error instanceof Error ? error.message : String(error)
227
- });
228
- }
152
+ // Domain-specific wrappers using helper functions
153
+ export async function findPostBySlug(slug: string): Promise<Post | null> {
154
+ return findOne(posts, { slug });
229
155
  }
230
156
 
231
- /**
232
- * Publish a post
233
- */
234
- export async function publishPost(id: string): Promise<Post> {
235
- try {
236
- const repo = getRepository(posts, PostRepository);
237
- const post = await repo.update(id, { status: 'published' });
238
-
239
- if (!post) {
240
- throw new NotFoundError('Post not found');
241
- }
242
-
243
- return post;
244
- } catch (error) {
245
- if (error instanceof NotFoundError) {
246
- throw error;
247
- }
248
-
249
- throw new DatabaseError('Failed to publish post', 500, {
250
- originalError: error instanceof Error ? error.message : String(error)
251
- });
252
- }
157
+ export async function findPublishedPosts(): Promise<Post[]> {
158
+ return findMany(posts, {
159
+ where: { status: 'published' },
160
+ orderBy: desc(posts.createdAt)
161
+ });
253
162
  }
254
163
 
255
- /**
256
- * Get all published posts
257
- */
258
- export async function getPublishedPosts(): Promise<Post[]> {
259
- const repo = getRepository(posts, PostRepository);
260
- return repo.findPublished();
164
+ export async function createPost(data: NewPost): Promise<Post> {
165
+ return createHelper(posts, data);
261
166
  }
262
167
 
263
- /**
264
- * Helper: Generate URL-friendly slug from title
265
- */
266
- function generateSlug(title: string): string {
267
- return title
268
- .toLowerCase()
269
- .replace(/[^a-z0-9]+/g, '-')
270
- .replace(/(^-|-$)/g, '');
271
- }
168
+ // Or use helper functions directly in routes for simple cases
169
+ // const post = await findOne(posts, { id: 1 });
272
170
  ```
273
171
 
274
- **4. Routes Layer** - HTTP API
172
+ **3. Routes Layer** - HTTP API
275
173
 
276
174
  ```typescript
277
175
  // src/server/routes/posts/contracts.ts
@@ -306,22 +204,40 @@ export const listPostsContract = {
306
204
  // src/server/routes/posts/index.ts
307
205
  import { createApp } from '@spfn/core/route';
308
206
  import { Transactional } from '@spfn/core/db';
309
- import { createPost, getPublishedPosts } from '../../services/posts';
207
+ import { findPostBySlug, createPost, findPublishedPosts } from '../../repositories/posts.repository';
310
208
  import { createPostContract, listPostsContract } from './contracts';
311
209
 
312
210
  const app = createApp();
313
211
 
314
212
  // POST /posts - Create new post (with transaction)
315
- app.bind(createPostContract, Transactional(), async (c) => {
213
+ app.bind(createPostContract, [Transactional()], async (c) => {
316
214
  const body = await c.data();
317
- const post = await createPost(body);
215
+
216
+ // Generate slug from title
217
+ const slug = body.title.toLowerCase()
218
+ .replace(/[^a-z0-9]+/g, '-')
219
+ .replace(/(^-|-$)/g, '');
220
+
221
+ // Check if slug exists
222
+ const existing = await findPostBySlug(slug);
223
+ if (existing) {
224
+ return c.json({ error: 'Post with this title already exists' }, 409);
225
+ }
226
+
227
+ // Create post
228
+ const post = await createPost({
229
+ ...body,
230
+ slug,
231
+ status: 'draft'
232
+ });
233
+
318
234
  // ✅ Auto-commit on success, auto-rollback on error
319
235
  return c.json(post, 201);
320
236
  });
321
237
 
322
238
  // GET /posts - List published posts (no transaction needed)
323
239
  app.bind(listPostsContract, async (c) => {
324
- const posts = await getPublishedPosts();
240
+ const posts = await findPublishedPosts();
325
241
  return c.json(posts);
326
242
  });
327
243
 
@@ -340,10 +256,10 @@ export default app;
340
256
 
341
257
  **✅ Reusability**
342
258
  - Services can be used by multiple routes
343
- - Repositories can be shared across services
259
+ - Data access functions can be shared across services
344
260
 
345
261
  **✅ Type Safety**
346
- - Types flow from Entity → Repository → Service → Route
262
+ - Types flow from Entity → Data Access → Service → Route
347
263
  - Full IDE autocomplete and error checking
348
264
 
349
265
  **✅ Maintainability**
@@ -355,7 +271,7 @@ export default app;
355
271
  | Layer | Responsibility | Examples |
356
272
  |-------|---------------|----------|
357
273
  | **Entity** | Define data structure | Schema, types, constraints |
358
- | **Repository** | Data access | CRUD, custom queries, joins |
274
+ | **Data Access** | Database operations | Helper functions, custom queries, joins |
359
275
  | **Service** | Business logic | Validation, orchestration, rules |
360
276
  | **Routes** | HTTP interface | Contracts, request handling |
361
277
 
@@ -366,21 +282,16 @@ export default app;
366
282
  - ✅ Export inferred types: `Post`, `NewPost`
367
283
  - ✅ Use TEXT with enum for status fields
368
284
 
369
- **Repository Layer:**
370
- - ✅ Extend `Repository<typeof table>` for custom methods
371
- - ✅ Use `getRepository(table)` or `getRepository(table, CustomRepo)`
372
- - ✅ Add domain-specific query methods
373
- - ✅ Return typed results
374
-
375
- **Service Layer:**
376
- - ✅ Use function-based pattern (export async functions)
377
- - ✅ Get repositories via `getRepository()` (singleton)
378
- - ✅ Implement business logic and validation
379
- - ✅ Throw descriptive errors
380
- - ✅ Keep functions focused and small
285
+ **Data Access Layer:**
286
+ - ✅ Use helper functions for simple CRUD: `findOne()`, `create()`, etc.
287
+ - ✅ Create domain-specific wrappers in `src/server/repositories/*.repository.ts`
288
+ - ✅ Export functions (not classes): `export async function findPostBySlug()`
289
+ - ✅ Use object-based where for simple queries: `{ id: 1 }`
290
+ - ✅ Use SQL-based where for complex queries: `and(eq(...), gt(...))`
291
+ - ✅ Full TypeScript type inference from table schemas
381
292
 
382
293
  **Routes Layer:**
383
- - ✅ Keep handlers thin (delegate to services)
294
+ - ✅ Keep handlers thin (delegate to services/data access)
384
295
  - ✅ Define contracts with TypeBox
385
296
  - ✅ Use `Transactional()` middleware for write operations
386
297
  - ✅ Use `c.data()` for validated input
@@ -399,61 +310,75 @@ File-based routing with contract validation and type safety.
399
310
  - Type-safe request/response handling
400
311
  - Method-level middleware control (skip auth per HTTP method)
401
312
 
402
- ### 🗄️ Database & Repository
403
- Drizzle ORM integration with repository pattern and pagination.
313
+ ### 🗄️ Database
314
+ Drizzle ORM integration with type-safe helper functions and automatic transaction handling.
404
315
 
405
316
  **[→ Read Database Documentation](./src/db/README.md)**
406
317
 
407
- **Guides:**
408
- - [Repository Pattern](./src/db/docs/repository.md)
409
- - [Schema Helpers](./src/db/docs/schema-helpers.md)
410
- - [Database Manager](./src/db/docs/database-manager.md)
318
+ **Key Features:**
319
+ - Helper functions for type-safe CRUD operations
320
+ - Automatic transaction handling and read/write separation
321
+ - Schema helpers: `id()`, `timestamps()`, `foreignKey()`
322
+ - Hybrid where clause support (objects or SQL)
323
+ - **Function schema auto-discovery** (see below)
324
+
325
+ ### 📦 Function Schema Discovery
326
+ Automatic discovery of database schemas from Superfunction ecosystem functions.
411
327
 
412
- #### Choosing a Repository Pattern
328
+ **[→ Read Database Manager Documentation](./src/db/manager/README.md)**
413
329
 
414
- SPFN offers two repository patterns. Choose based on your needs:
330
+ **Key Features:**
331
+ - Zero-config schema discovery from `@spfn/*` functions
332
+ - Functions declare schemas via `package.json`
333
+ - No hard dependencies between functions
334
+ - Efficient scanning (direct dependencies only)
335
+ - Function-specific migration support
415
336
 
416
- **Global Singleton (`getRepository`)**
417
- ```typescript
418
- import { getRepository } from '@spfn/core/db';
337
+ **How it works:**
419
338
 
420
- const repo = getRepository(users);
339
+ Functions declare their schemas in `package.json`:
340
+ ```json
341
+ {
342
+ "name": "@spfn/cms",
343
+ "spfn": {
344
+ "schemas": ["./dist/entities/*.js"],
345
+ "setupMessage": "📚 Setup guide..."
346
+ }
347
+ }
421
348
  ```
422
349
 
423
- - Simple API, minimal setup
424
- - ✅ Maximum memory efficiency
425
- - ⚠️ Requires manual `clearRepositoryCache()` in tests
426
- - ⚠️ Global state across all requests
427
- - 📝 **Use for:** Simple projects, prototypes, single-instance services
428
-
429
- **Request-Scoped (`getScopedRepository` + `RepositoryScope()`)** ⭐ Recommended
350
+ Superfunction automatically discovers and merges these schemas during migration generation:
430
351
  ```typescript
431
- import { getScopedRepository, RepositoryScope } from '@spfn/core/db';
352
+ import { getDrizzleConfig } from '@spfn/core'
432
353
 
433
- // Add middleware once (in server setup)
434
- app.use(RepositoryScope());
435
-
436
- // Use in routes/services
437
- const repo = getScopedRepository(users);
354
+ // Auto-discovers all function schemas
355
+ const config = getDrizzleConfig()
438
356
  ```
439
357
 
440
- - Automatic per-request isolation
441
- - ✅ No manual cache clearing needed
442
- - Test-friendly (each test gets fresh instances)
443
- -Production-ready with graceful degradation
444
- - 📝 **Use for:** Production apps, complex testing, team projects
358
+ **Install functions with automatic DB setup:**
359
+ ```bash
360
+ pnpm spfn add @spfn/cms
361
+ #Installs function
362
+ # Generates migrations
363
+ # ✅ Applies migrations
364
+ # ✅ Shows setup guide
365
+ ```
445
366
 
446
- **Comparison:**
367
+ **Create your own Superfunction packages:**
368
+ ```typescript
369
+ // 1. Define entities
370
+ export const myTable = pgTable('my_table', { ... })
447
371
 
448
- | Feature | `getRepository` | `getScopedRepository` |
449
- |---------|----------------|----------------------|
450
- | Setup | Zero config | Add middleware |
451
- | Test isolation | Manual | Automatic |
452
- | Memory | Shared cache | Per-request cache |
453
- | State | Global | Request-scoped |
454
- | Best for | Prototypes | Production |
372
+ // 2. Add to package.json
373
+ {
374
+ "spfn": {
375
+ "schemas": ["./dist/entities/*.js"]
376
+ }
377
+ }
455
378
 
456
- [→ See full request-scoped documentation](./src/db/repository/request-scope.ts)
379
+ // 3. Users install with one command
380
+ // pnpm spfn add @yourcompany/spfn-plugin
381
+ ```
457
382
 
458
383
  ### 🔄 Transactions
459
384
  Automatic transaction management with async context propagation.
@@ -485,6 +410,30 @@ Server configuration and lifecycle management.
485
410
 
486
411
  **[→ Read Server Documentation](./src/server/README.md)**
487
412
 
413
+ ### 📝 Logger
414
+ High-performance logging with multiple transports, sensitive data masking, and automatic validation.
415
+
416
+ **[→ Read Logger Documentation](./src/logger/README.md)**
417
+
418
+ **Key Features:**
419
+ - Adapter pattern (Pino for production, custom for full control)
420
+ - Sensitive data masking (passwords, tokens, API keys)
421
+ - File rotation (date and size-based) with automatic cleanup
422
+ - Configuration validation with clear error messages
423
+ - Multiple transports (Console, File, Slack, Email)
424
+
425
+ ### ⚙️ Code Generation
426
+ Automatic code generation with pluggable generators and centralized file watching.
427
+
428
+ **[→ Read Codegen Documentation](./src/codegen/README.md)**
429
+
430
+ **Key Features:**
431
+ - Orchestrator pattern for managing multiple generators
432
+ - Built-in contract generator for type-safe API clients
433
+ - Configuration-based setup (`.spfnrc.json` or `package.json`)
434
+ - Watch mode integrated into `spfn dev`
435
+ - Extensible with custom generators
436
+
488
437
  ## Module Exports
489
438
 
490
439
  ### Main Export
@@ -501,11 +450,17 @@ import type { RouteContext, RouteContract } from '@spfn/core/route';
501
450
  ### Database
502
451
  ```typescript
503
452
  import {
504
- getDb,
505
- Repository,
506
- getRepository
453
+ getDatabase,
454
+ findOne,
455
+ findMany,
456
+ create,
457
+ createMany,
458
+ updateOne,
459
+ updateMany,
460
+ deleteOne,
461
+ deleteMany,
462
+ count
507
463
  } from '@spfn/core/db';
508
- import type { Pageable, Page } from '@spfn/core/db';
509
464
  ```
510
465
 
511
466
  ### Transactions
@@ -522,6 +477,11 @@ import {
522
477
  import { initRedis, getRedis, getRedisRead } from '@spfn/core';
523
478
  ```
524
479
 
480
+ ### Logger
481
+ ```typescript
482
+ import { logger } from '@spfn/core';
483
+ ```
484
+
525
485
  ### Client (for frontend)
526
486
  ```typescript
527
487
  import { ContractClient, createClient } from '@spfn/core/client';
@@ -545,6 +505,17 @@ REDIS_READ_URL=redis://replica:6379
545
505
  PORT=8790
546
506
  HOST=localhost
547
507
  NODE_ENV=development
508
+
509
+ # Server Timeouts (optional, in milliseconds)
510
+ SERVER_TIMEOUT=120000 # Request timeout (default: 120000)
511
+ SERVER_KEEPALIVE_TIMEOUT=65000 # Keep-alive timeout (default: 65000)
512
+ SERVER_HEADERS_TIMEOUT=60000 # Headers timeout (default: 60000)
513
+ SHUTDOWN_TIMEOUT=30000 # Graceful shutdown timeout (default: 30000)
514
+
515
+ # Logger (optional)
516
+ LOGGER_ADAPTER=pino # pino | custom (default: pino)
517
+ LOGGER_FILE_ENABLED=true # Enable file logging (production only)
518
+ LOG_DIR=/var/log/myapp # Log directory (required when file logging enabled)
548
519
  ```
549
520
 
550
521
  ## Requirements
@@ -568,12 +539,14 @@ npm test -- --coverage # With coverage
568
539
 
569
540
  ### Guides
570
541
  - [File-based Routing](./src/route/README.md)
571
- - [Database & Repository](./src/db/README.md)
542
+ - [Database & Helper Functions](./src/db/README.md)
572
543
  - [Transaction Management](./src/db/docs/transactions.md)
573
544
  - [Redis Cache](./src/cache/README.md)
574
545
  - [Error Handling](./src/errors/README.md)
575
546
  - [Middleware](./src/middleware/README.md)
576
547
  - [Server Configuration](./src/server/README.md)
548
+ - [Logger](./src/logger/README.md)
549
+ - [Code Generation](./src/codegen/README.md)
577
550
 
578
551
  ### API Reference
579
552
  - See module-specific README files linked above
@@ -584,4 +557,4 @@ MIT
584
557
 
585
558
  ---
586
559
 
587
- Part of the [SPFN Framework](https://github.com/spfn/spfn)
560
+ Part of the [Superfunction Framework](https://github.com/spfn/spfn)
@@ -0,0 +1,80 @@
1
+ import { MiddlewareHandler, Hono } from 'hono';
2
+
3
+ declare module 'hono' {
4
+ interface ContextVariableMap {
5
+ _skipMiddlewares?: string[];
6
+ }
7
+ }
8
+ /**
9
+ * AutoRouteLoader: Simplified File-based Routing System
10
+ *
11
+ * Features:
12
+ * - Auto-discovery: Scans routes directory and auto-registers
13
+ * - Dynamic routes: [id] → :id, [...slug] → *
14
+ * - Statistics: Route registration stats for dashboard
15
+ * - Grouping: Natural grouping by directory structure
16
+ */
17
+ type RouteInfo = {
18
+ path: string;
19
+ file: string;
20
+ meta?: {
21
+ description?: string;
22
+ tags?: string[];
23
+ auth?: boolean;
24
+ [key: string]: unknown;
25
+ };
26
+ priority: number;
27
+ };
28
+ type RouteStats = {
29
+ total: number;
30
+ byPriority: {
31
+ static: number;
32
+ dynamic: number;
33
+ catchAll: number;
34
+ };
35
+ byTag: Record<string, number>;
36
+ routes: RouteInfo[];
37
+ };
38
+ declare class AutoRouteLoader {
39
+ private routesDir;
40
+ private routes;
41
+ private readonly debug;
42
+ private readonly middlewares;
43
+ constructor(routesDir: string, debug?: boolean, middlewares?: Array<{
44
+ name: string;
45
+ handler: MiddlewareHandler;
46
+ }>);
47
+ load(app: Hono): Promise<RouteStats>;
48
+ /**
49
+ * Load routes from an external directory (e.g., from SPFN function packages)
50
+ * Reads package.json spfn.prefix and mounts routes under that prefix
51
+ *
52
+ * @param app - Hono app instance
53
+ * @param routesDir - Directory containing route handlers
54
+ * @param packageName - Name of the package (for logging)
55
+ * @param prefix - Optional prefix to mount routes under (from package.json spfn.prefix)
56
+ * @returns Route statistics
57
+ */
58
+ loadExternalRoutes(app: Hono, routesDir: string, packageName: string, prefix?: string): Promise<RouteStats>;
59
+ getStats(): RouteStats;
60
+ private scanFiles;
61
+ private isValidRouteFile;
62
+ private loadRoute;
63
+ private extractContractPaths;
64
+ private calculateContractPriority;
65
+ private validateModule;
66
+ private registerContractBasedMiddlewares;
67
+ private categorizeAndLogError;
68
+ private logStats;
69
+ }
70
+ declare function loadRoutes(app: Hono, options?: {
71
+ routesDir?: string;
72
+ debug?: boolean;
73
+ middlewares?: Array<{
74
+ name: string;
75
+ handler: MiddlewareHandler;
76
+ }>;
77
+ includeFunctionRoutes?: boolean;
78
+ }): Promise<RouteStats>;
79
+
80
+ export { AutoRouteLoader as A, type RouteInfo as R, type RouteStats as a, loadRoutes as l };