@webjsdev/cli 0.10.35 → 0.10.37

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 (65) hide show
  1. package/lib/create.js +18 -11
  2. package/lib/doctor.js +1 -1
  3. package/lib/saas-template.js +27 -7
  4. package/package.json +1 -1
  5. package/templates/.agents/rules/workflow.md +1 -1
  6. package/templates/.github/copilot-instructions.md +1 -1
  7. package/templates/AGENTS.md +7 -6
  8. package/templates/CONVENTIONS.md +4 -4
  9. package/templates/gallery/app/apple-icon.ts +17 -0
  10. package/templates/gallery/app/features/async-render/page.ts +11 -1
  11. package/templates/gallery/app/features/boundaries/crash/page.ts +7 -0
  12. package/templates/gallery/app/features/boundaries/error.ts +16 -0
  13. package/templates/gallery/app/features/boundaries/gated/forbidden.ts +18 -0
  14. package/templates/gallery/app/features/boundaries/gated/page.ts +10 -0
  15. package/templates/gallery/app/features/boundaries/loading.ts +15 -0
  16. package/templates/gallery/app/features/boundaries/not-found.ts +17 -0
  17. package/templates/gallery/app/features/boundaries/page.ts +77 -0
  18. package/templates/gallery/app/features/boundaries/private/page.ts +10 -0
  19. package/templates/gallery/app/features/boundaries/private/unauthorized.ts +17 -0
  20. package/templates/gallery/app/features/caching/page.ts +9 -0
  21. package/templates/gallery/app/features/client-router/page.ts +3 -0
  22. package/templates/gallery/app/features/components/page.ts +11 -0
  23. package/templates/gallery/app/features/file-storage/file/[key]/route.ts +10 -1
  24. package/templates/gallery/app/features/file-storage/page.ts +2 -2
  25. package/templates/gallery/app/features/forms/page.ts +1 -1
  26. package/templates/gallery/app/features/rate-limit/page.ts +1 -1
  27. package/templates/gallery/app/features/route-handler/data/route.ts +34 -5
  28. package/templates/gallery/app/features/route-handler/page.ts +5 -2
  29. package/templates/gallery/app/features/routing/[id]/page.ts +13 -1
  30. package/templates/gallery/app/features/routing/legacy/page.ts +12 -0
  31. package/templates/gallery/app/features/routing/page.ts +9 -0
  32. package/templates/gallery/app/features/sessions/count/route.ts +12 -0
  33. package/templates/gallery/app/features/sessions/middleware.ts +7 -0
  34. package/templates/gallery/app/features/sessions/page.ts +19 -0
  35. package/templates/gallery/app/global-error.ts +46 -0
  36. package/templates/gallery/app/global-not-found.ts +22 -0
  37. package/templates/gallery/app/icon.ts +19 -0
  38. package/templates/gallery/app/manifest.ts +22 -0
  39. package/templates/gallery/app/opengraph-image.ts +20 -0
  40. package/templates/gallery/app/robots.ts +16 -0
  41. package/templates/gallery/app/sitemap.ts +23 -0
  42. package/templates/gallery/app/sitemaps/route.ts +17 -0
  43. package/templates/gallery/app/twitter-image.ts +19 -0
  44. package/templates/gallery/modules/caching/actions/bust-caches.server.ts +25 -0
  45. package/templates/gallery/modules/caching/components/cache-buster.ts +30 -0
  46. package/templates/gallery/modules/client-router/components/router-controls.ts +52 -0
  47. package/templates/gallery/modules/components/components/browser/counter-card.test.js +21 -1
  48. package/templates/gallery/modules/components/components/counter-card.ts +1 -1
  49. package/templates/gallery/modules/components/components/reactive-meter.ts +64 -0
  50. package/templates/gallery/modules/components/components/server-render.test.ts +20 -0
  51. package/templates/gallery/modules/components/components/task-loader.ts +48 -0
  52. package/templates/gallery/modules/components/components/theme-context.ts +67 -0
  53. package/templates/gallery/modules/directives/components/directive-demo.ts +162 -20
  54. package/templates/gallery/modules/file-storage/actions/store-upload.server.ts +3 -0
  55. package/templates/gallery/modules/file-storage/store.server.ts +29 -0
  56. package/templates/gallery/modules/rate-limit/components/rate-probe.ts +1 -1
  57. package/templates/gallery/modules/route-handler/components/rich-data.ts +31 -0
  58. package/templates/gallery/modules/server-actions/actions/greet.server.ts +9 -1
  59. package/templates/gallery/modules/server-actions/actions/greet.test.ts +37 -0
  60. package/templates/gallery/modules/server-actions/components/greeter.ts +1 -1
  61. package/templates/gallery/modules/sessions/session-config.server.ts +31 -0
  62. package/templates/gallery/modules/todo/components/todo-app.ts +5 -5
  63. package/templates/gallery/modules/websockets/components/ws-echo.ts +19 -1
  64. package/templates/gallery/modules/websockets/echo.test.ts +21 -0
  65. package/templates/instrumentation.ts +16 -0
