coaction 1.2.0 → 1.3.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/dist/index.js CHANGED
@@ -81,18 +81,23 @@ var createAsyncClientStore = (createStore, asyncStoreClientOption) => {
81
81
  }
82
82
  asyncClientStore.transport = transport;
83
83
  let syncingPromise = null;
84
- const fullSync = async () => {
84
+ let awaitingReconnectSync = false;
85
+ let reconnectSequenceBaseline = null;
86
+ const fullSync = async (allowLowerSequence = false) => {
85
87
  if (!syncingPromise) {
86
88
  syncingPromise = (async () => {
87
89
  const latest = await transport.emit("fullSync");
88
90
  if (typeof latest.sequence !== "number" || typeof latest.state !== "string") {
89
91
  throw new Error("Invalid fullSync payload");
90
92
  }
91
- if (latest.sequence < internal.sequence) {
93
+ const canApplyLowerSequence = allowLowerSequence && awaitingReconnectSync && reconnectSequenceBaseline !== null && reconnectSequenceBaseline === internal.sequence;
94
+ if (latest.sequence < internal.sequence && !canApplyLowerSequence) {
92
95
  return;
93
96
  }
94
97
  asyncClientStore.apply(JSON.parse(latest.state));
95
98
  internal.sequence = latest.sequence;
99
+ awaitingReconnectSync = false;
100
+ reconnectSequenceBaseline = null;
96
101
  })().finally(() => {
97
102
  syncingPromise = null;
98
103
  });
@@ -103,28 +108,55 @@ var createAsyncClientStore = (createStore, asyncStoreClientOption) => {
103
108
  throw new Error("transport.onConnect is required");
104
109
  }
105
110
  transport.onConnect?.(() => {
106
- void fullSync().catch((error) => {
111
+ awaitingReconnectSync = true;
112
+ reconnectSequenceBaseline = internal.sequence;
113
+ void fullSync(true).catch((error) => {
107
114
  if (process.env.NODE_ENV === "development") {
108
115
  console.error(error);
109
116
  }
110
117
  });
111
118
  });
112
119
  transport.listen("update", async (options) => {
120
+ let shouldFullSync = false;
121
+ let allowLowerSequence = false;
113
122
  try {
114
123
  if (typeof options.sequence !== "number") {
115
- await fullSync();
116
- return;
117
- }
118
- if (options.sequence <= internal.sequence) {
119
- return;
120
- }
121
- if (options.sequence === internal.sequence + 1) {
122
- internal.sequence = options.sequence;
124
+ shouldFullSync = true;
125
+ } else if (options.sequence <= internal.sequence) {
126
+ if (awaitingReconnectSync) {
127
+ shouldFullSync = true;
128
+ allowLowerSequence = true;
129
+ } else if (options.sequence === 0 && internal.sequence > 0) {
130
+ awaitingReconnectSync = true;
131
+ reconnectSequenceBaseline = internal.sequence;
132
+ shouldFullSync = true;
133
+ allowLowerSequence = true;
134
+ } else {
135
+ return;
136
+ }
137
+ } else if (options.sequence === internal.sequence + 1) {
123
138
  asyncClientStore.apply(void 0, options.patches);
139
+ internal.sequence = options.sequence;
140
+ awaitingReconnectSync = false;
141
+ reconnectSequenceBaseline = null;
142
+ return;
124
143
  } else {
125
- await fullSync();
144
+ shouldFullSync = true;
145
+ allowLowerSequence = awaitingReconnectSync;
146
+ }
147
+ if (shouldFullSync) {
148
+ await fullSync(allowLowerSequence);
126
149
  }
127
150
  } catch (error) {
151
+ if (!shouldFullSync) {
152
+ try {
153
+ await fullSync(awaitingReconnectSync);
154
+ } catch (syncError) {
155
+ if (process.env.NODE_ENV === "development") {
156
+ console.error(syncError);
157
+ }
158
+ }
159
+ }
128
160
  if (process.env.NODE_ENV === "development") {
129
161
  console.error(error);
130
162
  }
@@ -225,6 +257,15 @@ var isEqual = (x, y) => {
225
257
  }
226
258
  return x !== x && y !== y;
227
259
  };
260
+ var isUnsafeKey = (key) => key === "__proto__" || key === "prototype" || key === "constructor";
261
+ var assignOwnEnumerable = (target, source) => {
262
+ for (const key of Object.keys(source)) {
263
+ if (isUnsafeKey(key)) {
264
+ continue;
265
+ }
266
+ target[key] = source[key];
267
+ }
268
+ };
228
269
  var areShallowEqualWithArray = (prev, next) => {
229
270
  if (prev === null || next === null || prev.length !== next.length) {
230
271
  return false;
@@ -241,18 +282,26 @@ var mergeObject = (target, source, isSlice) => {
241
282
  if (isSlice) {
242
283
  if (typeof source === "object" && source !== null) {
243
284
  for (const key of Object.keys(source)) {
285
+ if (isUnsafeKey(key)) {
286
+ continue;
287
+ }
288
+ if (!Object.prototype.hasOwnProperty.call(target, key)) {
289
+ continue;
290
+ }
244
291
  const sourceValue = source[key];
245
292
  if (typeof sourceValue !== "object" || sourceValue === null) {
246
293
  continue;
247
294
  }
248
295
  const targetValue = target[key];
249
296
  if (typeof targetValue === "object" && targetValue !== null) {
250
- Object.assign(targetValue, sourceValue);
297
+ assignOwnEnumerable(targetValue, sourceValue);
251
298
  }
252
299
  }
253
300
  }
254
301
  } else {
255
- Object.assign(target, source);
302
+ if (typeof source === "object" && source !== null) {
303
+ assignOwnEnumerable(target, source);
304
+ }
256
305
  }
257
306
  };
258
307
  var uuid = () => {
package/dist/index.mjs CHANGED
@@ -53,18 +53,23 @@ var createAsyncClientStore = (createStore, asyncStoreClientOption) => {
53
53
  }
54
54
  asyncClientStore.transport = transport;
55
55
  let syncingPromise = null;
56
- const fullSync = async () => {
56
+ let awaitingReconnectSync = false;
57
+ let reconnectSequenceBaseline = null;
58
+ const fullSync = async (allowLowerSequence = false) => {
57
59
  if (!syncingPromise) {
58
60
  syncingPromise = (async () => {
59
61
  const latest = await transport.emit("fullSync");
60
62
  if (typeof latest.sequence !== "number" || typeof latest.state !== "string") {
61
63
  throw new Error("Invalid fullSync payload");
62
64
  }
63
- if (latest.sequence < internal.sequence) {
65
+ const canApplyLowerSequence = allowLowerSequence && awaitingReconnectSync && reconnectSequenceBaseline !== null && reconnectSequenceBaseline === internal.sequence;
66
+ if (latest.sequence < internal.sequence && !canApplyLowerSequence) {
64
67
  return;
65
68
  }
66
69
  asyncClientStore.apply(JSON.parse(latest.state));
67
70
  internal.sequence = latest.sequence;
71
+ awaitingReconnectSync = false;
72
+ reconnectSequenceBaseline = null;
68
73
  })().finally(() => {
69
74
  syncingPromise = null;
70
75
  });
@@ -75,28 +80,55 @@ var createAsyncClientStore = (createStore, asyncStoreClientOption) => {
75
80
  throw new Error("transport.onConnect is required");
76
81
  }
77
82
  transport.onConnect?.(() => {
78
- void fullSync().catch((error) => {
83
+ awaitingReconnectSync = true;
84
+ reconnectSequenceBaseline = internal.sequence;
85
+ void fullSync(true).catch((error) => {
79
86
  if (process.env.NODE_ENV === "development") {
80
87
  console.error(error);
81
88
  }
82
89
  });
83
90
  });
84
91
  transport.listen("update", async (options) => {
92
+ let shouldFullSync = false;
93
+ let allowLowerSequence = false;
85
94
  try {
86
95
  if (typeof options.sequence !== "number") {
87
- await fullSync();
88
- return;
89
- }
90
- if (options.sequence <= internal.sequence) {
91
- return;
92
- }
93
- if (options.sequence === internal.sequence + 1) {
94
- internal.sequence = options.sequence;
96
+ shouldFullSync = true;
97
+ } else if (options.sequence <= internal.sequence) {
98
+ if (awaitingReconnectSync) {
99
+ shouldFullSync = true;
100
+ allowLowerSequence = true;
101
+ } else if (options.sequence === 0 && internal.sequence > 0) {
102
+ awaitingReconnectSync = true;
103
+ reconnectSequenceBaseline = internal.sequence;
104
+ shouldFullSync = true;
105
+ allowLowerSequence = true;
106
+ } else {
107
+ return;
108
+ }
109
+ } else if (options.sequence === internal.sequence + 1) {
95
110
  asyncClientStore.apply(void 0, options.patches);
111
+ internal.sequence = options.sequence;
112
+ awaitingReconnectSync = false;
113
+ reconnectSequenceBaseline = null;
114
+ return;
96
115
  } else {
97
- await fullSync();
116
+ shouldFullSync = true;
117
+ allowLowerSequence = awaitingReconnectSync;
118
+ }
119
+ if (shouldFullSync) {
120
+ await fullSync(allowLowerSequence);
98
121
  }
99
122
  } catch (error) {
123
+ if (!shouldFullSync) {
124
+ try {
125
+ await fullSync(awaitingReconnectSync);
126
+ } catch (syncError) {
127
+ if (process.env.NODE_ENV === "development") {
128
+ console.error(syncError);
129
+ }
130
+ }
131
+ }
100
132
  if (process.env.NODE_ENV === "development") {
101
133
  console.error(error);
102
134
  }
@@ -200,6 +232,15 @@ var isEqual = (x, y) => {
200
232
  }
201
233
  return x !== x && y !== y;
202
234
  };
235
+ var isUnsafeKey = (key) => key === "__proto__" || key === "prototype" || key === "constructor";
236
+ var assignOwnEnumerable = (target, source) => {
237
+ for (const key of Object.keys(source)) {
238
+ if (isUnsafeKey(key)) {
239
+ continue;
240
+ }
241
+ target[key] = source[key];
242
+ }
243
+ };
203
244
  var areShallowEqualWithArray = (prev, next) => {
204
245
  if (prev === null || next === null || prev.length !== next.length) {
205
246
  return false;
@@ -216,18 +257,26 @@ var mergeObject = (target, source, isSlice) => {
216
257
  if (isSlice) {
217
258
  if (typeof source === "object" && source !== null) {
218
259
  for (const key of Object.keys(source)) {
260
+ if (isUnsafeKey(key)) {
261
+ continue;
262
+ }
263
+ if (!Object.prototype.hasOwnProperty.call(target, key)) {
264
+ continue;
265
+ }
219
266
  const sourceValue = source[key];
220
267
  if (typeof sourceValue !== "object" || sourceValue === null) {
221
268
  continue;
222
269
  }
223
270
  const targetValue = target[key];
224
271
  if (typeof targetValue === "object" && targetValue !== null) {
225
- Object.assign(targetValue, sourceValue);
272
+ assignOwnEnumerable(targetValue, sourceValue);
226
273
  }
227
274
  }
228
275
  }
229
276
  } else {
230
- Object.assign(target, source);
277
+ if (typeof source === "object" && source !== null) {
278
+ assignOwnEnumerable(target, source);
279
+ }
231
280
  }
232
281
  };
233
282
  var uuid = () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "coaction",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "A sleek JavaScript library designed for high-performance and multithreading web apps.",
5
5
  "keywords": [
6
6
  "coaction"