@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
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Svelar Gates & Policies
|
|
3
|
+
*
|
|
4
|
+
* Laravel-inspired authorization system with gates (closures),
|
|
5
|
+
* policies (class-based), and response objects.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* import { Gate } from 'svelar/auth';
|
|
10
|
+
*
|
|
11
|
+
* // Define gates
|
|
12
|
+
* Gate.define('edit-post', (user, post) => {
|
|
13
|
+
* return user.id === post.user_id;
|
|
14
|
+
* });
|
|
15
|
+
*
|
|
16
|
+
* Gate.define('admin-access', (user) => {
|
|
17
|
+
* return user.role === 'admin';
|
|
18
|
+
* });
|
|
19
|
+
*
|
|
20
|
+
* // Check authorization
|
|
21
|
+
* if (Gate.allows('edit-post', user, post)) { ... }
|
|
22
|
+
* if (Gate.denies('admin-access', user)) { throw ... }
|
|
23
|
+
*
|
|
24
|
+
* // Use policies
|
|
25
|
+
* class PostPolicy extends Policy {
|
|
26
|
+
* viewAny(user: any) { return true; }
|
|
27
|
+
* view(user: any, post: any) { return true; }
|
|
28
|
+
* create(user: any) { return !!user; }
|
|
29
|
+
* update(user: any, post: any) { return user.id === post.user_id; }
|
|
30
|
+
* delete(user: any, post: any) {
|
|
31
|
+
* return user.id === post.user_id || user.role === 'admin';
|
|
32
|
+
* }
|
|
33
|
+
* }
|
|
34
|
+
*
|
|
35
|
+
* Gate.policy('Post', new PostPolicy());
|
|
36
|
+
*
|
|
37
|
+
* // Check via policy
|
|
38
|
+
* Gate.forUser(user).allows('update', post);
|
|
39
|
+
* Gate.forUser(user).authorize('delete', post); // throws if denied
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export type GateCallback = (user: any, ...args: any[]) => boolean | GateResponse | Promise<boolean | GateResponse>;
|
|
43
|
+
export type BeforeCallback = (user: any, ability: string) => boolean | null | undefined | Promise<boolean | null | undefined>;
|
|
44
|
+
export type AfterCallback = (user: any, ability: string, result: boolean, ...args: any[]) => boolean | void | Promise<boolean | void>;
|
|
45
|
+
export declare class GateResponse {
|
|
46
|
+
readonly allowed: boolean;
|
|
47
|
+
readonly message?: string | undefined;
|
|
48
|
+
readonly code?: number | undefined;
|
|
49
|
+
constructor(allowed: boolean, message?: string | undefined, code?: number | undefined);
|
|
50
|
+
static allow(message?: string): GateResponse;
|
|
51
|
+
static deny(message?: string, code?: number): GateResponse;
|
|
52
|
+
toResponse(): Response;
|
|
53
|
+
}
|
|
54
|
+
export declare class AuthorizationError extends Error {
|
|
55
|
+
readonly statusCode: number;
|
|
56
|
+
constructor(message?: string, code?: number);
|
|
57
|
+
toResponse(): Response;
|
|
58
|
+
}
|
|
59
|
+
export declare abstract class Policy {
|
|
60
|
+
/**
|
|
61
|
+
* Perform pre-checks before any ability is checked.
|
|
62
|
+
* Return true to allow all, false to deny all, null to continue.
|
|
63
|
+
*/
|
|
64
|
+
before?(user: any, ability: string): boolean | null | undefined | Promise<boolean | null | undefined>;
|
|
65
|
+
viewAny?(user: any): boolean | GateResponse | Promise<boolean | GateResponse>;
|
|
66
|
+
view?(user: any, model: any): boolean | GateResponse | Promise<boolean | GateResponse>;
|
|
67
|
+
create?(user: any): boolean | GateResponse | Promise<boolean | GateResponse>;
|
|
68
|
+
update?(user: any, model: any): boolean | GateResponse | Promise<boolean | GateResponse>;
|
|
69
|
+
delete?(user: any, model: any): boolean | GateResponse | Promise<boolean | GateResponse>;
|
|
70
|
+
restore?(user: any, model: any): boolean | GateResponse | Promise<boolean | GateResponse>;
|
|
71
|
+
forceDelete?(user: any, model: any): boolean | GateResponse | Promise<boolean | GateResponse>;
|
|
72
|
+
}
|
|
73
|
+
declare class GateManager {
|
|
74
|
+
private gates;
|
|
75
|
+
private policies;
|
|
76
|
+
private policyModelMap;
|
|
77
|
+
private beforeCallbacks;
|
|
78
|
+
private afterCallbacks;
|
|
79
|
+
private superUserCallback?;
|
|
80
|
+
/**
|
|
81
|
+
* Define a gate
|
|
82
|
+
*/
|
|
83
|
+
define(ability: string, callback: GateCallback): this;
|
|
84
|
+
/**
|
|
85
|
+
* Register a policy for a model
|
|
86
|
+
*/
|
|
87
|
+
policy(modelName: string, policy: Policy): this;
|
|
88
|
+
/**
|
|
89
|
+
* Register a "before" callback that runs before all gates/policies
|
|
90
|
+
*/
|
|
91
|
+
before(callback: BeforeCallback): this;
|
|
92
|
+
/**
|
|
93
|
+
* Register an "after" callback that runs after all gates/policies
|
|
94
|
+
*/
|
|
95
|
+
after(callback: AfterCallback): this;
|
|
96
|
+
/**
|
|
97
|
+
* Define a super-user check.
|
|
98
|
+
* If this returns true, all gates and policies allow.
|
|
99
|
+
*/
|
|
100
|
+
defineSuperUser(callback: (user: any) => boolean): this;
|
|
101
|
+
/**
|
|
102
|
+
* Check if a user is allowed to perform an ability
|
|
103
|
+
*/
|
|
104
|
+
allows(ability: string, user: any, ...args: any[]): Promise<boolean>;
|
|
105
|
+
/**
|
|
106
|
+
* Check if a user is denied from performing an ability
|
|
107
|
+
*/
|
|
108
|
+
denies(ability: string, user: any, ...args: any[]): Promise<boolean>;
|
|
109
|
+
/**
|
|
110
|
+
* Check authorization, throw if denied
|
|
111
|
+
*/
|
|
112
|
+
authorize(ability: string, user: any, ...args: any[]): Promise<void>;
|
|
113
|
+
/**
|
|
114
|
+
* Inspect an ability check, returning a GateResponse
|
|
115
|
+
*/
|
|
116
|
+
inspect(ability: string, user: any, ...args: any[]): Promise<GateResponse>;
|
|
117
|
+
/**
|
|
118
|
+
* Create a scoped gate checker for a specific user
|
|
119
|
+
*/
|
|
120
|
+
forUser(user: any): UserGate;
|
|
121
|
+
/**
|
|
122
|
+
* Check if a gate or policy is defined for an ability
|
|
123
|
+
*/
|
|
124
|
+
has(ability: string): boolean;
|
|
125
|
+
/**
|
|
126
|
+
* Get the policy for a model (by class name or instance)
|
|
127
|
+
*/
|
|
128
|
+
getPolicyFor(model: any): Policy | undefined;
|
|
129
|
+
private check;
|
|
130
|
+
private checkPolicy;
|
|
131
|
+
}
|
|
132
|
+
export declare class UserGate {
|
|
133
|
+
private gate;
|
|
134
|
+
private user;
|
|
135
|
+
constructor(gate: GateManager, user: any);
|
|
136
|
+
allows(ability: string, ...args: any[]): Promise<boolean>;
|
|
137
|
+
denies(ability: string, ...args: any[]): Promise<boolean>;
|
|
138
|
+
authorize(ability: string, ...args: any[]): Promise<void>;
|
|
139
|
+
inspect(ability: string, ...args: any[]): Promise<GateResponse>;
|
|
140
|
+
/**
|
|
141
|
+
* Check multiple abilities at once
|
|
142
|
+
*/
|
|
143
|
+
any(abilities: string[], ...args: any[]): Promise<boolean>;
|
|
144
|
+
/**
|
|
145
|
+
* Check that ALL abilities are allowed
|
|
146
|
+
*/
|
|
147
|
+
all(abilities: string[], ...args: any[]): Promise<boolean>;
|
|
148
|
+
}
|
|
149
|
+
import { Middleware, type MiddlewareContext, type NextFunction } from '../middleware/Middleware.js';
|
|
150
|
+
/**
|
|
151
|
+
* Middleware that checks gate authorization.
|
|
152
|
+
*
|
|
153
|
+
* @example
|
|
154
|
+
* ```ts
|
|
155
|
+
* // In a controller:
|
|
156
|
+
* this.middleware(new GateMiddleware('edit-post'), { only: ['update', 'destroy'] });
|
|
157
|
+
* ```
|
|
158
|
+
*/
|
|
159
|
+
export declare class GateMiddleware extends Middleware {
|
|
160
|
+
private ability;
|
|
161
|
+
private modelResolver?;
|
|
162
|
+
constructor(ability: string, modelResolver?: ((ctx: MiddlewareContext) => any) | undefined);
|
|
163
|
+
handle(ctx: MiddlewareContext, next: NextFunction): Promise<Response | void>;
|
|
164
|
+
}
|
|
165
|
+
export declare const Gate: GateManager;
|
|
166
|
+
export {};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export { AuthManager, AuthenticateMiddleware, RequireAuthMiddleware, RedirectIfNotAuthenticated, guardAuth, signJwt, verifyJwt, type AuthConfig, type AuthUser, type GuardType, type JwtConfig, type JwtPayload, type JwtTokenPair, type TokenConfig, type PasswordResetConfig, type EmailVerificationConfig, type OtpConfig, } from './Auth.js';
|
|
2
|
+
export { Gate, Policy, GateResponse, AuthorizationError, GateMiddleware, UserGate, type GateCallback, type BeforeCallback, type AfterCallback, } from './Gate.js';
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
var ce=Object.defineProperty;var T=(o,e)=>()=>(o&&(e=o(o=0)),e);var R=(o,e)=>{for(var t in e)ce(o,t,{get:e[t],enumerable:!0})};function f(o,e){let t=Symbol.for(o),r=globalThis;return r[t]||(r[t]=e()),r[t]}var b=T(()=>{"use strict"});var C={};R(C,{Hash:()=>de});import{randomBytes as M,scrypt as V,timingSafeEqual as le}from"crypto";async function ue(o,e=16384){let t=M(16),r=64,n=await new Promise((s,i)=>{V(o,t,r,{N:e,r:8,p:1},(a,c)=>{a?i(a):s(c)})});return`$scrypt$N=${e}$${t.toString("base64")}$${n.toString("base64")}`}async function pe(o,e){let t=e.split("$");if(t.length!==5||t[1]!=="scrypt")return!1;let r=parseInt(t[2].replace("N=",""),10),n=Buffer.from(t[3],"base64"),s=Buffer.from(t[4],"base64"),i=s.length,a=await new Promise((c,l)=>{V(o,n,i,{N:r,r:8,p:1},(u,m)=>{u?l(u):c(m)})});return le(a,s)}var N,de,x=T(()=>{"use strict";b();N=class{config={driver:"scrypt",scryptCost:16384,bcryptRounds:12};configure(e){Object.assign(this.config,e)}async make(e){switch(this.config.driver){case"scrypt":return ue(e,this.config.scryptCost);case"bcrypt":try{return(await import("bcrypt")).default.hash(e,this.config.bcryptRounds??12)}catch{throw new Error('bcrypt driver requires the "bcrypt" package. Install it: npm install bcrypt')}case"argon2":try{return(await import("argon2")).default.hash(e)}catch{throw new Error('argon2 driver requires the "argon2" package. Install it: npm install argon2')}default:throw new Error(`Unsupported hash driver: ${this.config.driver}`)}}async verify(e,t){if(t.startsWith("$scrypt$"))return pe(e,t);if(t.startsWith("$2b$")||t.startsWith("$2a$")||t.startsWith("$2y$"))try{return(await import("bcrypt")).default.compare(e,t)}catch{throw new Error("bcrypt package required to verify bcrypt hashes.")}if(t.startsWith("$argon2"))try{return(await import("argon2")).default.verify(t,e)}catch{throw new Error("argon2 package required to verify argon2 hashes.")}return!1}needsRehash(e){if(this.config.driver==="scrypt"&&e.startsWith("$scrypt$")){let t=e.match(/N=(\d+)/);if(t)return parseInt(t[1],10)!==this.config.scryptCost}if(this.config.driver==="bcrypt"&&(e.startsWith("$2b$")||e.startsWith("$2a$"))){let t=e.match(/\$2[aby]\$(\d+)\$/);if(t)return parseInt(t[1],10)!==this.config.bcryptRounds}return this.config.driver==="scrypt"&&!e.startsWith("$scrypt$")||this.config.driver==="bcrypt"&&!e.startsWith("$2")||this.config.driver==="argon2"&&!e.startsWith("$argon2")}randomString(e=32){return M(Math.ceil(e/2)).toString("hex").slice(0,e)}randomToken(e=32){return M(e).toString("base64url")}},de=f("svelar.hash",()=>new N)});var d={};R(d,{Connection:()=>he});var D,he,h=T(()=>{"use strict";b();D=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 n=await this.createConnection(r);return this.connections.set(t,n),n.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 n=await this.connection(r),s=this.getConfig(r);switch(s.driver){case"sqlite":{let i=await this.rawClient(r),a=t.map(u=>typeof u=="boolean"?u?1:0:u instanceof Date?u.toISOString():u),c=i.prepare(e),l=e.trimStart().toUpperCase();return l.startsWith("SELECT")||l.startsWith("PRAGMA")||l.startsWith("WITH")?c.all(...a):c.run(...a)}case"postgres":return(await this.rawClient(r))(e,...t);case"mysql":{let i=await this.rawClient(r),[a]=await i.execute(e,t);return a}default:throw new Error(`Unsupported driver: ${s.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),n=await this.rawClient(t);switch(r.driver){case"sqlite":{n.exec("BEGIN");try{let s=await e();return n.exec("COMMIT"),s}catch(s){throw n.exec("ROLLBACK"),s}}case"postgres":{await n`BEGIN`;try{let s=await e();return await n`COMMIT`,s}catch(s){throw await n`ROLLBACK`,s}}case"mysql":{let s=await n.getConnection();await s.beginTransaction();try{let i=await e();return await s.commit(),s.release(),i}catch(i){throw await s.rollback(),s.release(),i}}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:n}=await import("drizzle-orm/better-sqlite3"),s=new r(t);return s.pragma("journal_mode = WAL"),s.pragma("foreign_keys = ON"),{drizzle:n(s),config:e,rawClient:s}}catch(r){let n;try{n=(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 s=new n(t);s.exec("PRAGMA journal_mode = WAL"),s.exec("PRAGMA foreign_keys = ON");let i={prepare(c){let l=s.prepare(c);return{all(...u){return l.all(...u)},run(...u){return l.run(...u)},get(...u){return l.get(...u)}}},exec(c){s.exec(c)},pragma(c){return s.prepare(`PRAGMA ${c}`).all()},close(){s.close()}},a;try{let{drizzle:c}=await import("drizzle-orm/better-sqlite3");a=c(i)}catch{a=i}return{drizzle:a,config:e,rawClient:i}}}async createPostgresConnection(e){let t=(await import("postgres")).default,{drizzle:r}=await import("drizzle-orm/postgres-js"),n=e.url??`postgres://${e.user}:${e.password}@${e.host??"localhost"}:${e.port??5432}/${e.database}`,s=t(n);return{drizzle:r(s),config:e,rawClient:s}}async createMySQLConnection(e){let t=await import("mysql2/promise"),{drizzle:r}=await import("drizzle-orm/mysql2"),n=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(n),config:e,rawClient:n}}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{}}},he=f("svelar.connection",()=>new D)});var X={};R(X,{EmailTemplates:()=>ge});import{randomUUID as w}from"crypto";var O,ge,Y=T(()=>{"use strict";b();O=class{config={driver:"memory"};templates=new Map;constructor(){this.registerDefaults()}configure(e){this.config=e}async register(e){let t={...e,id:w(),createdAt:Date.now(),updatedAt:Date.now()};if(this.config.driver==="memory")this.templates.set(e.name,t);else if(this.config.driver==="database")try{let{Connection:r}=await Promise.resolve().then(()=>(h(),d));await r.connection()}catch{this.templates.set(e.name,t)}return t}async render(e,t){let r=await this.get(e);if(!r)throw new Error(`Template "${e}" not found`);let n=this.interpolate(r.subject,t),s=this.interpolate(r.html,t),i=r.text?this.interpolate(r.text,t):void 0;return{subject:n,html:s,text:i}}async get(e){return this.templates.get(e)||null}async list(e){let t=Array.from(this.templates.values());return e&&(t=t.filter(r=>r.category===e)),t}async update(e,t){let r=this.templates.get(e);return r?(Object.assign(r,t,{updatedAt:Date.now()}),r):null}async delete(e){return this.templates.delete(e)}registerDefaults(){this.templates.set("welcome",{id:w(),name:"welcome",subject:"Welcome to {{appName}}, {{user.name}}!",html:`
|
|
2
|
+
<h1>Welcome, {{user.name}}!</h1>
|
|
3
|
+
<p>Thank you for joining {{appName}}.</p>
|
|
4
|
+
<p>Your account has been created with the email: <strong>{{user.email}}</strong></p>
|
|
5
|
+
<p><a href="{{confirmUrl}}">Confirm your email address</a></p>
|
|
6
|
+
`,text:`Welcome, {{user.name}}!
|
|
7
|
+
|
|
8
|
+
Confirm your email: {{confirmUrl}}`,variables:["appName","user.name","user.email","confirmUrl"],category:"auth",active:!0,createdAt:Date.now(),updatedAt:Date.now()}),this.templates.set("password-reset",{id:w(),name:"password-reset",subject:"Reset your {{appName}} password",html:`
|
|
9
|
+
<h1>Password Reset</h1>
|
|
10
|
+
<p>Hi {{user.name}},</p>
|
|
11
|
+
<p>Click the link below to reset your password. This link expires in 1 hour.</p>
|
|
12
|
+
<p><a href="{{resetUrl}}">Reset Password</a></p>
|
|
13
|
+
<p>If you didn't request this, you can ignore this email.</p>
|
|
14
|
+
`,text:`Reset your password: {{resetUrl}}
|
|
15
|
+
|
|
16
|
+
This link expires in 1 hour.`,variables:["appName","user.name","resetUrl"],category:"auth",active:!0,createdAt:Date.now(),updatedAt:Date.now()}),this.templates.set("email-verification",{id:w(),name:"email-verification",subject:"Verify your email address",html:`
|
|
17
|
+
<h1>Verify Your Email</h1>
|
|
18
|
+
<p>Hi {{user.name}},</p>
|
|
19
|
+
<p><a href="{{verifyUrl}}">Click here to verify your email address</a></p>
|
|
20
|
+
<p>This link expires in 24 hours.</p>
|
|
21
|
+
`,text:"Verify your email: {{verifyUrl}}",variables:["user.name","verifyUrl"],category:"auth",active:!0,createdAt:Date.now(),updatedAt:Date.now()}),this.templates.set("team-invitation",{id:w(),name:"team-invitation",subject:"{{inviter.name}} invited you to join {{team.name}}",html:`
|
|
22
|
+
<h1>Team Invitation</h1>
|
|
23
|
+
<p>Hi {{user.name}},</p>
|
|
24
|
+
<p><strong>{{inviter.name}}</strong> has invited you to join the team <strong>{{team.name}}</strong>.</p>
|
|
25
|
+
<p><a href="{{acceptUrl}}">Accept Invitation</a></p>
|
|
26
|
+
<p>This invitation expires in 3 days.</p>
|
|
27
|
+
`,text:"Accept: {{acceptUrl}}",variables:["user.name","inviter.name","team.name","acceptUrl"],category:"notification",active:!0,createdAt:Date.now(),updatedAt:Date.now()}),this.templates.set("invoice",{id:w(),name:"invoice",subject:"Invoice #{{invoice.number}} from {{appName}}",html:`
|
|
28
|
+
<h1>Invoice #{{invoice.number}}</h1>
|
|
29
|
+
<p>Hi {{customer.name}},</p>
|
|
30
|
+
<p>Thank you for your purchase!</p>
|
|
31
|
+
<p><strong>Amount:</strong> {{invoice.amount}}</p>
|
|
32
|
+
<p><strong>Date:</strong> {{invoice.date}}</p>
|
|
33
|
+
<p><a href="{{invoiceUrl}}">View Invoice</a></p>
|
|
34
|
+
`,text:`Invoice #{{invoice.number}}
|
|
35
|
+
Amount: {{invoice.amount}}`,variables:["appName","customer.name","invoice.number","invoice.amount","invoice.date","invoiceUrl"],category:"billing",active:!0,createdAt:Date.now(),updatedAt:Date.now()}),this.templates.set("subscription-confirmation",{id:w(),name:"subscription-confirmation",subject:"Subscription Confirmation",html:`
|
|
36
|
+
<h1>Subscription Confirmed</h1>
|
|
37
|
+
<p>Hi {{user.name}},</p>
|
|
38
|
+
<p>Your {{plan.name}} plan is now active.</p>
|
|
39
|
+
<p><strong>Price:</strong> {{plan.price}} / {{plan.interval}}</p>
|
|
40
|
+
<p>Your next billing date is {{nextBillingDate}}.</p>
|
|
41
|
+
`,text:`Your {{plan.name}} plan is active.
|
|
42
|
+
Next billing: {{nextBillingDate}}`,variables:["user.name","plan.name","plan.price","plan.interval","nextBillingDate"],category:"billing",active:!0,createdAt:Date.now(),updatedAt:Date.now()}),this.templates.set("otp-code",{id:w(),name:"otp-code",subject:"Your {{appName}} verification code: {{code}}",html:`
|
|
43
|
+
<h1>Your Verification Code</h1>
|
|
44
|
+
<p>Hi {{user.name}},</p>
|
|
45
|
+
<p>Your one-time verification code is:</p>
|
|
46
|
+
<p style="font-size: 32px; font-weight: bold; letter-spacing: 8px; text-align: center; padding: 16px; background: #f3f4f6; border-radius: 8px; font-family: monospace;">{{code}}</p>
|
|
47
|
+
<p>This code expires in {{expiresMinutes}} minutes.</p>
|
|
48
|
+
<p>If you didn't request this code, you can safely ignore this email.</p>
|
|
49
|
+
`,text:`Your verification code: {{code}}
|
|
50
|
+
|
|
51
|
+
This code expires in {{expiresMinutes}} minutes.`,variables:["appName","user.name","code","expiresMinutes","purpose"],category:"auth",active:!0,createdAt:Date.now(),updatedAt:Date.now()}),this.templates.set("subscription-canceled",{id:w(),name:"subscription-canceled",subject:"Your subscription has been canceled",html:`
|
|
52
|
+
<h1>Subscription Canceled</h1>
|
|
53
|
+
<p>Hi {{user.name}},</p>
|
|
54
|
+
<p>Your {{plan.name}} subscription has been canceled.</p>
|
|
55
|
+
<p>You have access until {{accessUntilDate}}.</p>
|
|
56
|
+
`,text:`Your {{plan.name}} subscription is canceled.
|
|
57
|
+
Access until: {{accessUntilDate}}`,variables:["user.name","plan.name","accessUntilDate"],category:"billing",active:!0,createdAt:Date.now(),updatedAt:Date.now()})}interpolate(e,t){let r=e;return r=r.replace(/\{\{#if\s+(\w+(?:\.\w+)*)\}\}([\s\S]*?)\{\{\/if\}\}/g,(n,s,i)=>this.getNestedValue(t,s)?i:""),r=r.replace(/\{\{#each\s+(\w+(?:\.\w+)*)\}\}([\s\S]*?)\{\{\/each\}\}/g,(n,s,i)=>{let a=this.getNestedValue(t,s);return Array.isArray(a)?a.map((c,l)=>{let u={...t,this:c,$index:l};return this.interpolate(i,u)}).join(""):""}),r=r.replace(/\{\{([\w.$]+)\}\}/g,(n,s)=>{let i=this.getNestedValue(t,s);return i!=null?String(i):""}),r}getNestedValue(e,t){return t.split(".").reduce((r,n)=>r?.[n],e)}},ge=f("svelar.emailTemplates",()=>new O)});var Q={};R(Q,{Mailable:()=>I,Mailer:()=>fe});function L(o){return typeof o=="string"?o:`${o.name} <${o.address}>`}function g(o){return o?Array.isArray(o)?o:[o]:[]}function K(o){return Buffer.isBuffer(o)?o.toString("base64"):Buffer.from(o).toString("base64")}var k,$,U,_,j,I,H,fe,Z=T(()=>{"use strict";b();k=class{async send(e){let t=g(e.to);return console.log(`[Mail] To: ${t.join(", ")} | Subject: ${e.subject}`),e.text&&console.log(`[Mail] Body: ${e.text.slice(0,200)}`),{accepted:t,rejected:[]}}},$=class{async send(e){return{accepted:g(e.to),rejected:[]}}},U=class{constructor(e){this.config=e}async send(e){try{let n=await(await import("nodemailer")).createTransport({host:this.config.host,port:this.config.port??587,secure:this.config.secure??!1,auth:this.config.auth}).sendMail({from:e.from?L(e.from):void 0,to:g(e.to).join(", "),cc:g(e.cc).join(", ")||void 0,bcc:g(e.bcc).join(", ")||void 0,replyTo:e.replyTo,subject:e.subject,text:e.text,html:e.html,attachments:e.attachments});return{accepted:n.accepted,rejected:n.rejected,messageId:n.messageId}}catch(t){throw t.code==="MODULE_NOT_FOUND"?new Error("SMTP driver requires nodemailer. Install: npm install nodemailer"):t}}},_=class{constructor(e){this.config=e}async send(e){let t=this.config.apiToken;if(!t)throw new Error("Postmark apiToken is required. Set it in your mailer config or POSTMARK_API_TOKEN env var.");let r=g(e.to),n=g(e.cc),s=g(e.bcc),i={From:e.from?L(e.from):void 0,To:r.join(", "),Subject:e.subject,MessageStream:this.config.messageStream||"outbound"};n.length>0&&(i.Cc=n.join(", ")),s.length>0&&(i.Bcc=s.join(", ")),e.replyTo&&(i.ReplyTo=e.replyTo),e.html&&(i.HtmlBody=e.html),e.text&&(i.TextBody=e.text),!i.HtmlBody&&!i.TextBody&&(i.TextBody=""),e.tags&&(i.Tag=Object.values(e.tags)[0]),e.attachments?.length&&(i.Attachments=e.attachments.map(l=>({Name:l.filename,Content:K(l.content),ContentType:l.contentType||"application/octet-stream"})));let a=await fetch("https://api.postmarkapp.com/email",{method:"POST",headers:{Accept:"application/json","Content-Type":"application/json","X-Postmark-Server-Token":t},body:JSON.stringify(i)});if(!a.ok){let l=await a.json().catch(()=>({Message:a.statusText}));throw new Error(`Postmark error ${a.status}: ${l.Message||JSON.stringify(l)}`)}let c=await a.json();return{accepted:r,rejected:[],messageId:c.MessageID}}},j=class{constructor(e){this.config=e}async send(e){let t=this.config.apiKey;if(!t)throw new Error("Resend apiKey is required. Set it in your mailer config or RESEND_API_KEY env var.");let r=g(e.to),n=g(e.cc),s=g(e.bcc),i={from:e.from?L(e.from):void 0,to:r,subject:e.subject};n.length>0&&(i.cc=n),s.length>0&&(i.bcc=s),e.replyTo&&(i.reply_to=[e.replyTo]),e.html&&(i.html=e.html),e.text&&(i.text=e.text),e.tags&&(i.tags=Object.entries(e.tags).map(([l,u])=>({name:l,value:u}))),e.attachments?.length&&(i.attachments=e.attachments.map(l=>({filename:l.filename,content:K(l.content),content_type:l.contentType||"application/octet-stream"})));let a=await fetch("https://api.resend.com/emails",{method:"POST",headers:{Authorization:`Bearer ${t}`,"Content-Type":"application/json"},body:JSON.stringify(i)});if(!a.ok){let l=await a.json().catch(()=>({message:a.statusText}));throw new Error(`Resend error ${a.status}: ${l.message||JSON.stringify(l)}`)}let c=await a.json();return{accepted:r,rejected:[],messageId:c.id}}},I=class{message={};to(e){return this.message.to=e,this}cc(e){return this.message.cc=e,this}bcc(e){return this.message.bcc=e,this}from(e){return this.message.from=e,this}replyTo(e){return this.message.replyTo=e,this}subject(e){return this.message.subject=e,this}text(e){return this.message.text=e,this}html(e){return this.message.html=e,this}attach(e,t,r){return this.message.attachments||(this.message.attachments=[]),this.message.attachments.push({filename:e,content:t,contentType:r}),this}tag(e,t){return this.message.tags||(this.message.tags={}),this.message.tags[e]=t,this}toMessage(){return this.message}},H=class{config=null;transports=new Map;configure(e){this.config=e,this.transports.clear()}async send(e,t){let r=this.resolveTransport(t);return!e.from&&this.config?.from&&(e.from=this.config.from),r.send(e)}async sendMailable(e,t){e.build();let r=e.toMessage();return!r.from&&this.config?.from&&(r.from=this.config.from),this.send(r,t)}mailer(e){let t=this.resolveTransport(e);return{send:r=>(!r.from&&this.config?.from&&(r.from=this.config.from),t.send(r))}}resolveTransport(e){let t=e??this.config?.default??"log";if(this.transports.has(t))return this.transports.get(t);if(!this.config){let s=new k;return this.transports.set(t,s),s}let r=this.config.mailers[t];if(!r)throw new Error(`Mailer "${t}" is not defined.`);let n;switch(r.driver){case"smtp":n=new U(r);break;case"postmark":n=new _(r);break;case"resend":n=new j(r);break;case"log":n=new k;break;case"null":n=new $;break;case"custom":{if(!r.transport)throw new Error(`Custom mail driver "${t}" requires a "transport" instance.`);n=r.transport;break}default:throw new Error(`Unknown mail driver: ${r.driver}`)}return this.transports.set(t,n),n}},fe=f("svelar.mail",()=>new H)});import{createHmac as v,randomBytes as E}from"crypto";var y=class{};function ee(o){return(typeof o=="string"?Buffer.from(o):o).toString("base64url")}function te(o){return Buffer.from(o,"base64url").toString("utf-8")}function re(o,e,t,r){return v(r==="HS384"?"sha384":r==="HS512"?"sha512":"sha256",t).update(`${o}.${e}`).digest("base64url")}function ne(o,e,t="HS256"){let r=ee(JSON.stringify({alg:t,typ:"JWT"})),n=ee(JSON.stringify(o)),s=re(r,n,e,t);return`${r}.${n}.${s}`}function se(o,e){let t=o.split(".");if(t.length!==3)return null;let[r,n,s]=t,i;try{i=JSON.parse(te(r))}catch{return null}let a=re(r,n,e,i.alg);if(s!==a)return null;try{let c=JSON.parse(te(n));return c.exp&&Date.now()/1e3>c.exp?null:c}catch{return null}}var q=class{config;currentUser=null;constructor(e){this.config={identifierColumn:"email",passwordColumn:"password",...e}}async attempt(e,t){let{Hash:r}=await Promise.resolve().then(()=>(x(),C)),n=e[this.config.identifierColumn],s=e[this.config.passwordColumn];if(!n||!s)return null;let i=await this.config.model.where(this.config.identifierColumn,n).first();if(!i)return null;let a=i.getAttribute(this.config.passwordColumn);return await r.verify(s,a)?(this.currentUser=i,t&&(t.set("auth_user_id",i.getAttribute("id")),t.regenerateId()),i):null}async attemptJwt(e){let{Hash:t}=await Promise.resolve().then(()=>(x(),C));if(!this.config.jwt)throw new Error("JWT configuration required for JWT guard.");let r=e[this.config.identifierColumn],n=e[this.config.passwordColumn];if(!r||!n)return null;let s=await this.config.model.where(this.config.identifierColumn,r).first();if(!s)return null;let i=s.getAttribute(this.config.passwordColumn);return await t.verify(n,i)?(this.currentUser=s,this.issueTokenPair(s)):null}async issueTokenPair(e){let t=this.config.jwt,r=t.expiresIn??3600,n=Math.floor(Date.now()/1e3),s={sub:e.getAttribute("id"),iat:n,exp:n+r,...t.issuer?{iss:t.issuer}:{}},i=ne(s,t.secret,t.algorithm),a=new Date((n+r)*1e3),c={user:e,token:i,expiresAt:a};if(t.refreshTokens){let l=t.refreshExpiresIn??604800,u=E(32).toString("base64url"),m=v("sha256",t.secret).update(u).digest("hex"),P=new Date((n+l)*1e3),{Connection:ae}=await Promise.resolve().then(()=>(h(),d)),oe=t.refreshTable??"refresh_tokens";await ae.raw(`INSERT INTO ${oe} (user_id, token, expires_at, created_at) VALUES (?, ?, ?, ?)`,[e.getAttribute("id"),m,P.toISOString(),new Date().toISOString()]),c.refreshToken=u,c.refreshExpiresAt=P}return c}async refreshJwt(e){if(!this.config.jwt)throw new Error("JWT configuration required.");if(!this.config.jwt.refreshTokens)throw new Error("Refresh tokens are not enabled. Set jwt.refreshTokens = true.");let t=this.config.jwt,r=v("sha256",t.secret).update(e).digest("hex"),{Connection:n}=await Promise.resolve().then(()=>(h(),d)),s=t.refreshTable??"refresh_tokens",i=await n.raw(`SELECT user_id, expires_at, revoked_at FROM ${s} WHERE token = ?`,[r]);if(i.length===0)return null;let a=i[0];if(a.revoked_at||new Date(a.expires_at)<new Date)return null;await n.raw(`UPDATE ${s} SET revoked_at = ? WHERE token = ?`,[new Date().toISOString(),r]);let c=await this.config.model.find(a.user_id);return c?(this.currentUser=c,this.issueTokenPair(c)):null}async revokeRefreshTokens(e){if(!this.config.jwt?.refreshTokens)return;let{Connection:t}=await Promise.resolve().then(()=>(h(),d)),r=this.config.jwt.refreshTable??"refresh_tokens";await t.raw(`UPDATE ${r} SET revoked_at = ? WHERE user_id = ? AND revoked_at IS NULL`,[new Date().toISOString(),e])}async resolveFromToken(e){if(!this.config.jwt)throw new Error("JWT configuration required.");let t=se(e,this.config.jwt.secret);if(!t)return null;let r=await this.config.model.find(t.sub);return r&&(this.currentUser=r),r}async resolveFromSession(e){let t=e.get("auth_user_id");if(!t)return null;let r=await this.config.model.find(t);return r&&(this.currentUser=r),r}async register(e){let{Hash:t}=await Promise.resolve().then(()=>(x(),C));e[this.config.passwordColumn]&&(e[this.config.passwordColumn]=await t.make(e[this.config.passwordColumn]));let r=await this.config.model.create(e);return this.currentUser=r,r}async logout(e){this.currentUser=null,e&&(e.forget("auth_user_id"),e.regenerateId())}user(){return this.currentUser}check(){return this.currentUser!==null}id(){return this.currentUser?.getAttribute("id")??null}async generateApiToken(e,t="default"){let r=E(32).toString("hex"),n=this.config.jwt?.secret??process.env.APP_KEY;if(!n)throw new Error("APP_KEY is not set. Set it in your .env file or pass jwt.secret in auth config.");let s=v("sha256",n).update(r).digest("hex"),{Connection:i}=await Promise.resolve().then(()=>(h(),d)),a=this.config.token?.table??"personal_access_tokens";return await i.raw(`INSERT INTO ${a} (user_id, name, token, created_at) VALUES (?, ?, ?, ?)`,[e.getAttribute("id"),t,s,new Date().toISOString()]),r}async resolveFromApiToken(e){let{Connection:t}=await Promise.resolve().then(()=>(h(),d)),r=this.config.token?.table??"personal_access_tokens",n=this.config.jwt?.secret??process.env.APP_KEY;if(!n)throw new Error("APP_KEY is not set. Set it in your .env file or pass jwt.secret in auth config.");let s=v("sha256",n).update(e).digest("hex"),i=await t.raw(`SELECT user_id FROM ${r} WHERE token = ?`,[s]);if(i.length===0)return null;let a=await this.config.model.find(i[0].user_id);return a&&(this.currentUser=a),a}async sendPasswordReset(e){let t=await this.config.model.where(this.config.identifierColumn,e).first();if(!t)return!1;let{Connection:r}=await Promise.resolve().then(()=>(h(),d)),n=this.config.passwordResets?.table??"password_resets",s=this.config.passwordResets?.expiresIn??3600;await this.ensureTable(n,`
|
|
58
|
+
CREATE TABLE IF NOT EXISTS ${n} (
|
|
59
|
+
email TEXT NOT NULL,
|
|
60
|
+
token TEXT NOT NULL,
|
|
61
|
+
expires_at TEXT NOT NULL,
|
|
62
|
+
created_at TEXT NOT NULL
|
|
63
|
+
)
|
|
64
|
+
`),await r.raw(`DELETE FROM ${n} WHERE email = ?`,[e]);let i=E(32).toString("base64url"),a=this.hashToken(i),c=new Date(Date.now()+s*1e3).toISOString();await r.raw(`INSERT INTO ${n} (email, token, expires_at, created_at) VALUES (?, ?, ?, ?)`,[e,a,c,new Date().toISOString()]);let l=this.config.appUrl??process.env.APP_URL??"http://localhost:5173",u=this.config.appName??process.env.APP_NAME??"Svelar",m=`${l}/reset-password?token=${i}&email=${encodeURIComponent(e)}`;return await this.sendAuthEmail("password-reset",e,{appName:u,"user.name":t.getAttribute("name")??e,resetUrl:m}),!0}async resetPassword(e,t,r){let{Connection:n}=await Promise.resolve().then(()=>(h(),d)),{Hash:s}=await Promise.resolve().then(()=>(x(),C)),i=this.config.passwordResets?.table??"password_resets",a=this.hashToken(e),c=await n.raw(`SELECT email, expires_at FROM ${i} WHERE token = ? AND email = ?`,[a,t]);if(c.length===0)return!1;let l=c[0];if(new Date(l.expires_at)<new Date)return await n.raw(`DELETE FROM ${i} WHERE email = ?`,[t]),!1;let u=await this.config.model.where(this.config.identifierColumn,t).first();if(!u)return!1;let m=await s.make(r);return await this.config.model.where("id",u.getAttribute("id")).update({[this.config.passwordColumn]:m}),await n.raw(`DELETE FROM ${i} WHERE email = ?`,[t]),await this.revokeRefreshTokens(u.getAttribute("id")),!0}async sendVerificationEmail(e){let{Connection:t}=await Promise.resolve().then(()=>(h(),d)),r=this.config.emailVerification?.table??"email_verifications",n=this.config.emailVerification?.expiresIn??86400,s=e.getAttribute(this.config.identifierColumn);await this.ensureTable(r,`
|
|
65
|
+
CREATE TABLE IF NOT EXISTS ${r} (
|
|
66
|
+
user_id TEXT NOT NULL,
|
|
67
|
+
token TEXT NOT NULL,
|
|
68
|
+
expires_at TEXT NOT NULL,
|
|
69
|
+
created_at TEXT NOT NULL
|
|
70
|
+
)
|
|
71
|
+
`),await t.raw(`DELETE FROM ${r} WHERE user_id = ?`,[e.getAttribute("id")]);let i=E(32).toString("base64url"),a=this.hashToken(i),c=new Date(Date.now()+n*1e3).toISOString();await t.raw(`INSERT INTO ${r} (user_id, token, expires_at, created_at) VALUES (?, ?, ?, ?)`,[e.getAttribute("id"),a,c,new Date().toISOString()]);let u=`${this.config.appUrl??process.env.APP_URL??"http://localhost:5173"}/verify-email?token=${i}&id=${e.getAttribute("id")}`;await this.sendAuthEmail("email-verification",s,{"user.name":e.getAttribute("name")??s,verifyUrl:u})}async verifyEmail(e,t){let{Connection:r}=await Promise.resolve().then(()=>(h(),d)),n=this.config.emailVerification?.table??"email_verifications",s=this.config.emailVerification?.verifiedColumn??"email_verified_at",i=this.hashToken(e),a=await r.raw(`SELECT user_id, expires_at FROM ${n} WHERE token = ? AND user_id = ?`,[i,t]);return a.length===0?!1:new Date(a[0].expires_at)<new Date?(await r.raw(`DELETE FROM ${n} WHERE user_id = ?`,[t]),!1):(await this.config.model.where("id",t).update({[s]:new Date().toISOString()}),await r.raw(`DELETE FROM ${n} WHERE user_id = ?`,[t]),!0)}isEmailVerified(e){let t=this.config.emailVerification?.verifiedColumn??"email_verified_at";return!!e.getAttribute(t)}async sendOtp(e,t="login"){let r=await this.config.model.where(this.config.identifierColumn,e).first();if(!r)return!1;let{Connection:n}=await Promise.resolve().then(()=>(h(),d)),s=this.config.otp?.table??"otp_codes",i=this.config.otp?.expiresIn??600,a=this.config.otp?.length??6;await this.ensureTable(s,`
|
|
72
|
+
CREATE TABLE IF NOT EXISTS ${s} (
|
|
73
|
+
email TEXT NOT NULL,
|
|
74
|
+
code TEXT NOT NULL,
|
|
75
|
+
purpose TEXT NOT NULL DEFAULT 'login',
|
|
76
|
+
expires_at TEXT NOT NULL,
|
|
77
|
+
used_at TEXT,
|
|
78
|
+
created_at TEXT NOT NULL
|
|
79
|
+
)
|
|
80
|
+
`),await n.raw(`DELETE FROM ${s} WHERE email = ? AND purpose = ?`,[e,t]);let c=this.generateOtpCode(a),l=this.hashToken(c),u=new Date(Date.now()+i*1e3).toISOString();await n.raw(`INSERT INTO ${s} (email, code, purpose, expires_at, created_at) VALUES (?, ?, ?, ?, ?)`,[e,l,t,u,new Date().toISOString()]);let m=this.config.appName??process.env.APP_NAME??"Svelar",P=Math.ceil(i/60);return await this.sendAuthEmail("otp-code",e,{appName:m,"user.name":r.getAttribute("name")??e,code:c,purpose:t,expiresMinutes:String(P)}),!0}async verifyOtp(e,t,r="login"){let{Connection:n}=await Promise.resolve().then(()=>(h(),d)),s=this.config.otp?.table??"otp_codes",i=this.hashToken(t),a=await n.raw(`SELECT email, expires_at, used_at FROM ${s} WHERE code = ? AND email = ? AND purpose = ? AND used_at IS NULL`,[i,e,r]);if(a.length===0)return null;if(new Date(a[0].expires_at)<new Date)return await n.raw(`DELETE FROM ${s} WHERE email = ? AND purpose = ?`,[e,r]),null;await n.raw(`UPDATE ${s} SET used_at = ? WHERE code = ? AND email = ? AND purpose = ?`,[new Date().toISOString(),i,e,r]);let c=await this.config.model.where(this.config.identifierColumn,e).first();return c&&(this.currentUser=c),c}async attemptOtp(e,t,r,n="login"){let s=await this.verifyOtp(e,t,n);return s?(r&&(r.set("auth_user_id",s.getAttribute("id")),r.regenerateId()),s):null}async cleanupExpiredTokens(){let{Connection:e}=await Promise.resolve().then(()=>(h(),d)),t=new Date().toISOString(),r=0,n=0,s=0,i=this.config.passwordResets?.table??"password_resets",a=this.config.emailVerification?.table??"email_verifications",c=this.config.otp?.table??"otp_codes";try{r=(await e.raw(`DELETE FROM ${i} WHERE expires_at < ?`,[t]))?.changes??0}catch{}try{n=(await e.raw(`DELETE FROM ${a} WHERE expires_at < ?`,[t]))?.changes??0}catch{}try{s=(await e.raw(`DELETE FROM ${c} WHERE expires_at < ? OR used_at IS NOT NULL`,[t]))?.changes??0}catch{}return{passwordResets:r,verifications:n,otpCodes:s}}hashToken(e){let t=this.config.jwt?.secret??process.env.APP_KEY;if(!t)throw new Error("APP_KEY is not set. Set it in your .env file or pass jwt.secret in auth config.");return v("sha256",t).update(e).digest("hex")}generateOtpCode(e){let t=E(e);return Array.from(t).map(r=>(r%10).toString()).join("")}tablesEnsured=new Set;async ensureTable(e,t){if(this.tablesEnsured.has(e))return;let{Connection:r}=await Promise.resolve().then(()=>(h(),d));await r.raw(t),this.tablesEnsured.add(e)}async sendAuthEmail(e,t,r){try{let{EmailTemplates:n}=await Promise.resolve().then(()=>(Y(),X)),{Mailer:s}=await Promise.resolve().then(()=>(Z(),Q)),i=await n.render(e,r);await s.send({to:t,subject:i.subject,html:i.html,text:i.text})}catch(n){console.error(`[Auth] Failed to send ${e} email to ${t}:`,n.message)}}},F=class extends y{constructor(t){super();this.authManager=t}async handle(t,r){let n=null;if(t.event.locals.session&&(n=await this.authManager.resolveFromSession(t.event.locals.session)),!n){let s=t.event.request.headers.get("authorization");if(s?.startsWith("Bearer ")){let i=s.slice(7);try{n=await this.authManager.resolveFromToken(i)}catch{n=await this.authManager.resolveFromApiToken(i)}}}return t.event.locals.user=n,t.event.locals.auth=this.authManager,r()}},W=class extends y{async handle(e,t){return e.event.locals.user?t():new Response(JSON.stringify({message:"Unauthenticated"}),{status:401,headers:{"Content-Type":"application/json"}})}},J=class extends y{constructor(t="/login"){super();this.redirectTo=t}async handle(t,r){if(!t.event.locals.user){let n=new URL(this.redirectTo,t.event.url.origin);return new Response(null,{status:302,headers:{Location:n.pathname}})}return r()}};function me(o="/login",e){return async t=>{let r=t.locals.user;if(!r){let{redirect:n}=await import("@sveltejs/kit");throw n(302,o)}if(e?.role&&r.role!==e.role){let{redirect:n}=await import("@sveltejs/kit");throw n(302,o)}return{}}}b();var p=class o{constructor(e,t,r){this.allowed=e;this.message=t;this.code=r}static allow(e){return new o(!0,e)}static deny(e="This action is unauthorized.",t=403){return new o(!1,e,t)}toResponse(){return this.allowed?new Response(null,{status:200}):new Response(JSON.stringify({message:this.message}),{status:this.code??403,headers:{"Content-Type":"application/json"}})}},A=class extends Error{statusCode;constructor(e="This action is unauthorized.",t=403){super(e),this.name="AuthorizationError",this.statusCode=t}toResponse(){return new Response(JSON.stringify({message:this.message}),{status:this.statusCode,headers:{"Content-Type":"application/json"}})}},B=class{},z=class{gates=new Map;policies=new Map;policyModelMap=new Map;beforeCallbacks=[];afterCallbacks=[];superUserCallback;define(e,t){return this.gates.set(e,t),this}policy(e,t){return this.policies.set(e,t),this}before(e){return this.beforeCallbacks.push(e),this}after(e){return this.afterCallbacks.push(e),this}defineSuperUser(e){return this.superUserCallback=e,this}async allows(e,t,...r){return await this.check(e,t,...r)}async denies(e,t,...r){return!await this.allows(e,t,...r)}async authorize(e,t,...r){let n=await this.inspect(e,t,...r);if(!n.allowed)throw new A(n.message,n.code)}async inspect(e,t,...r){if(!t)return p.deny("Unauthenticated.",401);if(this.superUserCallback&&this.superUserCallback(t))return p.allow();for(let i of this.beforeCallbacks){let a=await i(t,e);if(a===!0)return p.allow();if(a===!1)return p.deny()}let n=await this.checkPolicy(e,t,...r);if(n!==null)return n;let s=this.gates.get(e);if(s){let i=await s(t,...r);return i instanceof p?i:i?p.allow():p.deny()}return p.deny(`No gate or policy defined for ability "${e}".`)}forUser(e){return new S(this,e)}has(e){if(this.gates.has(e))return!0;for(let t of this.policies.values())if(typeof t[e]=="function")return!0;return!1}getPolicyFor(e){let t=typeof e=="string"?e:e?.constructor?.name??String(e);return this.policies.get(t)}async check(e,t,...r){let s=(await this.inspect(e,t,...r)).allowed;for(let i of this.afterCallbacks){let a=await i(t,e,s,...r);typeof a=="boolean"&&(s=a)}return s}async checkPolicy(e,t,...r){let n;if(r.length>0&&r[0]){let a=r[0]?.constructor?.name;a&&(n=this.policies.get(a))}if(!n){for(let a of this.policies.values())if(typeof a[e]=="function"){n=a;break}}if(!n)return null;if(n.before){let a=await n.before(t,e);if(a===!0)return p.allow();if(a===!1)return p.deny()}let s=n[e];if(typeof s!="function")return null;let i=await s.call(n,t,...r);return i instanceof p?i:i?p.allow():p.deny()}},S=class{constructor(e,t){this.gate=e;this.user=t}async allows(e,...t){return this.gate.allows(e,this.user,...t)}async denies(e,...t){return this.gate.denies(e,this.user,...t)}async authorize(e,...t){return this.gate.authorize(e,this.user,...t)}async inspect(e,...t){return this.gate.inspect(e,this.user,...t)}async any(e,...t){for(let r of e)if(await this.allows(r,...t))return!0;return!1}async all(e,...t){for(let r of e)if(await this.denies(r,...t))return!1;return!0}},G=class extends y{constructor(t,r){super();this.ability=t;this.modelResolver=r}async handle(t,r){let n=t.event.locals.user;if(!n)return new Response(JSON.stringify({message:"Unauthenticated"}),{status:401,headers:{"Content-Type":"application/json"}});let s=this.modelResolver?this.modelResolver(t):void 0,i=s?[s]:[];try{await ie.authorize(this.ability,n,...i)}catch(a){if(a instanceof A)return a.toResponse();throw a}return r()}},ie=f("svelar.gate",()=>new z);export{q as AuthManager,F as AuthenticateMiddleware,A as AuthorizationError,ie as Gate,G as GateMiddleware,p as GateResponse,B as Policy,J as RedirectIfNotAuthenticated,W as RequireAuthMiddleware,S as UserGate,me as guardAuth,ne as signJwt,se as verifyJwt};
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Svelar Broadcasting — Client-Side Helper
|
|
3
|
+
*
|
|
4
|
+
* Provides a reactive Svelte-friendly wrapper around pusher-js for
|
|
5
|
+
* real-time WebSocket communication with Soketi/Pusher, plus an SSE
|
|
6
|
+
* client for the zero-dependency driver.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```svelte
|
|
10
|
+
* <script>
|
|
11
|
+
* import { usePusher, useChannel, usePresenceChannel } from 'svelar/broadcasting/client';
|
|
12
|
+
*
|
|
13
|
+
* // Initialize once (e.g. in +layout.svelte)
|
|
14
|
+
* const echo = usePusher({
|
|
15
|
+
* key: 'svelar-key',
|
|
16
|
+
* wsHost: 'localhost',
|
|
17
|
+
* wsPort: 6001,
|
|
18
|
+
* forceTLS: false,
|
|
19
|
+
* authEndpoint: '/api/broadcasting/auth',
|
|
20
|
+
* });
|
|
21
|
+
*
|
|
22
|
+
* // Subscribe to channels
|
|
23
|
+
* const orders = useChannel('private-orders.123');
|
|
24
|
+
* const chat = usePresenceChannel('presence-chat.5');
|
|
25
|
+
*
|
|
26
|
+
* // Listen for events
|
|
27
|
+
* $effect(() => {
|
|
28
|
+
* orders.listen('OrderShipped', (data) => {
|
|
29
|
+
* console.log('Shipped:', data);
|
|
30
|
+
* });
|
|
31
|
+
* });
|
|
32
|
+
* </script>
|
|
33
|
+
* ```
|
|
34
|
+
*
|
|
35
|
+
* @module svelar/broadcasting/client
|
|
36
|
+
*/
|
|
37
|
+
export interface PusherOptions {
|
|
38
|
+
/** Pusher/Soketi app key */
|
|
39
|
+
key: string;
|
|
40
|
+
/** Pusher cluster (ignored when wsHost is set) */
|
|
41
|
+
cluster?: string;
|
|
42
|
+
/** WebSocket host (e.g. 'localhost' for Soketi) */
|
|
43
|
+
wsHost?: string;
|
|
44
|
+
/** WebSocket port (e.g. 6001 for Soketi) */
|
|
45
|
+
wsPort?: number;
|
|
46
|
+
/** Secure WebSocket port */
|
|
47
|
+
wssPort?: number;
|
|
48
|
+
/** Force TLS (default: false for Soketi) */
|
|
49
|
+
forceTLS?: boolean;
|
|
50
|
+
/** Auth endpoint for private/presence channels */
|
|
51
|
+
authEndpoint?: string;
|
|
52
|
+
/** Custom auth headers */
|
|
53
|
+
authHeaders?: Record<string, string>;
|
|
54
|
+
/** Enable debug logging */
|
|
55
|
+
debug?: boolean;
|
|
56
|
+
/** Enabled transports (default: ['ws', 'wss']) */
|
|
57
|
+
enabledTransports?: string[];
|
|
58
|
+
}
|
|
59
|
+
export interface ChannelInstance {
|
|
60
|
+
/** Listen for an event on this channel */
|
|
61
|
+
listen(event: string, callback: (data: any) => void): ChannelInstance;
|
|
62
|
+
/** Stop listening for a specific event */
|
|
63
|
+
stopListening(event: string): ChannelInstance;
|
|
64
|
+
/** Send a client event (whisper) to other channel members */
|
|
65
|
+
whisper(event: string, data: any): ChannelInstance;
|
|
66
|
+
/** Unsubscribe from this channel */
|
|
67
|
+
leave(): void;
|
|
68
|
+
/** The raw pusher-js channel object */
|
|
69
|
+
readonly raw: any;
|
|
70
|
+
}
|
|
71
|
+
export interface PresenceChannelInstance extends ChannelInstance {
|
|
72
|
+
/** Called when the initial member list is received */
|
|
73
|
+
here(callback: (members: any[]) => void): PresenceChannelInstance;
|
|
74
|
+
/** Called when a new member joins */
|
|
75
|
+
joining(callback: (member: any) => void): PresenceChannelInstance;
|
|
76
|
+
/** Called when a member leaves */
|
|
77
|
+
leaving(callback: (member: any) => void): PresenceChannelInstance;
|
|
78
|
+
}
|
|
79
|
+
export interface SSEChannelInstance {
|
|
80
|
+
/** Listen for a named event */
|
|
81
|
+
listen(event: string, callback: (data: any) => void): SSEChannelInstance;
|
|
82
|
+
/** Stop listening for a specific event */
|
|
83
|
+
stopListening(event: string): SSEChannelInstance;
|
|
84
|
+
/** Close the SSE connection */
|
|
85
|
+
close(): void;
|
|
86
|
+
/** The raw EventSource object */
|
|
87
|
+
readonly raw: EventSource;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Initialize and return a Pusher client singleton.
|
|
91
|
+
* Call this once in your root layout or app init.
|
|
92
|
+
*
|
|
93
|
+
* Requires `pusher-js` to be installed: `npm install pusher-js`
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* ```svelte
|
|
97
|
+
* <!-- +layout.svelte -->
|
|
98
|
+
* <script>
|
|
99
|
+
* import { usePusher } from 'svelar/broadcasting/client';
|
|
100
|
+
*
|
|
101
|
+
* const echo = usePusher({
|
|
102
|
+
* key: 'svelar-key',
|
|
103
|
+
* wsHost: 'localhost',
|
|
104
|
+
* wsPort: 6001,
|
|
105
|
+
* forceTLS: false,
|
|
106
|
+
* authEndpoint: '/api/broadcasting/auth',
|
|
107
|
+
* });
|
|
108
|
+
* </script>
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
export declare function usePusher(options: PusherOptions): {
|
|
112
|
+
disconnect: () => void;
|
|
113
|
+
raw: any;
|
|
114
|
+
};
|
|
115
|
+
/**
|
|
116
|
+
* Get the current Pusher instance (must call usePusher first).
|
|
117
|
+
*/
|
|
118
|
+
export declare function getPusher(): any;
|
|
119
|
+
/**
|
|
120
|
+
* Subscribe to a channel (public or private).
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* ```svelte
|
|
124
|
+
* <script>
|
|
125
|
+
* import { useChannel } from 'svelar/broadcasting/client';
|
|
126
|
+
*
|
|
127
|
+
* const channel = useChannel('private-orders.123');
|
|
128
|
+
*
|
|
129
|
+
* $effect(() => {
|
|
130
|
+
* channel.listen('OrderShipped', (data) => {
|
|
131
|
+
* console.log('Shipped!', data);
|
|
132
|
+
* });
|
|
133
|
+
*
|
|
134
|
+
* return () => channel.leave();
|
|
135
|
+
* });
|
|
136
|
+
* </script>
|
|
137
|
+
* ```
|
|
138
|
+
*/
|
|
139
|
+
export declare function useChannel(channelName: string): ChannelInstance;
|
|
140
|
+
/**
|
|
141
|
+
* Subscribe to a presence channel with member tracking.
|
|
142
|
+
*
|
|
143
|
+
* @example
|
|
144
|
+
* ```svelte
|
|
145
|
+
* <script>
|
|
146
|
+
* import { usePresenceChannel } from 'svelar/broadcasting/client';
|
|
147
|
+
*
|
|
148
|
+
* let members = $state([]);
|
|
149
|
+
*
|
|
150
|
+
* const chat = usePresenceChannel('presence-chat.5');
|
|
151
|
+
*
|
|
152
|
+
* $effect(() => {
|
|
153
|
+
* chat
|
|
154
|
+
* .here((m) => { members = m; })
|
|
155
|
+
* .joining((m) => { members = [...members, m]; })
|
|
156
|
+
* .leaving((m) => { members = members.filter(x => x.id !== m.id); })
|
|
157
|
+
* .listen('new-message', (data) => {
|
|
158
|
+
* console.log('Message:', data);
|
|
159
|
+
* });
|
|
160
|
+
*
|
|
161
|
+
* return () => chat.leave();
|
|
162
|
+
* });
|
|
163
|
+
* </script>
|
|
164
|
+
* ```
|
|
165
|
+
*/
|
|
166
|
+
export declare function usePresenceChannel(channelName: string): PresenceChannelInstance;
|
|
167
|
+
/**
|
|
168
|
+
* Leave (unsubscribe from) a channel.
|
|
169
|
+
*/
|
|
170
|
+
export declare function leaveChannel(channelName: string): void;
|
|
171
|
+
/**
|
|
172
|
+
* Subscribe to an SSE channel (for the SSE broadcast driver).
|
|
173
|
+
* No pusher-js dependency needed.
|
|
174
|
+
*
|
|
175
|
+
* @param channelName - The channel to subscribe to
|
|
176
|
+
* @param baseUrl - Base URL for the SSE endpoint (default: '/api/broadcasting')
|
|
177
|
+
*
|
|
178
|
+
* @example
|
|
179
|
+
* ```svelte
|
|
180
|
+
* <script>
|
|
181
|
+
* import { useSSE } from 'svelar/broadcasting/client';
|
|
182
|
+
*
|
|
183
|
+
* const channel = useSSE('private-orders.123');
|
|
184
|
+
*
|
|
185
|
+
* $effect(() => {
|
|
186
|
+
* channel.listen('OrderShipped', (data) => {
|
|
187
|
+
* console.log('Shipped!', data);
|
|
188
|
+
* });
|
|
189
|
+
*
|
|
190
|
+
* return () => channel.close();
|
|
191
|
+
* });
|
|
192
|
+
* </script>
|
|
193
|
+
* ```
|
|
194
|
+
*/
|
|
195
|
+
export declare function useSSE(channelName: string, baseUrl?: string): SSEChannelInstance;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var d=(e=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(e,{get:(t,n)=>(typeof require<"u"?require:t)[n]}):e)(function(e){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+e+'" is not supported')});var i=null,g=null;function p(e){if(i)return{disconnect:()=>i?.disconnect(),raw:i};g=e;let t;try{t=globalThis.__svelar_pusher??d("pusher-js")}catch{throw new Error("pusher-js is required for WebSocket broadcasting. Install it with: npm install pusher-js")}let n={cluster:e.cluster??"mt1",authEndpoint:e.authEndpoint??"/api/broadcasting/auth"};return e.wsHost&&(n.wsHost=e.wsHost,n.wsPort=e.wsPort??6001,n.wssPort=e.wssPort??e.wsPort??6001,n.forceTLS=e.forceTLS??!1,n.enabledTransports=e.enabledTransports??["ws","wss"],n.disableStats=!0),e.authHeaders&&(n.auth={headers:e.authHeaders}),e.debug&&(t.logToConsole=!0),i=new t(e.key,n),{disconnect:()=>{i?.disconnect(),i=null},raw:i}}function u(){if(!i)throw new Error("Pusher not initialized. Call usePusher() first.");return i}function v(e){let n=u().subscribe(e);return h(n,e)}function y(e){let t=u(),n=e.startsWith("presence-")?e:`presence-${e}`,s=t.subscribe(n);return{...h(s,n),here(r){return s.bind("pusher:subscription_succeeded",a=>{let o=[];a.each(l=>o.push(l.info)),r(o)}),this},joining(r){return s.bind("pusher:member_added",a=>{r(a.info)}),this},leaving(r){return s.bind("pusher:member_removed",a=>{r(a.info)}),this}}}function C(e){u().unsubscribe(e)}function w(e,t="/api/broadcasting"){let n=`${t}/${encodeURIComponent(e)}`,s=new EventSource(n),c=new Map;return{listen(r,a){let o=l=>{try{a(JSON.parse(l.data))}catch{a(l.data)}};return c.set(r,o),s.addEventListener(r,o),this},stopListening(r){let a=c.get(r);return a&&(s.removeEventListener(r,a),c.delete(r)),this},close(){s.close(),c.clear()},get raw(){return s}}}function h(e,t){return{listen(n,s){return e.bind(n,s),this},stopListening(n){return e.unbind(n),this},whisper(n,s){return e.trigger(`client-${n}`,s),this},leave(){i&&i.unsubscribe(t)},get raw(){return e}}}export{u as getPusher,C as leaveChannel,v as useChannel,y as usePresenceChannel,p as usePusher,w as useSSE};
|