@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,318 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Svelar Broadcasting
|
|
3
|
+
*
|
|
4
|
+
* Real-time event broadcasting with multiple drivers and Laravel-style
|
|
5
|
+
* channel authorization. Supports SSE (zero-dependency), Pusher/Soketi
|
|
6
|
+
* (WebSocket via pusher protocol), and a log driver for development.
|
|
7
|
+
*
|
|
8
|
+
* Channel types:
|
|
9
|
+
* - Public channels: Anyone can subscribe
|
|
10
|
+
* - Private channels: Require authorization (prefixed with 'private-')
|
|
11
|
+
* - Presence channels: Require authorization + track who's online (prefixed with 'presence-')
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* import { Broadcast } from 'svelar/broadcasting';
|
|
16
|
+
*
|
|
17
|
+
* // Configure
|
|
18
|
+
* Broadcast.configure({
|
|
19
|
+
* default: 'sse',
|
|
20
|
+
* drivers: {
|
|
21
|
+
* sse: { driver: 'sse' },
|
|
22
|
+
* pusher: {
|
|
23
|
+
* driver: 'pusher',
|
|
24
|
+
* key: env('PUSHER_KEY'),
|
|
25
|
+
* secret: env('PUSHER_SECRET'),
|
|
26
|
+
* appId: env('PUSHER_APP_ID'),
|
|
27
|
+
* cluster: env('PUSHER_CLUSTER', 'mt1'),
|
|
28
|
+
* host: env('PUSHER_HOST'), // For Soketi
|
|
29
|
+
* port: env<number>('PUSHER_PORT'), // For Soketi
|
|
30
|
+
* useTLS: env<boolean>('PUSHER_TLS', true),
|
|
31
|
+
* },
|
|
32
|
+
* },
|
|
33
|
+
* });
|
|
34
|
+
*
|
|
35
|
+
* // Register channel authorization
|
|
36
|
+
* Broadcast.channel('private-orders.{orderId}', async (user, { orderId }) => {
|
|
37
|
+
* return user.id === (await Order.findOrFail(orderId)).user_id;
|
|
38
|
+
* });
|
|
39
|
+
*
|
|
40
|
+
* Broadcast.channel('presence-chat.{roomId}', async (user, { roomId }) => {
|
|
41
|
+
* const room = await ChatRoom.findOrFail(roomId);
|
|
42
|
+
* if (!room.hasMember(user.id)) return false;
|
|
43
|
+
* return { id: user.id, name: user.name, avatar: user.avatar };
|
|
44
|
+
* });
|
|
45
|
+
*
|
|
46
|
+
* // Broadcast events from anywhere
|
|
47
|
+
* Broadcast.event('OrderShipped', { orderId: 123 })
|
|
48
|
+
* .on('private-orders.123')
|
|
49
|
+
* .send();
|
|
50
|
+
*
|
|
51
|
+
* // Or the shorthand
|
|
52
|
+
* Broadcast.to('private-orders.123').send('OrderShipped', { orderId: 123 });
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export interface BroadcastConfig {
|
|
56
|
+
default: string;
|
|
57
|
+
drivers: Record<string, BroadcastDriverConfig>;
|
|
58
|
+
}
|
|
59
|
+
export type BroadcastDriverConfig = {
|
|
60
|
+
driver: 'sse';
|
|
61
|
+
} | {
|
|
62
|
+
driver: 'log';
|
|
63
|
+
} | {
|
|
64
|
+
driver: 'pusher';
|
|
65
|
+
key: string;
|
|
66
|
+
secret: string;
|
|
67
|
+
appId: string;
|
|
68
|
+
cluster?: string;
|
|
69
|
+
host?: string;
|
|
70
|
+
port?: number;
|
|
71
|
+
useTLS?: boolean;
|
|
72
|
+
};
|
|
73
|
+
export interface BroadcastEvent {
|
|
74
|
+
event: string;
|
|
75
|
+
data: any;
|
|
76
|
+
id?: string;
|
|
77
|
+
}
|
|
78
|
+
/** Channel authorization callback */
|
|
79
|
+
export type ChannelAuthCallback = (user: any, params: Record<string, string>) => Promise<boolean | Record<string, any>> | boolean | Record<string, any>;
|
|
80
|
+
/** Presence channel member info */
|
|
81
|
+
export interface PresenceMember {
|
|
82
|
+
id: string | number;
|
|
83
|
+
[key: string]: any;
|
|
84
|
+
}
|
|
85
|
+
export type ChannelType = 'public' | 'private' | 'presence';
|
|
86
|
+
/**
|
|
87
|
+
* Determine channel type from its name.
|
|
88
|
+
* - 'private-xxx' → private
|
|
89
|
+
* - 'presence-xxx' → presence
|
|
90
|
+
* - anything else → public
|
|
91
|
+
*/
|
|
92
|
+
export declare function channelType(name: string): ChannelType;
|
|
93
|
+
declare class SSEChannel {
|
|
94
|
+
private subscribers;
|
|
95
|
+
readonly name: string;
|
|
96
|
+
readonly type: ChannelType;
|
|
97
|
+
constructor(name: string);
|
|
98
|
+
/**
|
|
99
|
+
* Create an SSE streaming response for this channel.
|
|
100
|
+
* For private/presence channels, authorization must be checked BEFORE calling this.
|
|
101
|
+
*/
|
|
102
|
+
stream(userId?: string | number, userInfo?: Record<string, any>): Response;
|
|
103
|
+
/**
|
|
104
|
+
* Send an event to all subscribers
|
|
105
|
+
*/
|
|
106
|
+
send(event: string, data: any, targetUserId?: string | number): void;
|
|
107
|
+
/** Internal send that excludes a specific subscriber (for join/leave events) */
|
|
108
|
+
private sendInternal;
|
|
109
|
+
/**
|
|
110
|
+
* Send to a specific user in this channel
|
|
111
|
+
*/
|
|
112
|
+
toUser(userId: string | number): {
|
|
113
|
+
send: (event: string, data: any) => void;
|
|
114
|
+
};
|
|
115
|
+
/**
|
|
116
|
+
* Get the number of active subscribers
|
|
117
|
+
*/
|
|
118
|
+
subscriberCount(): number;
|
|
119
|
+
/**
|
|
120
|
+
* Get presence members (for presence channels)
|
|
121
|
+
*/
|
|
122
|
+
getMembers(): PresenceMember[];
|
|
123
|
+
/**
|
|
124
|
+
* Check if a user is present in this channel
|
|
125
|
+
*/
|
|
126
|
+
hasMember(userId: string | number): boolean;
|
|
127
|
+
/**
|
|
128
|
+
* Whisper — send a client event to other users (not from server).
|
|
129
|
+
* Useful for typing indicators, cursor positions, etc.
|
|
130
|
+
*/
|
|
131
|
+
whisper(event: string, data: any, fromUserId: string | number): void;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Pusher/Soketi broadcast driver.
|
|
135
|
+
* Sends events via HTTP API (compatible with Pusher and Soketi).
|
|
136
|
+
* Clients connect using pusher-js.
|
|
137
|
+
*/
|
|
138
|
+
declare class PusherDriver {
|
|
139
|
+
private config;
|
|
140
|
+
constructor(config: Extract<BroadcastDriverConfig, {
|
|
141
|
+
driver: 'pusher';
|
|
142
|
+
}>);
|
|
143
|
+
/**
|
|
144
|
+
* Send an event via the Pusher HTTP API
|
|
145
|
+
*/
|
|
146
|
+
send(channels: string | string[], event: string, data: any): Promise<void>;
|
|
147
|
+
/**
|
|
148
|
+
* Generate auth signature for private/presence channel subscriptions.
|
|
149
|
+
* Used in the channel authorization endpoint.
|
|
150
|
+
*/
|
|
151
|
+
authenticate(socketId: string, channelName: string, presenceData?: {
|
|
152
|
+
user_id: string | number;
|
|
153
|
+
user_info?: Record<string, any>;
|
|
154
|
+
}): Promise<{
|
|
155
|
+
auth: string;
|
|
156
|
+
channel_data?: string;
|
|
157
|
+
}>;
|
|
158
|
+
/** Get the Pusher app key (needed by clients) */
|
|
159
|
+
get key(): string;
|
|
160
|
+
/** Get connection config for the client */
|
|
161
|
+
clientConfig(): Record<string, any>;
|
|
162
|
+
private md5;
|
|
163
|
+
private hmacSha256;
|
|
164
|
+
}
|
|
165
|
+
declare class BroadcastEventBuilder {
|
|
166
|
+
private manager;
|
|
167
|
+
private eventName;
|
|
168
|
+
private eventData;
|
|
169
|
+
private channels;
|
|
170
|
+
constructor(manager: BroadcastManager, eventName: string, eventData: any);
|
|
171
|
+
/** Specify which channel(s) to broadcast on */
|
|
172
|
+
on(...channels: string[]): BroadcastEventBuilder;
|
|
173
|
+
/** Send the event to all specified channels */
|
|
174
|
+
send(): Promise<void>;
|
|
175
|
+
}
|
|
176
|
+
declare class BroadcastManager {
|
|
177
|
+
private config;
|
|
178
|
+
private sseChannels;
|
|
179
|
+
private channelAuth;
|
|
180
|
+
private pusherDriver;
|
|
181
|
+
/**
|
|
182
|
+
* Configure broadcasting drivers.
|
|
183
|
+
*/
|
|
184
|
+
configure(config: BroadcastConfig): void;
|
|
185
|
+
/**
|
|
186
|
+
* Register an authorization callback for a private or presence channel.
|
|
187
|
+
* Pattern supports `{param}` placeholders that are extracted from the channel name.
|
|
188
|
+
*
|
|
189
|
+
* Return `true` for private channels to allow access.
|
|
190
|
+
* Return an object with user info for presence channels (or `false` to deny).
|
|
191
|
+
*
|
|
192
|
+
* @example
|
|
193
|
+
* // Private channel
|
|
194
|
+
* Broadcast.channel('private-orders.{orderId}', async (user, { orderId }) => {
|
|
195
|
+
* const order = await Order.findOrFail(orderId);
|
|
196
|
+
* return order.user_id === user.id;
|
|
197
|
+
* });
|
|
198
|
+
*
|
|
199
|
+
* // Presence channel — return user info to share with other members
|
|
200
|
+
* Broadcast.channel('presence-chat.{roomId}', async (user, { roomId }) => {
|
|
201
|
+
* const room = await ChatRoom.findOrFail(roomId);
|
|
202
|
+
* if (!room.hasMember(user.id)) return false;
|
|
203
|
+
* return { id: user.id, name: user.name, avatar: user.avatar };
|
|
204
|
+
* });
|
|
205
|
+
*/
|
|
206
|
+
channel(pattern: string, callback: ChannelAuthCallback): void;
|
|
207
|
+
/**
|
|
208
|
+
* Get or create an SSE channel for subscribing.
|
|
209
|
+
*/
|
|
210
|
+
channel(name: string): SSEChannel;
|
|
211
|
+
/**
|
|
212
|
+
* Authorize a user to subscribe to a private or presence channel.
|
|
213
|
+
* Called from your channel authorization endpoint.
|
|
214
|
+
*
|
|
215
|
+
* Returns `false` if denied, `true` for private channels,
|
|
216
|
+
* or a presence member object for presence channels.
|
|
217
|
+
*/
|
|
218
|
+
authorize(channelName: string, user: any): Promise<false | true | Record<string, any>>;
|
|
219
|
+
/**
|
|
220
|
+
* Authenticate a Pusher channel subscription.
|
|
221
|
+
* Use this in your channel auth endpoint for Pusher/Soketi.
|
|
222
|
+
*
|
|
223
|
+
* @example
|
|
224
|
+
* // src/routes/api/broadcasting/auth/+server.ts
|
|
225
|
+
* export const POST: RequestHandler = async ({ request, locals }) => {
|
|
226
|
+
* if (!locals.user) return new Response('Unauthorized', { status: 401 });
|
|
227
|
+
*
|
|
228
|
+
* const form = await request.formData();
|
|
229
|
+
* const socketId = form.get('socket_id') as string;
|
|
230
|
+
* const channelName = form.get('channel_name') as string;
|
|
231
|
+
*
|
|
232
|
+
* const auth = await Broadcast.authenticatePusher(channelName, socketId, locals.user);
|
|
233
|
+
* if (!auth) return new Response('Forbidden', { status: 403 });
|
|
234
|
+
*
|
|
235
|
+
* return new Response(JSON.stringify(auth), {
|
|
236
|
+
* headers: { 'Content-Type': 'application/json' },
|
|
237
|
+
* });
|
|
238
|
+
* };
|
|
239
|
+
*/
|
|
240
|
+
authenticatePusher(channelName: string, socketId: string, user: any): Promise<{
|
|
241
|
+
auth: string;
|
|
242
|
+
channel_data?: string;
|
|
243
|
+
} | false>;
|
|
244
|
+
/**
|
|
245
|
+
* Create an event builder for fluent broadcasting.
|
|
246
|
+
*
|
|
247
|
+
* @example
|
|
248
|
+
* Broadcast.event('OrderShipped', { orderId: 123 })
|
|
249
|
+
* .on('private-orders.123')
|
|
250
|
+
* .send();
|
|
251
|
+
*/
|
|
252
|
+
event(name: string, data: any): BroadcastEventBuilder;
|
|
253
|
+
/**
|
|
254
|
+
* Shorthand — get a sender for a channel.
|
|
255
|
+
*
|
|
256
|
+
* @example
|
|
257
|
+
* Broadcast.to('private-orders.123').send('OrderShipped', { orderId: 123 });
|
|
258
|
+
* Broadcast.to('notifications', userId).send('new-message', { text: 'Hello!' });
|
|
259
|
+
*/
|
|
260
|
+
to(channelName: string, userId?: string | number): {
|
|
261
|
+
send: (event: string, data: any) => Promise<void>;
|
|
262
|
+
};
|
|
263
|
+
/**
|
|
264
|
+
* Send an event to a channel. Used internally and by the event builder.
|
|
265
|
+
* Routes to the appropriate driver.
|
|
266
|
+
*/
|
|
267
|
+
sendToChannel(channelName: string, event: string, data: any, targetUserId?: string | number): Promise<void>;
|
|
268
|
+
/**
|
|
269
|
+
* Subscribe to an SSE channel. For private/presence channels,
|
|
270
|
+
* call `authorize()` first and check the result before calling `subscribe()`.
|
|
271
|
+
*
|
|
272
|
+
* @example
|
|
273
|
+
* // Public channel
|
|
274
|
+
* return Broadcast.subscribe('updates');
|
|
275
|
+
*
|
|
276
|
+
* // Private channel (check auth first)
|
|
277
|
+
* const allowed = await Broadcast.authorize('private-orders.123', locals.user);
|
|
278
|
+
* if (!allowed) return new Response('Forbidden', { status: 403 });
|
|
279
|
+
* return Broadcast.subscribe('private-orders.123', locals.user.id);
|
|
280
|
+
*
|
|
281
|
+
* // Presence channel (pass user info for member tracking)
|
|
282
|
+
* const presenceInfo = await Broadcast.authorize('presence-chat.1', locals.user);
|
|
283
|
+
* if (!presenceInfo) return new Response('Forbidden', { status: 403 });
|
|
284
|
+
* return Broadcast.subscribe('presence-chat.1', locals.user.id, presenceInfo);
|
|
285
|
+
*/
|
|
286
|
+
subscribe(channelName: string, userId?: string | number, userInfo?: Record<string, any>): Response;
|
|
287
|
+
/**
|
|
288
|
+
* Get members of a presence channel (SSE driver only)
|
|
289
|
+
*/
|
|
290
|
+
members(channelName: string): PresenceMember[];
|
|
291
|
+
/**
|
|
292
|
+
* Get the Pusher driver instance (for advanced usage)
|
|
293
|
+
*/
|
|
294
|
+
pusher(): PusherDriver;
|
|
295
|
+
/**
|
|
296
|
+
* Get all active channel names
|
|
297
|
+
*/
|
|
298
|
+
activeChannels(): string[];
|
|
299
|
+
/**
|
|
300
|
+
* Get total subscriber count across all SSE channels
|
|
301
|
+
*/
|
|
302
|
+
totalSubscribers(): number;
|
|
303
|
+
/**
|
|
304
|
+
* Remove empty SSE channels (cleanup)
|
|
305
|
+
*/
|
|
306
|
+
prune(): void;
|
|
307
|
+
/**
|
|
308
|
+
* Match a channel pattern like 'private-orders.{orderId}'
|
|
309
|
+
* against a channel name like 'private-orders.123'.
|
|
310
|
+
* Returns extracted params or null if no match.
|
|
311
|
+
*/
|
|
312
|
+
private matchPattern;
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Global Broadcast singleton
|
|
316
|
+
*/
|
|
317
|
+
export declare const Broadcast: BroadcastManager;
|
|
318
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
function b(c,e){let n=Symbol.for(c),t=globalThis;return t[n]||(t[n]=e()),t[n]}function d(c){return c.startsWith("private-")?"private":c.startsWith("presence-")?"presence":"public"}var l=class{subscribers=[];name;type;constructor(e){this.name=e,this.type=d(e)}stream(e,n){let t=this,i=new ReadableStream({start(r){let s={channel:t.name};t.type==="presence"&&(s.members=t.getMembers());let o=`event: connected
|
|
2
|
+
data: ${JSON.stringify(s)}
|
|
3
|
+
id: ${Date.now()}
|
|
4
|
+
|
|
5
|
+
`;r.enqueue(new TextEncoder().encode(o));let a={controller:r,userId:e,userInfo:n};t.subscribers.push(a),t.type==="presence"&&e!==void 0&&t.sendInternal("member:joined",{id:e,...n},a)},cancel(){let r=t.subscribers.findIndex(o=>o.controller===this._controller),s=t.subscribers.length;t.subscribers=t.subscribers.filter(o=>{try{return o.controller.enqueue(new TextEncoder().encode(`:
|
|
6
|
+
|
|
7
|
+
`)),!0}catch{return!1}}),t.type==="presence"&&e!==void 0&&t.subscribers.length<s&&t.sendInternal("member:left",{id:e,...n})}});return new Response(i,{headers:{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive","X-Accel-Buffering":"no"}})}send(e,n,t){let i=`event: ${e}
|
|
8
|
+
data: ${JSON.stringify(n)}
|
|
9
|
+
id: ${Date.now()}
|
|
10
|
+
|
|
11
|
+
`,r=new TextEncoder().encode(i);for(let s of this.subscribers)if(!(t!==void 0&&s.userId!==t))try{s.controller.enqueue(r)}catch{}}sendInternal(e,n,t){let i=`event: ${e}
|
|
12
|
+
data: ${JSON.stringify(n)}
|
|
13
|
+
id: ${Date.now()}
|
|
14
|
+
|
|
15
|
+
`,r=new TextEncoder().encode(i);for(let s of this.subscribers)if(s!==t)try{s.controller.enqueue(r)}catch{}}toUser(e){return{send:(n,t)=>this.send(n,t,e)}}subscriberCount(){return this.subscribers.length}getMembers(){return this.type!=="presence"?[]:this.subscribers.filter(e=>e.userId!==void 0).map(e=>({id:e.userId,...e.userInfo??{}}))}hasMember(e){return this.subscribers.some(n=>n.userId===e)}whisper(e,n,t){let i=`event: client-${e}
|
|
16
|
+
data: ${JSON.stringify(n)}
|
|
17
|
+
id: ${Date.now()}
|
|
18
|
+
|
|
19
|
+
`,r=new TextEncoder().encode(i);for(let s of this.subscribers)if(s.userId!==t)try{s.controller.enqueue(r)}catch{}}},h=class{config;constructor(e){this.config=e}async send(e,n,t){let i=Array.isArray(e)?e:[e],r=JSON.stringify({name:n,channels:i,data:JSON.stringify(t)}),s=Math.floor(Date.now()/1e3).toString(),o=await this.md5(r),a=["POST",`/apps/${this.config.appId}/events`,[`auth_key=${this.config.key}`,`auth_timestamp=${s}`,"auth_version=1.0",`body_md5=${o}`].join("&")].join(`
|
|
20
|
+
`),u=await this.hmacSha256(this.config.secret,a),y=this.config.useTLS!==!1?"https":"http",v=this.config.host??`api-${this.config.cluster??"mt1"}.pusher.com`,m=this.config.port??(this.config.useTLS!==!1?443:80),w=`${y}://${v}:${m}/apps/${this.config.appId}/events?auth_key=${this.config.key}&auth_timestamp=${s}&auth_version=1.0&body_md5=${o}&auth_signature=${u}`,g=await fetch(w,{method:"POST",headers:{"Content-Type":"application/json"},body:r});if(!g.ok){let C=await g.text();throw new Error(`Pusher API error (${g.status}): ${C}`)}}async authenticate(e,n,t){let i=`${e}:${n}`,r;t&&(r=JSON.stringify(t),i+=`:${r}`);let s=await this.hmacSha256(this.config.secret,i),o=`${this.config.key}:${s}`;return r?{auth:o,channel_data:r}:{auth:o}}get key(){return this.config.key}clientConfig(){let e={key:this.config.key,cluster:this.config.cluster??"mt1"};return this.config.host&&(e.wsHost=this.config.host,e.wsPort=this.config.port??6001,e.wssPort=this.config.port??6001,e.forceTLS=this.config.useTLS??!1,e.enabledTransports=["ws","wss"],e.disableStats=!0),e}async md5(e){let{createHash:n}=await import("crypto");return n("md5").update(e).digest("hex")}async hmacSha256(e,n){let{createHmac:t}=await import("crypto");return t("sha256",e).update(n).digest("hex")}},f=class{constructor(e,n,t){this.manager=e;this.eventName=n;this.eventData=t}channels=[];on(...e){return this.channels.push(...e),this}async send(){for(let e of this.channels)await this.manager.sendToChannel(e,this.eventName,this.eventData)}},p=class{config={default:"sse",drivers:{sse:{driver:"sse"}}};sseChannels=new Map;channelAuth=new Map;pusherDriver=null;configure(e){this.config=e;let n=Object.values(e.drivers).find(t=>t.driver==="pusher");n&&n.driver==="pusher"&&(this.pusherDriver=new h(n))}channel(e,n){if(n){this.channelAuth.set(e,n);return}return this.sseChannels.has(e)||this.sseChannels.set(e,new l(e)),this.sseChannels.get(e)}async authorize(e,n){if(d(e)==="public")return!0;for(let[i,r]of this.channelAuth){let s=this.matchPattern(i,e);if(s!==null)return await r(n,s)}return!1}async authenticatePusher(e,n,t){if(!this.pusherDriver)throw new Error("Pusher driver is not configured. Call Broadcast.configure() first.");let i=d(e);if(i==="public")return!1;let r=await this.authorize(e,t);if(r===!1)return!1;if(i==="presence"){let s=typeof r=="object"?{user_id:r.id??t.id,user_info:r}:{user_id:t.id,user_info:{id:t.id}};return this.pusherDriver.authenticate(n,e,s)}return this.pusherDriver.authenticate(n,e)}event(e,n){return new f(this,e,n)}to(e,n){return{send:async(t,i)=>{await this.sendToChannel(e,t,i,n)}}}async sendToChannel(e,n,t,i){let r=this.config.drivers[this.config.default];switch(r?.driver){case"pusher":this.pusherDriver||(this.pusherDriver=new h(r)),await this.pusherDriver.send(e,n,t);break;case"log":console.log(`[Broadcast] ${e} \u2192 ${n}:`,JSON.stringify(t));break;default:for(let[,s]of this.sseChannels)s.name===e&&s.send(n,t,i);break}}subscribe(e,n,t){return this.channel(e).stream(n,t)}members(e){let n=this.sseChannels.get(e);return n?n.getMembers():[]}pusher(){if(!this.pusherDriver)throw new Error("Pusher driver is not configured.");return this.pusherDriver}activeChannels(){return[...new Set([...this.sseChannels.values()].map(e=>e.name))]}totalSubscribers(){let e=0;for(let n of this.sseChannels.values())e+=n.subscriberCount();return e}prune(){for(let[e,n]of this.sseChannels)n.subscriberCount()===0&&this.sseChannels.delete(e)}matchPattern(e,n){let t=[],i=e.replace(/[.*+?^${}()|[\]\\]/g,a=>a==="{"||a==="}"?a:`\\${a}`).replace(/\\\{(\w+)\\\}/g,(a,u)=>(t.push(u),"([^.]+)")).replace(/\{(\w+)\}/g,(a,u)=>(t.push(u),"([^.]+)")),r=new RegExp(`^${i}$`),s=n.match(r);if(!s)return null;let o={};for(let a=0;a<t.length;a++)o[t[a]]=s[a+1];return o}},T=b("svelar.broadcast",()=>new p);export{T as Broadcast,d as channelType};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Svelar Cache
|
|
3
|
+
*
|
|
4
|
+
* Laravel-inspired cache with memory, file, and Redis drivers.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* import { Cache } from 'svelar/cache';
|
|
9
|
+
*
|
|
10
|
+
* Cache.configure({ default: 'memory', stores: { memory: { driver: 'memory' } } });
|
|
11
|
+
*
|
|
12
|
+
* await Cache.put('key', 'value', 3600); // 1 hour TTL
|
|
13
|
+
* const value = await Cache.get('key', 'default');
|
|
14
|
+
* await Cache.forget('key');
|
|
15
|
+
*
|
|
16
|
+
* // Remember pattern (fetch or compute)
|
|
17
|
+
* const users = await Cache.remember('all-users', 600, async () => {
|
|
18
|
+
* return await User.all();
|
|
19
|
+
* });
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export type CacheDriver = 'memory' | 'file' | 'redis' | 'null';
|
|
23
|
+
export interface CacheStoreConfig {
|
|
24
|
+
driver: CacheDriver;
|
|
25
|
+
/** Default TTL in seconds */
|
|
26
|
+
ttl?: number;
|
|
27
|
+
/** File cache: directory path */
|
|
28
|
+
path?: string;
|
|
29
|
+
/** Redis cache: connection URL or config */
|
|
30
|
+
url?: string;
|
|
31
|
+
/** Key prefix */
|
|
32
|
+
prefix?: string;
|
|
33
|
+
}
|
|
34
|
+
export interface CacheConfig {
|
|
35
|
+
default: string;
|
|
36
|
+
stores: Record<string, CacheStoreConfig>;
|
|
37
|
+
}
|
|
38
|
+
interface CacheStore {
|
|
39
|
+
get<T = any>(key: string): Promise<T | null>;
|
|
40
|
+
put(key: string, value: any, ttl?: number): Promise<void>;
|
|
41
|
+
forget(key: string): Promise<boolean>;
|
|
42
|
+
flush(): Promise<void>;
|
|
43
|
+
has(key: string): Promise<boolean>;
|
|
44
|
+
increment(key: string, amount?: number): Promise<number>;
|
|
45
|
+
decrement(key: string, amount?: number): Promise<number>;
|
|
46
|
+
}
|
|
47
|
+
declare class CacheManager {
|
|
48
|
+
private config;
|
|
49
|
+
private stores;
|
|
50
|
+
configure(config: CacheConfig): void;
|
|
51
|
+
store(name?: string): CacheStore;
|
|
52
|
+
get<T = any>(key: string, defaultValue?: T): Promise<T | null>;
|
|
53
|
+
put(key: string, value: any, ttl?: number): Promise<void>;
|
|
54
|
+
forget(key: string): Promise<boolean>;
|
|
55
|
+
flush(): Promise<void>;
|
|
56
|
+
has(key: string): Promise<boolean>;
|
|
57
|
+
increment(key: string, amount?: number): Promise<number>;
|
|
58
|
+
decrement(key: string, amount?: number): Promise<number>;
|
|
59
|
+
/**
|
|
60
|
+
* Get or compute a cached value
|
|
61
|
+
*/
|
|
62
|
+
remember<T>(key: string, ttl: number, callback: () => T | Promise<T>): Promise<T>;
|
|
63
|
+
/**
|
|
64
|
+
* Get or compute, caching forever
|
|
65
|
+
*/
|
|
66
|
+
rememberForever<T>(key: string, callback: () => T | Promise<T>): Promise<T>;
|
|
67
|
+
/**
|
|
68
|
+
* Get and delete
|
|
69
|
+
*/
|
|
70
|
+
pull<T = any>(key: string, defaultValue?: T): Promise<T | null>;
|
|
71
|
+
private createStore;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Global Cache singleton
|
|
75
|
+
*/
|
|
76
|
+
export declare const Cache: CacheManager;
|
|
77
|
+
export type { CacheStore };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{readFile as g,writeFile as f,unlink as h,mkdir as l}from"fs/promises";import{join as y,dirname as p}from"path";import{createHash as d}from"crypto";function m(i,e){let r=Symbol.for(i),t=globalThis;return t[r]||(t[r]=e()),t[r]}var a=class{store=new Map;async get(e){let r=this.store.get(e);return r?r.expiresAt&&Date.now()>r.expiresAt?(this.store.delete(e),null):r.value:null}async put(e,r,t){this.store.set(e,{value:r,expiresAt:t?Date.now()+t*1e3:null})}async forget(e){return this.store.delete(e)}async flush(){this.store.clear()}async has(e){let r=this.store.get(e);return r?r.expiresAt&&Date.now()>r.expiresAt?(this.store.delete(e),!1):!0:!1}async increment(e,r=1){let s=(await this.get(e)??0)+r,n=this.store.get(e);return await this.put(e,s,n?.expiresAt?Math.ceil((n.expiresAt-Date.now())/1e3):void 0),s}async decrement(e,r=1){return this.increment(e,-r)}},o=class{basePath;constructor(e){this.basePath=e.path??"storage/cache"}filePath(e){let r=d("md5").update(e).digest("hex");return y(this.basePath,r.slice(0,2),r)}async get(e){let r=this.filePath(e);try{let t=await g(r,"utf-8"),s=JSON.parse(t);return s.expiresAt&&Date.now()>s.expiresAt?(await h(r).catch(()=>{}),null):s.value}catch{return null}}async put(e,r,t){let s=this.filePath(e),n={value:r,expiresAt:t?Date.now()+t*1e3:null};await l(p(s),{recursive:!0}),await f(s,JSON.stringify(n))}async forget(e){try{return await h(this.filePath(e)),!0}catch{return!1}}async flush(){let{rm:e}=await import("fs/promises");await e(this.basePath,{recursive:!0,force:!0}),await l(this.basePath,{recursive:!0})}async has(e){return await this.get(e)!==null}async increment(e,r=1){let s=(await this.get(e)??0)+r;return await this.put(e,s),s}async decrement(e,r=1){return this.increment(e,-r)}},c=class{async get(){return null}async put(){}async forget(){return!0}async flush(){}async has(){return!1}async increment(){return 0}async decrement(){return 0}},u=class{config={default:"memory",stores:{memory:{driver:"memory"}}};stores=new Map;configure(e){this.config=e,this.stores.clear()}store(e){let r=e??this.config.default;if(this.stores.has(r))return this.stores.get(r);let t=this.config.stores[r];if(!t)throw new Error(`Cache store "${r}" is not defined.`);let s=this.createStore(t);return this.stores.set(r,s),s}async get(e,r){let t=this.store();return await t.has(e)?t.get(e):r??null}async put(e,r,t){return this.store().put(e,r,t??this.config.stores[this.config.default]?.ttl)}async forget(e){return this.store().forget(e)}async flush(){return this.store().flush()}async has(e){return this.store().has(e)}async increment(e,r){return this.store().increment(e,r)}async decrement(e,r){return this.store().decrement(e,r)}async remember(e,r,t){let s=await this.store().get(e);if(s!==null)return s;let n=await t();return await this.store().put(e,n,r),n}async rememberForever(e,r){let t=await this.store().get(e);if(t!==null)return t;let s=await r();return await this.store().put(e,s),s}async pull(e,r){let t=await this.get(e,r);return await this.forget(e),t}createStore(e){switch(e.driver){case"memory":return new a;case"file":return new o(e);case"null":return new c;case"redis":throw new Error("Redis cache requires ioredis. Install: npm install ioredis");default:throw new Error(`Unknown cache driver: ${e.driver}`)}}},x=m("svelar.cache",()=>new u);export{x as Cache};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Svelar CLI Runner
|
|
3
|
+
*/
|
|
4
|
+
import { Command } from './Command.js';
|
|
5
|
+
export declare class Cli {
|
|
6
|
+
private commands;
|
|
7
|
+
private version;
|
|
8
|
+
constructor(version?: string);
|
|
9
|
+
/**
|
|
10
|
+
* Register a command
|
|
11
|
+
*/
|
|
12
|
+
register(CommandClass: new () => Command): this;
|
|
13
|
+
/**
|
|
14
|
+
* Register a command instance
|
|
15
|
+
*/
|
|
16
|
+
add(command: Command): this;
|
|
17
|
+
/**
|
|
18
|
+
* Execute CLI with process arguments
|
|
19
|
+
*/
|
|
20
|
+
run(argv?: string[]): Promise<void>;
|
|
21
|
+
private parseArgs;
|
|
22
|
+
private showHelp;
|
|
23
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Svelar CLI Command Base
|
|
3
|
+
*/
|
|
4
|
+
export interface CommandFlag {
|
|
5
|
+
name: string;
|
|
6
|
+
alias?: string;
|
|
7
|
+
description: string;
|
|
8
|
+
type: 'boolean' | 'string';
|
|
9
|
+
default?: any;
|
|
10
|
+
}
|
|
11
|
+
export declare abstract class Command {
|
|
12
|
+
/** Command name (e.g. 'make:model') */
|
|
13
|
+
abstract name: string;
|
|
14
|
+
/** Description shown in help */
|
|
15
|
+
abstract description: string;
|
|
16
|
+
/** Positional arguments description */
|
|
17
|
+
arguments: string[];
|
|
18
|
+
/** Available flags */
|
|
19
|
+
flags: CommandFlag[];
|
|
20
|
+
/** Execute the command */
|
|
21
|
+
abstract handle(args: string[], flags: Record<string, any>): Promise<void>;
|
|
22
|
+
/**
|
|
23
|
+
* Bootstrap database configuration for CLI commands.
|
|
24
|
+
*
|
|
25
|
+
* Reads a `svelar.database.json` file if present, or uses env vars
|
|
26
|
+
* and sensible defaults (SQLite with `database.db`).
|
|
27
|
+
*/
|
|
28
|
+
protected bootstrap(): Promise<void>;
|
|
29
|
+
protected log(message: string): void;
|
|
30
|
+
protected info(message: string): void;
|
|
31
|
+
protected success(message: string): void;
|
|
32
|
+
protected warn(message: string): void;
|
|
33
|
+
protected error(message: string): void;
|
|
34
|
+
protected table(headers: string[], rows: string[][]): void;
|
|
35
|
+
protected newLine(): void;
|
|
36
|
+
}
|