webspresso 0.0.21 → 0.0.22

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.
@@ -51,6 +51,250 @@ function getColumnMeta(schema) {
51
51
  return decodeColumnMeta(schema.description);
52
52
  }
53
53
 
54
+ /**
55
+ * SchemaBuilder - Chainable wrapper for Zod schemas with validation and UI metadata
56
+ * @class
57
+ */
58
+ class SchemaBuilder {
59
+ /**
60
+ * @param {import('zod').ZodTypeAny} schema - Base Zod schema
61
+ * @param {import('./types').ColumnMeta} baseMeta - Base column metadata
62
+ * @param {typeof import('zod').z} z - Zod instance
63
+ */
64
+ constructor(schema, baseMeta, z) {
65
+ this._schema = schema;
66
+ this._baseMeta = { ...baseMeta };
67
+ this._validations = {};
68
+ this._ui = {};
69
+ this._z = z;
70
+ this._finalized = false;
71
+ }
72
+
73
+ /**
74
+ * Proxy all Zod validation methods
75
+ */
76
+ min(value) {
77
+ this._validations.min = value;
78
+ if (typeof this._schema.min === 'function') {
79
+ this._schema = this._schema.min(value);
80
+ }
81
+ return this;
82
+ }
83
+
84
+ max(value) {
85
+ this._validations.max = value;
86
+ if (typeof this._schema.max === 'function') {
87
+ this._schema = this._schema.max(value);
88
+ }
89
+ return this;
90
+ }
91
+
92
+ length(value) {
93
+ this._validations.length = value;
94
+ if (typeof this._schema.length === 'function') {
95
+ this._schema = this._schema.length(value);
96
+ }
97
+ return this;
98
+ }
99
+
100
+ minLength(value) {
101
+ this._validations.minLength = value;
102
+ if (typeof this._schema.min === 'function') {
103
+ this._schema = this._schema.min(value);
104
+ }
105
+ return this;
106
+ }
107
+
108
+ maxLength(value) {
109
+ this._validations.maxLength = value;
110
+ if (typeof this._schema.max === 'function') {
111
+ this._schema = this._schema.max(value);
112
+ }
113
+ return this;
114
+ }
115
+
116
+ email() {
117
+ this._validations.email = true;
118
+ if (typeof this._schema.email === 'function') {
119
+ this._schema = this._schema.email();
120
+ }
121
+ return this;
122
+ }
123
+
124
+ url() {
125
+ this._validations.url = true;
126
+ if (typeof this._schema.url === 'function') {
127
+ this._schema = this._schema.url();
128
+ }
129
+ return this;
130
+ }
131
+
132
+ pattern(regex) {
133
+ const patternStr = regex instanceof RegExp ? regex.source : regex;
134
+ this._validations.pattern = patternStr;
135
+ if (typeof this._schema.regex === 'function') {
136
+ this._schema = this._schema.regex(regex);
137
+ }
138
+ return this;
139
+ }
140
+
141
+ includes(str) {
142
+ this._validations.includes = str;
143
+ if (typeof this._schema.includes === 'function') {
144
+ this._schema = this._schema.includes(str);
145
+ }
146
+ return this;
147
+ }
148
+
149
+ startsWith(str) {
150
+ this._validations.startsWith = str;
151
+ if (typeof this._schema.startsWith === 'function') {
152
+ this._schema = this._schema.startsWith(str);
153
+ }
154
+ return this;
155
+ }
156
+
157
+ endsWith(str) {
158
+ this._validations.endsWith = str;
159
+ if (typeof this._schema.endsWith === 'function') {
160
+ this._schema = this._schema.endsWith(str);
161
+ }
162
+ return this;
163
+ }
164
+
165
+ positive() {
166
+ this._validations.positive = true;
167
+ if (typeof this._schema.positive === 'function') {
168
+ this._schema = this._schema.positive();
169
+ }
170
+ return this;
171
+ }
172
+
173
+ negative() {
174
+ this._validations.negative = true;
175
+ if (typeof this._schema.negative === 'function') {
176
+ this._schema = this._schema.negative();
177
+ }
178
+ return this;
179
+ }
180
+
181
+ int() {
182
+ this._validations.int = true;
183
+ if (typeof this._schema.int === 'function') {
184
+ this._schema = this._schema.int();
185
+ }
186
+ return this;
187
+ }
188
+
189
+ step(value) {
190
+ this._validations.step = value;
191
+ // Zod doesn't have step, but we store it for UI
192
+ return this;
193
+ }
194
+
195
+ nonempty() {
196
+ this._validations.nonempty = true;
197
+ if (typeof this._schema.min === 'function') {
198
+ this._schema = this._schema.min(1);
199
+ } else if (typeof this._schema.length === 'function') {
200
+ this._schema = this._schema.min(1);
201
+ }
202
+ return this;
203
+ }
204
+
205
+ nullable() {
206
+ this._baseMeta.nullable = true;
207
+ if (this._schema.nullable) {
208
+ this._schema = this._schema.nullable();
209
+ }
210
+ return this;
211
+ }
212
+
213
+ optional() {
214
+ if (this._schema.optional) {
215
+ this._schema = this._schema.optional();
216
+ }
217
+ return this;
218
+ }
219
+
220
+ /**
221
+ * Configure UI metadata
222
+ * @param {import('./types').UIMeta} options - UI configuration options
223
+ * @returns {SchemaBuilder}
224
+ */
225
+ config(options) {
226
+ if (options.label !== undefined) this._ui.label = options.label;
227
+ if (options.placeholder !== undefined) this._ui.placeholder = options.placeholder;
228
+ if (options.hint !== undefined) this._ui.hint = options.hint;
229
+ if (options.inputType !== undefined) this._ui.inputType = options.inputType;
230
+ if (options.hidden !== undefined) this._ui.hidden = options.hidden;
231
+ if (options.readonly !== undefined) this._ui.readonly = options.readonly;
232
+ if (options.width !== undefined) this._ui.width = options.width;
233
+ if (options.rows !== undefined) this._ui.rows = options.rows;
234
+ return this;
235
+ }
236
+
237
+ /**
238
+ * Finalize the schema and return Zod schema with metadata
239
+ * @returns {import('zod').ZodTypeAny}
240
+ */
241
+ _finalize() {
242
+ if (this._finalized) {
243
+ return this._schema;
244
+ }
245
+
246
+ // Merge validations and UI into base metadata
247
+ const finalMeta = {
248
+ ...this._baseMeta,
249
+ ...(Object.keys(this._validations).length > 0 && { validations: this._validations }),
250
+ ...(Object.keys(this._ui).length > 0 && { ui: this._ui }),
251
+ };
252
+
253
+ // Apply metadata to schema
254
+ this._schema = this._schema.describe(encodeColumnMeta(finalMeta));
255
+ this._finalized = true;
256
+ return this._schema;
257
+ }
258
+
259
+ /**
260
+ * Proxy unknown methods to underlying Zod schema
261
+ */
262
+ _proxyMethod(name, args) {
263
+ if (typeof this._schema[name] === 'function') {
264
+ this._schema = this._schema[name](...args);
265
+ return this;
266
+ }
267
+ throw new Error(`Method ${name} is not available on this schema type`);
268
+ }
269
+ }
270
+
271
+ // Proxy handler for unknown methods
272
+ const handler = {
273
+ get(target, prop) {
274
+ if (prop in target) {
275
+ return target[prop];
276
+ }
277
+ // Proxy unknown methods to Zod schema
278
+ if (typeof target._schema[prop] === 'function') {
279
+ return function(...args) {
280
+ return target._proxyMethod(prop, args);
281
+ };
282
+ }
283
+ return target._schema[prop];
284
+ },
285
+ };
286
+
287
+ /**
288
+ * Create a proxied SchemaBuilder instance
289
+ * @param {import('zod').ZodTypeAny} schema - Base Zod schema
290
+ * @param {import('./types').ColumnMeta} baseMeta - Base column metadata
291
+ * @param {typeof import('zod').z} z - Zod instance
292
+ * @returns {SchemaBuilder}
293
+ */
294
+ function createSchemaBuilder(schema, baseMeta, z) {
295
+ return new Proxy(new SchemaBuilder(schema, baseMeta, z), handler);
296
+ }
297
+
54
298
  /**
55
299
  * Create schema helpers bound to a Zod instance
56
300
  * @param {typeof import('zod').z} z - Zod instance
@@ -70,45 +314,55 @@ function createSchemaHelpers(z) {
70
314
  const helpers = {
71
315
  /**
72
316
  * Create a Zod object schema with database metadata
73
- * @param {Object} shape - Object shape with zdb fields
317
+ * Automatically finalizes SchemaBuilder instances in the shape
318
+ * @param {Object} shape - Object shape with zdb fields (can be SchemaBuilder instances)
74
319
  * @returns {import('zod').ZodObject}
75
320
  */
