@techninja/clearstack 0.3.17 → 0.3.20

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/BUILD_LOG.md CHANGED
@@ -204,6 +204,14 @@ These are the significant corrections:
204
204
  - **Fix:** Cast params to `any` before accessing custom properties
205
205
  - **Documented in:** JSDOC_TYPING.md → `list` Connector Params
206
206
 
207
+ ### Array model prototype items persist as real data
208
+
209
+ - **Expected:** `items: [{ sku: '', quantity: 0 }]` only defines the schema
210
+ - **Actual:** The prototype item exists as a real entry in the array at runtime
211
+ - **Impact:** Cart showed a ghost item with empty sku and 0 quantity; Stripe rejected the 0-price line item
212
+ - **Fix:** Always filter array model data with `.filter(i => i.sku)` before use
213
+ - **Documented in:** STATE_AND_ROUTING.md → Store Array Properties
214
+
207
215
  ---
208
216
 
209
217
  ## Metrics
@@ -316,8 +324,10 @@ Five new hybrids gotchas were found and documented:
316
324
  3. **Enumerable models need `@type {any}`** — tsc can't type `id: true`
317
325
  4. **List store descriptors need 3 casts** — descriptor, ready(), and map()
318
326
  5. **`list` connector params need cast** — `ModelIdentifier` not plain object
327
+ 6. **Array prototype items persist as real data** — the prototype item
328
+ used to define the array shape is included in the actual data
319
329
 
320
- All five are hybrids type system friction, not architectural issues. The
330
+ All six are hybrids type system friction, not architectural issues. The
321
331
  runtime behavior is correct — it's the JSDoc/tsc layer that struggles.
322
332
  Each was fixed in under 2 minutes once identified.
323
333
 
@@ -197,6 +197,20 @@ function toggle(host) {
197
197
  }
198
198
  ```
199
199
 
200
+ ```javascript
201
+ // BAD — silent catch hides the failure completely
202
+ try {
203
+ store.clear([Model]);
204
+ } catch {
205
+ /* list may not exist */
206
+ }
207
+ ```
208
+
209
+ **Never use empty `catch` blocks.** If you catch to prevent crashing,
210
+ you must `console.warn` or `console.error` so the failure is visible.
211
+ Silent catches turn bugs into ghosts — things "just don't work" with
212
+ zero console output to trace.
213
+
200
214
  ### ✅ Do
201
215
 
202
216
  ```javascript
@@ -215,6 +229,15 @@ function toggle(host) {
215
229
  }
216
230
  ```
217
231
 
232
+ ```javascript
233
+ // GOOD — catch to prevent crash, but log so failures are visible
234
+ try {
235
+ store.clear([Model]);
236
+ } catch (e) {
237
+ console.warn('[store] clear failed:', e.message);
238
+ }
239
+ ```
240
+
218
241
  ---
219
242
 
220
243
  ### ✅ Always Do This
@@ -251,10 +274,24 @@ export const fullName = (user) => `${user.firstName} ${user.lastName}`;
251
274
 
252
275
  ---
253
276
 
254
- ## File Size: Soft Warnings Before Hard Limits
277
+ ## File Size: Why 150 Lines
278
+
279
+ The 150-line limit isn't about the number — it's about **context cost**.
280
+ A 400-line file isn't hard to scroll through, but it's expensive to
281
+ _hold in mind_. Humans can only reason about a limited scope at once.
282
+ LLMs face the same constraint differently: every token spent reading a
283
+ bloated file is a token not spent on the actual task. Small files mean
284
+ context is spent on _building_, not on _understanding what you're
285
+ looking at_.
286
+
287
+ When every file is small enough to comprehend in one pass, both humans
288
+ and LLMs can generate, review, and modify code without re-reading
289
+ unrelated sections. Refactors become safe because the blast radius of
290
+ any change is one small file. The limit forces decomposition into
291
+ concept-sized units — not arbitrary chunks, but files where each one
292
+ answers a single question.
255
293
 
256
- The 150-line limit is a hard gate in CI, but treat **~120 lines as a yellow
257
- light.** When a file passes 120 lines:
294
+ Treat **~120 lines as a yellow light.** When a file passes 120 lines:
258
295
 
259
296
  1. Add a `// SPLIT CANDIDATE:` comment noting where a logical split could happen
