webspresso 0.0.112 → 0.0.114

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +26 -0
  2. package/bin/commands/admin-password.js +1 -7
  3. package/bin/commands/api.js +1 -7
  4. package/bin/commands/new.js +1 -7
  5. package/bin/commands/page.js +1 -7
  6. package/bin/commands/seed.js +1 -7
  7. package/bin/commands/service.js +1 -7
  8. package/bin/utils/prompt.js +17 -0
  9. package/core/cache/tagged-lru.js +9 -3
  10. package/core/client/adapter/datastar.js +602 -26
  11. package/core/client/api.js +151 -7
  12. package/core/client/controller.js +172 -11
  13. package/core/client/devtool.js +59 -33
  14. package/core/client/forms.js +479 -29
  15. package/core/client/fragments.js +456 -27
  16. package/core/client/index.js +18 -1
  17. package/core/client/server-fragments.js +125 -40
  18. package/core/client/utils.js +72 -17
  19. package/core/console/commands.js +26 -5
  20. package/core/console/repl.js +7 -1
  21. package/core/http/pipeline.js +82 -1
  22. package/core/openapi/build-from-api-routes.js +5 -5
  23. package/core/orm/json-fields.js +7 -6
  24. package/core/orm/schema-helpers.js +18 -15
  25. package/core/orm/utils/nanoid.js +7 -8
  26. package/core/plugins/plugin-manager.js +155 -1
  27. package/core/services/validator.js +12 -1
  28. package/core/validation/apply.js +3 -0
  29. package/core/validation/index.js +25 -0
  30. package/core/validation/zod-introspect.js +187 -0
  31. package/index.d.ts +113 -1
  32. package/package.json +12 -11
  33. package/plugins/mcp/providers/services.js +2 -2
  34. package/src/api/define-api.js +2 -0
  35. package/src/api/validation-i18n.js +6 -2
  36. package/src/bundle/index.js +140 -6
  37. package/src/dev/api-typegen.js +79 -22
  38. package/src/dev/typegen.js +61 -35
  39. package/src/dev-reload/index.js +44 -2
  40. package/src/file-router.js +24 -6
  41. package/src/routing/mount-discovered-routes.js +24 -6
  42. package/src/server.js +59 -3
package/README.md CHANGED
@@ -153,6 +153,31 @@ app.listen(PORT, () => {
153
153
  });
