recyclrjs 1.0.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/GX_DOCUMENTATION.md +403 -0
- package/LICENSE +21 -0
- package/README.md +92 -0
- package/index.js +1583 -0
- package/package.json +44 -0
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
# GX (RecyclrJS) — HTML Swaps + Stimulus “Singleton” Controller
|
|
2
|
+
|
|
3
|
+
GX is a tiny “server-driven UI” helper: it **fetches HTML**, **parses the response**, **picks out fragments**, and **swaps them into the current DOM** based on `data-gx-*` attributes.
|
|
4
|
+
Think “HTMX-ish”, but intentionally lightweight and designed to work cleanly with Stimulus.
|
|
5
|
+
|
|
6
|
+
This documentation matches the current implementation in:
|
|
7
|
+
|
|
8
|
+
- `index.js` (GX core)
|
|
9
|
+
- `gx-controller.js` (Stimulus controller that owns a single GX instance)
|
|
10
|
+
- `index.blade.php`, `side.blade.php` (example markup patterns)
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## 1) Mental Model
|
|
15
|
+
|
|
16
|
+
1. User clicks a link / button, or submits a form.
|
|
17
|
+
2. Stimulus controller `gx#handleRequest` reads `data-gx-*` attributes.
|
|
18
|
+
3. The `GX` instance is configured (URL/method/form/select rules) and then `gx.request()` runs.
|
|
19
|
+
4. `GX.request()`:
|
|
20
|
+
- `fetch()`es the URL
|
|
21
|
+
- reads the HTML as text
|
|
22
|
+
- picks **swap rules** (either from the element’s `data-gx-select` or via presets)
|
|
23
|
+
- extracts fragments from the response using `DOMParser`
|
|
24
|
+
- performs DOM swaps
|
|
25
|
+
- optionally pushes browser history
|
|
26
|
+
- optionally dispatches a custom event (default: `gx:updated`)
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## 2) Why the GX Stimulus Controller Must Be a Singleton
|
|
31
|
+
|
|
32
|
+
### The core rule
|
|
33
|
+
|
|
34
|
+
**Only one `GX` instance should exist per page.**
|
|
35
|
+
|
|
36
|
+
Creating a new `GX` instance per click can stack global listeners (especially `popstate`) and can burn CPU/memory (Firefox is extra sensitive here). The patched controller intentionally creates **exactly one** `GX` instance inside `connect()` and reuses it for every request.
|
|
37
|
+
|
|
38
|
+
✅ Good:
|
|
39
|
+
|
|
40
|
+
- `connect()` creates `this.gx = new GX(...)`
|
|
41
|
+
- `handleRequest()` only updates properties on `this.gx` and calls `this.gx.request()`
|
|
42
|
+
|
|
43
|
+
❌ Bad:
|
|
44
|
+
|
|
45
|
+
- `handleRequest()` creates `new GX(...)` every time
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## 3) Quick Start
|
|
50
|
+
|
|
51
|
+
### 3.1 Add the controller once (usually on `<body>`)
|
|
52
|
+
|
|
53
|
+
```html
|
|
54
|
+
<body data-controller="gx">
|
|
55
|
+
...
|
|
56
|
+
</body>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### 3.2 Trigger a swap with a click
|
|
60
|
+
|
|
61
|
+
```html
|
|
62
|
+
<a
|
|
63
|
+
href="/patients"
|
|
64
|
+
data-action="click->gx#handleRequest"
|
|
65
|
+
data-gx-select="#content"
|
|
66
|
+
>
|
|
67
|
+
Patients
|
|
68
|
+
</a>
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### 3.3 Trigger a swap with a form submit
|
|
72
|
+
|
|
73
|
+
```html
|
|
74
|
+
<form
|
|
75
|
+
action="/login"
|
|
76
|
+
method="post"
|
|
77
|
+
data-action="submit->gx#handleRequest"
|
|
78
|
+
data-gx-select="#content #sidebar"
|
|
79
|
+
>
|
|
80
|
+
...
|
|
81
|
+
</form>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
> Tip: Always include the event in `data-action` (`click->` / `submit->`).
|
|
85
|
+
> The shorthand `data-action="gx#handleRequest"` is easy to forget and harder to reason about.
|
|
86
|
+
> By default, the selector for the input is the output, and the default location is outerHTML, so if you find yourself writing #content@outerHTML->#content@outerHTML, you can just write #content
|
|
87
|
+
> Also by default, data-gx-method is get, so you can really slim it down to something like <a href="http://foo.bar" data-gx-select="#content" data-action="click->gx#handleRequest">Hello World</a>
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## 4) `data-gx-*` Attribute Reference
|
|
92
|
+
|
|
93
|
+
GX reads these attributes from the clicked/submitted element:
|
|
94
|
+
|
|
95
|
+
### Required
|
|
96
|
+
|
|
97
|
+
- **`data-gx-select`**
|
|
98
|
+
Swap rule string describing what to extract from the response and where to place it in the current DOM.
|
|
99
|
+
|
|
100
|
+
### Optional
|
|
101
|
+
|
|
102
|
+
- **`data-gx-url`**
|
|
103
|
+
Overrides the URL. If missing, GX will fall back to:
|
|
104
|
+
- `href` for anchors/buttons
|
|
105
|
+
- `action` for forms
|
|
106
|
+
|
|
107
|
+
- **`data-gx-method`**
|
|
108
|
+
`get | post | put` (lowercase preferred). If missing, GX falls back to the element’s `method` attribute or defaults to `get`.
|
|
109
|
+
|
|
110
|
+
- **`data-gx-form`**
|
|
111
|
+
CSS selector for the form whose fields should be submitted. Useful when a button is outside the `<form>`.
|
|
112
|
+
|
|
113
|
+
- **`data-gx-history`**
|
|
114
|
+
`on | off` (defaults to `on` in the controller unless overridden).
|
|
115
|
+
When enabled, GX will `history.pushState()` after a successful request.
|
|
116
|
+
|
|
117
|
+
- **`data-gx-debug`**
|
|
118
|
+
`on | off` (defaults to `off`). Enables verbose debug logging.
|
|
119
|
+
|
|
120
|
+
- **`data-gx-loading`** _(implemented)_
|
|
121
|
+
CSS selector for a spinner element. GX sets `display: block` while the request runs and hides it afterwards.
|
|
122
|
+
|
|
123
|
+
- **`data-gx-presets`** _(advanced)_
|
|
124
|
+
Overrides which presets are available for the request.
|
|
125
|
+
|
|
126
|
+
> Notes on incomplete/placeholder attributes:
|
|
127
|
+
>
|
|
128
|
+
> - `data-gx-disable` and `data-gx-error` currently exist in the controller plumbing, but the GX core does not yet apply them (they’re stored but not acted on).
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## 5) Swap Rules (`data-gx-select`) Syntax
|
|
133
|
+
|
|
134
|
+
A swap rule has the general form:
|
|
135
|
+
|
|
136
|
+
```
|
|
137
|
+
<sourceSelector>@<sourceLocation> -> <targetSelector>@<targetLocation>
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Rules are separated by spaces.
|
|
141
|
+
|
|
142
|
+
### 5.1 Minimal form (location defaults to `outerHTML`)
|
|
143
|
+
|
|
144
|
+
```
|
|
145
|
+
#content
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### 5.2 Full form
|
|
149
|
+
|
|
150
|
+
```
|
|
151
|
+
#content@outerHTML->#content@outerHTML
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### 5.3 Multiple swaps in one request
|
|
155
|
+
|
|
156
|
+
```
|
|
157
|
+
#content@outerHTML->#content@outerHTML #sidebar@outerHTML->#sidebar@outerHTML
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### 5.4 Supported target locations
|
|
161
|
+
|
|
162
|
+
These target locations are currently supported by `GX.render()`:
|
|
163
|
+
|
|
164
|
+
- `innerHTML`
|
|
165
|
+
- `outerHTML`
|
|
166
|
+
- `beforebegin`
|
|
167
|
+
- `afterbegin`
|
|
168
|
+
- `beforeend`
|
|
169
|
+
- `afterend`
|
|
170
|
+
|
|
171
|
+
Target insertion uses `insertAdjacentHTML()` for the `before*/after*` variants.
|
|
172
|
+
|
|
173
|
+
### 5.5 Source “locations” (what you extract from the response)
|
|
174
|
+
|
|
175
|
+
For the **source**, GX reads a property off the response element:
|
|
176
|
+
|
|
177
|
+
```js
|
|
178
|
+
doc.querySelector(sourceSelector)[sourceLocation];
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
So you can technically use any string property on an Element (e.g., `innerHTML`, `outerHTML`, `textContent`, `value`), **but** keep in mind:
|
|
182
|
+
|
|
183
|
+
- If your target location is not one of the supported ones (section 5.4), `render()` will throw.
|
|
184
|
+
- In practice, stick to `innerHTML` and `outerHTML`.
|
|
185
|
+
|
|
186
|
+
### 5.6 Conditional rules (reserved)
|
|
187
|
+
|
|
188
|
+
Rules may be prefixed with:
|
|
189
|
+
|
|
190
|
+
```
|
|
191
|
+
condition:...
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Example:
|
|
195
|
+
|
|
196
|
+
```
|
|
197
|
+
redirect:#content@outerHTML->#content@outerHTML
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Right now, conditions are only used in a limited way internally (ex: redirect/error paths). Treat this as reserved unless you’re extending GX.
|
|
201
|
+
|
|
202
|
+
### 5.7 Property list syntax (parsed but **not implemented**)
|
|
203
|
+
|
|
204
|
+
The `Location` helper parses bracket syntax like:
|
|
205
|
+
|
|
206
|
+
```
|
|
207
|
+
#avatar@outerHTML[src,alt]->#avatar@outerHTML
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
…but the actual “update only these props” logic is not implemented yet. Currently, if brackets are present, GX will short-circuit that rule.
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## 6) Presets System (Optional, but Powerful)
|
|
215
|
+
|
|
216
|
+
GX can choose swap rules automatically via a “presets” configuration.
|
|
217
|
+
|
|
218
|
+
### 6.1 How presets are selected
|
|
219
|
+
|
|
220
|
+
`GX.evaluateWithPresets()` will use presets if any of these are true:
|
|
221
|
+
|
|
222
|
+
- Server returns a `Recyclr-Use-Presets` header (comma/semicolon separated list)
|
|
223
|
+
- A trigger is matched:
|
|
224
|
+
- `response.redirected` → `config.triggers.redirect`
|
|
225
|
+
- `response.status` → `config.triggers["status:<code>"]`
|
|
226
|
+
|
|
227
|
+
If no preset is chosen, GX falls back to the request’s `data-gx-select`.
|
|
228
|
+
|
|
229
|
+
### 6.2 Presets config shape
|
|
230
|
+
|
|
231
|
+
```js
|
|
232
|
+
{
|
|
233
|
+
presets: {
|
|
234
|
+
refreshContent: [
|
|
235
|
+
"#content@outerHTML->#content@outerHTML",
|
|
236
|
+
"#sidebar@outerHTML->#sidebar@outerHTML"
|
|
237
|
+
],
|
|
238
|
+
|
|
239
|
+
// You can also provide a rule object:
|
|
240
|
+
toast: { literal: "<div>Saved</div>", dst: "#toasts", dstLoc: "beforeend" }
|
|
241
|
+
},
|
|
242
|
+
|
|
243
|
+
fallback: "refreshContent",
|
|
244
|
+
|
|
245
|
+
triggers: {
|
|
246
|
+
redirect: ["refreshContent"],
|
|
247
|
+
"status:401": ["refreshContent"]
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### 6.3 Loading presets externally + TTL cache
|
|
253
|
+
|
|
254
|
+
If `config.presetsUrl` is set, GX loads presets once and caches them in `localStorage` using `config.presetsTTLMs`.
|
|
255
|
+
|
|
256
|
+
This is meant for “drop-in behavior changes” without redeploying JS.
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
## 7) Browser History / Back-Forward Behavior
|
|
261
|
+
|
|
262
|
+
When history is enabled for a request:
|
|
263
|
+
|
|
264
|
+
- GX calls `history.pushState(null, 'Page Title', this.url)`
|
|
265
|
+
|
|
266
|
+
The Stimulus controller installs **one** `popstate` listener to re-run a GET request and refresh `#content`.
|
|
267
|
+
|
|
268
|
+
If you would rather force a full page reload on back/forward, comment in the `location.reload()` line in the controller.
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
## 8) Custom Events
|
|
273
|
+
|
|
274
|
+
When `dispatch` is enabled:
|
|
275
|
+
|
|
276
|
+
- GX dispatches a `CustomEvent` on `document` (or the provided target)
|
|
277
|
+
- Event name format is:
|
|
278
|
+
|
|
279
|
+
```
|
|
280
|
+
<identifier>:<eventName>
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
With the default config, that’s typically:
|
|
284
|
+
|
|
285
|
+
- `gx:updated`
|
|
286
|
+
|
|
287
|
+
You can override the event name from the server via `Recyclr-Event` response header.
|
|
288
|
+
|
|
289
|
+
### Example listener
|
|
290
|
+
|
|
291
|
+
```js
|
|
292
|
+
document.addEventListener("gx:updated", (e) => {
|
|
293
|
+
console.log("GX updated:", e.detail);
|
|
294
|
+
});
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
---
|
|
298
|
+
|
|
299
|
+
## 9) Performance & Memory Safety Checklist (Firefox-Friendly)
|
|
300
|
+
|
|
301
|
+
If you hear your laptop fans rev up after lots of navigation/modals, check these first:
|
|
302
|
+
|
|
303
|
+
### 9.1 Ensure you aren’t appending forever
|
|
304
|
+
|
|
305
|
+
Using target locations like `beforeend` / `afterend` **adds nodes** without removing old ones. That’s fine for “toasts”, but not for “pages”.
|
|
306
|
+
|
|
307
|
+
For pages/modals, prefer:
|
|
308
|
+
|
|
309
|
+
- A fixed container, swapped via `innerHTML` or `outerHTML`
|
|
310
|
+
|
|
311
|
+
### 9.2 Avoid global listeners in swapped components
|
|
312
|
+
|
|
313
|
+
Stimulus controllers **should** clean up on `disconnect()`, but only if:
|
|
314
|
+
|
|
315
|
+
- you attach listeners in `connect()` and remove them in `disconnect()`
|
|
316
|
+
- you don’t attach listeners to `window/document` without cleanup
|
|
317
|
+
|
|
318
|
+
### 9.3 Do not create a new GX per request
|
|
319
|
+
|
|
320
|
+
Again: **singleton GX instance**. Creating a new instance per click can stack listeners and retain references.
|
|
321
|
+
|
|
322
|
+
### 9.4 Cancel in-flight requests (nice-to-have)
|
|
323
|
+
|
|
324
|
+
GX currently doesn’t abort previous requests. If a user clicks rapidly, you can end up doing extra work.
|
|
325
|
+
If this becomes a real problem, add `AbortController` support in `GX.request()`.
|
|
326
|
+
|
|
327
|
+
---
|
|
328
|
+
|
|
329
|
+
## 10) Known Limitations / TODOs
|
|
330
|
+
|
|
331
|
+
- `data-gx-disable` is not applied yet (busy-state helper exists, but GX never targets a real element selector).
|
|
332
|
+
- `data-gx-error` is not displayed yet (stored but unused).
|
|
333
|
+
- Bracket property lists (`@outerHTML[src,alt]`) are parsed but not implemented.
|
|
334
|
+
- Target locations are limited to the list in section 5.4.
|
|
335
|
+
|
|
336
|
+
---
|
|
337
|
+
|
|
338
|
+
## 11) Troubleshooting
|
|
339
|
+
|
|
340
|
+
### “Nothing updated”
|
|
341
|
+
|
|
342
|
+
- Check that `data-gx-select` points at selectors that exist in the **response HTML**.
|
|
343
|
+
- Open DevTools → Network → verify response contains the fragment you expect.
|
|
344
|
+
|
|
345
|
+
### “Target selector not found”
|
|
346
|
+
|
|
347
|
+
- The _target_ selector must exist in the current DOM at the time of swap.
|
|
348
|
+
- If you are swapping the container that contains the trigger, avoid self-destructing the click target mid-flight.
|
|
349
|
+
|
|
350
|
+
### “Firefox CPU spikes”
|
|
351
|
+
|
|
352
|
+
- Look for repeated `popstate` listeners or duplicated controllers.
|
|
353
|
+
- Ensure modals are swapped/reused, not appended repeatedly.
|
|
354
|
+
|
|
355
|
+
---
|
|
356
|
+
|
|
357
|
+
## 12) Examples From Our Blade Templates (Patterns)
|
|
358
|
+
|
|
359
|
+
### “Swap main content + sidebar”
|
|
360
|
+
|
|
361
|
+
```html
|
|
362
|
+
<a
|
|
363
|
+
href="/some/page"
|
|
364
|
+
data-action="click->gx#handleRequest"
|
|
365
|
+
data-gx-method="get"
|
|
366
|
+
data-gx-select="#content #side-nav"
|
|
367
|
+
>
|
|
368
|
+
Go
|
|
369
|
+
</a>
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
### “Submit a form and refresh key regions”
|
|
373
|
+
|
|
374
|
+
```html
|
|
375
|
+
<form
|
|
376
|
+
action="/save"
|
|
377
|
+
method="post"
|
|
378
|
+
data-action="submit->gx#handleRequest"
|
|
379
|
+
data-gx-select="#content@outerHTML->#content@outerHTML #alerts@innerHTML->#alerts@innerHTML"
|
|
380
|
+
data-gx-loading="#global-spinner"
|
|
381
|
+
>
|
|
382
|
+
...
|
|
383
|
+
</form>
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
---
|
|
387
|
+
|
|
388
|
+
## 13) Extending GX (Recommended Next Steps)
|
|
389
|
+
|
|
390
|
+
If you want to tighten up UX and reduce bugs:
|
|
391
|
+
|
|
392
|
+
1. **Wire `data-gx-disable` into `applyBusyState()`**
|
|
393
|
+
- Use `this.disable` as a selector for the container you want to lock during requests.
|
|
394
|
+
|
|
395
|
+
2. **Implement bracket property updates**
|
|
396
|
+
- In `GX.evaluate()`, when `Location.properties()` returns a list, update only those properties on the _target element_.
|
|
397
|
+
|
|
398
|
+
3. **Add AbortController**
|
|
399
|
+
- Store `this._abort` in GX and abort previous in-flight requests on a new request.
|
|
400
|
+
|
|
401
|
+
That combo makes GX feel much closer to “production HTMX” without losing the simplicity.
|
|
402
|
+
|
|
403
|
+
---
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Christopher Cordine
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# RecyclrJS
|
|
2
|
+
|
|
3
|
+
RecyclrJS exports `GX`, a small browser-side helper for fetching HTML and swapping fragments into the current DOM.
|
|
4
|
+
|
|
5
|
+
Project site: https://rgx.cordine.site
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install recyclrjs
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```js
|
|
16
|
+
const GX = require('recyclrjs');
|
|
17
|
+
|
|
18
|
+
const gx = new GX({
|
|
19
|
+
url: '/patients',
|
|
20
|
+
method: 'get',
|
|
21
|
+
selection: '#content@outerHTML->#content@outerHTML'
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
gx.request();
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
You can also import the named property:
|
|
28
|
+
|
|
29
|
+
```js
|
|
30
|
+
const { GX } = require('recyclrjs');
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Vanilla DOM
|
|
34
|
+
|
|
35
|
+
If you do not want Stimulus, mount the built-in delegated listeners once:
|
|
36
|
+
|
|
37
|
+
```js
|
|
38
|
+
const Recyclr = require('recyclrjs');
|
|
39
|
+
|
|
40
|
+
Recyclr.mount(document);
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Example markup:
|
|
44
|
+
|
|
45
|
+
```html
|
|
46
|
+
<a href="/patients" data-gx-select="#content" recyclr="click">Patients</a>
|
|
47
|
+
|
|
48
|
+
<form action="/login" data-gx-select="#content" recyclr="submit">
|
|
49
|
+
...
|
|
50
|
+
</form>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Realtime
|
|
54
|
+
|
|
55
|
+
If you already have a `GX` instance, you can feed it updates from WebSocket or SSE messages:
|
|
56
|
+
|
|
57
|
+
```js
|
|
58
|
+
const Recyclr = require('recyclrjs');
|
|
59
|
+
|
|
60
|
+
const gx = new Recyclr({
|
|
61
|
+
url: '/patients',
|
|
62
|
+
selection: '#content@innerHTML->#content@innerHTML'
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const stream = Recyclr.createRecyclrStream({
|
|
66
|
+
wsUrl: '/realtime',
|
|
67
|
+
sseUrl: '/realtime',
|
|
68
|
+
gx
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
stream.start();
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The realtime payload should include `html`, and can optionally include `presets`, `eventName`, or `rules`.
|
|
75
|
+
|
|
76
|
+
## Versioning
|
|
77
|
+
|
|
78
|
+
RecyclrJS follows Semantic Versioning:
|
|
79
|
+
|
|
80
|
+
- `major.minor.patch`
|
|
81
|
+
- `patch` for small fixes
|
|
82
|
+
- `minor` for backward-compatible features
|
|
83
|
+
- `major` for breaking changes
|
|
84
|
+
|
|
85
|
+
The repo starts at `1.0.0`. For releases, keep GitHub tags in sync with npm publishes, for example `v1.0.1` or `v1.1.0`.
|
|
86
|
+
Using `npm version patch|minor|major` is the simplest path because it updates `package.json` and creates the matching git tag.
|
|
87
|
+
|
|
88
|
+
## Notes
|
|
89
|
+
|
|
90
|
+
- This package is CommonJS and targets the browser.
|
|
91
|
+
- `mount()` and `createRecyclrStream()` are the no-Stimulus runtime APIs. The `controllers/` folder remains as an adapter/example, not a runtime requirement.
|
|
92
|
+
- The full implementation notes live in [`GX_DOCUMENTATION.md`](./GX_DOCUMENTATION.md).
|