webspresso 0.0.95 → 0.0.97
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 +225 -0
- package/.agents/AGENTS.md +72 -0
- package/.agents/ARCHITECTURE.md +119 -0
- package/.agents/AUTH.md +103 -0
- package/.agents/CLI.md +115 -0
- package/.agents/COMPRESSION.md +59 -0
- package/.agents/CONVENTIONS.md +46 -0
- package/.agents/EMAIL.md +141 -0
- package/.agents/ERRORS.md +201 -0
- package/.agents/MCP.md +103 -0
- package/.agents/ORM.md +230 -0
- package/.agents/PLUGINS.md +419 -0
- package/.agents/QUEUE.md +113 -0
- package/.agents/REALTIME.md +213 -0
- package/.agents/ROUTING.md +292 -0
- package/.agents/SERVICES.md +341 -0
- package/.agents/SHUTDOWN.md +67 -0
- package/.agents/STREAMING.md +73 -0
- package/.agents/WORKFLOW.md +27 -0
- package/.agents/internal/DOCUMENTATION_REVIEW.md +485 -0
- package/.agents/internal/TECHNICAL_REVIEW.md +428 -0
- package/.agents/skills/webspresso-admin-and-plugins/SKILL.md +122 -0
- package/.agents/skills/webspresso-framework/SKILL.md +56 -0
- package/.agents/skills/webspresso-orm-and-database/SKILL.md +119 -0
- package/.agents/skills/webspresso-routing-and-api/SKILL.md +114 -0
- package/.agents/skills/webspresso-services/SKILL.md +101 -0
- package/.cursorrules +14 -0
- package/AGENTS.md +175 -0
- package/CLAUDE.md +54 -0
- package/README.md +18 -14
- package/bin/commands/admin-password.js +2 -1
- package/bin/commands/admin-setup.js +1 -1
- package/bin/commands/agents-init.js +209 -0
- package/bin/commands/api.js +114 -48
- package/bin/commands/audit-prune.js +1 -1
- package/bin/commands/doctor.js +35 -0
- package/bin/commands/favicon-generate.js +1 -1
- package/bin/commands/new.js +13 -2
- package/bin/commands/orm-map.js +5 -4
- package/bin/commands/page.js +75 -89
- package/bin/commands/polar-migrate.js +1 -1
- package/bin/commands/seed.js +1 -1
- package/bin/commands/service.js +77 -0
- package/bin/webspresso.js +2 -2
- package/core/auth/hash.js +10 -5
- package/core/compileSchema.js +1 -1
- package/core/errors/middleware.js +22 -2
- package/core/errors/normalize.js +29 -2
- package/{utils/schemaCache.js → core/schema-cache.js} +2 -3
- package/index.d.ts +306 -0
- package/index.js +35 -1
- package/package.json +36 -14
- package/plugins/admin-panel/app.js +10 -9
- package/plugins/admin-panel/client/parts/05-rich-text-file-helpers.js +22 -1
- package/plugins/admin-panel/core/api-extensions.js +4 -0
- package/plugins/basic-auth/index.js +3 -1
- package/plugins/content/client/inline-edit.js +12 -12
- package/plugins/cors.js +6 -7
- package/plugins/csrf/index.js +3 -2
- package/plugins/csv/engine.js +447 -0
- package/plugins/csv/index.js +94 -0
- package/plugins/csv/middleware.js +80 -0
- package/plugins/csv/services.js +138 -0
- package/plugins/file-manager/admin-component.js +613 -0
- package/plugins/file-manager/api-handlers.js +261 -0
- package/plugins/file-manager/core.js +601 -0
- package/plugins/file-manager/index.js +139 -0
- package/plugins/file-manager/security.js +217 -0
- package/plugins/index.js +45 -79
- package/plugins/mcp/transports/sse.js +4 -2
- package/plugins/site-analytics/admin-component.js +7 -8
- package/plugins/xlsx/engine.js +397 -0
- package/plugins/xlsx/index.js +91 -0
- package/plugins/xlsx/middleware.js +87 -0
- package/plugins/xlsx/services.js +118 -0
- package/src/api/api-loader.js +233 -0
- package/src/api/define-api.js +35 -0
- package/src/discovery/file-route-parser.js +201 -0
- package/src/discovery/scan-routes.js +265 -0
- package/src/file-router.js +9 -1
- package/src/modules/define-module.js +99 -0
- package/src/modules/module-discovery.js +117 -0
- package/src/pages/define-page.js +36 -0
- package/src/pages/page-loader.js +214 -0
- package/src/routing/mount-discovered-routes.js +78 -0
- package/src/routing/route-table.js +191 -0
- package/src/server.js +112 -15
- package/src/services/builtins/file-manager.js +178 -0
- package/src/services/discovery.js +6 -1
- package/src/services/index.js +6 -11
- package/bin/commands/skill.js +0 -187
- package/templates/skills/webspresso-usage/REFERENCE-framework.md +0 -289
- package/templates/skills/webspresso-usage/REFERENCE-kernel.md +0 -93
- package/templates/skills/webspresso-usage/SKILL.md +0 -48
package/index.d.ts
CHANGED
|
@@ -222,6 +222,11 @@ export function createMediaServices(options?: {
|
|
|
222
222
|
publicBasePath?: string;
|
|
223
223
|
}): Record<string, ServiceDefinition>;
|
|
224
224
|
|
|
225
|
+
export function createFileManagerServices(options?: {
|
|
226
|
+
baseDir?: string;
|
|
227
|
+
publicBasePath?: string;
|
|
228
|
+
}): Record<string, ServiceDefinition>;
|
|
229
|
+
|
|
225
230
|
export function createSystemServices(options?: {
|
|
226
231
|
db?: unknown;
|
|
227
232
|
serviceRegistry?: unknown;
|
|
@@ -706,6 +711,7 @@ export type Zdb = Record<string, any> & {
|
|
|
706
711
|
};
|
|
707
712
|
|
|
708
713
|
export const zdb: Zdb;
|
|
714
|
+
export const z: typeof import('zod').z;
|
|
709
715
|
|
|
710
716
|
export function createSchemaHelpers(z: typeof import('zod').z): Zdb;
|
|
711
717
|
|
|
@@ -1677,4 +1683,304 @@ export class McpServer {
|
|
|
1677
1683
|
export function mcpPlugin(options?: McpPluginOptions): WebspressoPlugin;
|
|
1678
1684
|
export function createMcpServer(options?: McpPluginOptions): McpServer;
|
|
1679
1685
|
|
|
1686
|
+
// --- File Manager Plugin ---
|
|
1687
|
+
|
|
1688
|
+
export interface FileManagerPluginOptions {
|
|
1689
|
+
baseDir?: string;
|
|
1690
|
+
publicBasePath?: string;
|
|
1691
|
+
apiPrefix?: string;
|
|
1692
|
+
pageTitle?: string;
|
|
1693
|
+
menuLabel?: string;
|
|
1694
|
+
menuOrder?: number;
|
|
1695
|
+
maxUploadBytes?: number;
|
|
1696
|
+
allowedExtensions?: string[] | null;
|
|
1697
|
+
allowedMimeTypes?: string[] | null;
|
|
1698
|
+
includePage?: boolean;
|
|
1699
|
+
}
|
|
1700
|
+
|
|
1701
|
+
export function fileManagerPlugin(options?: FileManagerPluginOptions): WebspressoPlugin;
|
|
1702
|
+
|
|
1703
|
+
// --- XLSX Plugin & Toolkit ---
|
|
1704
|
+
|
|
1705
|
+
export interface XlsxColumn {
|
|
1706
|
+
header?: string;
|
|
1707
|
+
key: string;
|
|
1708
|
+
width?: number;
|
|
1709
|
+
style?: Record<string, unknown>;
|
|
1710
|
+
}
|
|
1711
|
+
|
|
1712
|
+
export interface XlsxHeaderStyle {
|
|
1713
|
+
fill?: string;
|
|
1714
|
+
bg?: string;
|
|
1715
|
+
color?: string;
|
|
1716
|
+
textColor?: string;
|
|
1717
|
+
fontName?: string;
|
|
1718
|
+
fontSize?: number;
|
|
1719
|
+
bold?: boolean;
|
|
1720
|
+
align?: 'left' | 'center' | 'right';
|
|
1721
|
+
height?: number;
|
|
1722
|
+
}
|
|
1723
|
+
|
|
1724
|
+
export interface XlsxSheetConfig {
|
|
1725
|
+
name?: string;
|
|
1726
|
+
columns?: XlsxColumn[];
|
|
1727
|
+
rows?: Record<string, unknown>[];
|
|
1728
|
+
data?: Record<string, unknown>[];
|
|
1729
|
+
autoWidth?: boolean;
|
|
1730
|
+
striped?: boolean;
|
|
1731
|
+
headerStyle?: XlsxHeaderStyle;
|
|
1732
|
+
views?: Array<Record<string, unknown>>;
|
|
1733
|
+
}
|
|
1734
|
+
|
|
1735
|
+
export interface XlsxGenerateOptions {
|
|
1736
|
+
creator?: string;
|
|
1737
|
+
title?: string;
|
|
1738
|
+
created?: Date;
|
|
1739
|
+
sanitizeFormulas?: boolean;
|
|
1740
|
+
headerStyle?: XlsxHeaderStyle;
|
|
1741
|
+
sheets?: XlsxSheetConfig[] | XlsxSheetConfig;
|
|
1742
|
+
name?: string;
|
|
1743
|
+
columns?: XlsxColumn[];
|
|
1744
|
+
rows?: Record<string, unknown>[];
|
|
1745
|
+
data?: Record<string, unknown>[];
|
|
1746
|
+
}
|
|
1747
|
+
|
|
1748
|
+
export interface XlsxParseOptions {
|
|
1749
|
+
sheet?: string | number;
|
|
1750
|
+
headerRow?: number;
|
|
1751
|
+
dataStartRow?: number;
|
|
1752
|
+
raw?: boolean;
|
|
1753
|
+
transformRow?: (row: Record<string, unknown>, index: number) => Record<string, unknown>;
|
|
1754
|
+
}
|
|
1755
|
+
|
|
1756
|
+
export interface XlsxModelExportOptions {
|
|
1757
|
+
sheetName?: string;
|
|
1758
|
+
name?: string;
|
|
1759
|
+
creator?: string;
|
|
1760
|
+
title?: string;
|
|
1761
|
+
columns?: XlsxColumn[];
|
|
1762
|
+
headerStyle?: XlsxHeaderStyle;
|
|
1763
|
+
}
|
|
1764
|
+
|
|
1765
|
+
export interface XlsxPluginOptions {
|
|
1766
|
+
enabled?: boolean;
|
|
1767
|
+
sanitizeFormulas?: boolean;
|
|
1768
|
+
headerStyle?: XlsxHeaderStyle;
|
|
1769
|
+
db?: unknown;
|
|
1770
|
+
}
|
|
1771
|
+
|
|
1772
|
+
export interface XlsxToolkit {
|
|
1773
|
+
MIME_TYPE: string;
|
|
1774
|
+
generate(options: XlsxGenerateOptions): Promise<Buffer>;
|
|
1775
|
+
parse(input: Buffer | import('stream').Readable, options?: XlsxParseOptions): Promise<Record<string, unknown>[] | Record<string, Record<string, unknown>[]>>;
|
|
1776
|
+
fromModel(repository: unknown, filterOrQuery?: unknown, options?: XlsxModelExportOptions): Promise<Buffer>;
|
|
1777
|
+
createStreamWriter(outputStream: import('stream').Writable, options?: Record<string, unknown>): unknown;
|
|
1778
|
+
sanitizeFormula(value: unknown): unknown;
|
|
1779
|
+
applyHeaderStyle(worksheet: unknown, style?: XlsxHeaderStyle): void;
|
|
1780
|
+
applyAutoWidths(worksheet: unknown, columns: XlsxColumn[], rows: Record<string, unknown>[]): void;
|
|
1781
|
+
hexToArgb(hex: string): string;
|
|
1782
|
+
createMiddleware(globalOptions?: XlsxPluginOptions): import('express').RequestHandler;
|
|
1783
|
+
createServices(options?: Record<string, unknown>): Record<string, unknown>;
|
|
1784
|
+
}
|
|
1785
|
+
|
|
1786
|
+
export const xlsx: XlsxToolkit;
|
|
1787
|
+
export function xlsxPlugin(options?: XlsxPluginOptions): WebspressoPlugin;
|
|
1788
|
+
|
|
1789
|
+
// --- CSV Plugin & Toolkit ---
|
|
1790
|
+
|
|
1791
|
+
export interface CsvColumn {
|
|
1792
|
+
header?: string;
|
|
1793
|
+
key: string;
|
|
1794
|
+
}
|
|
1795
|
+
|
|
1796
|
+
export interface CsvGenerateOptions {
|
|
1797
|
+
rows?: Record<string, unknown>[];
|
|
1798
|
+
data?: Record<string, unknown>[];
|
|
1799
|
+
columns?: (CsvColumn | string)[];
|
|
1800
|
+
delimiter?: string;
|
|
1801
|
+
eol?: string;
|
|
1802
|
+
header?: boolean;
|
|
1803
|
+
bom?: boolean;
|
|
1804
|
+
sanitizeFormulas?: boolean;
|
|
1805
|
+
asBuffer?: boolean;
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1808
|
+
export interface CsvParseOptions {
|
|
1809
|
+
delimiter?: string;
|
|
1810
|
+
columns?: boolean | string[];
|
|
1811
|
+
skip_empty_lines?: boolean;
|
|
1812
|
+
trim?: boolean;
|
|
1813
|
+
relax_column_count?: boolean;
|
|
1814
|
+
transformRow?: (row: Record<string, unknown>, index: number) => Record<string, unknown>;
|
|
1815
|
+
[key: string]: unknown;
|
|
1816
|
+
}
|
|
1817
|
+
|
|
1818
|
+
export interface CsvModelExportOptions {
|
|
1819
|
+
columns?: (CsvColumn | string)[];
|
|
1820
|
+
delimiter?: string;
|
|
1821
|
+
eol?: string;
|
|
1822
|
+
bom?: boolean;
|
|
1823
|
+
sanitizeFormulas?: boolean;
|
|
1824
|
+
asBuffer?: boolean;
|
|
1825
|
+
}
|
|
1826
|
+
|
|
1827
|
+
export interface CsvPluginOptions {
|
|
1828
|
+
enabled?: boolean;
|
|
1829
|
+
delimiter?: string;
|
|
1830
|
+
bom?: boolean;
|
|
1831
|
+
sanitizeFormulas?: boolean;
|
|
1832
|
+
db?: unknown;
|
|
1833
|
+
}
|
|
1834
|
+
|
|
1835
|
+
export interface CsvStreamWriter {
|
|
1836
|
+
writeHeader(columns?: (CsvColumn | string)[]): void;
|
|
1837
|
+
writeRow(row: Record<string, unknown>): void;
|
|
1838
|
+
writeRows(rows: Record<string, unknown>[]): void;
|
|
1839
|
+
end(): void;
|
|
1840
|
+
stream: import('stream').Writable;
|
|
1841
|
+
}
|
|
1842
|
+
|
|
1843
|
+
export interface CsvToolkit {
|
|
1844
|
+
MIME_TYPE: string;
|
|
1845
|
+
UTF8_BOM: string;
|
|
1846
|
+
generate(options?: CsvGenerateOptions): Buffer | string;
|
|
1847
|
+
parse(input: Buffer | string | import('stream').Readable, options?: CsvParseOptions): Promise<Record<string, unknown>[]>;
|
|
1848
|
+
fromModel(repository: unknown, filterOrQuery?: unknown, options?: CsvModelExportOptions): Promise<Buffer>;
|
|
1849
|
+
createStreamWriter(outputStream: import('stream').Writable, options?: Record<string, unknown>): CsvStreamWriter;
|
|
1850
|
+
sanitizeFormula(value: unknown): unknown;
|
|
1851
|
+
escapeCsvField(value: unknown, options?: Record<string, unknown>): string;
|
|
1852
|
+
createMiddleware(globalOptions?: CsvPluginOptions): import('express').RequestHandler;
|
|
1853
|
+
createServices(options?: Record<string, unknown>): Record<string, unknown>;
|
|
1854
|
+
}
|
|
1855
|
+
|
|
1856
|
+
export const csv: CsvToolkit;
|
|
1857
|
+
export function csvPlugin(options?: CsvPluginOptions): WebspressoPlugin;
|
|
1858
|
+
|
|
1859
|
+
// --- Discovery, Pages, API & Modules ---
|
|
1860
|
+
|
|
1861
|
+
export interface PageContext<TParams = Record<string, string>, TQuery = Record<string, string>> {
|
|
1862
|
+
req: import('express').Request;
|
|
1863
|
+
res: import('express').Response;
|
|
1864
|
+
params: TParams;
|
|
1865
|
+
query: TQuery;
|
|
1866
|
+
service: <TInput = unknown, TOutput = unknown>(name: string, input?: TInput, opts?: unknown) => Promise<TOutput>;
|
|
1867
|
+
module: string | null;
|
|
1868
|
+
config: Record<string, unknown>;
|
|
1869
|
+
logger: Console;
|
|
1870
|
+
redirect: (url: string, status?: number) => { __redirected: boolean };
|
|
1871
|
+
error: (status?: number, message?: string) => never;
|
|
1872
|
+
fsy: Record<string, unknown>;
|
|
1873
|
+
locale: string;
|
|
1874
|
+
t: (key: string, params?: Record<string, unknown>) => string;
|
|
1875
|
+
db: unknown;
|
|
1876
|
+
}
|
|
1877
|
+
|
|
1878
|
+
export interface DefinePageOptions<TData = any, TParams = Record<string, string>, TQuery = Record<string, string>> {
|
|
1879
|
+
middleware?: (string | import('express').RequestHandler | [string, Record<string, unknown>])[];
|
|
1880
|
+
load?: (ctx: PageContext<TParams, TQuery>) => Promise<TData> | TData;
|
|
1881
|
+
head?: (data: TData, ctx: PageContext<TParams, TQuery>) => Record<string, unknown>;
|
|
1882
|
+
render?: (data: TData, ctx: PageContext<TParams, TQuery>) => string | Promise<string>;
|
|
1883
|
+
layout?: string | boolean;
|
|
1884
|
+
hooks?: Record<string, unknown>;
|
|
1885
|
+
meta?: Record<string, unknown>;
|
|
1886
|
+
[key: string]: unknown;
|
|
1887
|
+
}
|
|
1888
|
+
|
|
1889
|
+
export function definePage<TData = any, TParams = Record<string, string>, TQuery = Record<string, string>>(
|
|
1890
|
+
config: DefinePageOptions<TData, TParams, TQuery> | ((ctx: PageContext<TParams, TQuery>) => Promise<TData> | TData)
|
|
1891
|
+
): DefinePageOptions<TData, TParams, TQuery>;
|
|
1892
|
+
|
|
1893
|
+
export interface ApiContext<TBody = any, TParams = Record<string, string>, TQuery = Record<string, string>> {
|
|
1894
|
+
req: import('express').Request;
|
|
1895
|
+
res: import('express').Response;
|
|
1896
|
+
params: TParams;
|
|
1897
|
+
query: TQuery;
|
|
1898
|
+
body: TBody;
|
|
1899
|
+
input: { body?: TBody; params?: TParams; query?: TQuery; headers?: Record<string, string> };
|
|
1900
|
+
service: <TInput = unknown, TOutput = unknown>(name: string, input?: TInput, opts?: unknown) => Promise<TOutput>;
|
|
1901
|
+
module: string | null;
|
|
1902
|
+
config: Record<string, unknown>;
|
|
1903
|
+
logger: Console;
|
|
1904
|
+
db: unknown;
|
|
1905
|
+
}
|
|
1906
|
+
|
|
1907
|
+
export interface DefineApiOptions<TResult = any, TBody = any, TParams = Record<string, string>, TQuery = Record<string, string>> {
|
|
1908
|
+
description?: string;
|
|
1909
|
+
tags?: string[];
|
|
1910
|
+
middleware?: (string | import('express').RequestHandler | [string, Record<string, unknown>])[];
|
|
1911
|
+
schema?: {
|
|
1912
|
+
params?: unknown;
|
|
1913
|
+
query?: unknown;
|
|
1914
|
+
body?: unknown;
|
|
1915
|
+
headers?: unknown;
|
|
1916
|
+
} | ((helpers: { z: typeof import('zod').z }) => Record<string, unknown>);
|
|
1917
|
+
response?: Record<number | string, unknown>;
|
|
1918
|
+
handler: (req: import('express').Request, ctx: ApiContext<TBody, TParams, TQuery>) => Promise<TResult> | TResult;
|
|
1919
|
+
[key: string]: unknown;
|
|
1920
|
+
}
|
|
1921
|
+
|
|
1922
|
+
export function defineApi<TResult = any, TBody = any, TParams = Record<string, string>, TQuery = Record<string, string>>(
|
|
1923
|
+
config: DefineApiOptions<TResult, TBody, TParams, TQuery> | ((req: import('express').Request, ctx: ApiContext<TBody, TParams, TQuery>) => Promise<TResult> | TResult)
|
|
1924
|
+
): DefineApiOptions<TResult, TBody, TParams, TQuery>;
|
|
1925
|
+
|
|
1926
|
+
export interface DefineModuleOptions {
|
|
1927
|
+
name: string;
|
|
1928
|
+
version?: string;
|
|
1929
|
+
description?: string;
|
|
1930
|
+
imports?: string[];
|
|
1931
|
+
dependencies?: string[];
|
|
1932
|
+
pages?: boolean | { dir?: string; prefix?: string };
|
|
1933
|
+
api?: boolean | { dir?: string; prefix?: string };
|
|
1934
|
+
services?: Record<string, unknown> | boolean | { dir?: string };
|
|
1935
|
+
middlewares?: Record<string, import('express').RequestHandler> | boolean | { dir?: string };
|
|
1936
|
+
exports?: Record<string, unknown>;
|
|
1937
|
+
apiExports?: Record<string, unknown>;
|
|
1938
|
+
onInit?: (ctx: unknown) => void | Promise<void>;
|
|
1939
|
+
onDestroy?: (ctx: unknown) => void | Promise<void>;
|
|
1940
|
+
[key: string]: unknown;
|
|
1941
|
+
}
|
|
1942
|
+
|
|
1943
|
+
export function defineModule(config: DefineModuleOptions): (userOptions?: Record<string, unknown>) => WebspressoPlugin;
|
|
1944
|
+
|
|
1945
|
+
export interface RouteDescriptor {
|
|
1946
|
+
type: 'page' | 'api';
|
|
1947
|
+
method: string;
|
|
1948
|
+
path: string;
|
|
1949
|
+
file: string;
|
|
1950
|
+
source?: string;
|
|
1951
|
+
module?: string | null;
|
|
1952
|
+
description?: string;
|
|
1953
|
+
tags?: string[];
|
|
1954
|
+
moduleConfig?: unknown;
|
|
1955
|
+
}
|
|
1956
|
+
|
|
1957
|
+
export class RouteTable {
|
|
1958
|
+
routes: ReadonlyArray<RouteDescriptor>;
|
|
1959
|
+
constructor(routes?: RouteDescriptor[]);
|
|
1960
|
+
list(): Array<{ method: string; path: string; type: string; module: string | null; source: string; description?: string; tags?: string[] }>;
|
|
1961
|
+
get(method: string, path: string): RouteDescriptor | undefined;
|
|
1962
|
+
has(method: string, path: string): boolean;
|
|
1963
|
+
get size(): number;
|
|
1964
|
+
}
|
|
1965
|
+
|
|
1966
|
+
export function scanRoutes(options?: {
|
|
1967
|
+
rootDir?: string;
|
|
1968
|
+
pages?: boolean | { dir?: string; prefix?: string };
|
|
1969
|
+
api?: boolean | { dir?: string; prefix?: string };
|
|
1970
|
+
modules?: boolean | { dir?: string };
|
|
1971
|
+
}): RouteDescriptor[];
|
|
1972
|
+
|
|
1973
|
+
export function parseFileRoute(
|
|
1974
|
+
relativePath: string,
|
|
1975
|
+
options?: { type?: 'page' | 'api'; prefix?: string }
|
|
1976
|
+
): { path: string; method: string; isValid: boolean; isPrivate: boolean; originalMethodPart?: string };
|
|
1977
|
+
|
|
1978
|
+
export function compileRouteTable(
|
|
1979
|
+
descriptors?: RouteDescriptor[],
|
|
1980
|
+
options?: { explicitRoutes?: Array<{ method?: string; path: string }>; isDev?: boolean }
|
|
1981
|
+
): RouteTable;
|
|
1982
|
+
|
|
1983
|
+
|
|
1984
|
+
|
|
1985
|
+
|
|
1680
1986
|
|
package/index.js
CHANGED
|
@@ -52,6 +52,15 @@ const {
|
|
|
52
52
|
resetPluginManager
|
|
53
53
|
} = require('./src/plugin-manager');
|
|
54
54
|
|
|
55
|
+
// Discovery, Pages, API, Modules & Routing primitives
|
|
56
|
+
const { definePage } = require('./src/pages/define-page');
|
|
57
|
+
const { defineApi } = require('./src/api/define-api');
|
|
58
|
+
const { defineModule } = require('./src/modules/define-module');
|
|
59
|
+
const { scanRoutes } = require('./src/discovery/scan-routes');
|
|
60
|
+
const { parseFileRoute } = require('./src/discovery/file-route-parser');
|
|
61
|
+
const { compileRouteTable, RouteTable } = require('./src/routing/route-table');
|
|
62
|
+
const { mountDiscoveredRoutes } = require('./src/routing/mount-discovered-routes');
|
|
63
|
+
|
|
55
64
|
// ORM exports (lazy loaded)
|
|
56
65
|
const orm = require('./core/orm');
|
|
57
66
|
|
|
@@ -90,6 +99,11 @@ const {
|
|
|
90
99
|
mcpPlugin,
|
|
91
100
|
createMcpServer,
|
|
92
101
|
McpServer,
|
|
102
|
+
fileManagerPlugin,
|
|
103
|
+
xlsxPlugin,
|
|
104
|
+
xlsx,
|
|
105
|
+
csvPlugin,
|
|
106
|
+
csv,
|
|
93
107
|
} = require('./plugins');
|
|
94
108
|
|
|
95
109
|
const content = require('./core/content');
|
|
@@ -127,6 +141,7 @@ module.exports = {
|
|
|
127
141
|
createAuthServices: require('./src/services').createAuthServices,
|
|
128
142
|
createMailServices: require('./src/services').createMailServices,
|
|
129
143
|
createMediaServices: require('./src/services').createMediaServices,
|
|
144
|
+
createFileManagerServices: require('./src/services').createFileManagerServices,
|
|
130
145
|
createSystemServices: require('./src/services').createSystemServices,
|
|
131
146
|
createExchangeServices: require('./src/services').createExchangeServices,
|
|
132
147
|
validateServiceInput: require('./src/services').validateServiceInput,
|
|
@@ -205,8 +220,9 @@ module.exports = {
|
|
|
205
220
|
/** Event bus / plugin shell / view resolver / flows (`kernel.createApp` is distinct from SSR `createApp`) */
|
|
206
221
|
kernel,
|
|
207
222
|
|
|
208
|
-
// Direct zdb export (for convenience)
|
|
223
|
+
// Direct zdb & z export (for convenience)
|
|
209
224
|
zdb: orm.zdb,
|
|
225
|
+
z: require('./core/validation').z,
|
|
210
226
|
|
|
211
227
|
// Plugins
|
|
212
228
|
schemaExplorerPlugin,
|
|
@@ -243,6 +259,24 @@ module.exports = {
|
|
|
243
259
|
mcpPlugin,
|
|
244
260
|
createMcpServer,
|
|
245
261
|
McpServer,
|
|
262
|
+
fileManagerPlugin,
|
|
263
|
+
xlsxPlugin,
|
|
264
|
+
xlsx,
|
|
265
|
+
csvPlugin,
|
|
266
|
+
csv,
|
|
267
|
+
|
|
268
|
+
// Discovery, Pages, API, Modules & Routing primitives
|
|
269
|
+
definePage,
|
|
270
|
+
page: definePage,
|
|
271
|
+
defineApi,
|
|
272
|
+
api: defineApi,
|
|
273
|
+
defineModule,
|
|
274
|
+
module: defineModule,
|
|
275
|
+
scanRoutes,
|
|
276
|
+
parseFileRoute,
|
|
277
|
+
compileRouteTable,
|
|
278
|
+
RouteTable,
|
|
279
|
+
mountDiscoveredRoutes,
|
|
246
280
|
|
|
247
281
|
// Schema-driven CMS core (framework-agnostic)
|
|
248
282
|
content,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "webspresso",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.97",
|
|
4
4
|
"description": "Minimal, production-ready SSR framework for Node.js with file-based routing, Nunjucks templating, built-in i18n, and CLI tooling",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -38,10 +38,27 @@
|
|
|
38
38
|
},
|
|
39
39
|
"scripts": {
|
|
40
40
|
"test": "vitest run",
|
|
41
|
+
"test:unit": "vitest run tests/unit/",
|
|
42
|
+
"test:integration": "vitest run tests/integration/",
|
|
41
43
|
"test:security": "vitest run tests/security/",
|
|
42
|
-
"
|
|
44
|
+
"test:orm": "vitest run tests/**/orm* tests/**/database*",
|
|
45
|
+
"test:auth": "vitest run tests/**/auth* tests/unit/server-auth-branches.test.js",
|
|
46
|
+
"test:admin": "vitest run tests/**/admin*",
|
|
47
|
+
"test:services": "vitest run tests/**/service*",
|
|
48
|
+
"test:plugins": "vitest run tests/**/plugin* tests/unit/*plugin*.test.js",
|
|
49
|
+
"test:core": "vitest run tests/unit/core* tests/unit/server* tests/unit/routing* tests/unit/template*",
|
|
50
|
+
"test:branches": "vitest run tests/unit/*branches.test.js",
|
|
51
|
+
"test:fast": "vitest run tests/unit/*branches.test.js tests/unit/auth.test.js tests/unit/services.test.js tests/unit/server.test.js",
|
|
43
52
|
"test:watch": "vitest",
|
|
44
53
|
"test:coverage": "vitest run --coverage",
|
|
54
|
+
"test:coverage:branches": "vitest run tests/unit/*branches.test.js --coverage",
|
|
55
|
+
"test:coverage:unit": "vitest run tests/unit/ --coverage",
|
|
56
|
+
"test:coverage:core": "vitest run tests/unit/core* tests/unit/server* tests/unit/routing* tests/unit/template* --coverage",
|
|
57
|
+
"test:coverage:auth": "vitest run tests/**/auth* tests/unit/server-auth-branches.test.js --coverage",
|
|
58
|
+
"test:coverage:orm": "vitest run tests/**/orm* tests/**/database* --coverage",
|
|
59
|
+
"test:coverage:services": "vitest run tests/**/service* --coverage",
|
|
60
|
+
"test:coverage:plugins": "vitest run tests/**/plugin* tests/unit/*plugin*.test.js --coverage",
|
|
61
|
+
"test:coverage:fast": "vitest run tests/unit/*branches.test.js tests/unit/auth.test.js tests/unit/services.test.js tests/unit/server.test.js --coverage",
|
|
45
62
|
"test:e2e": "playwright test",
|
|
46
63
|
"test:e2e:ui": "playwright test --ui",
|
|
47
64
|
"test:e2e:debug": "playwright test --debug",
|
|
@@ -80,10 +97,12 @@
|
|
|
80
97
|
"index.d.ts",
|
|
81
98
|
"bin/",
|
|
82
99
|
"src/",
|
|
83
|
-
"utils/",
|
|
84
100
|
"core/",
|
|
85
101
|
"plugins/",
|
|
86
|
-
"
|
|
102
|
+
".agents/",
|
|
103
|
+
"AGENTS.md",
|
|
104
|
+
"CLAUDE.md",
|
|
105
|
+
".cursorrules",
|
|
87
106
|
"views/partials/webspresso-client-runtime.njk"
|
|
88
107
|
],
|
|
89
108
|
"dependencies": {
|
|
@@ -94,7 +113,7 @@
|
|
|
94
113
|
"commander": "^11.1.0",
|
|
95
114
|
"connect-timeout": "^1.9.1",
|
|
96
115
|
"cookie-parser": "^1.4.7",
|
|
97
|
-
"csv-parse": "^
|
|
116
|
+
"csv-parse": "^7.0.2",
|
|
98
117
|
"dayjs": "^1.11.19",
|
|
99
118
|
"exceljs": "^4.4.0",
|
|
100
119
|
"express": "^5.2.1",
|
|
@@ -103,11 +122,11 @@
|
|
|
103
122
|
"inquirer": "^8.2.6",
|
|
104
123
|
"knex": "^3.1.0",
|
|
105
124
|
"mjml": "^5.4.0",
|
|
106
|
-
"multer": "^2.
|
|
107
|
-
"nodemailer": "^
|
|
125
|
+
"multer": "^2.4.0",
|
|
126
|
+
"nodemailer": "^10.0.10",
|
|
108
127
|
"nunjucks": "^3.2.4",
|
|
109
|
-
"sanitize-html": "^2.17.
|
|
110
|
-
"sharp": "^0.35.
|
|
128
|
+
"sanitize-html": "^2.17.7",
|
|
129
|
+
"sharp": "^0.35.4",
|
|
111
130
|
"swup": "^4.8.3",
|
|
112
131
|
"yaml": "^2.8.3",
|
|
113
132
|
"zod": "^3.23.0",
|
|
@@ -142,11 +161,11 @@
|
|
|
142
161
|
}
|
|
143
162
|
},
|
|
144
163
|
"devDependencies": {
|
|
145
|
-
"@faker-js/faker": "^
|
|
164
|
+
"@faker-js/faker": "^10.6.0",
|
|
146
165
|
"@playwright/test": "^1.48.0",
|
|
147
166
|
"@types/express": "^4.17.21",
|
|
148
167
|
"@types/node": "^20.14.0",
|
|
149
|
-
"@vitest/coverage-v8": "^
|
|
168
|
+
"@vitest/coverage-v8": "^4.1.11",
|
|
150
169
|
"better-sqlite3": "^11.10.0",
|
|
151
170
|
"chokidar": "^3.5.3",
|
|
152
171
|
"docboot": "^0.3.0",
|
|
@@ -155,16 +174,19 @@
|
|
|
155
174
|
"release-it": "^20.2.1",
|
|
156
175
|
"supertest": "^6.3.4",
|
|
157
176
|
"typescript": "~5.6.3",
|
|
158
|
-
"vitest": "^
|
|
177
|
+
"vitest": "^4.1.11",
|
|
178
|
+
"vitest-doctor": "^0.3.0"
|
|
159
179
|
},
|
|
160
180
|
"engines": {
|
|
161
|
-
"node": ">=
|
|
181
|
+
"node": ">=20.0.0"
|
|
162
182
|
},
|
|
163
183
|
"overrides": {
|
|
164
184
|
"basic-ftp": "^5.3.0",
|
|
165
185
|
"tar": "^7.5.11",
|
|
166
186
|
"undici": "^6.24.0",
|
|
167
|
-
"uuid": "^11.1.1"
|
|
187
|
+
"uuid": "^11.1.1",
|
|
188
|
+
"js-yaml": "^5.4.2",
|
|
189
|
+
"qs": "^6.16.0"
|
|
168
190
|
},
|
|
169
191
|
"allowScripts": {
|
|
170
192
|
"better-sqlite3@11.10.0": true
|
|
@@ -248,9 +248,10 @@ function wrapCustomPageWithLayout(page, rawComp) {
|
|
|
248
248
|
return {
|
|
249
249
|
oninit: function(vnode) {
|
|
250
250
|
try {
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
251
|
+
const instance = typeof rawComp === 'function' ? rawComp.call(rawComp, vnode) : rawComp;
|
|
252
|
+
vnode.state.instance = instance;
|
|
253
|
+
if (instance && typeof instance.oninit === 'function') {
|
|
254
|
+
return instance.oninit.call(instance, vnode);
|
|
254
255
|
}
|
|
255
256
|
} catch (err) {
|
|
256
257
|
console.error('[Admin Panel] Error in oninit for custom page "' + page.id + '":', err);
|
|
@@ -260,7 +261,7 @@ function wrapCustomPageWithLayout(page, rawComp) {
|
|
|
260
261
|
oncreate: function(vnode) {
|
|
261
262
|
try {
|
|
262
263
|
const comp = vnode.state.instance || rawComp;
|
|
263
|
-
if (comp && typeof comp.oncreate === 'function') return comp.oncreate.call(
|
|
264
|
+
if (comp && typeof comp.oncreate === 'function') return comp.oncreate.call(comp, vnode);
|
|
264
265
|
} catch (err) {
|
|
265
266
|
console.error('[Admin Panel] Error in oncreate for custom page "' + page.id + '":', err);
|
|
266
267
|
}
|
|
@@ -268,7 +269,7 @@ function wrapCustomPageWithLayout(page, rawComp) {
|
|
|
268
269
|
onupdate: function(vnode) {
|
|
269
270
|
try {
|
|
270
271
|
const comp = vnode.state.instance || rawComp;
|
|
271
|
-
if (comp && typeof comp.onupdate === 'function') return comp.onupdate.call(
|
|
272
|
+
if (comp && typeof comp.onupdate === 'function') return comp.onupdate.call(comp, vnode);
|
|
272
273
|
} catch (err) {
|
|
273
274
|
console.error('[Admin Panel] Error in onupdate for custom page "' + page.id + '":', err);
|
|
274
275
|
}
|
|
@@ -276,7 +277,7 @@ function wrapCustomPageWithLayout(page, rawComp) {
|
|
|
276
277
|
onbeforeremove: function(vnode) {
|
|
277
278
|
try {
|
|
278
279
|
const comp = vnode.state.instance || rawComp;
|
|
279
|
-
if (comp && typeof comp.onbeforeremove === 'function') return comp.onbeforeremove.call(
|
|
280
|
+
if (comp && typeof comp.onbeforeremove === 'function') return comp.onbeforeremove.call(comp, vnode);
|
|
280
281
|
} catch (err) {
|
|
281
282
|
console.error('[Admin Panel] Error in onbeforeremove for custom page "' + page.id + '":', err);
|
|
282
283
|
}
|
|
@@ -284,7 +285,7 @@ function wrapCustomPageWithLayout(page, rawComp) {
|
|
|
284
285
|
onremove: function(vnode) {
|
|
285
286
|
try {
|
|
286
287
|
const comp = vnode.state.instance || rawComp;
|
|
287
|
-
if (comp && typeof comp.onremove === 'function') return comp.onremove.call(
|
|
288
|
+
if (comp && typeof comp.onremove === 'function') return comp.onremove.call(comp, vnode);
|
|
288
289
|
} catch (err) {
|
|
289
290
|
console.error('[Admin Panel] Error in onremove for custom page "' + page.id + '":', err);
|
|
290
291
|
}
|
|
@@ -305,9 +306,9 @@ function wrapCustomPageWithLayout(page, rawComp) {
|
|
|
305
306
|
var content;
|
|
306
307
|
try {
|
|
307
308
|
if (comp && typeof comp.view === 'function') {
|
|
308
|
-
content = comp.view.call(
|
|
309
|
+
content = comp.view.call(comp, vnode);
|
|
309
310
|
} else if (typeof comp === 'function') {
|
|
310
|
-
content = comp.call(
|
|
311
|
+
content = comp.call(comp, vnode);
|
|
311
312
|
} else {
|
|
312
313
|
content = comp;
|
|
313
314
|
}
|
|
@@ -305,7 +305,28 @@ const FileUploadField = {
|
|
|
305
305
|
}),
|
|
306
306
|
m('div', [
|
|
307
307
|
m('p.text-gray-600.dark:text-slate-400.mb-2', displayUrl ? 'Drag and drop to replace, or' : 'Drag and drop a file here, or'),
|
|
308
|
-
m('
|
|
308
|
+
m('div.flex.items-center.justify-center.gap-3.flex-wrap', [
|
|
309
|
+
m('label.text-blue-600.hover:text-blue-800.dark:text-blue-400.cursor-pointer.font-medium', { for: 'file-input-' + col.name }, displayUrl ? 'choose another file' : 'browse'),
|
|
310
|
+
typeof window !== 'undefined' && window.__openFileManagerPicker
|
|
311
|
+
? m('button.text-indigo-600.hover:text-indigo-800.dark:text-indigo-400.cursor-pointer.font-medium.flex.items-center.gap-1', {
|
|
312
|
+
type: 'button',
|
|
313
|
+
onclick: function(e) {
|
|
314
|
+
e.preventDefault();
|
|
315
|
+
e.stopPropagation();
|
|
316
|
+
window.__openFileManagerPicker({
|
|
317
|
+
accept: accept,
|
|
318
|
+
title: '📁 Ortam Kütüphanesinden Seç',
|
|
319
|
+
onSelect: function(pickedUrl) {
|
|
320
|
+
if (onChange) onChange(pickedUrl);
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
}, [
|
|
325
|
+
m('span', '•'),
|
|
326
|
+
m('span', 'Kütüphaneden Seç')
|
|
327
|
+
])
|
|
328
|
+
: null
|
|
329
|
+
])
|
|
309
330
|
]),
|
|
310
331
|
]),
|
|
311
332
|
m('input[type=hidden]', { name: col.name, value: typeof value === 'string' ? value : '' }),
|
|
@@ -24,6 +24,10 @@ let npmVersionCache = {
|
|
|
24
24
|
};
|
|
25
25
|
|
|
26
26
|
function fetchLatestNpmVersion(force = false) {
|
|
27
|
+
if ((process.env.NODE_ENV === 'test' || process.env.VITEST) && !force) {
|
|
28
|
+
return Promise.resolve(npmVersionCache.latestVersion || getWebspressoVersion());
|
|
29
|
+
}
|
|
30
|
+
|
|
27
31
|
const now = Date.now();
|
|
28
32
|
if (!force && npmVersionCache.latestVersion && (now - npmVersionCache.checkedAt < 30 * 60 * 1000)) {
|
|
29
33
|
return Promise.resolve(npmVersionCache.latestVersion);
|
|
@@ -117,7 +117,9 @@ function sendUnauthorized(req, res, config) {
|
|
|
117
117
|
|
|
118
118
|
if (config.challenge !== false) {
|
|
119
119
|
const realm = String(config.realm || 'Restricted Area')
|
|
120
|
-
.split(/[\r\n]/)[0]
|
|
120
|
+
.split(/[\r\n\0]/)[0]
|
|
121
|
+
.replace(/[\t]/g, ' ')
|
|
122
|
+
.replace(/\\/g, '\\\\')
|
|
121
123
|
.replace(/"/g, '\\"');
|
|
122
124
|
res.setHeader('WWW-Authenticate', `Basic realm="${realm}"`);
|
|
123
125
|
}
|
|
@@ -248,9 +248,14 @@
|
|
|
248
248
|
el.textContent = '';
|
|
249
249
|
return;
|
|
250
250
|
}
|
|
251
|
+
if (typeof window.DOMPurify !== 'undefined' && typeof window.DOMPurify.sanitize === 'function') {
|
|
252
|
+
el.innerHTML = window.DOMPurify.sanitize(htmlStr);
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
251
255
|
try {
|
|
252
|
-
var
|
|
253
|
-
|
|
256
|
+
var template = document.createElement('template');
|
|
257
|
+
template.innerHTML = htmlStr;
|
|
258
|
+
var content = template.content;
|
|
254
259
|
var allowedTags = {
|
|
255
260
|
p: 1, br: 1, b: 1, i: 1, strong: 1, em: 1, u: 1, s: 1, strike: 1,
|
|
256
261
|
h1: 1, h2: 1, h3: 1, h4: 1, h5: 1, h6: 1, ul: 1, ol: 1, li: 1,
|
|
@@ -260,14 +265,14 @@
|
|
|
260
265
|
var allowedAttrs = {
|
|
261
266
|
href: 1, title: 1, target: 1, rel: 1, class: 1, id: 1, src: 1, alt: 1, width: 1, height: 1
|
|
262
267
|
};
|
|
263
|
-
var allNodes =
|
|
268
|
+
var allNodes = content.querySelectorAll('*');
|
|
264
269
|
for (var i = allNodes.length - 1; i >= 0; i--) {
|
|
265
270
|
var node = allNodes[i];
|
|
266
271
|
var tag = node.tagName.toLowerCase();
|
|
267
272
|
if (!allowedTags[tag]) {
|
|
268
273
|
if (node.parentNode) {
|
|
269
274
|
node.parentNode.removeChild(node);
|
|
270
|
-
} else {
|
|
275
|
+
} else if (typeof node.remove === 'function') {
|
|
271
276
|
node.remove();
|
|
272
277
|
}
|
|
273
278
|
continue;
|
|
@@ -275,7 +280,7 @@
|
|
|
275
280
|
var attrs = Array.prototype.slice.call(node.attributes);
|
|
276
281
|
for (var j = 0; j < attrs.length; j++) {
|
|
277
282
|
var attrName = attrs[j].name.toLowerCase();
|
|
278
|
-
var attrVal = attrs[j].value.toLowerCase().replace(/[\x00-\x20\s]+/g, '').trim();
|
|
283
|
+
var attrVal = (attrs[j].value || '').toLowerCase().replace(/[\x00-\x20\s]+/g, '').trim();
|
|
279
284
|
if (attrName.indexOf('on') === 0 || !allowedAttrs[attrName]) {
|
|
280
285
|
node.removeAttribute(attrs[j].name);
|
|
281
286
|
} else if ((attrName === 'href' || attrName === 'src' || attrName === 'action' || attrName === 'formaction' || attrName === 'xlink:href') &&
|
|
@@ -287,13 +292,8 @@
|
|
|
287
292
|
}
|
|
288
293
|
}
|
|
289
294
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
el.removeChild(el.firstChild);
|
|
293
|
-
}
|
|
294
|
-
while (doc.body.firstChild) {
|
|
295
|
-
el.appendChild(doc.body.firstChild);
|
|
296
|
-
}
|
|
295
|
+
el.textContent = '';
|
|
296
|
+
el.appendChild(content.cloneNode(true));
|
|
297
297
|
} catch (e) {
|
|
298
298
|
el.textContent = htmlStr;
|
|
299
299
|
}
|