zero-query 0.9.8 → 1.0.0

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 (99) hide show
  1. package/README.md +55 -31
  2. package/cli/args.js +1 -1
  3. package/cli/commands/build.js +2 -2
  4. package/cli/commands/bundle.js +15 -15
  5. package/cli/commands/create.js +41 -7
  6. package/cli/commands/dev/devtools/index.js +1 -1
  7. package/cli/commands/dev/devtools/js/core.js +14 -14
  8. package/cli/commands/dev/devtools/js/elements.js +4 -4
  9. package/cli/commands/dev/devtools/js/stats.js +1 -1
  10. package/cli/commands/dev/devtools/styles.css +2 -2
  11. package/cli/commands/dev/index.js +2 -2
  12. package/cli/commands/dev/logger.js +1 -1
  13. package/cli/commands/dev/overlay.js +21 -14
  14. package/cli/commands/dev/server.js +5 -5
  15. package/cli/commands/dev/validator.js +7 -7
  16. package/cli/commands/dev/watcher.js +6 -6
  17. package/cli/help.js +4 -2
  18. package/cli/index.js +2 -2
  19. package/cli/scaffold/default/app/app.js +17 -18
  20. package/cli/scaffold/default/app/components/about.js +9 -9
  21. package/cli/scaffold/default/app/components/api-demo.js +6 -6
  22. package/cli/scaffold/default/app/components/contact-card.js +4 -4
  23. package/cli/scaffold/default/app/components/contacts/contacts.css +2 -2
  24. package/cli/scaffold/default/app/components/contacts/contacts.html +3 -3
  25. package/cli/scaffold/default/app/components/contacts/contacts.js +11 -11
  26. package/cli/scaffold/default/app/components/counter.js +8 -8
  27. package/cli/scaffold/default/app/components/home.js +13 -13
  28. package/cli/scaffold/default/app/components/not-found.js +1 -1
  29. package/cli/scaffold/default/app/components/playground/playground.css +1 -1
  30. package/cli/scaffold/default/app/components/playground/playground.html +11 -11
  31. package/cli/scaffold/default/app/components/playground/playground.js +11 -11
  32. package/cli/scaffold/default/app/components/todos.js +8 -8
  33. package/cli/scaffold/default/app/components/toolkit/toolkit.css +1 -1
  34. package/cli/scaffold/default/app/components/toolkit/toolkit.html +4 -4
  35. package/cli/scaffold/default/app/components/toolkit/toolkit.js +7 -7
  36. package/cli/scaffold/default/app/routes.js +1 -1
  37. package/cli/scaffold/default/app/store.js +1 -1
  38. package/cli/scaffold/default/global.css +2 -2
  39. package/cli/scaffold/default/index.html +2 -2
  40. package/cli/scaffold/minimal/app/app.js +6 -7
  41. package/cli/scaffold/minimal/app/components/about.js +5 -5
  42. package/cli/scaffold/minimal/app/components/counter.js +6 -6
  43. package/cli/scaffold/minimal/app/components/home.js +8 -8
  44. package/cli/scaffold/minimal/app/components/not-found.js +1 -1
  45. package/cli/scaffold/minimal/app/routes.js +1 -1
  46. package/cli/scaffold/minimal/app/store.js +1 -1
  47. package/cli/scaffold/minimal/global.css +2 -2
  48. package/cli/scaffold/minimal/index.html +1 -1
  49. package/cli/scaffold/ssr/app/app.js +29 -0
  50. package/cli/scaffold/ssr/app/components/about.js +28 -0
  51. package/cli/scaffold/ssr/app/components/home.js +37 -0
  52. package/cli/scaffold/ssr/app/components/not-found.js +15 -0
  53. package/cli/scaffold/ssr/app/routes.js +6 -0
  54. package/cli/scaffold/ssr/global.css +113 -0
  55. package/cli/scaffold/ssr/index.html +31 -0
  56. package/cli/scaffold/ssr/package.json +8 -0
  57. package/cli/scaffold/ssr/server/index.js +118 -0
  58. package/cli/utils.js +6 -6
  59. package/dist/zquery.dist.zip +0 -0
  60. package/dist/zquery.js +565 -228
  61. package/dist/zquery.min.js +2 -2
  62. package/index.d.ts +25 -12
  63. package/index.js +11 -7
  64. package/package.json +9 -3
  65. package/src/component.js +64 -63
  66. package/src/core.js +15 -15
  67. package/src/diff.js +38 -38
  68. package/src/errors.js +72 -18
  69. package/src/expression.js +15 -17
  70. package/src/http.js +4 -4
  71. package/src/package.json +1 -0
  72. package/src/reactive.js +75 -9
  73. package/src/router.js +104 -24
  74. package/src/ssr.js +133 -39
  75. package/src/store.js +103 -21
  76. package/src/utils.js +64 -12
  77. package/tests/audit.test.js +143 -15
  78. package/tests/cli.test.js +20 -20
  79. package/tests/component.test.js +121 -121
  80. package/tests/core.test.js +56 -56
  81. package/tests/diff.test.js +42 -42
  82. package/tests/errors.test.js +425 -147
  83. package/tests/expression.test.js +58 -53
  84. package/tests/http.test.js +20 -20
  85. package/tests/reactive.test.js +185 -24
  86. package/tests/router.test.js +501 -74
  87. package/tests/ssr.test.js +444 -10
  88. package/tests/store.test.js +264 -23
  89. package/tests/utils.test.js +163 -26
  90. package/types/collection.d.ts +2 -2
  91. package/types/component.d.ts +5 -5
  92. package/types/errors.d.ts +36 -4
  93. package/types/http.d.ts +3 -3
  94. package/types/misc.d.ts +9 -9
  95. package/types/reactive.d.ts +25 -3
  96. package/types/router.d.ts +10 -6
  97. package/types/ssr.d.ts +22 -2
  98. package/types/store.d.ts +40 -5
  99. package/types/utils.d.ts +1 -1
