@techninja/clearstack 0.2.16 → 0.2.18
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/docs/BACKEND_API_SPEC.md +85 -48
- package/docs/BUILD_LOG.md +42 -19
- package/docs/COMPONENT_PATTERNS.md +57 -51
- package/docs/CONVENTIONS.md +43 -31
- package/docs/FRONTEND_IMPLEMENTATION_RULES.md +57 -58
- package/docs/JSDOC_TYPING.md +1 -0
- package/docs/QUICKSTART.md +20 -18
- package/docs/SERVER_AND_DEPS.md +28 -29
- package/docs/STATE_AND_ROUTING.md +53 -52
- package/docs/TESTING.md +38 -37
- package/docs/app-spec/ENTITIES.md +16 -16
- package/docs/app-spec/README.md +4 -4
- package/lib/check.js +3 -1
- package/lib/package-gen.js +3 -0
- package/package.json +5 -2
- package/templates/fullstack/data/seed.json +1 -1
- package/templates/shared/.configs/.markdownlint.jsonc +9 -0
- package/templates/shared/.configs/.stylelintrc.json +16 -0
- package/templates/shared/.configs/jsconfig.json +2 -9
- package/templates/shared/.github/ISSUE_TEMPLATE/bug_report.md +1 -1
- package/templates/shared/.github/ISSUE_TEMPLATE/feature_request.md +1 -1
- package/templates/shared/.github/ISSUE_TEMPLATE/spec_correction.md +1 -1
- package/templates/shared/.github/pull_request_template.md +3 -0
- package/templates/shared/.github/workflows/spec.yml +3 -3
- package/templates/shared/docs/app-spec/README.md +8 -8
- package/templates/shared/docs/clearstack/BACKEND_API_SPEC.md +85 -48
- package/templates/shared/docs/clearstack/BUILD_LOG.md +42 -19
- package/templates/shared/docs/clearstack/COMPONENT_PATTERNS.md +57 -51
- package/templates/shared/docs/clearstack/CONVENTIONS.md +43 -31
- package/templates/shared/docs/clearstack/FRONTEND_IMPLEMENTATION_RULES.md +57 -58
- package/templates/shared/docs/clearstack/JSDOC_TYPING.md +1 -0
- package/templates/shared/docs/clearstack/QUICKSTART.md +20 -18
- package/templates/shared/docs/clearstack/SERVER_AND_DEPS.md +28 -29
- package/templates/shared/docs/clearstack/STATE_AND_ROUTING.md +53 -52
- package/templates/shared/docs/clearstack/TESTING.md +38 -37
- package/templates/shared/src/public/index.html +23 -23
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# Conventions
|
|
2
|
+
|
|
2
3
|
## Naming Rules & Anti-Patterns
|
|
3
4
|
|
|
4
5
|
> Quick reference for naming and what to avoid.
|
|
@@ -13,37 +14,37 @@
|
|
|
13
14
|
|
|
14
15
|
All custom element tags use **kebab-case** with an `app-` prefix:
|
|
15
16
|
|
|
16
|
-
| Thing
|
|
17
|
-
|
|
18
|
-
| Button atom
|
|
19
|
-
| Header organism
|
|
17
|
+
| Thing | Name | Tag |
|
|
18
|
+
| -------------------- | ------------- | --------------- |
|
|
19
|
+
| Button atom | `app-button` | `<app-button>` |
|
|
20
|
+
| Header organism | `app-header` | `<app-header>` |
|
|
20
21
|
| Page layout template | `page-layout` | `<page-layout>` |
|
|
21
|
-
| Home page view
|
|
22
|
+
| Home page view | `home-view` | `<home-view>` |
|
|
22
23
|
|
|
23
24
|
The `app-` prefix prevents collisions with native elements and third-party
|
|
24
25
|
components. Page views and templates may drop the prefix when unambiguous.
|
|
25
26
|
|
|
26
27
|
### Files & Directories
|
|
27
28
|
|
|
28
|
-
| Type
|
|
29
|
-
|
|
30
|
-
| Component file
|
|
31
|
-
| Component CSS
|
|
32
|
-
| Component dir
|
|
33
|
-
| Re-export
|
|
34
|
-
| Store model
|
|
35
|
-
| Utility function | camelCase
|
|
36
|
-
| Shared CSS
|
|
29
|
+
| Type | Convention | Example |
|
|
30
|
+
| ---------------- | ----------------- | ------------------------- |
|
|
31
|
+
| Component file | Match tag name | `app-button.js` |
|
|
32
|
+
| Component CSS | Match tag name | `app-button.css` |
|
|
33
|
+
| Component dir | Match tag name | `app-button/` |
|
|
34
|
+
| Re-export | Always `index.js` | `index.js` |
|
|
35
|
+
| Store model | PascalCase | `UserModel.js` |
|
|
36
|
+
| Utility function | camelCase | `formatDate.js` |
|
|
37
|
+
| Shared CSS | Descriptive kebab | `tokens.css`, `reset.css` |
|
|
37
38
|
|
|
38
39
|
### JavaScript
|
|
39
40
|
|
|
40
|
-
| Type
|
|
41
|
-
|
|
42
|
-
| Event handlers
|
|
43
|
-
| Store models
|
|
44
|
-
| Utility functions | camelCase verb
|
|
45
|
-
| Constants
|
|
46
|
-
| JSDoc typedefs
|
|
41
|
+
| Type | Convention | Example |
|
|
42
|
+
| ----------------- | ----------------- | ----------------------------- |
|
|
43
|
+
| Event handlers | `handle` + action | `handleClick`, `handleSubmit` |
|
|
44
|
+
| Store models | PascalCase noun | `UserModel`, `AppState` |
|
|
45
|
+
| Utility functions | camelCase verb | `formatDate`, `parseQuery` |
|
|
46
|
+
| Constants | UPPER_SNAKE | `MAX_RETRIES`, `API_BASE` |
|
|
47
|
+
| JSDoc typedefs | PascalCase | `@typedef {Object} User` |
|
|
47
48
|
|
|
48
49
|
---
|
|
49
50
|
|
|
@@ -52,12 +53,14 @@ components. Page views and templates may drop the prefix when unambiguous.
|
|
|
52
53
|
### ❌ Never Do This
|
|
53
54
|
|
|
54
55
|
**DOM queries inside components:**
|
|
56
|
+
|
|
55
57
|
```javascript
|
|
56
58
|
// BAD — breaks encapsulation, ignores shadow DOM
|
|
57
59
|
const el = document.querySelector('.my-thing');
|
|
58
60
|
```
|
|
59
61
|
|
|
60
62
|
**Manual event listeners:**
|
|
63
|
+
|
|
61
64
|
```javascript
|
|
62
65
|
// BAD — leaks memory, bypasses hybrids lifecycle
|
|
63
66
|
connectedCallback() {
|
|
@@ -66,18 +69,21 @@ connectedCallback() {
|
|
|
66
69
|
```
|
|
67
70
|
|
|
68
71
|
**Global mutable state:**
|
|
72
|
+
|
|
69
73
|
```javascript
|
|
70
74
|
// BAD — invisible dependencies, untraceable bugs
|
|
71
75
|
window.appState = { user: null };
|
|
72
76
|
```
|
|
73
77
|
|
|
74
78
|
**Imperative DOM manipulation:**
|
|
79
|
+
|
|
75
80
|
```javascript
|
|
76
81
|
// BAD — fights the reactive render cycle
|
|
77
82
|
host.shadowRoot.querySelector('span').textContent = 'updated';
|
|
78
83
|
```
|
|
79
84
|
|
|
80
85
|
**Business logic in render:**
|
|
86
|
+
|
|
81
87
|
```javascript
|
|
82
88
|
// BAD — render should be pure projection of state
|
|
83
89
|
render: ({ items }) => html`
|
|
@@ -86,11 +92,13 @@ render: ({ items }) => html`
|
|
|
86
92
|
```
|
|
87
93
|
|
|
88
94
|
**Files over 150 lines:**
|
|
95
|
+
|
|
89
96
|
```
|
|
90
97
|
// BAD — extract to utils/ or split into sub-components
|
|
91
98
|
```
|
|
92
99
|
|
|
93
100
|
**Deep nesting (>3 component levels):**
|
|
101
|
+
|
|
94
102
|
```
|
|
95
103
|
// BAD — flatten by composing at the page level
|
|
96
104
|
<app-layout>
|
|
@@ -109,14 +117,14 @@ let everything else propagate.
|
|
|
109
117
|
|
|
110
118
|
### Boundary Rules
|
|
111
119
|
|
|
112
|
-
| Layer
|
|
113
|
-
|
|
114
|
-
| **Utils**
|
|
115
|
-
| **Store connectors** | Let fetch failures propagate. Hybrids' `store.error()` catches them.
|
|
116
|
-
| **Components**
|
|
117
|
-
| **Event handlers**
|
|
118
|
-
| **Server routes**
|
|
119
|
-
| **Server infra**
|
|
120
|
+
| Layer | Responsibility | Example |
|
|
121
|
+
| -------------------- | ------------------------------------------------------------------------------ | --------------------------------------------------------------------- |
|
|
122
|
+
| **Utils** | Never catch. Return error values or throw. Caller decides. | `formatDate(null)` returns `''` |
|
|
123
|
+
| **Store connectors** | Let fetch failures propagate. Hybrids' `store.error()` catches them. | Don't wrap fetch in try/catch |
|
|
124
|
+
| **Components** | Display `store.pending()` / `store.error()` states. Never try/catch in render. | `${store.error(model) && html`<div class="error-message">...</div>`}` |
|
|
125
|
+
| **Event handlers** | Guard with `store.ready()` before accessing store properties. | `if (!store.ready(host.state)) return;` |
|
|
126
|
+
| **Server routes** | Return HTTP status + JSON error body. Never crash the process. | `res.status(404).json({ error: 'Not found' })` |
|
|
127
|
+
| **Server infra** | Handle process-level errors with clear messages and exit codes. | Port conflict → log message → `process.exit(1)` |
|
|
120
128
|
|
|
121
129
|
### Why This Matters
|
|
122
130
|
|
|
@@ -141,7 +149,7 @@ next layer up to handle what it can't.
|
|
|
141
149
|
// BAD — accesses store properties without ready guard
|
|
142
150
|
function toggle(host) {
|
|
143
151
|
const next = host.state.theme === 'light' ? 'dark' : 'light';
|
|
144
|
-
store.set(host.state, { theme: next });
|
|
152
|
+
store.set(host.state, { theme: next }); // crashes if model is in error state
|
|
145
153
|
}
|
|
146
154
|
```
|
|
147
155
|
|
|
@@ -168,16 +176,19 @@ function toggle(host) {
|
|
|
168
176
|
### ✅ Always Do This
|
|
169
177
|
|
|
170
178
|
**Declarative event binding:**
|
|
179
|
+
|
|
171
180
|
```javascript
|
|
172
|
-
html`<button onclick="${handleClick}">Go</button
|
|
181
|
+
html`<button onclick="${handleClick}">Go</button>`;
|
|
173
182
|
```
|
|
174
183
|
|
|
175
184
|
**Store for shared state:**
|
|
185
|
+
|
|
176
186
|
```javascript
|
|
177
187
|
state: store(AppState),
|
|
178
188
|
```
|
|
179
189
|
|
|
180
190
|
**Pure functions for logic:**
|
|
191
|
+
|
|
181
192
|
```javascript
|
|
182
193
|
// In src/utils/filterActive.js
|
|
183
194
|
export const filterActive = (items) => items.filter(i => i.active);
|
|
@@ -188,6 +199,7 @@ render: ({ items }) => html`<ul>${filterActive(items).map(...)}</ul>`,
|
|
|
188
199
|
```
|
|
189
200
|
|
|
190
201
|
**JSDoc on all exports:**
|
|
202
|
+
|
|
191
203
|
```javascript
|
|
192
204
|
/** @param {User} user */
|
|
193
205
|
export const fullName = (user) => `${user.firstName} ${user.lastName}`;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# Frontend Implementation Rules
|
|
2
|
+
|
|
2
3
|
## Hybrids.js "No-Build" Web Component Specification
|
|
3
4
|
|
|
4
5
|
> A concise, LLM-and-human-friendly specification for building web applications
|
|
@@ -17,15 +18,15 @@
|
|
|
17
18
|
|
|
18
19
|
**Sub-specifications:**
|
|
19
20
|
|
|
20
|
-
| Document
|
|
21
|
-
|
|
22
|
-
| [COMPONENT_PATTERNS.md](./COMPONENT_PATTERNS.md) | Authoring, light DOM, styling, layout engine, file size rules
|
|
23
|
-
| [JSDOC_TYPING.md](./JSDOC_TYPING.md)
|
|
24
|
-
| [STATE_AND_ROUTING.md](./STATE_AND_ROUTING.md)
|
|
25
|
-
| [CONVENTIONS.md](./CONVENTIONS.md)
|
|
26
|
-
| [SERVER_AND_DEPS.md](./SERVER_AND_DEPS.md)
|
|
27
|
-
| [BACKEND_API_SPEC.md](./BACKEND_API_SPEC.md)
|
|
28
|
-
| [TESTING.md](./TESTING.md)
|
|
21
|
+
| Document | Covers |
|
|
22
|
+
| ------------------------------------------------ | ----------------------------------------------------------------------- |
|
|
23
|
+
| [COMPONENT_PATTERNS.md](./COMPONENT_PATTERNS.md) | Authoring, light DOM, styling, layout engine, file size rules |
|
|
24
|
+
| [JSDOC_TYPING.md](./JSDOC_TYPING.md) | JSDoc typing strategy, tsc validation, component/model/handler patterns |
|
|
25
|
+
| [STATE_AND_ROUTING.md](./STATE_AND_ROUTING.md) | Store, routing, unified app state, realtime sync |
|
|
26
|
+
| [CONVENTIONS.md](./CONVENTIONS.md) | Naming conventions, anti-patterns |
|
|
27
|
+
| [SERVER_AND_DEPS.md](./SERVER_AND_DEPS.md) | Express server, import maps, vendor dependency loading |
|
|
28
|
+
| [BACKEND_API_SPEC.md](./BACKEND_API_SPEC.md) | REST endpoints, JSON Schema via HEAD, entity CRUD, realtime sync |
|
|
29
|
+
| [TESTING.md](./TESTING.md) | Testing philosophy, tools, patterns, build-phase checkpoints |
|
|
29
30
|
|
|
30
31
|
---
|
|
31
32
|
|
|
@@ -36,16 +37,16 @@ no transpiler, no compile step. Code runs exactly as written.
|
|
|
36
37
|
|
|
37
38
|
### Core Principles
|
|
38
39
|
|
|
39
|
-
| Principle
|
|
40
|
-
|
|
41
|
-
| No build tools
|
|
42
|
-
| ES modules only
|
|
43
|
-
| Import maps
|
|
44
|
-
| Small files
|
|
45
|
-
| Explicit over implicit
|
|
46
|
-
| Readable over clever
|
|
47
|
-
| Declarative over imperative
|
|
48
|
-
| Composition over inheritance | Build complex UIs by composing small components
|
|
40
|
+
| Principle | Rule |
|
|
41
|
+
| ---------------------------- | --------------------------------------------------------------------------- |
|
|
42
|
+
| No build tools | No Webpack, Vite, Rollup, esbuild, or Babel |
|
|
43
|
+
| ES modules only | All `.js` files are native ES modules served to the browser |
|
|
44
|
+
| Import maps | Bare specifiers (e.g. `"hybrids"`) resolved via `<script type="importmap">` |
|
|
45
|
+
| Small files | Every file ≤ **150 lines** before extracting shared logic |
|
|
46
|
+
| Explicit over implicit | Name things clearly; avoid magic strings and hidden conventions |
|
|
47
|
+
| Readable over clever | Optimize for comprehension by humans and LLMs alike |
|
|
48
|
+
| Declarative over imperative | Use framework patterns, not raw DOM manipulation |
|
|
49
|
+
| Composition over inheritance | Build complex UIs by composing small components |
|
|
49
50
|
|
|
50
51
|
### What This Means in Practice
|
|
51
52
|
|
|
@@ -72,15 +73,15 @@ web component framework built on plain objects and pure functions.
|
|
|
72
73
|
|
|
73
74
|
### Why Hybrids
|
|
74
75
|
|
|
75
|
-
| Need
|
|
76
|
-
|
|
77
|
-
| Components
|
|
78
|
-
| Templating
|
|
79
|
-
| State
|
|
80
|
-
| Routing
|
|
81
|
-
| Layout
|
|
82
|
-
| Localization | `localize()` — automatic template translation
|
|
83
|
-
| ES modules
|
|
76
|
+
| Need | Hybrids Provides |
|
|
77
|
+
| ------------ | --------------------------------------------------------------- |
|
|
78
|
+
| Components | `define()` — plain object definitions, no classes |
|
|
79
|
+
| Templating | `html``` — tagged template literals with reactive bindings |
|
|
80
|
+
| State | `store()` — global state with async storage, caching, relations |
|
|
81
|
+
| Routing | `router()` — view-graph-based routing with guards and dialogs |
|
|
82
|
+
| Layout | `layout=""` attribute — CSS layout engine in templates |
|
|
83
|
+
| Localization | `localize()` — automatic template translation |
|
|
84
|
+
| ES modules | Ships as raw ES modules in `src/` — no build needed |
|
|
84
85
|
|
|
85
86
|
### API Surface (v9.1)
|
|
86
87
|
|
|
@@ -88,27 +89,27 @@ These are the only imports you need:
|
|
|
88
89
|
|
|
89
90
|
```javascript
|
|
90
91
|
import {
|
|
91
|
-
define,
|
|
92
|
-
html,
|
|
93
|
-
store,
|
|
94
|
-
router,
|
|
95
|
-
mount,
|
|
96
|
-
parent,
|
|
97
|
-
children,
|
|
98
|
-
dispatch,
|
|
99
|
-
msg,
|
|
100
|
-
localize,
|
|
92
|
+
define, // Register a component
|
|
93
|
+
html, // Template tagged literal
|
|
94
|
+
store, // State management
|
|
95
|
+
router, // Routing
|
|
96
|
+
mount, // Mount component on existing element
|
|
97
|
+
parent, // Access parent component
|
|
98
|
+
children, // Access child components
|
|
99
|
+
dispatch, // Dispatch custom events
|
|
100
|
+
msg, // Localization messages
|
|
101
|
+
localize, // Register translations
|
|
101
102
|
} from 'hybrids';
|
|
102
103
|
```
|
|
103
104
|
|
|
104
105
|
### Why Not Alternatives
|
|
105
106
|
|
|
106
|
-
| Alternative
|
|
107
|
-
|
|
108
|
-
| **Lit**
|
|
109
|
-
| **Stencil**
|
|
110
|
-
| **Vanilla**
|
|
111
|
-
| **React/Vue/Svelte** | Require build steps, not native web components
|
|
107
|
+
| Alternative | Reason to pass |
|
|
108
|
+
| -------------------- | ------------------------------------------------------------------ |
|
|
109
|
+
| **Lit** | Class-based, heavier API surface, decorators encourage build tools |
|
|
110
|
+
| **Stencil** | Requires a compiler — violates no-build constraint |
|
|
111
|
+
| **Vanilla** | No state management, no templating — too much boilerplate |
|
|
112
|
+
| **React/Vue/Svelte** | Require build steps, not native web components |
|
|
112
113
|
|
|
113
114
|
---
|
|
114
115
|
|
|
@@ -185,12 +186,12 @@ import direction: **higher tiers import from lower tiers, never the reverse.**
|
|
|
185
186
|
|
|
186
187
|
### Tiers
|
|
187
188
|
|
|
188
|
-
| Tier
|
|
189
|
-
|
|
190
|
-
| **Atom**
|
|
191
|
-
| **Molecule** | `components/molecules/` | Small composition of 2–4 atoms that form a reusable unit.
|
|
189
|
+
| Tier | Location | Scope | Examples |
|
|
190
|
+
| ------------ | ----------------------- | ------------------------------------------------------------------ | --------------------------------------- |
|
|
191
|
+
| **Atom** | `components/atoms/` | Single-purpose UI primitive. One element, one job. | `app-button`, `app-icon`, `app-input` |
|
|
192
|
+
| **Molecule** | `components/molecules/` | Small composition of 2–4 atoms that form a reusable unit. | `nav-link`, `search-bar`, `form-field` |
|
|
192
193
|
| **Organism** | `components/organisms/` | Complex UI section. May contain molecules, atoms, and local state. | `app-header`, `app-footer`, `user-card` |
|
|
193
|
-
| **Template** | `components/templates/` | Page-level layout shell. Defines slot regions, no business logic.
|
|
194
|
+
| **Template** | `components/templates/` | Page-level layout shell. Defines slot regions, no business logic. | `page-layout`, `dashboard-layout` |
|
|
194
195
|
|
|
195
196
|
**Pages** (`src/pages/`) sit outside the component hierarchy. They are
|
|
196
197
|
route-bound views that compose templates and organisms.
|
|
@@ -214,13 +215,13 @@ Pages → Templates → Organisms → Molecules → Atoms
|
|
|
214
215
|
|
|
215
216
|
Use this checklist to decide if a component belongs at a higher tier:
|
|
216
217
|
|
|
217
|
-
| Signal
|
|
218
|
-
|
|
219
|
-
| It renders a single HTML element with props
|
|
220
|
-
| It composes 2–4 atoms into a reusable group
|
|
221
|
-
| It has its own local state or fetches data
|
|
222
|
-
| It defines layout regions via slots, no logic
|
|
223
|
-
| It's bound to a route and composes a full page | Put it in **pages/**
|
|
218
|
+
| Signal | Action |
|
|
219
|
+
| ---------------------------------------------- | ----------------------- |
|
|
220
|
+
| It renders a single HTML element with props | Keep as **atom** |
|
|
221
|
+
| It composes 2–4 atoms into a reusable group | Make it a **molecule** |
|
|
222
|
+
| It has its own local state or fetches data | Promote to **organism** |
|
|
223
|
+
| It defines layout regions via slots, no logic | Make it a **template** |
|
|
224
|
+
| It's bound to a route and composes a full page | Put it in **pages/** |
|
|
224
225
|
|
|
225
226
|
### File Anatomy (All Tiers)
|
|
226
227
|
|
|
@@ -235,5 +236,3 @@ app-button/
|
|
|
235
236
|
|
|
236
237
|
If a component needs helpers that push it past 150 lines, extract them to
|
|
237
238
|
`src/utils/` — not into sibling files within the component directory.
|
|
238
|
-
|
|
239
|
-
|
|
@@ -28,12 +28,12 @@ npx clearstack init -y # non-interactive (defaults)
|
|
|
28
28
|
|
|
29
29
|
The interactive prompt asks for:
|
|
30
30
|
|
|
31
|
-
| Prompt
|
|
32
|
-
|
|
33
|
-
| Project name | current directory name | Used in package.json and templates
|
|
34
|
-
| Description
|
|
35
|
-
| Mode
|
|
36
|
-
| Port
|
|
31
|
+
| Prompt | Default | Notes |
|
|
32
|
+
| ------------ | ---------------------- | --------------------------------------------------------------------------------- |
|
|
33
|
+
| Project name | current directory name | Used in package.json and templates |
|
|
34
|
+
| Description | `A Clearstack project` | Goes into package.json |
|
|
35
|
+
| Mode | — | **Fullstack**: Express + WebSocket + JSON DB + SSE. **Static**: localStorage only |
|
|
36
|
+
| Port | `3000` | Fullstack only. Set in `.env` |
|
|
37
37
|
|
|
38
38
|
If a `package.json` already exists, Clearstack merges into it — your existing fields (`author`, `license`, `engines`, `keywords`, etc.) are preserved.
|
|
39
39
|
|
|
@@ -120,10 +120,12 @@ git diff docs/ .configs/ # review what changed
|
|
|
120
120
|
```
|
|
121
121
|
|
|
122
122
|
This updates:
|
|
123
|
+
|
|
123
124
|
- `docs/clearstack/*.md` — spec documentation
|
|
124
125
|
- `.configs/*` — linter, formatter, type checker, test runner configs
|
|
125
126
|
|
|
126
127
|
This never touches:
|
|
128
|
+
|
|
127
129
|
- `docs/app-spec/` — your project specs
|
|
128
130
|
- `src/` — your code
|
|
129
131
|
- `scripts/` — your build scripts
|
|
@@ -163,15 +165,15 @@ spec:code → spec:docs → lint → format → typecheck → test
|
|
|
163
165
|
|
|
164
166
|
## Summary
|
|
165
167
|
|
|
166
|
-
| Task
|
|
167
|
-
|
|
168
|
-
| Install Clearstack
|
|
169
|
-
| Scaffold a project
|
|
170
|
-
| Install dependencies
|
|
171
|
-
| Start dev server
|
|
172
|
-
| Lint + format
|
|
173
|
-
| Type check
|
|
174
|
-
| Run tests
|
|
175
|
-
| Full spec check
|
|
176
|
-
| Update spec + configs | `npm run spec:update`
|
|
177
|
-
| Review spec changes
|
|
168
|
+
| Task | Command |
|
|
169
|
+
| --------------------- | -------------------------------------- |
|
|
170
|
+
| Install Clearstack | `npm install -D @techninja/clearstack` |
|
|
171
|
+
| Scaffold a project | `npx clearstack init` |
|
|
172
|
+
| Install dependencies | `npm install` |
|
|
173
|
+
| Start dev server | `npm run dev` / `npx serve public` |
|
|
174
|
+
| Lint + format | `npm run lint:fix && npm run format` |
|
|
175
|
+
| Type check | `npm run typecheck` |
|
|
176
|
+
| Run tests | `npm test` |
|
|
177
|
+
| Full spec check | `npm run spec` |
|
|
178
|
+
| Update spec + configs | `npm run spec:update` |
|
|
179
|
+
| Review spec changes | `git diff docs/ .configs/` |
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# Server & Dependencies
|
|
2
|
+
|
|
2
3
|
## Express Server, Import Maps & Vendor Loading
|
|
3
4
|
|
|
4
5
|
> How the backend serves the frontend and how dependencies are resolved
|
|
@@ -13,13 +14,13 @@
|
|
|
13
14
|
|
|
14
15
|
### Responsibilities
|
|
15
16
|
|
|
16
|
-
| Route
|
|
17
|
-
|
|
18
|
-
| `/`
|
|
19
|
-
| `/vendor/*`
|
|
20
|
-
| `/src/*`
|
|
21
|
-
| `/api/*`
|
|
22
|
-
| `/api/events` | SSE stream for realtime sync
|
|
17
|
+
| Route | Serves |
|
|
18
|
+
| ------------- | -------------------------------------- |
|
|
19
|
+
| `/` | `public/index.html` (app shell) |
|
|
20
|
+
| `/vendor/*` | `public/vendor/` (vendored ES modules) |
|
|
21
|
+
| `/src/*` | `src/` (application source, as-is) |
|
|
22
|
+
| `/api/*` | REST endpoints (see BACKEND_API_SPEC) |
|
|
23
|
+
| `/api/events` | SSE stream for realtime sync |
|
|
23
24
|
|
|
24
25
|
### Key Rules
|
|
25
26
|
|
|
@@ -65,9 +66,7 @@ import { cpSync, mkdirSync } from 'node:fs';
|
|
|
65
66
|
import { resolve } from 'node:path';
|
|
66
67
|
|
|
67
68
|
const VENDOR_DIR = resolve('public/vendor');
|
|
68
|
-
const DEPS = [
|
|
69
|
-
{ name: 'hybrids', src: 'node_modules/hybrids/src' },
|
|
70
|
-
];
|
|
69
|
+
const DEPS = [{ name: 'hybrids', src: 'node_modules/hybrids/src' }];
|
|
71
70
|
|
|
72
71
|
mkdirSync(VENDOR_DIR, { recursive: true });
|
|
73
72
|
|
|
@@ -112,25 +111,25 @@ declared in `public/index.html`.
|
|
|
112
111
|
```html
|
|
113
112
|
<!DOCTYPE html>
|
|
114
113
|
<html lang="en">
|
|
115
|
-
<head>
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
</head>
|
|
130
|
-
<body>
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
</body>
|
|
114
|
+
<head>
|
|
115
|
+
<meta charset="UTF-8" />
|
|
116
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
117
|
+
<title>App</title>
|
|
118
|
+
<link rel="stylesheet" href="/src/styles/reset.css" />
|
|
119
|
+
<link rel="stylesheet" href="/src/styles/tokens.css" />
|
|
120
|
+
|
|
121
|
+
<script type="importmap">
|
|
122
|
+
{
|
|
123
|
+
"imports": {
|
|
124
|
+
"hybrids": "/vendor/hybrids/index.js"
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
</script>
|
|
128
|
+
</head>
|
|
129
|
+
<body>
|
|
130
|
+
<app-router></app-router>
|
|
131
|
+
<script type="module" src="/src/router/index.js"></script>
|
|
132
|
+
</body>
|
|
134
133
|
</html>
|
|
135
134
|
```
|
|
136
135
|
|