260
297
  2. Continue working — don't split mid-feature
@@ -270,7 +307,9 @@ function moveObj(o, dx, dy) { ... }
270
307
  ### When a File Exceeds 150 Lines
271
308
 
272
309
  The **only correct response** is to split the file into two or more files.
273
- Never do any of the following to reduce line count:
310
+ The concepts have outgrown the container find the seam between them
311
+ and give each its own file. Never do any of the following to reduce
312
+ line count:
274
313
 
275
314
  - Remove or shorten JSDoc comments
276
315
  - Collapse multi-line expressions onto one line
@@ -0,0 +1,486 @@
1
+ # Platform Stacking
2
+
3
+ ## How Clearstack Projects Become Scaffoldable Platforms
4
+
5
+ > A Clearstack project can declare itself as a **platform** — a reusable
6
+ > foundation that scaffolds child projects with vendor files, config schemas,
7
+ > and override layers. This spec defines the contract.
8
+
9
+ ---
10
+
11
+ ## The Problem
12
+
13
+ Clearstack scaffolds vanilla projects. But some Clearstack projects are
14
+ themselves platforms — they provide components, store models, API handlers,
15
+ and build scripts that other projects consume and customize.
16
+
17
+ Without a stacking API, each platform reinvents its own CLI, its own
18
+ vendor sync, its own override conventions, and its own update-without-
19
+ clobbering logic. That's the same problem Clearstack already solved for
20
+ itself.
21
+
22
+ ## The Stack
23
+
24
+ ```
25
+ Clearstack (spec + CLI)
26
+ └── Platform (Clearstack project that declares itself scaffoldable)
27
+ └── Project (consumes the platform, overrides what it needs)
28
+ ```
29
+
30
+ Concrete example:
31
+
32
+ ```
33
+ Clearstack v0.3.17
34
+ └── StatiCart v1.x (e-commerce platform)
35
+ └── "My Coffee Shop" (store built on StatiCart)
36
+ ```
37
+
38
+ Each layer owns different things and has different update rules.
39
+
40
+ ---
41
+
42
+ ## 1. Platform Manifest
43
+
44
+ A Clearstack project becomes a platform by adding a `platform` key to its
45
+ `package.json`. This is the only requirement.
46
+
47
+ ```json
48
+ {
49
+ "name": "@techninja/staticart",
50
+ "version": "1.0.0",
51
+ "clearstack": {
52
+ "platform": {
53
+ "prefix": "staticart",
54
+ "vendorDir": "src/vendor/staticart",
55
+ "configFile": "staticart.config.json",
56
+ "configSchema": "staticart.schema.json",
57
+ "templates": "templates/",
58
+ "vendor": "vendor/",
59
+ "docs": "docs/staticart/",
60
+ "scripts": "scripts/",
61
+ "api": "api/"
62
+ }
63
+ }
64
+ }
65
+ ```
66
+
67
+ ### Manifest Fields
68
+
69
+ | Field | Required | Purpose |
70
+ | -------------- | -------- | ---------------------------------------------------------------- |
71
+ | `prefix` | Yes | Import map prefix (`#staticart/`). Must be unique per platform. |
72
+ | `vendorDir` | Yes | Where platform files land in the child project. Gitignored. |
73
+ | `configFile` | Yes | Project-level config file name. Never overwritten on update. |
74
+ | `configSchema` | No | JSON Schema for the config file. Used for validation. |
75
+ | `templates` | Yes | Directory in the npm package containing scaffold templates. |
76
+ | `vendor` | Yes | Directory in the npm package containing vendorable source files. |
77
+ | `docs` | No | Platform docs synced to child project on update. |
78
+ | `scripts` | No | Build/admin scripts copied on init, skipped on update. |
79
+ | `api` | No | API handler templates copied on init, skipped on update. |
80
+
81
+ ### The `prefix` Contract
82
+
83
+ The platform prefix becomes an import map namespace in the child project.
84
+ It follows the same `#prefix/` convention Clearstack uses for `#store/`,
85
+ `#atoms/`, etc.
86
+
87
+ ```html
88
+ <script type="importmap">
89
+ {
90
+ "imports": {
91
+ "#staticart/": "/vendor/staticart/",
92
+ "#store/": "/store/",
93
+ "#atoms/": "/components/atoms/"
94
+ }
95
+ }
96
+ </script>
97
+ ```
98
+
99
+ The child project can override any platform module by remapping a specific
100
+ path before the wildcard:
101
+
102
+ ```html
103
+ <script type="importmap">
104
+ {
105
+ "imports": {
106
+ "#staticart/product-card": "/components/my-product-card.js",
107
+ "#staticart/": "/vendor/staticart/"
108
+ }
109
+ }
110
+ </script>
111
+ ```
112
+
113
+ Import map specificity rules (longer prefix wins) make this work natively.
114
+
115
+ ---
116
+
117
+ ## 2. File Ownership Model
118
+
119
+ Every file in a stacked project belongs to exactly one layer. The layer
120
+ determines who creates it, who updates it, and who must never touch it.
121
+
122
+ ### Ownership Table
123
+
124
+ | Owner | Creates | Updates | Examples |
125
+ | -------------- | ---------------------- | ----------------------------- | -------------------------------------------------------- |
126
+ | **Clearstack** | `init` | `update` (always) | `docs/clearstack/`, `.configs/` |
127
+ | **Platform** | `init` | `update` (vendor + docs only) | `src/vendor/<prefix>/`, `docs/<prefix>/` |
128
+ | **Project** | `init` (from template) | Never (project owns it) | `src/components/`, config file, `tokens.css`, data files |
129
+
130
+ ### The Three Update Behaviors
131
+
132
+ **Always overwrite:** Spec docs and vendor files. These are the platform's
133
+ source of truth. The child project must not edit them directly.
134
+
135
+ - `docs/clearstack/*.md`
136
+ - `docs/<prefix>/*.md`
137
+ - `src/vendor/<prefix>/`
138
+
139
+ **Create once, never overwrite:** Templates that the project customizes.
140
+ On `update`, these are skipped unless `--force` is passed.
141
+
142
+ - `.configs/*`
143
+ - `scripts/*`
144
+ - `api/` handlers
145
+ - `src/index.html`
146
+
147
+ **Never touch:** Project-owned files. No init or update command writes
148
+ to these after initial creation.
149
+
150
+ - Config file (`staticart.config.json`)
151
+ - `src/styles/tokens.css`
152
+ - `src/data/`
153
+ - `src/locales/overrides*.json`
154
+ - `src/components/` (project overrides)
155
+
156
+ ---
157
+
158
+ ## 3. CLI Integration
159
+
160
+ The Clearstack CLI gains platform awareness. No new top-level commands —
161
+ platforms hook into the existing `init` and `update` flow.
162
+
163
+ ### `clearstack init`
164
+
165
+ Current behavior (unchanged):
166
+
167
+ 1. Scaffold Clearstack project structure
168
+ 2. Create `.configs/`, `docs/clearstack/`, `scripts/`, `src/`
169
+
170
+ New behavior when a platform is detected in `devDependencies`: 3. Read the platform's manifest from its `package.json` 4. Copy `templates/` → project root (respecting ownership rules) 5. Vendor `vendor/` → `src/vendor/<prefix>/` 6. Copy `docs/` → `docs/<prefix>/` 7. Copy `scripts/` → `scripts/` (skip existing) 8. Copy `api/` → `api/` (skip existing) 9. Generate import map entries for `#<prefix>/` 10. Create config file from template (if not exists)
171
+
172
+ ### `clearstack update`
173
+
174
+ Current behavior (unchanged):
175
+
176
+ 1. Sync `docs/clearstack/` (always overwrite)
177
+ 2. Sync `.configs/` (skip existing, `--force` to overwrite)
178
+
179
+ New behavior when a platform is detected: 3. Re-vendor `vendor/` → `src/vendor/<prefix>/` (always overwrite) 4. Sync `docs/<prefix>/` (always overwrite) 5. Skip everything else (config, templates, scripts, api)
180
+
181
+ ### Platform CLI Passthrough
182
+
183
+ A platform can also provide its own CLI commands via a `bin` entry. The
184
+ Clearstack CLI doesn't need to know about these — they're standard npm
185
+ bin scripts.
186
+
187
+ ```json
188
+ {
189
+ "bin": {
190
+ "staticart": "./bin/cli.js"
191
+ }
192
+ }
193
+ ```
194
+
195
+ The platform CLI can call Clearstack's API programmatically for shared
196
+ operations (vendoring, doc sync) rather than reimplementing them.
197
+
198
+ ---
199
+
200
+ ## 4. Vendor Sync
201
+
202
+ Platform vendor files are the equivalent of Clearstack's `src/vendor/hybrids/`
203
+ pattern — source files copied from `node_modules` into the project so the
204
+ browser can serve them directly.
205
+
206
+ ### How It Works
207
+
208
+ The platform's `vendor/` directory in the npm package contains the source
209
+ files that child projects consume. On `init` and `update`, these are copied
210
+ to `src/vendor/<prefix>/`.
211
+
212
+ ```
213
+ node_modules/@techninja/staticart/vendor/
214
+ ├── components/
215
+ │ ├── atoms/
216
+ │ ├── molecules/
217
+ │ └── organisms/
218
+ ├── store/
219
+ ├── utils/
220
+ └── styles/
221
+ ```
222
+
223
+ Becomes:
224
+
225
+ ```
226
+ src/vendor/staticart/
227
+ ├── components/
228
+ ├── store/
229
+ ├── utils/
230
+ └── styles/
231
+ ```
232
+
233
+ ### Vendor Directory Rules
234
+
235
+ - Always gitignored. Regenerated from the installed package version.
236
+ - Always overwritten on `update`. The platform owns these files.
237
+ - The child project never edits files in `src/vendor/<prefix>/`.
238
+ - To customize a vendored component, override it via the import map.
239
+
240
+ ### `postinstall` Hook
241
+
242
+ The platform should provide a setup script that runs on `npm install`,
243
+ similar to how Clearstack's `vendor-deps.js` works:
244
+
245
+ ```javascript
246
+ // In the platform's postinstall or the child project's setup.js
247
+ const PLATFORM_VENDOR = 'node_modules/@techninja/staticart/vendor';
248
+ const DEST = 'src/vendor/staticart';
249
+ cpSync(PLATFORM_VENDOR, DEST, { recursive: true });
250
+ ```
251
+
252
+ This ensures vendor files stay in sync with the installed package version
253
+ without requiring a manual `clearstack update`.
254
+
255
+ ---
256
+
257
+ ## 5. Override Layers
258
+
259
+ A stacked project has multiple override layers. Each layer can customize
260
+ the one below it without forking.
261
+
262
+ ### Component Overrides (Import Map)
263
+
264
+ The child project replaces a platform component by remapping its import:
265
+
266
+ ```html
267
+ <script type="importmap">
268
+ {
269
+ "imports": {
270
+ "#staticart/product-card": "/components/my-product-card.js",
271
+ "#staticart/": "/vendor/staticart/"
272
+ }
273
+ }
274
+ </script>
275
+ ```
276
+
277
+ The override component can:
278
+
279
+ - Replace the platform component entirely
280
+ - Import and wrap the original (decorator pattern)
281
+ - Import the original's helpers and compose differently
282
+
283
+ ### Style Overrides (CSS Custom Properties)
284
+
285
+ The platform defines all visual properties as CSS custom properties in its
286
+ vendored `tokens.css`. The child project overrides them in its own
287
+ `tokens.css`, which loads after the platform's:
288
+
289
+ ```css
290
+ /* src/styles/tokens.css — project overrides */
291
+ :root {
292
+ --color-primary: #8b4513; /* coffee brown, overrides platform blue */
293
+ }
294
+ ```
295
+
296
+ ### Config Overrides (Config File)
297
+
298
+ The platform reads its config file at runtime. The child project owns
299
+ this file entirely. The platform provides defaults for any missing keys.
300
+
301
+ ### i18n Overrides (Locale Files)
302
+
303
+ Four-layer cascade, split by responsibility:
304
+
305
+ | Layer | Owner | File | Purpose |
306
+ | ------------------- | -------- | ------------------------------- | ------------------------ |
307
+ | 1. Defaults | Platform | Hardcoded in `i18n.js` | English UI chrome |
308
+ | 2. Locale | Platform | `locales/<lang>.json` | Translated UI chrome |
309
+ | 3. Overrides | Project | `locales/overrides.json` | English project terms |
310
+ | 4. Locale overrides | Project | `locales/overrides.<lang>.json` | Translated project terms |
311
+
312
+ The platform translates its own UI strings (buttons, labels, status text).
313
+ The project translates its own domain terms (category names, variant labels,
314
+ product descriptions). Neither layer needs to know about the other's keys.
315
+
316
+ ---
317
+
318
+ ## 6. Spec Check Composition
319
+
320
+ Clearstack's spec checks must work across the stack. The platform can
321
+ declare additional check requirements that compose with Clearstack's base
322
+ checks.
323
+
324
+ ### What Composes Automatically
325
+
326
+ These Clearstack checks apply to all code regardless of layer:
327
+
328
+ - Line count limits (≤150 lines)
329
+ - Import map alias enforcement (no `../`)
330
+ - Prettier formatting
331
+ - ESLint rules
332
+ - Stylelint rules
333
+ - Security audit
334
+
335
+ ### What the Platform Can Add
336
+
337
+ The platform manifest can declare additional `jsconfig.json` paths for
338
+ type checking:
339
+
340
+ ```json
341
+ {
342
+ "clearstack": {
343
+ "platform": {
344
+ "tscDomains": ["api/jsconfig.json"]
345
+ }
346
+ }
347
+ }
348
+ ```
349
+
350
+ Clearstack's type checker auto-discovers `jsconfig.json` files, so this
351
+ usually works without explicit declaration. The manifest field exists for
352
+ edge cases where the platform needs type checking in non-standard locations.
353
+
354
+ ### Vendor Files Are Excluded
355
+
356
+ Spec checks skip `src/vendor/` by default (already in `SPEC_IGNORE_DIRS`).
357
+ Platform vendor files are the platform's responsibility to keep compliant —
358
+ the child project doesn't lint them.
359
+
360
+ ---
361
+
362
+ ## 7. Multi-Platform Stacking
363
+
364
+ A project can consume multiple platforms. Each gets its own prefix,
365
+ vendor directory, and config file.
366
+
367
+ ```html
368
+ <script type="importmap">
369
+ {
370
+ "imports": {
371
+ "#staticart/": "/vendor/staticart/",
372
+ "#blogengine/": "/vendor/blogengine/",
373
+ "#store/": "/store/"
374
+ }
375
+ }
376
+ </script>
377
+ ```
378
+
379
+ ### Conflict Resolution
380
+
381
+ - Import map prefixes must be unique. Two platforms cannot use the same prefix.
382
+ - Config files must have different names.
383
+ - CSS custom properties use platform-prefixed names to avoid collisions:
384
+ `--staticart-color-primary`, `--blogengine-color-primary`.
385
+ - Shared dependencies (e.g. both platforms need hybrids) are vendored once
386
+ at the Clearstack level (`src/vendor/hybrids/`), not per-platform.
387
+
388
+ ### Practical Limits
389
+
390
+ Multi-platform stacking is supported but not the common case. Most projects
391
+ use one platform. The architecture supports multiple platforms to avoid
392
+ painting into a corner, not because it's a recommended pattern.
393
+
394
+ ---
395
+
396
+ ## 8. Build Pipeline Integration
397
+
398
+ Platforms often provide build scripts (e.g. StatiCart's `build-products.js`).
399
+ These integrate with the child project's build pipeline.
400
+
401
+ ### Script Ownership
402
+
403
+ | Script | Copied on | Updated on | Owner |
404
+ | --------------------------- | ------------ | ---------- | --------------------------------- |
405
+ | `scripts/setup.js` | init | never | Project (may customize) |
406
+ | `scripts/vendor-deps.js` | init | never | Project (adds platform vendor) |
407
+ | `scripts/build-products.js` | init | never | Project (may customize) |
408
+ | Platform-internal scripts | never copied | n/a | Platform (runs from node_modules) |
409
+
410
+ The child project's `setup.js` (run on `postinstall`) should vendor both
411
+ Clearstack dependencies and platform files:
412
+
413
+ ```javascript
414
+ // scripts/setup.js
415
+ await import('./vendor-deps.js'); // hybrids → src/vendor/hybrids/
416
+ await import('./vendor-platform.js'); // staticart → src/vendor/staticart/
417
+ await import('./build-icons.js'); // lucide → icons.json
418
+ ```
419
+
420
+ ### CI/CD
421
+
422
+ The platform's GitHub Actions workflow template is copied on `init`. The
423
+ child project owns it and can customize. The platform documents what
424
+ environment variables and secrets are required.
425
+
426
+ ---
427
+
428
+ ## 9. Version Compatibility
429
+
430
+ ### Clearstack ↔ Platform
431
+
432
+ The platform declares its required Clearstack version as a `peerDependency`
433
+ (e.g. `"@techninja/clearstack": ">=0.3.17"`).
434
+
435
+ ### Platform ↔ Project
436
+
437
+ The child project pins the platform version in `dependencies` or
438
+ `devDependencies`. `npm update` + `clearstack update` brings in new
439
+ vendor files. The project's config, overrides, and custom components
440
+ are untouched.
441
+
442
+ ### Breaking Changes
443
+
444
+ When a platform makes a breaking change (new required config key, renamed
445
+ component, changed store model shape):
446
+
447
+ 1. Platform bumps major version
448
+ 2. Platform's `CHANGELOG.md` documents migration steps
449
+ 3. `clearstack update` warns if the installed platform version has a
450
+ major bump since last update
451
+ 4. `--force` is required to proceed with major version updates
452
+
453
+ ---
454
+
455
+ ## 10. Example
456
+
457
+ See `docs/app-spec/` for project-specific platform examples (e.g.
458
+ StatiCart package structure, child project layout, i18n layer mapping).
459
+
460
+ ---
461
+
462
+ ## Summary
463
+
464
+ | Concept | Mechanism |
465
+ | -------------------- | ------------------------------------------------------------------------------------------ |
466
+ | Platform declaration | `clearstack.platform` in `package.json` |
467
+ | Component override | Import map specificity (`#prefix/component` before `#prefix/`) |
468
+ | Style override | CSS custom properties in project `tokens.css` |
469
+ | Config override | Project-owned config file, platform provides defaults |
470
+ | i18n override | 4-layer cascade: platform defaults → locale → project overrides → project locale overrides |
471
+ | Vendor sync | `postinstall` + `clearstack update` copies platform source to `src/vendor/<prefix>/` |
472
+ | Spec checks | Clearstack checks compose automatically; vendor files excluded |
473
+ | Version management | semver + `peerDependencies` + `clearstack update` warnings |
474
+
475
+ ### Design Principles
476
+
477
+ 1. **Convention over configuration.** The import map prefix, vendor directory,
478
+ and override cascade all follow predictable patterns.
479
+ 2. **Ownership is explicit.** Every file belongs to exactly one layer.
480
+ No ambiguity about who can edit what.
481
+ 3. **Update without clobbering.** `clearstack update` never destroys
482
+ project customizations. Vendor files are regenerable. Config is sacred.
483
+ 4. **Override without forking.** Import maps, CSS custom properties, and
484
+ i18n cascades let projects customize without modifying platform source.
485
+ 5. **Spec checks compose.** A stacked project passes the same checks as
486
+ a vanilla Clearstack project. No special configuration needed.