@@ -1,11 +1,11 @@
1
- // home.js Landing page / dashboard
1
+ // home.js - Landing page / dashboard
2
2
  //
3
3
  // Features used:
4
- // $.component define a component with state + render
5
- // $.signal reactive value container
6
- // $.computed derived reactive value
7
- // $.effect side-effect that auto-tracks signals
8
- // $.getStore access the global store
4
+ // $.component - define a component with state + render
5
+ // $.signal - reactive value container
6
+ // $.computed - derived reactive value
7
+ // $.effect - side-effect that auto-tracks signals
8
+ // $.getStore - access the global store
9
9
 
10
10
  $.component('home-page', {
11
11
  styles: `
@@ -136,7 +136,7 @@ $.component('home-page', {
136
136
  render() {
137
137
  const store = $.getStore('main');
138
138
  const s = this.state;
139
- const opLabels = { add: '+', subtract: '', multiply: '×', divide: '÷', power: '^', modulo: '%' };
139
+ const opLabels = { add: '+', subtract: '-', multiply: '×', divide: '÷', power: '^', modulo: '%' };
140
140
  const maxH = Math.max(1, ...s.sigHistory.map(v => Math.sqrt(Math.abs(v) + 1)));
141
141
  return `
142
142
  <div class="page-header">
@@ -146,13 +146,13 @@ $.component('home-page', {
146
146
 
147
147
  <div class="card card-accent">
148
148
  <h3><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="var(--accent)" style="width:20px;height:20px;vertical-align:-4px;margin-right:0.25rem;"><path stroke-linecap="round" stroke-linejoin="round" d="m3.75 13.5 10.5-11.25L12 10.5h8.25L9.75 21.75 12 13.5H3.75Z"/></svg> Reactive Signals Lab</h3>
149
- <p style="margin-bottom:.65rem;">Signals are <strong>reactive primitives</strong> values that automatically notify dependents when they change.
149
+ <p style="margin-bottom:.65rem;">Signals are <strong>reactive primitives</strong> - values that automatically notify dependents when they change.
150
150
  Adjust <strong>A</strong> and <strong>B</strong> below and watch the computed result update instantly, with no manual DOM wiring.</p>
151
151
 
152
152
  <div class="sig-lab">
153
153
  <div class="sig-row">
154
154
  <div class="sig-op">
155
- <button class="btn btn-sm btn-ghost" @click="bumpA(-1)">−</button>
155
+ <button class="btn btn-sm btn-ghost" @click="bumpA(-1)">-</button>
156
156
  <div class="sig-val"><small>A</small> <span>${s.sigA}</span></div>
157
157
  <button class="btn btn-sm btn-ghost" @click="bumpA(1)">+</button>
158
158
  </div>
@@ -170,7 +170,7 @@ $.component('home-page', {
170
170
  </div>
171
171
  </div>
172
172
  <div class="sig-op">
173
- <button class="btn btn-sm btn-ghost" @click="bumpB(-1)">−</button>
173
+ <button class="btn btn-sm btn-ghost" @click="bumpB(-1)">-</button>
174
174
  <div class="sig-val"><small>B</small> <span>${s.sigB}</span></div>
175
175
  <button class="btn btn-sm btn-ghost" @click="bumpB(1)">+</button>
176
176
  </div>
@@ -186,7 +186,7 @@ $.component('home-page', {
186
186
  <div class="sig-graph">
187
187
  ${s.sigHistory.map(v => `<div class="sig-bar" style="height:${Math.max(6, (Math.sqrt(Math.abs(v) + 1) / maxH) * 56)}px;"></div>`).join('')}
188
188
  </div>
189
- <div style="font-size:.75rem;color:var(--text-muted);margin-top:.35rem;">Result history ${s.sigHistory.length} values tracked by <code>effect()</code></div>
189
+ <div style="font-size:.75rem;color:var(--text-muted);margin-top:.35rem;">Result history - ${s.sigHistory.length} values tracked by <code>effect()</code></div>
190
190
  ` : ''}
191
191
  </div>
192
192
 
@@ -197,11 +197,11 @@ $.component('home-page', {
197
197
  </div>
198
198
  <div class="sig-concept">
199
199
  <h4>Computed</h4>
200
- <p>A derived value that re-evaluates when its dependencies change. <code>$.computed(() => A + B)</code> recalculates whenever A or B updates no manual calls.</p>
200
+ <p>A derived value that re-evaluates when its dependencies change. <code>$.computed(() => A + B)</code> recalculates whenever A or B updates - no manual calls.</p>
201
201
  </div>
202
202
  <div class="sig-concept">
203
203
  <h4>Effect</h4>
204
- <p>A side-effect that runs automatically when tracked signals change. The bar chart above is powered by <code>$.effect()</code> it records every new result.</p>
204
+ <p>A side-effect that runs automatically when tracked signals change. The bar chart above is powered by <code>$.effect()</code> - it records every new result.</p>
205
205
  </div>
206
206
  </div>
207
207
  </div>
@@ -1,4 +1,4 @@
1
- // not-found.js 404 fallback page
1
+ // not-found.js - 404 fallback page
2
2
  //
3
3
  // Uses $.getRouter() to display the unmatched path.
4
4
 
@@ -1,4 +1,4 @@
1
- /* playground.css scoped styles for the playground page component */
1
+ /* playground.css - scoped styles for the playground page component */
2
2
 
3
3
  .pg-section { display: flex; align-items: center; gap: .5rem;
4
4
  font-size: .72rem; font-weight: 600; text-transform: uppercase;
@@ -1,12 +1,12 @@
1
1
  <!--
2
- playground.html external template for playground-page component
2
+ playground.html - external template for playground-page component
3
3
 
4
- Interactive UI patterns event modifiers, animations, reactive bindings,
4
+ Interactive UI patterns - event modifiers, animations, reactive bindings,
5
5
  and plugins. Uses {{expression}} syntax for data binding.
6
6
  -->
7
7
  <div class="page-header" z-cloak>
8
8
  <h1>Playground</h1>
9
- <p class="subtitle">Interactive UI patterns event modifiers, animations, reactive bindings, and plugins.</p>
9
+ <p class="subtitle">Interactive UI patterns - event modifiers, animations, reactive bindings, and plugins.</p>
10
10
  </div>
11
11
 
12
12
  <!-- ── Interaction Patterns ──────────────────────────── -->
@@ -14,7 +14,7 @@
14
14
 
15
15
  <div class="card" style="position:relative;z-index:2;">
16
16
  <h3>Dropdown → Modal Flow</h3>
17
- <p>Select an action from the <code>@click.outside</code> dropdown it opens a confirmation modal. Dismiss via overlay (<code>@click.self</code>) or <kbd style="padding:.1rem .35rem;background:var(--bg-hover);border-radius:4px;font-size:.82rem;">Esc</kbd>.</p>
17
+ <p>Select an action from the <code>@click.outside</code> dropdown - it opens a confirmation modal. Dismiss via overlay (<code>@click.self</code>) or <kbd style="padding:.1rem .35rem;background:var(--bg-hover);border-radius:4px;font-size:.82rem;">Esc</kbd>.</p>
18
18
  <div style="display:flex;align-items:center;gap:1rem;flex-wrap:wrap;">
19
19
  <div class="pg-dropdown" @click.outside="closeDropdown">
20
20
  <button class="btn btn-primary" @click="toggleDropdown">
@@ -30,7 +30,7 @@
30
30
  <button z-else class="btn btn-ghost" disabled>✓ Once fired</button>
31
31
  </div>
32
32
 
33
- <!-- Modal faded in via JS, dismissed via overlay @click.self or Escape -->
33
+ <!-- Modal - faded in via JS, dismissed via overlay @click.self or Escape -->
34
34
  <div class="pg-modal-backdrop" z-if="modalOpen" style="display:none" @click.self="closeModal">
35
35
  <div class="pg-modal" @keydown.escape="closeModal" tabindex="-1">
36
36
  <h3>Confirm: <span z-text="dropdownSelected || 'Action'"></span></h3>
@@ -57,7 +57,7 @@
57
57
  <span id="pg-acc-icon-a" class="pg-accordion-icon">▸</span>
58
58
  </button>
59
59
  <div id="pg-acc-a" style="display:none">
60
- <div class="pg-accordion-body">A zero-dependency frontend library for reactive SPAs components, routing, stores, HTTP, and 200+ utilities in one ~28 KB file.</div>
60
+ <div class="pg-accordion-body">A zero-dependency frontend library for reactive SPAs - components, routing, stores, HTTP, and 200+ utilities in one ~28 KB file.</div>
61
61
  </div>
62
62
  </div>
63
63
  <div class="pg-accordion-item">
@@ -87,7 +87,7 @@
87
87
  <div class="card-grid">
88
88
  <div class="card">
89
89
  <h3>Dynamic Styles</h3>
90
- <p>Object binding via <code>z-style</code> inline styles update reactively.</p>
90
+ <p>Object binding via <code>z-style</code> - inline styles update reactively.</p>
91
91
  <div class="pg-style-preview" z-style="{ color: previewColor, fontSize: previewSize + 'px', borderRadius: previewRadius + 'px', borderColor: previewColor }">
92
92
  Live Preview
93
93
  </div>
@@ -100,14 +100,14 @@
100
100
 
101
101
  <div class="card">
102
102
  <h3>Rich Content</h3>
103
- <p>Render trusted HTML with <code>z-html</code> only use with sanitized content.</p>
103
+ <p>Render trusted HTML with <code>z-html</code> - only use with sanitized content.</p>
104
104
  <textarea z-model="richContent" class="input" rows="2" style="resize:vertical;"></textarea>
105
105
  <div class="pg-rich-preview" style="margin-top:.5rem;" z-html="richContent"></div>
106
106
  </div>
107
107
 
108
108
  <div class="card">
109
109
  <h3>Conditional Classes</h3>
110
- <p>Switch a single class from a set using <code>z-class</code> the object syntax applies only the matching key.</p>
110
+ <p>Switch a single class from a set using <code>z-class</code> - the object syntax applies only the matching key.</p>
111
111
  <div class="pg-variant-row">
112
112
  <div class="pg-variant-box" z-class="{'info': variant === 'info', 'success': variant === 'success', 'warning': variant === 'warning', 'danger': variant === 'danger'}">
113
113
  {{variant.charAt(0).toUpperCase() + variant.slice(1)}} Variant
@@ -128,7 +128,7 @@
128
128
 
129
129
  <div class="card">
130
130
  <h3>Key &amp; System Modifiers</h3>
131
- <p>Template-level keyboard filtering no manual <code>e.key</code> checks needed.</p>
131
+ <p>Template-level keyboard filtering - no manual <code>e.key</code> checks needed.</p>
132
132
  <div style="display:flex;gap:.5rem;flex-wrap:wrap;align-items:center;">
133
133
  <input type="text" class="input input-sm" placeholder="Press Enter…" @keyup.enter="logShortcut('Enter ↵')" style="flex:1;min-width:140px;" />
134
134
  <button class="btn btn-sm btn-outline" @click.ctrl="logShortcut('Ctrl+Click')">Ctrl+Click</button>
@@ -144,7 +144,7 @@
144
144
  <div class="pg-section">Plugin System</div>
145
145
 
146
146
  <div class="card">
147
- <h3><code>$.fn</code> Extend the Collection</h3>
147
+ <h3><code>$.fn</code> - Extend the Collection</h3>
148
148
  <p>Register custom chainable methods on every <code>$(...)</code> selection. Click the buttons to trigger each plugin.</p>
149
149
  <div id="pg-plugin-box" class="pg-plugin-target">✦ Target Element</div>
150
150
  <div class="pg-plugin-btns">
@@ -1,14 +1,14 @@
1
- // playground.js Interactive UI patterns
1
+ // playground.js - Interactive UI patterns
2
2
  //
3
3
  // Features used:
4
- // @click.outside / .self / .once event modifiers
5
- // @keydown.escape / @keyup.enter keyboard modifiers
6
- // fadeIn / fadeOut / slideToggle DOM animations
7
- // z-style reactive inline styles
8
- // z-html trusted HTML rendering
9
- // $.fn collection plugin system
10
- // z-skip morph opt-out
11
- // templateUrl / styleUrl external files
4
+ // @click.outside / .self / .once - event modifiers
5
+ // @keydown.escape / @keyup.enter - keyboard modifiers
6
+ // fadeIn / fadeOut / slideToggle - DOM animations
7
+ // z-style - reactive inline styles
8
+ // z-html - trusted HTML rendering
9
+ // $.fn - collection plugin system
10
+ // z-skip - morph opt-out
11
+ // templateUrl / styleUrl - external files
12
12
 
13
13
  // ── $.fn Plugins ─────────────────────────────────────────────────
14
14
  $.fn.highlight = function (color = 'var(--accent)') {
@@ -88,7 +88,7 @@ $.component('playground-page', {
88
88
  $.bus.emit('toast', { message: `${item} confirmed!`, type: 'success' });
89
89
  },
90
90
 
91
- /* Accordion pure DOM, z-skip prevents morph interference */
91
+ /* Accordion - pure DOM, z-skip prevents morph interference */
92
92
  toggleAccordion(id) {
93
93
  $(this._el).find(`#pg-acc-${id}`).slideToggle(250);
94
94
  $(this._el).find(`#pg-acc-icon-${id}`).toggleClass('open');
@@ -112,6 +112,6 @@ $.component('playground-page', {
112
112
  /* @click.once */
113
113
  onceAction() {
114
114
  this.state.onceClicked = true;
115
- $.bus.emit('toast', { message: 'Fired once handler auto-removed!', type: 'info' });
115
+ $.bus.emit('toast', { message: 'Fired once - handler auto-removed!', type: 'info' });
116
116
  },
117
117
  });
@@ -1,12 +1,12 @@
1
- // todos.js Todo list with global store
1
+ // todos.js - Todo list with global store
2
2
  //
3
3
  // Features used:
4
- // $.getStore / dispatch / subscribe centralized state management
5
- // z-model / z-ref form bindings
6
- // z-for + z-key keyed list rendering with diffing
7
- // z-class / z-if / z-show conditional rendering
8
- // @submit.prevent / @keydown.escape event modifiers
9
- // mounted / destroyed lifecycle setup & teardown
4
+ // $.getStore / dispatch / subscribe - centralized state management
5
+ // z-model / z-ref - form bindings
6
+ // z-for + z-key - keyed list rendering with diffing
7
+ // z-class / z-if / z-show - conditional rendering
8
+ // @submit.prevent / @keydown.escape - event modifiers
9
+ // mounted / destroyed lifecycle - setup & teardown
10
10
 
11
11
  $.component('todos-page', {
12
12
  styles: `
@@ -204,7 +204,7 @@ $.component('todos-page', {
204
204
 
205
205
  <div z-if="filtered.length === 0" class="td-empty">
206
206
  <div class="td-empty-icon">${this.state.total === 0 ? '📝' : '🔍'}</div>
207
- <p>${this.state.total === 0 ? 'No todos yet type something above and hit Add!' : 'No matching todos for this filter.'}</p>
207
+ <p>${this.state.total === 0 ? 'No todos yet - type something above and hit Add!' : 'No matching todos for this filter.'}</p>
208
208
  </div>
209
209
 
210
210
  <ul z-else class="td-list">
@@ -1,4 +1,4 @@
1
- /* toolkit.css scoped styles for the toolkit page component */
1
+ /* toolkit.css - scoped styles for the toolkit page component */
2
2
 
3
3
  .tk-tabs { display: flex; padding: .35rem;
4
4
  background: var(--bg-surface);
@@ -1,5 +1,5 @@
1
1
  <!--
2
- toolkit.html external template for toolkit-page component
2
+ toolkit.html - external template for toolkit-page component
3
3
 
4
4
  HTTP client with interceptors, 30+ utility functions, and advanced
5
5
  store patterns. Uses {{expression}} syntax and z-if / z-for / z-show.
@@ -65,7 +65,7 @@
65
65
  <div z-else-if="activeTab === 'utils'">
66
66
  <div class="card">
67
67
  <h3>Utility Belt</h3>
68
- <p>30+ built-in helpers select a category to run a live demo.</p>
68
+ <p>30+ built-in helpers - select a category to run a live demo.</p>
69
69
 
70
70
  <div class="tk-util-grid">
71
71
  <div class="tk-util-card {{activeUtil === 'pipe' ? 'selected' : ''}}" @click="runUtil('pipe')">
@@ -102,7 +102,7 @@
102
102
  <div z-else>
103
103
  <div class="card" style="margin-bottom:.75rem;">
104
104
  <h3>Store Inspector</h3>
105
- <p>Live view into the reactive store middleware, snapshots, and action history.</p>
105
+ <p>Live view into the reactive store - middleware, snapshots, and action history.</p>
106
106
 
107
107
  <div style="display:flex;gap:.5rem;flex-wrap:wrap;margin-bottom:1rem;">
108
108
  <div class="tk-stat">
@@ -134,7 +134,7 @@
134
134
 
135
135
  <div class="card card-muted" z-show="storeLog.length > 0">
136
136
  <h3>Middleware Log</h3>
137
- <p style="color:var(--text-muted);font-size:.85rem;margin-bottom:.5rem;">Real-time log from <code>store.use()</code> interact with other pages to generate entries.</p>
137
+ <p style="color:var(--text-muted);font-size:.85rem;margin-bottom:.5rem;">Real-time log from <code>store.use()</code> - interact with other pages to generate entries.</p>
138
138
  <div class="tk-log">
139
139
  <div z-for="entry in storeLog" z-key="{{entry.id}}" class="tk-log-entry">
140
140
  <span style="color:var(--text-muted);font-size:.78rem;min-width:65px;">{{entry.time}}</span>
@@ -1,11 +1,11 @@
1
- // toolkit.js HTTP, utilities, and advanced store features
1
+ // toolkit.js - HTTP, utilities, and advanced store features
2
2
  //
3
3
  // Features used:
4
- // $.http / $.post / $.put / $.delete HTTP client + interceptors
5
- // $.pipe / $.memoize / $.retry functional utilities
6
- // $.groupBy / $.chunk / $.unique collection helpers
7
- // store.use / snapshot / history middleware & time-travel
8
- // templateUrl / styleUrl external template & styles
4
+ // $.http / $.post / $.put / $.delete - HTTP client + interceptors
5
+ // $.pipe / $.memoize / $.retry - functional utilities
6
+ // $.groupBy / $.chunk / $.unique - collection helpers
7
+ // store.use / snapshot / history - middleware & time-travel
8
+ // templateUrl / styleUrl - external template & styles
9
9
 
10
10
  $.component('toolkit-page', {
11
11
  templateUrl: 'toolkit.html',
@@ -32,7 +32,7 @@ $.component('toolkit-page', {
32
32
  actionCount: 0,
33
33
  }),
34
34
 
35
- // init() lifecycle runs before the first render
35
+ // init() lifecycle - runs before the first render
36
36
  init() {
37
37
  this._prevBaseURL = $.http.getConfig().baseURL || '';
38
38
  $.http.configure({ baseURL: 'https://jsonplaceholder.typicode.com' });
@@ -1,4 +1,4 @@
1
- // routes.js Route definitions
1
+ // routes.js - Route definitions
2
2
  //
3
3
  // Maps URL paths to component tag names.
4
4
  // Also supports :params, wildcards, and lazy loading via `load`.
@@ -1,4 +1,4 @@
1
- // store.js Global state management
1
+ // store.js - Global state management
2
2
  //
3
3
  // Defines a centralized store with state, actions, and getters.
4
4
  // Any component can access it via $.getStore('main').
@@ -1,4 +1,4 @@
1
- /* global.css responsive scaffold styles
1
+ /* global.css - responsive scaffold styles
2
2
  *
3
3
  * Uses CSS custom properties for easy theming. Feel free to modify.
4
4
  * Dark theme by default, light theme via [data-theme="light"].
@@ -504,7 +504,7 @@ code { background: var(--bg-hover); padding: 2px 6px; border-radius: 4px; font-s
504
504
  @keyframes toast-out { from { opacity: 1; transform: translateY(0) scale(1); } to { opacity: 0; transform: translateY(-10px) scale(0.95); } }
505
505
 
506
506
  /* -- Route Transition -- */
507
- #app { animation: fade-in 0.25s var(--ease-out); }
507
+ z-outlet { animation: fade-in 0.25s var(--ease-out); }
508
508
  @keyframes fade-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: translateY(0); } }
509
509
 
510
510
  /* -- Responsive: Mobile -- */
@@ -86,8 +86,8 @@
86
86
  <div class="overlay" id="overlay"></div>
87
87
 
88
88
  <!-- @ Router Outlet @ -->
89
- <main class="content" id="app"></main>
90
- <!-- @ Mount point is set in app/app.js @ -->
89
+ <z-outlet class="content"></z-outlet>
90
+ <!-- @ Auto-detected by $.router() @ -->
91
91
 
92
92
  <!-- Toast container for notifications -->
93
93
  <div class="toast-container" id="toasts"></div>
@@ -1,13 +1,13 @@
1
- // app.js Application entry point
1
+ // app.js - Application entry point
2
2
  //
3
3
  // Bootstraps the app: imports components, sets up routing,
4
4
  // and wires the responsive sidebar.
5
5
  //
6
6
  // Key APIs used:
7
- // $.router SPA navigation (history mode)
8
- // $.ready run after DOM is loaded
9
- // $.on global delegated event listeners
10
- // $.storage localStorage wrapper
7
+ // $.router - SPA navigation (history mode)
8
+ // $.ready - run after DOM is loaded
9
+ // $.on - global delegated event listeners
10
+ // $.storage - localStorage wrapper
11
11
 
12
12
  import './store.js';
13
13
  import './components/home.js';
@@ -20,7 +20,6 @@ import { routes } from './routes.js';
20
20
  // Router
21
21
  // ---------------------------------------------------------------------------
22
22
  const router = $.router({
23
- el: '#app',
24
23
  routes,
25
24
  fallback: 'not-found',
26
25
  mode: 'history'
@@ -75,7 +74,7 @@ $.ready(() => {
75
74
  const current = window.location.pathname;
76
75
  $.all(`.nav-link[z-link="${current}"]`).addClass('active');
77
76
 
78
- console.log('⚡ {{NAME}} powered by zQuery v' + $.version);
77
+ console.log('⚡ {{NAME}} - powered by zQuery v' + $.version);
79
78
  });
80
79
 
81
80
  // ---------------------------------------------------------------------------
@@ -1,9 +1,9 @@
1
- // about.js About page with theme switcher
1
+ // about.js - About page with theme switcher
2
2
  //
3
3
  // Features used:
4
- // $.storage localStorage wrapper (get / set)
5
- // $.version library version string
6
- // data-theme dark / light theming
4
+ // $.storage - localStorage wrapper (get / set)
5
+ // $.version - library version string
6
+ // data-theme - dark / light theming
7
7
 
8
8
  $.component('about-page', {
9
9
  styles: `
@@ -40,7 +40,7 @@ $.component('about-page', {
40
40
  return `
41
41
  <div class="page-header">
42
42
  <h1>About</h1>
43
- <p class="subtitle">zQuery v${$.version} zero-dependency frontend micro-library.</p>
43
+ <p class="subtitle">zQuery v${$.version} - zero-dependency frontend micro-library.</p>
44
44
  </div>
45
45
 
46
46
  <div class="card">
@@ -1,9 +1,9 @@
1
- // counter.js Interactive counter
1
+ // counter.js - Interactive counter
2
2
  //
3
3
  // Features used:
4
- // $.getStore / store.subscribe shared state + live re-render
5
- // @click / z-model + z-number events + two-way binding
6
- // z-if / z-for / z-key conditional & keyed list rendering
4
+ // $.getStore / store.subscribe - shared state + live re-render
5
+ // @click / z-model + z-number - events + two-way binding
6
+ // z-if / z-for / z-key - conditional & keyed list rendering
7
7
 
8
8
  $.component('counter-page', {
9
9
  styles: `
@@ -62,7 +62,7 @@ $.component('counter-page', {
62
62
  decrement() {
63
63
  const store = $.getStore('main');
64
64
  store.dispatch('decrement');
65
- this._pushHistory('', store.state.step, store.state.count);
65
+ this._pushHistory('-', store.state.step, store.state.count);
66
66
  },
67
67
 
68
68
  setStep(e) {
@@ -96,7 +96,7 @@ $.component('counter-page', {
96
96
  </div>
97
97
 
98
98
  <div class="ctr-actions">
99
- <button class="btn btn-outline" @click="decrement">− Subtract</button>
99
+ <button class="btn btn-outline" @click="decrement">- Subtract</button>
100
100
  <button class="btn btn-primary" @click="increment">+ Add</button>
101
101
  </div>
102
102
 
@@ -1,10 +1,10 @@
1
- // home.js Landing page
1
+ // home.js - Landing page
2
2
  //
3
3
  // Features used:
4
- // $.component define a component
5
- // $.getStore read/dispatch global store
6
- // store.subscribe re-render on store changes
7
- // @click / z-model event binding + two-way input
4
+ // $.component - define a component
5
+ // $.getStore - read/dispatch global store
6
+ // store.subscribe - re-render on store changes
7
+ // @click / z-model - event binding + two-way input
8
8
 
9
9
  $.component('home-page', {
10
10
  state: () => ({
@@ -41,7 +41,7 @@ $.component('home-page', {
41
41
  <div class="card">
42
42
  <h3>Getting Started</h3>
43
43
  <p>
44
- This is the <strong>minimal</strong> scaffold three pages, a global store,
44
+ This is the <strong>minimal</strong> scaffold - three pages, a global store,
45
45
  and the router. Edit the files in <code>app/</code> to start building.
46
46
  </p>
47
47
  <p>
@@ -52,9 +52,9 @@ $.component('home-page', {
52
52
 
53
53
  <div class="card">
54
54
  <h3>Global Store</h3>
55
- <p>The counter from <a z-link="/counter">Counter</a> is backed by <code>$.store</code> its value persists across pages:</p>
55
+ <p>The counter from <a z-link="/counter">Counter</a> is backed by <code>$.store</code> - its value persists across pages:</p>
56
56
  <div style="display:flex;align-items:center;gap:.75rem;margin-top:.75rem;">
57
- <button class="btn btn-outline btn-sm" @click="decrement">−</button>
57
+ <button class="btn btn-outline btn-sm" @click="decrement">-</button>
58
58
  <span style="font-size:1.25rem;font-weight:700;color:var(--accent);min-width:2rem;text-align:center;">${count}</span>
59
59
  <button class="btn btn-primary btn-sm" @click="increment">+</button>
60
60
  <button class="btn btn-outline btn-sm" @click="reset" style="margin-left:.5rem;">Reset</button>
@@ -1,4 +1,4 @@
1
- // not-found.js 404 fallback page
1
+ // not-found.js - 404 fallback page
2
2
  //
3
3
  // Uses $.getRouter() to display the unmatched path.
4
4
 
@@ -1,4 +1,4 @@
1
- // routes.js Route definitions
1
+ // routes.js - Route definitions
2
2
  //
3
3
  // Maps URL paths to component tag names.
4
4
 
@@ -1,4 +1,4 @@
1
- // store.js Global state management
1
+ // store.js - Global state management
2
2
  //
3
3
  // A simple centralized store. Any component can access it
4
4
  // via $.getStore('main') and dispatch actions to update state.
@@ -1,4 +1,4 @@
1
- /* global.css minimal scaffold styles
1
+ /* global.css - minimal scaffold styles
2
2
  *
3
3
  * Dark theme by default, light theme via [data-theme="light"].
4
4
  * Modify the CSS variables below to customise the look and feel.
@@ -259,7 +259,7 @@ code { background: var(--bg-hover); padding: 2px 6px; border-radius: 4px; font-s
259
259
  .muted { color: var(--text-muted); }
260
260
 
261
261
  /* -- Route Transition -- */
262
- #app { animation: fade-in 0.25s var(--ease-out); }
262
+ z-outlet { animation: fade-in 0.25s var(--ease-out); }
263
263
  @keyframes fade-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: translateY(0); } }
264
264
 
265
265
  /* -- Responsive: Mobile -- */
@@ -38,7 +38,7 @@
38
38
  <div class="overlay" id="overlay"></div>
39
39
 
40
40
  <!-- Router Outlet -->
41
- <main class="content" id="app"></main>
41
+ <z-outlet class="content"></z-outlet>
42
42
 
43
43
  </body>
44
44
  </html>
@@ -0,0 +1,29 @@
1
+ // app.js - Client entry point
2
+ //
3
+ // Imports shared component definitions and registers them with zQuery.
4
+ // The SSR server imports the same definitions via createSSRApp().
5
+
6
+ import { homePage } from './components/home.js';
7
+ import { aboutPage } from './components/about.js';
8
+ import { notFound } from './components/not-found.js';
9
+ import { routes } from './routes.js';
10
+
11
+ // Register shared component definitions on the client
12
+ $.component('home-page', homePage);
13
+ $.component('about-page', aboutPage);
14
+ $.component('not-found', notFound);
15
+
16
+ // Client-side router
17
+ const router = $.router({
18
+ routes,
19
+ fallback: 'not-found',
20
+ mode: 'history'
21
+ });
22
+
23
+ // Active nav highlighting
24
+ router.onChange((to) => {
25
+ $.qsa('.nav-link').forEach(link => {
26
+ const href = link.getAttribute('z-link') || link.getAttribute('href');
27
+ link.classList.toggle('active', href === to.path);
28
+ });
29
+ });
@@ -0,0 +1,28 @@
1
+ // about.js - About page component
2
+
3
+ export const aboutPage = {
4
+ state: () => ({
5
+ features: [
6
+ 'createSSRApp() - isolated component registry for Node.js',
7
+ 'renderToString() - render a component to an HTML string',
8
+ 'renderPage() - full HTML document with meta tags',
9
+ 'renderBatch() - render multiple components in one call',
10
+ 'Hydration markers (data-zq-ssr) for client takeover',
11
+ 'SEO: description, canonical URL, Open Graph tags',
12
+ ]
13
+ }),
14
+
15
+ render() {
16
+ const list = this.state.features.map(f => `<li>${f}</li>`).join('');
17
+ return `
18
+ <div class="page-header">
19
+ <h1>About</h1>
20
+ <p class="subtitle">SSR capabilities in this scaffold.</p>
21
+ </div>
22
+ <div class="card">
23
+ <h3>SSR API</h3>
24
+ <ul style="padding-left:1.2rem; line-height:2;">${list}</ul>
25
+ </div>
26
+ `;
27
+ }
28
+ };
@@ -0,0 +1,37 @@
1
+ // home.js - Home page component
2
+ //
3
+ // Exports a plain definition object that works on both client and server.
4
+ // The client registers it with $.component(), the server with app.component().
5
+
6
+ export const homePage = {
7
+ state: () => ({
8
+ greeting: 'Hello',
9
+ timestamp: new Date().toLocaleTimeString(),
10
+ }),
11
+
12
+ // init() runs on both client and server - no DOM required
13
+ init() {
14
+ const hour = new Date().getHours();
15
+ this.state.greeting =
16
+ hour < 12 ? 'Good morning' :
17
+ hour < 18 ? 'Good afternoon' : 'Good evening';
18
+ },
19
+
20
+ render() {
21
+ return `
22
+ <div class="page-header">
23
+ <h1>${this.state.greeting} 👋</h1>
24
+ <p class="subtitle">Rendered with zQuery SSR</p>
25
+ </div>
26
+ <div class="card">
27
+ <h3>Server-Side Rendering</h3>
28
+ <p>
29
+ This page was rendered to HTML on the server and served as a complete
30
+ document. The same component definition powers both the SSR server and
31
+ the client-side SPA.
32
+ </p>
33
+ <p>Rendered at <strong>${this.state.timestamp}</strong></p>
34
+ </div>
35
+ `;
36
+ }
37
+ };