@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.
Files changed (207) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +110 -0
  3. package/dist/actions/index.d.ts +101 -0
  4. package/dist/actions/index.js +1 -0
  5. package/dist/api-keys/index.d.ts +58 -0
  6. package/dist/api-keys/index.js +1 -0
  7. package/dist/audit/index.d.ts +52 -0
  8. package/dist/audit/index.js +1 -0
  9. package/dist/auth/Auth.d.ts +283 -0
  10. package/dist/auth/Gate.d.ts +166 -0
  11. package/dist/auth/index.d.ts +2 -0
  12. package/dist/auth/index.js +80 -0
  13. package/dist/broadcasting/client.d.ts +195 -0
  14. package/dist/broadcasting/client.js +1 -0
  15. package/dist/broadcasting/index.d.ts +318 -0
  16. package/dist/broadcasting/index.js +20 -0
  17. package/dist/cache/index.d.ts +77 -0
  18. package/dist/cache/index.js +1 -0
  19. package/dist/cli/Cli.d.ts +23 -0
  20. package/dist/cli/Command.d.ts +36 -0
  21. package/dist/cli/bin.d.ts +8 -0
  22. package/dist/cli/bin.js +5856 -0
  23. package/dist/cli/commands/KeyGenerateCommand.d.ts +16 -0
  24. package/dist/cli/commands/MakeActionCommand.d.ts +15 -0
  25. package/dist/cli/commands/MakeBroadcastingCommand.d.ts +29 -0
  26. package/dist/cli/commands/MakeChannelCommand.d.ts +18 -0
  27. package/dist/cli/commands/MakeCommandCommand.d.ts +16 -0
  28. package/dist/cli/commands/MakeConfigCommand.d.ts +13 -0
  29. package/dist/cli/commands/MakeControllerCommand.d.ts +28 -0
  30. package/dist/cli/commands/MakeDashboardCommand.d.ts +34 -0
  31. package/dist/cli/commands/MakeDockerCommand.d.ts +32 -0
  32. package/dist/cli/commands/MakeEventCommand.d.ts +11 -0
  33. package/dist/cli/commands/MakeJobCommand.d.ts +11 -0
  34. package/dist/cli/commands/MakeListenerCommand.d.ts +16 -0
  35. package/dist/cli/commands/MakeMiddlewareCommand.d.ts +11 -0
  36. package/dist/cli/commands/MakeMigrationCommand.d.ts +17 -0
  37. package/dist/cli/commands/MakeModelCommand.d.ts +25 -0
  38. package/dist/cli/commands/MakeObserverCommand.d.ts +23 -0
  39. package/dist/cli/commands/MakePluginCommand.d.ts +11 -0
  40. package/dist/cli/commands/MakeProviderCommand.d.ts +11 -0
  41. package/dist/cli/commands/MakeRepositoryCommand.d.ts +22 -0
  42. package/dist/cli/commands/MakeRequestCommand.d.ts +15 -0
  43. package/dist/cli/commands/MakeResourceCommand.d.ts +30 -0
  44. package/dist/cli/commands/MakeRouteCommand.d.ts +42 -0
  45. package/dist/cli/commands/MakeSchemaCommand.d.ts +20 -0
  46. package/dist/cli/commands/MakeSeederCommand.d.ts +11 -0
  47. package/dist/cli/commands/MakeServiceCommand.d.ts +28 -0
  48. package/dist/cli/commands/MakeTaskCommand.d.ts +12 -0
  49. package/dist/cli/commands/MigrateCommand.d.ts +26 -0
  50. package/dist/cli/commands/NewCommand.d.ts +21 -0
  51. package/dist/cli/commands/NewCommandTemplates.d.ts +123 -0
  52. package/dist/cli/commands/PluginInstallCommand.d.ts +16 -0
  53. package/dist/cli/commands/PluginListCommand.d.ts +11 -0
  54. package/dist/cli/commands/PluginPublishCommand.d.ts +22 -0
  55. package/dist/cli/commands/QueueFailedCommand.d.ts +9 -0
  56. package/dist/cli/commands/QueueFlushCommand.d.ts +9 -0
  57. package/dist/cli/commands/QueueRetryCommand.d.ts +16 -0
  58. package/dist/cli/commands/QueueWorkCommand.d.ts +25 -0
  59. package/dist/cli/commands/RoutesListCommand.d.ts +30 -0
  60. package/dist/cli/commands/ScheduleRunCommand.d.ts +15 -0
  61. package/dist/cli/commands/SeedCommand.d.ts +14 -0
  62. package/dist/cli/commands/TinkerCommand.d.ts +10 -0
  63. package/dist/cli/index.d.ts +36 -0
  64. package/dist/cli/index.js +1973 -0
  65. package/dist/cli/ts-resolve-hook.mjs +74 -0
  66. package/dist/cli/ts-resolver.mjs +8 -0
  67. package/dist/config/Config.d.ts +65 -0
  68. package/dist/config/index.d.ts +1 -0
  69. package/dist/config/index.js +1 -0
  70. package/dist/container/Application.d.ts +33 -0
  71. package/dist/container/Container.d.ts +70 -0
  72. package/dist/container/ServiceProvider.d.ts +21 -0
  73. package/dist/container/index.d.ts +3 -0
  74. package/dist/container/index.js +1 -0
  75. package/dist/dashboard/index.d.ts +123 -0
  76. package/dist/dashboard/index.js +5 -0
  77. package/dist/database/Connection.d.ts +80 -0
  78. package/dist/database/Migration.d.ts +76 -0
  79. package/dist/database/SchemaBuilder.d.ts +91 -0
  80. package/dist/database/Seeder.d.ts +9 -0
  81. package/dist/database/index.d.ts +4 -0
  82. package/dist/database/index.js +4 -0
  83. package/dist/email-templates/index.d.ts +51 -0
  84. package/dist/email-templates/index.js +57 -0
  85. package/dist/errors/Handler.d.ts +100 -0
  86. package/dist/errors/index.d.ts +1 -0
  87. package/dist/errors/index.js +5 -0
  88. package/dist/events/EventServiceProvider.d.ts +82 -0
  89. package/dist/events/Listener.d.ts +28 -0
  90. package/dist/events/index.d.ts +80 -0
  91. package/dist/events/index.js +1 -0
  92. package/dist/excel/index.d.ts +154 -0
  93. package/dist/excel/index.js +1 -0
  94. package/dist/feature-flags/index.d.ts +158 -0
  95. package/dist/feature-flags/index.js +59 -0
  96. package/dist/forms/index.d.ts +81 -0
  97. package/dist/forms/index.js +1 -0
  98. package/dist/hashing/Hash.d.ts +51 -0
  99. package/dist/hashing/index.d.ts +1 -0
  100. package/dist/hashing/index.js +1 -0
  101. package/dist/hooks/index.d.ts +135 -0
  102. package/dist/hooks/index.js +5 -0
  103. package/dist/http/index.d.ts +201 -0
  104. package/dist/http/index.js +2 -0
  105. package/dist/i18n/index.d.ts +81 -0
  106. package/dist/i18n/index.js +1 -0
  107. package/dist/index.d.ts +54 -0
  108. package/dist/index.js +127 -0
  109. package/dist/logging/LogViewer.d.ts +95 -0
  110. package/dist/logging/LogViewer.js +1 -0
  111. package/dist/logging/index.d.ts +83 -0
  112. package/dist/logging/index.js +3 -0
  113. package/dist/mail/index.d.ts +149 -0
  114. package/dist/mail/index.js +1 -0
  115. package/dist/middleware/Middleware.d.ts +208 -0
  116. package/dist/middleware/index.d.ts +1 -0
  117. package/dist/middleware/index.js +1 -0
  118. package/dist/notifications/index.d.ts +85 -0
  119. package/dist/notifications/index.js +2 -0
  120. package/dist/orm/Model.d.ts +123 -0
  121. package/dist/orm/Observer.d.ts +34 -0
  122. package/dist/orm/QueryBuilder.d.ts +119 -0
  123. package/dist/orm/Relationship.d.ts +58 -0
  124. package/dist/orm/index.d.ts +4 -0
  125. package/dist/orm/index.js +1 -0
  126. package/dist/pagination/index.d.ts +8 -0
  127. package/dist/pagination/index.js +0 -0
  128. package/dist/pdf/GeneratePdfJob.d.ts +99 -0
  129. package/dist/pdf/GeneratePdfJob.js +41 -0
  130. package/dist/pdf/index.d.ts +328 -0
  131. package/dist/pdf/index.js +41 -0
  132. package/dist/permissions/index.d.ts +161 -0
  133. package/dist/permissions/index.js +60 -0
  134. package/dist/plugins/BootstrapPlugins.d.ts +11 -0
  135. package/dist/plugins/PluginInstaller.d.ts +30 -0
  136. package/dist/plugins/PluginInstaller.js +1 -0
  137. package/dist/plugins/PluginPublisher.d.ts +32 -0
  138. package/dist/plugins/PluginPublisher.js +1 -0
  139. package/dist/plugins/PluginRegistry.d.ts +55 -0
  140. package/dist/plugins/PluginRegistry.js +1 -0
  141. package/dist/plugins/index.d.ts +206 -0
  142. package/dist/plugins/index.js +1 -0
  143. package/dist/queue/JobMonitor.d.ts +109 -0
  144. package/dist/queue/JobMonitor.js +5 -0
  145. package/dist/queue/index.d.ts +279 -0
  146. package/dist/queue/index.js +5 -0
  147. package/dist/repositories/index.d.ts +147 -0
  148. package/dist/repositories/index.js +1 -0
  149. package/dist/routing/Controller.d.ts +115 -0
  150. package/dist/routing/FormRequest.d.ts +94 -0
  151. package/dist/routing/Resource.d.ts +213 -0
  152. package/dist/routing/Response.d.ts +138 -0
  153. package/dist/routing/index.d.ts +4 -0
  154. package/dist/routing/index.js +5 -0
  155. package/dist/scheduler/ScheduleMonitor.d.ts +141 -0
  156. package/dist/scheduler/ScheduleMonitor.js +1 -0
  157. package/dist/scheduler/SchedulerLock.d.ts +33 -0
  158. package/dist/scheduler/index.d.ts +208 -0
  159. package/dist/scheduler/index.js +34 -0
  160. package/dist/services/index.d.ts +79 -0
  161. package/dist/services/index.js +1 -0
  162. package/dist/session/Session.d.ts +166 -0
  163. package/dist/session/index.d.ts +1 -0
  164. package/dist/session/index.js +16 -0
  165. package/dist/storage/index.d.ts +154 -0
  166. package/dist/storage/index.js +1 -0
  167. package/dist/support/Pipeline.d.ts +65 -0
  168. package/dist/support/date.d.ts +136 -0
  169. package/dist/support/date.js +1 -0
  170. package/dist/support/index.d.ts +8 -0
  171. package/dist/support/index.js +1 -0
  172. package/dist/support/singleton.d.ts +10 -0
  173. package/dist/support/uuid.d.ts +40 -0
  174. package/dist/teams/index.d.ts +91 -0
  175. package/dist/teams/index.js +78 -0
  176. package/dist/uploads/index.d.ts +63 -0
  177. package/dist/uploads/index.js +2 -0
  178. package/dist/validation/index.d.ts +46 -0
  179. package/dist/validation/index.js +1 -0
  180. package/dist/webhooks/index.d.ts +66 -0
  181. package/dist/webhooks/index.js +1 -0
  182. package/package.json +338 -0
  183. package/src/i18n/LanguageSwitcher.svelte +47 -0
  184. package/src/i18n/index.ts +113 -0
  185. package/src/ui/Alert.svelte +22 -0
  186. package/src/ui/Avatar.svelte +18 -0
  187. package/src/ui/AvatarFallback.svelte +18 -0
  188. package/src/ui/AvatarImage.svelte +12 -0
  189. package/src/ui/Badge.svelte +27 -0
  190. package/src/ui/Button.svelte +51 -0
  191. package/src/ui/Card.svelte +15 -0
  192. package/src/ui/CardContent.svelte +15 -0
  193. package/src/ui/CardDescription.svelte +15 -0
  194. package/src/ui/CardFooter.svelte +15 -0
  195. package/src/ui/CardHeader.svelte +15 -0
  196. package/src/ui/CardTitle.svelte +15 -0
  197. package/src/ui/Icon.svelte +81 -0
  198. package/src/ui/Input.svelte +40 -0
  199. package/src/ui/Label.svelte +20 -0
  200. package/src/ui/Separator.svelte +10 -0
  201. package/src/ui/Tabs.svelte +23 -0
  202. package/src/ui/TabsContent.svelte +27 -0
  203. package/src/ui/TabsList.svelte +19 -0
  204. package/src/ui/TabsTrigger.svelte +28 -0
  205. package/src/ui/Toaster.svelte +279 -0
  206. package/src/ui/index.ts +31 -0
  207. package/src/ui/toast.ts +212 -0
