webspresso 0.0.62 → 0.0.64

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/index.d.ts ADDED
@@ -0,0 +1,482 @@
1
+ /**
2
+ * Type definitions for webspresso (CommonJS package).
3
+ */
4
+
5
+ import type { Application, NextFunction, Request, RequestHandler, Response } from 'express';
6
+ import type { Knex } from 'knex';
7
+ import type { ZodObject, ZodTypeAny } from 'zod';
8
+
9
+ // --- Express / app ---
10
+
11
+ export interface ErrorPageContext {
12
+ fsy: Record<string, unknown>;
13
+ locale: string;
14
+ isDev: boolean;
15
+ url: string;
16
+ method: string;
17
+ [key: string]: unknown;
18
+ }
19
+
20
+ export interface CreateAppOptions {
21
+ pagesDir: string;
22
+ viewsDir?: string;
23
+ publicDir?: string;
24
+ logging?: boolean;
25
+ helmet?: boolean | Record<string, unknown>;
26
+ middlewares?: Record<string, RequestHandler>;
27
+ plugins?: WebspressoPlugin[];
28
+ assets?: {
29
+ version?: string;
30
+ manifestPath?: string;
31
+ prefix?: string;
32
+ };
33
+ errorPages?: {
34
+ notFound?: string | ((req: Request, res: Response, ctx: ErrorPageContext) => unknown);
35
+ serverError?:
36
+ | string
37
+ | ((err: unknown, req: Request, res: Response, ctx: ErrorPageContext) => unknown);
38
+ timeout?: string | ((req: Request, res: Response, ctx: ErrorPageContext) => unknown);
39
+ };
40
+ timeout?: string | false;
41
+ auth?: unknown;
42
+ db?: DatabaseInstance | null;
43
+ setupRoutes?: (app: Application, ctx: SetupRoutesContext) => void;
44
+ [key: string]: unknown;
45
+ }
46
+
47
+ export interface SetupRoutesContext {
48
+ nunjucksEnv: unknown;
49
+ authMiddleware?: RequestHandler;
50
+ pluginManager: PluginManager;
51
+ options: CreateAppOptions;
52
+ }
53
+
54
+ export interface CreateAppResult {
55
+ app: Application;
56
+ nunjucksEnv: unknown;
57
+ pluginManager: PluginManager;
58
+ authMiddleware?: RequestHandler;
59
+ }
60
+
61
+ export function createApp(options?: CreateAppOptions): CreateAppResult;
62
+
63
+ // --- App context ---
64
+
65
+ export function attachDbMiddleware(req: Request, res: Response, next: NextFunction): void;
66
+
67
+ export function getAppContext(): { db: DatabaseInstance | null };
68
+
69
+ export function getDb(): DatabaseInstance;
70
+
71
+ export function hasDb(): boolean;
72
+
73
+ export function resetAppContext(): void;
74
+
75
+ export function setAppContext(partial: { db?: DatabaseInstance | null }): void;
76
+
77
+ // --- File router ---
78
+
79
+ export function mountPages(
80
+ app: Application,
81
+ options: Record<string, unknown>
82
+ ): unknown;
83
+
84
+ export function filePathToRoute(filePath: string, pagesDir: string): string;
85
+
86
+ export function extractMethodFromFilename(filename: string): string | null;
87
+
88
+ export function scanDirectory(
89
+ dir: string,
90
+ options?: Record<string, unknown>
91
+ ): unknown[];
92
+
93
+ export function loadI18n(pagesDir: string, routePath?: string): Record<string, unknown>;
94
+
95
+ export function createTranslator(
96
+ dictionaries: Record<string, unknown>,
97
+ locale: string
98
+ ): (key: string, params?: Record<string, unknown>) => string;
99
+
100
+ export function detectLocale(
101
+ req: Request,
102
+ supportedLocales: string[],
103
+ defaultLocale: string
104
+ ): string;
105
+
106
+ // --- Helpers / assets ---
107
+
108
+ export function createHelpers(context: Record<string, unknown>): Record<string, unknown>;
109
+
110
+ export const utils: Record<string, unknown>;
111
+
112
+ export class AssetManager {
113
+ constructor(options?: Record<string, unknown>);
114
+ [key: string]: unknown;
115
+ }
116
+
117
+ export function configureAssets(
118
+ nunjucksEnv: unknown,
119
+ options?: Record<string, unknown>
120
+ ): void;
121
+
122
+ export function getAssetManager(): AssetManager | null;
123
+
124
+ // --- Plugins ---
125
+
126
+ export interface RoutesReadyContext {
127
+ app: Application;
128
+ nunjucksEnv: unknown;
129
+ options: CreateAppOptions;
130
+ db: DatabaseInstance | null;
131
+ routes: unknown;
132
+ usePlugin(name: string): unknown;
133
+ addHelper(name: string, fn: (...args: unknown[]) => unknown): void;
134
+ addFilter(name: string, fn: (...args: unknown[]) => unknown): void;
135
+ addRoute(method: string, path: string, ...handlers: RequestHandler[]): void;
136
+ [key: string]: unknown;
137
+ }
138
+
139
+ export interface PluginRegisterContext {
140
+ app: Application;
141
+ nunjucksEnv: unknown;
142
+ options: Record<string, unknown>;
143
+ db: DatabaseInstance | null;
144
+ usePlugin(name: string): unknown;
145
+ addHelper(name: string, fn: (...args: unknown[]) => unknown): void;
146
+ addFilter(name: string, fn: (...args: unknown[]) => unknown): void;
147
+ addRoute(method: string, path: string, ...handlers: RequestHandler[]): void;
148
+ routes: unknown;
149
+ [key: string]: unknown;
150
+ }
151
+
152
+ export interface WebspressoPlugin {
153
+ name: string;
154
+ version: string;
155
+ dependencies?: Record<string, string>;
156
+ register?(ctx: PluginRegisterContext): void | Promise<void>;
157
+ onRoutesReady?(ctx: RoutesReadyContext): void;
158
+ onReady?(): void | Promise<void>;
159
+ api?: Record<string, unknown>;
160
+ csp?: Record<string, unknown>;
161
+ [key: string]: unknown;
162
+ }
163
+
164
+ export class PluginManager {
165
+ [key: string]: unknown;
166
+ }
167
+
168
+ export function createPluginManager(): PluginManager;
169
+
170
+ export function getPluginManager(): PluginManager | null;
171
+
172
+ export function resetPluginManager(): void;
173
+
174
+ // --- ORM: scopes & model ---
175
+
176
+ export interface ScopeContext {
177
+ tenantId?: unknown;
178
+ withTrashed?: boolean;
179
+ onlyTrashed?: boolean;
180
+ [key: string]: unknown;
181
+ }
182
+
183
+ export type RelationType = 'belongsTo' | 'hasMany' | 'hasOne';
184
+
185
+ export interface RelationDefinition {
186
+ type: RelationType;
187
+ model: () => ModelDefinition;
188
+ foreignKey: string;
189
+ localKey?: string;
190
+ }
191
+
192
+ export interface AdminMetadata {
193
+ enabled?: boolean;
194
+ label?: string;
195
+ icon?: string | null;
196
+ customFields?: Record<string, unknown>;
197
+ queries?: Record<string, (repo: Repository) => Promise<unknown>>;
198
+ }
199
+
200
+ export interface RestMetadata {
201
+ enabled?: boolean;
202
+ path?: string;
203
+ allowInclude?: string[];
204
+ }
205
+
206
+ export interface ScopeOptions {
207
+ softDelete?: boolean;
208
+ timestamps?: boolean;
209
+ tenant?: string | null;
210
+ }
211
+
212
+ export interface ModelOptions {
213
+ name: string;
214
+ table: string;
215
+ schema: ZodObject<Record<string, ZodTypeAny>>;
216
+ primaryKey?: string;
217
+ relations?: Record<string, RelationDefinition>;
218
+ scopes?: ScopeOptions;
219
+ admin?: AdminMetadata;
220
+ rest?: RestMetadata;
221
+ hooks?: Record<string, (...args: unknown[]) => unknown>;
222
+ hidden?: string[];
223
+ }
224
+
225
+ export interface ModelDefinition {
226
+ name: string;
227
+ table: string;
228
+ schema: ZodObject<Record<string, ZodTypeAny>>;
229
+ primaryKey: string;
230
+ relations: Record<string, RelationDefinition>;
231
+ scopes: {
232
+ softDelete: boolean;
233
+ timestamps: boolean;
234
+ tenant: string | null;
235
+ };
236
+ columns: Map<string, unknown>;
237
+ admin: {
238
+ enabled: boolean;
239
+ label: string;
240
+ icon: string | null;
241
+ customFields: Record<string, unknown>;
242
+ queries: Record<string, (repo: Repository) => Promise<unknown>>;
243
+ };
244
+ rest: {
245
+ enabled: boolean;
246
+ path: string | null;
247
+ allowInclude: string[] | null;
248
+ };
249
+ hidden: string[];
250
+ hooks: Record<string, unknown>;
251
+ }
252
+
253
+ export function defineModel(options: ModelOptions): ModelDefinition;
254
+
255
+ export function getModel(name: string): ModelDefinition | undefined;
256
+
257
+ export function getAllModels(): Map<string, ModelDefinition>;
258
+
259
+ export function hasModel(name: string): boolean;
260
+
261
+ export function clearRegistry(): void;
262
+
263
+ // --- ORM: schema & columns ---
264
+
265
+ /** zdb column helpers (id, string, timestamp, …) + `schema()` */
266
+ export type Zdb = Record<string, any> & {
267
+ schema(shape: Record<string, ZodTypeAny>): ZodObject<Record<string, ZodTypeAny>>;
268
+ };
269
+
270
+ export const zdb: Zdb;
271
+
272
+ export function createSchemaHelpers(z: typeof import('zod').z): Zdb;
273
+
274
+ export function extractColumnsFromSchema(schema: ZodObject<Record<string, ZodTypeAny>>): Map<string, unknown>;
275
+
276
+ export function getColumnMeta(schema: ZodTypeAny): unknown;
277
+
278
+ // --- ORM: query / repository ---
279
+
280
+ export interface FindOptions {
281
+ with?: string[];
282
+ select?: string[];
283
+ }
284
+
285
+ export interface PaginatedResult {
286
+ data: Record<string, unknown>[];
287
+ total: number;
288
+ page: number;
289
+ perPage: number;
290
+ totalPages: number;
291
+ }
292
+
293
+ export interface QueryBuilder {
294
+ where(column: string, value: unknown): this;
295
+ where(column: string, operator: string, value: unknown): this;
296
+ where(conditions: Record<string, unknown>): this;
297
+ orWhere(...args: unknown[]): this;
298
+ whereIn(column: string, values: unknown[]): this;
299
+ whereBetween(column: string, range: [unknown, unknown]): this;
300
+ select(...columns: string[]): this;
301
+ orderBy(column: string, direction?: 'asc' | 'desc'): this;
302
+ limit(n: number): this;
303
+ offset(n: number): this;
304
+ with(...relations: string[]): this;
305
+ withTrashed(): this;
306
+ onlyTrashed(): this;
307
+ first(): Promise<Record<string, unknown> | null>;
308
+ list(): Promise<Record<string, unknown>[]>;
309
+ get(): Promise<Record<string, unknown>[]>;
310
+ count(): Promise<number>;
311
+ paginate(page?: number, perPage?: number): Promise<PaginatedResult>;
312
+ exists(): Promise<boolean>;
313
+ clone(): QueryBuilder;
314
+ getWiths(): string[];
315
+ delete(): Promise<number>;
316
+ update(data: Record<string, unknown>): Promise<number>;
317
+ [key: string]: unknown;
318
+ }
319
+
320
+ export interface Repository {
321
+ findById(id: string | number, options?: FindOptions): Promise<Record<string, unknown> | null>;
322
+ findOne(conditions: Record<string, unknown>, options?: FindOptions): Promise<Record<string, unknown> | null>;
323
+ findAll(options?: FindOptions): Promise<Record<string, unknown>[]>;
324
+ create(data: Record<string, unknown>): Promise<Record<string, unknown>>;
325
+ createMany(data: Record<string, unknown>[]): Promise<Record<string, unknown>[]>;
326
+ update(id: string | number, data: Record<string, unknown>): Promise<Record<string, unknown> | null>;
327
+ updateWhere(conditions: Record<string, unknown>, data: Record<string, unknown>): Promise<number>;
328
+ delete(id: string | number): Promise<boolean>;
329
+ forceDelete(id: string | number): Promise<boolean>;
330
+ restore(id: string | number): Promise<Record<string, unknown> | null>;
331
+ query(): QueryBuilder;
332
+ raw(sql: string, bindings?: unknown[]): Promise<unknown>;
333
+ count(conditions?: Record<string, unknown>): Promise<number>;
334
+ exists(conditions: Record<string, unknown>): Promise<boolean>;
335
+ with(...relations: string[]): QueryBuilder;
336
+ model: ModelDefinition;
337
+ }
338
+
339
+ // --- ORM: database ---
340
+
341
+ export interface MigrationConfig {
342
+ directory?: string;
343
+ tableName?: string;
344
+ }
345
+
346
+ export interface DatabaseConfig {
347
+ client?: string;
348
+ connection?: string | Record<string, unknown>;
349
+ models?: string;
350
+ migrations?: MigrationConfig;
351
+ pool?: Record<string, unknown>;
352
+ useNullAsDefault?: boolean;
353
+ [key: string]: unknown;
354
+ }
355
+
356
+ export interface MigrationStatus {
357
+ name: string;
358
+ completed: boolean;
359
+ ran_at: Date | null;
360
+ batch: number | null;
361
+ }
362
+
363
+ export interface MigrationResult {
364
+ batch: number;
365
+ migrations: string[];
366
+ }
367
+
368
+ export interface MakeMigrationResult {
369
+ filename: string;
370
+ filepath: string;
371
+ content: string | null;
372
+ }
373
+
374
+ export interface MigrationManager {
375
+ latest(): Promise<MigrationResult>;
376
+ rollback(options?: { all?: boolean }): Promise<MigrationResult>;
377
+ currentVersion(): Promise<string>;
378
+ status(): Promise<MigrationStatus[]>;
379
+ make(name: string, options?: { content?: string }): Promise<string | MakeMigrationResult>;
380
+ up(name: string): Promise<void>;
381
+ down(name: string): Promise<void>;
382
+ getConfig(): MigrationConfig & { directory: string; tableName: string };
383
+ hasTable(): Promise<boolean>;
384
+ unlock(): Promise<void>;
385
+ }
386
+
387
+ export interface TransactionContext {
388
+ trx: Knex.Transaction;
389
+ getRepository(modelName: string, scopeContext?: ScopeContext): Repository;
390
+ createRepository(model: ModelDefinition, scopeContext?: ScopeContext): Repository;
391
+ }
392
+
393
+ export interface DatabaseInstance {
394
+ knex: Knex;
395
+ migrate: MigrationManager;
396
+ getModel(name: string): ModelDefinition;
397
+ hasModel(name: string): boolean;
398
+ getAllModels(): ModelDefinition[];
399
+ registerModel(model: ModelDefinition): void;
400
+ getRepository(modelName: string, scopeContext?: ScopeContext): Repository;
401
+ createRepository(model: ModelDefinition, scopeContext?: ScopeContext): Repository;
402
+ query(modelName: string, scopeContext?: ScopeContext): QueryBuilder;
403
+ transaction<T>(callback: (ctx: TransactionContext) => Promise<T>): Promise<T>;
404
+ createSeeder(): unknown;
405
+ destroy(): Promise<void>;
406
+ }
407
+
408
+ export function createDatabase(config: DatabaseConfig): DatabaseInstance;
409
+
410
+ // --- ORM: nanoid / zod ---
411
+
412
+ export function generateNanoid(options?: { maxLength?: number } | number): string;
413
+
414
+ export const zodNanoid: ZodTypeAny;
415
+
416
+ export function extendZ(z: typeof import('zod').z): typeof import('zod').z;
417
+
418
+ // --- ORM: sanitize ---
419
+
420
+ export function omitHiddenColumns(record: unknown, model: ModelDefinition): unknown;
421
+
422
+ export function sanitizeForOutput(
423
+ records: unknown,
424
+ model: ModelDefinition
425
+ ): unknown;
426
+
427
+ // --- ORM: events ---
428
+
429
+ export const ModelEvents: {
430
+ on(event: string, handler: (...args: unknown[]) => void): void;
431
+ emit(event: string, ...args: unknown[]): void;
432
+ emitAsync?(event: string, ...args: unknown[]): Promise<void>;
433
+ [key: string]: unknown;
434
+ };
435
+
436
+ export const Hooks: Record<string, string>;
437
+
438
+ export class HookCancellationError extends Error {
439
+ reason: string;
440
+ model: string;
441
+ hook: string;
442
+ constructor(reason: string, model: string, hook: string);
443
+ }
444
+
445
+ export function createEventContext(
446
+ model: string,
447
+ operation: string,
448
+ trx?: unknown | null
449
+ ): {
450
+ model: string;
451
+ operation: string;
452
+ trx: unknown | null;
453
+ isCancelled: boolean;
454
+ cancelReason: string | null;
455
+ cancel(reason?: string): void;
456
+ };
457
+
458
+ // --- Built-in plugins (subset re-exported from main index.js) ---
459
+
460
+ export function schemaExplorerPlugin(options?: Record<string, unknown>): WebspressoPlugin;
461
+
462
+ export function adminPanelPlugin(options?: Record<string, unknown>): WebspressoPlugin;
463
+
464
+ export function siteAnalyticsPlugin(options?: Record<string, unknown>): WebspressoPlugin;
465
+
466
+ export function auditLogPlugin(options?: Record<string, unknown>): WebspressoPlugin;
467
+
468
+ export function recaptchaPlugin(options?: Record<string, unknown>): WebspressoPlugin;
469
+
470
+ export function swaggerPlugin(options?: Record<string, unknown>): WebspressoPlugin;
471
+
472
+ export function healthCheckPlugin(options?: Record<string, unknown>): WebspressoPlugin;
473
+
474
+ export interface RestResourcePluginOptions {
475
+ path?: string;
476
+ middleware?: RequestHandler[];
477
+ models?: string[] | null;
478
+ excludeModels?: string[];
479
+ filter?: (model: ModelDefinition) => boolean;
480
+ }
481
+
482
+ export function restResourcePlugin(options?: RestResourcePluginOptions): WebspressoPlugin;
package/index.js CHANGED
@@ -38,7 +38,7 @@ const {
38
38
  const orm = require('./core/orm');
39
39
 
40
40
  // Built-in plugins
41
- const { schemaExplorerPlugin, adminPanelPlugin, siteAnalyticsPlugin, auditLogPlugin, recaptchaPlugin, swaggerPlugin, healthCheckPlugin } = require('./plugins');
41
+ const { schemaExplorerPlugin, adminPanelPlugin, siteAnalyticsPlugin, auditLogPlugin, recaptchaPlugin, swaggerPlugin, healthCheckPlugin, restResourcePlugin } = require('./plugins');
42
42
 
43
43
  module.exports = {
44
44
  // Main API
@@ -89,4 +89,5 @@ module.exports = {
89
89
  recaptchaPlugin,
90
90
  swaggerPlugin,
91
91
  healthCheckPlugin,
92
+ restResourcePlugin,
92
93
  };
package/package.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "webspresso",
3
- "version": "0.0.62",
3
+ "version": "0.0.64",
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
+ "types": "index.d.ts",
6
7
  "bin": {
7
8
  "webspresso": "./bin/webspresso.js"
8
9
  },
@@ -14,6 +15,7 @@
14
15
  "test:e2e:ui": "playwright test --ui",
15
16
  "test:e2e:debug": "playwright test --debug",
16
17
  "test:e2e:headed": "playwright test --headed",
18
+ "check:types": "tsc --project tests/ts-smoke/tsconfig.json",
17
19
  "release": "release-it"
18
20
  },
19
21
  "keywords": [
@@ -34,6 +36,7 @@
34
36
  },
35
37
  "files": [
36
38
  "index.js",
39
+ "index.d.ts",
37
40
  "bin/",
38
41
  "src/",
39
42
  "utils/",
@@ -82,6 +85,8 @@
82
85
  }
83
86
  },
84
87
  "devDependencies": {
88
+ "@types/express": "^4.17.21",
89
+ "@types/node": "^20.14.0",
85
90
  "@faker-js/faker": "^9.9.0",
86
91
  "@playwright/test": "^1.48.0",
87
92
  "@vitest/coverage-v8": "^3.0.0",
@@ -90,6 +95,7 @@
90
95
  "dotenv": "^16.3.1",
91
96
  "release-it": "^19.0.0",
92
97
  "supertest": "^6.3.4",
98
+ "typescript": "~5.6.3",
93
99
  "vitest": "^3.0.0"
94
100
  },
95
101
  "engines": {