76
321
  schema(shape) {
77
- return z.object(shape);
322
+ const finalizedShape = {};
323
+ for (const [key, value] of Object.entries(shape)) {
324
+ // If it's a SchemaBuilder, finalize it
325
+ if (value && typeof value._finalize === 'function') {
326
+ finalizedShape[key] = value._finalize();
327
+ } else {
328
+ finalizedShape[key] = value;
329
+ }
330
+ }
331
+ return z.object(finalizedShape);
78
332
  },
79
333
  /**
80
334
  * Primary key column (bigint, auto-increment)
81
335
  * @param {Partial<import('./types').ColumnMeta>} [options={}]
82
- * @returns {import('zod').ZodNumber}
336
+ * @returns {SchemaBuilder}
83
337
  */
84
338
  id(options = {}) {
85
339
  const schema = z.number().int().positive().optional();
86
- return withMeta(schema, {
340
+ return createSchemaBuilder(schema, {
87
341
  type: 'bigint',
88
342
  primary: true,
89
343
  autoIncrement: true,
90
344
  ...options,
91
- });
345
+ }, z);
92
346
  },
93
347
 
94
348
  /**
95
349
  * UUID primary key column
96
350
  * @param {Partial<import('./types').ColumnMeta>} [options={}]
97
- * @returns {import('zod').ZodString}
351
+ * @returns {SchemaBuilder}
98
352
  */