154
154
  ```
155
155
 
156
+ ### 5. Client Behavior Controller (`pages/notes.client.ts`)
157
+
158
+ Co-locate reactive client logic right next to `pages/notes.njk` (`<div data-w-controller="notes">`) with automatic bundling and server-import boundary protection:
159
+
160
+ ```typescript
161
+ import { defineController } from 'webspresso/client';
162
+
163
+ export default defineController({
164
+ state: () => ({ title: '', saving: false }),
165
+ actions: {
166
+ async create(ctx) {
167
+ if (!ctx.state.title.trim()) return;
168
+ ctx.state.saving = true;
169
+ try {
170
+ await ctx.api.post('/api/notes', { title: ctx.state.title });
171
+ ctx.state.title = '';
172
+ await ctx.fragments.invalidate('notes'); // Refresh SSR fragments tagged with "notes"
173
+ } finally {
174
+ ctx.state.saving = false;
175
+ }
176
+ },
177
+ },
178
+ });
179
+ ```
180
+
156
181
  ---
157
182
 
158
183
  ## ⚡ High-Performance Architecture (Cold Start & AOT)
@@ -202,6 +227,7 @@ Detailed topic guides are available in the repository:
202
227
 
203
228
  - **[Installation & First App](docs/getting-started/installation.md)** — Node.js requirements, setup, and first steps.
204
229
  - **[File-Based Routing & API Routes](.agents/ROUTING.md)** — Routing conventions, parameters, and cascading layouts.
230
+ - **[Client Behavior & SSR Fragments](docs/guides/client-behavior.md)** — `defineController`, `defineFragment`, forms, and auto-revalidation.
205
231
  - **[SSR Streaming & Chunked Transfer](.agents/STREAMING.md)** — Out-of-order slots, `_loading.njk` skeletons, and TTFB optimization.
206
232
  - **[Database & ORM Guide](.agents/ORM.md)** — Repositories, relations, soft deletes, and ambient transactions.
207
233
  - **[Dual Authentication System](.agents/AUTH.md)** — Sessions, JWT rotation, magic links, and device management.
@@ -3,13 +3,7 @@
3
3
  * Reset admin user password via CLI
4
4
  */
5
5
 
6
- let _inquirer;
7
- const inquirer = {
8
- prompt: (...args) => {
9
- if (!_inquirer) _inquirer = require('inquirer');
10
- return _inquirer.prompt(...args);
11
- },
12
- };
6
+ const inquirer = require('../utils/prompt');
13
7
  const { loadDbConfig, createDbInstance } = require('../utils/db');
14
8
 
15
9
  function registerCommand(program) {
@@ -6,13 +6,7 @@
6
6
  * @module bin/commands/api
7
7
  */
8
8
 
9
- let _inquirer;
10
- const inquirer = {
11
- prompt: (...args) => {
12
- if (!_inquirer) _inquirer = require('inquirer');
13
- return _inquirer.prompt(...args);
14
- },
15
- };
9
+ const inquirer = require('../utils/prompt');
16
10
  const fs = require('fs');
17
11
  const path = require('path');
18
12
 
@@ -3,13 +3,7 @@
3
3
  * Creates a new Webspresso project
4
4
  */
5
5
 
6
- let _inquirer;
7
- const inquirer = {
8
- prompt: (...args) => {
9
- if (!_inquirer) _inquirer = require('inquirer');
10
- return _inquirer.prompt(...args);
11
- },
12
- };
6
+ const inquirer = require('../utils/prompt');
13
7
  const fs = require('fs');
14
8
  const path = require('path');
15
9
 
@@ -6,13 +6,7 @@
6
6
  * @module bin/commands/page
7
7
  */
8
8
 
9
- let _inquirer;
10
- const inquirer = {
11
- prompt: (...args) => {
12
- if (!_inquirer) _inquirer = require('inquirer');
13
- return _inquirer.prompt(...args);
14
- },
15
- };
9
+ const inquirer = require('../utils/prompt');
16
10
  const fs = require('fs');
17
11
  const path = require('path');
18
12
 
@@ -3,13 +3,7 @@
3
3
  * Run database seeders to populate database with fake data
4
4
  */
5
5
 
6
- let _inquirer;
7
- const inquirer = {
8
- prompt: (...args) => {
9
- if (!_inquirer) _inquirer = require('inquirer');
10
- return _inquirer.prompt(...args);
11
- },
12
- };
6
+ const inquirer = require('../utils/prompt');
13
7
  const fs = require('fs');
14
8
  const path = require('path');
15
9
  const { execSync } = require('child_process');
@@ -5,13 +5,7 @@
5
5
  */
6
6
 
7
7
  const path = require('path');
8
- let _inquirer;
9
- const inquirer = {
10
- prompt: (...args) => {
11
- if (!_inquirer) _inquirer = require('inquirer');
12
- return _inquirer.prompt(...args);
13
- },
14
- };
8
+ const inquirer = require('../utils/prompt');
15
9
  const { resolveDbConfigIfExists, createDbInstance } = require('../utils/db');
16
10
 
17
11
  /**
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Shared lazy ESM loader for Inquirer v13+ in CommonJS CLI commands.
3
+ */
4
+ let _inquirer;
5
+
6
+ const inquirer = {
7
+ async prompt(...args) {
8
+ if (!_inquirer) {
9
+ const mod = await import('inquirer');
10
+ _inquirer = mod.default || mod;
11
+ }
12
+ return _inquirer.prompt(...args);
13
+ },
14
+ };
15
+
16
+ module.exports = inquirer;
17
+
@@ -165,14 +165,18 @@ class TaggedLruCache {
165
165
  set(key, value, options = {}) {
166
166
  const tags = normalizeTags(options.tags);
167
167
 
168
- // If key already had tags, clean them up first before assigning new tags
168
+ // Clear previous tags before lru.set so stale tags never survive if lru.set rejects (e.g. > maxBytes)
169
169
  if (this.keyToTags.has(key)) {
170
170
  this._removeKeyFromTags(key);
171
171
  }
172
172
 
173
- this._addKeyToTags(key, tags);
173
+ // Run lru.set first so any internal _removeEntry('delete') on overwrite fires onEvict BEFORE we bind new tags
174
174
  this.lru.set(key, value, options);
175
175
 
176
+ if (tags.length > 0 && this.lru.has(key)) {
177
+ this._addKeyToTags(key, tags);
178
+ }
179
+
176
180
  return this;
177
181
  }
178
182
 
@@ -290,7 +294,9 @@ class TaggedLruCache {
290
294
  if (this.keyToTags.has(key)) {
291
295
  this._removeKeyFromTags(key);
292
296
  }
293
- this._addKeyToTags(key, tags);
297
+ if (tags.length > 0 && this.lru.has(key)) {
298
+ this._addKeyToTags(key, tags);
299
+ }
294
300
  }
295
301
  return val;
296
302
  }