@spfn/core 0.1.0-alpha.8 → 0.1.0-alpha.81

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 +169 -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 +131 -92
  7. package/dist/client/index.js +93 -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 +1500 -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 +1486 -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 +2404 -2179
  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 +897 -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 +1238 -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 +2400 -2061
  41. package/dist/server/index.js.map +1 -1
  42. package/dist/types-DYueuoD6.d.ts +162 -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,41 @@ 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 { ConflictError } from '@spfn/core/errors';
208
+ import { findPostBySlug, createPost, findPublishedPosts } from '../../repositories/posts.repository';
310
209
  import { createPostContract, listPostsContract } from './contracts';
311
210
 
312
211
  const app = createApp();
313
212
 
314
213
  // POST /posts - Create new post (with transaction)
315
- app.bind(createPostContract, Transactional(), async (c) => {
214
+ app.bind(createPostContract, [Transactional()], async (c) => {
316
215
  const body = await c.data();
317
- const post = await createPost(body);
216
+
217
+ // Generate slug from title
218
+ const slug = body.title.toLowerCase()
219
+ .replace(/[^a-z0-9]+/g, '-')
220
+ .replace(/(^-|-$)/g, '');
221
+
222
+ // Check if slug exists
223
+ const existing = await findPostBySlug(slug);
224
+ if (existing) {
225
+ throw new ConflictError('Post with this title already exists', { slug });
226
+ }
227
+
228
+ // Create post
229
+ const post = await createPost({
230
+ ...body,
231
+ slug,
232
+ status: 'draft'
233
+ });
234
+
318
235
  // ✅ Auto-commit on success, auto-rollback on error
319
236
  return c.json(post, 201);
320
237
  });
321
238
 
322
239
  // GET /posts - List published posts (no transaction needed)
323
240
  app.bind(listPostsContract, async (c) => {
324
- const posts = await getPublishedPosts();
241
+ const posts = await findPublishedPosts();
325
242
  return c.json(posts);
326
243
  });
327
244
 
@@ -340,10 +257,10 @@ export default app;
340
257
 
341
258
  **✅ Reusability**
342
259
  - Services can be used by multiple routes
343
- - Repositories can be shared across services
260
+ - Data access functions can be shared across services
344
261
 
345
262
  **✅ Type Safety**
346
- - Types flow from Entity → Repository → Service → Route
263
+ - Types flow from Entity → Data Access → Service → Route
347
264
  - Full IDE autocomplete and error checking
348
265
 
349
266
  **✅ Maintainability**
@@ -355,7 +272,7 @@ export default app;
355
272
  | Layer | Responsibility | Examples |
356
273
  |-------|---------------|----------|
357
274
  | **Entity** | Define data structure | Schema, types, constraints |
358
- | **Repository** | Data access | CRUD, custom queries, joins |
275
+ | **Data Access** | Database operations | Helper functions, custom queries, joins |
359
276
  | **Service** | Business logic | Validation, orchestration, rules |
360
277
  | **Routes** | HTTP interface | Contracts, request handling |
361
278
 
@@ -366,21 +283,16 @@ export default app;
366
283
  - ✅ Export inferred types: `Post`, `NewPost`
367
284
  - ✅ Use TEXT with enum for status fields
368
285
 
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
286
+ **Data Access Layer:**
287
+ - ✅ Use helper functions for simple CRUD: `findOne()`, `create()`, etc.
288
+ - ✅ Create domain-specific wrappers in `src/server/repositories/*.repository.ts`
289
+ - ✅ Export functions (not classes): `export async function findPostBySlug()`
290
+ - ✅ Use object-based where for simple queries: `{ id: 1 }`
291
+ - ✅ Use SQL-based where for complex queries: `and(eq(...), gt(...))`
292
+ - ✅ Full TypeScript type inference from table schemas
381
293
 
382
294
  **Routes Layer:**
383
- - ✅ Keep handlers thin (delegate to services)
295
+ - ✅ Keep handlers thin (delegate to services/data access)
384
296
  - ✅ Define contracts with TypeBox
385
297
  - ✅ Use `Transactional()` middleware for write operations
386
298
  - ✅ Use `c.data()` for validated input
@@ -399,61 +311,75 @@ File-based routing with contract validation and type safety.
399
311
  - Type-safe request/response handling
400
312
  - Method-level middleware control (skip auth per HTTP method)
401
313
 
402
- ### 🗄️ Database & Repository
403
- Drizzle ORM integration with repository pattern and pagination.
314
+ ### 🗄️ Database
315
+ Drizzle ORM integration with type-safe helper functions and automatic transaction handling.
404
316
 
405
317
  **[→ Read Database Documentation](./src/db/README.md)**
406
318
 
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)
319
+ **Key Features:**
320
+ - Helper functions for type-safe CRUD operations
321
+ - Automatic transaction handling and read/write separation
322
+ - Schema helpers: `id()`, `timestamps()`, `foreignKey()`
323
+ - Hybrid where clause support (objects or SQL)
324
+ - **Function schema auto-discovery** (see below)
325
+
326
+ ### 📦 Function Schema Discovery
327
+ Automatic discovery of database schemas from Superfunction ecosystem functions.
411
328
 
412
- #### Choosing a Repository Pattern
329
+ **[→ Read Database Manager Documentation](./src/db/manager/README.md)**
413
330
 
414
- SPFN offers two repository patterns. Choose based on your needs:
331
+ **Key Features:**
332
+ - Zero-config schema discovery from `@spfn/*` functions
333
+ - Functions declare schemas via `package.json`
334
+ - No hard dependencies between functions
335
+ - Efficient scanning (direct dependencies only)
336
+ - Function-specific migration support
415
337
 
416
- **Global Singleton (`getRepository`)**
417
- ```typescript
418
- import { getRepository } from '@spfn/core/db';
338
+ **How it works:**
419
339
 
420
- const repo = getRepository(users);
340
+ Functions declare their schemas in `package.json`:
341
+ ```json
342
+ {
343
+ "name": "@spfn/cms",
344
+ "spfn": {
345
+ "schemas": ["./dist/entities/*.js"],
346
+ "setupMessage": "📚 Setup guide..."
347
+ }
348
+ }
421
349
  ```
422
350
 
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
351
+ Superfunction automatically discovers and merges these schemas during migration generation:
430
352
  ```typescript
431
- import { getScopedRepository, RepositoryScope } from '@spfn/core/db';
353
+ import { getDrizzleConfig } from '@spfn/core'
432
354
 
433
- // Add middleware once (in server setup)
434
- app.use(RepositoryScope());
435
-
436
- // Use in routes/services
437
- const repo = getScopedRepository(users);
355
+ // Auto-discovers all function schemas
356
+ const config = getDrizzleConfig()
438
357
  ```
439
358
 
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
359
+ **Install functions with automatic DB setup:**
360
+ ```bash
361
+ pnpm spfn add @spfn/cms
362
+ #Installs function
363
+ # Generates migrations
364
+ # ✅ Applies migrations
365
+ # ✅ Shows setup guide
366
+ ```
445
367
 
446
- **Comparison:**
368
+ **Create your own Superfunction packages:**
369
+ ```typescript
370
+ // 1. Define entities
371
+ export const myTable = pgTable('my_table', { ... })
447
372
 
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 |
373
+ // 2. Add to package.json
374
+ {
375
+ "spfn": {
376
+ "schemas": ["./dist/entities/*.js"]
377
+ }
378
+ }
455
379
 
456
- [→ See full request-scoped documentation](./src/db/repository/request-scope.ts)
380
+ // 3. Users install with one command
381
+ // pnpm spfn add @yourcompany/spfn-plugin
382
+ ```
457
383
 
458
384
  ### 🔄 Transactions
459
385
  Automatic transaction management with async context propagation.
@@ -485,6 +411,30 @@ Server configuration and lifecycle management.
485
411
 
486
412
  **[→ Read Server Documentation](./src/server/README.md)**
487
413
 
414
+ ### 📝 Logger
415
+ High-performance logging with multiple transports, sensitive data masking, and automatic validation.
416
+
417
+ **[→ Read Logger Documentation](./src/logger/README.md)**
418
+
419
+ **Key Features:**
420
+ - Adapter pattern (Pino for production, custom for full control)
421
+ - Sensitive data masking (passwords, tokens, API keys)
422
+ - File rotation (date and size-based) with automatic cleanup
423
+ - Configuration validation with clear error messages
424
+ - Multiple transports (Console, File, Slack, Email)
425
+
426
+ ### ⚙️ Code Generation
427
+ Automatic code generation with pluggable generators and centralized file watching.
428
+
429
+ **[→ Read Codegen Documentation](./src/codegen/README.md)**
430
+
431
+ **Key Features:**
432
+ - Orchestrator pattern for managing multiple generators
433
+ - Built-in contract generator for type-safe API clients
434
+ - Configuration-based setup (`.spfnrc.json` or `package.json`)
435
+ - Watch mode integrated into `spfn dev`
436
+ - Extensible with custom generators
437
+
488
438
  ## Module Exports
489
439
 
490
440
  ### Main Export
@@ -501,11 +451,17 @@ import type { RouteContext, RouteContract } from '@spfn/core/route';
501
451
  ### Database
502
452
  ```typescript
503
453
  import {
504
- getDb,
505
- Repository,
506
- getRepository
454
+ getDatabase,
455
+ findOne,
456
+ findMany,
457
+ create,
458
+ createMany,
459
+ updateOne,
460
+ updateMany,
461
+ deleteOne,
462
+ deleteMany,
463
+ count
507
464
  } from '@spfn/core/db';
508
- import type { Pageable, Page } from '@spfn/core/db';
509
465
  ```
510
466
 
511
467
  ### Transactions
@@ -522,6 +478,11 @@ import {
522
478
  import { initRedis, getRedis, getRedisRead } from '@spfn/core';
523
479
  ```
524
480
 
481
+ ### Logger
482
+ ```typescript
483
+ import { logger } from '@spfn/core';
484
+ ```
485
+
525
486
  ### Client (for frontend)
526
487
  ```typescript
527
488
  import { ContractClient, createClient } from '@spfn/core/client';
@@ -545,6 +506,17 @@ REDIS_READ_URL=redis://replica:6379
545
506
  PORT=8790
546
507
  HOST=localhost
547
508
  NODE_ENV=development
509
+
510
+ # Server Timeouts (optional, in milliseconds)
511
+ SERVER_TIMEOUT=120000 # Request timeout (default: 120000)
512
+ SERVER_KEEPALIVE_TIMEOUT=65000 # Keep-alive timeout (default: 65000)
513
+ SERVER_HEADERS_TIMEOUT=60000 # Headers timeout (default: 60000)
514
+ SHUTDOWN_TIMEOUT=30000 # Graceful shutdown timeout (default: 30000)
515
+
516
+ # Logger (optional)
517
+ LOGGER_ADAPTER=pino # pino | custom (default: pino)
518
+ LOGGER_FILE_ENABLED=true # Enable file logging (production only)
519
+ LOG_DIR=/var/log/myapp # Log directory (required when file logging enabled)
548
520
  ```
549
521
 
550
522
  ## Requirements
@@ -568,12 +540,14 @@ npm test -- --coverage # With coverage
568
540
 
569
541
  ### Guides
570
542
  - [File-based Routing](./src/route/README.md)
571
- - [Database & Repository](./src/db/README.md)
543
+ - [Database & Helper Functions](./src/db/README.md)
572
544
  - [Transaction Management](./src/db/docs/transactions.md)
573
545
  - [Redis Cache](./src/cache/README.md)
574
546
  - [Error Handling](./src/errors/README.md)
575
547
  - [Middleware](./src/middleware/README.md)
576
548
  - [Server Configuration](./src/server/README.md)
549
+ - [Logger](./src/logger/README.md)
550
+ - [Code Generation](./src/codegen/README.md)
577
551
 
578
552
  ### API Reference
579
553
  - See module-specific README files linked above
@@ -584,4 +558,4 @@ MIT
584
558
 
585
559
  ---
586
560
 
587
- Part of the [SPFN Framework](https://github.com/spfn/spfn)
561
+ 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 };