@@ -0,0 +1,74 @@
1
+ /**
2
+ * ESM resolve hook for Svelar CLI:
3
+ * 1. Resolves 'svelar' and 'svelar/*' imports directly from THIS package's dist/
4
+ * (bypasses node_modules entirely — no symlinks, junctions, or platform issues)
5
+ * 2. Rewrites .js → .ts when the .js file doesn't exist
6
+ */
7
+
8
+ import { existsSync } from 'node:fs';
9
+ import { resolve as pathResolve, sep, dirname } from 'node:path';
10
+ import { fileURLToPath, pathToFileURL } from 'node:url';
11
+
12
+ // This file lives at <svelar-root>/dist/cli/ts-resolve-hook.mjs
13
+ // So the svelar package root is two directories up from here.
14
+ const thisFileDir = dirname(fileURLToPath(import.meta.url));
15
+ const svelarRoot = pathResolve(thisFileDir, '..', '..');
16
+
17
+ /**
18
+ * Resolve 'svelar' or 'svelar/<subpath>' by pointing directly at our own dist/.
19
+ * This works on every OS because it never touches node_modules or symlinks.
20
+ */
21
+ function resolveSvelar(specifier) {
22
+ if (specifier === 'svelar') {
23
+ const target = pathResolve(svelarRoot, 'dist', 'index.js');
24
+ if (existsSync(target)) return pathToFileURL(target).href;
25
+ return null;
26
+ }
27
+
28
+ if (specifier.startsWith('svelar/')) {
29
+ const subpath = specifier.slice('svelar/'.length); // e.g. 'database', 'orm', 'auth'
30
+ const target = pathResolve(svelarRoot, 'dist', subpath, 'index.js');
31
+ if (existsSync(target)) return pathToFileURL(target).href;
32
+
33
+ // Also try direct file (e.g. svelar/something.js)
34
+ const directTarget = pathResolve(svelarRoot, 'dist', subpath + '.js');
35
+ if (existsSync(directTarget)) return pathToFileURL(directTarget).href;
36
+
37
+ return null;
38
+ }
39
+
40
+ return null;
41
+ }
42
+
43
+ export async function resolve(specifier, context, nextResolve) {
44
+ // 1. Intercept svelar imports FIRST — before Node tries (and fails) to resolve them
45
+ if (specifier === 'svelar' || specifier.startsWith('svelar/')) {
46
+ const resolved = resolveSvelar(specifier);
47
+ if (resolved) {
48
+ return { url: resolved, shortCircuit: true };
49
+ }
50
+ }
51
+
52
+ // 2. Handle relative .js → .ts resolution
53
+ if (specifier.startsWith('.') && specifier.endsWith('.js')) {
54
+ try {
55
+ const parentPath = context.parentURL ? fileURLToPath(context.parentURL) : process.cwd();
56
+ const parentDir = parentPath.endsWith(sep) || parentPath.endsWith('/')
57
+ ? parentPath
58
+ : dirname(parentPath);
59
+ const jsPath = pathResolve(parentDir, specifier);
60
+
61
+ if (!existsSync(jsPath)) {
62
+ const tsPath = jsPath.replace(/\.js$/, '.ts');
63
+ if (existsSync(tsPath)) {
64
+ return { url: pathToFileURL(tsPath).href, shortCircuit: true };
65
+ }
66
+ }
67
+ } catch {
68
+ // Fall through
69
+ }
70
+ }
71
+
72
+ // 3. Default resolution
73
+ return nextResolve(specifier, context);
74
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Register hook: resolves .js imports to .ts files when the .js doesn't exist.
3
+ * Use with: node --import ./ts-resolver.mjs
4
+ */
5
+
6
+ import { register } from 'node:module';
7
+
8
+ register('./ts-resolve-hook.mjs', import.meta.url);
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Svelar Config
3
+ *
4
+ * Environment-aware configuration management with optional
5
+ * directory-based config loading (like Laravel's config/ folder).
6
+ */
7
+ /**
8
+ * Get an environment variable with an optional default.
9
+ * Similar to Laravel's env() helper.
10
+ *
11
+ * @example
12
+ * env('DB_HOST', 'localhost') // string
13
+ * env<number>('DB_PORT', 5432) // number
14
+ * env<boolean>('APP_DEBUG', false) // boolean
15
+ */
16
+ export declare function env<T extends string | number | boolean = string>(key: string, defaultValue?: T): T;
17
+ declare class ConfigManager {
18
+ private items;
19
+ /**
20
+ * Clear all configuration
21
+ */
22
+ clear(): void;
23
+ /**
24
+ * Load configuration from an object
25
+ */
26
+ load(config: Record<string, any>): void;
27
+ /**
28
+ * Load all config files from a directory.
29
+ * Each file becomes a top-level config key based on its filename.
30
+ *
31
+ * Files must export a default object (or a named `config` export).
32
+ *
33
+ * @example
34
+ * // config/app.ts → config.get('app.name')
35
+ * // config/database.ts → config.get('database.default')
36
+ * // config/mail.ts → config.get('mail.driver')
37
+ *
38
+ * await config.loadFromDirectory('./config');
39
+ *
40
+ * @param dirPath - Path to the config directory (relative to cwd or absolute)
41
+ */
42
+ loadFromDirectory(dirPath: string): Promise<string[]>;
43
+ /**
44
+ * Get a configuration value using dot notation
45
+ * @example config.get('database.default') // 'sqlite'
46
+ */
47
+ get<T = any>(key: string, defaultValue?: T): T;
48
+ /**
49
+ * Set a configuration value using dot notation
50
+ */
51
+ set(key: string, value: any): void;
52
+ /**
53
+ * Check if a configuration key exists
54
+ */
55
+ has(key: string): boolean;
56
+ /**
57
+ * Get all configuration as a plain object
58
+ */
59
+ all(): Record<string, any>;
60
+ }
61
+ /**
62
+ * Global config singleton
63
+ */
64
+ export declare const config: ConfigManager;
65
+ export {};
@@ -0,0 +1 @@
1
+ export { config, env } from './Config.js';
@@ -0,0 +1 @@
1
+ function h(a,n){let t=Symbol.for(a),e=globalThis;return e[t]||(e[t]=n()),e[t]}function T(a,n){let t=process.env[a];return t===void 0?n!==void 0?n:"":t==="true"?!0:t==="false"?!1:t==="null"?null:/^\d+$/.test(t)?Number(t):t}var u=class{items=new Map;clear(){this.items.clear()}load(n){for(let[t,e]of Object.entries(n))this.set(t,e)}async loadFromDirectory(n){let{resolve:t,basename:e,extname:s}=await import("path"),{existsSync:r,readdirSync:i}=await import("fs"),{pathToFileURL:m}=await import("url"),l=t(n);if(!r(l))return[];let y=i(l).filter(o=>(o.endsWith(".ts")||o.endsWith(".js"))&&!o.startsWith(".")),g=[];for(let o of y){let d=e(o,s(o)),p=t(l,o);try{let f=await import(m(p).href),c=f.default??f.config??f;c&&typeof c=="object"&&!Array.isArray(c)&&(this.set(d,c),g.push(d))}catch{}}return g}get(n,t){let e=n.split("."),s=this.items.get(e[0]);for(let r=1;r<e.length;r++){if(s==null)return t;s=s[e[r]]}return s??t}set(n,t){let e=n.split(".");if(e.length===1){this.items.set(n,t);return}let s=this.items.get(e[0]);(s===void 0||typeof s!="object")&&(s={},this.items.set(e[0],s));let r=s;for(let i=1;i<e.length-1;i++)(r[e[i]]===void 0||typeof r[e[i]]!="object")&&(r[e[i]]={}),r=r[e[i]];r[e[e.length-1]]=t}has(n){return this.get(n)!==void 0}all(){let n={};for(let[t,e]of this.items)n[t]=e;return n}},b=h("svelar.config",()=>new u);export{b as config,T as env};
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Svelar Application
3
+ *
4
+ * Bootstraps the framework by registering and booting all service providers.
5
+ */
6
+ import { Container } from './Container.js';
7
+ import { ServiceProvider } from './ServiceProvider.js';
8
+ export declare class Application {
9
+ readonly container: Container;
10
+ private providers;
11
+ private booted;
12
+ constructor(container?: Container);
13
+ /**
14
+ * Register a service provider
15
+ */
16
+ register(ProviderClass: new (app: Container) => ServiceProvider): this;
17
+ /**
18
+ * Bootstrap the application: register all, then boot all
19
+ */
20
+ bootstrap(): Promise<this>;
21
+ /**
22
+ * Check if the application has been bootstrapped
23
+ */
24
+ isBooted(): boolean;
25
+ /**
26
+ * Resolve a service from the container
27
+ */
28
+ make<T = any>(name: string): Promise<T>;
29
+ /**
30
+ * Get the list of registered providers
31
+ */
32
+ getProviders(): ServiceProvider[];
33
+ }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Svelar IoC Container
3
+ *
4
+ * Laravel-inspired service container with dependency injection,
5
+ * singleton support, and service provider lifecycle.
6
+ */
7
+ export type Factory<T = any> = (container: Container) => T;
8
+ export type AsyncFactory<T = any> = (container: Container) => T | Promise<T>;
9
+ export declare class Container {
10
+ private bindings;
11
+ private aliases;
12
+ private resolved;
13
+ /**
14
+ * Register a binding in the container
15
+ */
16
+ bind<T>(name: string, factory: AsyncFactory<T>): void;
17
+ /**
18
+ * Register a singleton binding (resolved once, then cached)
19
+ */
20
+ singleton<T>(name: string, factory: AsyncFactory<T>): void;
21
+ /**
22
+ * Register an existing instance as a singleton
23
+ */
24
+ instance<T>(name: string, value: T): void;
25
+ /**
26
+ * Create an alias for a binding
27
+ */
28
+ alias(alias: string, target: string): void;
29
+ /**
30
+ * Resolve a binding from the container
31
+ */
32
+ make<T = any>(name: string): Promise<T>;
33
+ /**
34
+ * Synchronous resolve (only works for already-resolved singletons or sync factories)
35
+ */
36
+ makeSync<T = any>(name: string): T;
37
+ /**
38
+ * Check if a binding exists
39
+ */
40
+ has(name: string): boolean;
41
+ /**
42
+ * Check if a binding has been resolved
43
+ */
44
+ isResolved(name: string): boolean;
45
+ /**
46
+ * Tag bindings for group resolution
47
+ */
48
+ tag(names: string[], tag: string): void;
49
+ /**
50
+ * Resolve all bindings with a given tag
51
+ */
52
+ tagged<T = any>(tag: string): Promise<T[]>;
53
+ /**
54
+ * Flush all resolved singletons (useful for testing)
55
+ */
56
+ flush(): void;
57
+ /**
58
+ * Remove a binding entirely
59
+ */
60
+ forget(name: string): void;
61
+ /**
62
+ * Get all binding names
63
+ */
64
+ getBindings(): string[];
65
+ private resolveAlias;
66
+ }
67
+ /**
68
+ * Global application container
69
+ */
70
+ export declare const container: Container;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Svelar Service Provider
3
+ *
4
+ * Base class for registering services into the IoC container.
5
+ * Follows Laravel's register/boot lifecycle.
6
+ */
7
+ import { Container } from './Container.js';
8
+ export declare abstract class ServiceProvider {
9
+ protected app: Container;
10
+ constructor(app: Container);
11
+ /**
12
+ * Register bindings into the container.
13
+ * Called before any provider is booted.
14
+ */
15
+ abstract register(): void | Promise<void>;
16
+ /**
17
+ * Bootstrap services after all providers are registered.
18
+ * Use this for logic that depends on other services being available.
19
+ */
20
+ boot(): void | Promise<void>;
21
+ }
@@ -0,0 +1,3 @@
1
+ export { Container, container, type Factory, type AsyncFactory } from './Container.js';
2
+ export { ServiceProvider } from './ServiceProvider.js';
3
+ export { Application } from './Application.js';
@@ -0,0 +1 @@
1
+ function c(r,n){let i=Symbol.for(r),t=globalThis;return t[i]||(t[i]=n()),t[i]}var e=class{bindings=new Map;aliases=new Map;resolved=new Set;bind(n,i){this.bindings.set(n,{factory:i,singleton:!1,tags:[]})}singleton(n,i){this.bindings.set(n,{factory:i,singleton:!0,tags:[]})}instance(n,i){this.bindings.set(n,{factory:()=>i,singleton:!0,instance:i,tags:[]})}alias(n,i){this.aliases.set(n,i)}async make(n){let i=this.resolveAlias(n),t=this.bindings.get(i);if(!t)throw new Error(`No binding found for "${n}" in the container.`);if(t.singleton&&t.instance!==void 0)return t.instance;let s=await t.factory(this);return t.singleton&&(t.instance=s),this.resolved.add(i),s}makeSync(n){let i=this.resolveAlias(n),t=this.bindings.get(i);if(!t)throw new Error(`No binding found for "${n}" in the container.`);if(t.singleton&&t.instance!==void 0)return t.instance;let s=t.factory(this);if(s instanceof Promise)throw new Error(`Binding "${n}" has an async factory. Use container.make() instead of container.makeSync().`);return t.singleton&&(t.instance=s),this.resolved.add(i),s}has(n){let i=this.resolveAlias(n);return this.bindings.has(i)}isResolved(n){return this.resolved.has(this.resolveAlias(n))}tag(n,i){for(let t of n){let s=this.bindings.get(t);s&&s.tags.push(i)}}async tagged(n){let i=[];for(let[t,s]of this.bindings)s.tags.includes(n)&&i.push(await this.make(t));return i}flush(){for(let n of this.bindings.values())n.singleton&&(n.instance=void 0);this.resolved.clear()}forget(n){this.bindings.delete(n),this.resolved.delete(n)}getBindings(){return[...this.bindings.keys()]}resolveAlias(n){return this.aliases.get(n)??n}},d=c("svelar.container",()=>new e);var o=class{app;constructor(n){this.app=n}boot(){}};var a=class{container;providers=[];booted=!1;constructor(n){this.container=n??new e,this.container.instance("app",this),this.container.instance("container",this.container)}register(n){let i=new n(this.container);return this.providers.push(i),this}async bootstrap(){if(this.booted)return this;for(let n of this.providers)await n.register();for(let n of this.providers)await n.boot();return this.booted=!0,this}isBooted(){return this.booted}async make(n){return this.container.make(n)}getProviders(){return[...this.providers]}};export{a as Application,e as Container,o as ServiceProvider,d as container};
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Svelar Admin Dashboard Plugin
3
+ *
4
+ * Provides a centralized admin dashboard that aggregates system health,
5
+ * queue metrics, scheduler status, and logs for monitoring and management.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * import { Dashboard, configureDashboard, getDashboardHealth } from 'svelar/dashboard';
10
+ *
11
+ * // Configure the dashboard
12
+ * configureDashboard({
13
+ * enabled: true,
14
+ * prefix: '/admin/dashboard',
15
+ * refreshInterval: 5000,
16
+ * });
17
+ *
18
+ * // Get system health
19
+ * const health = getDashboardHealth();
20
+ * console.log(`Uptime: ${health.uptime}ms`);
21
+ *
22
+ * // Get full dashboard data
23
+ * const data = Dashboard.getDashboardData();
24
+ * ```
25
+ */
26
+ import type { Container } from '../container/Container.js';
27
+ import { Plugin } from '../plugins/index.js';
28
+ import { type QueueHealth } from '../queue/JobMonitor.js';
29
+ import { type SchedulerHealth } from '../scheduler/ScheduleMonitor.js';
30
+ export interface DashboardConfig {
31
+ /** Enable or disable the dashboard */
32
+ enabled?: boolean;
33
+ /** Route prefix for dashboard endpoints (default: '/admin/dashboard') */
34
+ prefix?: string;
35
+ /** Interval in ms for automatic health collection (default: 5000) */
36
+ refreshInterval?: number;
37
+ /** Section visibility toggles */
38
+ sections?: {
39
+ logs?: boolean;
40
+ queue?: boolean;
41
+ scheduler?: boolean;
42
+ system?: boolean;
43
+ };
44
+ }
45
+ export interface SystemHealth {
46
+ status: 'healthy' | 'degraded' | 'critical';
47
+ uptime: number;
48
+ nodeVersion: string;
49
+ memoryUsage: {
50
+ heapUsed: number;
51
+ heapTotal: number;
52
+ rss: number;
53
+ external: number;
54
+ };
55
+ cpuUsage: {
56
+ user: number;
57
+ system: number;
58
+ };
59
+ queueHealth: QueueHealth;
60
+ schedulerHealth: SchedulerHealth;
61
+ logStats: {
62
+ totalEntries: number;
63
+ byLevel: Record<string, number>;
64
+ byChannel: Record<string, number>;
65
+ };
66
+ timestamp: string;
67
+ }
68
+ declare class DashboardPlugin extends Plugin {
69
+ readonly name = "svelar-dashboard";
70
+ readonly version = "1.0.0";
71
+ readonly description = "Admin dashboard with system health monitoring";
72
+ private dashboardConfig;
73
+ private startTime;
74
+ private healthCollectionInterval;
75
+ /**
76
+ * Return plugin configuration
77
+ */
78
+ config(): {
79
+ key: string;
80
+ defaults: Required<DashboardConfig>;
81
+ };
82
+ /**
83
+ * Register services and configuration
84
+ */
85
+ register(app: Container): Promise<void>;
86
+ /**
87
+ * Bootstrap the dashboard plugin
88
+ */
89
+ boot(app: Container): Promise<void>;
90
+ /**
91
+ * Clean up on shutdown
92
+ */
93
+ shutdown(): Promise<void>;
94
+ /**
95
+ * Update dashboard configuration
96
+ */
97
+ setConfig(config: DashboardConfig): void;
98
+ /**
99
+ * Get aggregated system health from all monitors
100
+ */
101
+ getSystemHealth(): Promise<SystemHealth>;
102
+ /**
103
+ * Get complete dashboard data with all sections
104
+ */
105
+ getDashboardData(): Promise<Record<string, any>>;
106
+ }
107
+ /**
108
+ * Global Dashboard singleton
109
+ */
110
+ export declare const Dashboard: DashboardPlugin;
111
+ /**
112
+ * Configure the dashboard
113
+ */
114
+ export declare function configureDashboard(config: DashboardConfig): void;
115
+ /**
116
+ * Quick access to system health
117
+ */
118
+ export declare function getDashboardHealth(): Promise<SystemHealth>;
119
+ /**
120
+ * Quick access to full dashboard data
121
+ */
122
+ export declare function getDashboardData(): Promise<Record<string, any>>;
123
+ export {};
@@ -0,0 +1,5 @@
1
+ var x=Object.defineProperty;var T=(l,e)=>()=>(l&&(e=l(l=0)),e);var N=(l,e)=>{for(var t in e)x(l,t,{get:e[t],enumerable:!0})};function d(l,e){let t=Symbol.for(l),s=globalThis;return s[t]||(s[t]=e()),s[t]}var g=T(()=>{"use strict"});var f={};N(f,{Connection:()=>A});var C,A,p=T(()=>{"use strict";g();C=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 s=this.config.connections[t];if(!s)throw new Error(`Database connection "${t}" is not defined in configuration.`);let n=await this.createConnection(s);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=[],s){let n=await this.connection(s),r=this.getConfig(s);switch(r.driver){case"sqlite":{let i=await this.rawClient(s),a=t.map(c=>typeof c=="boolean"?c?1:0:c instanceof Date?c.toISOString():c),o=i.prepare(e),u=e.trimStart().toUpperCase();return u.startsWith("SELECT")||u.startsWith("PRAGMA")||u.startsWith("WITH")?o.all(...a):o.run(...a)}case"postgres":return(await this.rawClient(s))(e,...t);case"mysql":{let i=await this.rawClient(s),[a]=await i.execute(e,t);return a}default:throw new Error(`Unsupported driver: ${r.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 s=this.config.connections[t];if(!s)throw new Error(`Database connection "${t}" is not defined.`);return s}async disconnect(e){if(e){let t=this.connections.get(e);t&&(await this.closeConnection(t),this.connections.delete(e))}else{for(let[t,s]of this.connections)await this.closeConnection(s);this.connections.clear()}}isConnected(e){return this.connections.has(e??this.defaultName)}async transaction(e,t){let s=this.getConfig(t),n=await this.rawClient(t);switch(s.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: ${s.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 s=(await import("better-sqlite3")).default,{drizzle:n}=await import("drizzle-orm/better-sqlite3"),r=new s(t);return r.pragma("journal_mode = WAL"),r.pragma("foreign_keys = ON"),{drizzle:n(r),config:e,rawClient:r}}catch(s){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: ${s instanceof Error?s.message:String(s)}`)}let r=new n(t);r.exec("PRAGMA journal_mode = WAL"),r.exec("PRAGMA foreign_keys = ON");let i={prepare(o){let u=r.prepare(o);return{all(...c){return u.all(...c)},run(...c){return u.run(...c)},get(...c){return u.get(...c)}}},exec(o){r.exec(o)},pragma(o){return r.prepare(`PRAGMA ${o}`).all()},close(){r.close()}},a;try{let{drizzle:o}=await import("drizzle-orm/better-sqlite3");a=o(i)}catch{a=i}return{drizzle:a,config:e,rawClient:i}}}async createPostgresConnection(e){let t=(await import("postgres")).default,{drizzle:s}=await import("drizzle-orm/postgres-js"),n=e.url??`postgres://${e.user}:${e.password}@${e.host??"localhost"}:${e.port??5432}/${e.database}`,r=t(n);return{drizzle:s(r),config:e,rawClient:r}}async createMySQLConnection(e){let t=await import("mysql2/promise"),{drizzle:s}=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:s(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{}}},A=d("svelar.connection",()=>new C)});import{hostname as O}from"os";var Q,L=T(()=>{"use strict";Q=`${O()}:${process.pid}:${Math.random().toString(36).slice(2,10)}`});var y=class{description;dependencies;async register(e){}async boot(e){}async shutdown(){}migrations(){return[]}config(){return null}middleware(){return[]}commands(){return[]}routes(){return[]}listeners(){return[]}publishables(){return{}}};g();g();var E=class{ringBuffer=[];maxSize=1e4;currentIndex=0;tailSubscribers=[];addEntry(e){let t={timestamp:e.timestamp,level:e.level,channel:e.channel??"default",message:e.message,context:e.context};this.ringBuffer.length<this.maxSize?this.ringBuffer.push(t):(this.ringBuffer[this.currentIndex]=t,this.currentIndex=(this.currentIndex+1)%this.maxSize);for(let s of this.tailSubscribers)try{s(t)}catch{}}query(e={}){let t=this.getAllEntries();if(e.level){let r={debug:0,info:1,warn:2,error:3,fatal:4},i=r[e.level];t=t.filter(a=>r[a.level]>=i)}if(e.channel&&(t=t.filter(r=>r.channel===e.channel)),e.since){let r=e.since.getTime();t=t.filter(i=>new Date(i.timestamp).getTime()>=r)}if(e.until){let r=e.until.getTime();t=t.filter(i=>new Date(i.timestamp).getTime()<=r)}if(e.search){let r=e.search.toLowerCase();t=t.filter(i=>i.message.toLowerCase().includes(r)||JSON.stringify(i.context).toLowerCase().includes(r))}let s=e.offset??0,n=e.limit??100;return t.slice(s,s+n)}getStats(){let e=this.getAllEntries(),t={totalEntries:e.length,byLevel:{},byChannel:{}};for(let s of e)t.byLevel[s.level]=(t.byLevel[s.level]??0)+1,t.byChannel[s.channel]=(t.byChannel[s.channel]??0)+1;return t}getRecentErrors(e=10){return this.getAllEntries().filter(s=>s.level==="error"||s.level==="fatal").slice(-e).reverse()}clear(){this.ringBuffer=[],this.currentIndex=0}tail(e,t){let s=t?n=>{this.matchesFilter(n,t)&&e(n)}:e;return this.tailSubscribers.push(s),()=>{let n=this.tailSubscribers.indexOf(s);n>-1&&this.tailSubscribers.splice(n,1)}}getAllEntries(){return this.ringBuffer.length<this.maxSize?[...this.ringBuffer]:[...this.ringBuffer.slice(this.currentIndex),...this.ringBuffer.slice(0,this.currentIndex)]}matchesFilter(e,t){if(t.level){let s={debug:0,info:1,warn:2,error:3,fatal:4},n=s[t.level];if(s[e.level]<n)return!1}if(t.channel&&e.channel!==t.channel)return!1;if(t.since){let s=t.since.getTime();if(new Date(e.timestamp).getTime()<s)return!1}if(t.until){let s=t.until.getTime();if(new Date(e.timestamp).getTime()>s)return!1}if(t.search){let s=t.search.toLowerCase();if(!(e.message.toLowerCase().includes(s)||JSON.stringify(e.context).toLowerCase().includes(s)))return!1}return!0}},m=d("svelar.logViewer",()=>new E);g();var k=class{_config=null;_bullmq=null;_metrics={processed:[],failed:[]};configure(e){this._config=e}recordProcessed(){this._metrics.processed.push(Date.now());let e=Date.now()-36e5;this._metrics.processed=this._metrics.processed.filter(t=>t>e)}recordFailed(){this._metrics.failed.push(Date.now());let e=Date.now()-36e5;this._metrics.failed=this._metrics.failed.filter(t=>t>e)}async getCounts(e="default"){let t=this._config?.connections[this._config.default]?.driver;return t==="redis"?this._getRedisJobCounts(e):t==="database"?this._getDatabaseJobCounts(e):{waiting:0,active:0,completed:0,failed:0,delayed:0,total:0}}async listJobs(e={}){let t=this._config?.connections[this._config.default]?.driver;return t==="redis"?this._listRedisJobs(e):t==="database"?this._listDatabaseJobs(e):[]}async getJob(e){let t=this._config?.connections[this._config.default]?.driver;return t==="redis"?this._getRedisJob(e):t==="database"?this._getDatabaseJob(e):null}async retryJob(e){let t=this._config?.connections[this._config.default]?.driver;return t==="redis"?this._retryRedisJob(e):t==="database"?this._retryDatabaseJob(e):!1}async deleteJob(e){let t=this._config?.connections[this._config.default]?.driver;return t==="redis"?this._deleteRedisJob(e):t==="database"?this._deleteDatabaseJob(e):!1}async getHealth(){let e=this._config?.connections[this._config.default]?.driver??"sync",t=await this.getCounts("default"),s=Date.now()-36e5,n=this._metrics.processed.filter(a=>a>s).length,r=this._metrics.failed.filter(a=>a>s).length,i=n+r;return{driver:e,queues:{default:t},failureRate:i>0?r/i*100:0,throughput:n}}async flushCompleted(e="default"){return this._config?.connections[this._config.default]?.driver==="redis"?this._flushRedisCompleted(e):0}async flushFailed(e="default"){let t=this._config?.connections[this._config.default]?.driver;return t==="redis"?this._flushRedisFailed(e):t==="database"?this._flushDatabaseFailed(e):0}async getBullMQ(){if(this._bullmq)return this._bullmq;try{return this._bullmq=await Function('return import("bullmq")')(),this._bullmq}catch{throw new Error("bullmq is required for the Redis queue monitor.")}}getRedisConnection(){let e=this._config?.connections[this._config.default]??{};if(e.url){let t=new URL(e.url);return{host:t.hostname||"localhost",port:parseInt(t.port)||6379,password:t.password||e.password||void 0,db:parseInt(t.pathname?.slice(1)||"0")||e.db||0}}return{host:e.host??"localhost",port:e.port??6379,password:e.password,db:e.db??0}}async getRedisQueue(e){let t=await this.getBullMQ(),s=this.getRedisConnection(),n=this._config?.connections[this._config.default]??{};return new t.Queue(e,{connection:s,prefix:n.prefix??"svelar"})}async _getRedisJobCounts(e){let t=await this.getRedisQueue(e);try{let s=await t.getJobCounts("waiting","active","completed","failed","delayed");return{waiting:s.waiting??0,active:s.active??0,completed:s.completed??0,failed:s.failed??0,delayed:s.delayed??0,total:(s.waiting??0)+(s.active??0)+(s.completed??0)+(s.failed??0)+(s.delayed??0)}}finally{await t.close()}}async _listRedisJobs(e){let t=await this.getRedisQueue(e.queue??"default"),s=e.limit??50,n=e.offset??0;try{let r;return e.status?r=await t.getJobs([e.status],n,n+s-1):r=await t.getJobs(["waiting","active","completed","failed","delayed"],n,n+s-1),r.filter(i=>i!=null).filter(i=>!e.jobClass||i.name===e.jobClass).map(i=>this._bullmqJobToInfo(i))}finally{await t.close()}}async _getRedisJob(e){let t=await this.getRedisQueue("default");try{let s=await t.getJob(e);return s?this._bullmqJobToInfo(s):null}finally{await t.close()}}async _retryRedisJob(e){let t=await this.getRedisQueue("default");try{let s=await t.getJob(e);return s?(await s.retry(),!0):!1}catch{return!1}finally{await t.close()}}async _deleteRedisJob(e){let t=await this.getRedisQueue("default");try{let s=await t.getJob(e);return s?(await s.remove(),!0):!1}catch{return!1}finally{await t.close()}}async _flushRedisCompleted(e){let t=await this.getRedisQueue(e);try{let s=await t.getJobs(["completed"]),n=0;for(let r of s)r&&(await r.remove(),n++);return n}finally{await t.close()}}async _flushRedisFailed(e){let t=await this.getRedisQueue(e);try{let s=await t.getJobs(["failed"]),n=0;for(let r of s)r&&(await r.remove(),n++);return n}finally{await t.close()}}_bullmqJobToInfo(e){let t=e.finishedOn?e.failedReason?"failed":"completed":e.delay&&e.delay>0&&!e.processedOn?"delayed":e.processedOn?"active":"waiting";return{id:e.id,jobClass:e.name??e.data?.jobClass??"Unknown",queue:e.queueName??"default",status:t,attempts:e.attemptsMade??0,maxAttempts:e.opts?.attempts??3,payload:JSON.stringify(e.data??{}),error:e.failedReason??void 0,createdAt:e.timestamp??Date.now(),processedAt:e.processedOn??void 0,failedAt:e.failedReason?e.finishedOn:void 0,delayedUntil:e.delay?e.timestamp+e.delay:void 0}}async getDbConnection(){let{Connection:e}=await Promise.resolve().then(()=>(p(),f));return e}getDbTable(){return this._config?.connections[this._config.default]?.table??"svelar_jobs"}async _getDatabaseJobCounts(e){let t=await this.getDbConnection(),s=this.getDbTable(),n=Math.floor(Date.now()/1e3),i=(await t.raw(`SELECT
2
+ SUM(CASE WHEN reserved_at IS NULL AND available_at <= ? THEN 1 ELSE 0 END) as waiting,
3
+ SUM(CASE WHEN reserved_at IS NOT NULL THEN 1 ELSE 0 END) as active,
4
+ SUM(CASE WHEN reserved_at IS NULL AND available_at > ? THEN 1 ELSE 0 END) as delayed
5
+ FROM ${s} WHERE queue = ?`,[n,n,e]))?.[0]??{},a=Number(i.waiting)||0,o=Number(i.active)||0,u=Number(i.delayed)||0;return{waiting:a,active:o,completed:0,failed:0,delayed:u,total:a+o+u}}async _listDatabaseJobs(e){let t=await this.getDbConnection(),s=this.getDbTable(),n=e.limit??50,r=e.offset??0,i=Math.floor(Date.now()/1e3),a="WHERE 1=1",o=[];return e.queue&&(a+=" AND queue = ?",o.push(e.queue)),e.status==="waiting"?(a+=" AND reserved_at IS NULL AND available_at <= ?",o.push(i)):e.status==="active"?a+=" AND reserved_at IS NOT NULL":e.status==="delayed"&&(a+=" AND reserved_at IS NULL AND available_at > ?",o.push(i)),o.push(n,r),(await t.raw(`SELECT * FROM ${s} ${a} ORDER BY created_at DESC LIMIT ? OFFSET ?`,o)??[]).map(c=>{let v=JSON.parse(c.payload??"{}"),h=c.reserved_at!=null,I=!h&&c.available_at>i;return{id:c.id,jobClass:v.jobClass??"Unknown",queue:c.queue,status:h?"active":I?"delayed":"waiting",attempts:c.attempts??0,maxAttempts:c.max_attempts??3,payload:c.payload,createdAt:(c.created_at??0)*1e3,delayedUntil:I?c.available_at*1e3:void 0}})}async _getDatabaseJob(e){let t=await this.getDbConnection(),s=this.getDbTable(),n=await t.raw(`SELECT * FROM ${s} WHERE id = ?`,[e]);if(!n||n.length===0)return null;let r=n[0],i=JSON.parse(r.payload??"{}"),a=Math.floor(Date.now()/1e3);return{id:r.id,jobClass:i.jobClass??"Unknown",queue:r.queue,status:r.reserved_at?"active":r.available_at>a?"delayed":"waiting",attempts:r.attempts??0,maxAttempts:r.max_attempts??3,payload:r.payload,createdAt:(r.created_at??0)*1e3}}async _retryDatabaseJob(e){let t=await this.getDbConnection(),s=this.getDbTable(),n=Math.floor(Date.now()/1e3);try{return await t.raw(`UPDATE ${s} SET reserved_at = NULL, available_at = ?, attempts = 0 WHERE id = ?`,[n,e]),!0}catch{return!1}}async _deleteDatabaseJob(e){let t=await this.getDbConnection(),s=this.getDbTable();try{return await t.raw(`DELETE FROM ${s} WHERE id = ?`,[e]),!0}catch{return!1}}async _flushDatabaseFailed(e){let t=await this.getDbConnection(),s=this.getDbTable();return 0}},_=d("svelar.jobMonitor",()=>new k);L();function b(l,e,t){if(l==="*")return null;let s=new Set;for(let n of l.split(",")){let[r,i]=n.split("/"),a=i?parseInt(i,10):1;if(r==="*")for(let o=e;o<=t;o+=a)s.add(o);else if(r.includes("-")){let[o,u]=r.split("-"),c=parseInt(o,10),v=parseInt(u,10);for(let h=c;h<=v;h+=a)s.add(h)}else s.add(parseInt(r,10))}return[...s].sort((n,r)=>n-r)}function M(l){let e=l.trim().split(/\s+/);if(e.length!==5)throw new Error(`Invalid cron expression: "${l}". Expected 5 fields.`);return{minute:b(e[0],0,59),hour:b(e[1],0,23),dayOfMonth:b(e[2],1,31),month:b(e[3],1,12),dayOfWeek:b(e[4],0,6)}}function P(l,e){let t=M(l),s=e.getMinutes(),n=e.getHours(),r=e.getDate(),i=e.getMonth()+1,a=e.getDay();return!(t.minute&&!t.minute.includes(s)||t.hour&&!t.hour.includes(n)||t.dayOfMonth&&!t.dayOfMonth.includes(r)||t.month&&!t.month.includes(i)||t.dayOfWeek&&!t.dayOfWeek.includes(a))}g();var R=class{scheduler=null;startTime=new Date;taskEnabled=new Map;configure(e){this.scheduler=e,this.startTime=new Date;for(let t of e.getTasks())this.taskEnabled.has(t.name)||this.taskEnabled.set(t.name,!0)}async listTasks(){if(!this.scheduler)return[];let e=this.scheduler.getTasks(),t=await this.loadHistoryFromDb(e.map(s=>s.name));return e.map(s=>this.createTaskInfo(s,t.get(s.name)||[]))}async getTask(e){if(!this.scheduler)return null;let t=this.scheduler.getTasks().find(n=>n.name===e);if(!t)return null;let s=await this.loadHistoryFromDb([e]);return this.createTaskInfo(t,s.get(e)||[])}enableTask(e){this.taskEnabled.set(e,!0)}disableTask(e){this.taskEnabled.set(e,!1)}async runTask(e){if(!this.scheduler)throw new Error("Scheduler not configured");if(this.taskEnabled.has(e)&&!this.taskEnabled.get(e))throw new Error(`Task "${e}" is disabled`);let t=this.scheduler.getTasks().find(n=>n.name===e);if(!t)throw new Error(`Task "${e}" not found`);let s=await t.executeTask();await this.persistResult(s).catch(()=>{})}async getTaskHistory(e,t=10){return(await this.loadHistoryFromDb([e],t)).get(e)||[]}async getHealth(){if(!this.scheduler)return{totalTasks:0,enabledTasks:0,runningTasks:0,lastErrors:[],uptime:0};let e=this.scheduler.getTasks(),t=e.filter(i=>!this.taskEnabled.has(i.name)||this.taskEnabled.get(i.name)).length,s=e.filter(i=>i.isRunning()).length,n=[];try{let{Connection:i}=await Promise.resolve().then(()=>(p(),f)),a=await i.raw("SELECT task, error, ran_at FROM scheduled_task_runs WHERE error IS NOT NULL ORDER BY ran_at DESC LIMIT 10");for(let o of a)n.push({task:o.task,error:o.error,timestamp:new Date(o.ran_at)})}catch{}let r=Date.now()-this.startTime.getTime();return{totalTasks:e.length,enabledTasks:t,runningTasks:s,lastErrors:n,uptime:r}}async loadHistoryFromDb(e,t=20){let s=new Map;if(e.length===0)return s;try{let{Connection:n}=await Promise.resolve().then(()=>(p(),f)),r=e.map(()=>"?").join(", "),i=await n.raw(`SELECT task, success, duration, error, ran_at FROM scheduled_task_runs WHERE task IN (${r}) ORDER BY ran_at DESC LIMIT ?`,[...e,e.length*t]);for(let a of i){s.has(a.task)||s.set(a.task,[]);let o=s.get(a.task);o.length<t&&o.push({timestamp:new Date(a.ran_at),success:!!a.success,duration:a.duration,error:a.error||void 0})}}catch{}return s}async persistResult(e){let{Connection:t}=await Promise.resolve().then(()=>(p(),f));await t.raw("INSERT INTO scheduled_task_runs (task, success, duration, error, ran_at) VALUES (?, ?, ?, ?, ?)",[e.task,e.success,e.duration,e.error||null,e.timestamp.toISOString()])}createTaskInfo(e,t){let s=e.getExpression(),n=t.length>0?t[0]:null,r=!this.taskEnabled.has(e.name)||this.taskEnabled.get(e.name)===!0;return{name:e.name,expression:s,humanReadable:this.humanizeExpression(s),nextRun:this.getNextRun(s),lastRun:n?.timestamp,lastDuration:n?.duration,lastStatus:n?.success?"success":"failed",enabled:r,isRunning:e.isRunning(),history:t.slice(0,5)}}humanizeExpression(e){let t=e.trim().split(/\s+/);if(t.length!==5)return e;let[s,n,r,i,a]=t;if(e==="* * * * *")return"Every minute";if(e==="*/5 * * * *")return"Every 5 minutes";if(e==="*/10 * * * *")return"Every 10 minutes";if(e==="*/15 * * * *")return"Every 15 minutes";if(e==="*/30 * * * *")return"Every 30 minutes";if(e==="0 * * * *")return"Every hour";if(e==="0 0 * * *")return"Daily at midnight";if(e==="0 0 * * 0")return"Weekly (Sunday at midnight)";if(e==="0 0 1 * *")return"Monthly (1st at midnight)";if(e==="0 0 1 1 *")return"Yearly (Jan 1st at midnight)";if(r==="*"&&i==="*"&&a==="*"&&n!=="*"&&s!=="*"){let o=parseInt(n,10),u=parseInt(s,10);return`Daily at ${o.toString().padStart(2,"0")}:${u.toString().padStart(2,"0")}`}if(a==="1-5"&&r==="*"&&i==="*"&&n!=="*"&&s!=="*"){let o=parseInt(n,10),u=parseInt(s,10);return`Weekdays at ${o.toString().padStart(2,"0")}:${u.toString().padStart(2,"0")}`}return e}getNextRun(e){let t=new Date,s=new Date(t);s.setSeconds(0),s.setMilliseconds(0),s.setMinutes(s.getMinutes()+1);let n=366*24*60,r=0;for(;r<n;){if(P(e,s))return s;s.setMinutes(s.getMinutes()+1),r++}return t}},w=d("svelar.scheduleMonitor",()=>new R);var D=class extends y{name="svelar-dashboard";version="1.0.0";description="Admin dashboard with system health monitoring";dashboardConfig={enabled:!0,prefix:"/admin/dashboard",refreshInterval:5e3,sections:{logs:!0,queue:!0,scheduler:!0,system:!0}};startTime=Date.now();healthCollectionInterval=null;config(){return{key:"dashboard",defaults:this.dashboardConfig}}async register(e){e.instance("config.dashboard",this.dashboardConfig),e.instance("svelar.logViewer",m),e.instance("svelar.jobMonitor",_),e.instance("svelar.scheduleMonitor",w)}async boot(e){this.dashboardConfig.enabled&&this.dashboardConfig.refreshInterval>0&&(this.healthCollectionInterval=setInterval(()=>{this.getSystemHealth().catch(t=>{console.error("[Dashboard] Health collection error:",t)})},this.dashboardConfig.refreshInterval))}async shutdown(){this.healthCollectionInterval&&(clearInterval(this.healthCollectionInterval),this.healthCollectionInterval=null)}setConfig(e){this.dashboardConfig={...this.dashboardConfig,...e,sections:{...this.dashboardConfig.sections,...e.sections??{}}},this.healthCollectionInterval&&(clearInterval(this.healthCollectionInterval),this.healthCollectionInterval=null),this.dashboardConfig.enabled&&this.dashboardConfig.refreshInterval>0&&(this.healthCollectionInterval=setInterval(()=>{this.getSystemHealth().catch(t=>{console.error("[Dashboard] Health collection error:",t)})},this.dashboardConfig.refreshInterval))}async getSystemHealth(){let e=Date.now()-this.startTime,t=process.memoryUsage(),s=process.cpuUsage(),n=await _.getHealth(),r=await w.getHealth(),i=m.getStats(),a="healthy";return n.failureRate>50||r.lastErrors.length>5?a="critical":(n.failureRate>20||r.lastErrors.length>2)&&(a="degraded"),t.heapUsed/t.heapTotal*100>90&&(a=a==="healthy"?"degraded":a),{status:a,uptime:e,nodeVersion:process.version,memoryUsage:{heapUsed:t.heapUsed,heapTotal:t.heapTotal,rss:t.rss,external:t.external},cpuUsage:{user:s.user,system:s.system},queueHealth:n,schedulerHealth:r,logStats:i,timestamp:new Date().toISOString()}}async getDashboardData(){let e=await this.getSystemHealth(),t={health:e,config:this.dashboardConfig};return this.dashboardConfig.sections.logs&&(t.logs={stats:m.getStats(),recentErrors:m.getRecentErrors(20)}),this.dashboardConfig.sections.queue&&(t.queue={health:e.queueHealth}),this.dashboardConfig.sections.scheduler&&(t.scheduler={health:e.schedulerHealth,tasks:await w.listTasks()}),this.dashboardConfig.sections.system&&(t.system={uptime:e.uptime,nodeVersion:e.nodeVersion,memoryUsage:e.memoryUsage,cpuUsage:e.cpuUsage}),t}},S=d("svelar.dashboard",()=>new D);function ie(l){S.setConfig(l)}async function ae(){return S.getSystemHealth()}async function oe(){return S.getDashboardData()}export{S as Dashboard,ie as configureDashboard,oe as getDashboardData,ae as getDashboardHealth};
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Svelar Database Connection
3
+ *
4
+ * Manages database connections with support for SQLite, PostgreSQL, and MySQL.
5
+ * Wraps Drizzle ORM for the underlying driver.
6
+ */
7
+ export type DatabaseDriver = 'sqlite' | 'postgres' | 'mysql';
8
+ export interface DatabaseConfig {
9
+ driver: DatabaseDriver;
10
+ /** SQLite file path */
11
+ filename?: string;
12
+ /** Host for postgres/mysql */
13
+ host?: string;
14
+ /** Port for postgres/mysql */
15
+ port?: number;
16
+ /** Database name */
17
+ database?: string;
18
+ /** Database user */
19
+ user?: string;
20
+ /** Database password */
21
+ password?: string;
22
+ /** Connection URL (alternative to individual fields) */
23
+ url?: string;
24
+ /** Enable debug/query logging */
25
+ debug?: boolean;
26
+ }
27
+ export interface ConnectionsConfig {
28
+ default: DatabaseDriver;
29
+ connections: Record<string, DatabaseConfig>;
30
+ }
31
+ export type DrizzleInstance = any;
32
+ declare class ConnectionManager {
33
+ private connections;
34
+ private config;
35
+ private defaultName;
36
+ /**
37
+ * Initialize the connection manager with configuration
38
+ */
39
+ configure(config: ConnectionsConfig): void;
40
+ /**
41
+ * Get or create a database connection by name
42
+ */
43
+ connection(name?: string): Promise<DrizzleInstance>;
44
+ /**
45
+ * Get the raw client (e.g. better-sqlite3 Database, pg Pool, mysql2 connection)
46
+ */
47
+ rawClient(name?: string): Promise<any>;
48
+ /**
49
+ * Execute raw SQL
50
+ */
51
+ raw(sql: string, bindings?: any[], connectionName?: string): Promise<any>;
52
+ /**
53
+ * Get the driver type for a connection
54
+ */
55
+ getDriver(name?: string): DatabaseDriver;
56
+ /**
57
+ * Get connection config
58
+ */
59
+ getConfig(name?: string): DatabaseConfig;
60
+ /**
61
+ * Close all connections
62
+ */
63
+ disconnect(name?: string): Promise<void>;
64
+ /**
65
+ * Check if a connection is active
66
+ */
67
+ isConnected(name?: string): boolean;
68
+ /**
69
+ * Run a callback inside a database transaction.
70
+ * Automatically commits on success, rolls back on error.
71
+ */
72
+ transaction<T>(callback: () => Promise<T>, connectionName?: string): Promise<T>;
73
+ private createConnection;
74
+ private createSQLiteConnection;
75
+ private createPostgresConnection;
76
+ private createMySQLConnection;
77
+ private closeConnection;
78
+ }
79
+ export declare const Connection: ConnectionManager;
80
+ export {};
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Svelar Migration System
3
+ *
4
+ * Laravel-like database migrations with up/down support,
5
+ * batch tracking, and rollback capabilities.
6
+ */
7
+ import { Schema } from './SchemaBuilder.js';
8
+ export declare abstract class Migration {
9
+ protected schema: Schema;
10
+ /** Apply the migration */
11
+ abstract up(): Promise<void>;
12
+ /** Reverse the migration */
13
+ abstract down(): Promise<void>;
14
+ }
15
+ export interface MigrationFile {
16
+ name: string;
17
+ timestamp: string;
18
+ path: string;
19
+ migration: Migration;
20
+ }
21
+ export declare class Migrator {
22
+ private migrationsTable;
23
+ private connectionName?;
24
+ constructor(connectionName?: string);
25
+ /**
26
+ * Ensure the migrations tracking table exists
27
+ */
28
+ ensureMigrationsTable(): Promise<void>;
29
+ /**
30
+ * Run all pending migrations
31
+ */
32
+ run(migrations: MigrationFile[]): Promise<string[]>;
33
+ /**
34
+ * Rollback the last batch of migrations
35
+ */
36
+ rollback(migrations: MigrationFile[]): Promise<string[]>;
37
+ /**
38
+ * Reset all migrations (rollback everything)
39
+ */
40
+ reset(migrations: MigrationFile[]): Promise<string[]>;
41
+ /**
42
+ * Refresh: reset + run all
43
+ */
44
+ refresh(migrations: MigrationFile[]): Promise<{
45
+ reset: string[];
46
+ migrated: string[];
47
+ }>;
48
+ /**
49
+ * Fresh: drop ALL tables then re-run all migrations.
50
+ * Unlike refresh (which calls each migration's down() method),
51
+ * fresh simply drops every table in the database and starts clean.
52
+ */
53
+ fresh(migrations: MigrationFile[]): Promise<{
54
+ dropped: string[];
55
+ migrated: string[];
56
+ }>;
57
+ /**
58
+ * Drop all tables in the database.
59
+ * Returns the list of dropped table names.
60
+ */
61
+ dropAllTables(): Promise<string[]>;
62
+ /**
63
+ * Get list of already-ran migration names
64
+ */
65
+ getRanMigrations(): Promise<string[]>;
66
+ /**
67
+ * Get the status of all migrations
68
+ */
69
+ status(migrations: MigrationFile[]): Promise<{
70
+ name: string;
71
+ ran: boolean;
72
+ batch: number | null;
73
+ }[]>;
74
+ private getNextBatch;
75
+ private getLastBatch;
76
+ }