@beeblock/svelar 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +110 -0
- package/dist/actions/index.d.ts +101 -0
- package/dist/actions/index.js +1 -0
- package/dist/api-keys/index.d.ts +58 -0
- package/dist/api-keys/index.js +1 -0
- package/dist/audit/index.d.ts +52 -0
- package/dist/audit/index.js +1 -0
- package/dist/auth/Auth.d.ts +283 -0
- package/dist/auth/Gate.d.ts +166 -0
- package/dist/auth/index.d.ts +2 -0
- package/dist/auth/index.js +80 -0
- package/dist/broadcasting/client.d.ts +195 -0
- package/dist/broadcasting/client.js +1 -0
- package/dist/broadcasting/index.d.ts +318 -0
- package/dist/broadcasting/index.js +20 -0
- package/dist/cache/index.d.ts +77 -0
- package/dist/cache/index.js +1 -0
- package/dist/cli/Cli.d.ts +23 -0
- package/dist/cli/Command.d.ts +36 -0
- package/dist/cli/bin.d.ts +8 -0
- package/dist/cli/bin.js +5856 -0
- package/dist/cli/commands/KeyGenerateCommand.d.ts +16 -0
- package/dist/cli/commands/MakeActionCommand.d.ts +15 -0
- package/dist/cli/commands/MakeBroadcastingCommand.d.ts +29 -0
- package/dist/cli/commands/MakeChannelCommand.d.ts +18 -0
- package/dist/cli/commands/MakeCommandCommand.d.ts +16 -0
- package/dist/cli/commands/MakeConfigCommand.d.ts +13 -0
- package/dist/cli/commands/MakeControllerCommand.d.ts +28 -0
- package/dist/cli/commands/MakeDashboardCommand.d.ts +34 -0
- package/dist/cli/commands/MakeDockerCommand.d.ts +32 -0
- package/dist/cli/commands/MakeEventCommand.d.ts +11 -0
- package/dist/cli/commands/MakeJobCommand.d.ts +11 -0
- package/dist/cli/commands/MakeListenerCommand.d.ts +16 -0
- package/dist/cli/commands/MakeMiddlewareCommand.d.ts +11 -0
- package/dist/cli/commands/MakeMigrationCommand.d.ts +17 -0
- package/dist/cli/commands/MakeModelCommand.d.ts +25 -0
- package/dist/cli/commands/MakeObserverCommand.d.ts +23 -0
- package/dist/cli/commands/MakePluginCommand.d.ts +11 -0
- package/dist/cli/commands/MakeProviderCommand.d.ts +11 -0
- package/dist/cli/commands/MakeRepositoryCommand.d.ts +22 -0
- package/dist/cli/commands/MakeRequestCommand.d.ts +15 -0
- package/dist/cli/commands/MakeResourceCommand.d.ts +30 -0
- package/dist/cli/commands/MakeRouteCommand.d.ts +42 -0
- package/dist/cli/commands/MakeSchemaCommand.d.ts +20 -0
- package/dist/cli/commands/MakeSeederCommand.d.ts +11 -0
- package/dist/cli/commands/MakeServiceCommand.d.ts +28 -0
- package/dist/cli/commands/MakeTaskCommand.d.ts +12 -0
- package/dist/cli/commands/MigrateCommand.d.ts +26 -0
- package/dist/cli/commands/NewCommand.d.ts +21 -0
- package/dist/cli/commands/NewCommandTemplates.d.ts +123 -0
- package/dist/cli/commands/PluginInstallCommand.d.ts +16 -0
- package/dist/cli/commands/PluginListCommand.d.ts +11 -0
- package/dist/cli/commands/PluginPublishCommand.d.ts +22 -0
- package/dist/cli/commands/QueueFailedCommand.d.ts +9 -0
- package/dist/cli/commands/QueueFlushCommand.d.ts +9 -0
- package/dist/cli/commands/QueueRetryCommand.d.ts +16 -0
- package/dist/cli/commands/QueueWorkCommand.d.ts +25 -0
- package/dist/cli/commands/RoutesListCommand.d.ts +30 -0
- package/dist/cli/commands/ScheduleRunCommand.d.ts +15 -0
- package/dist/cli/commands/SeedCommand.d.ts +14 -0
- package/dist/cli/commands/TinkerCommand.d.ts +10 -0
- package/dist/cli/index.d.ts +36 -0
- package/dist/cli/index.js +1973 -0
- package/dist/cli/ts-resolve-hook.mjs +74 -0
- package/dist/cli/ts-resolver.mjs +8 -0
- package/dist/config/Config.d.ts +65 -0
- package/dist/config/index.d.ts +1 -0
- package/dist/config/index.js +1 -0
- package/dist/container/Application.d.ts +33 -0
- package/dist/container/Container.d.ts +70 -0
- package/dist/container/ServiceProvider.d.ts +21 -0
- package/dist/container/index.d.ts +3 -0
- package/dist/container/index.js +1 -0
- package/dist/dashboard/index.d.ts +123 -0
- package/dist/dashboard/index.js +5 -0
- package/dist/database/Connection.d.ts +80 -0
- package/dist/database/Migration.d.ts +76 -0
- package/dist/database/SchemaBuilder.d.ts +91 -0
- package/dist/database/Seeder.d.ts +9 -0
- package/dist/database/index.d.ts +4 -0
- package/dist/database/index.js +4 -0
- package/dist/email-templates/index.d.ts +51 -0
- package/dist/email-templates/index.js +57 -0
- package/dist/errors/Handler.d.ts +100 -0
- package/dist/errors/index.d.ts +1 -0
- package/dist/errors/index.js +5 -0
- package/dist/events/EventServiceProvider.d.ts +82 -0
- package/dist/events/Listener.d.ts +28 -0
- package/dist/events/index.d.ts +80 -0
- package/dist/events/index.js +1 -0
- package/dist/excel/index.d.ts +154 -0
- package/dist/excel/index.js +1 -0
- package/dist/feature-flags/index.d.ts +158 -0
- package/dist/feature-flags/index.js +59 -0
- package/dist/forms/index.d.ts +81 -0
- package/dist/forms/index.js +1 -0
- package/dist/hashing/Hash.d.ts +51 -0
- package/dist/hashing/index.d.ts +1 -0
- package/dist/hashing/index.js +1 -0
- package/dist/hooks/index.d.ts +135 -0
- package/dist/hooks/index.js +5 -0
- package/dist/http/index.d.ts +201 -0
- package/dist/http/index.js +2 -0
- package/dist/i18n/index.d.ts +81 -0
- package/dist/i18n/index.js +1 -0
- package/dist/index.d.ts +54 -0
- package/dist/index.js +127 -0
- package/dist/logging/LogViewer.d.ts +95 -0
- package/dist/logging/LogViewer.js +1 -0
- package/dist/logging/index.d.ts +83 -0
- package/dist/logging/index.js +3 -0
- package/dist/mail/index.d.ts +149 -0
- package/dist/mail/index.js +1 -0
- package/dist/middleware/Middleware.d.ts +208 -0
- package/dist/middleware/index.d.ts +1 -0
- package/dist/middleware/index.js +1 -0
- package/dist/notifications/index.d.ts +85 -0
- package/dist/notifications/index.js +2 -0
- package/dist/orm/Model.d.ts +123 -0
- package/dist/orm/Observer.d.ts +34 -0
- package/dist/orm/QueryBuilder.d.ts +119 -0
- package/dist/orm/Relationship.d.ts +58 -0
- package/dist/orm/index.d.ts +4 -0
- package/dist/orm/index.js +1 -0
- package/dist/pagination/index.d.ts +8 -0
- package/dist/pagination/index.js +0 -0
- package/dist/pdf/GeneratePdfJob.d.ts +99 -0
- package/dist/pdf/GeneratePdfJob.js +41 -0
- package/dist/pdf/index.d.ts +328 -0
- package/dist/pdf/index.js +41 -0
- package/dist/permissions/index.d.ts +161 -0
- package/dist/permissions/index.js +60 -0
- package/dist/plugins/BootstrapPlugins.d.ts +11 -0
- package/dist/plugins/PluginInstaller.d.ts +30 -0
- package/dist/plugins/PluginInstaller.js +1 -0
- package/dist/plugins/PluginPublisher.d.ts +32 -0
- package/dist/plugins/PluginPublisher.js +1 -0
- package/dist/plugins/PluginRegistry.d.ts +55 -0
- package/dist/plugins/PluginRegistry.js +1 -0
- package/dist/plugins/index.d.ts +206 -0
- package/dist/plugins/index.js +1 -0
- package/dist/queue/JobMonitor.d.ts +109 -0
- package/dist/queue/JobMonitor.js +5 -0
- package/dist/queue/index.d.ts +279 -0
- package/dist/queue/index.js +5 -0
- package/dist/repositories/index.d.ts +147 -0
- package/dist/repositories/index.js +1 -0
- package/dist/routing/Controller.d.ts +115 -0
- package/dist/routing/FormRequest.d.ts +94 -0
- package/dist/routing/Resource.d.ts +213 -0
- package/dist/routing/Response.d.ts +138 -0
- package/dist/routing/index.d.ts +4 -0
- package/dist/routing/index.js +5 -0
- package/dist/scheduler/ScheduleMonitor.d.ts +141 -0
- package/dist/scheduler/ScheduleMonitor.js +1 -0
- package/dist/scheduler/SchedulerLock.d.ts +33 -0
- package/dist/scheduler/index.d.ts +208 -0
- package/dist/scheduler/index.js +34 -0
- package/dist/services/index.d.ts +79 -0
- package/dist/services/index.js +1 -0
- package/dist/session/Session.d.ts +166 -0
- package/dist/session/index.d.ts +1 -0
- package/dist/session/index.js +16 -0
- package/dist/storage/index.d.ts +154 -0
- package/dist/storage/index.js +1 -0
- package/dist/support/Pipeline.d.ts +65 -0
- package/dist/support/date.d.ts +136 -0
- package/dist/support/date.js +1 -0
- package/dist/support/index.d.ts +8 -0
- package/dist/support/index.js +1 -0
- package/dist/support/singleton.d.ts +10 -0
- package/dist/support/uuid.d.ts +40 -0
- package/dist/teams/index.d.ts +91 -0
- package/dist/teams/index.js +78 -0
- package/dist/uploads/index.d.ts +63 -0
- package/dist/uploads/index.js +2 -0
- package/dist/validation/index.d.ts +46 -0
- package/dist/validation/index.js +1 -0
- package/dist/webhooks/index.d.ts +66 -0
- package/dist/webhooks/index.js +1 -0
- package/package.json +338 -0
- package/src/i18n/LanguageSwitcher.svelte +47 -0
- package/src/i18n/index.ts +113 -0
- package/src/ui/Alert.svelte +22 -0
- package/src/ui/Avatar.svelte +18 -0
- package/src/ui/AvatarFallback.svelte +18 -0
- package/src/ui/AvatarImage.svelte +12 -0
- package/src/ui/Badge.svelte +27 -0
- package/src/ui/Button.svelte +51 -0
- package/src/ui/Card.svelte +15 -0
- package/src/ui/CardContent.svelte +15 -0
- package/src/ui/CardDescription.svelte +15 -0
- package/src/ui/CardFooter.svelte +15 -0
- package/src/ui/CardHeader.svelte +15 -0
- package/src/ui/CardTitle.svelte +15 -0
- package/src/ui/Icon.svelte +81 -0
- package/src/ui/Input.svelte +40 -0
- package/src/ui/Label.svelte +20 -0
- package/src/ui/Separator.svelte +10 -0
- package/src/ui/Tabs.svelte +23 -0
- package/src/ui/TabsContent.svelte +27 -0
- package/src/ui/TabsList.svelte +19 -0
- package/src/ui/TabsTrigger.svelte +28 -0
- package/src/ui/Toaster.svelte +279 -0
- package/src/ui/index.ts +31 -0
- package/src/ui/toast.ts +212 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Raoni
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/beeblock/svelar/main/docs/assets/svelar-full-logo.svg" alt="Svelar" width="180" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h1 align="center">Svelar</h1>
|
|
6
|
+
|
|
7
|
+
Laravel-inspired framework on top of SvelteKit 2. Brings the developer experience of Laravel — routing, middleware, Eloquent-style ORM, service container, auth, sessions, caching, queues, mail, broadcasting, and more — into the SvelteKit ecosystem.
|
|
8
|
+
|
|
9
|
+
**Batteries included.** Authentication, dashboard, admin panel, email templates, API keys, team management, file uploads, and more — all out of the box so you can focus on building your core business features.
|
|
10
|
+
|
|
11
|
+
## Documentation
|
|
12
|
+
|
|
13
|
+
Full documentation is available at **[svelar.dev](https://svelar.dev)**.
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# Scaffold a new project
|
|
19
|
+
npx svelar new my-app
|
|
20
|
+
cd my-app
|
|
21
|
+
npm install
|
|
22
|
+
npx svelar migrate
|
|
23
|
+
npm run dev
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Or add to an existing SvelteKit project:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm install @beeblock/svelar
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
See the [Getting Started guide](https://svelar.dev/docs/getting-started) for a complete walkthrough.
|
|
33
|
+
|
|
34
|
+
## Features
|
|
35
|
+
|
|
36
|
+
| Module | Import | Description |
|
|
37
|
+
|--------|--------|-------------|
|
|
38
|
+
| **ORM** | `svelar/orm` | Eloquent-style models with relationships, soft deletes, scopes, eager loading |
|
|
39
|
+
| **Database** | `svelar/database` | Schema builder, migrations, seeders (SQLite, PostgreSQL, MySQL) |
|
|
40
|
+
| **Auth** | `svelar/auth` | Session, JWT (with refresh tokens), API tokens, password reset, email verification, OTP login |
|
|
41
|
+
| **Middleware** | `svelar/middleware` | CORS, CSRF, rate limiting, origin validation, request signatures |
|
|
42
|
+
| **Routing** | `svelar/routing` | Controllers, form requests, API resources, response helpers |
|
|
43
|
+
| **Validation** | `svelar/validation` | Zod-based validation with FormRequest DTOs |
|
|
44
|
+
| **Session** | `svelar/session` | Cookie, memory, Redis, and database session stores |
|
|
45
|
+
| **Hashing** | `svelar/hashing` | scrypt (zero-dep), bcrypt, argon2 |
|
|
46
|
+
| **Queue** | `svelar/queue` | Job dispatching with sync, memory, and Redis (BullMQ) drivers |
|
|
47
|
+
| **Scheduler** | `svelar/scheduler` | Cron-based task scheduling with distributed locking and DB history |
|
|
48
|
+
| **Events** | `svelar/events` | Typed event dispatcher with listeners and subscribers |
|
|
49
|
+
| **Broadcasting** | `svelar/broadcasting` | Server-Sent Events and Pusher/Soketi WebSocket support |
|
|
50
|
+
| **Cache** | `svelar/cache` | Memory and Redis cache with TTL and remember pattern |
|
|
51
|
+
| **Storage** | `svelar/storage` | Local and S3-compatible file storage |
|
|
52
|
+
| **Mail** | `svelar/mail` | SMTP, Postmark, Resend, log, and null transports with Mailable classes |
|
|
53
|
+
| **Excel** | `svelar/excel` | Import/export Excel files with streaming support for large datasets |
|
|
54
|
+
| **Notifications** | `svelar/notifications` | Multi-channel notifications (mail, database, broadcast) |
|
|
55
|
+
| **Logging** | `svelar/logging` | File-based logger with levels and rotation |
|
|
56
|
+
| **HTTP Client** | `svelar/http` | Client-side CSRF fetch + server-side fluent HTTP client for third-party APIs |
|
|
57
|
+
| **Permissions** | `svelar/permissions` | Role-based access control with permissions and gates |
|
|
58
|
+
| **i18n** | `svelar/i18n` | Paraglide-js integration with language switcher |
|
|
59
|
+
| **Forms** | `svelar/forms` | SvelteKit Superforms integration helpers |
|
|
60
|
+
| **UI Components** | `svelar/ui` | Button, Card, Input, Alert, Badge, Avatar, Tabs, Icon, Toaster |
|
|
61
|
+
| **Hooks** | `svelar/hooks` | One-line SvelteKit hooks setup with sensible defaults |
|
|
62
|
+
| **Container** | `svelar/container` | IoC container with singleton/transient bindings |
|
|
63
|
+
| **Plugins** | `svelar/plugins` | Plugin discovery, publishing, and CLI management |
|
|
64
|
+
| **PDF** | `svelar/pdf` | PDF generation with PDFKit (default) and Gotenberg drivers |
|
|
65
|
+
| **Feature Flags** | `svelar/feature-flags` | Per-user, per-team, and percentage rollout feature flags |
|
|
66
|
+
| **Audit** | `svelar/audit` | Model change tracking |
|
|
67
|
+
| **API Keys** | `svelar/api-keys` | API key generation, validation, and management |
|
|
68
|
+
| **Webhooks** | `svelar/webhooks` | Webhook dispatch and signature verification |
|
|
69
|
+
| **Teams** | `svelar/teams` | Multi-tenant team management with database-backed storage |
|
|
70
|
+
| **Email Templates** | `svelar/email-templates` | Database-stored templates with variable interpolation |
|
|
71
|
+
| **Uploads** | `svelar/uploads` | File upload handling with validation (local + S3) |
|
|
72
|
+
| **Dashboard** | `svelar/dashboard` | Admin dashboard with job/scheduler monitoring and log viewer |
|
|
73
|
+
|
|
74
|
+
## CLI
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
npx svelar new my-app # scaffold a new project
|
|
78
|
+
npx svelar make:model Post -m -c # model + migration + controller
|
|
79
|
+
npx svelar make:service PaymentService # service class
|
|
80
|
+
npx svelar make:job SendEmail # queue job
|
|
81
|
+
npx svelar make:task CleanupTokens # scheduled task
|
|
82
|
+
npx svelar migrate # run migrations
|
|
83
|
+
npx svelar schedule:run # start the scheduler
|
|
84
|
+
npx svelar queue:work # process queue jobs
|
|
85
|
+
npx svelar tinker # interactive REPL
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
30+ code generation commands available. Run `npx svelar` to see all commands.
|
|
89
|
+
|
|
90
|
+
## Database Support
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
# SQLite (recommended for development)
|
|
94
|
+
npm install better-sqlite3
|
|
95
|
+
|
|
96
|
+
# PostgreSQL
|
|
97
|
+
npm install postgres
|
|
98
|
+
|
|
99
|
+
# MySQL
|
|
100
|
+
npm install mysql2
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Requirements
|
|
104
|
+
|
|
105
|
+
- Node.js >= 20
|
|
106
|
+
- SvelteKit >= 2.0
|
|
107
|
+
|
|
108
|
+
## License
|
|
109
|
+
|
|
110
|
+
MIT
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Svelar Actions
|
|
3
|
+
*
|
|
4
|
+
* Single-responsibility action classes for complex business operations.
|
|
5
|
+
* Each action does ONE thing, following the Command pattern.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* import { Action } from 'svelar/actions';
|
|
10
|
+
*
|
|
11
|
+
* class RegisterUserAction extends Action<RegisterDTO, User> {
|
|
12
|
+
* constructor(
|
|
13
|
+
* private userRepo: UserRepository,
|
|
14
|
+
* private mailer: Mailer,
|
|
15
|
+
* ) {
|
|
16
|
+
* super();
|
|
17
|
+
* }
|
|
18
|
+
*
|
|
19
|
+
* async execute(data: RegisterDTO): Promise<User> {
|
|
20
|
+
* const user = await this.userRepo.create({
|
|
21
|
+
* name: data.name,
|
|
22
|
+
* email: data.email,
|
|
23
|
+
* password: await Hash.make(data.password),
|
|
24
|
+
* });
|
|
25
|
+
*
|
|
26
|
+
* await this.mailer.send({
|
|
27
|
+
* to: user.email,
|
|
28
|
+
* template: 'welcome',
|
|
29
|
+
* });
|
|
30
|
+
*
|
|
31
|
+
* return user;
|
|
32
|
+
* }
|
|
33
|
+
* }
|
|
34
|
+
*
|
|
35
|
+
* // Usage
|
|
36
|
+
* const action = new RegisterUserAction(userRepo, mailer);
|
|
37
|
+
* const user = await action.run(dto);
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export interface ActionResult<T = any> {
|
|
41
|
+
success: boolean;
|
|
42
|
+
data?: T;
|
|
43
|
+
error?: string;
|
|
44
|
+
}
|
|
45
|
+
export type ActionMiddleware<TInput = any> = (input: TInput, next: (input: TInput) => Promise<any>) => Promise<any>;
|
|
46
|
+
export declare abstract class Action<TInput = any, TOutput = any> {
|
|
47
|
+
private beforeHooks;
|
|
48
|
+
private afterHooks;
|
|
49
|
+
private middlewarePipeline;
|
|
50
|
+
/**
|
|
51
|
+
* Execute the action logic. Override this in subclasses.
|
|
52
|
+
*/
|
|
53
|
+
abstract execute(input: TInput): Promise<TOutput>;
|
|
54
|
+
/**
|
|
55
|
+
* Run the action with before/after hooks and middleware pipeline
|
|
56
|
+
*/
|
|
57
|
+
run(input: TInput): Promise<TOutput>;
|
|
58
|
+
/**
|
|
59
|
+
* Run the action and catch errors, returning an ActionResult
|
|
60
|
+
*/
|
|
61
|
+
runSafe(input: TInput): Promise<ActionResult<TOutput>>;
|
|
62
|
+
/**
|
|
63
|
+
* Register a before hook
|
|
64
|
+
*/
|
|
65
|
+
before(hook: (input: TInput) => Promise<void> | void): this;
|
|
66
|
+
/**
|
|
67
|
+
* Register an after hook
|
|
68
|
+
*/
|
|
69
|
+
after(hook: (input: TInput, output: TOutput) => Promise<void> | void): this;
|
|
70
|
+
/**
|
|
71
|
+
* Add middleware to the action pipeline
|
|
72
|
+
*/
|
|
73
|
+
through(middleware: ActionMiddleware<TInput>): this;
|
|
74
|
+
/**
|
|
75
|
+
* Run the action through the middleware pipeline
|
|
76
|
+
*/
|
|
77
|
+
private runWithMiddleware;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* An action that can be chained into a pipeline of actions.
|
|
81
|
+
* The output of one becomes the input of the next.
|
|
82
|
+
*/
|
|
83
|
+
export declare abstract class ChainableAction<TInput = any, TOutput = any> extends Action<TInput, TOutput> {
|
|
84
|
+
/**
|
|
85
|
+
* Chain another action after this one
|
|
86
|
+
*/
|
|
87
|
+
then<TNext>(nextAction: ChainableAction<TOutput, TNext>): ChainableAction<TInput, TNext>;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Create an action from a simple function, without creating a class.
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* ```ts
|
|
94
|
+
* const sendEmail = inlineAction(async (data: { to: string; body: string }) => {
|
|
95
|
+
* await mailer.send(data);
|
|
96
|
+
* });
|
|
97
|
+
*
|
|
98
|
+
* await sendEmail.run({ to: 'user@example.com', body: 'Hello!' });
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
export declare function inlineAction<TInput = any, TOutput = any>(fn: (input: TInput) => Promise<TOutput>): Action<TInput, TOutput>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var i=class{beforeHooks=[];afterHooks=[];middlewarePipeline=[];async run(t){for(let u of this.beforeHooks)await u(t);let e;this.middlewarePipeline.length>0?e=await this.runWithMiddleware(t):e=await this.execute(t);for(let u of this.afterHooks)await u(t,e);return e}async runSafe(t){try{return{success:!0,data:await this.run(t)}}catch(e){return{success:!1,error:e.message}}}before(t){return this.beforeHooks.push(t),this}after(t){return this.afterHooks.push(t),this}through(t){return this.middlewarePipeline.push(t),this}async runWithMiddleware(t){let e=0,u=this.middlewarePipeline,o=async a=>{if(e>=u.length)return this.execute(a);let p=u[e++];return p(a,o)};return o(t)}},r=class extends i{then(t){return new s(this,t)}},s=class extends r{constructor(e,u){super();this.first=e;this.second=u}async execute(e){let u=await this.first.run(e);return this.second.run(u)}};function T(n){class t extends i{async execute(u){return n(u)}}return new t}export{i as Action,r as ChainableAction,T as inlineAction};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Svelar API Key Management
|
|
3
|
+
* Generate, validate, and revoke API keys for programmatic access.
|
|
4
|
+
*/
|
|
5
|
+
export interface ApiKeyRecord {
|
|
6
|
+
id: string;
|
|
7
|
+
userId: string | number;
|
|
8
|
+
name: string;
|
|
9
|
+
key: string;
|
|
10
|
+
prefix: string;
|
|
11
|
+
lastUsedAt?: number;
|
|
12
|
+
expiresAt?: number;
|
|
13
|
+
permissions?: string[];
|
|
14
|
+
metadata?: Record<string, any>;
|
|
15
|
+
createdAt: number;
|
|
16
|
+
revokedAt?: number;
|
|
17
|
+
}
|
|
18
|
+
export interface CreateKeyOptions {
|
|
19
|
+
name: string;
|
|
20
|
+
userId: string | number;
|
|
21
|
+
prefix?: string;
|
|
22
|
+
permissions?: string[];
|
|
23
|
+
expiresIn?: number;
|
|
24
|
+
metadata?: Record<string, any>;
|
|
25
|
+
}
|
|
26
|
+
export interface ApiKeyConfig {
|
|
27
|
+
driver: 'database' | 'memory';
|
|
28
|
+
table?: string;
|
|
29
|
+
prefix?: string;
|
|
30
|
+
hashAlgorithm?: string;
|
|
31
|
+
}
|
|
32
|
+
declare class ApiKeyManager {
|
|
33
|
+
private config;
|
|
34
|
+
private keys;
|
|
35
|
+
configure(config: ApiKeyConfig): void;
|
|
36
|
+
private hashKey;
|
|
37
|
+
private generateRandomKey;
|
|
38
|
+
/** Generate a new API key. Returns the PLAIN TEXT key (only shown once!) */
|
|
39
|
+
create(options: CreateKeyOptions): Promise<{
|
|
40
|
+
record: ApiKeyRecord;
|
|
41
|
+
plainTextKey: string;
|
|
42
|
+
}>;
|
|
43
|
+
/** Validate a key and return the associated record */
|
|
44
|
+
validate(plainTextKey: string): Promise<ApiKeyRecord | null>;
|
|
45
|
+
/** Revoke a key */
|
|
46
|
+
revoke(keyId: string): Promise<boolean>;
|
|
47
|
+
/** List keys for a user */
|
|
48
|
+
listForUser(userId: string | number): Promise<ApiKeyRecord[]>;
|
|
49
|
+
/** Rotate a key (revoke old, create new with same settings) */
|
|
50
|
+
rotate(keyId: string): Promise<{
|
|
51
|
+
record: ApiKeyRecord;
|
|
52
|
+
plainTextKey: string;
|
|
53
|
+
} | null>;
|
|
54
|
+
/** Check if a key has a specific permission */
|
|
55
|
+
hasPermission(plainTextKey: string, permission: string): Promise<boolean>;
|
|
56
|
+
}
|
|
57
|
+
export declare const ApiKeys: ApiKeyManager;
|
|
58
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var w=Object.defineProperty;var h=(c,e)=>()=>(c&&(e=c(c=0)),e);var C=(c,e)=>{for(var t in e)w(c,t,{get:e[t],enumerable:!0})};function u(c,e){let t=Symbol.for(c),r=globalThis;return r[t]||(r[t]=e()),r[t]}var p=h(()=>{"use strict"});var f={};C(f,{Connection:()=>b});var m,b,y=h(()=>{"use strict";p();m=class{connections=new Map;config=null;defaultName="default";configure(e){this.config=e,this.defaultName=e.default}async connection(e){let t=e??this.defaultName;if(this.connections.has(t))return this.connections.get(t).drizzle;if(!this.config)throw new Error("Database not configured. Call Connection.configure() first, or register DatabaseServiceProvider.");let r=this.config.connections[t];if(!r)throw new Error(`Database connection "${t}" is not defined in configuration.`);let i=await this.createConnection(r);return this.connections.set(t,i),i.drizzle}async rawClient(e){let t=e??this.defaultName;return await this.connection(t),this.connections.get(t).rawClient}async raw(e,t=[],r){let i=await this.connection(r),n=this.getConfig(r);switch(n.driver){case"sqlite":{let s=await this.rawClient(r),l=t.map(o=>typeof o=="boolean"?o?1:0:o instanceof Date?o.toISOString():o),a=s.prepare(e),d=e.trimStart().toUpperCase();return d.startsWith("SELECT")||d.startsWith("PRAGMA")||d.startsWith("WITH")?a.all(...l):a.run(...l)}case"postgres":return(await this.rawClient(r))(e,...t);case"mysql":{let s=await this.rawClient(r),[l]=await s.execute(e,t);return l}default:throw new Error(`Unsupported driver: ${n.driver}`)}}getDriver(e){return this.getConfig(e).driver}getConfig(e){let t=e??this.defaultName;if(!this.config)throw new Error("Database not configured.");let r=this.config.connections[t];if(!r)throw new Error(`Database connection "${t}" is not defined.`);return r}async disconnect(e){if(e){let t=this.connections.get(e);t&&(await this.closeConnection(t),this.connections.delete(e))}else{for(let[t,r]of this.connections)await this.closeConnection(r);this.connections.clear()}}isConnected(e){return this.connections.has(e??this.defaultName)}async transaction(e,t){let r=this.getConfig(t),i=await this.rawClient(t);switch(r.driver){case"sqlite":{i.exec("BEGIN");try{let n=await e();return i.exec("COMMIT"),n}catch(n){throw i.exec("ROLLBACK"),n}}case"postgres":{await i`BEGIN`;try{let n=await e();return await i`COMMIT`,n}catch(n){throw await i`ROLLBACK`,n}}case"mysql":{let n=await i.getConnection();await n.beginTransaction();try{let s=await e();return await n.commit(),n.release(),s}catch(s){throw await n.rollback(),n.release(),s}}default:throw new Error(`Unsupported driver: ${r.driver}`)}}async createConnection(e){switch(e.driver){case"sqlite":return this.createSQLiteConnection(e);case"postgres":return this.createPostgresConnection(e);case"mysql":return this.createMySQLConnection(e);default:throw new Error(`Unsupported database driver: ${e.driver}`)}}async createSQLiteConnection(e){let t=e.filename??e.database??":memory:";try{let r=(await import("better-sqlite3")).default,{drizzle:i}=await import("drizzle-orm/better-sqlite3"),n=new r(t);return n.pragma("journal_mode = WAL"),n.pragma("foreign_keys = ON"),{drizzle:i(n),config:e,rawClient:n}}catch(r){let i;try{i=(await new Function("mod","return import(mod)")("node:sqlite")).DatabaseSync}catch{throw new Error(`No SQLite driver available. Install better-sqlite3 (npm install better-sqlite3) or use Node.js v22+ which includes built-in SQLite support. Original error: ${r instanceof Error?r.message:String(r)}`)}let n=new i(t);n.exec("PRAGMA journal_mode = WAL"),n.exec("PRAGMA foreign_keys = ON");let s={prepare(a){let d=n.prepare(a);return{all(...o){return d.all(...o)},run(...o){return d.run(...o)},get(...o){return d.get(...o)}}},exec(a){n.exec(a)},pragma(a){return n.prepare(`PRAGMA ${a}`).all()},close(){n.close()}},l;try{let{drizzle:a}=await import("drizzle-orm/better-sqlite3");l=a(s)}catch{l=s}return{drizzle:l,config:e,rawClient:s}}}async createPostgresConnection(e){let t=(await import("postgres")).default,{drizzle:r}=await import("drizzle-orm/postgres-js"),i=e.url??`postgres://${e.user}:${e.password}@${e.host??"localhost"}:${e.port??5432}/${e.database}`,n=t(i);return{drizzle:r(n),config:e,rawClient:n}}async createMySQLConnection(e){let t=await import("mysql2/promise"),{drizzle:r}=await import("drizzle-orm/mysql2"),i=t.createPool({host:e.host??"localhost",port:e.port??3306,database:e.database,user:e.user,password:e.password,uri:e.url});return{drizzle:r(i),config:e,rawClient:i}}async closeConnection(e){try{switch(e.config.driver){case"sqlite":e.rawClient.close();break;case"postgres":await e.rawClient.end();break;case"mysql":await e.rawClient.end();break}}catch{}}},b=u("svelar.connection",()=>new m)});p();import{randomBytes as v,createHash as A,timingSafeEqual as x}from"crypto";import{randomUUID as D}from"crypto";var g=class{config={driver:"memory",prefix:"sk_"};keys=[];configure(e){this.config=e}hashKey(e){return A("sha256").update(e).digest("hex")}generateRandomKey(e=32){return v(e).toString("base64url")}async create(e){let t=e.prefix||this.config.prefix||"sk_",r=`${t}${this.generateRandomKey(32)}`,i=this.hashKey(r),n={id:D(),userId:e.userId,name:e.name,key:i,prefix:t,permissions:e.permissions||[],metadata:e.metadata,createdAt:Date.now(),expiresAt:e.expiresIn?Date.now()+e.expiresIn*1e3:void 0};if(this.config.driver==="memory")this.keys.push(n);else if(this.config.driver==="database")try{let{Connection:s}=await Promise.resolve().then(()=>(y(),f));await s.connection()}catch{this.keys.push(n)}return{record:n,plainTextKey:r}}async validate(e){let t=this.hashKey(e);for(let r of this.keys)if(!r.revokedAt&&!(r.expiresAt&&r.expiresAt<Date.now()))try{if(x(Buffer.from(r.key),Buffer.from(t)))return r.lastUsedAt=Date.now(),r}catch{continue}return null}async revoke(e){let t=this.keys.find(r=>r.id===e);return t?(t.revokedAt=Date.now(),!0):!1}async listForUser(e){return this.keys.filter(t=>t.userId===e&&!t.revokedAt).map(t=>({...t}))}async rotate(e){let t=this.keys.find(r=>r.id===e);return!t||t.revokedAt?null:(await this.revoke(e),this.create({name:`${t.name} (rotated)`,userId:t.userId,prefix:t.prefix,permissions:t.permissions,metadata:t.metadata}))}async hasPermission(e,t){let r=await this.validate(e);return r?!r.permissions||r.permissions.length===0?!0:r.permissions.includes(t):!1}},R=u("svelar.apiKeys",()=>new g);export{R as ApiKeys};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Svelar Audit Logging
|
|
3
|
+
* Track user actions across the application for compliance and debugging.
|
|
4
|
+
*/
|
|
5
|
+
export interface AuditEntry {
|
|
6
|
+
id: string;
|
|
7
|
+
userId: string | number | null;
|
|
8
|
+
action: string;
|
|
9
|
+
modelType: string;
|
|
10
|
+
modelId: string | number;
|
|
11
|
+
oldValues?: Record<string, any>;
|
|
12
|
+
newValues?: Record<string, any>;
|
|
13
|
+
metadata?: Record<string, any>;
|
|
14
|
+
ipAddress?: string;
|
|
15
|
+
userAgent?: string;
|
|
16
|
+
timestamp: number;
|
|
17
|
+
}
|
|
18
|
+
export interface AuditFilter {
|
|
19
|
+
userId?: string | number;
|
|
20
|
+
action?: string;
|
|
21
|
+
modelType?: string;
|
|
22
|
+
modelId?: string | number;
|
|
23
|
+
since?: number;
|
|
24
|
+
until?: number;
|
|
25
|
+
limit?: number;
|
|
26
|
+
offset?: number;
|
|
27
|
+
}
|
|
28
|
+
export type AuditDriver = 'database' | 'memory' | 'log';
|
|
29
|
+
export interface AuditConfig {
|
|
30
|
+
driver: AuditDriver;
|
|
31
|
+
table?: string;
|
|
32
|
+
enabled?: boolean;
|
|
33
|
+
exclude?: string[];
|
|
34
|
+
}
|
|
35
|
+
declare class AuditManager {
|
|
36
|
+
private config;
|
|
37
|
+
private entries;
|
|
38
|
+
private maxEntries;
|
|
39
|
+
configure(config: AuditConfig): void;
|
|
40
|
+
log(entry: Omit<AuditEntry, 'id' | 'timestamp'>): Promise<void>;
|
|
41
|
+
query(filter: AuditFilter): Promise<AuditEntry[]>;
|
|
42
|
+
forModel(type: string, id: string | number): Promise<AuditEntry[]>;
|
|
43
|
+
byUser(userId: string | number, limit?: number): Promise<AuditEntry[]>;
|
|
44
|
+
getStats(): Promise<{
|
|
45
|
+
total: number;
|
|
46
|
+
byAction: Record<string, number>;
|
|
47
|
+
byModel: Record<string, number>;
|
|
48
|
+
}>;
|
|
49
|
+
}
|
|
50
|
+
export declare function auditable(target: any): any;
|
|
51
|
+
export declare const Audit: AuditManager;
|
|
52
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var b=Object.defineProperty;var f=(s,t)=>()=>(s&&(t=s(s=0)),t);var C=(s,t)=>{for(var e in t)b(s,e,{get:t[e],enumerable:!0})};function u(s,t){let e=Symbol.for(s),i=globalThis;return i[e]||(i[e]=t()),i[e]}var m=f(()=>{"use strict"});var h={};C(h,{Connection:()=>v});var p,v,w=f(()=>{"use strict";m();p=class{connections=new Map;config=null;defaultName="default";configure(t){this.config=t,this.defaultName=t.default}async connection(t){let e=t??this.defaultName;if(this.connections.has(e))return this.connections.get(e).drizzle;if(!this.config)throw new Error("Database not configured. Call Connection.configure() first, or register DatabaseServiceProvider.");let i=this.config.connections[e];if(!i)throw new Error(`Database connection "${e}" is not defined in configuration.`);let r=await this.createConnection(i);return this.connections.set(e,r),r.drizzle}async rawClient(t){let e=t??this.defaultName;return await this.connection(e),this.connections.get(e).rawClient}async raw(t,e=[],i){let r=await this.connection(i),n=this.getConfig(i);switch(n.driver){case"sqlite":{let o=await this.rawClient(i),l=e.map(c=>typeof c=="boolean"?c?1:0:c instanceof Date?c.toISOString():c),a=o.prepare(t),d=t.trimStart().toUpperCase();return d.startsWith("SELECT")||d.startsWith("PRAGMA")||d.startsWith("WITH")?a.all(...l):a.run(...l)}case"postgres":return(await this.rawClient(i))(t,...e);case"mysql":{let o=await this.rawClient(i),[l]=await o.execute(t,e);return l}default:throw new Error(`Unsupported driver: ${n.driver}`)}}getDriver(t){return this.getConfig(t).driver}getConfig(t){let e=t??this.defaultName;if(!this.config)throw new Error("Database not configured.");let i=this.config.connections[e];if(!i)throw new Error(`Database connection "${e}" is not defined.`);return i}async disconnect(t){if(t){let e=this.connections.get(t);e&&(await this.closeConnection(e),this.connections.delete(t))}else{for(let[e,i]of this.connections)await this.closeConnection(i);this.connections.clear()}}isConnected(t){return this.connections.has(t??this.defaultName)}async transaction(t,e){let i=this.getConfig(e),r=await this.rawClient(e);switch(i.driver){case"sqlite":{r.exec("BEGIN");try{let n=await t();return r.exec("COMMIT"),n}catch(n){throw r.exec("ROLLBACK"),n}}case"postgres":{await r`BEGIN`;try{let n=await t();return await r`COMMIT`,n}catch(n){throw await r`ROLLBACK`,n}}case"mysql":{let n=await r.getConnection();await n.beginTransaction();try{let o=await t();return await n.commit(),n.release(),o}catch(o){throw await n.rollback(),n.release(),o}}default:throw new Error(`Unsupported driver: ${i.driver}`)}}async createConnection(t){switch(t.driver){case"sqlite":return this.createSQLiteConnection(t);case"postgres":return this.createPostgresConnection(t);case"mysql":return this.createMySQLConnection(t);default:throw new Error(`Unsupported database driver: ${t.driver}`)}}async createSQLiteConnection(t){let e=t.filename??t.database??":memory:";try{let i=(await import("better-sqlite3")).default,{drizzle:r}=await import("drizzle-orm/better-sqlite3"),n=new i(e);return n.pragma("journal_mode = WAL"),n.pragma("foreign_keys = ON"),{drizzle:r(n),config:t,rawClient:n}}catch(i){let r;try{r=(await new Function("mod","return import(mod)")("node:sqlite")).DatabaseSync}catch{throw new Error(`No SQLite driver available. Install better-sqlite3 (npm install better-sqlite3) or use Node.js v22+ which includes built-in SQLite support. Original error: ${i instanceof Error?i.message:String(i)}`)}let n=new r(e);n.exec("PRAGMA journal_mode = WAL"),n.exec("PRAGMA foreign_keys = ON");let o={prepare(a){let d=n.prepare(a);return{all(...c){return d.all(...c)},run(...c){return d.run(...c)},get(...c){return d.get(...c)}}},exec(a){n.exec(a)},pragma(a){return n.prepare(`PRAGMA ${a}`).all()},close(){n.close()}},l;try{let{drizzle:a}=await import("drizzle-orm/better-sqlite3");l=a(o)}catch{l=o}return{drizzle:l,config:t,rawClient:o}}}async createPostgresConnection(t){let e=(await import("postgres")).default,{drizzle:i}=await import("drizzle-orm/postgres-js"),r=t.url??`postgres://${t.user}:${t.password}@${t.host??"localhost"}:${t.port??5432}/${t.database}`,n=e(r);return{drizzle:i(n),config:t,rawClient:n}}async createMySQLConnection(t){let e=await import("mysql2/promise"),{drizzle:i}=await import("drizzle-orm/mysql2"),r=e.createPool({host:t.host??"localhost",port:t.port??3306,database:t.database,user:t.user,password:t.password,uri:t.url});return{drizzle:i(r),config:t,rawClient:r}}async closeConnection(t){try{switch(t.config.driver){case"sqlite":t.rawClient.close();break;case"postgres":await t.rawClient.end();break;case"mysql":await t.rawClient.end();break}}catch{}}},v=u("svelar.connection",()=>new p)});m();import{randomUUID as A}from"crypto";var y=class{config={driver:"memory",enabled:!0};entries=[];maxEntries=1e4;configure(t){this.config=t}async log(t){if(!this.config.enabled||this.config.exclude?.includes(t.modelType))return;let e={...t,id:A(),timestamp:Date.now()};if(this.config.driver==="memory")this.entries.push(e),this.entries.length>this.maxEntries&&this.entries.shift();else if(this.config.driver==="log")console.log("[Audit]",JSON.stringify(e));else if(this.config.driver==="database")try{let{Connection:i}=await Promise.resolve().then(()=>(w(),h));await i.connection()}catch{this.entries.push(e)}}async query(t){let e=[...this.entries];t.userId!==void 0&&(e=e.filter(n=>n.userId===t.userId)),t.action&&(e=e.filter(n=>n.action===t.action)),t.modelType&&(e=e.filter(n=>n.modelType===t.modelType)),t.modelId!==void 0&&(e=e.filter(n=>n.modelId===t.modelId)),t.since&&(e=e.filter(n=>n.timestamp>=t.since)),t.until&&(e=e.filter(n=>n.timestamp<=t.until)),e.sort((n,o)=>o.timestamp-n.timestamp);let i=t.offset||0,r=t.limit||100;return e.slice(i,i+r)}async forModel(t,e){return this.query({modelType:t,modelId:e,limit:1e3})}async byUser(t,e){return this.query({userId:t,limit:e||100})}async getStats(){let t={},e={};for(let i of this.entries)t[i.action]=(t[i.action]||0)+1,e[i.modelType]=(e[i.modelType]||0)+1;return{total:this.entries.length,byAction:t,byModel:e}}};function P(s){let t=s.create||s.insert,e=s.update,i=s.delete;return t&&(s.create=async function(r){let n=await t.call(this,r);return await g.log({userId:null,action:"created",modelType:s.name,modelId:n.id,newValues:r}),n}),e&&(s.update=async function(r,n){let o=await e.call(this,r,n);return await g.log({userId:null,action:"updated",modelType:s.name,modelId:r,newValues:n}),o}),i&&(s.delete=async function(r){let n=await i.call(this,r);return await g.log({userId:null,action:"deleted",modelType:s.name,modelId:r}),n}),s}var g=u("svelar.audit",()=>new y);export{g as Audit,P as auditable};
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Svelar Auth
|
|
3
|
+
*
|
|
4
|
+
* Authentication system with guards, JWT support, and session-based auth.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* // Session-based auth
|
|
9
|
+
* const auth = new AuthManager({ guard: 'session', model: User });
|
|
10
|
+
* const user = await auth.attempt({ email: 'john@example.com', password: 'secret' });
|
|
11
|
+
*
|
|
12
|
+
* // JWT auth
|
|
13
|
+
* const auth = new AuthManager({ guard: 'jwt', model: User, jwt: { secret: '...' } });
|
|
14
|
+
* const { user, token } = await auth.attemptJwt({ email: '...', password: '...' });
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export type GuardType = 'session' | 'jwt' | 'token';
|
|
18
|
+
export interface AuthConfig {
|
|
19
|
+
guard: GuardType;
|
|
20
|
+
/** The Model class to query users from */
|
|
21
|
+
model: any;
|
|
22
|
+
/** Column used for login identifier (default: 'email') */
|
|
23
|
+
identifierColumn?: string;
|
|
24
|
+
/** Column storing the hashed password (default: 'password') */
|
|
25
|
+
passwordColumn?: string;
|
|
26
|
+
/** JWT configuration */
|
|
27
|
+
jwt?: JwtConfig;
|
|
28
|
+
/** API token configuration */
|
|
29
|
+
token?: TokenConfig;
|
|
30
|
+
/** Application URL for generating email links */
|
|
31
|
+
appUrl?: string;
|
|
32
|
+
/** Application name for email templates */
|
|
33
|
+
appName?: string;
|
|
34
|
+
/** Password reset configuration */
|
|
35
|
+
passwordResets?: PasswordResetConfig;
|
|
36
|
+
/** Email verification configuration */
|
|
37
|
+
emailVerification?: EmailVerificationConfig;
|
|
38
|
+
/** OTP (one-time password) configuration */
|
|
39
|
+
otp?: OtpConfig;
|
|
40
|
+
}
|
|
41
|
+
export interface PasswordResetConfig {
|
|
42
|
+
/** Table name (default: 'password_resets') */
|
|
43
|
+
table?: string;
|
|
44
|
+
/** Token lifetime in seconds (default: 3600 = 1 hour) */
|
|
45
|
+
expiresIn?: number;
|
|
46
|
+
}
|
|
47
|
+
export interface EmailVerificationConfig {
|
|
48
|
+
/** Table name (default: 'email_verifications') */
|
|
49
|
+
table?: string;
|
|
50
|
+
/** Token lifetime in seconds (default: 86400 = 24 hours) */
|
|
51
|
+
expiresIn?: number;
|
|
52
|
+
/** Column on user model storing verification timestamp (default: 'email_verified_at') */
|
|
53
|
+
verifiedColumn?: string;
|
|
54
|
+
}
|
|
55
|
+
export interface OtpConfig {
|
|
56
|
+
/** Table name (default: 'otp_codes') */
|
|
57
|
+
table?: string;
|
|
58
|
+
/** Code lifetime in seconds (default: 600 = 10 minutes) */
|
|
59
|
+
expiresIn?: number;
|
|
60
|
+
/** Code length (default: 6) */
|
|
61
|
+
length?: number;
|
|
62
|
+
}
|
|
63
|
+
export interface JwtConfig {
|
|
64
|
+
secret: string;
|
|
65
|
+
expiresIn?: number;
|
|
66
|
+
algorithm?: 'HS256' | 'HS384' | 'HS512';
|
|
67
|
+
issuer?: string;
|
|
68
|
+
/** Enable refresh tokens (default: false) */
|
|
69
|
+
refreshTokens?: boolean;
|
|
70
|
+
/** Refresh token lifetime in seconds (default: 604800 = 7 days) */
|
|
71
|
+
refreshExpiresIn?: number;
|
|
72
|
+
/** Table storing refresh tokens (default: 'refresh_tokens') */
|
|
73
|
+
refreshTable?: string;
|
|
74
|
+
}
|
|
75
|
+
export interface TokenConfig {
|
|
76
|
+
/** Table storing API tokens */
|
|
77
|
+
table?: string;
|
|
78
|
+
/** Header name for token (default: 'Authorization') */
|
|
79
|
+
header?: string;
|
|
80
|
+
}
|
|
81
|
+
export interface AuthUser {
|
|
82
|
+
getAttribute(key: string): any;
|
|
83
|
+
[key: string]: any;
|
|
84
|
+
}
|
|
85
|
+
export interface JwtPayload {
|
|
86
|
+
sub: string | number;
|
|
87
|
+
iat: number;
|
|
88
|
+
exp: number;
|
|
89
|
+
iss?: string;
|
|
90
|
+
[key: string]: any;
|
|
91
|
+
}
|
|
92
|
+
export interface JwtTokenPair {
|
|
93
|
+
user: AuthUser;
|
|
94
|
+
token: string;
|
|
95
|
+
expiresAt: Date;
|
|
96
|
+
refreshToken?: string;
|
|
97
|
+
refreshExpiresAt?: Date;
|
|
98
|
+
}
|
|
99
|
+
export declare function signJwt(payload: JwtPayload, secret: string, algorithm?: string): string;
|
|
100
|
+
export declare function verifyJwt(token: string, secret: string): JwtPayload | null;
|
|
101
|
+
export declare class AuthManager {
|
|
102
|
+
private config;
|
|
103
|
+
private currentUser;
|
|
104
|
+
constructor(userConfig: AuthConfig);
|
|
105
|
+
/**
|
|
106
|
+
* Attempt session-based login.
|
|
107
|
+
* Returns the user on success, null on failure.
|
|
108
|
+
*/
|
|
109
|
+
attempt(credentials: Record<string, any>, session?: any): Promise<AuthUser | null>;
|
|
110
|
+
/**
|
|
111
|
+
* Attempt JWT-based login.
|
|
112
|
+
* Returns user + access token (+ refresh token if enabled) on success, null on failure.
|
|
113
|
+
*/
|
|
114
|
+
attemptJwt(credentials: Record<string, any>): Promise<JwtTokenPair | null>;
|
|
115
|
+
/**
|
|
116
|
+
* Issue an access token (and optionally a refresh token) for a user.
|
|
117
|
+
*/
|
|
118
|
+
private issueTokenPair;
|
|
119
|
+
/**
|
|
120
|
+
* Exchange a refresh token for a new access token + refresh token pair.
|
|
121
|
+
* The old refresh token is revoked (rotation).
|
|
122
|
+
*/
|
|
123
|
+
refreshJwt(refreshToken: string): Promise<JwtTokenPair | null>;
|
|
124
|
+
/**
|
|
125
|
+
* Revoke all refresh tokens for a user (e.g. on logout or password change).
|
|
126
|
+
*/
|
|
127
|
+
revokeRefreshTokens(userId: string | number): Promise<void>;
|
|
128
|
+
/**
|
|
129
|
+
* Resolve user from a JWT token
|
|
130
|
+
*/
|
|
131
|
+
resolveFromToken(token: string): Promise<AuthUser | null>;
|
|
132
|
+
/**
|
|
133
|
+
* Resolve user from session
|
|
134
|
+
*/
|
|
135
|
+
resolveFromSession(session: any): Promise<AuthUser | null>;
|
|
136
|
+
/**
|
|
137
|
+
* Register a new user
|
|
138
|
+
*/
|
|
139
|
+
register(attributes: Record<string, any>): Promise<AuthUser>;
|
|
140
|
+
/**
|
|
141
|
+
* Logout
|
|
142
|
+
*/
|
|
143
|
+
logout(session?: any): Promise<void>;
|
|
144
|
+
/**
|
|
145
|
+
* Get the currently authenticated user
|
|
146
|
+
*/
|
|
147
|
+
user(): AuthUser | null;
|
|
148
|
+
/**
|
|
149
|
+
* Check if a user is authenticated
|
|
150
|
+
*/
|
|
151
|
+
check(): boolean;
|
|
152
|
+
/**
|
|
153
|
+
* Get the authenticated user's ID
|
|
154
|
+
*/
|
|
155
|
+
id(): any;
|
|
156
|
+
/**
|
|
157
|
+
* Generate an API token for a user
|
|
158
|
+
*/
|
|
159
|
+
generateApiToken(user: AuthUser, name?: string): Promise<string>;
|
|
160
|
+
/**
|
|
161
|
+
* Resolve user from an API token
|
|
162
|
+
*/
|
|
163
|
+
resolveFromApiToken(plainToken: string): Promise<AuthUser | null>;
|
|
164
|
+
/**
|
|
165
|
+
* Send a password reset email.
|
|
166
|
+
* Generates a token, stores its hash, and sends the password-reset email template.
|
|
167
|
+
* Auto-creates the password_resets table if it doesn't exist.
|
|
168
|
+
*/
|
|
169
|
+
sendPasswordReset(email: string): Promise<boolean>;
|
|
170
|
+
/**
|
|
171
|
+
* Reset a user's password using a valid reset token.
|
|
172
|
+
* Validates the token, updates the password, and revokes all refresh tokens.
|
|
173
|
+
*/
|
|
174
|
+
resetPassword(token: string, email: string, newPassword: string): Promise<boolean>;
|
|
175
|
+
/**
|
|
176
|
+
* Send an email verification link.
|
|
177
|
+
* Generates a token, stores its hash, and sends the email-verification template.
|
|
178
|
+
* Auto-creates the email_verifications table if it doesn't exist.
|
|
179
|
+
*/
|
|
180
|
+
sendVerificationEmail(user: AuthUser): Promise<void>;
|
|
181
|
+
/**
|
|
182
|
+
* Verify an email address using a valid verification token.
|
|
183
|
+
* Sets the email_verified_at column on the user.
|
|
184
|
+
*/
|
|
185
|
+
verifyEmail(token: string, userId: string | number): Promise<boolean>;
|
|
186
|
+
/**
|
|
187
|
+
* Check if a user has verified their email.
|
|
188
|
+
*/
|
|
189
|
+
isEmailVerified(user: AuthUser): boolean;
|
|
190
|
+
/**
|
|
191
|
+
* Send an OTP code via email.
|
|
192
|
+
* Generates a numeric code, stores its hash, and sends the otp-code email template.
|
|
193
|
+
* Auto-creates the otp_codes table if it doesn't exist.
|
|
194
|
+
*
|
|
195
|
+
* @param email - User email address
|
|
196
|
+
* @param purpose - Purpose identifier (default: 'login')
|
|
197
|
+
* @returns true if sent (user exists), false if user not found
|
|
198
|
+
*/
|
|
199
|
+
sendOtp(email: string, purpose?: string): Promise<boolean>;
|
|
200
|
+
/**
|
|
201
|
+
* Verify an OTP code without creating a session.
|
|
202
|
+
* Returns the user if valid, null otherwise.
|
|
203
|
+
*/
|
|
204
|
+
verifyOtp(email: string, code: string, purpose?: string): Promise<AuthUser | null>;
|
|
205
|
+
/**
|
|
206
|
+
* Verify OTP and create a session (OTP login).
|
|
207
|
+
* Combines verifyOtp + session creation in one step.
|
|
208
|
+
*/
|
|
209
|
+
attemptOtp(email: string, code: string, session?: any, purpose?: string): Promise<AuthUser | null>;
|
|
210
|
+
/**
|
|
211
|
+
* Delete expired tokens from password_resets, email_verifications, and otp_codes tables.
|
|
212
|
+
* Call this from a scheduled task (e.g., daily).
|
|
213
|
+
*/
|
|
214
|
+
cleanupExpiredTokens(): Promise<{
|
|
215
|
+
passwordResets: number;
|
|
216
|
+
verifications: number;
|
|
217
|
+
otpCodes: number;
|
|
218
|
+
}>;
|
|
219
|
+
private hashToken;
|
|
220
|
+
private generateOtpCode;
|
|
221
|
+
private tablesEnsured;
|
|
222
|
+
private ensureTable;
|
|
223
|
+
private sendAuthEmail;
|
|
224
|
+
}
|
|
225
|
+
import { Middleware, type MiddlewareContext, type NextFunction } from '../middleware/Middleware.js';
|
|
226
|
+
/**
|
|
227
|
+
* Authentication middleware that resolves the user from session or JWT.
|
|
228
|
+
*/
|
|
229
|
+
export declare class AuthenticateMiddleware extends Middleware {
|
|
230
|
+
private authManager;
|
|
231
|
+
constructor(authManager: AuthManager);
|
|
232
|
+
handle(ctx: MiddlewareContext, next: NextFunction): Promise<Response | void>;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Middleware that requires authentication — returns 401 JSON if not authenticated.
|
|
236
|
+
* Best for API routes.
|
|
237
|
+
*/
|
|
238
|
+
export declare class RequireAuthMiddleware extends Middleware {
|
|
239
|
+
handle(ctx: MiddlewareContext, next: NextFunction): Promise<Response | void>;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Middleware that redirects unauthenticated users to a login page.
|
|
243
|
+
* Best for page routes (dashboard, admin, etc.).
|
|
244
|
+
*
|
|
245
|
+
* @example
|
|
246
|
+
* ```ts
|
|
247
|
+
* // In +layout.server.ts to protect an entire route group:
|
|
248
|
+
* import { guardAuth } from 'svelar/auth';
|
|
249
|
+
*
|
|
250
|
+
* export const load = guardAuth();
|
|
251
|
+
* // or with a custom redirect:
|
|
252
|
+
* export const load = guardAuth('/signin');
|
|
253
|
+
* ```
|
|
254
|
+
*/
|
|
255
|
+
export declare class RedirectIfNotAuthenticated extends Middleware {
|
|
256
|
+
private redirectTo;
|
|
257
|
+
constructor(redirectTo?: string);
|
|
258
|
+
handle(ctx: MiddlewareContext, next: NextFunction): Promise<Response | void>;
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Convenience helper to create a SvelteKit load function that guards a route.
|
|
262
|
+
* Use in `+layout.server.ts` to protect all pages under a route group.
|
|
263
|
+
*
|
|
264
|
+
* @example
|
|
265
|
+
* ```ts
|
|
266
|
+
* // src/routes/dashboard/+layout.server.ts
|
|
267
|
+
* import { guardAuth } from 'svelar/auth';
|
|
268
|
+
* export const load = guardAuth();
|
|
269
|
+
*
|
|
270
|
+
* // src/routes/admin/+layout.server.ts — custom redirect
|
|
271
|
+
* import { guardAuth } from 'svelar/auth';
|
|
272
|
+
* export const load = guardAuth('/login');
|
|
273
|
+
*
|
|
274
|
+
* // With role check
|
|
275
|
+
* import { guardAuth } from 'svelar/auth';
|
|
276
|
+
* export const load = guardAuth('/dashboard', { role: 'admin' });
|
|
277
|
+
* ```
|
|
278
|
+
*/
|
|
279
|
+
export declare function guardAuth(redirectTo?: string, options?: {
|
|
280
|
+
role?: string;
|
|
281
|
+
}): (event: {
|
|
282
|
+
locals: Record<string, any>;
|
|
283
|
+
}) => Promise<{}>;
|