@legendapp/state 3.0.0-alpha.9 → 3.0.0-beta.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/.DS_Store +0 -0
- package/config/configureLegendState.d.mts +13 -0
- package/config/configureLegendState.d.ts +13 -0
- package/config/configureLegendState.js +45 -0
- package/config/configureLegendState.mjs +43 -0
- package/config/enable$GetSet.js +2 -1
- package/config/enable$GetSet.mjs +2 -1
- package/config/enableReactTracking.js +2 -1
- package/config/enableReactTracking.mjs +2 -1
- package/config/enableReactUse.js +2 -1
- package/config/enableReactUse.mjs +2 -1
- package/config/enable_PeekAssign.js +2 -1
- package/config/enable_PeekAssign.mjs +2 -1
- package/helpers/trackHistory.js +2 -2
- package/helpers/trackHistory.mjs +2 -2
- package/index.d.mts +103 -79
- package/index.d.ts +103 -79
- package/index.js +326 -316
- package/index.mjs +323 -314
- package/package.json +36 -1
- package/persist-plugins/async-storage.d.mts +6 -3
- package/persist-plugins/async-storage.d.ts +6 -3
- package/persist-plugins/async-storage.js +8 -4
- package/persist-plugins/async-storage.mjs +8 -5
- package/persist-plugins/indexeddb.d.mts +6 -4
- package/persist-plugins/indexeddb.d.ts +6 -4
- package/persist-plugins/indexeddb.js +35 -15
- package/persist-plugins/indexeddb.mjs +35 -16
- package/persist-plugins/mmkv.d.mts +5 -1
- package/persist-plugins/mmkv.d.ts +5 -1
- package/persist-plugins/mmkv.js +10 -5
- package/persist-plugins/mmkv.mjs +10 -6
- package/react-reactive/enableReactComponents.d.mts +9 -0
- package/react-reactive/enableReactComponents.d.ts +9 -0
- package/react-reactive/enableReactComponents.js +19 -0
- package/react-reactive/enableReactComponents.mjs +17 -0
- package/react-reactive/enableReactNativeComponents.d.mts +22 -0
- package/react-reactive/enableReactNativeComponents.d.ts +22 -0
- package/react-reactive/enableReactNativeComponents.js +53 -0
- package/react-reactive/enableReactNativeComponents.mjs +51 -0
- package/react-reactive/enableReactive.d.mts +5 -0
- package/react-reactive/enableReactive.d.ts +5 -0
- package/react-reactive/enableReactive.js +24 -0
- package/react-reactive/enableReactive.mjs +22 -0
- package/react-reactive/enableReactive.native.d.mts +5 -0
- package/react-reactive/enableReactive.native.d.ts +5 -0
- package/react-reactive/enableReactive.native.js +58 -0
- package/react-reactive/enableReactive.native.mjs +56 -0
- package/react-reactive/enableReactive.web.d.mts +5 -0
- package/react-reactive/enableReactive.web.d.ts +5 -0
- package/react-reactive/enableReactive.web.js +58 -0
- package/react-reactive/enableReactive.web.mjs +56 -0
- package/react.d.mts +39 -34
- package/react.d.ts +39 -34
- package/react.js +39 -17
- package/react.mjs +39 -17
- package/sync-plugins/crud.d.mts +21 -23
- package/sync-plugins/crud.d.ts +21 -23
- package/sync-plugins/crud.js +224 -112
- package/sync-plugins/crud.mjs +226 -114
- package/sync-plugins/fetch.js +12 -8
- package/sync-plugins/fetch.mjs +13 -9
- package/sync-plugins/firebase.d.mts +27 -0
- package/sync-plugins/firebase.d.ts +27 -0
- package/sync-plugins/firebase.js +373 -0
- package/sync-plugins/firebase.mjs +368 -0
- package/sync-plugins/keel.d.mts +43 -26
- package/sync-plugins/keel.d.ts +43 -26
- package/sync-plugins/keel.js +145 -99
- package/sync-plugins/keel.mjs +147 -99
- package/sync-plugins/supabase.d.mts +19 -9
- package/sync-plugins/supabase.d.ts +19 -9
- package/sync-plugins/supabase.js +52 -21
- package/sync-plugins/supabase.mjs +53 -22
- package/sync-plugins/tanstack-query.d.mts +2 -2
- package/sync-plugins/tanstack-query.d.ts +2 -2
- package/sync-plugins/tanstack-query.js +22 -5
- package/sync-plugins/tanstack-query.mjs +22 -5
- package/sync-plugins/tanstack-react-query.d.mts +1 -1
- package/sync-plugins/tanstack-react-query.d.ts +1 -1
- package/sync-plugins/tanstack-react-query.js +8 -1
- package/sync-plugins/tanstack-react-query.mjs +8 -1
- package/sync.d.mts +74 -200
- package/sync.d.ts +74 -200
- package/sync.js +495 -281
- package/sync.mjs +500 -286
package/sync-plugins/keel.js
CHANGED
|
@@ -1,94 +1,105 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var ksuid = require('ksuid');
|
|
4
3
|
var state = require('@legendapp/state');
|
|
5
|
-
var sync = require('@legendapp/state/sync');
|
|
6
4
|
var crud = require('@legendapp/state/sync-plugins/crud');
|
|
5
|
+
var ksuid = require('ksuid');
|
|
7
6
|
|
|
8
7
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
9
8
|
|
|
10
9
|
var ksuid__default = /*#__PURE__*/_interopDefault(ksuid);
|
|
11
10
|
|
|
12
11
|
// src/sync-plugins/keel.ts
|
|
13
|
-
var { clone } = state.internal;
|
|
14
12
|
var KeelKeys = ["createdAt", "updatedAt"];
|
|
15
13
|
function generateKeelId() {
|
|
16
14
|
return ksuid__default.default.randomSync().string;
|
|
17
15
|
}
|
|
18
|
-
var keelConfig = {};
|
|
19
16
|
var modifiedClients = /* @__PURE__ */ new WeakSet();
|
|
20
|
-
var
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
17
|
+
var isAuthed$ = state.observable(false);
|
|
18
|
+
var isAuthing$ = state.observable(false);
|
|
19
|
+
async function ensureAuthToken(props, force) {
|
|
20
|
+
if (!force && isAuthed$.get()) {
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
const { client, refreshAuth } = props;
|
|
24
|
+
let isAuthed = await client.auth.isAuthenticated().then(({ data }) => data);
|
|
24
25
|
if (!isAuthed) {
|
|
25
|
-
|
|
26
|
+
if (!force && isAuthing$.get()) {
|
|
27
|
+
return state.when(
|
|
28
|
+
() => !isAuthing$.get(),
|
|
29
|
+
() => isAuthed$.get()
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
isAuthing$.set(true);
|
|
33
|
+
if (refreshAuth) {
|
|
34
|
+
await refreshAuth();
|
|
35
|
+
}
|
|
36
|
+
isAuthed = await client.auth.isAuthenticated().then(({ data }) => data);
|
|
37
|
+
if (!isAuthed) {
|
|
38
|
+
isAuthed = await client.auth.refresh().then(({ data }) => data);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (isAuthed) {
|
|
42
|
+
state.batch(() => {
|
|
43
|
+
isAuthed$.set(true);
|
|
44
|
+
isAuthing$.set(false);
|
|
45
|
+
});
|
|
46
|
+
} else {
|
|
47
|
+
setTimeout(() => ensureAuthToken(
|
|
48
|
+
props,
|
|
49
|
+
/*force*/
|
|
50
|
+
true
|
|
51
|
+
), 1e3);
|
|
26
52
|
}
|
|
27
53
|
return isAuthed;
|
|
28
54
|
}
|
|
29
|
-
async function handleApiError(error, retry) {
|
|
55
|
+
async function handleApiError(props, error, retry) {
|
|
30
56
|
if (error.type === "unauthorized" || error.type === "forbidden") {
|
|
31
57
|
console.warn("Keel token expired, refreshing...");
|
|
32
|
-
|
|
58
|
+
isAuthed$.set(false);
|
|
59
|
+
await ensureAuthToken(props);
|
|
33
60
|
}
|
|
34
61
|
}
|
|
35
62
|
function convertObjectToCreate(item) {
|
|
36
|
-
const cloned =
|
|
37
|
-
Object.keys(
|
|
63
|
+
const cloned = {};
|
|
64
|
+
Object.keys(item).forEach((key) => {
|
|
38
65
|
if (key.endsWith("Id")) {
|
|
39
|
-
if (
|
|
40
|
-
cloned[key.slice(0, -2)] = { id:
|
|
66
|
+
if (item[key]) {
|
|
67
|
+
cloned[key.slice(0, -2)] = { id: item[key] };
|
|
68
|
+
}
|
|
69
|
+
} else if (key !== "createdAt" && key !== "updatedAt") {
|
|
70
|
+
if (item[key] === void 0) {
|
|
71
|
+
cloned[key] = null;
|
|
72
|
+
} else {
|
|
73
|
+
cloned[key] = item[key];
|
|
41
74
|
}
|
|
42
|
-
delete cloned[key];
|
|
43
75
|
}
|
|
44
76
|
});
|
|
45
|
-
delete cloned.createdAt;
|
|
46
|
-
delete cloned.updatedAt;
|
|
47
77
|
return cloned;
|
|
48
78
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const queries = client.api.queries;
|
|
63
|
-
Object.keys(queries).forEach((key) => {
|
|
64
|
-
if (key.startsWith("list")) {
|
|
65
|
-
const oldFn = queries[key];
|
|
66
|
-
queries[key] = (i) => {
|
|
67
|
-
const realtimeKey = [key, ...Object.values(i.where || {})].filter((value) => value && typeof value !== "object").join("/");
|
|
68
|
-
const subscribe = ({ refresh }) => {
|
|
69
|
-
if (realtimeKey) {
|
|
70
|
-
return realtimePlugin.subscribe(realtimeKey, refresh);
|
|
71
|
-
}
|
|
72
|
-
};
|
|
73
|
-
return oldFn(i).then((ret) => {
|
|
74
|
-
if (subscribe) {
|
|
75
|
-
ret.subscribe = subscribe;
|
|
76
|
-
ret.subscribeKey = realtimeKey;
|
|
77
|
-
}
|
|
78
|
-
return ret;
|
|
79
|
-
});
|
|
79
|
+
var realtimeState = { current: {} };
|
|
80
|
+
function setupRealtime(props) {
|
|
81
|
+
const { client } = props;
|
|
82
|
+
if (client && !modifiedClients.has(client)) {
|
|
83
|
+
modifiedClients.add(client);
|
|
84
|
+
const queries = client.api.queries;
|
|
85
|
+
Object.keys(queries).forEach((key) => {
|
|
86
|
+
if (key.startsWith("list")) {
|
|
87
|
+
const origFn = queries[key];
|
|
88
|
+
queries[key] = (i) => {
|
|
89
|
+
realtimeState.current = {
|
|
90
|
+
lastAction: key,
|
|
91
|
+
lastParams: i
|
|
80
92
|
};
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
93
|
+
return origFn(i);
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
});
|
|
84
97
|
}
|
|
85
98
|
}
|
|
86
99
|
var NumPerPage = 200;
|
|
87
|
-
async function getAllPages(listFn, params) {
|
|
100
|
+
async function getAllPages(props, listFn, params) {
|
|
88
101
|
const allData = [];
|
|
89
102
|
let pageInfo = void 0;
|
|
90
|
-
let subscribe_;
|
|
91
|
-
let subscribeKey_;
|
|
92
103
|
const { first: firstParam } = params;
|
|
93
104
|
do {
|
|
94
105
|
const first = firstParam ? Math.min(firstParam - allData.length, NumPerPage) : NumPerPage;
|
|
@@ -100,13 +111,9 @@ async function getAllPages(listFn, params) {
|
|
|
100
111
|
pageInfo = void 0;
|
|
101
112
|
const ret = await listFn(paramsWithCursor);
|
|
102
113
|
if (ret) {
|
|
103
|
-
const { data, error
|
|
104
|
-
if (subscribe) {
|
|
105
|
-
subscribe_ = subscribe;
|
|
106
|
-
subscribeKey_ = subscribeKey;
|
|
107
|
-
}
|
|
114
|
+
const { data, error } = ret;
|
|
108
115
|
if (error) {
|
|
109
|
-
await handleApiError(error);
|
|
116
|
+
await handleApiError(props, error);
|
|
110
117
|
throw new Error(error.message);
|
|
111
118
|
} else if (data) {
|
|
112
119
|
pageInfo = data.pageInfo;
|
|
@@ -114,21 +121,25 @@ async function getAllPages(listFn, params) {
|
|
|
114
121
|
}
|
|
115
122
|
}
|
|
116
123
|
} while (pageInfo == null ? void 0 : pageInfo.hasNextPage);
|
|
117
|
-
return
|
|
124
|
+
return allData;
|
|
118
125
|
}
|
|
119
126
|
function syncedKeel(props) {
|
|
120
|
-
const { realtimePlugin } = keelConfig;
|
|
121
|
-
props = { ...keelConfig, ...props };
|
|
122
127
|
const {
|
|
123
128
|
get: getParam,
|
|
124
129
|
list: listParam,
|
|
125
130
|
create: createParam,
|
|
126
131
|
update: updateParam,
|
|
127
132
|
delete: deleteParam,
|
|
133
|
+
subscribe: subscribeParam,
|
|
128
134
|
first,
|
|
129
135
|
where: whereParam,
|
|
130
136
|
waitFor,
|
|
137
|
+
waitForSet,
|
|
131
138
|
fieldDeleted,
|
|
139
|
+
realtime,
|
|
140
|
+
mode,
|
|
141
|
+
onError,
|
|
142
|
+
requireAuth = true,
|
|
132
143
|
...rest
|
|
133
144
|
} = props;
|
|
134
145
|
const { changesSince } = props;
|
|
@@ -137,28 +148,38 @@ function syncedKeel(props) {
|
|
|
137
148
|
const subscribeFnKey$ = state.observable("");
|
|
138
149
|
const fieldCreatedAt = "createdAt";
|
|
139
150
|
const fieldUpdatedAt = "updatedAt";
|
|
151
|
+
const setupSubscribe = realtime ? async (getParams) => {
|
|
152
|
+
const { lastAction, lastParams } = realtimeState.current;
|
|
153
|
+
const { path, plugin } = realtime;
|
|
154
|
+
if (lastAction && path && plugin) {
|
|
155
|
+
const key = await path(lastAction, lastParams);
|
|
156
|
+
subscribeFn = () => realtime.plugin.subscribe(key, getParams);
|
|
157
|
+
subscribeFnKey$.set(key);
|
|
158
|
+
}
|
|
159
|
+
} : void 0;
|
|
140
160
|
const list = listParam ? async (listParams) => {
|
|
141
|
-
const { lastSync
|
|
161
|
+
const { lastSync } = listParams;
|
|
142
162
|
const queryBySync = !!lastSync && changesSince === "last-sync";
|
|
143
163
|
const where = Object.assign(
|
|
144
164
|
queryBySync ? { updatedAt: { after: new Date(lastSync + 1) } } : {},
|
|
145
165
|
state.isFunction(whereParam) ? whereParam() : whereParam
|
|
146
166
|
);
|
|
147
167
|
const params = { where, first };
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
168
|
+
realtimeState.current = {};
|
|
169
|
+
const promise = getAllPages(props, listParam, params);
|
|
170
|
+
if (realtime) {
|
|
171
|
+
setupSubscribe(listParams);
|
|
152
172
|
}
|
|
153
|
-
return
|
|
173
|
+
return promise;
|
|
154
174
|
} : void 0;
|
|
155
175
|
const get = getParam ? async (getParams) => {
|
|
156
176
|
const { refresh } = getParams;
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
177
|
+
realtimeState.current = {};
|
|
178
|
+
const promise = getParam({ refresh });
|
|
179
|
+
if (realtime) {
|
|
180
|
+
setupSubscribe(getParams);
|
|
161
181
|
}
|
|
182
|
+
const { data, error } = await promise;
|
|
162
183
|
if (error) {
|
|
163
184
|
throw new Error(error.message);
|
|
164
185
|
} else {
|
|
@@ -167,41 +188,54 @@ function syncedKeel(props) {
|
|
|
167
188
|
} : void 0;
|
|
168
189
|
const onSaved = ({ saved }) => {
|
|
169
190
|
if (saved) {
|
|
170
|
-
|
|
171
|
-
if (updatedAt && realtimePlugin) {
|
|
191
|
+
if (realtime == null ? void 0 : realtime.plugin) {
|
|
172
192
|
const subscribeFnKey = subscribeFnKey$.get();
|
|
173
193
|
if (subscribeFnKey) {
|
|
174
|
-
|
|
194
|
+
realtime == null ? void 0 : realtime.plugin.setSaved(subscribeFnKey);
|
|
175
195
|
}
|
|
176
196
|
}
|
|
177
197
|
}
|
|
178
198
|
};
|
|
179
|
-
const handleSetError = async (error, params,
|
|
180
|
-
var _a, _b
|
|
181
|
-
const { retryNum,
|
|
182
|
-
if (
|
|
199
|
+
const handleSetError = async (error, params, input, fn, from) => {
|
|
200
|
+
var _a, _b;
|
|
201
|
+
const { retryNum, update: update2 } = params;
|
|
202
|
+
if (from === "create" && ((_a = error.message) == null ? void 0 : _a.includes("for the unique")) && ((_b = error.message) == null ? void 0 : _b.includes("must be unique"))) {
|
|
183
203
|
if (__DEV__) {
|
|
184
204
|
console.log("Creating duplicate data already saved, just ignore.");
|
|
185
205
|
}
|
|
206
|
+
params.cancelRetry = true;
|
|
186
207
|
update2({
|
|
187
208
|
value: {},
|
|
188
209
|
mode: "assign"
|
|
189
210
|
});
|
|
211
|
+
} else if (from === "delete") {
|
|
212
|
+
if (error.message === "record not found") {
|
|
213
|
+
if (__DEV__) {
|
|
214
|
+
console.log("Deleting non-existing data, just ignore.");
|
|
215
|
+
}
|
|
216
|
+
params.cancelRetry = true;
|
|
217
|
+
}
|
|
190
218
|
} else if (error.type === "bad_request") {
|
|
191
|
-
|
|
219
|
+
onError == null ? void 0 : onError(new Error(error.message), params, {
|
|
220
|
+
error,
|
|
221
|
+
params,
|
|
222
|
+
input,
|
|
223
|
+
type: from,
|
|
224
|
+
action: fn.name || fn.toString()
|
|
225
|
+
});
|
|
192
226
|
if (retryNum > 4) {
|
|
193
|
-
cancelRetry
|
|
227
|
+
params.cancelRetry = true;
|
|
194
228
|
}
|
|
195
|
-
throw new Error(error.message);
|
|
229
|
+
throw new Error(error.message, { cause: { input } });
|
|
196
230
|
} else {
|
|
197
|
-
await handleApiError(error);
|
|
198
|
-
throw new Error(error.message);
|
|
231
|
+
await handleApiError(props, error);
|
|
232
|
+
throw new Error(error.message, { cause: { input } });
|
|
199
233
|
}
|
|
200
234
|
};
|
|
201
235
|
const create = createParam ? async (input, params) => {
|
|
202
236
|
const { data, error } = await createParam(convertObjectToCreate(input));
|
|
203
237
|
if (error) {
|
|
204
|
-
handleSetError(error, params,
|
|
238
|
+
await handleSetError(error, params, input, createParam, "create");
|
|
205
239
|
}
|
|
206
240
|
return data;
|
|
207
241
|
} : void 0;
|
|
@@ -209,44 +243,58 @@ function syncedKeel(props) {
|
|
|
209
243
|
const id = input.id;
|
|
210
244
|
const values = convertObjectToCreate(input);
|
|
211
245
|
delete values.id;
|
|
212
|
-
delete values.createdAt;
|
|
213
|
-
delete values.updatedAt;
|
|
214
246
|
if (!state.isEmpty(values)) {
|
|
215
247
|
const { data, error } = await updateParam({ where: { id }, values });
|
|
216
248
|
if (error) {
|
|
217
|
-
handleSetError(error, params,
|
|
249
|
+
await handleSetError(error, params, input, updateParam, "update");
|
|
218
250
|
}
|
|
219
251
|
return data;
|
|
220
252
|
}
|
|
221
253
|
} : void 0;
|
|
222
|
-
const deleteFn = deleteParam ? async (
|
|
223
|
-
const { data, error } = await deleteParam({ id });
|
|
254
|
+
const deleteFn = deleteParam ? async (value, params) => {
|
|
255
|
+
const { data, error } = await deleteParam({ id: value.id });
|
|
224
256
|
if (error) {
|
|
225
|
-
handleSetError(error, params,
|
|
257
|
+
await handleSetError(error, params, value, deleteParam, "delete");
|
|
226
258
|
}
|
|
227
259
|
return data;
|
|
228
260
|
} : void 0;
|
|
229
|
-
|
|
261
|
+
if (realtime) {
|
|
262
|
+
setupRealtime(props);
|
|
263
|
+
}
|
|
264
|
+
const subscribe = realtime ? (params) => {
|
|
230
265
|
let unsubscribe = void 0;
|
|
231
266
|
state.when(subscribeFnKey$, () => {
|
|
232
267
|
unsubscribe = subscribeFn(params);
|
|
233
268
|
});
|
|
269
|
+
const unsubscribeParam = subscribeParam == null ? void 0 : subscribeParam(params);
|
|
234
270
|
return () => {
|
|
235
271
|
unsubscribe == null ? void 0 : unsubscribe();
|
|
272
|
+
unsubscribeParam == null ? void 0 : unsubscribeParam();
|
|
236
273
|
};
|
|
237
|
-
};
|
|
274
|
+
} : subscribeParam;
|
|
238
275
|
return crud.syncedCrud({
|
|
239
276
|
...rest,
|
|
240
277
|
as: asType,
|
|
278
|
+
mode: mode || "merge",
|
|
241
279
|
list,
|
|
242
280
|
create,
|
|
243
281
|
update,
|
|
244
282
|
delete: deleteFn,
|
|
245
|
-
waitFor: () =>
|
|
283
|
+
waitFor: () => {
|
|
284
|
+
ensureAuthToken(props);
|
|
285
|
+
return [requireAuth ? isAuthed$ : true, waitFor || true];
|
|
286
|
+
},
|
|
287
|
+
waitForSet: (params) => {
|
|
288
|
+
ensureAuthToken(props);
|
|
289
|
+
return [
|
|
290
|
+
requireAuth ? isAuthed$ : true,
|
|
291
|
+
() => waitForSet ? state.isFunction(waitForSet) ? waitForSet(params) : waitForSet : true
|
|
292
|
+
];
|
|
293
|
+
},
|
|
246
294
|
onSaved,
|
|
247
295
|
fieldCreatedAt,
|
|
248
296
|
fieldUpdatedAt,
|
|
249
|
-
fieldDeleted
|
|
297
|
+
fieldDeleted,
|
|
250
298
|
changesSince,
|
|
251
299
|
updatePartial: true,
|
|
252
300
|
subscribe,
|
|
@@ -257,7 +305,5 @@ function syncedKeel(props) {
|
|
|
257
305
|
}
|
|
258
306
|
|
|
259
307
|
exports.KeelKeys = KeelKeys;
|
|
260
|
-
exports.configureSyncedKeel = configureSyncedKeel;
|
|
261
308
|
exports.generateKeelId = generateKeelId;
|
|
262
|
-
exports.getSyncedKeelConfiguration = getSyncedKeelConfiguration;
|
|
263
309
|
exports.syncedKeel = syncedKeel;
|