@cocreate/elements 1.46.0 → 2.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.
@@ -6,8 +6,15 @@ import { getAttributes } from "@cocreate/utils";
6
6
  import { initDataKey, getData, getDataObject } from "./getDataHandler.js";
7
7
  import { read, save } from "./crudHandler.js";
8
8
  import { setData } from "./setDataHandler.js";
9
- import { elements, forms, keys, debounce } from "./mapRegistry.js";
10
- import { unsubscribe} from "./subscribeHandler.js";
9
+ import {
10
+ elements,
11
+ forms,
12
+ keys,
13
+ debounce,
14
+ elementSubscriptions,
15
+ subscriptions
16
+ } from "./mapRegistry.js";
17
+ import { unsubscribe } from "./subscribeHandler.js";
11
18
 
12
19
  const selector = "[storage], [database], [array], [render-json]";
13
20
 
@@ -36,36 +43,31 @@ export async function init(element) {
36
43
  elements.has(element[i]) ||
37
44
  element[i].tagName === "FORM" ||
38
45
  element[i].getAttribute("crud") === "false"
39
- )
46
+ ) {
40
47
  continue;
48
+ }
41
49
 
42
50
  promises.push(
43
51
  (async () => {
44
- let data = await initElement(element[i]);
45
- if (data) {
46
- let dataKey = initDataKey(element[i], data);
47
- return { string: dataKey.string, data };
52
+ const el = element[i];
53
+ try {
54
+ const data = await initElement(el);
55
+ if (data) {
56
+ const dataKey = initDataKey(el, data);
57
+ // read() already batches elements by dataKey. Schedule each
58
+ // ready element without waiting for unrelated async fields.
59
+ await read(el, data, dataKey);
60
+ }
61
+ } catch (error) {
62
+ // Allow initialization to be retried if filter setup failed.
63
+ if (elements.get(el) === "") elements.delete(el);
64
+ console.error("[CoCreateElements] Failed to initialize element:", el, error);
48
65
  }
49
66
  })()
50
67
  );
51
68
  }
52
69
 
53
- let results = await Promise.all(promises);
54
- let dataObjects = new Map();
55
-
56
- for (let res of results) {
57
- if (res) dataObjects.set(res.string, res.data);
58
- }
59
-
60
- if (dataObjects && dataObjects.size > 0) {
61
- for (let key of dataObjects.keys()) {
62
- let keyEntry = keys.get(key);
63
- if (keyEntry) {
64
- let { data, elements: mappedElements } = keyEntry;
65
- read(Array.from(mappedElements.keys()), data, { string: key });
66
- }
67
- }
68
- }
70
+ await Promise.all(promises);
69
71
  }
70
72
 
71
73
  /**
@@ -76,14 +78,17 @@ export async function init(element) {
76
78
  */
77
79
  export async function initElement(el) {
78
80
  let data = getDataObject(el);
79
- if (!data || !data.type) return;
81
+ if (!data || !data.type) {
82
+ return;
83
+ }
80
84
 
81
85
  if (
82
86
  data.object &&
83
87
  data.object !== "pending" &&
84
88
  !/^[0-9a-fA-F]{24}$/.test(data.object)
85
- )
89
+ ) {
86
90
  return;
91
+ }
87
92
 
