@ea-lab/reactive-json-docs 2.6.0 → 2.7.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ea-lab/reactive-json-docs",
3
- "version": "2.6.0",
3
+ "version": "2.7.0",
4
4
  "description": "Complete documentation for Reactive-JSON - Components, examples and LLM-parsable guides",
5
5
  "main": "public/rjbuild/docs/index.yaml",
6
6
  "files": [
@@ -242,6 +242,26 @@ Editing either field mirrors live to the other, and the whole group produces a s
242
242
  - Leaving `mergeKey` unset keeps the default behavior — one independent sync per instance.
243
243
  - Known limitation: if the owner unmounts while a sync is still pending, that in-flight edit is not persisted (the new owner only writes on the next edit).
244
244
 
245
+ ### Overriding the grouping (advanced)
246
+
247
+ The group coordination — membership, owner election, and the live broadcast — is not hard-wired into `DataSync`. It is resolved from a **`utility` plugin** named `dataSyncGroups`, falling back to the core singleton registry when none is supplied. This lets an app swap *just* the grouping behavior (e.g. a registry scoped per tab, or one that bridges across iframes) without overriding the whole `DataSync` element.
248
+
249
+ Register your own under `plugins.utility`:
250
+
251
+ ```js
252
+ const myDataSyncGroups = {
253
+ joinSyncGroup(key, member) { /* … returns a leave() function */ },
254
+ broadcastToGroup(key, fromMember, data) { /* … */ },
255
+ getOwner(key) { /* … returns the current writer or null */ },
256
+ };
257
+
258
+ const plugins = mergeComponentCollections([
259
+ { utility: { dataSyncGroups: myDataSyncGroups } },
260
+ ]);
261
+ ```
262
+
263
+ The registry must be a **shared singleton**: every `DataSync` that resolves the same `dataSyncGroups` object joins the same groups. Provide one instance, not one per render.
264
+
245
265
  ## Using with Templates
246
266
 
247
267
  `DataSync` supports `TemplateContext`, so it can be used inside templates to sync individual items in a list:
@@ -138,6 +138,12 @@ renderView:
138
138
  details:
139
139
  type: Markdown
140
140
  content: "Milliseconds between retry attempts. Default: `5000`."
141
+ - term:
142
+ code: mergeKey
143
+ after: " (string, optional)"
144
+ details:
145
+ type: Markdown
146
+ content: "Path to a **stable identity** for the syncable (typically its `submission_url`). All `DataSync` instances resolving to the same value form a shared group: edits are mirrored live across them and persisted by a single owner. See **Shared Syncables** below."
141
147
 
142
148
  - type: Markdown
143
149
  content: |
@@ -268,6 +274,67 @@ renderView:
268
274
 
269
275
  `DataSync` only triggers synchronization when the `data` field of the Syncable Object changes. Changes to `status`, `submission_url`, or other fields are ignored. This prevents feedback loops when the component updates the status or when the server response updates the object.
270
276
 
277
+ ## Shared Syncables (`mergeKey`)
278
+
279
+ When the **same resource** is mounted in several places at once — the same record shown in two views, or one editable item embedded in multiple components — each `DataSync` would normally watch its own copy and POST independently. That causes duplicate writes and lets the copies drift apart.
280
+
281
+ Set `mergeKey` to make those instances cooperate. It is a path resolving to a **stable identity** for the syncable (commonly its `submission_url`). Every `DataSync` that resolves to the same value forms a **group**:
282
+
283
+ - **One writer (the owner).** The first instance to join is the group's owner, re-elected automatically if it unmounts. Only the owner sends requests.
284
+ - **Live mirror.** When any member's `data` changes, that data is applied to every other member's store — with their own change-watcher suppressed, so they neither re-broadcast nor POST. All copies stay in sync on screen.
285
+ - **Single, coalesced write.** Persistence is delegated to the owner, which (re)schedules its one debounced sync and reads the **latest** shared data when it fires. Rapid edits across different members collapse into a single request carrying the final value — no concurrent writers, no stale-data write, no revert.
286
+ - **Server propagation.** On a successful sync, the owner broadcasts the (server-enriched) response `data` back to the group.
287
+
288
+ ```yaml
289
+ data:
290
+ cardA:
291
+ submission_url: "/api/items/123"
292
+ data: { title: "Hello" }
293
+ cardB:
294
+ submission_url: "/api/items/123" # SAME entity, second mount
295
+ data: { title: "Hello" }
296
+
297
+ renderView:
298
+ - type: TextField
299
+ dataLocation: ~~.cardA.data.title
300
+ - type: DataSync
301
+ path: ~~.cardA
302
+ mergeKey: ~~.cardA.submission_url
303
+
304
+ - type: TextField
305
+ dataLocation: ~~.cardB.data.title
306
+ - type: DataSync
307
+ path: ~~.cardB
308
+ mergeKey: ~~.cardB.submission_url
309
+ ```
310
+
311
+ Editing either field mirrors live to the other, and the whole group produces a single POST.
312
+
313
+ **Notes**
314
+ - The library stays agnostic about what makes two syncables "the same": you pick the identity by pointing `mergeKey` at whatever field is stable per resource (usually `submission_url`).
315
+ - Leaving `mergeKey` unset keeps the default behavior — one independent sync per instance.
316
+ - Known limitation: if the owner unmounts while a sync is still pending, that in-flight edit is not persisted (the new owner only writes on the next edit).
317
+
318
+ ### Overriding the grouping (advanced)
319
+
320
+ The group coordination — membership, owner election, and the live broadcast — is not hard-wired into `DataSync`. It is resolved from a **`utility` plugin** named `dataSyncGroups`, falling back to the core singleton registry when none is supplied. This lets an app swap *just* the grouping behavior (e.g. a registry scoped per tab, or one that bridges across iframes) without overriding the whole `DataSync` element.
321
+
322
+ Register your own under `plugins.utility`:
323
+
324
+ ```js
325
+ const myDataSyncGroups = {
326
+ joinSyncGroup(key, member) { /* … returns a leave() function */ },
327
+ broadcastToGroup(key, fromMember, data) { /* … */ },
328
+ getOwner(key) { /* … returns the current writer or null */ },
329
+ };
330
+
331
+ const plugins = mergeComponentCollections([
332
+ { utility: { dataSyncGroups: myDataSyncGroups } },
333
+ ]);
334
+ ```
335
+
336
+ The registry must be a **shared singleton**: every `DataSync` that resolves the same `dataSyncGroups` object joins the same groups. Provide one instance, not one per render.
337
+
271
338
  ## Using with Templates
272
339
 
273
340
  `DataSync` supports `TemplateContext`, so it can be used inside templates to sync individual items in a list: