jails-js 6.9.8 → 6.9.9
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/ai/anti-patterns.md +102 -0
- package/ai/api.md +388 -0
- package/ai/architecture.md +79 -0
- package/ai/best-practices.md +183 -0
- package/ai/concepts.md +228 -0
- package/ai/directives.md +556 -0
- package/ai/examples.md +199 -0
- package/ai/faq.md +70 -0
- package/ai/glossary.md +28 -0
- package/ai/json/api.json +190 -0
- package/ai/json/directives.json +107 -0
- package/ai/json/patterns.json +69 -0
- package/ai/json/recipes.json +96 -0
- package/ai/llms.txt +96 -0
- package/ai/overview.md +45 -0
- package/ai/patterns.md +182 -0
- package/ai/recipes.md +393 -0
- package/package.json +24 -2
- package/readme.md +22 -9
- package/tsconfig.json +0 -1
- package/types.d.ts +3 -3
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"name": "Counter",
|
|
4
|
+
"description": "Minimal local state update with delegated events.",
|
|
5
|
+
"examples": [
|
|
6
|
+
"<app-counter><button data-subtract>-</button><span html-inner=\"counter\">0</span><button data-add>+</button></app-counter>",
|
|
7
|
+
"state.set(s => { s.counter += 1 })"
|
|
8
|
+
],
|
|
9
|
+
"rules": ["Define counter in model.", "Use delegated click handlers."],
|
|
10
|
+
"relatedConcepts": ["state.set", "html-inner", "on"],
|
|
11
|
+
"antiPatterns": ["Using DOM text as source of truth."],
|
|
12
|
+
"performanceNotes": "One root listener per event pattern; stable scalar update."
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"name": "Loading and Error States",
|
|
16
|
+
"description": "Fetch data in controller and render loading/error/data branches.",
|
|
17
|
+
"examples": ["<p html-if=\"loading\">Loading</p>", "<p html-if=\"error\">{{ error.message }}</p>", "<li html-for=\"user in users\">{{ user.name }}</li>"],
|
|
18
|
+
"rules": ["Fetch in main/controller, not template.", "Keep loading, error, and data explicit in model."],
|
|
19
|
+
"relatedConcepts": ["html-if", "html-for", "dependencies"],
|
|
20
|
+
"antiPatterns": ["Side effects in directives."],
|
|
21
|
+
"performanceNotes": "Loop renders only when data branch is active if guarded by html-if."
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"name": "Modal",
|
|
25
|
+
"description": "Toggle dialog branch with boolean state.",
|
|
26
|
+
"examples": ["<section html-if=\"open\" role=\"dialog\" aria-modal=\"true\">...</section>"],
|
|
27
|
+
"rules": ["Use state for open/closed.", "Use state.set().then() for focus after opening."],
|
|
28
|
+
"relatedConcepts": ["html-if", "state.set"],
|
|
29
|
+
"antiPatterns": ["Expecting internal input values to persist after html-if removal."],
|
|
30
|
+
"performanceNotes": "Creates/removes dialog DOM; use CSS hiding for large persistent dialogs."
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"name": "Tabs",
|
|
34
|
+
"description": "Use one activeTab state value for buttons and panels.",
|
|
35
|
+
"examples": ["<button data-tab=\"profile\" html-class=\"activeTab === 'profile' ? 'active' : ''\">Profile</button>"],
|
|
36
|
+
"rules": ["Use delegateTarget.dataset.tab.", "Keep active state single-source."],
|
|
37
|
+
"relatedConcepts": ["html-class", "html-if", "delegateTarget"],
|
|
38
|
+
"antiPatterns": ["Reading event.target.dataset from nested elements."],
|
|
39
|
+
"performanceNotes": "Small panels can be conditionally created; large panels may need preservation strategy."
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"name": "Accordion",
|
|
43
|
+
"description": "Use html-for plus openIndex state.",
|
|
44
|
+
"examples": ["<article html-for=\"item in items\"><button data-toggle html-data-index=\"$index\">{{ item.title }}</button><div html-if=\"openIndex === $index\">{{ item.body }}</div></article>"],
|
|
45
|
+
"rules": ["Convert dataset indexes to numbers.", "Avoid heavy content in every repeated branch."],
|
|
46
|
+
"relatedConcepts": ["html-for", "$index", "html-if"],
|
|
47
|
+
"antiPatterns": ["Nested expensive widgets in every item without lazy/static boundary."],
|
|
48
|
+
"performanceNotes": "Render cost grows with number of items and content branch size."
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"name": "Fetch API With Service Dependency",
|
|
52
|
+
"description": "Inject HTTP service and update state from async controller logic.",
|
|
53
|
+
"examples": ["register('app-posts', appPosts, { http })", "const posts = await http.get('/posts')"],
|
|
54
|
+
"rules": ["Inject app-specific HTTP clients.", "Reflect async state through model keys."],
|
|
55
|
+
"relatedConcepts": ["dependencies", "main", "state.set"],
|
|
56
|
+
"antiPatterns": ["Hard-coded services in generic components."],
|
|
57
|
+
"performanceNotes": "Keeps unused service code out of reusable modules."
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"name": "Optimistic Updates",
|
|
61
|
+
"description": "Update state before API confirmation and rollback on failure.",
|
|
62
|
+
"examples": ["const previous = state.get().todos", "await state.set({ todos })"],
|
|
63
|
+
"rules": ["Snapshot previous state.", "Rollback in catch."],
|
|
64
|
+
"relatedConcepts": ["state.get", "state.set"],
|
|
65
|
+
"antiPatterns": ["Mutating previous snapshot in place."],
|
|
66
|
+
"performanceNotes": "Immediate UI feedback; clone collections to avoid reference bugs."
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"name": "Debounced Search",
|
|
70
|
+
"description": "Debounce input events and keep input browser-owned with html-static.",
|
|
71
|
+
"examples": ["<input type=\"search\" html-static>", "timer = setTimeout(() => run(query), 250)"],
|
|
72
|
+
"rules": ["Clear previous timer.", "Use html-static if state does not need every typed character."],
|
|
73
|
+
"relatedConcepts": ["html-static", "on", "state.set"],
|
|
74
|
+
"antiPatterns": ["Network request and render on every keystroke."],
|
|
75
|
+
"performanceNotes": "Reduces request frequency and render churn."
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"name": "Intersection Observer Lazy Rendering",
|
|
79
|
+
"description": "Set visible state when component enters viewport and cleanup observer on unmount.",
|
|
80
|
+
"examples": ["<section html-if=\"visible\">Expensive content</section>", "unmount(() => observer.disconnect())"],
|
|
81
|
+
"rules": ["Disconnect observer after use.", "Cleanup in unmount."],
|
|
82
|
+
"relatedConcepts": ["html-if", "unmount"],
|
|
83
|
+
"antiPatterns": ["Observers retaining detached elements."],
|
|
84
|
+
"performanceNotes": "Defers creating expensive DOM until needed."
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"name": "Virtual List Boundary",
|
|
88
|
+
"description": "Let a virtual list library own a static subtree while Jails owns surrounding state.",
|
|
89
|
+
"examples": ["<div class=\"virtual-list\" html-static></div>"],
|
|
90
|
+
"rules": ["Do not put html-for inside external virtualized root.", "Keep surrounding counters in Jails state."],
|
|
91
|
+
"relatedConcepts": ["html-static", "DOM ownership"],
|
|
92
|
+
"antiPatterns": ["Two renderers owning the same list DOM."],
|
|
93
|
+
"performanceNotes": "Avoids huge Jails loop renders and protects virtualizer DOM."
|
|
94
|
+
}
|
|
95
|
+
]
|
|
96
|
+
|
package/ai/llms.txt
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# Jails
|
|
2
|
+
|
|
3
|
+
## Philosophy
|
|
4
|
+
|
|
5
|
+
Jails enhances existing HTML custom elements. It is not a full-page SPA runtime by default. Use SSR/SSG/CMS/static HTML for first paint, then mount small interactive islands. HTML declares rendering; JS controllers declare behavior.
|
|
6
|
+
|
|
7
|
+
## Rendering Model
|
|
8
|
+
|
|
9
|
+
Call `register(name, module, dependencies?)`, then `start(target?)`. `start()` scans `target || document.body` for registered custom elements and bootstraps one component instance per element. State comes from required `model` plus optional `html-model`. Optional `view(state)` returns render-only scope. Optional `template({ elm, children })` returns embedded HTML. Directives and `{{ }}` evaluate JavaScript expressions. `state.set()` updates state, triggers DOM updates, and returns a Promise resolved after update.
|
|
10
|
+
|
|
11
|
+
## Core Rules
|
|
12
|
+
|
|
13
|
+
- Component boundary is the custom element.
|
|
14
|
+
- State is component-local by default.
|
|
15
|
+
- Parent updates can pass props to children unless child calls `state.protected([...])`.
|
|
16
|
+
- Use `main()` for mount-time setup.
|
|
17
|
+
- Use delegated `on()` events; read `event.delegateTarget`.
|
|
18
|
+
- Use `unmount()` for cleanup.
|
|
19
|
+
- Use `emit()` for bubbling DOM events; use `publish/subscribe` for cross-tree global messages.
|
|
20
|
+
- Use `html-static` when DOM is owned by browser/external libraries.
|
|
21
|
+
|
|
22
|
+
## Directives
|
|
23
|
+
|
|
24
|
+
- `html-if="expr"`: conditionally creates/removes node. Falsy branch absent from DOM.
|
|
25
|
+
- `html-for="item in list"`: repeats element for arrays/objects. Provides `$index` and `$key`. Keyed diffing not documented; assume order-based.
|
|
26
|
+
- `html-inner="expr"`: writes expression into element, replacing fallback content after mount.
|
|
27
|
+
- `html-model="{ ... }"`: initial state from markup; overrides model props at bootstrap.
|
|
28
|
+
- `html-*="expr"`: binds real attribute after stripping `html-`; boolean attrs are present truthy, removed falsy.
|
|
29
|
+
- `html-static`: skips virtual DOM/diff updates for node and children.
|
|
30
|
+
- `{{ expr }}`: text interpolation; delimiters configurable with `templateConfig({ tags: [...] })`.
|
|
31
|
+
- `<template>`: native inert content; use to hide unresolved dynamic fragments before data exists.
|
|
32
|
+
|
|
33
|
+
## Common Patterns
|
|
34
|
+
|
|
35
|
+
- Progressive island: static HTML + component behavior.
|
|
36
|
+
- Page-scoped state: page component owns data/loading/error.
|
|
37
|
+
- Services via dependencies: `register('x', module, { http, validations })`.
|
|
38
|
+
- Delegated events: `on('click', '[data-add]', add)`.
|
|
39
|
+
- Static integration: third-party widget root has `html-static`.
|
|
40
|
+
- Fetch flow: `main(load)`, set loading/data/error through state.
|
|
41
|
+
|
|
42
|
+
## Best Practices
|
|
43
|
+
|
|
44
|
+
- Keep directive expressions cheap and deterministic.
|
|
45
|
+
- Derive render-only values in `view`, not persistent state.
|
|
46
|
+
- Define all local state keys in `model`.
|
|
47
|
+
- Use `state.set().then()` for focus/measure/animation after render.
|
|
48
|
+
- Prefer DOM events over global pub/sub when components share a DOM path.
|
|
49
|
+
- Keep templates flat; avoid deeply nested `html-for`.
|
|
50
|
+
- Use `html-inner` fallback or `<template>` to avoid marker flash.
|
|
51
|
+
- Cleanup subscriptions/observers/widgets in `unmount()`.
|
|
52
|
+
|
|
53
|
+
## Anti-patterns
|
|
54
|
+
|
|
55
|
+
- Side effects in directives/mustache/view.
|
|
56
|
+
- Mutating Jails-managed DOM with external libraries without `html-static`.
|
|
57
|
+
- One event listener per repeated item.
|
|
58
|
+
- Using `event.target` instead of `delegateTarget`.
|
|
59
|
+
- Treating `html-model` as reactive after mount.
|
|
60
|
+
- Large serialized payloads in HTML attributes.
|
|
61
|
+
- Global pub/sub for local child-parent messages.
|
|
62
|
+
- Assuming keyed loop reconciliation without runtime proof.
|
|
63
|
+
|
|
64
|
+
## Canonical Architecture
|
|
65
|
+
|
|
66
|
+
`main.ts` registers components and starts Jails. Components export default controller and required `model`; optional `view` and `template`. Controllers coordinate events, services, and state. HTML contains custom element islands and directives. Services live outside components or are injected as dependencies.
|
|
67
|
+
|
|
68
|
+
## Minimal Examples
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
import { register, start } from 'jails-js'
|
|
72
|
+
import * as counter from './counter'
|
|
73
|
+
|
|
74
|
+
register('app-counter', counter)
|
|
75
|
+
start()
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
```html
|
|
79
|
+
<app-counter>
|
|
80
|
+
<button data-subtract>-</button>
|
|
81
|
+
<span html-inner="counter">0</span>
|
|
82
|
+
<button data-add>+</button>
|
|
83
|
+
</app-counter>
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
export default function appCounter({ main, on, state }) {
|
|
88
|
+
main(() => {
|
|
89
|
+
on('click', '[data-add]', () => state.set(s => { s.counter += 1 }))
|
|
90
|
+
on('click', '[data-subtract]', () => state.set(s => { s.counter -= 1 }))
|
|
91
|
+
})
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export const model = { counter: 0 }
|
|
95
|
+
```
|
|
96
|
+
|
package/ai/overview.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Jails Overview
|
|
2
|
+
|
|
3
|
+
Jails is a client-side JavaScript framework for attaching behavior to existing HTML custom elements. A Jails component is an ES module registered against a custom element name. When `start()` runs, Jails scans a target DOM tree, finds registered component elements, creates one component instance per element, initializes local state from the component `model` and optional `html-model`, evaluates template directives, and binds component-scoped helpers.
|
|
4
|
+
|
|
5
|
+
Jails is optimized for progressively enhanced HTML. The default assumption is that the server, static generator, CMS, or application template engine owns first paint. Jails then enhances specific islands of interactivity instead of owning the whole document as a single application tree.
|
|
6
|
+
|
|
7
|
+
## Philosophy
|
|
8
|
+
|
|
9
|
+
- HTML is the primary data source for UI structure.
|
|
10
|
+
- JavaScript modules provide behavior, state transitions, event handling, and optional embedded templates.
|
|
11
|
+
- Components are custom elements enhanced by Jails, not necessarily shadow-DOM web components.
|
|
12
|
+
- Rendering is directive-driven: the DOM declares which state values affect which nodes or attributes.
|
|
13
|
+
- Event handling is delegated from the component root, so child DOM can be replaced without rebinding every child listener.
|
|
14
|
+
- Static regions can be excluded from diffing with `html-static` when the browser or a third-party library must own that subtree.
|
|
15
|
+
|
|
16
|
+
## Rendering Philosophy
|
|
17
|
+
|
|
18
|
+
Jails updates existing DOM through directives and mustache expressions. The template system evaluates expressions against the component view scope. State changes through `state.set()` schedule DOM updates and return a Promise that resolves after the update cycle.
|
|
19
|
+
|
|
20
|
+
The rendering boundary is the component element. Parent state updates can flow into child component props unless a child protects specific properties with `state.protected()`. `html-static` creates a sub-boundary inside a component: Jails does not diff that node or its children after it is marked static.
|
|
21
|
+
|
|
22
|
+
## Performance Philosophy
|
|
23
|
+
|
|
24
|
+
- Render only islands that need client-side behavior.
|
|
25
|
+
- Keep first paint server-rendered or static when possible.
|
|
26
|
+
- Use event delegation to reduce listener count.
|
|
27
|
+
- Use `html-static` to avoid diffing immutable or externally mutated regions.
|
|
28
|
+
- Keep directive expressions cheap because they execute during template updates.
|
|
29
|
+
- Avoid deeply nested reactive loops because `html-for` creates repeated DOM work.
|
|
30
|
+
|
|
31
|
+
## Core Primitives
|
|
32
|
+
|
|
33
|
+
- `register(name, module, dependencies?)`: associate a custom element name with a component module.
|
|
34
|
+
- `start(target?)`: scan and bootstrap registered elements in `target` or `document.body`.
|
|
35
|
+
- Component controller: default export function receiving helpers.
|
|
36
|
+
- `model`: required exported object or function that initializes local state.
|
|
37
|
+
- `view`: optional exported function that derives render-only values from state.
|
|
38
|
+
- `template`: optional exported function that returns embedded HTML.
|
|
39
|
+
- Directives: `html-if`, `html-for`, `html-inner`, `html-model`, `html-*`, `html-static`, and native `<template>`.
|
|
40
|
+
- Helpers: `main`, `elm`, `dataset`, `query`, `dependencies`, `innerHTML`, `state`, `on`, `off`, `emit`, `trigger`, `publish`, `subscribe`, `effect`, `unmount`.
|
|
41
|
+
|
|
42
|
+
## Mental Model
|
|
43
|
+
|
|
44
|
+
Treat each Jails component as a DOM island with local state and delegated events. The HTML declares render dependencies; the controller declares lifecycle and behavior. State changes drive directive updates. DOM nodes inside `html-static` are owned by the browser or external libraries. Parent-child communication uses prop updates, DOM events, or global pub/sub depending on distance and direction.
|
|
45
|
+
|
package/ai/patterns.md
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# Architectural Patterns
|
|
2
|
+
|
|
3
|
+
## Progressive Island Component
|
|
4
|
+
|
|
5
|
+
Intent: make server-rendered HTML interactive without turning the page into a single JavaScript-owned application.
|
|
6
|
+
|
|
7
|
+
Structure:
|
|
8
|
+
|
|
9
|
+
```html
|
|
10
|
+
<hello-world>
|
|
11
|
+
<h1>Hello World!</h1>
|
|
12
|
+
<button class="add">+</button>
|
|
13
|
+
<span html-inner="counter">0</span>
|
|
14
|
+
</hello-world>
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
register('hello-world', helloWorld)
|
|
19
|
+
start()
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Responsibilities: HTML owns first paint; controller owns behavior; state owns values that directives render.
|
|
23
|
+
|
|
24
|
+
Why it exists: Jails targets SSR/SSG/CMS pages where most content is static and only selected areas need interactivity.
|
|
25
|
+
|
|
26
|
+
Performance implications: less JavaScript and fewer hydration boundaries than full-page SPA rendering.
|
|
27
|
+
|
|
28
|
+
Scalability implications: teams can add isolated components without centralizing the whole page in one app shell.
|
|
29
|
+
|
|
30
|
+
## Page-Scoped Store
|
|
31
|
+
|
|
32
|
+
Intent: keep mutable UI state local to the component or page island that renders it.
|
|
33
|
+
|
|
34
|
+
Structure:
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
export const model = {
|
|
38
|
+
items: [],
|
|
39
|
+
loading: false,
|
|
40
|
+
error: null
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Responsibilities: the page component fetches data, owns loading/error states, and passes relevant props to child components through parent updates.
|
|
45
|
+
|
|
46
|
+
Why it exists: Jails docs emphasize local component state and parent-to-child prop updates, not a global store.
|
|
47
|
+
|
|
48
|
+
Performance implications: local updates constrain render work to the component boundary.
|
|
49
|
+
|
|
50
|
+
Scalability implications: state ownership remains visible. Cross-tree communication uses pub/sub only when DOM hierarchy cannot carry the message.
|
|
51
|
+
|
|
52
|
+
## Services Through Dependencies
|
|
53
|
+
|
|
54
|
+
Intent: make generic components reusable across applications with app-specific services injected at registration time.
|
|
55
|
+
|
|
56
|
+
Structure:
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
register('form-validation', formValidation, { validations, masks })
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Responsibilities: shared service modules perform validation, HTTP, masks, or business logic; component coordinates UI events and state.
|
|
63
|
+
|
|
64
|
+
Why it exists: prevents generic components from bundling every application-specific rule.
|
|
65
|
+
|
|
66
|
+
Performance implications: avoids importing unused validation/business code into generic libraries.
|
|
67
|
+
|
|
68
|
+
Scalability implications: supports library distribution and app-specific behavior without forking components.
|
|
69
|
+
|
|
70
|
+
## Delegated Event Handling
|
|
71
|
+
|
|
72
|
+
Intent: bind a small number of listeners at the component root.
|
|
73
|
+
|
|
74
|
+
Structure:
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
main(() => {
|
|
78
|
+
on('click', '[data-remove]', remove)
|
|
79
|
+
})
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Responsibilities: use stable selectors, read `event.delegateTarget`, and keep handlers independent of child node identity.
|
|
83
|
+
|
|
84
|
+
Why it exists: child nodes may be replaced by directive updates. Delegation keeps handlers active.
|
|
85
|
+
|
|
86
|
+
Performance implications: avoids one listener per repeated item and reduces rebinding after render.
|
|
87
|
+
|
|
88
|
+
Scalability implications: list and conditional rendering remain predictable.
|
|
89
|
+
|
|
90
|
+
## Template Composition
|
|
91
|
+
|
|
92
|
+
Intent: package fully featured widgets while still allowing consumer content.
|
|
93
|
+
|
|
94
|
+
Structure:
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
export const template = ({ children }) => html`
|
|
98
|
+
<section>
|
|
99
|
+
${children}
|
|
100
|
+
<p>Counter: {{ counter }}</p>
|
|
101
|
+
</section>
|
|
102
|
+
`
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Responsibilities: templates define embedded markup; controllers define behavior; `children` preserves caller-provided content.
|
|
106
|
+
|
|
107
|
+
Why it exists: Jails supports both logic-only components and self-contained web components.
|
|
108
|
+
|
|
109
|
+
Performance implications: embedded templates add client-side HTML creation, so prefer server-rendered HTML for generic page content.
|
|
110
|
+
|
|
111
|
+
Scalability implications: useful for widgets such as chat, consent bars, counters, and mini-apps.
|
|
112
|
+
|
|
113
|
+
## Static Third-Party Integration
|
|
114
|
+
|
|
115
|
+
Intent: let external libraries mutate DOM without Jails overwriting their changes.
|
|
116
|
+
|
|
117
|
+
Structure:
|
|
118
|
+
|
|
119
|
+
```html
|
|
120
|
+
<div class="swiper" html-static>...</div>
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Responsibilities: external library owns the static subtree; Jails owns surrounding state and events.
|
|
124
|
+
|
|
125
|
+
Why it exists: DOM-diffing renderers conflict with libraries that imperatively mutate DOM.
|
|
126
|
+
|
|
127
|
+
Performance implications: Jails skips diff work for that subtree.
|
|
128
|
+
|
|
129
|
+
Scalability implications: each integration has an explicit ownership boundary.
|
|
130
|
+
|
|
131
|
+
## Fetch Flow
|
|
132
|
+
|
|
133
|
+
Intent: perform asynchronous I/O in controller logic and reflect status through state.
|
|
134
|
+
|
|
135
|
+
Structure:
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
export default function appList({ main, state }) {
|
|
139
|
+
main(() => {
|
|
140
|
+
load()
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
const load = () => {
|
|
144
|
+
state.set({ loading: true, error: null })
|
|
145
|
+
fetch('/api/items')
|
|
146
|
+
.then(r => r.json())
|
|
147
|
+
.then(items => state.set({ items, loading: false }))
|
|
148
|
+
.catch(error => state.set({ error, loading: false }))
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Responsibilities: services/fetch handle I/O; state holds loading/error/data; template renders states with directives.
|
|
154
|
+
|
|
155
|
+
Why it exists: keeps templates deterministic and side-effect-free.
|
|
156
|
+
|
|
157
|
+
Performance implications: avoid re-fetching in render paths.
|
|
158
|
+
|
|
159
|
+
Scalability implications: cleanly separates data acquisition from DOM projection.
|
|
160
|
+
|
|
161
|
+
## Parent-Child Communication
|
|
162
|
+
|
|
163
|
+
Intent: communicate down through props/state updates and up through DOM events.
|
|
164
|
+
|
|
165
|
+
Structure:
|
|
166
|
+
|
|
167
|
+
```ts
|
|
168
|
+
// child
|
|
169
|
+
emit('time-elapsed', '10 seconds elapsed')
|
|
170
|
+
|
|
171
|
+
// parent
|
|
172
|
+
on('time-elapsed', 'child-component', handleElapsed)
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Responsibilities: parent owns data it passes down; child emits user/domain events upward; siblings use pub/sub when no shared ancestor is practical.
|
|
176
|
+
|
|
177
|
+
Why it exists: follows DOM event semantics and keeps component coupling low.
|
|
178
|
+
|
|
179
|
+
Performance implications: bubbling events avoid global subscriptions for local communication.
|
|
180
|
+
|
|
181
|
+
Scalability implications: choose the narrowest communication channel that reaches the recipient.
|
|
182
|
+
|