88
93
  if (!elements.has(el)) {
89
94
  elements.set(el, "");
@@ -124,10 +129,10 @@ export async function initElement(el) {
124
129
  await filter.init(el);
125
130
 
126
131
  if (el.getFilter) {
127
- data.$filter = await el.getFilter();
132
+ data.filter = await el.getFilter();
128
133
 
129
134
  el.setFilter = (filter) => {
130
- data.$filter = filter;
135
+ data.filter = filter;
131
136
  el.hasRead = false;
132
137
  read(el, data);
133
138
  };
@@ -160,16 +165,13 @@ export async function remove(element) {
160
165
 
161
166
  form = form.elements;
162
167
  for (let formEl of form.keys()) {
168
+ removeSubscription(formEl);
163
169
  let key = elements.get(formEl);
164
170
  if (key) {
165
171
  let keyEntry = keys.get(key);
166
172
  if (keyEntry) {
167
173
  keyEntry.elements.delete(formEl);
168
- if (!keyEntry.elements.size) {
169
- // Last element watching this key was removed -> trigger socket unsubscribe
170
- unsubscribe(keyEntry.dataKey, keyEntry.data);
171
- keys.delete(key);
172
- }
174
+ if (!keyEntry.elements.size) keys.delete(key);
173
175
  }
174
176
  elements.delete(formEl);
175
177
  }
@@ -180,15 +182,13 @@ export async function remove(element) {
180
182
  let keyEntry = keys.get(key);
181
183
  if (keyEntry) {
182
184
  keyEntry.elements.delete(el);
183
- if (!keyEntry.elements.size) {
184
- unsubscribe(keyEntry.dataKey, keyEntry.data);
185
- keys.delete(key);
186
- }
185
+ if (!keyEntry.elements.size) keys.delete(key);
187
186
  }
188
187
  }
189
188
  elements.delete(el);
190
189
  forms.delete(el);
191
190
  } else {
191
+ removeSubscription(el);
192
192
  let key = elements.get(el);
193
193
  if (key) {
194
194
  if (el.getFilter) filter.filters.delete(el);
@@ -198,11 +198,7 @@ export async function remove(element) {
198
198
  keyEntry.elements.delete(el);
199
199
  elements.delete(el);
200
200
 
201
- if (!keyEntry.elements.size) {
202
- // Last element watching this key was removed -> trigger socket unsubscribe
203
- unsubscribe(keyEntry.dataKey, keyEntry.data);
204
- keys.delete(key);
205
- }
201
+ if (!keyEntry.elements.size) keys.delete(key);
206
202
  } else {
207
203
  elements.delete(el);
208
204
  }
@@ -220,11 +216,27 @@ export async function remove(element) {
220
216
  }
221
217
  }
222
218
 
219
+ function removeSubscription(element) {
220
+ const subscription = elementSubscriptions.get(element);
221
+ if (!subscription) return;
222
+
223
+ const subscriptionEntry = subscriptions.get(subscription);
224
+ if (subscriptionEntry) {
225
+ subscriptionEntry.elements.delete(element);
226
+ if (!subscriptionEntry.elements.size) {
227
+ unsubscribe(subscriptionEntry.subscription);
228
+ subscriptions.delete(subscription);
229
+ }
230
+ }
231
+
232
+ elementSubscriptions.delete(element);
233
+ }
234
+
223
235
  /**
224
236
  * Initializes global CRUD socket response listeners.
225
237
  */
226
238
  export function initSocket() {
227
- const type = ["storage", "database", "array", "index", "object", "filter"];
239
+ const type = ["storage", "database", "array", "index", "object"];
228
240
  const method = ["create", "update", "delete"];
229
241
 
230
242
  for (let i = 0; i < type.length; i++) {
@@ -233,9 +245,11 @@ export function initSocket() {
233
245
 
234
246
  CRUD.listen(action, function (data) {
235
247
  if (
236
- data.resolved &&
248
+ data.key &&
249
+ !data.updateDB &&
237
250
  data.status === "received" &&
238
- CRUD.socket.clientId === data.clientId
251
+ !data.method.includes(".delete") &&
252
+ CRUD.socket.frameId === data.frameId
239
253
  )
240
254
  return;
241
255
  if (data.rendered) return;
@@ -253,6 +267,7 @@ Observer.init({
253
267
  types: ["addedNodes"],
254
268
  selector: selector,
255
269
  callback: function (mutation) {
270
+ if (mutation.move) return;
256
271
  init([mutation.target]);
257
272
  }
258
273
  });
@@ -262,7 +277,7 @@ Observer.init({
262
277
  types: ["removedNodes"],
263
278
  selector: selector,
264
279
  callback: function (mutation) {
265
- if (mutation.target.hasAttribute("render-clone")) return;
280
+ if (mutation.move) return;
266
281
  remove(mutation.target);
267
282
  }
268
283
  });
@@ -273,6 +288,8 @@ Observer.init({
273
288
  attributeFilter: [
274
289
  "organization_id",
275
290
  "host",
291
+ "namespace",
292
+ "room",
276
293
  "storage",
277
294
  "database",
278
295
  "array",
@@ -290,6 +307,39 @@ Observer.init({
290
307
  }
291
308
  });
292
309
 
310
+ Observer.init({
311
+ name: "CoCreateElementsReadAttribute",
312
+ types: ["attributes"],
313
+ attributeFilter: ["read"],
314
+ selector,
315
+ callback: async function (mutation) {
316
+ const el = mutation.target;
317
+ const currentValue = el.getAttribute("read");
318
+ if (currentValue === mutation.oldValue) return;
319
+
320
+ if (currentValue === "false") {
321
+ el.hasRead = false;
322
+ for (const [key, pending] of debounce) {
323
+ pending.elements?.delete(el);
324
+ if (pending.elements && !pending.elements.size) {
325
+ clearTimeout(pending.timer);
326
+ debounce.delete(key);
327
+ }
328
+ }
329
+ return;
330
+ }
331
+
332
+ if (!elements.has(el)) {
333
+ await init([el]);
334
+ } else if (el.getFilter && el.setFilter) {
335
+ el.setFilter(await el.getFilter());
336
+ } else {
337
+ el.hasRead = false;
338
+ await read(el);
339
+ }
340
+ }
341
+ });
342
+
293
343
  init();
294
344
 
295
- export default { init, initElement, initSocket, remove };
345
+ export default { init, initElement, initSocket, remove };
@@ -2,5 +2,14 @@ export const elements = new WeakMap();
2
2
  export const forms = new WeakMap();
3
3
  export const keys = new Map();
4
4
  export const debounce = new Map();
5
+ export const elementSubscriptions = new WeakMap();
6
+ export const subscriptions = new Map();
5
7
 
6
- export default { elements, forms, keys, debounce}
8
+ export default {
9
+ elements,
10
+ forms,
11
+ keys,
12
+ debounce,
13
+ elementSubscriptions,
14
+ subscriptions
15
+ }