jails-js 6.9.7 → 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.
@@ -0,0 +1,183 @@
1
+ # Best Practices
2
+
3
+ ## Prefer server-rendered HTML for first paint
4
+
5
+ Rationale: Jails components enhance existing HTML. Users should see useful content before JavaScript starts.
6
+
7
+ Example:
8
+
9
+ ```html
10
+ <hello-world>
11
+ <h1>Hello World!</h1>
12
+ <span html-inner="counter">0</span>
13
+ </hello-world>
14
+ ```
15
+
16
+ Counter-example: rendering all initial content only from a client-side `template` when static HTML would work.
17
+
18
+ Performance implications: less client-side rendering work and faster perceived load.
19
+
20
+ ## Use `main()` as the readable lifecycle entry point
21
+
22
+ Rationale: docs position `main()` as the place to list mount-time behavior before implementation details.
23
+
24
+ Example:
25
+
26
+ ```ts
27
+ main(() => {
28
+ on('click', '[data-add]', add)
29
+ load()
30
+ })
31
+ ```
32
+
33
+ Counter-example: scattering event registration across unrelated helper functions.
34
+
35
+ Performance implications: no direct runtime gain, but clearer lifecycle reduces duplicate bindings.
36
+
37
+ ## Use delegated events with stable selectors
38
+
39
+ Rationale: child nodes can be replaced by rendering. Delegation keeps listeners attached to the component root.
40
+
41
+ Example:
42
+
43
+ ```ts
44
+ on('click', '[data-remove]', remove)
45
+ ```
46
+
47
+ Counter-example: querying every item in `html-for` and attaching one listener each.
48
+
49
+ Performance implications: fewer listeners and no rebinding after DOM updates.
50
+
51
+ ## Use `event.delegateTarget`
52
+
53
+ Rationale: `event.target` may be a nested element inside the matched selector.
54
+
55
+ Example:
56
+
57
+ ```ts
58
+ const id = e.delegateTarget.dataset.id
59
+ ```
60
+
61
+ Counter-example:
62
+
63
+ ```ts
64
+ const id = e.target.dataset.id
65
+ ```
66
+
67
+ Performance implications: avoids defensive DOM traversal and event bugs.
68
+
69
+ ## Keep templates declarative and expressions cheap
70
+
71
+ Rationale: directive expressions run during updates.
72
+
73
+ Example:
74
+
75
+ ```html
76
+ <div html-class="counterClass">{{ counter }}</div>
77
+ ```
78
+
79
+ ```ts
80
+ export const view = state => ({
81
+ ...state,
82
+ counterClass: state.counter > 10 ? 'bigger' : ''
83
+ })
84
+ ```
85
+
86
+ Counter-example:
87
+
88
+ ```html
89
+ <li html-for="item in items.filter(expensiveFilter).sort(expensiveSort)">
90
+ ```
91
+
92
+ Performance implications: avoids repeated expensive work on every render.
93
+
94
+ ## Use `html-static` for external DOM ownership
95
+
96
+ Rationale: third-party libraries like Swiper mutate DOM. Jails should skip that subtree.
97
+
98
+ Example:
99
+
100
+ ```html
101
+ <div class="swiper" html-static></div>
102
+ ```
103
+
104
+ Counter-example: initializing a DOM-mutating library inside a normal directive-managed subtree.
105
+
106
+ Performance implications: avoids diff work and prevents render conflicts.
107
+
108
+ ## Keep state ownership local and explicit
109
+
110
+ Rationale: Jails state is component-scoped. Local ownership makes updates predictable.
111
+
112
+ Example:
113
+
114
+ ```ts
115
+ export const model = {
116
+ loading: false,
117
+ items: []
118
+ }
119
+ ```
120
+
121
+ Counter-example: hidden global mutable objects read directly by many components.
122
+
123
+ Performance implications: smaller update boundaries.
124
+
125
+ ## Use dependencies for app-specific services
126
+
127
+ Rationale: generic components should not bundle every application-specific rule.
128
+
129
+ Example:
130
+
131
+ ```ts
132
+ register('form-validation', formValidation, { validations, masks })
133
+ ```
134
+
135
+ Counter-example: importing country-specific validations directly into a generic component package.
136
+
137
+ Performance implications: avoids unused code in reusable components.
138
+
139
+ ## Use `state.set().then()` for post-render work
140
+
141
+ Rationale: `state.set()` returns a Promise for next-tick behavior after DOM updates.
142
+
143
+ Example:
144
+
145
+ ```ts
146
+ state.set({ open: true }).then(() => {
147
+ elm.querySelector('[data-close]').focus()
148
+ })
149
+ ```
150
+
151
+ Counter-example: measuring or focusing DOM immediately after `state.set()` and assuming the DOM is already updated.
152
+
153
+ Performance implications: avoids layout reads against stale DOM.
154
+
155
+ ## Cleanup external resources in `unmount()`
156
+
157
+ Rationale: detached components should not retain subscriptions, timers, observers, or third-party instances.
158
+
159
+ Example:
160
+
161
+ ```ts
162
+ const unsubscribe = subscribe('event', handler)
163
+ unmount(() => unsubscribe())
164
+ ```
165
+
166
+ Counter-example: global subscriptions without cleanup.
167
+
168
+ Performance implications: prevents leaks and duplicate callbacks.
169
+
170
+ ## Prefer DOM events before global pub/sub
171
+
172
+ Rationale: bubbling DOM events express local parent-child communication with narrower scope.
173
+
174
+ Example:
175
+
176
+ ```ts
177
+ emit('item-selected', item)
178
+ ```
179
+
180
+ Counter-example: publishing global events for a child-to-parent message.
181
+
182
+ Performance implications: less global fan-out and lower coupling.
183
+
package/ai/concepts.md ADDED
@@ -0,0 +1,228 @@
1
+ # Jails Concepts
2
+
3
+ ## Component
4
+
5
+ Definition: a JavaScript module registered to a custom element tag.
6
+
7
+ Purpose: attach behavior, local state, lifecycle callbacks, event delegation, optional rendering transforms, and optional embedded HTML to each matching DOM element.
8
+
9
+ Lifecycle:
10
+
11
+ 1. `register()` stores the module under a custom element name.
12
+ 2. `start()` scans for matching elements.
13
+ 3. Jails creates one controller instance for each element.
14
+ 4. `model` initializes state. If `html-model` exists, it contributes initial state and may override model properties.
15
+ 5. Optional `template` may generate embedded HTML.
16
+ 6. `main()` callbacks run after mount.
17
+ 7. `state.set()` updates state and refreshes affected template output.
18
+ 8. `unmount()` callbacks run when the component element is detached.
19
+
20
+ Responsibilities: own behavior for its element, register component-scoped listeners, update local state, and keep side effects outside template expressions.
21
+
22
+ Limitations: component code should not assume it owns the whole page. Child components can receive parent props unless protected. The current repository documents behavior but does not include runtime source, so low-level diff details are inferred from docs.
23
+
24
+ Example:
25
+
26
+ ```ts
27
+ import type { Component, Model } from 'jails-js'
28
+
29
+ export default function appCounter({ main, on, state }: Component) {
30
+ main(() => {
31
+ on('click', '[data-add]', add)
32
+ })
33
+
34
+ const add = () => {
35
+ state.set(s => {
36
+ s.counter += 1
37
+ })
38
+ }
39
+ }
40
+
41
+ export const model: Model = {
42
+ counter: 0
43
+ }
44
+ ```
45
+
46
+ Anti-patterns: relying on global DOM selectors for component internals, mutating DOM that Jails also owns, embedding expensive computations in directives.
47
+
48
+ ## Model
49
+
50
+ Definition: required export that initializes component state.
51
+
52
+ Purpose: provide deterministic state shape and document local state ownership.
53
+
54
+ Lifecycle: read during component bootstrap before render updates. A function model receives `{ elm, initialState, dependencies }`.
55
+
56
+ Responsibilities: initialize state from defaults, `data-*` attributes, dependencies, or `html-model`.
57
+
58
+ Limitations: do not fetch remote data inside the model; use controller lifecycle for asynchronous work.
59
+
60
+ Example:
61
+
62
+ ```ts
63
+ export const model = ({ elm }) => ({
64
+ counter: Number(elm.dataset.counter || 0)
65
+ })
66
+ ```
67
+
68
+ Anti-patterns: using undeclared state keys later in the controller, hiding business side effects in model construction.
69
+
70
+ ## View
71
+
72
+ Definition: optional export that maps state to render scope.
73
+
74
+ Purpose: derive template-only values without storing them in component state.
75
+
76
+ Lifecycle: evaluated when state is rendered. The returned object is the scope used by directives and mustache expressions.
77
+
78
+ Responsibilities: format values, derive booleans/classes/labels, keep templates simple.
79
+
80
+ Limitations: should be pure. Do not mutate state or perform I/O.
81
+
82
+ Example:
83
+
84
+ ```ts
85
+ export const view = state => ({
86
+ ...state,
87
+ counterClass: state.counter > 10 ? 'bigger' : ''
88
+ })
89
+ ```
90
+
91
+ Anti-patterns: side effects in `view`, expensive sorting/filtering on every update without caching or precomputing in state transitions.
92
+
93
+ ## Template
94
+
95
+ Definition: optional export that returns an HTML string for the component.
96
+
97
+ Purpose: distribute fully featured web components with embedded markup, or share SSR template functions separately.
98
+
99
+ Lifecycle: runs during component setup before directive processing.
100
+
101
+ Responsibilities: return deterministic HTML, preserve `children` when component users provide slotted-like content, use `html` and `attributes` helpers for template strings.
102
+
103
+ Limitations: template output is string HTML. It should not perform runtime side effects.
104
+
105
+ Example:
106
+
107
+ ```ts
108
+ import { html, attributes } from 'jails-js/html'
109
+
110
+ export const template = ({ children }) => html`
111
+ <section ${attributes({ title: 'Counter' })}>
112
+ ${children}
113
+ <p>Counter: {{ counter }}</p>
114
+ <button data-add>+</button>
115
+ </section>
116
+ `
117
+ ```
118
+
119
+ Anti-patterns: giant embedded templates with mixed business logic, omitting `${children}` when the component API expects consumer content.
120
+
121
+ ## Directives
122
+
123
+ Definition: HTML attributes interpreted by Jails during rendering.
124
+
125
+ Purpose: declaratively bind state expressions to nodes, attributes, loops, conditions, and static boundaries.
126
+
127
+ Lifecycle: parsed from component HTML and evaluated during initial render and later state updates.
128
+
129
+ Responsibilities: keep render behavior visible in markup.
130
+
131
+ Limitations: directive values are JavaScript expressions. They should be deterministic and cheap.
132
+
133
+ Example:
134
+
135
+ ```html
136
+ <p html-if="isVisible">Visible</p>
137
+ <span html-inner="counter">0</span>
138
+ <img html-src="imageUrl" alt="">
139
+ ```
140
+
141
+ Anti-patterns: network calls in expressions, large inline computations, depending on browser-visible `html-*` attributes after Jails strips them.
142
+
143
+ ## Rendering
144
+
145
+ Definition: the process of evaluating the current view scope into DOM changes.
146
+
147
+ Purpose: update only the component DOM declared by directives and mustache expressions.
148
+
149
+ Lifecycle: initial render during mount, subsequent renders after `state.set()` or parent prop updates.
150
+
151
+ Responsibilities: preserve component boundaries, evaluate conditions/loops/attributes/text, skip static nodes.
152
+
153
+ Limitations: exact diff algorithm is not present in this repository. Docs explicitly state `innerHTML()` uses DOM diffing and `html-static` skips virtual DOM updates for the marked node and children.
154
+
155
+ Example:
156
+
157
+ ```ts
158
+ state.set({ isVisible: false }).then(state => {
159
+ // runs after DOM update
160
+ })
161
+ ```
162
+
163
+ Anti-patterns: mutating managed DOM between updates without `html-static`, assuming synchronous DOM updates immediately after `state.set()`.
164
+
165
+ ## Store / State
166
+
167
+ Definition: component-local state managed through the `state` helper.
168
+
169
+ Purpose: hold the mutable data that drives directive updates.
170
+
171
+ Lifecycle: initialized from `model`, updated through `state.set()`, read through `state.get()`, optionally protected from parent overwrites.
172
+
173
+ Responsibilities: own mutable UI state for one component instance.
174
+
175
+ Limitations: no repository evidence of a global store primitive. Use global pub/sub for cross-tree messages, not as a hidden shared state replacement.
176
+
177
+ Example:
178
+
179
+ ```ts
180
+ state.set(s => {
181
+ s.isVisible = !s.isVisible
182
+ })
183
+ ```
184
+
185
+ Anti-patterns: directly mutating `state.get()` without `state.set()`, using component state for data owned by a child form input that can be browser-owned with `html-static`.
186
+
187
+ ## Services
188
+
189
+ Definition: application-specific modules or injected dependencies used by components for external work.
190
+
191
+ Purpose: isolate API calls, validation rules, masks, and other reusable behavior outside UI controllers.
192
+
193
+ Lifecycle: imported directly or injected through `register(name, module, dependencies)`.
194
+
195
+ Responsibilities: perform I/O and business logic while controllers coordinate UI state.
196
+
197
+ Limitations: Jails documents dependency injection, not a formal service container.
198
+
199
+ Example:
200
+
201
+ ```ts
202
+ register('form-validation', formValidation, { validations, masks })
203
+ ```
204
+
205
+ Anti-patterns: hard-coding app-specific services inside generic library components.
206
+
207
+ ## Event Delegation
208
+
209
+ Definition: component-scoped event handling where listeners are attached to the component root and matched against child selectors.
210
+
211
+ Purpose: reduce event listener count and keep listeners working when child nodes are replaced.
212
+
213
+ Lifecycle: usually registered inside `main()`. `off()` can remove listeners.
214
+
215
+ Responsibilities: use stable selectors such as `[data-add]`, read the matched element from `e.delegateTarget`.
216
+
217
+ Limitations: events must bubble to be caught by delegated listeners. Non-bubbling events require care or direct DOM APIs if unsupported.
218
+
219
+ Example:
220
+
221
+ ```ts
222
+ on('click', '[data-add]', event => {
223
+ event.delegateTarget.disabled = true
224
+ })
225
+ ```
226
+
227
+ Anti-patterns: attaching listeners to every repeated item inside `html-for`, relying on `event.target` when a nested child may have received the original click.
228
+