@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,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Svelar Permissions
|
|
3
|
+
*
|
|
4
|
+
* Spatie-inspired roles & permissions system. Provides:
|
|
5
|
+
* - Role model with permissions assignment
|
|
6
|
+
* - Permission model
|
|
7
|
+
* - HasRoles trait (mixin) for any model
|
|
8
|
+
* - RequirePermission / RequireRole middleware
|
|
9
|
+
* - Default migrations for roles, permissions, and pivot tables
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* import { Permission, Role, HasRoles } from 'svelar/permissions';
|
|
14
|
+
*
|
|
15
|
+
* // Models are auto-configured after migrations run
|
|
16
|
+
* const admin = await Role.create({ name: 'admin', guard: 'web' });
|
|
17
|
+
* const perm = await Permission.create({ name: 'manage-users', guard: 'web' });
|
|
18
|
+
*
|
|
19
|
+
* await admin.givePermission(perm);
|
|
20
|
+
*
|
|
21
|
+
* // On a user model:
|
|
22
|
+
* class User extends HasRoles(Model) { ... }
|
|
23
|
+
*
|
|
24
|
+
* await user.assignRole('admin');
|
|
25
|
+
* await user.givePermission('edit-posts');
|
|
26
|
+
*
|
|
27
|
+
* user.hasRole('admin'); // true
|
|
28
|
+
* user.hasPermission('manage-users'); // true (via admin role)
|
|
29
|
+
* user.can('edit-posts'); // true (direct permission)
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
import { Middleware, type MiddlewareContext, type NextFunction } from '../middleware/Middleware.js';
|
|
33
|
+
export interface PermissionRecord {
|
|
34
|
+
id: number;
|
|
35
|
+
name: string;
|
|
36
|
+
guard: string;
|
|
37
|
+
description?: string;
|
|
38
|
+
created_at?: string;
|
|
39
|
+
updated_at?: string;
|
|
40
|
+
}
|
|
41
|
+
export interface RoleRecord {
|
|
42
|
+
id: number;
|
|
43
|
+
name: string;
|
|
44
|
+
guard: string;
|
|
45
|
+
description?: string;
|
|
46
|
+
created_at?: string;
|
|
47
|
+
updated_at?: string;
|
|
48
|
+
}
|
|
49
|
+
declare class PermissionManager {
|
|
50
|
+
private connectionGetter?;
|
|
51
|
+
/**
|
|
52
|
+
* Configure the database connection for permissions
|
|
53
|
+
*/
|
|
54
|
+
configure(getConnection: () => Promise<any>): void;
|
|
55
|
+
private db;
|
|
56
|
+
createPermission(data: {
|
|
57
|
+
name: string;
|
|
58
|
+
guard?: string;
|
|
59
|
+
description?: string;
|
|
60
|
+
}): Promise<PermissionRecord>;
|
|
61
|
+
findPermission(name: string, guard?: string): Promise<PermissionRecord | null>;
|
|
62
|
+
findPermissionById(id: number): Promise<PermissionRecord | null>;
|
|
63
|
+
allPermissions(guard?: string): Promise<PermissionRecord[]>;
|
|
64
|
+
deletePermission(name: string, guard?: string): Promise<void>;
|
|
65
|
+
createRole(data: {
|
|
66
|
+
name: string;
|
|
67
|
+
guard?: string;
|
|
68
|
+
description?: string;
|
|
69
|
+
}): Promise<RoleRecord>;
|
|
70
|
+
findRole(name: string, guard?: string): Promise<RoleRecord | null>;
|
|
71
|
+
findRoleById(id: number): Promise<RoleRecord | null>;
|
|
72
|
+
allRoles(guard?: string): Promise<RoleRecord[]>;
|
|
73
|
+
deleteRole(name: string, guard?: string): Promise<void>;
|
|
74
|
+
giveRolePermission(roleId: number, permissionId: number): Promise<void>;
|
|
75
|
+
revokeRolePermission(roleId: number, permissionId: number): Promise<void>;
|
|
76
|
+
getRolePermissions(roleId: number): Promise<PermissionRecord[]>;
|
|
77
|
+
roleHasPermission(roleId: number, permissionName: string): Promise<boolean>;
|
|
78
|
+
assignRole(modelType: string, modelId: number, roleId: number): Promise<void>;
|
|
79
|
+
removeRole(modelType: string, modelId: number, roleId: number): Promise<void>;
|
|
80
|
+
getModelRoles(modelType: string, modelId: number): Promise<RoleRecord[]>;
|
|
81
|
+
modelHasRole(modelType: string, modelId: number, roleName: string): Promise<boolean>;
|
|
82
|
+
giveModelPermission(modelType: string, modelId: number, permissionId: number): Promise<void>;
|
|
83
|
+
revokeModelPermission(modelType: string, modelId: number, permissionId: number): Promise<void>;
|
|
84
|
+
getModelDirectPermissions(modelType: string, modelId: number): Promise<PermissionRecord[]>;
|
|
85
|
+
/**
|
|
86
|
+
* Get ALL permissions for a model (direct + via roles)
|
|
87
|
+
*/
|
|
88
|
+
getModelAllPermissions(modelType: string, modelId: number): Promise<PermissionRecord[]>;
|
|
89
|
+
/**
|
|
90
|
+
* Check if a model has a permission (direct or via role)
|
|
91
|
+
*/
|
|
92
|
+
modelHasPermission(modelType: string, modelId: number, permissionName: string): Promise<boolean>;
|
|
93
|
+
/**
|
|
94
|
+
* Sync roles for a model (remove all, then assign new ones)
|
|
95
|
+
*/
|
|
96
|
+
syncRoles(modelType: string, modelId: number, roleNames: string[], guard?: string): Promise<void>;
|
|
97
|
+
/**
|
|
98
|
+
* Sync permissions for a model (remove all direct, then assign new ones)
|
|
99
|
+
*/
|
|
100
|
+
syncPermissions(modelType: string, modelId: number, permissionNames: string[], guard?: string): Promise<void>;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Mixin that adds role & permission methods to any Model class.
|
|
104
|
+
*
|
|
105
|
+
* @example
|
|
106
|
+
* ```ts
|
|
107
|
+
* class User extends HasRoles(Model) {
|
|
108
|
+
* static table = 'users';
|
|
109
|
+
* // ...
|
|
110
|
+
* }
|
|
111
|
+
*
|
|
112
|
+
* const user = await User.find(1);
|
|
113
|
+
* await user.assignRole('admin');
|
|
114
|
+
* await user.givePermission('manage-users');
|
|
115
|
+
*
|
|
116
|
+
* console.log(await user.hasRole('admin')); // true
|
|
117
|
+
* console.log(await user.can('manage-users')); // true
|
|
118
|
+
* ```
|
|
119
|
+
*/
|
|
120
|
+
type Constructor = new (...args: any[]) => any;
|
|
121
|
+
export interface HasRolesInstance {
|
|
122
|
+
readonly _modelType: string;
|
|
123
|
+
readonly _modelId: number;
|
|
124
|
+
assignRole(roleName: string, guard?: string): Promise<void>;
|
|
125
|
+
removeRole(roleName: string, guard?: string): Promise<void>;
|
|
126
|
+
hasRole(roleName: string, guard?: string): Promise<boolean>;
|
|
127
|
+
givePermission(permissionName: string, guard?: string): Promise<void>;
|
|
128
|
+
revokePermission(permissionName: string, guard?: string): Promise<void>;
|
|
129
|
+
hasPermission(permissionName: string, guard?: string): Promise<boolean>;
|
|
130
|
+
can(permissionName: string, guard?: string): Promise<boolean>;
|
|
131
|
+
getRoles(): Promise<RoleRecord[]>;
|
|
132
|
+
getAllPermissions(): Promise<PermissionRecord[]>;
|
|
133
|
+
getDirectPermissions(): Promise<PermissionRecord[]>;
|
|
134
|
+
}
|
|
135
|
+
export declare function HasRoles<TBase extends Constructor>(Base: TBase): TBase & (new (...args: any[]) => HasRolesInstance);
|
|
136
|
+
/**
|
|
137
|
+
* Middleware that requires a specific permission
|
|
138
|
+
*/
|
|
139
|
+
export declare class RequirePermissionMiddleware extends Middleware {
|
|
140
|
+
private permission;
|
|
141
|
+
constructor(permission: string);
|
|
142
|
+
handle(ctx: MiddlewareContext, next: NextFunction): Promise<Response | void>;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Middleware that requires a specific role
|
|
146
|
+
*/
|
|
147
|
+
export declare class RequireRoleMiddleware extends Middleware {
|
|
148
|
+
private role;
|
|
149
|
+
constructor(role: string);
|
|
150
|
+
handle(ctx: MiddlewareContext, next: NextFunction): Promise<Response | void>;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* SQL statements for creating the permissions tables.
|
|
154
|
+
* These are database-agnostic and work with SQLite, PostgreSQL, and MySQL.
|
|
155
|
+
*/
|
|
156
|
+
export declare const PERMISSIONS_MIGRATION_SQL: {
|
|
157
|
+
up: string[];
|
|
158
|
+
down: string[];
|
|
159
|
+
};
|
|
160
|
+
export declare const Permissions: PermissionManager;
|
|
161
|
+
export {};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
var T=Object.defineProperty;var E=(c,e)=>()=>(c&&(e=c(c=0)),e);var P=(c,e)=>{for(var s in e)T(c,s,{get:e[s],enumerable:!0})};function p(c,e){let s=Symbol.for(c),t=globalThis;return t[s]||(t[s]=e()),t[s]}var h=E(()=>{"use strict"});var R={};P(R,{Connection:()=>b});var g,b,y=E(()=>{"use strict";h();g=class{connections=new Map;config=null;defaultName="default";configure(e){this.config=e,this.defaultName=e.default}async connection(e){let s=e??this.defaultName;if(this.connections.has(s))return this.connections.get(s).drizzle;if(!this.config)throw new Error("Database not configured. Call Connection.configure() first, or register DatabaseServiceProvider.");let t=this.config.connections[s];if(!t)throw new Error(`Database connection "${s}" is not defined in configuration.`);let n=await this.createConnection(t);return this.connections.set(s,n),n.drizzle}async rawClient(e){let s=e??this.defaultName;return await this.connection(s),this.connections.get(s).rawClient}async raw(e,s=[],t){let n=await this.connection(t),r=this.getConfig(t);switch(r.driver){case"sqlite":{let i=await this.rawClient(t),a=s.map(l=>typeof l=="boolean"?l?1:0:l instanceof Date?l.toISOString():l),d=i.prepare(e),m=e.trimStart().toUpperCase();return m.startsWith("SELECT")||m.startsWith("PRAGMA")||m.startsWith("WITH")?d.all(...a):d.run(...a)}case"postgres":return(await this.rawClient(t))(e,...s);case"mysql":{let i=await this.rawClient(t),[a]=await i.execute(e,s);return a}default:throw new Error(`Unsupported driver: ${r.driver}`)}}getDriver(e){return this.getConfig(e).driver}getConfig(e){let s=e??this.defaultName;if(!this.config)throw new Error("Database not configured.");let t=this.config.connections[s];if(!t)throw new Error(`Database connection "${s}" is not defined.`);return t}async disconnect(e){if(e){let s=this.connections.get(e);s&&(await this.closeConnection(s),this.connections.delete(e))}else{for(let[s,t]of this.connections)await this.closeConnection(t);this.connections.clear()}}isConnected(e){return this.connections.has(e??this.defaultName)}async transaction(e,s){let t=this.getConfig(s),n=await this.rawClient(s);switch(t.driver){case"sqlite":{n.exec("BEGIN");try{let r=await e();return n.exec("COMMIT"),r}catch(r){throw n.exec("ROLLBACK"),r}}case"postgres":{await n`BEGIN`;try{let r=await e();return await n`COMMIT`,r}catch(r){throw await n`ROLLBACK`,r}}case"mysql":{let r=await n.getConnection();await r.beginTransaction();try{let i=await e();return await r.commit(),r.release(),i}catch(i){throw await r.rollback(),r.release(),i}}default:throw new Error(`Unsupported driver: ${t.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 s=e.filename??e.database??":memory:";try{let t=(await import("better-sqlite3")).default,{drizzle:n}=await import("drizzle-orm/better-sqlite3"),r=new t(s);return r.pragma("journal_mode = WAL"),r.pragma("foreign_keys = ON"),{drizzle:n(r),config:e,rawClient:r}}catch(t){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: ${t instanceof Error?t.message:String(t)}`)}let r=new n(s);r.exec("PRAGMA journal_mode = WAL"),r.exec("PRAGMA foreign_keys = ON");let i={prepare(d){let m=r.prepare(d);return{all(...l){return m.all(...l)},run(...l){return m.run(...l)},get(...l){return m.get(...l)}}},exec(d){r.exec(d)},pragma(d){return r.prepare(`PRAGMA ${d}`).all()},close(){r.close()}},a;try{let{drizzle:d}=await import("drizzle-orm/better-sqlite3");a=d(i)}catch{a=i}return{drizzle:a,config:e,rawClient:i}}}async createPostgresConnection(e){let s=(await import("postgres")).default,{drizzle:t}=await import("drizzle-orm/postgres-js"),n=e.url??`postgres://${e.user}:${e.password}@${e.host??"localhost"}:${e.port??5432}/${e.database}`,r=s(n);return{drizzle:t(r),config:e,rawClient:r}}async createMySQLConnection(e){let s=await import("mysql2/promise"),{drizzle:t}=await import("drizzle-orm/mysql2"),n=s.createPool({host:e.host??"localhost",port:e.port??3306,database:e.database,user:e.user,password:e.password,uri:e.url});return{drizzle:t(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{}}},b=p("svelar.connection",()=>new g)});var u=class{};h();var w=class{connectionGetter;configure(e){this.connectionGetter=e}async db(){if(this.connectionGetter)return this.connectionGetter();let{Connection:e}=await Promise.resolve().then(()=>(y(),R));return e}async createPermission(e){let s=await this.db(),t=new Date().toISOString(),n=e.guard??"web";return await s.raw("INSERT INTO permissions (name, guard, description, created_at, updated_at) VALUES (?, ?, ?, ?, ?)",[e.name,n,e.description??null,t,t]),(await s.raw("SELECT * FROM permissions WHERE name = ? AND guard = ?",[e.name,n]))[0]}async findPermission(e,s="web"){return(await(await this.db()).raw("SELECT * FROM permissions WHERE name = ? AND guard = ?",[e,s]))[0]??null}async findPermissionById(e){return(await(await this.db()).raw("SELECT * FROM permissions WHERE id = ?",[e]))[0]??null}async allPermissions(e="web"){return(await this.db()).raw("SELECT * FROM permissions WHERE guard = ? ORDER BY name",[e])}async deletePermission(e,s="web"){await(await this.db()).raw("DELETE FROM permissions WHERE name = ? AND guard = ?",[e,s])}async createRole(e){let s=await this.db(),t=new Date().toISOString(),n=e.guard??"web";return await s.raw("INSERT INTO roles (name, guard, description, created_at, updated_at) VALUES (?, ?, ?, ?, ?)",[e.name,n,e.description??null,t,t]),(await s.raw("SELECT * FROM roles WHERE name = ? AND guard = ?",[e.name,n]))[0]}async findRole(e,s="web"){return(await(await this.db()).raw("SELECT * FROM roles WHERE name = ? AND guard = ?",[e,s]))[0]??null}async findRoleById(e){return(await(await this.db()).raw("SELECT * FROM roles WHERE id = ?",[e]))[0]??null}async allRoles(e="web"){return(await this.db()).raw("SELECT * FROM roles WHERE guard = ? ORDER BY name",[e])}async deleteRole(e,s="web"){await(await this.db()).raw("DELETE FROM roles WHERE name = ? AND guard = ?",[e,s])}async giveRolePermission(e,s){let t=await this.db();try{await t.raw("INSERT INTO role_has_permissions (role_id, permission_id) VALUES (?, ?)",[e,s])}catch{}}async revokeRolePermission(e,s){await(await this.db()).raw("DELETE FROM role_has_permissions WHERE role_id = ? AND permission_id = ?",[e,s])}async getRolePermissions(e){return(await this.db()).raw(`SELECT p.* FROM permissions p
|
|
2
|
+
INNER JOIN role_has_permissions rp ON p.id = rp.permission_id
|
|
3
|
+
WHERE rp.role_id = ?
|
|
4
|
+
ORDER BY p.name`,[e])}async roleHasPermission(e,s){return(await(await this.db()).raw(`SELECT 1 FROM role_has_permissions rp
|
|
5
|
+
INNER JOIN permissions p ON p.id = rp.permission_id
|
|
6
|
+
WHERE rp.role_id = ? AND p.name = ?`,[e,s])).length>0}async assignRole(e,s,t){let n=await this.db();try{await n.raw("INSERT INTO model_has_roles (model_type, model_id, role_id) VALUES (?, ?, ?)",[e,s,t])}catch{}}async removeRole(e,s,t){await(await this.db()).raw("DELETE FROM model_has_roles WHERE model_type = ? AND model_id = ? AND role_id = ?",[e,s,t])}async getModelRoles(e,s){return(await this.db()).raw(`SELECT r.* FROM roles r
|
|
7
|
+
INNER JOIN model_has_roles mr ON r.id = mr.role_id
|
|
8
|
+
WHERE mr.model_type = ? AND mr.model_id = ?
|
|
9
|
+
ORDER BY r.name`,[e,s])}async modelHasRole(e,s,t){return(await(await this.db()).raw(`SELECT 1 FROM model_has_roles mr
|
|
10
|
+
INNER JOIN roles r ON r.id = mr.role_id
|
|
11
|
+
WHERE mr.model_type = ? AND mr.model_id = ? AND r.name = ?`,[e,s,t])).length>0}async giveModelPermission(e,s,t){let n=await this.db();try{await n.raw("INSERT INTO model_has_permissions (model_type, model_id, permission_id) VALUES (?, ?, ?)",[e,s,t])}catch{}}async revokeModelPermission(e,s,t){await(await this.db()).raw("DELETE FROM model_has_permissions WHERE model_type = ? AND model_id = ? AND permission_id = ?",[e,s,t])}async getModelDirectPermissions(e,s){return(await this.db()).raw(`SELECT p.* FROM permissions p
|
|
12
|
+
INNER JOIN model_has_permissions mp ON p.id = mp.permission_id
|
|
13
|
+
WHERE mp.model_type = ? AND mp.model_id = ?
|
|
14
|
+
ORDER BY p.name`,[e,s])}async getModelAllPermissions(e,s){return(await this.db()).raw(`SELECT DISTINCT p.* FROM permissions p
|
|
15
|
+
LEFT JOIN model_has_permissions mp
|
|
16
|
+
ON p.id = mp.permission_id AND mp.model_type = ? AND mp.model_id = ?
|
|
17
|
+
LEFT JOIN role_has_permissions rp ON p.id = rp.permission_id
|
|
18
|
+
LEFT JOIN model_has_roles mr
|
|
19
|
+
ON rp.role_id = mr.role_id AND mr.model_type = ? AND mr.model_id = ?
|
|
20
|
+
WHERE mp.permission_id IS NOT NULL OR mr.role_id IS NOT NULL
|
|
21
|
+
ORDER BY p.name`,[e,s,e,s])}async modelHasPermission(e,s,t){let n=await this.db();return(await n.raw(`SELECT 1 FROM model_has_permissions mp
|
|
22
|
+
INNER JOIN permissions p ON p.id = mp.permission_id
|
|
23
|
+
WHERE mp.model_type = ? AND mp.model_id = ? AND p.name = ?`,[e,s,t])).length>0?!0:(await n.raw(`SELECT 1 FROM model_has_roles mr
|
|
24
|
+
INNER JOIN role_has_permissions rp ON rp.role_id = mr.role_id
|
|
25
|
+
INNER JOIN permissions p ON p.id = rp.permission_id
|
|
26
|
+
WHERE mr.model_type = ? AND mr.model_id = ? AND p.name = ?`,[e,s,t])).length>0}async syncRoles(e,s,t,n="web"){await(await this.db()).raw("DELETE FROM model_has_roles WHERE model_type = ? AND model_id = ?",[e,s]);for(let i of t){let a=await this.findRole(i,n);a&&await this.assignRole(e,s,a.id)}}async syncPermissions(e,s,t,n="web"){await(await this.db()).raw("DELETE FROM model_has_permissions WHERE model_type = ? AND model_id = ?",[e,s]);for(let i of t){let a=await this.findPermission(i,n);a&&await this.giveModelPermission(e,s,a.id)}}};function I(c){class e extends c{get _modelType(){return this.constructor.name||"User"}get _modelId(){return this.id??this.getAttribute?.("id")}async assignRole(t,n="web"){let r=await o.findRole(t,n);if(!r)throw new Error(`Role "${t}" does not exist.`);await o.assignRole(this._modelType,this._modelId,r.id)}async removeRole(t,n="web"){let r=await o.findRole(t,n);r&&await o.removeRole(this._modelType,this._modelId,r.id)}async syncRoles(t,n="web"){await o.syncRoles(this._modelType,this._modelId,t,n)}async hasRole(t){return o.modelHasRole(this._modelType,this._modelId,t)}async hasAnyRole(...t){for(let n of t)if(await this.hasRole(n))return!0;return!1}async hasAllRoles(...t){for(let n of t)if(!await this.hasRole(n))return!1;return!0}async getRoles(){return o.getModelRoles(this._modelType,this._modelId)}async givePermission(t,n="web"){let r=await o.findPermission(t,n);if(!r)throw new Error(`Permission "${t}" does not exist.`);await o.giveModelPermission(this._modelType,this._modelId,r.id)}async revokePermission(t,n="web"){let r=await o.findPermission(t,n);r&&await o.revokeModelPermission(this._modelType,this._modelId,r.id)}async syncPermissions(t,n="web"){await o.syncPermissions(this._modelType,this._modelId,t,n)}async hasPermission(t){return o.modelHasPermission(this._modelType,this._modelId,t)}async can(t){return this.hasPermission(t)}async cannot(t){return!await this.hasPermission(t)}async getAllPermissions(){return o.getModelAllPermissions(this._modelType,this._modelId)}async getDirectPermissions(){return o.getModelDirectPermissions(this._modelType,this._modelId)}}return e}var f=class extends u{constructor(s){super();this.permission=s}async handle(s,t){let n=s.event.locals.user;if(!n)return new Response(JSON.stringify({message:"Unauthenticated"}),{status:401,headers:{"Content-Type":"application/json"}});let r=n.constructor?.name||"User",i=n.id??n.getAttribute?.("id");return await o.modelHasPermission(r,i,this.permission)?t():new Response(JSON.stringify({message:`Missing permission: ${this.permission}`}),{status:403,headers:{"Content-Type":"application/json"}})}},N=class extends u{constructor(s){super();this.role=s}async handle(s,t){let n=s.event.locals.user;if(!n)return new Response(JSON.stringify({message:"Unauthenticated"}),{status:401,headers:{"Content-Type":"application/json"}});let r=n.constructor?.name||"User",i=n.id??n.getAttribute?.("id");return await o.modelHasRole(r,i,this.role)?t():new Response(JSON.stringify({message:`Missing role: ${this.role}`}),{status:403,headers:{"Content-Type":"application/json"}})}},S={up:[`CREATE TABLE IF NOT EXISTS permissions (
|
|
27
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
28
|
+
name VARCHAR(255) NOT NULL,
|
|
29
|
+
guard VARCHAR(255) NOT NULL DEFAULT 'web',
|
|
30
|
+
description TEXT,
|
|
31
|
+
created_at DATETIME,
|
|
32
|
+
updated_at DATETIME,
|
|
33
|
+
UNIQUE(name, guard)
|
|
34
|
+
)`,`CREATE TABLE IF NOT EXISTS roles (
|
|
35
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
36
|
+
name VARCHAR(255) NOT NULL,
|
|
37
|
+
guard VARCHAR(255) NOT NULL DEFAULT 'web',
|
|
38
|
+
description TEXT,
|
|
39
|
+
created_at DATETIME,
|
|
40
|
+
updated_at DATETIME,
|
|
41
|
+
UNIQUE(name, guard)
|
|
42
|
+
)`,`CREATE TABLE IF NOT EXISTS role_has_permissions (
|
|
43
|
+
role_id INTEGER NOT NULL,
|
|
44
|
+
permission_id INTEGER NOT NULL,
|
|
45
|
+
PRIMARY KEY (role_id, permission_id),
|
|
46
|
+
FOREIGN KEY (role_id) REFERENCES roles(id) ON DELETE CASCADE,
|
|
47
|
+
FOREIGN KEY (permission_id) REFERENCES permissions(id) ON DELETE CASCADE
|
|
48
|
+
)`,`CREATE TABLE IF NOT EXISTS model_has_roles (
|
|
49
|
+
model_type VARCHAR(255) NOT NULL,
|
|
50
|
+
model_id INTEGER NOT NULL,
|
|
51
|
+
role_id INTEGER NOT NULL,
|
|
52
|
+
PRIMARY KEY (model_type, model_id, role_id),
|
|
53
|
+
FOREIGN KEY (role_id) REFERENCES roles(id) ON DELETE CASCADE
|
|
54
|
+
)`,`CREATE TABLE IF NOT EXISTS model_has_permissions (
|
|
55
|
+
model_type VARCHAR(255) NOT NULL,
|
|
56
|
+
model_id INTEGER NOT NULL,
|
|
57
|
+
permission_id INTEGER NOT NULL,
|
|
58
|
+
PRIMARY KEY (model_type, model_id, permission_id),
|
|
59
|
+
FOREIGN KEY (permission_id) REFERENCES permissions(id) ON DELETE CASCADE
|
|
60
|
+
)`],down:["DROP TABLE IF EXISTS model_has_permissions","DROP TABLE IF EXISTS model_has_roles","DROP TABLE IF EXISTS role_has_permissions","DROP TABLE IF EXISTS roles","DROP TABLE IF EXISTS permissions"]},o=p("svelar.permissions",()=>new w);export{I as HasRoles,S as PERMISSIONS_MIGRATION_SQL,o as Permissions,f as RequirePermissionMiddleware,N as RequireRoleMiddleware};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Svelar Bootstrap Plugins
|
|
3
|
+
*
|
|
4
|
+
* Auto-discovers and boots enabled plugins at app startup.
|
|
5
|
+
*/
|
|
6
|
+
import type { Container } from '../container/Container.js';
|
|
7
|
+
/**
|
|
8
|
+
* Bootstrap plugins from the plugin registry
|
|
9
|
+
* Loads enabled plugins from configuration and boots them
|
|
10
|
+
*/
|
|
11
|
+
export declare function bootstrapPlugins(app: Container, enabledPlugins?: string[]): Promise<void>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Svelar Plugin Installer
|
|
3
|
+
*
|
|
4
|
+
* Handles installing plugins from npm and wiring them up.
|
|
5
|
+
*/
|
|
6
|
+
import { type PublishResult } from './PluginPublisher.js';
|
|
7
|
+
export interface InstallResult {
|
|
8
|
+
success: boolean;
|
|
9
|
+
pluginName: string;
|
|
10
|
+
version: string;
|
|
11
|
+
published: PublishResult | null;
|
|
12
|
+
error?: string;
|
|
13
|
+
}
|
|
14
|
+
declare class PluginInstallerService {
|
|
15
|
+
/**
|
|
16
|
+
* Install a plugin package and register it
|
|
17
|
+
*/
|
|
18
|
+
install(packageName: string, options?: {
|
|
19
|
+
publish?: boolean;
|
|
20
|
+
}): Promise<InstallResult>;
|
|
21
|
+
/**
|
|
22
|
+
* Uninstall a plugin
|
|
23
|
+
*/
|
|
24
|
+
uninstall(pluginName: string): Promise<boolean>;
|
|
25
|
+
private runNpmInstall;
|
|
26
|
+
private runNpmUninstall;
|
|
27
|
+
private loadPluginClass;
|
|
28
|
+
}
|
|
29
|
+
export declare const PluginInstaller: PluginInstallerService;
|
|
30
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var b=(u=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(u,{get:(s,e)=>(typeof require<"u"?require:s)[e]}):u)(function(u){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+u+'" is not supported')});function f(u,s){let e=Symbol.for(u),i=globalThis;return i[e]||(i[e]=s()),i[e]}var y=class{plugins=new Map;enabledPlugins=new Set;async discover(){let{join:s}=await import("path"),{existsSync:e,readdirSync:i}=await import("fs"),{readFile:c}=await import("fs/promises"),o=[],t=s(process.cwd(),"node_modules");if(!e(t))return o;try{let p=i(t,{withFileTypes:!0});for(let l of p){if(!l.isDirectory())continue;let g=l.name;if(g.startsWith("."))continue;let a=g.startsWith("svelar-");if(!a)continue;let d=s(t,g,"package.json");if(e(d))try{let n=await c(d,"utf-8"),r=JSON.parse(n),m=r.keywords?.includes("svelar-plugin");if(!a&&!m)continue;let h={name:r.name||g,version:r.version||"0.0.0",description:r.description||"",packageName:g,installed:!0,enabled:!1,hasConfig:!!r.svelar?.config,hasMigrations:!!r.svelar?.migrations};o.push(h),this.plugins.set(h.name,h)}catch{}}}catch{}return o}enable(s){let e=this.plugins.get(s);if(!e)throw new Error(`Plugin "${s}" not found in registry.`);this.enabledPlugins.add(s),e.enabled=!0}disable(s){this.enabledPlugins.delete(s);let e=this.plugins.get(s);e&&(e.enabled=!1)}isEnabled(s){return this.enabledPlugins.has(s)}list(){return[...this.plugins.values()]}listEnabled(){return[...this.plugins.values()].filter(s=>this.enabledPlugins.has(s.name))}get(s){return this.plugins.get(s)}register(s){this.plugins.set(s.name,s)}},w=f("svelar.pluginRegistry",()=>new y);var P=class{async publish(s,e){let{mkdir:i,copyFile:c}=await import("fs/promises"),{join:o,dirname:t}=await import("path"),{existsSync:p}=await import("fs"),l={configs:[],migrations:[],assets:[]},g=s.publishables?.()||{};for(let[a,d]of Object.entries(g))for(let n of d){if(e?.only&&n.type!==e.only)continue;let r=o(process.cwd(),n.dest),m=t(r);if(!(p(r)&&!e?.force))try{await i(m,{recursive:!0}),await c(n.source,r),n.type==="config"?l.configs.push(r):n.type==="migration"?l.migrations.push(r):n.type==="asset"&&l.assets.push(r)}catch(h){console.warn(`Failed to publish ${n.source} to ${r}:`,h)}}return l}async preview(s){let e={configs:[],migrations:[],assets:[]},i=s.publishables?.()||{};for(let[c,o]of Object.entries(i))for(let t of o){let p=b("path").join(process.cwd(),t.dest);t.type==="config"?e.configs.push(p):t.type==="migration"?e.migrations.push(p):t.type==="asset"&&e.assets.push(p)}return e}},M=f("svelar.pluginPublisher",()=>new P);var v=class{async install(s,e){let{spawn:i}=await import("child_process"),{promisify:c}=await import("util"),{join:o}=await import("path"),{readFile:t}=await import("fs/promises"),{existsSync:p}=await import("fs");try{await this.runNpmInstall(s);let l=w,g=await l.discover(),a;for(let n of g)if(n.packageName===s||n.name===s){a=n;break}if(!a)return{success:!1,pluginName:s,version:"0.0.0",published:null,error:`Plugin not found after installation. Make sure ${s} is a valid Svelar plugin.`};l.enable(a.name);let d=null;if(e?.publish!==!1)try{let n=await this.loadPluginClass(a.packageName);n&&(d=await M.publish(new n))}catch(n){console.warn("Failed to publish plugin assets:",n)}return{success:!0,pluginName:a.name,version:a.version,published:d}}catch(l){return{success:!1,pluginName:s,version:"0.0.0",published:null,error:l?.message??String(l)}}}async uninstall(s){try{let e=w,i=e.get(s);return i?(e.disable(s),await this.runNpmUninstall(i.packageName),!0):!1}catch(e){return console.error("Failed to uninstall plugin:",e),!1}}async runNpmInstall(s){return new Promise((e,i)=>{let{spawn:c}=b("child_process"),o=c("npm",["install",s],{cwd:process.cwd(),stdio:"inherit"});o.on("close",t=>{t===0?e():i(new Error(`npm install exited with code ${t}`))}),o.on("error",i)})}async runNpmUninstall(s){return new Promise((e,i)=>{let{spawn:c}=b("child_process"),o=c("npm",["uninstall",s],{cwd:process.cwd(),stdio:"inherit"});o.on("close",t=>{t===0?e():i(new Error(`npm uninstall exited with code ${t}`))}),o.on("error",i)})}async loadPluginClass(s){try{let e=await import(s);return e.default||Object.values(e)[0]}catch{return null}}},I=f("svelar.pluginInstaller",()=>new v);export{I as PluginInstaller};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Svelar Plugin Publisher
|
|
3
|
+
*
|
|
4
|
+
* Handles publishing a plugin's configuration files, migrations,
|
|
5
|
+
* and assets to the user's application.
|
|
6
|
+
*/
|
|
7
|
+
import type { Plugin } from './index.js';
|
|
8
|
+
export interface PublishResult {
|
|
9
|
+
configs: string[];
|
|
10
|
+
migrations: string[];
|
|
11
|
+
assets: string[];
|
|
12
|
+
}
|
|
13
|
+
export interface PublishableFile {
|
|
14
|
+
source: string;
|
|
15
|
+
dest: string;
|
|
16
|
+
type: 'config' | 'migration' | 'asset';
|
|
17
|
+
}
|
|
18
|
+
declare class PluginPublisherService {
|
|
19
|
+
/**
|
|
20
|
+
* Publish a plugin's publishable assets
|
|
21
|
+
*/
|
|
22
|
+
publish(plugin: Plugin, options?: {
|
|
23
|
+
force?: boolean;
|
|
24
|
+
only?: 'config' | 'migrations' | 'assets';
|
|
25
|
+
}): Promise<PublishResult>;
|
|
26
|
+
/**
|
|
27
|
+
* Check what would be published without actually publishing
|
|
28
|
+
*/
|
|
29
|
+
preview(plugin: Plugin): Promise<PublishResult>;
|
|
30
|
+
}
|
|
31
|
+
export declare const PluginPublisher: PluginPublisherService;
|
|
32
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var y=(e=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(e,{get:(t,s)=>(typeof require<"u"?require:t)[s]}):e)(function(e){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+e+'" is not supported')});function p(e,t){let s=Symbol.for(e),i=globalThis;return i[s]||(i[s]=t()),i[s]}var u=class{async publish(t,s){let{mkdir:i,copyFile:f}=await import("fs/promises"),{join:a,dirname:r}=await import("path"),{existsSync:l}=await import("fs"),c={configs:[],migrations:[],assets:[]},g=t.publishables?.()||{};for(let[P,b]of Object.entries(g))for(let o of b){if(s?.only&&o.type!==s.only)continue;let n=a(process.cwd(),o.dest),h=r(n);if(!(l(n)&&!s?.force))try{await i(h,{recursive:!0}),await f(o.source,n),o.type==="config"?c.configs.push(n):o.type==="migration"?c.migrations.push(n):o.type==="asset"&&c.assets.push(n)}catch(m){console.warn(`Failed to publish ${o.source} to ${n}:`,m)}}return c}async preview(t){let s={configs:[],migrations:[],assets:[]},i=t.publishables?.()||{};for(let[f,a]of Object.entries(i))for(let r of a){let l=y("path").join(process.cwd(),r.dest);r.type==="config"?s.configs.push(l):r.type==="migration"?s.migrations.push(l):r.type==="asset"&&s.assets.push(l)}return s}},j=p("svelar.pluginPublisher",()=>new u);export{j as PluginPublisher};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Svelar Plugin Registry
|
|
3
|
+
*
|
|
4
|
+
* Discovers and manages installed plugins by scanning node_modules
|
|
5
|
+
* for packages with the 'svelar-plugin' keyword or 'svelar-' prefix.
|
|
6
|
+
*/
|
|
7
|
+
export interface PluginMeta {
|
|
8
|
+
name: string;
|
|
9
|
+
version: string;
|
|
10
|
+
description: string;
|
|
11
|
+
packageName: string;
|
|
12
|
+
installed: boolean;
|
|
13
|
+
enabled: boolean;
|
|
14
|
+
hasConfig: boolean;
|
|
15
|
+
hasMigrations: boolean;
|
|
16
|
+
}
|
|
17
|
+
declare class PluginRegistryService {
|
|
18
|
+
private plugins;
|
|
19
|
+
private enabledPlugins;
|
|
20
|
+
/**
|
|
21
|
+
* Scan node_modules for svelar plugins
|
|
22
|
+
* Looks for packages with 'svelar-plugin' keyword or starting with 'svelar-'
|
|
23
|
+
*/
|
|
24
|
+
discover(): Promise<PluginMeta[]>;
|
|
25
|
+
/**
|
|
26
|
+
* Register a plugin as enabled
|
|
27
|
+
*/
|
|
28
|
+
enable(name: string): void;
|
|
29
|
+
/**
|
|
30
|
+
* Disable a plugin
|
|
31
|
+
*/
|
|
32
|
+
disable(name: string): void;
|
|
33
|
+
/**
|
|
34
|
+
* Check if plugin is enabled
|
|
35
|
+
*/
|
|
36
|
+
isEnabled(name: string): boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Get all registered plugin metadata
|
|
39
|
+
*/
|
|
40
|
+
list(): PluginMeta[];
|
|
41
|
+
/**
|
|
42
|
+
* Get enabled plugins
|
|
43
|
+
*/
|
|
44
|
+
listEnabled(): PluginMeta[];
|
|
45
|
+
/**
|
|
46
|
+
* Get a plugin by name
|
|
47
|
+
*/
|
|
48
|
+
get(name: string): PluginMeta | undefined;
|
|
49
|
+
/**
|
|
50
|
+
* Register a plugin in the registry
|
|
51
|
+
*/
|
|
52
|
+
register(meta: PluginMeta): void;
|
|
53
|
+
}
|
|
54
|
+
export declare const PluginRegistry: PluginRegistryService;
|
|
55
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function p(g,n){let e=Symbol.for(g),i=globalThis;return i[e]||(i[e]=n()),i[e]}var l=class{plugins=new Map;enabledPlugins=new Set;async discover(){let{join:n}=await import("path"),{existsSync:e,readdirSync:i}=await import("fs"),{readFile:h}=await import("fs/promises"),a=[],o=n(process.cwd(),"node_modules");if(!e(o))return a;try{let f=i(o,{withFileTypes:!0});for(let u of f){if(!u.isDirectory())continue;let s=u.name;if(s.startsWith("."))continue;let c=s.startsWith("svelar-");if(!c)continue;let d=n(o,s,"package.json");if(e(d))try{let b=await h(d,"utf-8"),t=JSON.parse(b),P=t.keywords?.includes("svelar-plugin");if(!c&&!P)continue;let r={name:t.name||s,version:t.version||"0.0.0",description:t.description||"",packageName:s,installed:!0,enabled:!1,hasConfig:!!t.svelar?.config,hasMigrations:!!t.svelar?.migrations};a.push(r),this.plugins.set(r.name,r)}catch{}}}catch{}return a}enable(n){let e=this.plugins.get(n);if(!e)throw new Error(`Plugin "${n}" not found in registry.`);this.enabledPlugins.add(n),e.enabled=!0}disable(n){this.enabledPlugins.delete(n);let e=this.plugins.get(n);e&&(e.enabled=!1)}isEnabled(n){return this.enabledPlugins.has(n)}list(){return[...this.plugins.values()]}listEnabled(){return[...this.plugins.values()].filter(n=>this.enabledPlugins.has(n.name))}get(n){return this.plugins.get(n)}register(n){this.plugins.set(n.name,n)}},y=p("svelar.pluginRegistry",()=>new l);export{y as PluginRegistry};
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Svelar Plugin System
|
|
3
|
+
*
|
|
4
|
+
* Extensible plugin architecture inspired by Laravel packages.
|
|
5
|
+
* Plugins can register service providers, middleware, commands,
|
|
6
|
+
* routes, migrations, and configuration.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* import { Plugin, PluginManager } from 'svelar/plugins';
|
|
11
|
+
*
|
|
12
|
+
* // Define a plugin
|
|
13
|
+
* class StripePlugin extends Plugin {
|
|
14
|
+
* name = 'svelar-stripe';
|
|
15
|
+
* version = '1.0.0';
|
|
16
|
+
*
|
|
17
|
+
* async register(app) {
|
|
18
|
+
* app.singleton('stripe', () => new Stripe(process.env.STRIPE_KEY));
|
|
19
|
+
* }
|
|
20
|
+
*
|
|
21
|
+
* async boot(app) {
|
|
22
|
+
* // Register routes, middleware, etc.
|
|
23
|
+
* }
|
|
24
|
+
*
|
|
25
|
+
* migrations() {
|
|
26
|
+
* return ['create_payments_table', 'create_subscriptions_table'];
|
|
27
|
+
* }
|
|
28
|
+
*
|
|
29
|
+
* config() {
|
|
30
|
+
* return {
|
|
31
|
+
* key: 'stripe',
|
|
32
|
+
* defaults: { currency: 'usd', webhook_secret: '' },
|
|
33
|
+
* };
|
|
34
|
+
* }
|
|
35
|
+
* }
|
|
36
|
+
*
|
|
37
|
+
* // Register plugins
|
|
38
|
+
* const plugins = new PluginManager(app);
|
|
39
|
+
* plugins.use(new StripePlugin());
|
|
40
|
+
* await plugins.boot();
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
import type { Container } from '../container/Container.js';
|
|
44
|
+
import type { Middleware } from '../middleware/Middleware.js';
|
|
45
|
+
export interface PluginConfig {
|
|
46
|
+
key: string;
|
|
47
|
+
defaults: Record<string, any>;
|
|
48
|
+
}
|
|
49
|
+
export interface PluginMigration {
|
|
50
|
+
name: string;
|
|
51
|
+
up: () => Promise<void>;
|
|
52
|
+
down: () => Promise<void>;
|
|
53
|
+
}
|
|
54
|
+
export interface PluginRoute {
|
|
55
|
+
method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
56
|
+
path: string;
|
|
57
|
+
handler: (event: any) => Promise<Response>;
|
|
58
|
+
middleware?: string[];
|
|
59
|
+
}
|
|
60
|
+
export interface PluginCommand {
|
|
61
|
+
name: string;
|
|
62
|
+
description: string;
|
|
63
|
+
handler: (args: string[]) => Promise<void>;
|
|
64
|
+
}
|
|
65
|
+
export type PluginHook = 'app:boot' | 'app:shutdown' | 'request:before' | 'request:after' | 'model:creating' | 'model:created' | 'model:updating' | 'model:updated' | 'model:deleting' | 'model:deleted' | string;
|
|
66
|
+
export declare abstract class Plugin {
|
|
67
|
+
/** Unique plugin identifier (e.g., 'svelar-stripe') */
|
|
68
|
+
abstract readonly name: string;
|
|
69
|
+
/** Plugin version (semver) */
|
|
70
|
+
abstract readonly version: string;
|
|
71
|
+
/** Human-readable description */
|
|
72
|
+
description?: string;
|
|
73
|
+
/** Plugin dependencies (other plugin names) */
|
|
74
|
+
dependencies?: string[];
|
|
75
|
+
/**
|
|
76
|
+
* Register services, bindings, and configuration.
|
|
77
|
+
* Called before boot.
|
|
78
|
+
*/
|
|
79
|
+
register(app: Container): Promise<void>;
|
|
80
|
+
/**
|
|
81
|
+
* Bootstrap the plugin after all plugins are registered.
|
|
82
|
+
* Use for setup that depends on other services.
|
|
83
|
+
*/
|
|
84
|
+
boot(app: Container): Promise<void>;
|
|
85
|
+
/**
|
|
86
|
+
* Clean up when the plugin is unloaded
|
|
87
|
+
*/
|
|
88
|
+
shutdown(): Promise<void>;
|
|
89
|
+
/**
|
|
90
|
+
* Return migration files this plugin provides
|
|
91
|
+
*/
|
|
92
|
+
migrations(): string[];
|
|
93
|
+
/**
|
|
94
|
+
* Return default configuration for this plugin
|
|
95
|
+
*/
|
|
96
|
+
config(): PluginConfig | null;
|
|
97
|
+
/**
|
|
98
|
+
* Return middleware this plugin provides
|
|
99
|
+
*/
|
|
100
|
+
middleware(): Array<{
|
|
101
|
+
name: string;
|
|
102
|
+
handler: Middleware | ((ctx: any, next: any) => Promise<any>);
|
|
103
|
+
}>;
|
|
104
|
+
/**
|
|
105
|
+
* Return CLI commands this plugin provides
|
|
106
|
+
*/
|
|
107
|
+
commands(): PluginCommand[];
|
|
108
|
+
/**
|
|
109
|
+
* Return API routes this plugin provides
|
|
110
|
+
*/
|
|
111
|
+
routes(): PluginRoute[];
|
|
112
|
+
/**
|
|
113
|
+
* Return event listeners this plugin registers
|
|
114
|
+
*/
|
|
115
|
+
listeners(): Array<{
|
|
116
|
+
event: string;
|
|
117
|
+
handler: (...args: any[]) => void | Promise<void>;
|
|
118
|
+
}>;
|
|
119
|
+
/**
|
|
120
|
+
* Return publishable files (configs, migrations, assets)
|
|
121
|
+
* Map of type -> array of { source, dest, type }
|
|
122
|
+
*/
|
|
123
|
+
publishables?(): Record<string, Array<{
|
|
124
|
+
source: string;
|
|
125
|
+
dest: string;
|
|
126
|
+
type: 'config' | 'migration' | 'asset';
|
|
127
|
+
}>>;
|
|
128
|
+
}
|
|
129
|
+
export declare class PluginManager {
|
|
130
|
+
private app;
|
|
131
|
+
private plugins;
|
|
132
|
+
private registered;
|
|
133
|
+
private booted;
|
|
134
|
+
private hooks;
|
|
135
|
+
constructor(app: Container);
|
|
136
|
+
/**
|
|
137
|
+
* Register a plugin
|
|
138
|
+
*/
|
|
139
|
+
use(plugin: Plugin): this;
|
|
140
|
+
/**
|
|
141
|
+
* Register multiple plugins
|
|
142
|
+
*/
|
|
143
|
+
useMany(plugins: Plugin[]): this;
|
|
144
|
+
/**
|
|
145
|
+
* Register and boot all plugins (respects dependency order)
|
|
146
|
+
*/
|
|
147
|
+
boot(): Promise<void>;
|
|
148
|
+
/**
|
|
149
|
+
* Shutdown all plugins (reverse order)
|
|
150
|
+
*/
|
|
151
|
+
shutdown(): Promise<void>;
|
|
152
|
+
/**
|
|
153
|
+
* Get a registered plugin by name
|
|
154
|
+
*/
|
|
155
|
+
get<T extends Plugin>(name: string): T | undefined;
|
|
156
|
+
/**
|
|
157
|
+
* Check if a plugin is registered
|
|
158
|
+
*/
|
|
159
|
+
has(name: string): boolean;
|
|
160
|
+
/**
|
|
161
|
+
* Get all registered plugin names
|
|
162
|
+
*/
|
|
163
|
+
names(): string[];
|
|
164
|
+
/**
|
|
165
|
+
* Get all plugins
|
|
166
|
+
*/
|
|
167
|
+
all(): Plugin[];
|
|
168
|
+
/**
|
|
169
|
+
* Register a hook listener
|
|
170
|
+
*/
|
|
171
|
+
on(hook: PluginHook, handler: (...args: any[]) => Promise<void> | void): void;
|
|
172
|
+
/**
|
|
173
|
+
* Trigger a hook
|
|
174
|
+
*/
|
|
175
|
+
triggerHook(hook: PluginHook, ...args: any[]): Promise<void>;
|
|
176
|
+
/**
|
|
177
|
+
* Get all routes from all plugins
|
|
178
|
+
*/
|
|
179
|
+
getRoutes(): PluginRoute[];
|
|
180
|
+
/**
|
|
181
|
+
* Get all commands from all plugins
|
|
182
|
+
*/
|
|
183
|
+
getCommands(): PluginCommand[];
|
|
184
|
+
/**
|
|
185
|
+
* Get all middleware from all plugins
|
|
186
|
+
*/
|
|
187
|
+
getMiddleware(): Array<{
|
|
188
|
+
name: string;
|
|
189
|
+
handler: any;
|
|
190
|
+
}>;
|
|
191
|
+
/**
|
|
192
|
+
* Get all migrations from all plugins
|
|
193
|
+
*/
|
|
194
|
+
getMigrations(): string[];
|
|
195
|
+
/**
|
|
196
|
+
* Get all configurations from all plugins
|
|
197
|
+
*/
|
|
198
|
+
getConfigs(): PluginConfig[];
|
|
199
|
+
private registerPlugin;
|
|
200
|
+
private bootPlugin;
|
|
201
|
+
private resolveDependencyOrder;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Load plugins from a directory (for auto-discovery)
|
|
205
|
+
*/
|
|
206
|
+
export declare function discoverPlugins(pluginDir: string): Promise<Plugin[]>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var g=class{description;dependencies;async register(t){}async boot(t){}async shutdown(){}migrations(){return[]}config(){return null}middleware(){return[]}commands(){return[]}routes(){return[]}listeners(){return[]}publishables(){return{}}},d=class{constructor(t){this.app=t}plugins=new Map;registered=new Set;booted=new Set;hooks=new Map;use(t){if(this.plugins.has(t.name))throw new Error(`Plugin "${t.name}" is already registered.`);return this.plugins.set(t.name,t),this}useMany(t){for(let e of t)this.use(e);return this}async boot(){let t=this.resolveDependencyOrder();for(let e of t)await this.registerPlugin(e);for(let e of t)await this.bootPlugin(e);await this.triggerHook("app:boot")}async shutdown(){await this.triggerHook("app:shutdown");let t=[...this.booted].reverse();for(let e of t){let i=this.plugins.get(e);i&&await i.shutdown()}}get(t){return this.plugins.get(t)}has(t){return this.plugins.has(t)}names(){return[...this.plugins.keys()]}all(){return[...this.plugins.values()]}on(t,e){this.hooks.has(t)||this.hooks.set(t,[]),this.hooks.get(t).push(e)}async triggerHook(t,...e){let i=this.hooks.get(t);if(i)for(let n of i)await n(...e)}getRoutes(){let t=[];for(let e of this.plugins.values())t.push(...e.routes());return t}getCommands(){let t=[];for(let e of this.plugins.values())t.push(...e.commands());return t}getMiddleware(){let t=[];for(let e of this.plugins.values())t.push(...e.middleware());return t}getMigrations(){let t=[];for(let e of this.plugins.values())t.push(...e.migrations());return t}getConfigs(){let t=[];for(let e of this.plugins.values()){let i=e.config();i&&t.push(i)}return t}async registerPlugin(t){if(this.registered.has(t))return;let e=this.plugins.get(t);if(!e)throw new Error(`Plugin "${t}" not found.`);let i=e.config();i&&this.app.instance(`config.${i.key}`,i.defaults);for(let{event:n,handler:s}of e.listeners())this.on(n,s);await e.register(this.app),this.registered.add(t)}async bootPlugin(t){if(this.booted.has(t))return;let e=this.plugins.get(t);e&&(await e.boot(this.app),this.booted.add(t))}resolveDependencyOrder(){let t=new Set,e=[],i=(n,s)=>{if(t.has(n))return;if(s.has(n))throw new Error(`Circular plugin dependency detected: ${[...s,n].join(" -> ")}`);s.add(n);let o=this.plugins.get(n);if(o?.dependencies)for(let r of o.dependencies){if(!this.plugins.has(r))throw new Error(`Plugin "${n}" requires "${r}" which is not registered.`);i(r,s)}s.delete(n),t.add(n),e.push(n)};for(let n of this.plugins.keys())i(n,new Set);return e}};async function u(a){let{readdir:t}=await import("fs/promises"),{join:e}=await import("path"),i=[];try{let n=await t(a,{withFileTypes:!0});for(let s of n)if(s.isDirectory())try{let o=await import(e(a,s.name,"index.js")),r=o.default||o[Object.keys(o)[0]];r&&r.prototype instanceof g&&i.push(new r)}catch{}else if(s.isFile()&&(s.name.endsWith(".ts")||s.name.endsWith(".js")))try{let o=await import(e(a,s.name)),r=o.default||o[Object.keys(o)[0]];r&&r.prototype instanceof g&&i.push(new r)}catch{}}catch{}return i}export{g as Plugin,d as PluginManager,u as discoverPlugins};
|