@techninja/clearstack 0.2.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.
- package/LICENSE +21 -0
- package/README.md +81 -0
- package/bin/cli.js +62 -0
- package/docs/BACKEND_API_SPEC.md +281 -0
- package/docs/BUILD_LOG.md +193 -0
- package/docs/COMPONENT_PATTERNS.md +481 -0
- package/docs/CONVENTIONS.md +226 -0
- package/docs/FRONTEND_IMPLEMENTATION_RULES.md +239 -0
- package/docs/JSDOC_TYPING.md +86 -0
- package/docs/QUICKSTART.md +190 -0
- package/docs/SERVER_AND_DEPS.md +163 -0
- package/docs/STATE_AND_ROUTING.md +363 -0
- package/docs/TESTING.md +268 -0
- package/docs/app-spec/ENTITIES.md +37 -0
- package/docs/app-spec/README.md +19 -0
- package/lib/check.js +115 -0
- package/lib/copy.js +43 -0
- package/lib/init.js +73 -0
- package/lib/package-gen.js +83 -0
- package/lib/update.js +73 -0
- package/package.json +69 -0
- package/templates/fullstack/data/seed.json +1 -0
- package/templates/fullstack/src/api/db.js +75 -0
- package/templates/fullstack/src/api/entities.js +114 -0
- package/templates/fullstack/src/api/events.js +35 -0
- package/templates/fullstack/src/api/schemas.js +104 -0
- package/templates/fullstack/src/api/validate.js +52 -0
- package/templates/fullstack/src/pages/home/home-view.js +19 -0
- package/templates/fullstack/src/router/index.js +16 -0
- package/templates/fullstack/src/server.js +46 -0
- package/templates/fullstack/src/store/AppState.js +33 -0
- package/templates/fullstack/src/store/UserPrefs.js +31 -0
- package/templates/fullstack/src/store/realtimeSync.js +54 -0
- package/templates/shared/.configs/.prettierrc +8 -0
- package/templates/shared/.configs/eslint.config.js +64 -0
- package/templates/shared/.configs/jsconfig.json +24 -0
- package/templates/shared/.configs/web-test-runner.config.js +8 -0
- package/templates/shared/.env +9 -0
- package/templates/shared/.github/ISSUE_TEMPLATE/bug_report.md +42 -0
- package/templates/shared/.github/ISSUE_TEMPLATE/feature_request.md +30 -0
- package/templates/shared/.github/ISSUE_TEMPLATE/spec_correction.md +26 -0
- package/templates/shared/.github/pull_request_template.md +51 -0
- package/templates/shared/.github/workflows/spec.yml +46 -0
- package/templates/shared/README.md +22 -0
- package/templates/shared/docs/app-spec/README.md +40 -0
- package/templates/shared/docs/clearstack/BACKEND_API_SPEC.md +281 -0
- package/templates/shared/docs/clearstack/BUILD_LOG.md +193 -0
- package/templates/shared/docs/clearstack/COMPONENT_PATTERNS.md +481 -0
- package/templates/shared/docs/clearstack/CONVENTIONS.md +226 -0
- package/templates/shared/docs/clearstack/FRONTEND_IMPLEMENTATION_RULES.md +239 -0
- package/templates/shared/docs/clearstack/JSDOC_TYPING.md +86 -0
- package/templates/shared/docs/clearstack/QUICKSTART.md +190 -0
- package/templates/shared/docs/clearstack/SERVER_AND_DEPS.md +163 -0
- package/templates/shared/docs/clearstack/STATE_AND_ROUTING.md +363 -0
- package/templates/shared/docs/clearstack/TESTING.md +268 -0
- package/templates/shared/public/index.html +26 -0
- package/templates/shared/scripts/build-icons.js +86 -0
- package/templates/shared/scripts/vendor-deps.js +25 -0
- package/templates/shared/src/components/atoms/app-badge/app-badge.css +4 -0
- package/templates/shared/src/components/atoms/app-badge/app-badge.js +23 -0
- package/templates/shared/src/components/atoms/app-badge/app-badge.test.js +26 -0
- package/templates/shared/src/components/atoms/app-badge/index.js +1 -0
- package/templates/shared/src/components/atoms/app-button/app-button.css +3 -0
- package/templates/shared/src/components/atoms/app-button/app-button.js +41 -0
- package/templates/shared/src/components/atoms/app-button/app-button.test.js +43 -0
- package/templates/shared/src/components/atoms/app-button/index.js +1 -0
- package/templates/shared/src/components/atoms/app-icon/app-icon.css +4 -0
- package/templates/shared/src/components/atoms/app-icon/app-icon.js +57 -0
- package/templates/shared/src/components/atoms/app-icon/app-icon.test.js +30 -0
- package/templates/shared/src/components/atoms/app-icon/index.js +1 -0
- package/templates/shared/src/components/atoms/theme-toggle/index.js +1 -0
- package/templates/shared/src/components/atoms/theme-toggle/theme-toggle.css +10 -0
- package/templates/shared/src/components/atoms/theme-toggle/theme-toggle.js +42 -0
- package/templates/shared/src/styles/buttons.css +79 -0
- package/templates/shared/src/styles/components.css +31 -0
- package/templates/shared/src/styles/forms.css +20 -0
- package/templates/shared/src/styles/reset.css +32 -0
- package/templates/shared/src/styles/shared.css +135 -0
- package/templates/shared/src/styles/tokens.css +65 -0
- package/templates/shared/src/utils/formatDate.js +41 -0
- package/templates/shared/src/utils/statusColors.js +60 -0
- package/templates/static/src/pages/home/home-view.js +38 -0
- package/templates/static/src/router/index.js +16 -0
- package/templates/static/src/store/AppState.js +26 -0
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
# Frontend Implementation Rules
|
|
2
|
+
## Hybrids.js "No-Build" Web Component Specification
|
|
3
|
+
|
|
4
|
+
> A concise, LLM-and-human-friendly specification for building web applications
|
|
5
|
+
> with Hybrids v9 using ES modules, no bundler, and atomic design principles.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Table of Contents
|
|
10
|
+
|
|
11
|
+
**This file:** Foundation — philosophy, framework, structure, atomic design.
|
|
12
|
+
|
|
13
|
+
1. [Philosophy & Constraints](#1-philosophy--constraints)
|
|
14
|
+
2. [Framework Choice: Hybrids.js](#2-framework-choice-hybridsjs)
|
|
15
|
+
3. [Project Structure](#3-project-structure)
|
|
16
|
+
4. [Atomic Design Hierarchy](#4-atomic-design-hierarchy)
|
|
17
|
+
|
|
18
|
+
**Sub-specifications:**
|
|
19
|
+
|
|
20
|
+
| Document | Covers |
|
|
21
|
+
|---|---|
|
|
22
|
+
| [COMPONENT_PATTERNS.md](./COMPONENT_PATTERNS.md) | Authoring, light DOM, styling, layout engine, file size rules |
|
|
23
|
+
| [JSDOC_TYPING.md](./JSDOC_TYPING.md) | JSDoc typing strategy, tsc validation, component/model/handler patterns |
|
|
24
|
+
| [STATE_AND_ROUTING.md](./STATE_AND_ROUTING.md) | Store, routing, unified app state, realtime sync |
|
|
25
|
+
| [CONVENTIONS.md](./CONVENTIONS.md) | Naming conventions, anti-patterns |
|
|
26
|
+
| [SERVER_AND_DEPS.md](./SERVER_AND_DEPS.md) | Express server, import maps, vendor dependency loading |
|
|
27
|
+
| [BACKEND_API_SPEC.md](./BACKEND_API_SPEC.md) | REST endpoints, JSON Schema via HEAD, entity CRUD, realtime sync |
|
|
28
|
+
| [TESTING.md](./TESTING.md) | Testing philosophy, tools, patterns, build-phase checkpoints |
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 1. Philosophy & Constraints
|
|
33
|
+
|
|
34
|
+
This specification defines a **zero-build** frontend architecture. No bundler,
|
|
35
|
+
no transpiler, no compile step. Code runs exactly as written.
|
|
36
|
+
|
|
37
|
+
### Core Principles
|
|
38
|
+
|
|
39
|
+
| Principle | Rule |
|
|
40
|
+
|---|---|
|
|
41
|
+
| No build tools | No Webpack, Vite, Rollup, esbuild, or Babel |
|
|
42
|
+
| ES modules only | All `.js` files are native ES modules served to the browser |
|
|
43
|
+
| Import maps | Bare specifiers (e.g. `"hybrids"`) resolved via `<script type="importmap">` |
|
|
44
|
+
| Small files | Every file ≤ **150 lines** before extracting shared logic |
|
|
45
|
+
| Explicit over implicit | Name things clearly; avoid magic strings and hidden conventions |
|
|
46
|
+
| Readable over clever | Optimize for comprehension by humans and LLMs alike |
|
|
47
|
+
| Declarative over imperative | Use framework patterns, not raw DOM manipulation |
|
|
48
|
+
| Composition over inheritance | Build complex UIs by composing small components |
|
|
49
|
+
|
|
50
|
+
### What This Means in Practice
|
|
51
|
+
|
|
52
|
+
- You can open any `.js` file in the browser devtools and see the source as-is.
|
|
53
|
+
- Adding a new component means creating files and importing them — no config.
|
|
54
|
+
- Third-party dependencies are vendored as ES modules at install time.
|
|
55
|
+
- The server is a thin static file host with no middleware transforms.
|
|
56
|
+
|
|
57
|
+
### Constraints
|
|
58
|
+
|
|
59
|
+
- **No TypeScript** — plain JavaScript with JSDoc comments where types help.
|
|
60
|
+
- **No CSS preprocessors** — plain CSS with custom properties for tokens.
|
|
61
|
+
- **No framework CLI** — manual file creation following this spec's conventions.
|
|
62
|
+
- **No dynamic `import()` for core components** — static imports for the
|
|
63
|
+
dependency graph to remain visible and traceable.
|
|
64
|
+
- **Node.js ≥ 18** — required for the Express server and `--watch` flag.
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## 2. Framework Choice: Hybrids.js
|
|
69
|
+
|
|
70
|
+
We use **[Hybrids v9](https://hybrids.js.org)** — a functional, declarative
|
|
71
|
+
web component framework built on plain objects and pure functions.
|
|
72
|
+
|
|
73
|
+
### Why Hybrids
|
|
74
|
+
|
|
75
|
+
| Need | Hybrids Provides |
|
|
76
|
+
|---|---|
|
|
77
|
+
| Components | `define()` — plain object definitions, no classes |
|
|
78
|
+
| Templating | `html``` — tagged template literals with reactive bindings |
|
|
79
|
+
| State | `store()` — global state with async storage, caching, relations |
|
|
80
|
+
| Routing | `router()` — view-graph-based routing with guards and dialogs |
|
|
81
|
+
| Layout | `layout=""` attribute — CSS layout engine in templates |
|
|
82
|
+
| Localization | `localize()` — automatic template translation |
|
|
83
|
+
| ES modules | Ships as raw ES modules in `src/` — no build needed |
|
|
84
|
+
|
|
85
|
+
### API Surface (v9.1)
|
|
86
|
+
|
|
87
|
+
These are the only imports you need:
|
|
88
|
+
|
|
89
|
+
```javascript
|
|
90
|
+
import {
|
|
91
|
+
define, // Register a component
|
|
92
|
+
html, // Template tagged literal
|
|
93
|
+
store, // State management
|
|
94
|
+
router, // Routing
|
|
95
|
+
mount, // Mount component on existing element
|
|
96
|
+
parent, // Access parent component
|
|
97
|
+
children, // Access child components
|
|
98
|
+
dispatch, // Dispatch custom events
|
|
99
|
+
msg, // Localization messages
|
|
100
|
+
localize, // Register translations
|
|
101
|
+
} from 'hybrids';
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Why Not Alternatives
|
|
105
|
+
|
|
106
|
+
| Alternative | Reason to pass |
|
|
107
|
+
|---|---|
|
|
108
|
+
| **Lit** | Class-based, heavier API surface, decorators encourage build tools |
|
|
109
|
+
| **Stencil** | Requires a compiler — violates no-build constraint |
|
|
110
|
+
| **Vanilla** | No state management, no templating — too much boilerplate |
|
|
111
|
+
| **React/Vue/Svelte** | Require build steps, not native web components |
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## 3. Project Structure
|
|
116
|
+
|
|
117
|
+
```
|
|
118
|
+
project-root/
|
|
119
|
+
├── public/ # Static assets served at /
|
|
120
|
+
│ ├── index.html # App shell with import map
|
|
121
|
+
│ └── vendor/ # Vendored ES module deps (generated)
|
|
122
|
+
│ └── hybrids/ # Copied from node_modules at install
|
|
123
|
+
│
|
|
124
|
+
├── src/ # Application source (served at /src/)
|
|
125
|
+
│ ├── components/ # UI components (atomic design)
|
|
126
|
+
│ │ ├── atoms/ # Smallest UI primitives
|
|
127
|
+
│ │ │ └── app-button/
|
|
128
|
+
│ │ │ ├── app-button.js # Component definition
|
|
129
|
+
│ │ │ ├── app-button.css # Scoped styles
|
|
130
|
+
│ │ │ └── index.js # Re-export
|
|
131
|
+
│ │ ├── molecules/ # Compositions of atoms
|
|
132
|
+
│ │ │ └── search-bar/
|
|
133
|
+
│ │ ├── organisms/ # Complex UI sections
|
|
134
|
+
│ │ │ └── app-header/
|
|
135
|
+
│ │ └── templates/ # Page-level layout shells
|
|
136
|
+
│ │ └── page-layout/
|
|
137
|
+
│ │
|
|
138
|
+
│ ├── pages/ # Route-bound view components
|
|
139
|
+
│ │ ├── home/
|
|
140
|
+
│ │ │ ├── home-view.js
|
|
141
|
+
│ │ │ └── index.js
|
|
142
|
+
│ │ └── about/
|
|
143
|
+
│ │
|
|
144
|
+
│ ├── store/ # Hybrids store model definitions
|
|
145
|
+
│ │ ├── AppState.js # Singleton: global app state
|
|
146
|
+
│ │ └── UserModel.js # Enumerable: user records
|
|
147
|
+
│ │
|
|
148
|
+
│ ├── router/ # Router shell component
|
|
149
|
+
│ │ └── index.js
|
|
150
|
+
│ │
|
|
151
|
+
│ ├── styles/ # Shared CSS
|
|
152
|
+
│ │ ├── tokens.css # Design tokens (colors, spacing)
|
|
153
|
+
│ │ └── reset.css # Minimal CSS reset
|
|
154
|
+
│ │
|
|
155
|
+
│ └── utils/ # Pure helper functions
|
|
156
|
+
│ └── formatDate.js
|
|
157
|
+
│
|
|
158
|
+
├── scripts/ # Build/install scripts
|
|
159
|
+
│ └── vendor-deps.js # Copies deps to public/vendor/
|
|
160
|
+
│
|
|
161
|
+
├── src/server.js # Express entry point
|
|
162
|
+
├── package.json # type: "module", postinstall hook
|
|
163
|
+
├── README.md # Project overview
|
|
164
|
+
└── FRONTEND_IMPLEMENTATION_RULES.md # This file
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Key Conventions
|
|
168
|
+
|
|
169
|
+
- **One component per directory.** Each gets its own folder with `.js`, `.css`,
|
|
170
|
+
and `index.js` (re-export).
|
|
171
|
+
- **`src/` is served as-is** — the browser loads these files directly.
|
|
172
|
+
- **`public/vendor/`** is gitignored and regenerated on `npm install` via the
|
|
173
|
+
`postinstall` script.
|
|
174
|
+
- **No barrel files** beyond the per-component `index.js`. Import from the
|
|
175
|
+
component directory, not from a giant `components/index.js`.
|
|
176
|
+
- **Pages are not components.** They live in `src/pages/`, not in
|
|
177
|
+
`src/components/`. They compose components but are themselves route targets.
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## 4. Atomic Design Hierarchy
|
|
182
|
+
|
|
183
|
+
Components are organized into four tiers. Each tier has a clear scope and
|
|
184
|
+
import direction: **higher tiers import from lower tiers, never the reverse.**
|
|
185
|
+
|
|
186
|
+
### Tiers
|
|
187
|
+
|
|
188
|
+
| Tier | Location | Scope | Examples |
|
|
189
|
+
|---|---|---|---|
|
|
190
|
+
| **Atom** | `components/atoms/` | Single-purpose UI primitive. One element, one job. | `app-button`, `app-icon`, `app-input` |
|
|
191
|
+
| **Molecule** | `components/molecules/` | Small composition of 2–4 atoms that form a reusable unit. | `nav-link`, `search-bar`, `form-field` |
|
|
192
|
+
| **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. | `page-layout`, `dashboard-layout` |
|
|
194
|
+
|
|
195
|
+
**Pages** (`src/pages/`) sit outside the component hierarchy. They are
|
|
196
|
+
route-bound views that compose templates and organisms.
|
|
197
|
+
|
|
198
|
+
### Import Direction
|
|
199
|
+
|
|
200
|
+
```
|
|
201
|
+
Pages → Templates → Organisms → Molecules → Atoms
|
|
202
|
+
↑
|
|
203
|
+
Store / Utils
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
- Atoms import **nothing** from other component tiers.
|
|
207
|
+
- Molecules import only from **atoms**.
|
|
208
|
+
- Organisms import from **molecules** and **atoms**.
|
|
209
|
+
- Templates import from **organisms**, **molecules**, and **atoms**.
|
|
210
|
+
- Pages import from **any component tier** and from **store/router**.
|
|
211
|
+
- **Store models and utils** are shared — any tier may import them.
|
|
212
|
+
|
|
213
|
+
### When to Promote a Component
|
|
214
|
+
|
|
215
|
+
Use this checklist to decide if a component belongs at a higher tier:
|
|
216
|
+
|
|
217
|
+
| Signal | Action |
|
|
218
|
+
|---|---|
|
|
219
|
+
| It renders a single HTML element with props | Keep as **atom** |
|
|
220
|
+
| It composes 2–4 atoms into a reusable group | Make it a **molecule** |
|
|
221
|
+
| It has its own local state or fetches data | Promote to **organism** |
|
|
222
|
+
| It defines layout regions via slots, no logic | Make it a **template** |
|
|
223
|
+
| It's bound to a route and composes a full page | Put it in **pages/** |
|
|
224
|
+
|
|
225
|
+
### File Anatomy (All Tiers)
|
|
226
|
+
|
|
227
|
+
Every component directory contains exactly three files:
|
|
228
|
+
|
|
229
|
+
```
|
|
230
|
+
app-button/
|
|
231
|
+
├── app-button.js # Component definition (define + html + logic)
|
|
232
|
+
├── app-button.css # Scoped styles for this component
|
|
233
|
+
└── index.js # export { default } from './app-button.js';
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
If a component needs helpers that push it past 150 lines, extract them to
|
|
237
|
+
`src/utils/` — not into sibling files within the component directory.
|
|
238
|
+
|
|
239
|
+
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# JSDoc Typing Strategy
|
|
2
|
+
## Type Safety Without TypeScript
|
|
3
|
+
|
|
4
|
+
> JSDoc annotations provide editor intellisense, LLM comprehension, and
|
|
5
|
+
> compile-time type checking — all without a build step.
|
|
6
|
+
> See [COMPONENT_PATTERNS.md](./COMPONENT_PATTERNS.md) for component authoring.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## How It Works
|
|
11
|
+
|
|
12
|
+
TypeScript's `tsc` compiler validates JSDoc annotations via `--checkJs`,
|
|
13
|
+
giving us compile-time type checking without a build step.
|
|
14
|
+
|
|
15
|
+
A `jsconfig.json` at the project root enables `checkJs: true`. Running
|
|
16
|
+
`npm run typecheck` invokes `tsc --project jsconfig.json` which:
|
|
17
|
+
|
|
18
|
+
1. Reads all `.js` files in `src/` and `scripts/`
|
|
19
|
+
2. Parses JSDoc annotations as type information
|
|
20
|
+
3. Reports type errors exactly like TypeScript would
|
|
21
|
+
4. Emits nothing — `noEmit: true`
|
|
22
|
+
|
|
23
|
+
This means `@typedef`, `@type`, `@param`, and `@returns` are not just
|
|
24
|
+
documentation — they are **enforced types**.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Component Properties
|
|
29
|
+
|
|
30
|
+
```javascript
|
|
31
|
+
/**
|
|
32
|
+
* @typedef {Object} AppButtonHost
|
|
33
|
+
* @property {string} label - Button display text
|
|
34
|
+
* @property {'primary'|'secondary'|'ghost'} variant - Visual style
|
|
35
|
+
* @property {boolean} disabled - Disabled state
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
/** @type {import('hybrids').Component<AppButtonHost>} */
|
|
39
|
+
export default define({
|
|
40
|
+
tag: 'app-button',
|
|
41
|
+
label: '',
|
|
42
|
+
variant: 'primary',
|
|
43
|
+
disabled: false,
|
|
44
|
+
render: ({ label, variant, disabled }) => html`
|
|
45
|
+
<button class="${variant}" disabled="${disabled}">${label}</button>
|
|
46
|
+
`,
|
|
47
|
+
});
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Store Models
|
|
51
|
+
|
|
52
|
+
```javascript
|
|
53
|
+
/**
|
|
54
|
+
* @typedef {Object} User
|
|
55
|
+
* @property {string} id - Unique identifier
|
|
56
|
+
* @property {string} firstName
|
|
57
|
+
* @property {string} lastName
|
|
58
|
+
* @property {string} email
|
|
59
|
+
*/
|
|
60
|
+
|
|
61
|
+
/** @type {import('hybrids').Model<User>} */
|
|
62
|
+
const UserModel = { id: true, firstName: '', lastName: '', email: '' };
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Event Handlers
|
|
66
|
+
|
|
67
|
+
```javascript
|
|
68
|
+
/**
|
|
69
|
+
* Increment the counter on the host element.
|
|
70
|
+
* @param {AppCounterHost & HTMLElement} host
|
|
71
|
+
* @param {MouseEvent} event
|
|
72
|
+
*/
|
|
73
|
+
function handleClick(host, event) {
|
|
74
|
+
host.count++;
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Rules
|
|
79
|
+
|
|
80
|
+
- Every exported component gets a `@typedef` for its host interface.
|
|
81
|
+
- Every store model gets a `@typedef` for its shape.
|
|
82
|
+
- Event handlers document `host` and `event` param types.
|
|
83
|
+
- Keep JSDoc blocks to 3–5 lines. No novels.
|
|
84
|
+
- Use `/** @type {any} */ (expr)` for framework type limitations (e.g.
|
|
85
|
+
`store.pending()` on array results) — document why with a comment.
|
|
86
|
+
- Run `npm run typecheck` before committing. Zero errors required.
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
# Quickstart
|
|
2
|
+
|
|
3
|
+
Get a spec-compliant project running, develop against it, and keep it in sync as the spec evolves.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
- Node.js ≥ 20
|
|
8
|
+
- npm ≥ 10
|
|
9
|
+
|
|
10
|
+
## 1. Scaffold a New Project
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npx clearstack init
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
The interactive prompt asks for:
|
|
17
|
+
|
|
18
|
+
| Prompt | Default | Notes |
|
|
19
|
+
|---|---|---|
|
|
20
|
+
| Project name | `my-app` | Creates a directory with this name |
|
|
21
|
+
| Description | `A Clearstack project` | Goes into package.json |
|
|
22
|
+
| Mode | — | **Fullstack**: Express + WebSocket + JSON DB + SSE. **Static**: localStorage only, no server |
|
|
23
|
+
| Port | `3000` | Fullstack only. Set in `.env` |
|
|
24
|
+
| Include examples? | Yes | Starter components demonstrating spec patterns |
|
|
25
|
+
|
|
26
|
+
This generates a complete project directory:
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
my-app/
|
|
30
|
+
├── .configs/ # eslint, prettier, jsconfig, web-test-runner
|
|
31
|
+
├── .github/ # CI workflow, PR + issue templates
|
|
32
|
+
├── docs/ # Spec docs (upstream-managed)
|
|
33
|
+
│ ├── clearstack/ # Clearstack spec docs (synced on update)
|
|
34
|
+
│ └── app-spec/ # Your project-specific specs (never overwritten)
|
|
35
|
+
├── public/ # Static assets, index.html, import map
|
|
36
|
+
├── scripts/ # vendor-deps, build-icons, spec checker
|
|
37
|
+
├── src/
|
|
38
|
+
│ ├── components/ # atoms/, molecules/, organisms/, pages/
|
|
39
|
+
│ ├── store/ # Hybrids store models
|
|
40
|
+
│ ├── styles/ # Global CSS with native nesting
|
|
41
|
+
│ ├── router/ # Client-side routing
|
|
42
|
+
│ └── utils/ # Shared helpers
|
|
43
|
+
├── tests/ # Node + browser tests
|
|
44
|
+
├── data/ # JSON DB (fullstack only)
|
|
45
|
+
├── src/server.js # Express server (fullstack only)
|
|
46
|
+
├── .env # PORT, spec thresholds
|
|
47
|
+
└── package.json
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## 2. Install and Run
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
cd my-app
|
|
54
|
+
npm install
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
`postinstall` automatically runs `vendor-deps.js` (copies hybrids to `public/vendor/`) and `build-icons.js` (extracts Lucide SVGs to `public/icons.json`).
|
|
58
|
+
|
|
59
|
+
### Fullstack mode
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
npm run dev # node --watch src/server.js
|
|
63
|
+
# → http://localhost:3000
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Static mode
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
npx serve public # or any static file server
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## 3. Development Workflow
|
|
73
|
+
|
|
74
|
+
### Create a component
|
|
75
|
+
|
|
76
|
+
Every component is a plain ES module that exports a Hybrids descriptor. Place it in the appropriate atomic design tier:
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
src/components/atoms/ # buttons, icons, badges
|
|
80
|
+
src/components/molecules/ # form fields, cards
|
|
81
|
+
src/components/organisms/ # lists, editors, canvases
|
|
82
|
+
src/components/pages/ # route-level views
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Register it in `src/components/index.js` and add a `<script>` or import map entry in `public/index.html` if needed.
|
|
86
|
+
|
|
87
|
+
### Key rules while coding
|
|
88
|
+
|
|
89
|
+
- **≤150 lines per `.js` / `.css` file.** Add `// SPLIT CANDIDATE:` at 120 lines. When you hit the limit, extract a module.
|
|
90
|
+
- **Light DOM by default.** No `shadowRoot`. Shared styles in `src/styles/` apply everywhere.
|
|
91
|
+
- **JSDoc for types.** `@typedef`, `@param`, `@returns` — validated by `tsc --checkJs`.
|
|
92
|
+
- **No build step.** Every file runs as-is in the browser via ES modules and import maps.
|
|
93
|
+
|
|
94
|
+
### Run checks during development
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
npm run lint:fix # ESLint auto-fix
|
|
98
|
+
npm run format # Prettier auto-format
|
|
99
|
+
npm run typecheck # JSDoc type validation
|
|
100
|
+
npm test # Node + browser tests
|
|
101
|
+
npm run spec # Full interactive spec compliance check
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Or run individual spec checks:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
npm run spec:code # Code files ≤150 lines
|
|
108
|
+
npm run spec:docs # Doc files ≤500 lines
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## 4. Project-Specific Specs
|
|
112
|
+
|
|
113
|
+
The `docs/app-spec/` directory is yours. Upstream updates never touch it. Use it for:
|
|
114
|
+
|
|
115
|
+
- Entity schemas and relationships
|
|
116
|
+
- Project-specific patterns or conventions
|
|
117
|
+
- API documentation beyond the base spec
|
|
118
|
+
- Architecture decisions (ADRs)
|
|
119
|
+
- Deviations from the spec (with rationale)
|
|
120
|
+
|
|
121
|
+
See [docs/app-spec/README.md](./app-spec/README.md) for the full guide.
|
|
122
|
+
|
|
123
|
+
## 5. Update Spec Docs
|
|
124
|
+
|
|
125
|
+
When the spec evolves upstream, pull the latest docs into your project:
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
npx clearstack update
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
This:
|
|
132
|
+
- Copies updated spec docs from the package into your `docs/clearstack/` directory
|
|
133
|
+
- Syncs `.configs/` to latest standards
|
|
134
|
+
- Skips `docs/app-spec/` entirely — your project-specific specs are safe
|
|
135
|
+
- Writes a `.specversion` file so you can track which version you're on
|
|
136
|
+
- Prints which files changed so you can review with `git diff docs/ .configs/`
|
|
137
|
+
|
|
138
|
+
Review the diff, adjust your code if needed, then commit.
|
|
139
|
+
|
|
140
|
+
## 6. Check Spec Compliance
|
|
141
|
+
|
|
142
|
+
Run the full compliance suite at any time:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
npx clearstack check
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
This runs from the scaffolder package itself (no local scripts needed). It checks:
|
|
149
|
+
|
|
150
|
+
1. **Code line counts** — all `.js` / `.css` files ≤150 lines
|
|
151
|
+
2. **Doc line counts** — all `.md` files ≤500 lines
|
|
152
|
+
3. **ESLint** — linting with the spec's config
|
|
153
|
+
4. **Prettier** — formatting with the spec's config
|
|
154
|
+
5. **JSDoc types** — `tsc --checkJs` against jsconfig
|
|
155
|
+
|
|
156
|
+
The same checks run in CI via the GitHub Actions workflow scaffolded into `.github/`.
|
|
157
|
+
|
|
158
|
+
### Configuring thresholds
|
|
159
|
+
|
|
160
|
+
Override defaults in `.env`:
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
SPEC_CODE_MAX_LINES=150
|
|
164
|
+
SPEC_DOCS_MAX_LINES=500
|
|
165
|
+
SPEC_CODE_EXTENSIONS=.js,.css
|
|
166
|
+
SPEC_DOCS_EXTENSIONS=.md
|
|
167
|
+
SPEC_IGNORE_DIRS=node_modules,public/vendor,.git,.configs
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## 7. CI Pipeline
|
|
171
|
+
|
|
172
|
+
The scaffolded `.github/workflows/` runs all spec checks on every PR. A PR cannot merge unless all checks pass. The workflow runs:
|
|
173
|
+
|
|
174
|
+
```
|
|
175
|
+
npm run spec:code → npm run spec:docs → npm run lint → npm run format → npm run typecheck → npm test
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Summary
|
|
179
|
+
|
|
180
|
+
| Task | Command |
|
|
181
|
+
|---|---|
|
|
182
|
+
| Scaffold a project | `npx clearstack init` |
|
|
183
|
+
| Install dependencies | `npm install` |
|
|
184
|
+
| Start dev server | `npm run dev` (fullstack) / `npx serve public` (static) |
|
|
185
|
+
| Lint + format | `npm run lint:fix && npm run format` |
|
|
186
|
+
| Type check | `npm run typecheck` |
|
|
187
|
+
| Run tests | `npm test` |
|
|
188
|
+
| Full spec check | `npm run spec` or `npx clearstack check` |
|
|
189
|
+
| Update spec docs | `npx clearstack update` |
|
|
190
|
+
| Review spec changes | `git diff docs/ .configs/` |
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# Server & Dependencies
|
|
2
|
+
## Express Server, Import Maps & Vendor Loading
|
|
3
|
+
|
|
4
|
+
> How the backend serves the frontend and how dependencies are resolved
|
|
5
|
+
> without a build step.
|
|
6
|
+
> See [BACKEND_API_SPEC.md](./BACKEND_API_SPEC.md) for the REST API contract.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Express Server
|
|
11
|
+
|
|
12
|
+
`src/server.js` is a minimal Express app that serves static files and the API.
|
|
13
|
+
|
|
14
|
+
### Responsibilities
|
|
15
|
+
|
|
16
|
+
| Route | Serves |
|
|
17
|
+
|---|---|
|
|
18
|
+
| `/` | `public/index.html` (app shell) |
|
|
19
|
+
| `/vendor/*` | `public/vendor/` (vendored ES modules) |
|
|
20
|
+
| `/src/*` | `src/` (application source, as-is) |
|
|
21
|
+
| `/api/*` | REST endpoints (see BACKEND_API_SPEC) |
|
|
22
|
+
| `/api/events` | SSE stream for realtime sync |
|
|
23
|
+
|
|
24
|
+
### Key Rules
|
|
25
|
+
|
|
26
|
+
- **No middleware transforms.** Files are served byte-for-byte.
|
|
27
|
+
- **Correct MIME types.** `.js` files must be served as `application/javascript`.
|
|
28
|
+
- **No templating engine.** The HTML shell is a static file.
|
|
29
|
+
- **CORS not needed** — frontend and API share the same origin.
|
|
30
|
+
|
|
31
|
+
### src/server.js Structure
|
|
32
|
+
|
|
33
|
+
```javascript
|
|
34
|
+
import express from 'express';
|
|
35
|
+
|
|
36
|
+
const app = express();
|
|
37
|
+
const PORT = process.env.PORT || 3000;
|
|
38
|
+
|
|
39
|
+
// Static: vendored deps and app shell
|
|
40
|
+
app.use(express.static('public'));
|
|
41
|
+
|
|
42
|
+
// Static: application source (ES modules served directly)
|
|
43
|
+
app.use('/src', express.static('src'));
|
|
44
|
+
|
|
45
|
+
// API routes
|
|
46
|
+
// (see BACKEND_API_SPEC.md for full contract)
|
|
47
|
+
app.use(express.json());
|
|
48
|
+
|
|
49
|
+
// ... mount API routes here ...
|
|
50
|
+
|
|
51
|
+
app.listen(PORT, () => console.log(`http://localhost:${PORT}`));
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Vendor Dependency Loading
|
|
57
|
+
|
|
58
|
+
Third-party ES module packages are copied from `node_modules/` into
|
|
59
|
+
`public/vendor/` at install time. This makes them servable as static files.
|
|
60
|
+
|
|
61
|
+
### scripts/vendor-deps.js
|
|
62
|
+
|
|
63
|
+
```javascript
|
|
64
|
+
import { cpSync, mkdirSync } from 'node:fs';
|
|
65
|
+
import { resolve } from 'node:path';
|
|
66
|
+
|
|
67
|
+
const VENDOR_DIR = resolve('public/vendor');
|
|
68
|
+
const DEPS = [
|
|
69
|
+
{ name: 'hybrids', src: 'node_modules/hybrids/src' },
|
|
70
|
+
];
|
|
71
|
+
|
|
72
|
+
mkdirSync(VENDOR_DIR, { recursive: true });
|
|
73
|
+
|
|
74
|
+
for (const dep of DEPS) {
|
|
75
|
+
const dest = resolve(VENDOR_DIR, dep.name);
|
|
76
|
+
cpSync(dep.src, dest, { recursive: true });
|
|
77
|
+
console.log(`Vendored: ${dep.name} → ${dest}`);
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Wiring
|
|
82
|
+
|
|
83
|
+
In `package.json`:
|
|
84
|
+
|
|
85
|
+
```json
|
|
86
|
+
{
|
|
87
|
+
"scripts": {
|
|
88
|
+
"postinstall": "node scripts/vendor-deps.js"
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Running `npm install` automatically vendors dependencies. The `public/vendor/`
|
|
94
|
+
directory should be in `.gitignore`.
|
|
95
|
+
|
|
96
|
+
### Adding a New Dependency
|
|
97
|
+
|
|
98
|
+
1. `npm install <package>`
|
|
99
|
+
2. Add an entry to the `DEPS` array in `scripts/vendor-deps.js`
|
|
100
|
+
3. Add a mapping in the import map (see below)
|
|
101
|
+
4. Run `npm run postinstall` (or re-run `npm install`)
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Import Map
|
|
106
|
+
|
|
107
|
+
The browser resolves bare specifiers like `'hybrids'` via an import map
|
|
108
|
+
declared in `public/index.html`.
|
|
109
|
+
|
|
110
|
+
### public/index.html
|
|
111
|
+
|
|
112
|
+
```html
|
|
113
|
+
<!DOCTYPE html>
|
|
114
|
+
<html lang="en">
|
|
115
|
+
<head>
|
|
116
|
+
<meta charset="UTF-8">
|
|
117
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
118
|
+
<title>App</title>
|
|
119
|
+
<link rel="stylesheet" href="/src/styles/reset.css">
|
|
120
|
+
<link rel="stylesheet" href="/src/styles/tokens.css">
|
|
121
|
+
|
|
122
|
+
<script type="importmap">
|
|
123
|
+
{
|
|
124
|
+
"imports": {
|
|
125
|
+
"hybrids": "/vendor/hybrids/index.js"
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
</script>
|
|
129
|
+
</head>
|
|
130
|
+
<body>
|
|
131
|
+
<app-router></app-router>
|
|
132
|
+
<script type="module" src="/src/router/index.js"></script>
|
|
133
|
+
</body>
|
|
134
|
+
</html>
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### How It Works
|
|
138
|
+
|
|
139
|
+
1. Browser encounters `import { html } from 'hybrids'` in any ES module.
|
|
140
|
+
2. Import map resolves `'hybrids'` → `/vendor/hybrids/index.js`.
|
|
141
|
+
3. Server serves the file from `public/vendor/hybrids/index.js`.
|
|
142
|
+
4. Hybrids' internal imports use relative paths — they resolve naturally.
|
|
143
|
+
|
|
144
|
+
### Rules
|
|
145
|
+
|
|
146
|
+
- **One import map per page.** It must be declared before any `<script type="module">`.
|
|
147
|
+
- **No dynamic import map generation.** The map is static HTML.
|
|
148
|
+
- **Version pinning** is handled by `package-lock.json` + the vendor copy.
|
|
149
|
+
The vendored files always match the installed version.
|
|
150
|
+
|
|
151
|
+
### Cache Busting
|
|
152
|
+
|
|
153
|
+
For production, append a version query param to the import map entries:
|
|
154
|
+
|
|
155
|
+
```json
|
|
156
|
+
{
|
|
157
|
+
"imports": {
|
|
158
|
+
"hybrids": "/vendor/hybrids/index.js?v=9.1.22"
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Or use versioned directory names: `/vendor/hybrids@9.1.22/index.js`.
|