webspresso 0.0.97 → 0.0.99
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/.agents/ADMIN.md +163 -5
- package/.agents/AGENTS.md +1 -1
- package/.agents/ARCHITECTURE.md +10 -0
- package/.agents/AUTH.md +169 -0
- package/.agents/CLI.md +195 -1
- package/.agents/ORM.md +88 -4
- package/.agents/PLUGINS.md +221 -9
- package/.agents/QUEUE.md +127 -0
- package/.agents/ROUTING.md +127 -7
- package/AGENTS.md +1 -0
- package/bin/commands/add-esbuild.js +80 -0
- package/bin/commands/add-tailwind.js +76 -31
- package/bin/commands/analytics-prune.js +57 -0
- package/bin/commands/api.js +207 -15
- package/bin/commands/build.js +99 -0
- package/bin/commands/dev.js +64 -35
- package/bin/commands/doctor.js +26 -0
- package/bin/commands/email-prune.js +47 -0
- package/bin/commands/make-job.js +86 -0
- package/bin/commands/make-model.js +250 -0
- package/bin/commands/new.js +45 -28
- package/bin/commands/openapi.js +102 -0
- package/bin/commands/page.js +6 -3
- package/bin/commands/queue-prune.js +59 -0
- package/bin/commands/routes.js +52 -0
- package/bin/commands/service.js +6 -3
- package/bin/commands/typegen.js +106 -0
- package/bin/commands/upload-prune.js +79 -0
- package/bin/webspresso.js +22 -0
- package/core/applySchema.js +13 -6
- package/core/auth/hash.js +21 -12
- package/core/auth/index.js +60 -0
- package/core/auth/manager.js +134 -0
- package/core/auth/middleware.js +88 -0
- package/core/auth/oauth.js +517 -0
- package/core/auth/qrcode.js +499 -0
- package/core/auth/totp.js +504 -0
- package/core/compression/middleware.js +23 -17
- package/core/openapi/build-from-api-routes.js +160 -29
- package/core/orm/cache/fingerprint.js +15 -1
- package/core/orm/cache/memory-provider.js +27 -2
- package/core/orm/eager-loader.js +144 -10
- package/core/orm/index.js +20 -7
- package/core/orm/model.js +53 -7
- package/core/orm/query-builder.js +90 -4
- package/core/orm/repository.js +44 -3
- package/core/orm/search.js +213 -0
- package/core/queue/adapters/database.js +94 -0
- package/core/queue/adapters/memory.js +62 -0
- package/core/queue/adapters/redis.js +80 -0
- package/core/queue/cron.js +331 -0
- package/core/queue/index.js +8 -1
- package/core/queue/manager.js +205 -1
- package/core/queue/scheduler.js +359 -0
- package/core/sse/index.js +19 -0
- package/core/sse/stream.js +430 -0
- package/core/ssr/stream.js +11 -0
- package/index.d.ts +1188 -10
- package/index.js +88 -96
- package/package.json +1 -1
- package/plugins/admin-panel/api.js +279 -17
- package/plugins/admin-panel/app.js +114 -22
- package/plugins/admin-panel/client/parts/01-state-api-breadcrumb.js +314 -9
- package/plugins/admin-panel/client/parts/04-field-renderers.js +264 -67
- package/plugins/admin-panel/client/parts/05-rich-text-file-helpers.js +333 -36
- package/plugins/admin-panel/client/parts/06-login-setup-forms.js +119 -28
- package/plugins/admin-panel/client/parts/07-model-list.js +49 -8
- package/plugins/admin-panel/client/parts/08-record-list.js +248 -23
- package/plugins/admin-panel/client/parts/09-record-form.js +59 -12
- package/plugins/admin-panel/client/parts/10-export-registry.js +23 -0
- package/plugins/admin-panel/client/verify-spa-parts.js +2 -0
- package/plugins/admin-panel/core/admin-module.js +13 -7
- package/plugins/admin-panel/core/api-extensions.js +30 -2
- package/plugins/admin-panel/core/registry.js +96 -16
- package/plugins/admin-panel/field-renderers/file-upload.js +187 -104
- package/plugins/admin-panel/field-renderers/index.js +2 -0
- package/plugins/admin-panel/index.js +96 -0
- package/plugins/admin-panel/lib/login-rate-limiter.js +119 -0
- package/plugins/admin-panel/modules/admin-users.js +16 -1
- package/plugins/admin-panel/modules/bulk-actions.js +13 -5
- package/plugins/admin-panel/modules/custom-pages.js +492 -3
- package/plugins/admin-panel/modules/dashboard.js +283 -0
- package/plugins/admin-panel/modules/menu.js +56 -18
- package/plugins/admin-panel/modules/user-management.js +24 -6
- package/plugins/api-key/crypto.js +101 -0
- package/plugins/api-key/index.js +288 -0
- package/plugins/api-key/middleware.js +461 -0
- package/plugins/api-key/migration-template.js +48 -0
- package/plugins/api-key/model.js +50 -0
- package/plugins/api-key/scopes.js +125 -0
- package/plugins/audit-log/admin-component.js +487 -59
- package/plugins/audit-log/api-handlers.js +148 -0
- package/plugins/audit-log/index.js +153 -3
- package/plugins/audit-log/purge.js +60 -10
- package/plugins/dashboard/app.js +152 -27
- package/plugins/dashboard/index.js +143 -39
- package/plugins/dashboard/styles.js +78 -0
- package/plugins/data-exchange/export-xlsx.js +9 -2
- package/plugins/data-exchange/import.js +9 -2
- package/plugins/data-exchange/index.js +6 -3
- package/plugins/email/index.js +132 -2
- package/plugins/email/purge.js +60 -10
- package/plugins/file-manager/admin-component.js +20 -4
- package/plugins/file-manager/index.js +21 -4
- package/plugins/index.js +10 -0
- package/plugins/oauth/index.js +338 -0
- package/plugins/orm-cache-admin/index.js +4 -2
- package/plugins/queue/index.js +15 -0
- package/plugins/schema-explorer/admin-component.js +714 -0
- package/plugins/schema-explorer/relations-graph.js +205 -0
- package/plugins/schema-explorer.js +248 -69
- package/plugins/site-analytics/admin-component.js +23 -0
- package/plugins/site-analytics/api-handlers.js +42 -0
- package/plugins/site-analytics/index.js +147 -2
- package/plugins/site-analytics/purge.js +168 -0
- package/plugins/swagger.js +261 -13
- package/plugins/upload/index.js +26 -0
- package/plugins/upload/orphan-pruner.js +292 -0
- package/src/api/api-loader.js +4 -1
- package/src/bundle/index.js +517 -0
- package/src/bundle/manifest.js +165 -0
- package/src/dev/banner.js +296 -0
- package/src/dev/colors.js +70 -0
- package/src/dev/index.js +62 -0
- package/src/dev/profiler.js +354 -0
- package/src/dev/typegen.js +497 -0
- package/src/dev-reload/index.js +611 -0
- package/src/discovery/file-route-parser.js +44 -1
- package/src/discovery/scan-routes.js +2 -2
- package/src/file-router.js +347 -162
- package/src/helpers.js +967 -98
- package/src/njk-frontmatter.js +12 -0
- package/src/pages/page-loader.js +61 -11
- package/src/routing/cascading.js +378 -0
- package/src/routing/middleware-resolver.js +92 -0
- package/src/server.js +635 -63
package/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import type { Application, NextFunction, Request, RequestHandler, Response } from 'express';
|
|
6
6
|
import type { Knex } from 'knex';
|
|
7
7
|
import type { ZodObject, ZodTypeAny } from 'zod';
|
|
8
|
+
import type { EventEmitter } from 'events';
|
|
8
9
|
|
|
9
10
|
/** Registered as createApp middlewares[name]: plain handler or (options) => handler (for middleware: ['name', options]). */
|
|
10
11
|
export type WebspressoRegisteredMiddleware =
|
|
@@ -45,6 +46,7 @@ export interface CreateAppOptions {
|
|
|
45
46
|
enabled?: boolean;
|
|
46
47
|
stylesheets?: boolean;
|
|
47
48
|
scripts?: boolean;
|
|
49
|
+
auto?: boolean;
|
|
48
50
|
};
|
|
49
51
|
/** Trust proxy configuration (default: 1) */
|
|
50
52
|
trustProxy?: boolean | number | string;
|
|
@@ -66,6 +68,17 @@ export interface CreateAppOptions {
|
|
|
66
68
|
compression?: boolean | CompressionOptions;
|
|
67
69
|
/** Shutdown and lifecycle configuration */
|
|
68
70
|
shutdown?: ShutdownOptions;
|
|
71
|
+
/** Development tooling and DX options */
|
|
72
|
+
dev?: {
|
|
73
|
+
banner?: boolean;
|
|
74
|
+
routeTable?: boolean;
|
|
75
|
+
profiler?: boolean;
|
|
76
|
+
nPlusOne?: boolean;
|
|
77
|
+
nPlusOneThreshold?: number;
|
|
78
|
+
slowQueryMs?: number;
|
|
79
|
+
verbose?: boolean;
|
|
80
|
+
colors?: boolean;
|
|
81
|
+
};
|
|
69
82
|
server?: {
|
|
70
83
|
port?: number;
|
|
71
84
|
shutdown?: ShutdownOptions;
|
|
@@ -83,10 +96,84 @@ export interface CreateAppOptions {
|
|
|
83
96
|
services?: {
|
|
84
97
|
dir?: string;
|
|
85
98
|
};
|
|
99
|
+
/** Development SSE live/hot reload configuration */
|
|
100
|
+
devReload?: boolean | DevReloadOptions;
|
|
101
|
+
/** Client-side JS incremental bundler (esbuild context() API) */
|
|
102
|
+
bundle?: boolean | DevBundlerOptions;
|
|
86
103
|
setupRoutes?: (app: Application, ctx: SetupRoutesContext) => void;
|
|
87
104
|
[key: string]: unknown;
|
|
88
105
|
}
|
|
89
106
|
|
|
107
|
+
export interface DevReloadOptions {
|
|
108
|
+
enabled?: boolean;
|
|
109
|
+
watch?: string[];
|
|
110
|
+
watchCss?: boolean;
|
|
111
|
+
watchTemplates?: boolean;
|
|
112
|
+
watchPublicAll?: boolean;
|
|
113
|
+
templateExtensions?: string[];
|
|
114
|
+
pageExtensions?: string[];
|
|
115
|
+
autoInject?: boolean;
|
|
116
|
+
debounce?: number;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export interface DevReloadHub {
|
|
120
|
+
broadcast(type: 'css' | 'full' | 'error' | 'clear-error' | string, payload?: Record<string, unknown>): void;
|
|
121
|
+
close(): void;
|
|
122
|
+
getClientsCount(): number;
|
|
123
|
+
getWatchersCount(): number;
|
|
124
|
+
getWatchedPaths(): string[];
|
|
125
|
+
attachNunjucks(env: unknown): void;
|
|
126
|
+
setOnTemplateChange(fn: (file?: string) => void): void;
|
|
127
|
+
invalidateTemplateCache(file?: string): void;
|
|
128
|
+
triggerTemplateReload(filename?: string, reason?: string): void;
|
|
129
|
+
triggerCssReload(filename?: string): void;
|
|
130
|
+
handleSse(req: any, res: any): void;
|
|
131
|
+
handleScript(req: any, res: any): void;
|
|
132
|
+
autoInjectMiddleware(req: any, res: any, next: () => void): void;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export interface DevBundlerOptions {
|
|
136
|
+
enabled?: boolean;
|
|
137
|
+
entryPoints?: string[] | Record<string, string>;
|
|
138
|
+
outdir?: string;
|
|
139
|
+
bundle?: boolean;
|
|
140
|
+
sourcemap?: boolean;
|
|
141
|
+
minify?: boolean;
|
|
142
|
+
target?: string | string[];
|
|
143
|
+
format?: string;
|
|
144
|
+
metafile?: boolean;
|
|
145
|
+
plugins?: any[];
|
|
146
|
+
esbuild?: any;
|
|
147
|
+
watch?: boolean;
|
|
148
|
+
esbuildOptions?: Record<string, unknown>;
|
|
149
|
+
[key: string]: unknown;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export interface DevBundler {
|
|
153
|
+
isAvailable(): boolean;
|
|
154
|
+
isWatching(): boolean;
|
|
155
|
+
isDisposed(): boolean;
|
|
156
|
+
getEntryPoints(): string[] | Record<string, string>;
|
|
157
|
+
getOutdir(): string;
|
|
158
|
+
startWatch(): Promise<any>;
|
|
159
|
+
rebuild(): Promise<any>;
|
|
160
|
+
dispose(): Promise<void>;
|
|
161
|
+
getContext(): any;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export interface BuildBundleResult {
|
|
165
|
+
result: unknown;
|
|
166
|
+
manifest: Record<string, unknown>;
|
|
167
|
+
manifestPath: string;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export function createDevBundler(options?: DevBundlerOptions, devReloadHub?: unknown): DevBundler;
|
|
171
|
+
export function buildBundle(options?: DevBundlerOptions): Promise<BuildBundleResult>;
|
|
172
|
+
export function generateManifest(metafile: unknown, options?: unknown): Record<string, unknown>;
|
|
173
|
+
export function createManifestPlugin(options?: unknown): unknown;
|
|
174
|
+
export function findDefaultEntryPoints(cwd?: string): string[] | Record<string, string>;
|
|
175
|
+
export function findPageEntryPoints(cwd?: string): Record<string, string>;
|
|
176
|
+
|
|
90
177
|
export interface ServiceContext {
|
|
91
178
|
req?: Request;
|
|
92
179
|
res?: Response;
|
|
@@ -314,6 +401,11 @@ export interface WebspressoApplication extends Application {
|
|
|
314
401
|
enableShutdownHooks(): this;
|
|
315
402
|
disableShutdownHooks(): this;
|
|
316
403
|
setErrorHandler(handler: (error: unknown, req: Request, res: Response, next: NextFunction) => Promise<unknown> | unknown): this;
|
|
404
|
+
broadcastDevReload?(type: 'css' | 'full' | string, payload?: Record<string, unknown>): void;
|
|
405
|
+
devReloadHub?: DevReloadHub;
|
|
406
|
+
devBundler?: DevBundler;
|
|
407
|
+
printDevBanner?(opts?: Record<string, unknown>): void;
|
|
408
|
+
formatDevBanner?(opts?: Record<string, unknown>): { bannerText: string; plainText: string; summary: Record<string, unknown> };
|
|
317
409
|
}
|
|
318
410
|
|
|
319
411
|
export interface CreateAppResult {
|
|
@@ -326,6 +418,11 @@ export interface CreateAppResult {
|
|
|
326
418
|
|
|
327
419
|
export function createApp(options?: CreateAppOptions): CreateAppResult;
|
|
328
420
|
|
|
421
|
+
export function formatDevBanner(context?: Record<string, unknown>, options?: Record<string, unknown>): { bannerText: string; plainText: string; summary: Record<string, unknown> };
|
|
422
|
+
export function printDevBanner(context?: Record<string, unknown>, options?: Record<string, unknown>): void;
|
|
423
|
+
export function formatRouteTable(routeTable: unknown, options?: Record<string, unknown>): string;
|
|
424
|
+
export function createDevProfilerMiddleware(options?: Record<string, unknown>): RequestHandler;
|
|
425
|
+
|
|
329
426
|
// --- Exceptions & Errors ---
|
|
330
427
|
|
|
331
428
|
export interface ErrorOptions {
|
|
@@ -444,8 +541,47 @@ export function mountPages(
|
|
|
444
541
|
registerDynamicFileRoutes: () => void;
|
|
445
542
|
};
|
|
446
543
|
|
|
544
|
+
export interface CascadingLayoutInfo {
|
|
545
|
+
layoutRelPath: string;
|
|
546
|
+
fullPath: string;
|
|
547
|
+
configPath: string | null;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
export namespace cascading {
|
|
551
|
+
export function getDirectoryChain(relativeFilePath: string): string[];
|
|
552
|
+
export function templateHasExtends(templateBody?: string | null, fullPath?: string): boolean;
|
|
553
|
+
export function findCascadingMiddlewareFiles(baseDir: string, relativeFilePath: string, isDev?: boolean): string[];
|
|
554
|
+
export function loadCascadingMiddlewares(
|
|
555
|
+
baseDir: string,
|
|
556
|
+
relativeFilePath: string,
|
|
557
|
+
middlewareRegistry?: Record<string, unknown>,
|
|
558
|
+
isDev?: boolean
|
|
559
|
+
): Array<(req: any, res: any, next: (err?: any) => void) => void>;
|
|
560
|
+
export function runMiddleware(mw: Function, req: any, res: any): Promise<void>;
|
|
561
|
+
export function runMiddlewareChain(chain: Function[], req: any, res: any): Promise<void>;
|
|
562
|
+
export function findCascadingLayouts(baseDir: string, relativeFilePath: string, isDev?: boolean): CascadingLayoutInfo[];
|
|
563
|
+
export function executeCascadingLayoutLoads(layouts: CascadingLayoutInfo[], req: any, ctx: any, isDev?: boolean): Promise<void>;
|
|
564
|
+
export function applyCascadingLayouts(
|
|
565
|
+
nunjucksEnv: any,
|
|
566
|
+
layouts: CascadingLayoutInfo[],
|
|
567
|
+
pageHtml: string,
|
|
568
|
+
renderContext: Record<string, unknown>,
|
|
569
|
+
isDev?: boolean
|
|
570
|
+
): string;
|
|
571
|
+
export function clearCascadingCaches(): void;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
export function resolveMiddlewares(
|
|
575
|
+
middlewareConfig: unknown[] | null | undefined,
|
|
576
|
+
middlewareRegistry?: Record<string, unknown>
|
|
577
|
+
): Array<(req: any, res: any, next: (err?: any) => void) => void>;
|
|
578
|
+
|
|
447
579
|
export function filePathToRoute(filePath: string, pagesDir: string): string;
|
|
448
580
|
|
|
581
|
+
export function isRouteGroupSegment(segment: string): boolean;
|
|
582
|
+
|
|
583
|
+
export function stripRouteGroups(route: string): string;
|
|
584
|
+
|
|
449
585
|
export function extractMethodFromFilename(filename: string): {
|
|
450
586
|
method: string;
|
|
451
587
|
baseName: string;
|
|
@@ -521,6 +657,19 @@ export const utils: Record<string, unknown>;
|
|
|
521
657
|
|
|
522
658
|
export class AssetManager {
|
|
523
659
|
constructor(options?: Record<string, unknown>);
|
|
660
|
+
resolve(assetPath: string): string;
|
|
661
|
+
asset(assetPath: string): string;
|
|
662
|
+
js(src: string, attributes?: Record<string, unknown>): string;
|
|
663
|
+
css(href: string, attributes?: Record<string, unknown>): string;
|
|
664
|
+
pageJs(name: string, attributes?: Record<string, unknown>): string;
|
|
665
|
+
pageJsUrl(name: string): string;
|
|
666
|
+
pageCss(name: string, attributes?: Record<string, unknown>): string;
|
|
667
|
+
pageCssUrl(name: string): string;
|
|
668
|
+
hasPageJs(name: string): boolean;
|
|
669
|
+
hasPageCss(name: string): boolean;
|
|
670
|
+
getRouteAssetCandidates(routeFile?: string, routePath?: string): string[];
|
|
671
|
+
pageAssets(nameOrOptions?: string | Record<string, unknown>, attributes?: Record<string, unknown>): string;
|
|
672
|
+
pageAssetsForRoute(routeFile?: string, routePath?: string, options?: Record<string, unknown>): string;
|
|
524
673
|
[key: string]: unknown;
|
|
525
674
|
}
|
|
526
675
|
|
|
@@ -603,10 +752,111 @@ export interface RelationDefinition {
|
|
|
603
752
|
localKey?: string;
|
|
604
753
|
}
|
|
605
754
|
|
|
755
|
+
export interface AdminPermissions {
|
|
756
|
+
create?: boolean;
|
|
757
|
+
edit?: boolean;
|
|
758
|
+
delete?: boolean;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
export interface AdminSortConfig {
|
|
762
|
+
column: string;
|
|
763
|
+
direction?: 'asc' | 'desc';
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
export type AdminBadgeColor =
|
|
767
|
+
| 'green'
|
|
768
|
+
| 'red'
|
|
769
|
+
| 'blue'
|
|
770
|
+
| 'amber'
|
|
771
|
+
| 'yellow'
|
|
772
|
+
| 'purple'
|
|
773
|
+
| 'violet'
|
|
774
|
+
| 'indigo'
|
|
775
|
+
| 'gray'
|
|
776
|
+
| string;
|
|
777
|
+
|
|
778
|
+
export interface AdminColumnConfig {
|
|
779
|
+
label?: string;
|
|
780
|
+
badge?: Record<string, AdminBadgeColor>;
|
|
781
|
+
hidden?: boolean;
|
|
782
|
+
readonly?: boolean;
|
|
783
|
+
sortable?: boolean;
|
|
784
|
+
[key: string]: unknown;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
export interface AdminActionConfig {
|
|
788
|
+
id: string;
|
|
789
|
+
label: string;
|
|
790
|
+
icon?: string;
|
|
791
|
+
color?: string;
|
|
792
|
+
models?: string | string[];
|
|
793
|
+
confirm?: boolean;
|
|
794
|
+
confirmMessage?: string;
|
|
795
|
+
showWhen?: Record<string, unknown>;
|
|
796
|
+
handler?: (record: any, modelName: string, ctx: any) => Promise<unknown> | unknown;
|
|
797
|
+
visible?: (record: any, modelName: string) => boolean;
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
export interface AdminBulkActionConfig {
|
|
801
|
+
id: string;
|
|
802
|
+
label: string;
|
|
803
|
+
icon?: string;
|
|
804
|
+
color?: string;
|
|
805
|
+
models?: string | string[];
|
|
806
|
+
confirm?: boolean;
|
|
807
|
+
confirmMessage?: string;
|
|
808
|
+
handler?: (records: any[], modelName: string, ctx: any) => Promise<unknown> | unknown;
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
export interface AdminHookContext<TData = any, TRecord = any> {
|
|
812
|
+
model: string;
|
|
813
|
+
data?: TData;
|
|
814
|
+
record?: TRecord;
|
|
815
|
+
id?: string | number;
|
|
816
|
+
req?: any;
|
|
817
|
+
user?: any;
|
|
818
|
+
db?: any;
|
|
819
|
+
stats?: any;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
export interface AdminHooksConfig {
|
|
823
|
+
beforeCreate?: (ctx: AdminHookContext) => Promise<void> | void;
|
|
824
|
+
afterCreate?: (ctx: AdminHookContext) => Promise<void> | void;
|
|
825
|
+
beforeUpdate?: (ctx: AdminHookContext) => Promise<void> | void;
|
|
826
|
+
afterUpdate?: (ctx: AdminHookContext) => Promise<void> | void;
|
|
827
|
+
beforeDelete?: (ctx: AdminHookContext) => Promise<void> | void;
|
|
828
|
+
afterDelete?: (ctx: AdminHookContext) => Promise<void> | void;
|
|
829
|
+
beforeRestore?: (ctx: AdminHookContext) => Promise<void> | void;
|
|
830
|
+
afterRestore?: (ctx: AdminHookContext) => Promise<void> | void;
|
|
831
|
+
onRecordView?: (ctx: AdminHookContext) => Promise<void> | void;
|
|
832
|
+
onDashboardLoad?: (ctx: AdminHookContext) => Promise<void> | void;
|
|
833
|
+
[key: string]: ((ctx: AdminHookContext) => Promise<void> | void) | undefined;
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
export interface AdminRelationConfig {
|
|
837
|
+
displayField?: string;
|
|
838
|
+
searchFields?: string[];
|
|
839
|
+
limit?: number;
|
|
840
|
+
[key: string]: unknown;
|
|
841
|
+
}
|
|
842
|
+
|
|
606
843
|
export interface AdminMetadata {
|
|
607
844
|
enabled?: boolean;
|
|
608
845
|
label?: string;
|
|
609
846
|
icon?: string | null;
|
|
847
|
+
group?: string | null;
|
|
848
|
+
menuOrder?: number | null;
|
|
849
|
+
listColumns?: string[];
|
|
850
|
+
searchFields?: string[];
|
|
851
|
+
displayField?: string;
|
|
852
|
+
readOnly?: boolean;
|
|
853
|
+
permissions?: AdminPermissions;
|
|
854
|
+
defaultSort?: AdminSortConfig;
|
|
855
|
+
columns?: Record<string, AdminColumnConfig>;
|
|
856
|
+
actions?: AdminActionConfig[];
|
|
857
|
+
bulkActions?: AdminBulkActionConfig[];
|
|
858
|
+
hooks?: AdminHooksConfig;
|
|
859
|
+
relations?: Record<string, AdminRelationConfig>;
|
|
610
860
|
customFields?: Record<string, unknown>;
|
|
611
861
|
queries?: Record<string, (repo: Repository) => Promise<unknown>>;
|
|
612
862
|
}
|
|
@@ -653,6 +903,7 @@ export interface ModelOptions<TSchema extends ZodObject<any> = ZodObject<Record<
|
|
|
653
903
|
maxIncludes?: number;
|
|
654
904
|
maxFilterConditions?: number;
|
|
655
905
|
};
|
|
906
|
+
searchable?: string[];
|
|
656
907
|
cache?: boolean | 'auto' | 'smart' | { strategy: 'auto' | 'smart' };
|
|
657
908
|
}
|
|
658
909
|
|
|
@@ -687,6 +938,7 @@ export interface ModelDefinition<TModel = Record<string, any>> {
|
|
|
687
938
|
maxIncludes?: number;
|
|
688
939
|
maxFilterConditions?: number;
|
|
689
940
|
};
|
|
941
|
+
searchable?: string[];
|
|
690
942
|
hooks: Record<string, unknown>;
|
|
691
943
|
cache?: boolean | 'auto' | 'smart' | { strategy: 'auto' | 'smart' };
|
|
692
944
|
}
|
|
@@ -723,7 +975,10 @@ export function getColumnMeta(schema: ZodTypeAny): unknown;
|
|
|
723
975
|
|
|
724
976
|
export interface FindOptions {
|
|
725
977
|
with?: string[];
|
|
978
|
+
include?: string[];
|
|
726
979
|
select?: string[];
|
|
980
|
+
limit?: number;
|
|
981
|
+
offset?: number;
|
|
727
982
|
}
|
|
728
983
|
|
|
729
984
|
export interface PaginatedResult<T = Record<string, any>> {
|
|
@@ -734,6 +989,16 @@ export interface PaginatedResult<T = Record<string, any>> {
|
|
|
734
989
|
totalPages: number;
|
|
735
990
|
}
|
|
736
991
|
|
|
992
|
+
export interface SearchOptions {
|
|
993
|
+
mode?: 'auto' | 'fts' | 'like';
|
|
994
|
+
language?: string;
|
|
995
|
+
matchAllWords?: boolean;
|
|
996
|
+
exact?: boolean;
|
|
997
|
+
rank?: boolean;
|
|
998
|
+
ftsTable?: string;
|
|
999
|
+
boolean?: 'and' | 'or';
|
|
1000
|
+
}
|
|
1001
|
+
|
|
737
1002
|
export interface QueryBuilder<T = Record<string, any>> {
|
|
738
1003
|
where(column: keyof T | string, value: unknown): this;
|
|
739
1004
|
where(column: keyof T | string, operator: string, value: unknown): this;
|
|
@@ -743,14 +1008,19 @@ export interface QueryBuilder<T = Record<string, any>> {
|
|
|
743
1008
|
whereBetween(column: keyof T | string, range: [unknown, unknown]): this;
|
|
744
1009
|
select(...columns: (keyof T | string)[]): this;
|
|
745
1010
|
orderBy(column: keyof T | string, direction?: 'asc' | 'desc'): this;
|
|
1011
|
+
orderByRaw(sql: string, bindings?: unknown[]): this;
|
|
1012
|
+
search(term: string, columns?: string | string[], options?: SearchOptions): this;
|
|
1013
|
+
orSearch(term: string, columns?: string | string[], options?: SearchOptions): this;
|
|
746
1014
|
limit(n: number): this;
|
|
747
1015
|
offset(n: number): this;
|
|
748
1016
|
with(...relations: string[]): this;
|
|
1017
|
+
include(...relations: string[]): this;
|
|
749
1018
|
withTrashed(): this;
|
|
750
1019
|
onlyTrashed(): this;
|
|
751
1020
|
first(): Promise<T | null>;
|
|
752
1021
|
list(): Promise<T[]>;
|
|
753
1022
|
get(): Promise<T[]>;
|
|
1023
|
+
find(): Promise<T[]>;
|
|
754
1024
|
count(): Promise<number>;
|
|
755
1025
|
paginate(page?: number, perPage?: number): Promise<PaginatedResult<T>>;
|
|
756
1026
|
exists(): Promise<boolean>;
|
|
@@ -765,6 +1035,7 @@ export interface Repository<T = Record<string, any>> {
|
|
|
765
1035
|
findById(id: string | number, options?: FindOptions): Promise<T | null>;
|
|
766
1036
|
findOne(conditions: Partial<T> | Record<string, unknown>, options?: FindOptions): Promise<T | null>;
|
|
767
1037
|
findAll(options?: FindOptions): Promise<T[]>;
|
|
1038
|
+
find(conditions?: Partial<T> | Record<string, unknown>, options?: FindOptions): Promise<T[]>;
|
|
768
1039
|
create(data: Partial<T> | Record<string, unknown>): Promise<T>;
|
|
769
1040
|
createMany(data: (Partial<T> | Record<string, unknown>)[]): Promise<T[]>;
|
|
770
1041
|
update(id: string | number, data: Partial<T> | Record<string, unknown>): Promise<T | null>;
|
|
@@ -777,6 +1048,8 @@ export interface Repository<T = Record<string, any>> {
|
|
|
777
1048
|
count(conditions?: Partial<T> | Record<string, unknown>): Promise<number>;
|
|
778
1049
|
exists(conditions: Partial<T> | Record<string, unknown>): Promise<boolean>;
|
|
779
1050
|
with(...relations: string[]): QueryBuilder<T>;
|
|
1051
|
+
include(...relations: string[]): QueryBuilder<T>;
|
|
1052
|
+
search(term: string, columns?: string | string[], options?: SearchOptions): QueryBuilder<T>;
|
|
780
1053
|
model: ModelDefinition<T>;
|
|
781
1054
|
}
|
|
782
1055
|
|
|
@@ -855,8 +1128,30 @@ export interface MigrationManager {
|
|
|
855
1128
|
unlock(): Promise<void>;
|
|
856
1129
|
}
|
|
857
1130
|
|
|
1131
|
+
export interface WebspressoModels {
|
|
1132
|
+
[key: string]: Record<string, any>;
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
export interface WebspressoServices {
|
|
1136
|
+
[key: string]: { input: any; output: any };
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
export interface WebspressoServiceCaller {
|
|
1140
|
+
<K extends keyof WebspressoServices>(
|
|
1141
|
+
name: K,
|
|
1142
|
+
input?: WebspressoServices[K]['input'],
|
|
1143
|
+
opts?: unknown
|
|
1144
|
+
): Promise<WebspressoServices[K]['output']>;
|
|
1145
|
+
<TInput = unknown, TOutput = unknown>(
|
|
1146
|
+
name: string,
|
|
1147
|
+
input?: TInput,
|
|
1148
|
+
opts?: unknown
|
|
1149
|
+
): Promise<TOutput>;
|
|
1150
|
+
}
|
|
1151
|
+
|
|
858
1152
|
export interface TransactionContext {
|
|
859
1153
|
trx: Knex.Transaction;
|
|
1154
|
+
getRepository<K extends keyof WebspressoModels>(modelName: K, scopeContext?: ScopeContext): Repository<WebspressoModels[K]>;
|
|
860
1155
|
getRepository<T = Record<string, any>>(modelName: string, scopeContext?: ScopeContext): Repository<T>;
|
|
861
1156
|
createRepository<T = Record<string, any>>(model: ModelDefinition<T>, scopeContext?: ScopeContext): Repository<T>;
|
|
862
1157
|
}
|
|
@@ -868,6 +1163,7 @@ export interface DatabaseInstance {
|
|
|
868
1163
|
hasModel(name: string): boolean;
|
|
869
1164
|
getAllModels(): ModelDefinition<any>[];
|
|
870
1165
|
registerModel<T = Record<string, any>>(model: ModelDefinition<T>): void;
|
|
1166
|
+
getRepository<K extends keyof WebspressoModels>(modelName: K, scopeContext?: ScopeContext): Repository<WebspressoModels[K]>;
|
|
871
1167
|
getRepository<T = Record<string, any>>(modelName: string, scopeContext?: ScopeContext): Repository<T>;
|
|
872
1168
|
createRepository<T = Record<string, any>>(model: ModelDefinition<T>, scopeContext?: ScopeContext): Repository<T>;
|
|
873
1169
|
query<T = Record<string, any>>(modelName: string, scopeContext?: ScopeContext): QueryBuilder<T>;
|
|
@@ -974,21 +1270,316 @@ export function validateQueryComplexity(
|
|
|
974
1270
|
includes: string[];
|
|
975
1271
|
};
|
|
976
1272
|
|
|
977
|
-
|
|
1273
|
+
export function buildSearchClause(params: {
|
|
1274
|
+
knex: any;
|
|
1275
|
+
model: any;
|
|
1276
|
+
term: string;
|
|
1277
|
+
columns?: string | string[];
|
|
1278
|
+
options?: SearchOptions;
|
|
1279
|
+
}): {
|
|
1280
|
+
raw: true;
|
|
1281
|
+
sql: string;
|
|
1282
|
+
bindings: any[];
|
|
1283
|
+
boolean: 'and' | 'or';
|
|
1284
|
+
rankClause: { sql: string; bindings: any[] } | null;
|
|
1285
|
+
} | null;
|
|
1286
|
+
|
|
1287
|
+
export function tokenizeSearchTerm(term: string): string[];
|
|
1288
|
+
|
|
1289
|
+
export function detectDialect(knex: any): 'sqlite' | 'postgres' | 'mysql' | 'generic';
|
|
1290
|
+
|
|
1291
|
+
export interface SchemaExplorerPluginOptions {
|
|
1292
|
+
/** Raw JSON API endpoint path (default: '/_schema') */
|
|
1293
|
+
path?: string;
|
|
1294
|
+
/** Force enable/disable raw API (default: auto based on NODE_ENV) */
|
|
1295
|
+
enabled?: boolean;
|
|
1296
|
+
/** Model names to exclude from schema exploration */
|
|
1297
|
+
exclude?: string[];
|
|
1298
|
+
/** Include column metadata (default: true) */
|
|
1299
|
+
includeColumns?: boolean;
|
|
1300
|
+
/** Include relation metadata (default: true) */
|
|
1301
|
+
includeRelations?: boolean;
|
|
1302
|
+
/** Include scope configuration (default: true) */
|
|
1303
|
+
includeScopes?: boolean;
|
|
1304
|
+
/** Custom authorization check for raw endpoint */
|
|
1305
|
+
authorize?: (req: any) => boolean;
|
|
1306
|
+
/** Register interactive page in Admin Panel when adminPanelPlugin is present (default: true) */
|
|
1307
|
+
admin?: boolean;
|
|
1308
|
+
/** Path within admin panel (default: '/schema') */
|
|
1309
|
+
adminPath?: string;
|
|
1310
|
+
/** Admin sidebar menu label (default: 'Schema Explorer') */
|
|
1311
|
+
menuLabel?: string;
|
|
1312
|
+
/** Admin sidebar icon (default: 'database') */
|
|
1313
|
+
menuIcon?: string;
|
|
1314
|
+
/** Admin sidebar menu order (default: 85) */
|
|
1315
|
+
menuOrder?: number;
|
|
1316
|
+
/** Admin page title (default: menuLabel or 'Schema Explorer') */
|
|
1317
|
+
title?: string;
|
|
1318
|
+
/** Admin page title alias */
|
|
1319
|
+
adminTitle?: string;
|
|
1320
|
+
/** Admin API prefix (default: '/schema') */
|
|
1321
|
+
apiPrefix?: string;
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
export interface DashboardPluginOptions {
|
|
1325
|
+
/** Base URL path for the developer dashboard (default: '/_webspresso') */
|
|
1326
|
+
path?: string;
|
|
1327
|
+
/** Custom dashboard title displayed in HTML <title> and header (default: 'Webspresso Developer Dashboard') */
|
|
1328
|
+
title?: string;
|
|
1329
|
+
/** Enable or disable the plugin (default: true) */
|
|
1330
|
+
enabled?: boolean;
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
export function dashboardPlugin(options?: DashboardPluginOptions): WebspressoPlugin;
|
|
1334
|
+
export function devToolbarPlugin(options?: DashboardPluginOptions): WebspressoPlugin;
|
|
1335
|
+
export function devtoolsPlugin(options?: DashboardPluginOptions): WebspressoPlugin;
|
|
1336
|
+
|
|
1337
|
+
export function schemaExplorerPlugin(options?: SchemaExplorerPluginOptions): WebspressoPlugin;
|
|
978
1338
|
|
|
979
|
-
export
|
|
1339
|
+
export interface AdminPanelPluginOptions {
|
|
1340
|
+
/** Database instance (required) */
|
|
1341
|
+
db?: any;
|
|
1342
|
+
/** Base URL path for the admin panel (default: '/_admin') */
|
|
1343
|
+
path?: string;
|
|
1344
|
+
/** Enable or disable the plugin (default: true) */
|
|
1345
|
+
enabled?: boolean;
|
|
1346
|
+
/** Custom admin panel title displayed in HTML <title> and header (default: 'Admin Panel') */
|
|
1347
|
+
title?: string;
|
|
1348
|
+
/** Dynamic page title template where %s is page title and %b is base title (default: '%s | %b') */
|
|
1349
|
+
titleTemplate?: string;
|
|
1350
|
+
/** Session secret for admin auth cookie encryption */
|
|
1351
|
+
sessionSecret?: string;
|
|
1352
|
+
/** Auth manager instance for optional user management integration */
|
|
1353
|
+
auth?: any;
|
|
1354
|
+
/** User management configuration */
|
|
1355
|
+
userManagement?: {
|
|
1356
|
+
enabled?: boolean;
|
|
1357
|
+
model?: string;
|
|
1358
|
+
fields?: Record<string, string>;
|
|
1359
|
+
};
|
|
1360
|
+
/** Staff user management option (default: true) */
|
|
1361
|
+
adminUsers?: boolean | { enabled?: boolean };
|
|
1362
|
+
/** Custom registry configuration callback */
|
|
1363
|
+
configure?: (registry: any) => void;
|
|
1364
|
+
/** POST URL for file uploads */
|
|
1365
|
+
uploadUrl?: string;
|
|
1366
|
+
/** Sanitize rich-text HTML values (default: true) */
|
|
1367
|
+
richTextSanitize?: boolean;
|
|
1368
|
+
/** Auto-register data exchange export/import services (default: true) */
|
|
1369
|
+
dataExchange?: boolean;
|
|
1370
|
+
/** Admin login brute-force protection / rate limiting (default: enabled with 5 attempts per 15 mins) */
|
|
1371
|
+
loginRateLimit?: boolean | {
|
|
1372
|
+
enabled?: boolean;
|
|
1373
|
+
maxAttempts?: number;
|
|
1374
|
+
windowMs?: number;
|
|
1375
|
+
blockDurationMs?: number;
|
|
1376
|
+
};
|
|
1377
|
+
[key: string]: unknown;
|
|
1378
|
+
}
|
|
980
1379
|
|
|
981
|
-
export function adminPanelPlugin(options?:
|
|
1380
|
+
export function adminPanelPlugin(options?: AdminPanelPluginOptions): WebspressoPlugin;
|
|
982
1381
|
|
|
983
1382
|
export function dataExchangePlugin(options?: Record<string, unknown>): WebspressoPlugin;
|
|
984
1383
|
|
|
985
|
-
export
|
|
1384
|
+
export interface SiteAnalyticsRetentionConfig {
|
|
1385
|
+
enabled?: boolean;
|
|
1386
|
+
days?: number;
|
|
1387
|
+
errorDays?: number;
|
|
1388
|
+
intervalMs?: number;
|
|
1389
|
+
runOnStart?: boolean;
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
export interface SiteAnalyticsPurgeResult {
|
|
1393
|
+
viewsDeleted: number;
|
|
1394
|
+
errorsDeleted: number;
|
|
1395
|
+
viewsCutoff: string;
|
|
1396
|
+
errorsCutoff: string;
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
export interface SiteAnalyticsPurgeOptions {
|
|
1400
|
+
days?: number;
|
|
1401
|
+
errorDays?: number;
|
|
1402
|
+
olderThan?: Date;
|
|
1403
|
+
errorsOlderThan?: Date;
|
|
1404
|
+
tableName?: string;
|
|
1405
|
+
errorsTableName?: string;
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
export interface SiteAnalyticsPluginOptions {
|
|
1409
|
+
db?: any;
|
|
1410
|
+
tableName?: string;
|
|
1411
|
+
errorsTableName?: string;
|
|
1412
|
+
excludePaths?: (string | RegExp)[];
|
|
1413
|
+
excludeBots?: boolean;
|
|
1414
|
+
customBots?: string[];
|
|
1415
|
+
anonymizeIp?: boolean;
|
|
1416
|
+
trustProxy?: boolean;
|
|
1417
|
+
collectErrors?: boolean;
|
|
1418
|
+
dashboard?: boolean;
|
|
1419
|
+
retentionDays?: number;
|
|
1420
|
+
errorRetentionDays?: number;
|
|
1421
|
+
cleanupIntervalMs?: number;
|
|
1422
|
+
autoCleanup?: boolean;
|
|
1423
|
+
cleanupOnStart?: boolean;
|
|
1424
|
+
retention?: boolean | SiteAnalyticsRetentionConfig;
|
|
1425
|
+
[key: string]: unknown;
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1428
|
+
export function siteAnalyticsPlugin(options?: SiteAnalyticsPluginOptions): WebspressoPlugin & {
|
|
1429
|
+
api?: {
|
|
1430
|
+
purge(options?: SiteAnalyticsPurgeOptions): Promise<SiteAnalyticsPurgeResult>;
|
|
1431
|
+
purgeAnalyticsData(knex: any, options?: SiteAnalyticsPurgeOptions): Promise<SiteAnalyticsPurgeResult>;
|
|
1432
|
+
startCleanup(intervalMs?: number): void;
|
|
1433
|
+
stopCleanup(): void;
|
|
1434
|
+
getRetentionConfig(): SiteAnalyticsRetentionConfig;
|
|
1435
|
+
};
|
|
1436
|
+
dispose?(): void;
|
|
1437
|
+
shutdown?(): void;
|
|
1438
|
+
};
|
|
1439
|
+
|
|
1440
|
+
export interface AuditLogRetentionConfig {
|
|
1441
|
+
enabled?: boolean;
|
|
1442
|
+
autoCleanup?: boolean;
|
|
1443
|
+
days?: number;
|
|
1444
|
+
intervalMs?: number;
|
|
1445
|
+
runOnStart?: boolean;
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1448
|
+
export interface AuditLogPluginOptions {
|
|
1449
|
+
db: any;
|
|
1450
|
+
title?: string;
|
|
1451
|
+
adminTitle?: string;
|
|
1452
|
+
pageTitle?: string;
|
|
1453
|
+
adminPath?: string;
|
|
1454
|
+
tableName?: string;
|
|
1455
|
+
includeDefaultPage?: boolean;
|
|
1456
|
+
apiPrefix?: string;
|
|
1457
|
+
registerRecordAction?: boolean;
|
|
1458
|
+
retentionDays?: number;
|
|
1459
|
+
cleanupIntervalMs?: number;
|
|
1460
|
+
autoCleanup?: boolean;
|
|
1461
|
+
cleanupOnStart?: boolean;
|
|
1462
|
+
cleanupStartDelayMs?: number;
|
|
1463
|
+
retention?: boolean | AuditLogRetentionConfig;
|
|
1464
|
+
[key: string]: unknown;
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
export function auditLogPlugin(options?: AuditLogPluginOptions): WebspressoPlugin & {
|
|
1468
|
+
api?: {
|
|
1469
|
+
purge(options?: { tableName?: string; days?: number; retentionDays?: number; olderThan?: Date | string | number }): Promise<number>;
|
|
1470
|
+
purgeAuditLogs(knex?: any, options?: { tableName?: string; days?: number; retentionDays?: number; olderThan?: Date | string | number }): Promise<number>;
|
|
1471
|
+
startCleanup(intervalMs?: number): void;
|
|
1472
|
+
stopCleanup(): void;
|
|
1473
|
+
getRetentionConfig(): AuditLogRetentionConfig;
|
|
1474
|
+
queryLogs(filters?: Record<string, unknown>): Promise<any[]>;
|
|
1475
|
+
queryTimeline(model: string, recordId: string | number): Promise<any[]>;
|
|
1476
|
+
getRecordTimeline(model: string, recordId: string | number): Promise<any[]>;
|
|
1477
|
+
getMigrationTemplate(tableName?: string): string;
|
|
1478
|
+
};
|
|
1479
|
+
dispose?(): void;
|
|
1480
|
+
shutdown?(): void;
|
|
1481
|
+
};
|
|
1482
|
+
|
|
1483
|
+
export interface EmailRetentionConfig {
|
|
1484
|
+
enabled?: boolean;
|
|
1485
|
+
autoCleanup?: boolean;
|
|
1486
|
+
days?: number;
|
|
1487
|
+
intervalMs?: number;
|
|
1488
|
+
runOnStart?: boolean;
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1491
|
+
export interface EmailPluginOptions {
|
|
1492
|
+
db?: any;
|
|
1493
|
+
title?: string;
|
|
1494
|
+
adminTitle?: string;
|
|
1495
|
+
transport?: any;
|
|
1496
|
+
smtp?: any;
|
|
1497
|
+
defaults?: Record<string, unknown>;
|
|
1498
|
+
templatesDir?: string;
|
|
1499
|
+
templates?: Record<string, string>;
|
|
1500
|
+
tableName?: string;
|
|
1501
|
+
includeAdminPage?: boolean;
|
|
1502
|
+
adminPath?: string;
|
|
1503
|
+
apiPrefix?: string;
|
|
1504
|
+
auth?: any;
|
|
1505
|
+
authEmails?: any;
|
|
1506
|
+
emailTemplates?: any;
|
|
1507
|
+
manifest?: any;
|
|
1508
|
+
logToDb?: boolean;
|
|
1509
|
+
retentionDays?: number;
|
|
1510
|
+
cleanupIntervalMs?: number;
|
|
1511
|
+
autoCleanup?: boolean;
|
|
1512
|
+
cleanupOnStart?: boolean;
|
|
1513
|
+
cleanupStartDelayMs?: number;
|
|
1514
|
+
retention?: boolean | EmailRetentionConfig;
|
|
1515
|
+
[key: string]: unknown;
|
|
1516
|
+
}
|
|
986
1517
|
|
|
987
|
-
export function
|
|
1518
|
+
export function emailPlugin(options?: EmailPluginOptions): WebspressoPlugin & {
|
|
1519
|
+
api?: {
|
|
1520
|
+
send(options: Record<string, unknown>): Promise<any>;
|
|
1521
|
+
sendTemplate(name: string, options: Record<string, unknown>): Promise<any>;
|
|
1522
|
+
preview(nameOrOpts: any, data?: Record<string, unknown>): Promise<any>;
|
|
1523
|
+
registerTemplate(id: string, source: string): void;
|
|
1524
|
+
listTemplates(): any[];
|
|
1525
|
+
verifyConnection(): Promise<any>;
|
|
1526
|
+
queryLogs(filters?: Record<string, unknown>): Promise<any>;
|
|
1527
|
+
purge(options?: { tableName?: string; days?: number; retentionDays?: number; olderThan?: Date | string | number }): Promise<number>;
|
|
1528
|
+
purgeLogs(options?: { tableName?: string; days?: number; retentionDays?: number; olderThan?: Date | string | number }): Promise<number>;
|
|
1529
|
+
startCleanup(intervalMs?: number): void;
|
|
1530
|
+
stopCleanup(): void;
|
|
1531
|
+
getRetentionConfig(): EmailRetentionConfig;
|
|
1532
|
+
getMigrationTemplate(tableName?: string): string;
|
|
1533
|
+
isTransportReady(): boolean;
|
|
1534
|
+
loadFromManifest(chunks: any): void;
|
|
1535
|
+
emailPluginOptionsFromManifest(manifest: any): any;
|
|
1536
|
+
};
|
|
1537
|
+
dispose?(): void;
|
|
1538
|
+
shutdown?(): void;
|
|
1539
|
+
};
|
|
988
1540
|
|
|
989
1541
|
export function recaptchaPlugin(options?: Record<string, unknown>): WebspressoPlugin;
|
|
990
1542
|
|
|
991
|
-
export
|
|
1543
|
+
export interface SwaggerPluginOptions {
|
|
1544
|
+
/** Base path for docs (default: '/_swagger') */
|
|
1545
|
+
path?: string;
|
|
1546
|
+
/** UI playground engine: 'swagger' or 'scalar' (default: 'swagger') */
|
|
1547
|
+
ui?: 'swagger' | 'scalar';
|
|
1548
|
+
/** Enable plugin (defaults to true in development) */
|
|
1549
|
+
enabled?: boolean;
|
|
1550
|
+
/** Custom authorization guard */
|
|
1551
|
+
authorize?: (req: any) => boolean;
|
|
1552
|
+
/** Automatically merge ORM model schemas into components.schemas */
|
|
1553
|
+
includeOrmSchemas?: boolean;
|
|
1554
|
+
/** Exclude specific ORM models from documentation */
|
|
1555
|
+
ormExclude?: string[];
|
|
1556
|
+
/** OpenAPI info.title override */
|
|
1557
|
+
title?: string;
|
|
1558
|
+
/** OpenAPI info.version override */
|
|
1559
|
+
version?: string;
|
|
1560
|
+
/** OpenAPI info.description override */
|
|
1561
|
+
description?: string;
|
|
1562
|
+
/** Primary server URL in spec */
|
|
1563
|
+
serverUrl?: string;
|
|
1564
|
+
/** Theme for Scalar UI ('default', 'purple', 'solarized', etc.) */
|
|
1565
|
+
theme?: string;
|
|
1566
|
+
/** Dark mode default for Scalar UI */
|
|
1567
|
+
darkMode?: boolean;
|
|
1568
|
+
/** Auto-register in Admin Panel if admin-panel plugin is present (default: true) */
|
|
1569
|
+
admin?: boolean;
|
|
1570
|
+
/** Admin menu and page title (default: 'API Docs') */
|
|
1571
|
+
adminTitle?: string;
|
|
1572
|
+
/** Admin menu sort order (default: 92) */
|
|
1573
|
+
adminOrder?: number;
|
|
1574
|
+
/** Custom options passed to Scalar */
|
|
1575
|
+
scalarOptions?: Record<string, unknown>;
|
|
1576
|
+
/** Custom options passed to Swagger UI */
|
|
1577
|
+
swaggerOptions?: Record<string, unknown>;
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1580
|
+
export function swaggerPlugin(options?: SwaggerPluginOptions): WebspressoPlugin;
|
|
1581
|
+
export function openapiPlugin(options?: SwaggerPluginOptions): WebspressoPlugin;
|
|
1582
|
+
export function apiDocsPlugin(options?: SwaggerPluginOptions): WebspressoPlugin;
|
|
992
1583
|
|
|
993
1584
|
export function healthCheckPlugin(options?: Record<string, unknown>): WebspressoPlugin;
|
|
994
1585
|
|
|
@@ -1124,6 +1715,86 @@ export interface BasicAuthPluginOptions {
|
|
|
1124
1715
|
|
|
1125
1716
|
export function basicAuthPlugin(options?: BasicAuthPluginOptions): WebspressoPlugin;
|
|
1126
1717
|
|
|
1718
|
+
export interface ApiKeyRecord {
|
|
1719
|
+
id?: number | string;
|
|
1720
|
+
name: string;
|
|
1721
|
+
key_hash: string;
|
|
1722
|
+
key_hint: string;
|
|
1723
|
+
prefix?: string;
|
|
1724
|
+
user_id?: number | string | null;
|
|
1725
|
+
scopes?: string[] | string | null;
|
|
1726
|
+
rate_limit?: number | null;
|
|
1727
|
+
metadata?: any;
|
|
1728
|
+
expires_at?: Date | string | null;
|
|
1729
|
+
last_used_at?: Date | string | null;
|
|
1730
|
+
revoked_at?: Date | string | null;
|
|
1731
|
+
created_at?: Date | string;
|
|
1732
|
+
updated_at?: Date | string;
|
|
1733
|
+
user?: any;
|
|
1734
|
+
[key: string]: any;
|
|
1735
|
+
}
|
|
1736
|
+
|
|
1737
|
+
export interface ApiKeyPluginOptions {
|
|
1738
|
+
/** Database instance or Knex instance. */
|
|
1739
|
+
db?: any;
|
|
1740
|
+
/** Knex instance. */
|
|
1741
|
+
knex?: any;
|
|
1742
|
+
/** Table name for API keys. Default 'api_keys'. */
|
|
1743
|
+
tableName?: string;
|
|
1744
|
+
/** Default key prefix. Default 'wsp_live_'. */
|
|
1745
|
+
prefix?: string;
|
|
1746
|
+
/** Random byte length for key generation. Default 24. */
|
|
1747
|
+
byteLength?: number;
|
|
1748
|
+
/** Protect all matching routes globally. Default false. */
|
|
1749
|
+
global?: boolean;
|
|
1750
|
+
/** Route prefixes to protect when global: true. Default true (all routes). */
|
|
1751
|
+
routes?: string | string[] | boolean;
|
|
1752
|
+
/** Path prefixes to bypass authentication. */
|
|
1753
|
+
skipPaths?: string[];
|
|
1754
|
+
/** Custom predicate function to skip authentication. */
|
|
1755
|
+
skip?: (req: Request) => boolean;
|
|
1756
|
+
/** Static in-memory keys for testing, serverless, or microservices. */
|
|
1757
|
+
keys?: Array<{ key?: string; key_hash?: string; name?: string; scopes?: string[] | string; [key: string]: any }> | Record<string, any>;
|
|
1758
|
+
/** Automatically update last_used_at timestamp. Default true. */
|
|
1759
|
+
trackUsage?: boolean;
|
|
1760
|
+
/** Throttle interval in milliseconds for last_used_at updates. Default 60000. */
|
|
1761
|
+
updateLastUsedThrottleMs?: number;
|
|
1762
|
+
/** Allow query parameter extraction (?apiKey=...). Default false. */
|
|
1763
|
+
allowQueryParam?: boolean;
|
|
1764
|
+
/** Query parameter name. Default 'apiKey'. */
|
|
1765
|
+
queryParamName?: string;
|
|
1766
|
+
/** Custom key extraction function. */
|
|
1767
|
+
extractKey?: (req: Request) => string | null;
|
|
1768
|
+
/** Custom key lookup by SHA-256 hash. */
|
|
1769
|
+
findKeyByHash?: (hash: string, req?: Request) => Promise<ApiKeyRecord | null> | ApiKeyRecord | null;
|
|
1770
|
+
/** Custom verification hook. */
|
|
1771
|
+
verify?: (rawKey: string, req?: Request) => Promise<ApiKeyRecord | null> | ApiKeyRecord | null;
|
|
1772
|
+
/** Custom user loader for key. */
|
|
1773
|
+
getUser?: (record: ApiKeyRecord, req?: Request) => Promise<any> | any;
|
|
1774
|
+
/** Callback hook on successful authentication. */
|
|
1775
|
+
onAuthenticated?: (req: Request, record: ApiKeyRecord) => void | Promise<void>;
|
|
1776
|
+
/** Custom 401 Unauthorized response handler, string, or object. */
|
|
1777
|
+
unauthorizedResponse?: ((req: Request, res: Response, info?: any) => unknown) | string | Record<string, unknown>;
|
|
1778
|
+
/** Custom 403 Forbidden response handler, string, or object. */
|
|
1779
|
+
forbiddenResponse?: ((req: Request, res: Response, info?: any) => unknown) | string | Record<string, unknown>;
|
|
1780
|
+
}
|
|
1781
|
+
|
|
1782
|
+
export interface ApiKeyRouteOptions {
|
|
1783
|
+
/** Required scope(s) for this route. Supports wildcards ('*', 'reports:*'). */
|
|
1784
|
+
scopes?: string | string[];
|
|
1785
|
+
/** Whether all required scopes must be present (true) or any scope (false). Default true. */
|
|
1786
|
+
requireAllScopes?: boolean;
|
|
1787
|
+
/** If true, allows requests without API key to pass through without error. Default false. */
|
|
1788
|
+
optional?: boolean;
|
|
1789
|
+
/** Custom 401 Unauthorized response. */
|
|
1790
|
+
unauthorizedResponse?: ((req: Request, res: Response, info?: any) => unknown) | string | Record<string, unknown>;
|
|
1791
|
+
/** Custom 403 Forbidden response. */
|
|
1792
|
+
forbiddenResponse?: ((req: Request, res: Response, info?: any) => unknown) | string | Record<string, unknown>;
|
|
1793
|
+
}
|
|
1794
|
+
|
|
1795
|
+
export function apiKeyPlugin(options?: ApiKeyPluginOptions): WebspressoPlugin;
|
|
1796
|
+
export function createApiKeyMiddleware(options?: ApiKeyPluginOptions & ApiKeyRouteOptions): RequestHandler;
|
|
1797
|
+
|
|
1127
1798
|
export interface ContentPluginOptions {
|
|
1128
1799
|
db: import('./index').Database;
|
|
1129
1800
|
adminPath?: string;
|
|
@@ -1183,6 +1854,42 @@ export function createLocalFileProvider(options?: {
|
|
|
1183
1854
|
publicBasePath?: string;
|
|
1184
1855
|
}): UploadStorageProvider;
|
|
1185
1856
|
|
|
1857
|
+
export interface OrphanedUploadsResult {
|
|
1858
|
+
destDir: string;
|
|
1859
|
+
totalFiles: number;
|
|
1860
|
+
totalBytes: number;
|
|
1861
|
+
totalBytesFormatted: string;
|
|
1862
|
+
referencedCount: number;
|
|
1863
|
+
orphanedCount: number;
|
|
1864
|
+
orphanedBytes: number;
|
|
1865
|
+
orphanedBytesFormatted: string;
|
|
1866
|
+
orphanedFiles: Array<{
|
|
1867
|
+
relativePath: string;
|
|
1868
|
+
absolutePath: string;
|
|
1869
|
+
size: number;
|
|
1870
|
+
publicUrl: string;
|
|
1871
|
+
mtime: Date;
|
|
1872
|
+
}>;
|
|
1873
|
+
dryRun?: boolean;
|
|
1874
|
+
deletedCount?: number;
|
|
1875
|
+
deletedBytes?: number;
|
|
1876
|
+
deletedBytesFormatted?: string;
|
|
1877
|
+
deletedFiles?: string[];
|
|
1878
|
+
}
|
|
1879
|
+
|
|
1880
|
+
export function scanOrphanedUploads(options?: {
|
|
1881
|
+
db?: any;
|
|
1882
|
+
destDir?: string;
|
|
1883
|
+
publicBasePath?: string;
|
|
1884
|
+
}): Promise<OrphanedUploadsResult>;
|
|
1885
|
+
|
|
1886
|
+
export function pruneOrphanedUploads(options?: {
|
|
1887
|
+
db?: any;
|
|
1888
|
+
destDir?: string;
|
|
1889
|
+
publicBasePath?: string;
|
|
1890
|
+
dryRun?: boolean;
|
|
1891
|
+
}): Promise<OrphanedUploadsResult>;
|
|
1892
|
+
|
|
1186
1893
|
// --- Realtime Layer Types ---
|
|
1187
1894
|
|
|
1188
1895
|
export interface RealtimeSubscriptionCallbacks {
|
|
@@ -1508,6 +2215,13 @@ export class MemoryQueueAdapter implements QueueAdapter {
|
|
|
1508
2215
|
getJob(jobId: string | number): Promise<Job | null>;
|
|
1509
2216
|
getStats(): Promise<{ pending: number; running: number; completed: number; failed: number; total: number }>;
|
|
1510
2217
|
clear(): Promise<void>;
|
|
2218
|
+
clean?(options?: {
|
|
2219
|
+
status?: string | string[];
|
|
2220
|
+
days?: number;
|
|
2221
|
+
retentionDays?: number;
|
|
2222
|
+
olderThan?: Date | string | number;
|
|
2223
|
+
limit?: number;
|
|
2224
|
+
}): Promise<number>;
|
|
1511
2225
|
}
|
|
1512
2226
|
|
|
1513
2227
|
export class DatabaseQueueAdapter implements QueueAdapter {
|
|
@@ -1521,6 +2235,13 @@ export class DatabaseQueueAdapter implements QueueAdapter {
|
|
|
1521
2235
|
getJob(jobId: string | number): Promise<Job | null>;
|
|
1522
2236
|
getStats(): Promise<{ pending: number; running: number; completed: number; failed: number; total: number }>;
|
|
1523
2237
|
clear(): Promise<void>;
|
|
2238
|
+
clean(options?: {
|
|
2239
|
+
status?: string | string[];
|
|
2240
|
+
days?: number;
|
|
2241
|
+
retentionDays?: number;
|
|
2242
|
+
olderThan?: Date | string | number;
|
|
2243
|
+
limit?: number;
|
|
2244
|
+
}): Promise<number>;
|
|
1524
2245
|
}
|
|
1525
2246
|
|
|
1526
2247
|
export class RedisQueueAdapter implements QueueAdapter {
|
|
@@ -1533,6 +2254,13 @@ export class RedisQueueAdapter implements QueueAdapter {
|
|
|
1533
2254
|
getJob(jobId: string | number): Promise<Job | null>;
|
|
1534
2255
|
getStats(): Promise<{ pending: number; running: number; completed: number; failed: number; total: number }>;
|
|
1535
2256
|
clear(): Promise<void>;
|
|
2257
|
+
clean(options?: {
|
|
2258
|
+
status?: string | string[];
|
|
2259
|
+
days?: number;
|
|
2260
|
+
retentionDays?: number;
|
|
2261
|
+
olderThan?: Date | string | number;
|
|
2262
|
+
limit?: number;
|
|
2263
|
+
}): Promise<number>;
|
|
1536
2264
|
}
|
|
1537
2265
|
|
|
1538
2266
|
export interface DispatchOptions {
|
|
@@ -1543,28 +2271,130 @@ export interface DispatchOptions {
|
|
|
1543
2271
|
backoff?: number | { type?: 'exponential' | 'fixed'; delay?: number };
|
|
1544
2272
|
}
|
|
1545
2273
|
|
|
1546
|
-
export
|
|
2274
|
+
export interface QueueRetentionConfig {
|
|
2275
|
+
enabled?: boolean;
|
|
2276
|
+
autoCleanup?: boolean;
|
|
2277
|
+
days?: number;
|
|
2278
|
+
statuses?: string[];
|
|
2279
|
+
intervalMs?: number;
|
|
2280
|
+
runOnStart?: boolean;
|
|
2281
|
+
}
|
|
2282
|
+
|
|
2283
|
+
export interface ScheduleOptions {
|
|
2284
|
+
id?: string;
|
|
2285
|
+
overlap?: boolean;
|
|
2286
|
+
maxRuns?: number;
|
|
2287
|
+
utc?: boolean;
|
|
2288
|
+
runImmediately?: boolean;
|
|
2289
|
+
delay?: number | Date;
|
|
2290
|
+
attempts?: number;
|
|
2291
|
+
priority?: number;
|
|
2292
|
+
timeout?: number;
|
|
2293
|
+
backoff?: number | { type?: 'exponential' | 'fixed'; delay?: number };
|
|
2294
|
+
[key: string]: unknown;
|
|
2295
|
+
}
|
|
2296
|
+
|
|
2297
|
+
export interface ScheduleInfo {
|
|
2298
|
+
id: string;
|
|
2299
|
+
name: string;
|
|
2300
|
+
cronExpression: string;
|
|
2301
|
+
cron?: CronExpression;
|
|
2302
|
+
data: unknown;
|
|
2303
|
+
options: ScheduleOptions;
|
|
2304
|
+
runsCount: number;
|
|
2305
|
+
lastRunAt: Date | null;
|
|
2306
|
+
nextRunAt: Date | null;
|
|
2307
|
+
isRunning: boolean;
|
|
2308
|
+
activeJobId: string | number | null;
|
|
2309
|
+
registeredAt: Date;
|
|
2310
|
+
}
|
|
2311
|
+
|
|
2312
|
+
export class CronExpression {
|
|
2313
|
+
rawExpression: string;
|
|
2314
|
+
resolvedExpression: string;
|
|
2315
|
+
utc: boolean;
|
|
2316
|
+
minutes: Set<number>;
|
|
2317
|
+
hours: Set<number>;
|
|
2318
|
+
doms: Set<number>;
|
|
2319
|
+
months: Set<number>;
|
|
2320
|
+
dows: Set<number>;
|
|
2321
|
+
constructor(expression: string, options?: { utc?: boolean });
|
|
2322
|
+
match(date?: Date): boolean;
|
|
2323
|
+
getNextDate(fromDate?: Date): Date | null;
|
|
2324
|
+
getNextDates(count?: number, fromDate?: Date): Date[];
|
|
2325
|
+
static isValid(expression: string): boolean;
|
|
2326
|
+
static parse(expression: string, options?: { utc?: boolean }): CronExpression;
|
|
2327
|
+
}
|
|
2328
|
+
|
|
2329
|
+
export function parseCron(expression: string, options?: { utc?: boolean }): CronExpression;
|
|
2330
|
+
export function isValidCron(expression: string): boolean;
|
|
2331
|
+
export function resolveCronAlias(str: string): string;
|
|
2332
|
+
export const CRON_ALIASES: Record<string, string>;
|
|
2333
|
+
|
|
2334
|
+
export class TaskScheduler {
|
|
2335
|
+
queue: QueueManager;
|
|
2336
|
+
maxIntervalMs: number;
|
|
2337
|
+
isRunning: boolean;
|
|
2338
|
+
schedules: Map<string, any>;
|
|
2339
|
+
constructor(queue: QueueManager, options?: { autoStart?: boolean; maxIntervalMs?: number });
|
|
2340
|
+
schedule(cronExpression: string | CronExpression, jobName: string, data?: unknown, options?: ScheduleOptions): ScheduleInfo;
|
|
2341
|
+
cron(cronExpression: string | CronExpression, jobName: string, data?: unknown, options?: ScheduleOptions): ScheduleInfo;
|
|
2342
|
+
unschedule(idOrJobName: string): boolean;
|
|
2343
|
+
getSchedules(): ScheduleInfo[];
|
|
2344
|
+
getSchedule(idOrJobName: string): ScheduleInfo | null;
|
|
2345
|
+
trigger(idOrJobName: string): Promise<Job | null>;
|
|
2346
|
+
start(): this;
|
|
2347
|
+
stop(): this;
|
|
2348
|
+
clear(): void;
|
|
2349
|
+
destroy(): void;
|
|
2350
|
+
}
|
|
2351
|
+
|
|
2352
|
+
export class QueueManager extends EventEmitter {
|
|
1547
2353
|
adapter: QueueAdapter;
|
|
1548
2354
|
concurrency: number;
|
|
1549
2355
|
pollInterval: number;
|
|
1550
2356
|
isRunning: boolean;
|
|
1551
2357
|
isPaused: boolean;
|
|
1552
2358
|
isDraining: boolean;
|
|
2359
|
+
retentionConfig: QueueRetentionConfig;
|
|
2360
|
+
scheduler: TaskScheduler;
|
|
1553
2361
|
constructor(options?: {
|
|
1554
2362
|
adapter?: QueueAdapter;
|
|
1555
2363
|
concurrency?: number;
|
|
1556
2364
|
pollInterval?: number;
|
|
1557
2365
|
jobsDir?: string | null;
|
|
1558
2366
|
autoStart?: boolean;
|
|
2367
|
+
retention?: boolean | QueueRetentionConfig;
|
|
2368
|
+
retentionDays?: number;
|
|
2369
|
+
retentionStatuses?: string[];
|
|
2370
|
+
cleanupIntervalMs?: number;
|
|
2371
|
+
autoCleanup?: boolean;
|
|
2372
|
+
cleanupOnStart?: boolean;
|
|
1559
2373
|
});
|
|
1560
2374
|
define(name: string, handler: (job: Job, ctx: { queue: QueueManager }) => Promise<any> | any, options?: DispatchOptions): this;
|
|
1561
2375
|
has(name: string): boolean;
|
|
1562
2376
|
dispatch(name: string, data?: unknown, options?: DispatchOptions): Promise<Job>;
|
|
2377
|
+
schedule(cronExpression: string | CronExpression, name: string, data?: unknown, options?: ScheduleOptions): ScheduleInfo;
|
|
2378
|
+
cron(cronExpression: string | CronExpression, name: string, data?: unknown, options?: ScheduleOptions): ScheduleInfo;
|
|
2379
|
+
unschedule(idOrJobName: string): boolean;
|
|
2380
|
+
getSchedules(): ScheduleInfo[];
|
|
2381
|
+
getSchedule(idOrJobName: string): ScheduleInfo | null;
|
|
1563
2382
|
start(): this;
|
|
1564
2383
|
pause(): this;
|
|
1565
2384
|
resume(): this;
|
|
1566
2385
|
drain(timeoutMs?: number): Promise<void>;
|
|
1567
2386
|
stop(): Promise<void>;
|
|
2387
|
+
clean(options?: {
|
|
2388
|
+
status?: string | string[];
|
|
2389
|
+
days?: number;
|
|
2390
|
+
retentionDays?: number;
|
|
2391
|
+
olderThan?: Date | string | number;
|
|
2392
|
+
limit?: number;
|
|
2393
|
+
}): Promise<number>;
|
|
2394
|
+
runCleanup(): Promise<number>;
|
|
2395
|
+
startCleanup(customIntervalMs?: number): void;
|
|
2396
|
+
stopCleanup(): void;
|
|
2397
|
+
getRetentionConfig(): QueueRetentionConfig;
|
|
1568
2398
|
getStats(): Promise<Record<string, unknown>>;
|
|
1569
2399
|
getJob(jobId: string | number): Promise<Job | null>;
|
|
1570
2400
|
loadJobsFromDirectory(dirPath: string): void;
|
|
@@ -1576,6 +2406,12 @@ export function createQueueManager(options?: {
|
|
|
1576
2406
|
pollInterval?: number;
|
|
1577
2407
|
jobsDir?: string | null;
|
|
1578
2408
|
autoStart?: boolean;
|
|
2409
|
+
retention?: boolean | QueueRetentionConfig;
|
|
2410
|
+
retentionDays?: number;
|
|
2411
|
+
retentionStatuses?: string[];
|
|
2412
|
+
cleanupIntervalMs?: number;
|
|
2413
|
+
autoCleanup?: boolean;
|
|
2414
|
+
cleanupOnStart?: boolean;
|
|
1579
2415
|
}): QueueManager;
|
|
1580
2416
|
|
|
1581
2417
|
export function queuePlugin(options?: {
|
|
@@ -1588,7 +2424,28 @@ export function queuePlugin(options?: {
|
|
|
1588
2424
|
pollInterval?: number;
|
|
1589
2425
|
jobsDir?: string;
|
|
1590
2426
|
autoStart?: boolean;
|
|
1591
|
-
|
|
2427
|
+
retention?: boolean | QueueRetentionConfig;
|
|
2428
|
+
retentionDays?: number;
|
|
2429
|
+
retentionStatuses?: string[];
|
|
2430
|
+
cleanupIntervalMs?: number;
|
|
2431
|
+
autoCleanup?: boolean;
|
|
2432
|
+
cleanupOnStart?: boolean;
|
|
2433
|
+
}): WebspressoPlugin & {
|
|
2434
|
+
api?: {
|
|
2435
|
+
getQueueManager(): QueueManager | null;
|
|
2436
|
+
clean(options?: {
|
|
2437
|
+
status?: string | string[];
|
|
2438
|
+
days?: number;
|
|
2439
|
+
retentionDays?: number;
|
|
2440
|
+
olderThan?: Date | string | number;
|
|
2441
|
+
limit?: number;
|
|
2442
|
+
}): Promise<number>;
|
|
2443
|
+
schedule(cronExpression: string | CronExpression, name: string, data?: unknown, options?: ScheduleOptions): ScheduleInfo | undefined;
|
|
2444
|
+
cron(cronExpression: string | CronExpression, name: string, data?: unknown, options?: ScheduleOptions): ScheduleInfo | undefined;
|
|
2445
|
+
unschedule(idOrJobName: string): boolean | undefined;
|
|
2446
|
+
getSchedules(): ScheduleInfo[];
|
|
2447
|
+
};
|
|
2448
|
+
};
|
|
1592
2449
|
|
|
1593
2450
|
// --- SSR Streaming & Chunked Transfer ---
|
|
1594
2451
|
|
|
@@ -1619,6 +2476,54 @@ export const ssr: {
|
|
|
1619
2476
|
renderStream: typeof renderStream;
|
|
1620
2477
|
};
|
|
1621
2478
|
|
|
2479
|
+
// --- Server-Sent Events (SSE) Streaming ---
|
|
2480
|
+
|
|
2481
|
+
export interface SseOptions {
|
|
2482
|
+
status?: number;
|
|
2483
|
+
headers?: Record<string, string>;
|
|
2484
|
+
heartbeat?: number | boolean;
|
|
2485
|
+
heartbeatComment?: string;
|
|
2486
|
+
retry?: number;
|
|
2487
|
+
map?: (chunk: any) => any;
|
|
2488
|
+
}
|
|
2489
|
+
|
|
2490
|
+
export interface SseEventObject {
|
|
2491
|
+
data?: any;
|
|
2492
|
+
event?: string;
|
|
2493
|
+
id?: string | number;
|
|
2494
|
+
retry?: number;
|
|
2495
|
+
}
|
|
2496
|
+
|
|
2497
|
+
export class ServerSentEventsStream extends EventEmitter {
|
|
2498
|
+
req: import('http').IncomingMessage;
|
|
2499
|
+
res: import('http').ServerResponse;
|
|
2500
|
+
options: SseOptions;
|
|
2501
|
+
isOpen: boolean;
|
|
2502
|
+
isClosed: boolean;
|
|
2503
|
+
|
|
2504
|
+
constructor(req: import('http').IncomingMessage, res: import('http').ServerResponse, options?: SseOptions);
|
|
2505
|
+
send(payloadOrData: any | SseEventObject, event?: string, id?: string | number): Promise<boolean>;
|
|
2506
|
+
comment(text: string): boolean;
|
|
2507
|
+
heartbeat(comment?: string): boolean;
|
|
2508
|
+
retry(ms: number): boolean;
|
|
2509
|
+
pipe(source: AsyncIterable<any> | Iterable<any> | import('stream').Readable, mapFn?: (chunk: any) => any): Promise<void>;
|
|
2510
|
+
stream(generatorFn: (stream: ServerSentEventsStream) => any): Promise<void>;
|
|
2511
|
+
close(): void;
|
|
2512
|
+
}
|
|
2513
|
+
|
|
2514
|
+
export function createSseStream(
|
|
2515
|
+
req: import('http').IncomingMessage,
|
|
2516
|
+
res: import('http').ServerResponse,
|
|
2517
|
+
sourceOrOptions?: SseOptions | AsyncIterable<any> | Iterable<any> | import('stream').Readable | ((stream: ServerSentEventsStream) => any),
|
|
2518
|
+
maybeOptions?: SseOptions
|
|
2519
|
+
): ServerSentEventsStream | Promise<void>;
|
|
2520
|
+
|
|
2521
|
+
export const sseStream: {
|
|
2522
|
+
ServerSentEventsStream: typeof ServerSentEventsStream;
|
|
2523
|
+
createSseStream: typeof createSseStream;
|
|
2524
|
+
formatSseFrame: (data?: any, event?: string, id?: string | number, retry?: number) => string;
|
|
2525
|
+
};
|
|
2526
|
+
|
|
1622
2527
|
// --- Model Context Protocol (MCP) Plugin ---
|
|
1623
2528
|
|
|
1624
2529
|
export interface McpPluginOptions {
|
|
@@ -1863,7 +2768,7 @@ export interface PageContext<TParams = Record<string, string>, TQuery = Record<s
|
|
|
1863
2768
|
res: import('express').Response;
|
|
1864
2769
|
params: TParams;
|
|
1865
2770
|
query: TQuery;
|
|
1866
|
-
service:
|
|
2771
|
+
service: WebspressoServiceCaller;
|
|
1867
2772
|
module: string | null;
|
|
1868
2773
|
config: Record<string, unknown>;
|
|
1869
2774
|
logger: Console;
|
|
@@ -1897,7 +2802,7 @@ export interface ApiContext<TBody = any, TParams = Record<string, string>, TQuer
|
|
|
1897
2802
|
query: TQuery;
|
|
1898
2803
|
body: TBody;
|
|
1899
2804
|
input: { body?: TBody; params?: TParams; query?: TQuery; headers?: Record<string, string> };
|
|
1900
|
-
service:
|
|
2805
|
+
service: WebspressoServiceCaller;
|
|
1901
2806
|
module: string | null;
|
|
1902
2807
|
config: Record<string, unknown>;
|
|
1903
2808
|
logger: Console;
|
|
@@ -1980,6 +2885,279 @@ export function compileRouteTable(
|
|
|
1980
2885
|
options?: { explicitRoutes?: Array<{ method?: string; path: string }>; isDev?: boolean }
|
|
1981
2886
|
): RouteTable;
|
|
1982
2887
|
|
|
2888
|
+
export interface GenerateTypesOptions {
|
|
2889
|
+
/** Root directory of the project (default: process.cwd()) */
|
|
2890
|
+
rootDir?: string;
|
|
2891
|
+
/** Alias for rootDir */
|
|
2892
|
+
cwd?: string;
|
|
2893
|
+
/** Models directory relative to root (default: 'models') */
|
|
2894
|
+
modelsDir?: string;
|
|
2895
|
+
/** Services directory relative to root (default: 'services') */
|
|
2896
|
+
servicesDir?: string;
|
|
2897
|
+
/** Pages directory relative to root (default: 'pages') */
|
|
2898
|
+
pagesDir?: string;
|
|
2899
|
+
/** Output directory (default: '.webspresso') */
|
|
2900
|
+
outDir?: string;
|
|
2901
|
+
/** Custom output file path (default: '.webspresso/types.d.ts') */
|
|
2902
|
+
outFile?: string;
|
|
2903
|
+
/** Create companion webspresso-env.d.ts at project root (default: true) */
|
|
2904
|
+
createEnvFile?: boolean;
|
|
2905
|
+
/** Write declaration files to disk (default: true) */
|
|
2906
|
+
write?: boolean;
|
|
2907
|
+
/** Suppress console output (default: false) */
|
|
2908
|
+
silent?: boolean;
|
|
2909
|
+
}
|
|
2910
|
+
|
|
2911
|
+
export interface GenerateTypesResult {
|
|
2912
|
+
code: string;
|
|
2913
|
+
modelsCount: number;
|
|
2914
|
+
servicesCount: number;
|
|
2915
|
+
routesCount: number;
|
|
2916
|
+
outFile: string;
|
|
2917
|
+
envFile: string | null;
|
|
2918
|
+
}
|
|
2919
|
+
|
|
2920
|
+
export function generateTypes(options?: GenerateTypesOptions): GenerateTypesResult;
|
|
2921
|
+
|
|
2922
|
+
// --- OAuth 2.0 & Social Login Plugin ---
|
|
2923
|
+
|
|
2924
|
+
export class OAuthError extends Error {
|
|
2925
|
+
code: string;
|
|
2926
|
+
details?: any;
|
|
2927
|
+
constructor(message: string, code?: string, details?: any);
|
|
2928
|
+
}
|
|
2929
|
+
|
|
2930
|
+
export interface OAuthUserProfile {
|
|
2931
|
+
provider: string;
|
|
2932
|
+
id: string;
|
|
2933
|
+
email?: string | null;
|
|
2934
|
+
emailVerified?: boolean;
|
|
2935
|
+
name?: string | null;
|
|
2936
|
+
firstName?: string | null;
|
|
2937
|
+
lastName?: string | null;
|
|
2938
|
+
username?: string | null;
|
|
2939
|
+
avatarUrl?: string | null;
|
|
2940
|
+
bio?: string | null;
|
|
2941
|
+
location?: string | null;
|
|
2942
|
+
locale?: string | null;
|
|
2943
|
+
raw: Record<string, any>;
|
|
2944
|
+
[key: string]: any;
|
|
2945
|
+
}
|
|
2946
|
+
|
|
2947
|
+
export interface OAuthProviderConfig {
|
|
2948
|
+
name?: string;
|
|
2949
|
+
clientId: string;
|
|
2950
|
+
clientSecret: string;
|
|
2951
|
+
redirectUri?: string;
|
|
2952
|
+
authorizationUrl?: string;
|
|
2953
|
+
tokenUrl?: string;
|
|
2954
|
+
userInfoUrl?: string;
|
|
2955
|
+
scopes?: string[] | string;
|
|
2956
|
+
defaultScopes?: string[];
|
|
2957
|
+
params?: Record<string, any>;
|
|
2958
|
+
normalizeProfile?: (raw: any, extra?: any) => OAuthUserProfile;
|
|
2959
|
+
fetchExtra?: (tokens: any) => Promise<any>;
|
|
2960
|
+
}
|
|
2961
|
+
|
|
2962
|
+
export interface OAuthClientConfig {
|
|
2963
|
+
secret?: string;
|
|
2964
|
+
stateTtlMs?: number;
|
|
2965
|
+
providers?: Record<string, OAuthProviderConfig>;
|
|
2966
|
+
}
|
|
2967
|
+
|
|
2968
|
+
export class OAuthClient {
|
|
2969
|
+
secret: string;
|
|
2970
|
+
stateTtlMs: number;
|
|
2971
|
+
providers: Record<string, OAuthProviderConfig>;
|
|
2972
|
+
constructor(config?: OAuthClientConfig);
|
|
2973
|
+
registerProvider(name: string, options?: OAuthProviderConfig): this;
|
|
2974
|
+
getProvider(name: string): OAuthProviderConfig;
|
|
2975
|
+
getProviderNames(): string[];
|
|
2976
|
+
getAuthorizationUrl(providerName: string, options?: {
|
|
2977
|
+
redirectUri?: string;
|
|
2978
|
+
scopes?: string[] | string;
|
|
2979
|
+
stateData?: Record<string, any>;
|
|
2980
|
+
pkce?: boolean;
|
|
2981
|
+
extraParams?: Record<string, any>;
|
|
2982
|
+
}): { url: string; state: string; codeVerifier?: string };
|
|
2983
|
+
exchangeCode(providerName: string, params: {
|
|
2984
|
+
code: string;
|
|
2985
|
+
redirectUri?: string;
|
|
2986
|
+
codeVerifier?: string;
|
|
2987
|
+
}): Promise<Record<string, any>>;
|
|
2988
|
+
fetchUserProfile(providerName: string, tokens: Record<string, any>): Promise<OAuthUserProfile>;
|
|
2989
|
+
handleCallback(providerName: string, params: {
|
|
2990
|
+
code: string;
|
|
2991
|
+
state: string;
|
|
2992
|
+
redirectUri?: string;
|
|
2993
|
+
}): Promise<{ provider: string; user: OAuthUserProfile; tokens: Record<string, any>; stateData: Record<string, any> }>;
|
|
2994
|
+
}
|
|
2995
|
+
|
|
2996
|
+
export function createOAuthClient(config?: OAuthClientConfig): OAuthClient;
|
|
2997
|
+
export function generatePKCE(): { codeVerifier: string; codeChallenge: string; codeChallengeMethod: 'S256' };
|
|
2998
|
+
export function createSignedState(payload: Record<string, any>, secret: string): string;
|
|
2999
|
+
export function verifySignedState(stateString: string, secret: string, maxAgeMs?: number): Record<string, any>;
|
|
3000
|
+
|
|
3001
|
+
export interface OAuthPluginOptions {
|
|
3002
|
+
client?: OAuthClient;
|
|
3003
|
+
providers?: Record<string, OAuthProviderConfig>;
|
|
3004
|
+
secret?: string;
|
|
3005
|
+
pathPrefix?: string;
|
|
3006
|
+
routes?: boolean;
|
|
3007
|
+
pkce?: boolean;
|
|
3008
|
+
stateTtlMs?: number;
|
|
3009
|
+
defaultReturnTo?: string;
|
|
3010
|
+
failureRedirect?: string;
|
|
3011
|
+
findOrCreateUser?: (params: {
|
|
3012
|
+
provider: string;
|
|
3013
|
+
profile: OAuthUserProfile;
|
|
3014
|
+
tokens: Record<string, any>;
|
|
3015
|
+
stateData: Record<string, any>;
|
|
3016
|
+
req: any;
|
|
3017
|
+
res: any;
|
|
3018
|
+
db?: any;
|
|
3019
|
+
}) => Promise<any>;
|
|
3020
|
+
onSuccess?: (params: {
|
|
3021
|
+
provider: string;
|
|
3022
|
+
user: any;
|
|
3023
|
+
profile: OAuthUserProfile;
|
|
3024
|
+
tokens: Record<string, any>;
|
|
3025
|
+
stateData: Record<string, any>;
|
|
3026
|
+
req: any;
|
|
3027
|
+
res: any;
|
|
3028
|
+
client: OAuthClient;
|
|
3029
|
+
}) => Promise<void | any>;
|
|
3030
|
+
onError?: (params: {
|
|
3031
|
+
error: Error | OAuthError;
|
|
3032
|
+
provider: string;
|
|
3033
|
+
req: any;
|
|
3034
|
+
res: any;
|
|
3035
|
+
client: OAuthClient;
|
|
3036
|
+
}) => Promise<void | any>;
|
|
3037
|
+
db?: any;
|
|
3038
|
+
}
|
|
3039
|
+
|
|
3040
|
+
export function oauthPlugin(options?: OAuthPluginOptions): WebspressoPlugin;
|
|
3041
|
+
|
|
3042
|
+
// ==========================================
|
|
3043
|
+
// 2FA / TOTP (RFC 6238, RFC 4226, RFC 4648)
|
|
3044
|
+
// ==========================================
|
|
3045
|
+
|
|
3046
|
+
export interface Base32EncodeOptions {
|
|
3047
|
+
padding?: boolean;
|
|
3048
|
+
}
|
|
3049
|
+
|
|
3050
|
+
export interface TotpSecretOptions extends Base32EncodeOptions {
|
|
3051
|
+
length?: number;
|
|
3052
|
+
}
|
|
3053
|
+
|
|
3054
|
+
export interface HotpOptions {
|
|
3055
|
+
digits?: number;
|
|
3056
|
+
algorithm?: 'sha1' | 'sha256' | 'sha512' | string;
|
|
3057
|
+
encoding?: 'base32' | 'ascii' | 'utf8' | 'hex';
|
|
3058
|
+
}
|
|
3059
|
+
|
|
3060
|
+
export interface TotpOptions extends HotpOptions {
|
|
3061
|
+
time?: number | Date;
|
|
3062
|
+
step?: number;
|
|
3063
|
+
epoch?: number;
|
|
3064
|
+
}
|
|
3065
|
+
|
|
3066
|
+
export interface TotpVerifyOptions extends TotpOptions {
|
|
3067
|
+
window?: number;
|
|
3068
|
+
detailed?: boolean;
|
|
3069
|
+
}
|
|
3070
|
+
|
|
3071
|
+
export interface TotpUriOptions {
|
|
3072
|
+
secret: string;
|
|
3073
|
+
account?: string;
|
|
3074
|
+
issuer?: string;
|
|
3075
|
+
label?: string;
|
|
3076
|
+
algorithm?: string;
|
|
3077
|
+
digits?: number;
|
|
3078
|
+
period?: number;
|
|
3079
|
+
}
|
|
3080
|
+
|
|
3081
|
+
export interface QrSvgOptions {
|
|
3082
|
+
size?: number;
|
|
3083
|
+
margin?: number;
|
|
3084
|
+
darkColor?: string;
|
|
3085
|
+
lightColor?: string;
|
|
3086
|
+
version?: number;
|
|
3087
|
+
ecc?: 'L' | 'M' | 'Q' | 'H';
|
|
3088
|
+
mask?: number;
|
|
3089
|
+
}
|
|
3090
|
+
|
|
3091
|
+
export interface RecoveryCodeOptions {
|
|
3092
|
+
count?: number;
|
|
3093
|
+
length?: number;
|
|
3094
|
+
hyphen?: boolean;
|
|
3095
|
+
}
|
|
3096
|
+
|
|
3097
|
+
export interface TotpSetupOptions extends TotpSecretOptions {
|
|
3098
|
+
user?: any;
|
|
3099
|
+
account?: string;
|
|
3100
|
+
issuer?: string;
|
|
3101
|
+
recoveryCodeCount?: number;
|
|
3102
|
+
qrOptions?: QrSvgOptions;
|
|
3103
|
+
algorithm?: string;
|
|
3104
|
+
digits?: number;
|
|
3105
|
+
period?: number;
|
|
3106
|
+
}
|
|
3107
|
+
|
|
3108
|
+
export interface TotpSetupResult {
|
|
3109
|
+
secret: string;
|
|
3110
|
+
uri: string;
|
|
3111
|
+
qrSvg: string;
|
|
3112
|
+
qrDataUri: string;
|
|
3113
|
+
recoveryCodes: string[];
|
|
3114
|
+
recoveryCodesHashed: string[];
|
|
3115
|
+
}
|
|
3116
|
+
|
|
3117
|
+
export function base32Encode(buffer: Buffer | string, options?: Base32EncodeOptions): string;
|
|
3118
|
+
export function base32Decode(input: string): Buffer;
|
|
3119
|
+
export function generateTotpSecret(options?: TotpSecretOptions): string;
|
|
3120
|
+
export function generateHotp(secret: string | Buffer, counter: number | bigint, options?: HotpOptions): string;
|
|
3121
|
+
export function verifyHotp(token: string, secret: string | Buffer, counter: number | bigint, options?: HotpOptions): boolean;
|
|
3122
|
+
export function generateTotp(secret: string | Buffer, options?: TotpOptions): string;
|
|
3123
|
+
export function verifyTotp(token: string, secret: string | Buffer, options?: TotpVerifyOptions): boolean | { valid: boolean; delta: number | null };
|
|
3124
|
+
export function generateTotpUri(options: TotpUriOptions): string;
|
|
3125
|
+
export function generateTotpQrSvg(uriOrOptions: string | TotpUriOptions, svgOptions?: QrSvgOptions): string;
|
|
3126
|
+
export function generateTotpQrDataUri(uriOrOptions: string | TotpUriOptions, svgOptions?: QrSvgOptions): string;
|
|
3127
|
+
export function generateRecoveryCodes(options?: RecoveryCodeOptions): string[];
|
|
3128
|
+
export function hashRecoveryCode(code: string): string;
|
|
3129
|
+
export function verifyRecoveryCode(code: string, hashedCodes: string[]): { valid: boolean; matchedIndex: number; remainingHashedCodes: string[] };
|
|
3130
|
+
export function createTotpSetup(options?: TotpSetupOptions): TotpSetupResult;
|
|
3131
|
+
|
|
3132
|
+
export function createQrMatrix(text: string, options?: QrSvgOptions): { matrix: boolean[][]; size: number; version: number };
|
|
3133
|
+
export function generateQrSvg(text: string, options?: QrSvgOptions): string;
|
|
3134
|
+
export function generateQrDataUri(text: string, options?: QrSvgOptions): string;
|
|
3135
|
+
|
|
3136
|
+
export namespace totp {
|
|
3137
|
+
export {
|
|
3138
|
+
base32Encode,
|
|
3139
|
+
base32Decode,
|
|
3140
|
+
generateTotpSecret,
|
|
3141
|
+
generateHotp,
|
|
3142
|
+
verifyHotp,
|
|
3143
|
+
generateTotp,
|
|
3144
|
+
verifyTotp,
|
|
3145
|
+
generateTotpUri,
|
|
3146
|
+
generateTotpQrSvg,
|
|
3147
|
+
generateTotpQrDataUri,
|
|
3148
|
+
generateRecoveryCodes,
|
|
3149
|
+
hashRecoveryCode,
|
|
3150
|
+
verifyRecoveryCode,
|
|
3151
|
+
createTotpSetup,
|
|
3152
|
+
createQrMatrix,
|
|
3153
|
+
generateQrSvg,
|
|
3154
|
+
generateQrDataUri,
|
|
3155
|
+
};
|
|
3156
|
+
}
|
|
3157
|
+
|
|
3158
|
+
|
|
3159
|
+
|
|
3160
|
+
|
|
1983
3161
|
|
|
1984
3162
|
|
|
1985
3163
|
|