99
353
  uuid(options = {}) {
100
354
  const schema = z.string().uuid().optional();
101
- return withMeta(schema, {
355
+ return createSchemaBuilder(schema, {
102
356
  type: 'uuid',
103
357
  primary: true,
104
358
  ...options,
105
- });
359
+ }, z);
106
360
  },
107
361
 
108
362
  /**
109
363
  * String column (varchar)
110
364
  * @param {Partial<import('./types').ColumnMeta>} [options={}]
111
- * @returns {import('zod').ZodString}
365
+ * @returns {SchemaBuilder}
112
366
  */
113
367
  string(options = {}) {
114
368
  const { maxLength = 255, nullable = false, ...rest } = options;
@@ -116,18 +370,18 @@ function createSchemaHelpers(z) {
116
370
  if (nullable) {
117
371
  schema = schema.nullable().optional();
118
372
  }
119
- return withMeta(schema, {
373
+ return createSchemaBuilder(schema, {
120
374
  type: 'string',
121
375
  maxLength,
122
376
  nullable,
123
377
  ...rest,
124
- });
378
+ }, z);
125
379
  },
126
380
 
127
381
  /**
128
382
  * Text column (unlimited length)
129
383
  * @param {Partial<import('./types').ColumnMeta>} [options={}]
130
- * @returns {import('zod').ZodString}
384
+ * @returns {SchemaBuilder}
131
385
  */
132
386
  text(options = {}) {
133
387
  const { nullable = false, ...rest } = options;
@@ -135,17 +389,17 @@ function createSchemaHelpers(z) {
135
389
  if (nullable) {
136
390
  schema = schema.nullable().optional();
137
391
  }
138
- return withMeta(schema, {
392
+ return createSchemaBuilder(schema, {
139
393
  type: 'text',
140
394
  nullable,
141
395
  ...rest,
142
- });
396
+ }, z);
143
397
  },
144
398
 
145
399
  /**
146
400
  * Integer column
147
401
  * @param {Partial<import('./types').ColumnMeta>} [options={}]
148
- * @returns {import('zod').ZodNumber}
402
+ * @returns {SchemaBuilder}
149
403
  */
150
404
  integer(options = {}) {
151
405
  const { nullable = false, ...rest } = options;
@@ -153,17 +407,17 @@ function createSchemaHelpers(z) {
153
407
  if (nullable) {
154
408
  schema = schema.nullable().optional();
155
409
  }
156
- return withMeta(schema, {
410
+ return createSchemaBuilder(schema, {
157
411
  type: 'integer',
158
412
  nullable,
159
413
  ...rest,
160
- });
414
+ }, z);
161
415
  },
162
416
 
163
417
  /**
164
418
  * Big integer column
165
419
  * @param {Partial<import('./types').ColumnMeta>} [options={}]
166
- * @returns {import('zod').ZodNumber}
420
+ * @returns {SchemaBuilder}
167
421
  */
168
422
  bigint(options = {}) {
169
423
  const { nullable = false, ...rest } = options;
@@ -171,17 +425,17 @@ function createSchemaHelpers(z) {
171
425
  if (nullable) {
172
426
  schema = schema.nullable().optional();
173
427
  }
174
- return withMeta(schema, {
428
+ return createSchemaBuilder(schema, {
175
429
  type: 'bigint',
176
430
  nullable,
177
431
  ...rest,
178
- });
432
+ }, z);
179
433
  },
180
434
 
181
435
  /**
182
436
  * Float column
183
437
  * @param {Partial<import('./types').ColumnMeta>} [options={}]
184
- * @returns {import('zod').ZodNumber}
438
+ * @returns {SchemaBuilder}
185
439
  */
186
440
  float(options = {}) {
187
441
  const { nullable = false, ...rest } = options;
@@ -189,17 +443,17 @@ function createSchemaHelpers(z) {
189
443
  if (nullable) {
190
444
  schema = schema.nullable().optional();
191
445
  }
192
- return withMeta(schema, {
446
+ return createSchemaBuilder(schema, {
193
447
  type: 'float',
194
448
  nullable,
195
449
  ...rest,
196
- });
450
+ }, z);
197
451
  },
198
452
 
199
453
  /**
200
454
  * Decimal column
201
455
  * @param {Partial<import('./types').ColumnMeta>} [options={}]
202
- * @returns {import('zod').ZodNumber}
456
+ * @returns {SchemaBuilder}
203
457
  */
204
458
  decimal(options = {}) {
205
459
  const { precision = 10, scale = 2, nullable = false, ...rest } = options;
@@ -207,19 +461,19 @@ function createSchemaHelpers(z) {
207
461
  if (nullable) {
208
462
  schema = schema.nullable().optional();
209
463
  }
210
- return withMeta(schema, {
464
+ return createSchemaBuilder(schema, {
211
465
  type: 'decimal',
212
466
  precision,
213
467
  scale,
214
468
  nullable,
215
469
  ...rest,
216
- });
470
+ }, z);
217
471
  },
218
472
 
219
473
  /**
220
474
  * Boolean column
221
475
  * @param {Partial<import('./types').ColumnMeta>} [options={}]
222
- * @returns {import('zod').ZodBoolean}
476
+ * @returns {SchemaBuilder}
223
477
  */
224
478
  boolean(options = {}) {
225
479
  const { nullable = false, default: defaultValue, ...rest } = options;
@@ -230,18 +484,18 @@ function createSchemaHelpers(z) {
230
484
  if (nullable) {
231
485
  schema = schema.nullable().optional();
232
486
  }
233
- return withMeta(schema, {
487
+ return createSchemaBuilder(schema, {
234
488
  type: 'boolean',
235
489
  nullable,
236
490
  default: defaultValue,
237
491
  ...rest,
238
- });
492
+ }, z);
239
493
  },
240
494
 
241
495
  /**
242
496
  * Date column (date only, no time)
243
497
  * @param {Partial<import('./types').ColumnMeta>} [options={}]
244
- * @returns {import('zod').ZodDate}
498
+ * @returns {SchemaBuilder}
245
499
  */
246
500
  date(options = {}) {
247
501
  const { nullable = false, ...rest } = options;
@@ -249,17 +503,17 @@ function createSchemaHelpers(z) {
249
503
  if (nullable) {
250
504
  schema = schema.nullable().optional();
251
505
  }
252
- return withMeta(schema, {
506
+ return createSchemaBuilder(schema, {
253
507
  type: 'date',
254
508
  nullable,
255
509
  ...rest,
256
- });
510
+ }, z);
257
511
  },
258
512
 
259
513
  /**
260
514
  * Datetime column
261
515
  * @param {Partial<import('./types').ColumnMeta>} [options={}]
262
- * @returns {import('zod').ZodDate}
516
+ * @returns {SchemaBuilder}
263
517
  */
264
518
  datetime(options = {}) {
265
519
  const { nullable = false, ...rest } = options;
@@ -267,17 +521,17 @@ function createSchemaHelpers(z) {
267
521
  if (nullable) {
268
522
  schema = schema.nullable().optional();
269
523
  }
270
- return withMeta(schema, {
524
+ return createSchemaBuilder(schema, {
271
525
  type: 'datetime',
272
526
  nullable,
273
527
  ...rest,
274
- });
528
+ }, z);
275
529
  },
276
530
 
277
531
  /**
278
532
  * Timestamp column (with optional auto behavior)
279
533
  * @param {Partial<import('./types').ColumnMeta>} [options={}]
280
- * @returns {import('zod').ZodDate}
534
+ * @returns {SchemaBuilder}
281
535
  */
282
536
  timestamp(options = {}) {
283
537
  const { nullable = false, auto, ...rest } = options;
@@ -286,18 +540,18 @@ function createSchemaHelpers(z) {
286
540
  if (nullable || auto) {
287
541
  schema = schema.nullable().optional();
288
542
  }
289
- return withMeta(schema, {
543
+ return createSchemaBuilder(schema, {
290
544
  type: 'timestamp',
291
545
  nullable: nullable || !!auto,
292
546
  auto,
293
547
  ...rest,
294
- });
548
+ }, z);
295
549
  },
296
550
 
297
551
  /**
298
552
  * JSON column
299
553
  * @param {Partial<import('./types').ColumnMeta>} [options={}]
300
- * @returns {import('zod').ZodUnknown}
554
+ * @returns {SchemaBuilder}
301
555
  */
302
556
  json(options = {}) {
303
557
  const { nullable = false, ...rest } = options;
@@ -305,18 +559,18 @@ function createSchemaHelpers(z) {
305
559
  if (nullable) {
306
560
  schema = schema.nullable().optional();
307
561
  }
308
- return withMeta(schema, {
562
+ return createSchemaBuilder(schema, {
309
563
  type: 'json',
310
564
  nullable,
311
565
  ...rest,
312
- });
566
+ }, z);
313
567
  },
314
568
 
315
569
  /**
316
570
  * Array column (stored as JSON in database)
317
571
  * @param {import('zod').ZodTypeAny} [itemSchema] - Schema for array items (default: z.any())
318
572
  * @param {Partial<import('./types').ColumnMeta>} [options={}]
319
- * @returns {import('zod').ZodArray}
573
+ * @returns {SchemaBuilder}
320
574
  */
321
575
  array(itemSchema, options = {}) {
322
576
  // If first argument is options object (backward compatibility)
@@ -333,18 +587,18 @@ function createSchemaHelpers(z) {
333
587
  schema = schema.nullable().optional();
334
588
  }
335
589
 
336
- return withMeta(schema, {
590
+ return createSchemaBuilder(schema, {
337
591
  type: 'array',
338
592
  nullable,
339
593
  ...rest,
340
- });
594
+ }, z);
341
595
  },
342
596
 
343
597
  /**
344
598
  * Enum column
345
599
  * @param {string[]} values - Allowed enum values
346
600
  * @param {Partial<import('./types').ColumnMeta>} [options={}]
347
- * @returns {import('zod').ZodEnum}
601
+ * @returns {SchemaBuilder}
348
602
  */
349
603
  enum(values, options = {}) {
350
604
  const { nullable = false, default: defaultValue, ...rest } = options;
@@ -355,20 +609,20 @@ function createSchemaHelpers(z) {
355
609
  if (nullable) {
356
610
  schema = schema.nullable().optional();
357
611
  }
358
- return withMeta(schema, {
612
+ return createSchemaBuilder(schema, {
359
613
  type: 'enum',
360
614
  enumValues: values,
361
615
  nullable,
362
616
  default: defaultValue,
363
617
  ...rest,
364
- });
618
+ }, z);
365
619
  },
366
620
 
367
621
  /**
368
622
  * Foreign key column (references another table)
369
623
  * @param {string} references - Referenced table name
370
624
  * @param {Partial<import('./types').ColumnMeta>} [options={}]
371
- * @returns {import('zod').ZodNumber}
625
+ * @returns {SchemaBuilder}
372
626
  */
373
627
  foreignKey(references, options = {}) {
374
628
  const { referenceColumn = 'id', nullable = false, ...rest } = options;
@@ -376,20 +630,20 @@ function createSchemaHelpers(z) {
376
630
  if (nullable) {
377
631
  schema = schema.nullable().optional();
378
632
  }
379
- return withMeta(schema, {
633
+ return createSchemaBuilder(schema, {
380
634
  type: 'bigint',
381
635
  references,
382
636
  referenceColumn,
383
637
  nullable,
384
638
  ...rest,
385
- });
639
+ }, z);
386
640
  },
387
641
 
388
642
  /**
389
643
  * UUID foreign key column
390
644
  * @param {string} references - Referenced table name
391
645
  * @param {Partial<import('./types').ColumnMeta>} [options={}]
392
- * @returns {import('zod').ZodString}
646
+ * @returns {SchemaBuilder}
393
647
  */
394
648
  foreignUuid(references, options = {}) {
395
649
  const { referenceColumn = 'id', nullable = false, ...rest } = options;
@@ -397,13 +651,13 @@ function createSchemaHelpers(z) {
397
651
  if (nullable) {
398
652
  schema = schema.nullable().optional();
399
653
  }
400
- return withMeta(schema, {
654
+ return createSchemaBuilder(schema, {
401
655
  type: 'uuid',
402
656
  references,
403
657
  referenceColumn,
404
658
  nullable,
405
659
  ...rest,
406
- });
660
+ }, z);
407
661
  },
408
662
  };
409
663
 
package/core/orm/types.js CHANGED
@@ -12,6 +12,38 @@
12
12
  * @typedef {'id'|'string'|'text'|'integer'|'bigint'|'float'|'decimal'|'boolean'|'date'|'datetime'|'timestamp'|'json'|'array'|'enum'|'uuid'} ColumnType
13
13
  */
14
14
 
15
+ /**
16
+ * @typedef {Object} ValidationMeta
17
+ * @property {number} [min] - Minimum value/length
18
+ * @property {number} [max] - Maximum value/length
19
+ * @property {number} [minLength] - Minimum length (for strings/arrays)
20
+ * @property {number} [maxLength] - Maximum length (for strings/arrays)
21
+ * @property {number} [length] - Exact length
22
+ * @property {string} [pattern] - Regex pattern (as string)
23
+ * @property {boolean} [email] - Must be valid email
24
+ * @property {boolean} [url] - Must be valid URL
25
+ * @property {boolean} [positive] - Must be positive number
26
+ * @property {boolean} [negative] - Must be negative number
27
+ * @property {boolean} [int] - Must be integer
28
+ * @property {number} [step] - Step value for numbers
29
+ * @property {boolean} [nonempty] - Cannot be empty
30
+ * @property {string} [includes] - String must include this
31
+ * @property {string} [startsWith] - String must start with this
32
+ * @property {string} [endsWith] - String must end with this
33
+ */
34
+
35
+ /**
36
+ * @typedef {Object} UIMeta
37
+ * @property {string} [label] - Form label text
38
+ * @property {string} [placeholder] - Input placeholder
39
+ * @property {string} [hint] - Help text shown below input
40
+ * @property {string} [inputType] - HTML input type (email, tel, url, currency, etc.)
41
+ * @property {boolean} [hidden] - Hide field in forms
42
+ * @property {boolean} [readonly] - Make field read-only
43
+ * @property {string} [width] - Input width (half, third, full)
44
+ * @property {number} [rows] - Number of rows for textarea
45
+ */
46
+
15
47
  /**
16
48
  * @typedef {Object} ColumnMeta
17
49
  * @property {ColumnType} type - Database column type
@@ -28,6 +60,8 @@
28
60
  * @property {string} [references] - Referenced table for foreign keys
29
61
  * @property {string} [referenceColumn='id'] - Referenced column name
30
62
  * @property {'create'|'update'} [auto] - Auto-set on create or update (for timestamps)
63
+ * @property {ValidationMeta} [validations] - Validation rules
64
+ * @property {UIMeta} [ui] - UI metadata for admin panel
31
65
  */
32
66
 
33
67
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "webspresso",
3
- "version": "0.0.21",
3
+ "version": "0.0.22",
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
  "bin": {