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,102 @@
1
+ # Anti-patterns
2
+
3
+ ## Side effects in template expressions
4
+
5
+ Why harmful: directive and mustache expressions are render logic. Side effects can run repeatedly and unpredictably during updates.
6
+
7
+ Symptoms: duplicate requests, counters changing during render, inconsistent DOM.
8
+
9
+ Performance impact: unnecessary I/O or CPU work on every update.
10
+
11
+ Recommended alternative: move side effects into controller functions called from `main()` or event handlers.
12
+
13
+ ## Deeply nested `html-for`
14
+
15
+ Why harmful: repeated loops multiply DOM creation and update work.
16
+
17
+ Symptoms: slow state updates, input lag, large DOM trees.
18
+
19
+ Performance impact: O(n*m) or worse render work depending on nesting and directives.
20
+
21
+ Recommended alternative: flatten data, paginate, virtualize, or make heavy regions static/external.
22
+
23
+ ## Mutating Jails-managed DOM with third-party libraries
24
+
25
+ Why harmful: Jails may diff the same subtree and overwrite external mutations.
26
+
27
+ Symptoms: widgets reset after state updates, duplicated nodes, lost event state.
28
+
29
+ Performance impact: unnecessary reconciliation and library reinitialization.
30
+
31
+ Recommended alternative: put the external library subtree under `html-static`.
32
+
33
+ ## Assuming `state.set()` updates DOM synchronously
34
+
35
+ Why harmful: docs expose a Promise specifically for after-update work.
36
+
37
+ Symptoms: focus/measure code reads old nodes or missing new nodes.
38
+
39
+ Performance impact: repeated layout work or failed retries.
40
+
41
+ Recommended alternative: use `state.set(...).then(...)`.
42
+
43
+ ## Using `event.target` in delegated handlers
44
+
45
+ Why harmful: the real event target may be a child inside the selected element.
46
+
47
+ Symptoms: missing `dataset`, wrong element updates, brittle DOM traversal.
48
+
49
+ Performance impact: extra defensive queries.
50
+
51
+ Recommended alternative: use `event.delegateTarget`.
52
+
53
+ ## Global pub/sub for local parent-child communication
54
+
55
+ Why harmful: global channels hide ownership and make event flow harder to trace.
56
+
57
+ Symptoms: unrelated components reacting to events, forgotten unsubscriptions.
58
+
59
+ Performance impact: unnecessary global fan-out.
60
+
61
+ Recommended alternative: use `emit()` and delegated `on()` for DOM-reachable communication.
62
+
63
+ ## Storing browser-owned input values in state unnecessarily
64
+
65
+ Why harmful: every keystroke can trigger state updates and DOM reconciliation.
66
+
67
+ Symptoms: cursor jumps, overwritten user input, excessive renders.
68
+
69
+ Performance impact: high-frequency input renders.
70
+
71
+ Recommended alternative: use normal inputs or `html-static` and read values on submit/input handlers.
72
+
73
+ ## Giant embedded `template` functions
74
+
75
+ Why harmful: large string templates mix structure, rendering rules, and business assumptions.
76
+
77
+ Symptoms: difficult review, duplicated markup, hard-to-test components.
78
+
79
+ Performance impact: more client-side HTML generation where server-rendered HTML may be cheaper.
80
+
81
+ Recommended alternative: use logic-featured components with external HTML, or split template fragments.
82
+
83
+ ## Undeclared state shape
84
+
85
+ Why harmful: missing model keys make templates and controllers less inferable.
86
+
87
+ Symptoms: `undefined` render output, inconsistent initial branches.
88
+
89
+ Performance impact: extra defensive conditions and update churn.
90
+
91
+ Recommended alternative: define all local state keys in `model`.
92
+
93
+ ## Expensive derived values stored as persistent state
94
+
95
+ Why harmful: render-only values clutter state and can become stale.
96
+
97
+ Symptoms: duplicated fields such as `counter` and `counterLabel` falling out of sync.
98
+
99
+ Performance impact: extra state writes.
100
+
101
+ Recommended alternative: use pure `view(state)` for render-only derivations.
102
+
package/ai/api.md ADDED
@@ -0,0 +1,388 @@
1
+ # Public API
2
+
3
+ ## `register(name, module, dependencies?)`
4
+
5
+ Signature:
6
+
7
+ ```ts
8
+ register(name: string, module: Module, dependencies?: object): void
9
+ ```
10
+
11
+ Parameters:
12
+
13
+ - `name`: custom element tag name, for example `'app-counter'`.
14
+ - `module`: component module namespace, usually `import * as appCounter from './components/app-counter'`.
15
+ - `dependencies`: optional object injected into component helpers as `dependencies`.
16
+
17
+ Return value: not documented.
18
+
19
+ Side effects: stores the association between the custom element name and component module.
20
+
21
+ Lifecycle implications: must run before `start()` for components that should bootstrap during that scan.
22
+
23
+ Example:
24
+
25
+ ```ts
26
+ import { register, start } from 'jails-js'
27
+ import http from './shared/http'
28
+ import * as myComponent from './components/my-component'
29
+
30
+ register('my-component', myComponent, { http })
31
+ start()
32
+ ```
33
+
34
+ Related APIs: `start`, `dependencies`.
35
+
36
+ ## `start(target?)`
37
+
38
+ Signature:
39
+
40
+ ```ts
41
+ start(target?: HTMLElement): void
42
+ ```
43
+
44
+ Parameters:
45
+
46
+ - `target`: optional root element to scan. Defaults to `document.body`.
47
+
48
+ Return value: not documented.
49
+
50
+ Side effects: scans for registered custom elements and starts their bootstrap process.
51
+
52
+ Lifecycle implications: safe to call multiple times, but docs advise avoiding unnecessary calls.
53
+
54
+ Example:
55
+
56
+ ```ts
57
+ register('hello-world', helloWorld)
58
+ start()
59
+ ```
60
+
61
+ Related APIs: `register`.
62
+
63
+ ## `templateConfig(options)`
64
+
65
+ Signature:
66
+
67
+ ```ts
68
+ jails.templateConfig({ tags: [open: string, close: string] }): void
69
+ ```
70
+
71
+ Parameters:
72
+
73
+ - `tags`: two-string array for mustache delimiters.
74
+
75
+ Return value: not documented.
76
+
77
+ Side effects: changes global template delimiters from default `{{` and `}}`.
78
+
79
+ Lifecycle implications: call before templates are processed.
80
+
81
+ Example:
82
+
83
+ ```ts
84
+ jails.templateConfig({ tags: ['@{', '}'] })
85
+ ```
86
+
87
+ Related APIs: mustache interpolation.
88
+
89
+ ## `publish(name, data?)`
90
+
91
+ Signature:
92
+
93
+ ```ts
94
+ publish(name: string, data?: any): void
95
+ ```
96
+
97
+ Parameters:
98
+
99
+ - `name`: global event name.
100
+ - `data`: optional payload.
101
+
102
+ Side effects: notifies subscribers globally.
103
+
104
+ Lifecycle implications: useful for components in different DOM trees.
105
+
106
+ Example:
107
+
108
+ ```ts
109
+ publish('my-component:fetched', data)
110
+ ```
111
+
112
+ Related APIs: `subscribe`, component helper `publish`.
113
+
114
+ ## `subscribe(name, fn)`
115
+
116
+ Signature:
117
+
118
+ ```ts
119
+ subscribe(name: string, fn: Function): Function
120
+ ```
121
+
122
+ Parameters:
123
+
124
+ - `name`: global event name.
125
+ - `fn`: callback receiving published data.
126
+
127
+ Return value: unsubscribe function.
128
+
129
+ Side effects: registers a global subscription.
130
+
131
+ Lifecycle implications: call returned function in `unmount()` when subscription lifetime is tied to a component.
132
+
133
+ Example:
134
+
135
+ ```ts
136
+ const unsubscribe = subscribe('my-component:fetched', data => {
137
+ console.log(data)
138
+ })
139
+ ```
140
+
141
+ Related APIs: `publish`, `unmount`.
142
+
143
+ ## `html`
144
+
145
+ Signature:
146
+
147
+ ```ts
148
+ html(strings: TemplateStringsArray, ...values: any[]): string
149
+ ```
150
+
151
+ Parameters: template string parts and interpolated values.
152
+
153
+ Return value: HTML string for template composition.
154
+
155
+ Side effects: none documented.
156
+
157
+ Example:
158
+
159
+ ```ts
160
+ import { html } from 'jails-js/html'
161
+
162
+ export const template = ({ children }) => html`
163
+ <section>
164
+ ${children}
165
+ <p>{{ counter }}</p>
166
+ </section>
167
+ `
168
+ ```
169
+
170
+ Related APIs: `template`, `attributes`.
171
+
172
+ ## `attributes`
173
+
174
+ Signature:
175
+
176
+ ```ts
177
+ attributes(values: Record<string, any>): string
178
+ ```
179
+
180
+ Parameters:
181
+
182
+ - object of attributes to serialize into template HTML.
183
+
184
+ Return value: attribute string.
185
+
186
+ Example:
187
+
188
+ ```ts
189
+ import { attributes } from 'jails-js/html'
190
+
191
+ html`<button ${attributes({ id: 'my-button-id' })}>+</button>`
192
+ ```
193
+
194
+ Related APIs: `html`, `template`.
195
+
196
+ # Component Module Exports
197
+
198
+ ## Default controller
199
+
200
+ Signature:
201
+
202
+ ```ts
203
+ export default function componentName(helpers: Component): void | Promise<void>
204
+ ```
205
+
206
+ Async controllers are supported. Code after `await` does not run until the awaited Promise resolves.
207
+
208
+ Side effects: registers lifecycle callbacks, event handlers, external widgets, subscriptions, and state transitions.
209
+
210
+ ## `model`
211
+
212
+ Signature:
213
+
214
+ ```ts
215
+ export const model: Model = {}
216
+ export const model = ({ elm, initialState, dependencies }): Model => ({})
217
+ ```
218
+
219
+ Return value: initial local state object.
220
+
221
+ Side effects: should be none.
222
+
223
+ ## `view`
224
+
225
+ Signature:
226
+
227
+ ```ts
228
+ export const view: View = (state: any) => any
229
+ ```
230
+
231
+ Return value: render scope object.
232
+
233
+ Side effects: should be none.
234
+
235
+ ## `template`
236
+
237
+ Signature:
238
+
239
+ ```ts
240
+ export const template: Template = ({ elm, children }) => string
241
+ ```
242
+
243
+ Return value: HTML string inserted/rendered for the component.
244
+
245
+ # Component Helpers
246
+
247
+ ## `main(fn)`
248
+
249
+ ```ts
250
+ main(fn: Function): void
251
+ ```
252
+
253
+ Registers the entry-point callback for code that should run after the component is mounted.
254
+
255
+ ## `elm`
256
+
257
+ ```ts
258
+ elm: HTMLElement
259
+ ```
260
+
261
+ Reference to the component custom element.
262
+
263
+ ## `dataset(key)` / `dataset(target, key)`
264
+
265
+ ```ts
266
+ dataset(key: string): any
267
+ dataset(target: HTMLElement, key: string): any
268
+ ```
269
+
270
+ Parses a `data-*` value from the component or target child.
271
+
272
+ ## `query(cssSelector)`
273
+
274
+ ```ts
275
+ query(cssSelector: string): Array<Promise<HTMLElement>>
276
+ ```
277
+
278
+ Finds child Jails components that may not be ready yet. Each result resolves when the component is available with public methods.
279
+
280
+ ## `dependencies`
281
+
282
+ ```ts
283
+ dependencies: Record<string, any>
284
+ ```
285
+
286
+ Dependencies object supplied by `register()`.
287
+
288
+ ## `innerHTML(html)` / `innerHTML(target, html)`
289
+
290
+ ```ts
291
+ innerHTML(html: string): void
292
+ innerHTML(target: HTMLElement, html: string): void
293
+ ```
294
+
295
+ Updates a target with an HTML string using DOM diffing.
296
+
297
+ ## `state.set(newprops)` / `state.set(mutator)`
298
+
299
+ ```ts
300
+ state.set(newprops: object): Promise<object>
301
+ state.set((currentState: object) => void): Promise<object>
302
+ ```
303
+
304
+ Updates local state. Object form merges specified properties. Function form receives current state for mutation. Promise resolves after DOM updates and can be used as next-tick behavior.
305
+
306
+ ## `state.get()`
307
+
308
+ ```ts
309
+ state.get(): object
310
+ ```
311
+
312
+ Returns the current state object.
313
+
314
+ ## `state.protected(props)`
315
+
316
+ ```ts
317
+ state.protected(props: Array<string>): void
318
+ ```
319
+
320
+ Prevents listed child component props from being overwritten by parent updates.
321
+
322
+ ## `on(event, selector?, callback)`
323
+
324
+ ```ts
325
+ on(event: string, cssSelector: string, callback: Function): void
326
+ on(event: string, callback: Function): void
327
+ on(attributePattern: string, cssSelector: string, callback: Function): void
328
+ ```
329
+
330
+ Registers component-scoped delegated events. Attribute-change syntax uses patterns such as `on('[src]', 'iframe', callback)`. Delegated event objects include `delegateTarget`.
331
+
332
+ ## `off(event, callback)`
333
+
334
+ ```ts
335
+ off(event: string, callback: Function): void
336
+ ```
337
+
338
+ Removes an event listener.
339
+
340
+ ## `emit(event, data?)`
341
+
342
+ ```ts
343
+ emit(event: string, data?: any): void
344
+ ```
345
+
346
+ Emits a bubbling DOM CustomEvent from the component.
347
+
348
+ ## `trigger(event, data?)` / `trigger(event, selector, data?)`
349
+
350
+ ```ts
351
+ trigger(event: string, data?: object): void
352
+ trigger(event: string, selector: string, data?: object): void
353
+ ```
354
+
355
+ Triggers an event or custom event from the component or a child matched by selector.
356
+
357
+ ## `publish(event, data?)`
358
+
359
+ ```ts
360
+ publish(event: string, data?: any): void
361
+ ```
362
+
363
+ Publishes a global pub/sub event.
364
+
365
+ ## `subscribe(event, callback)`
366
+
367
+ ```ts
368
+ subscribe(event: string, callback: Function): Function
369
+ ```
370
+
371
+ Subscribes globally and returns an unsubscribe function.
372
+
373
+ ## `effect(callback)`
374
+
375
+ ```ts
376
+ effect(callback: Function): void | Promise<any>
377
+ ```
378
+
379
+ Registers a sync or async function that receives parent props when parent updates occur. The callback may override or compose props.
380
+
381
+ ## `unmount(callback)`
382
+
383
+ ```ts
384
+ unmount(callback: Function): void
385
+ ```
386
+
387
+ Registers cleanup to run when the component element is detached from DOM.
388
+
@@ -0,0 +1,79 @@
1
+ # Architecture
2
+
3
+ ## Runtime Shape
4
+
5
+ Jails is a singleton API. Applications register component modules globally, then call `start()` to scan a DOM subtree. The framework is intended to be used once per page, not as multiple framework instances.
6
+
7
+ ## Rendering Pipeline
8
+
9
+ 1. Application imports component modules.
10
+ 2. Application calls `register(name, module, dependencies?)`.
11
+ 3. Application calls `start(target?)`.
12
+ 4. Jails scans `target` or `document.body` for registered custom elements.
13
+ 5. For each element, Jails builds component state from `model` and `html-model`.
14
+ 6. If a `template` export exists, Jails can generate embedded HTML using `{ elm, children }`.
15
+ 7. Jails evaluates template directives and mustache expressions against the render scope.
16
+ 8. Controller `main()` callbacks run after mount.
17
+ 9. Later `state.set()` calls update state and trigger template updates.
18
+
19
+ ## Update Flow
20
+
21
+ State updates go through `state.set()`. Object form changes only specified properties. Function form receives the current state and can mutate it. The method returns a Promise that resolves after DOM updates; use that Promise for next-tick work such as measuring updated DOM or starting animations.
22
+
23
+ `view(state)` runs as a render-scope transform. Values returned from `view` are intended for HTML only and do not become persistent state unless stored explicitly through `state.set()`.
24
+
25
+ Parent state changes can update child component props by default. A child can call `state.protected(['propName'])` to prevent selected props from being overwritten by parent updates.
26
+
27
+ ## DOM Ownership
28
+
29
+ The component element is the main rendering boundary. Jails owns directive-managed DOM inside that boundary. The browser owns normal form behavior unless the component state overwrites it. Third-party libraries may own subtrees marked with `html-static`.
30
+
31
+ Ownership rules:
32
+
33
+ - Jails-owned: nodes/attributes/text controlled by directives and mustache expressions.
34
+ - Browser-owned: native input value persistence when inputs are not re-rendered, especially with `html-static`.
35
+ - External-library-owned: widgets initialized by controller code inside `html-static` subtrees.
36
+ - Parent-owned: props passed down to children unless protected by child state.
37
+
38
+ ## Event System
39
+
40
+ `on()` registers delegated listeners at the component root. The selector argument identifies child elements. The event object is augmented with `delegateTarget`, which is the element that matched the selector. This is safer than `event.target`, which may be a nested element.
41
+
42
+ DOM events and custom events bubble. `emit()` sends CustomEvents upward to parents and siblings reachable by bubbling. `trigger()` programmatically dispatches events from the component or a selected child. `publish()` and `subscribe()` provide global pub/sub for components in different DOM trees.
43
+
44
+ Attribute changes can be observed with `on('[src]', 'iframe', callback)` and similar patterns. The callback receives `{ target, value, attribute }`.
45
+
46
+ ## Template System
47
+
48
+ Directives and mustache expressions evaluate JavaScript expressions. Default interpolation delimiters are `{{` and `}}`; `templateConfig()` can change them when a server renderer uses the same delimiters.
49
+
50
+ `html-if` controls conditional node existence. `html-for` repeats nodes for arrays or objects and provides `$index` and `$key`. `html-inner` writes expression results into an element. `html-*` maps expression values to real attributes. `html-static` skips diffing for a node and its children. Native `<template>` hides unresolved dynamic fragments from first paint.
51
+
52
+ ## Rendering Boundaries
53
+
54
+ Primary boundary: each registered custom element instance.
55
+
56
+ Nested boundary: child components receive parent props but have their own controller, state, lifecycle, and event root.
57
+
58
+ Static boundary: `html-static` excludes a subtree from Jails updates.
59
+
60
+ Conditional boundary: `html-if` may remove an entire subtree, causing child component detach and cleanup.
61
+
62
+ Loop boundary: `html-for` creates repeated instances of the directive element. The docs do not specify keyed reconciliation; assume position-based behavior.
63
+
64
+ ## Store Interactions
65
+
66
+ Jails documents component-local state rather than a centralized store. Use local state for UI ownership. Use parent props for downward data. Use DOM events for upward communication. Use pub/sub only for components in separate trees.
67
+
68
+ ## Service Interactions
69
+
70
+ Services can be imported directly or injected through `register()` dependencies. Injection is preferred for reusable components whose behavior depends on application-specific rules, for example form validations and masks.
71
+
72
+ ## SSR and SSG Implications
73
+
74
+ Jails is designed to enhance already rendered HTML, so it fits SSR and SSG. Server-rendered HTML appears before JavaScript loads. Directives then activate after `start()`. For shared SSR templates, export template functions separately and render them through the server framework. For fully featured components, export a `template` from the component module.
75
+
76
+ ## Hydration Behavior
77
+
78
+ The docs describe progressive enhancement rather than React-style full hydration. Jails scans existing DOM and applies behavior/directives. It does not require JavaScript to render the whole page on the server. Use native `<template>` or `html-inner` fallback content to avoid visible unresolved markers during first paint.
79
+