@@ -82,7 +82,7 @@ export class TodoApp extends WebComponent({
82
82
  </span>
83
83
  <div class="grid gap-0.5">
84
84
  <h2 class="m-0 text-[1.4rem] font-bold tracking-[-0.02em] leading-none text-foreground">Tasks</h2>
85
- <p class="m-0 text-[13px] text-muted-foreground/70 leading-none">${done} of ${list.length} done</p>
85
+ <p class="m-0 text-[13px] text-muted-foreground leading-none">${done} of ${list.length} done</p>
86
86
  </div>
87
87
  <span class="ml-auto text-[13px] font-semibold tabular-nums text-muted-foreground">${pct}%</span>
88
88
  </div>
@@ -97,7 +97,7 @@ export class TodoApp extends WebComponent({
97
97
  class="flex items-center gap-2 p-2 pl-4 rounded-2xl bg-card border border-border shadow-[0_1px_0_0_color-mix(in_oklch,var(--foreground)_5%,transparent)]">
98
98
  <input type="hidden" name="intent" value="create" />
99
99
  <input name="title" required maxlength="280" autocomplete="off" placeholder="What needs doing?"
100
- class="flex-1 min-w-0 bg-transparent border-0 outline-none text-foreground text-[15px] placeholder:text-muted-foreground/70 py-1.5" />
100
+ class="flex-1 min-w-0 bg-transparent border-0 outline-none text-foreground text-[15px] placeholder:text-muted-foreground py-1.5" />
101
101
  <button type="submit"
102
102
  class="shrink-0 px-4 py-2 rounded-xl bg-primary text-primary-foreground font-semibold text-sm border-0 cursor-pointer transition-all hover:bg-primary/90 active:scale-[0.97]">Add</button>
103
103
  </form>
@@ -120,17 +120,17 @@ export class TodoApp extends WebComponent({
120
120
  </button>
121
121
  <!-- The title is a <label for> the toggle: clicking the text toggles
122
122
  the task (works on JS and no-JS paths, and screen readers). -->
123
- <label for="t-${todo.id}" class="flex-1 min-w-0 text-[15px] leading-snug break-words cursor-pointer select-none ${todo.completed ? 'line-through text-muted-foreground/70' : 'text-foreground'}">${todo.title}</label>
123
+ <label for="t-${todo.id}" class="flex-1 min-w-0 text-[15px] leading-snug break-words cursor-pointer select-none ${todo.completed ? 'line-through text-muted-foreground' : 'text-foreground'}">${todo.title}</label>
124
124
  <!-- Delete: a proper icon button, revealed on row hover / focus. -->
125
125
  <button type="submit" name="intent" value="delete" aria-label="Delete task"
126
126
  @click=${(e: Event) => this.removeTodo(e, todo)}
127
- class="shrink-0 grid place-items-center w-7 h-7 rounded-lg border-0 bg-transparent text-muted-foreground/70 cursor-pointer opacity-0 group-hover:opacity-100 focus:opacity-100 transition-all hover:text-destructive hover:bg-[color-mix(in_oklch,var(--color-destructive)_12%,transparent)]">
127
+ class="shrink-0 grid place-items-center w-7 h-7 rounded-lg border-0 bg-transparent text-muted-foreground cursor-pointer opacity-0 group-hover:opacity-100 focus:opacity-100 transition-all hover:text-destructive hover:bg-[color-mix(in_oklch,var(--color-destructive)_12%,transparent)]">
128
128
  <svg viewBox="0 0 24 24" class="w-4 h-4 stroke-current fill-none" style="stroke-width:2;stroke-linecap:round;stroke-linejoin:round"><path d="M18 6 6 18M6 6l12 12"/></svg>
129
129
  </button>
130
130
  </form>
131
131
  </li>
132
132
  `) : html`
133
- <li class="text-center text-muted-foreground/70 text-sm py-14 border border-dashed border-border rounded-2xl">No tasks yet. Add your first one above.</li>
133
+ <li class="text-center text-muted-foreground text-sm py-14 border border-dashed border-border rounded-2xl">No tasks yet. Add your first one above.</li>
134
134
  `}
135
135
  </ul>
136
136
  </section>
@@ -4,7 +4,7 @@
4
4
  // it never runs during SSR) and closed in disconnectedCallback. At SSR the
5
5
  // component renders its disconnected state, so with JS off the page still reads
6
6
  // (a live socket has no no-JS equivalent, which is the honest fallback here).
7
- import { WebComponent, signal, html, connectWS } from '@webjsdev/core';
7
+ import { WebComponent, signal, html, connectWS, renderStream } from '@webjsdev/core';
8
8
 
9
9
  export class WsEcho extends WebComponent {
10
10
  private connected = signal(false);
@@ -36,6 +36,19 @@ export class WsEcho extends WebComponent {
36
36
  input.value = '';
37
37
  }
38
38
 
39
+ #streamN = 0;
40
+ // renderStream() applies a <webjs-stream> payload with native DOM methods:
41
+ // the SAME element-level applier a connectWS onMessage handler (or a
42
+ // broadcast() push) uses for surgical live updates, so a chat / presence /
43
+ // toast reuses it instead of hand-written DOM code. Here a button drives it
44
+ // locally; over a channel the server would send this HTML string.
45
+ private applyStreamUpdate() {
46
+ this.#streamN += 1;
47
+ renderStream(
48
+ `<webjs-stream action="append" target="ws-stream-log"><template><li class="px-3 py-2 rounded-xl bg-card border border-border text-[15px] text-foreground">streamed row #${this.#streamN}</li></template></webjs-stream>`,
49
+ );
50
+ }
51
+
39
52
  render() {
40
53
  const on = this.connected.get();
41
54
  return html`
@@ -55,6 +68,11 @@ export class WsEcho extends WebComponent {
55
68
  <li class="px-3 py-2 rounded-xl bg-card border border-border text-[15px] text-foreground font-mono">${line}</li>
56
69
  `)}
57
70
  </ul>
71
+ <div class="grid gap-2 border-t border-border pt-4">
72
+ <button @click=${() => this.applyStreamUpdate()}
73
+ class="w-fit px-3.5 py-1.5 rounded-xl bg-card border border-border text-foreground text-sm cursor-pointer transition-colors hover:border-border-strong">renderStream() an element-level update</button>
74
+ <ul id="ws-stream-log" class="grid gap-1.5 list-none m-0 p-0"></ul>
75
+ </div>
58
76
  </div>
59
77
  `;
60
78
  }
@@ -0,0 +1,21 @@
1
+ // Example node test for booting the app in-process (from @webjsdev/server).
2
+ // startServer({ appDir, port }) boots the whole app and resolves to
3
+ // { server, close } (the same entry `webjs start` uses; port 0 picks a free
4
+ // port, so a test never collides). Close it in a finally so the port is
5
+ // released. This is the realistic way to smoke-test that the app boots and
6
+ // listens; for asserting on responses without a socket, use the handle()
7
+ // harness (see modules/server-actions/actions/greet.test.ts and the docs).
8
+ import { test } from 'node:test';
9
+ import assert from 'node:assert/strict';
10
+ import { startServer } from '@webjsdev/server';
11
+
12
+ const appDir = process.cwd();
13
+
14
+ test('startServer boots the app in-process on an ephemeral port', async () => {
15
+ const { server, close } = await startServer({ appDir, dev: true, port: 0 });
16
+ try {
17
+ assert.ok(server.address(), 'the server is listening');
18
+ } finally {
19
+ await close();
20
+ }
21
+ });
@@ -0,0 +1,16 @@
1
+ // Optional boot-time hook (app root, sibling of app/). register() runs once at
2
+ // server start, the place to wire APM / logging / tracing. setOnError(fn) (from
3
+ // @webjsdev/server) registers a sink for every request error the framework
4
+ // catches (an SSR render crash, a thrown server action, a 500), so you can
5
+ // forward it to Sentry / a logger with the request context. Call setOnError
6
+ // INSIDE register() so it runs within the instrumentation context (a top-level
7
+ // call has no context yet and is a no-op). Delete this file if you do not need
8
+ // the hook.
9
+ import { setOnError } from '@webjsdev/server';
10
+
11
+ export function register() {
12
+ setOnError((error, ctx) => {
13
+ // Replace with your APM. `ctx` carries request context (e.g. a correlation id).
14
+ console.error('[instrumentation] request error:', error, ctx ?? '');
15
+ });
16
+ }