@absolutejs/sync-expo 0.0.1 → 0.0.2
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/README.md +4 -0
- package/dist/bridge.js +352 -0
- package/dist/bridge.js.map +10 -0
- package/dist/index.js +1164 -1163
- package/dist/index.js.map +5 -5
- package/package.json +8 -3
package/README.md
CHANGED
|
@@ -14,6 +14,10 @@ AbsoluteJS provisions this package automatically for Expo applications that use
|
|
|
14
14
|
`@absolutejs/sync`. Direct package consumers can use the exported adapter
|
|
15
15
|
functions without AbsoluteJS.
|
|
16
16
|
|
|
17
|
+
The `@absolutejs/sync-expo/client` and `@absolutejs/sync-expo/bridge` subpaths
|
|
18
|
+
remain free of Expo and React Native runtime imports for WebViews and bridge
|
|
19
|
+
contract tests.
|
|
20
|
+
|
|
17
21
|
Background execution is an acceleration only. Foreground startup, resume, and
|
|
18
22
|
connectivity recovery remain authoritative because Android and iOS decide when
|
|
19
23
|
deferrable work is allowed to run.
|
package/dist/bridge.js
ADDED
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
// src/bridge.ts
|
|
2
|
+
var requireRecord = (value, label) => {
|
|
3
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
4
|
+
throw new TypeError(`Expo Sync bridge ${label} is invalid.`);
|
|
5
|
+
return value;
|
|
6
|
+
};
|
|
7
|
+
var requireString = (value, label) => {
|
|
8
|
+
if (typeof value !== "string" || value.length === 0 || value.length > 512)
|
|
9
|
+
throw new TypeError(`Expo Sync bridge ${label} is invalid.`);
|
|
10
|
+
return value;
|
|
11
|
+
};
|
|
12
|
+
var requireCollectionRecord = (value) => {
|
|
13
|
+
const record = requireRecord(value, "collection record");
|
|
14
|
+
if (!Array.isArray(record.rows) || typeof record.version !== "number" || !Number.isSafeInteger(record.version) || record.version < 0)
|
|
15
|
+
throw new TypeError("Expo Sync bridge collection record is invalid.");
|
|
16
|
+
return structuredClone(record);
|
|
17
|
+
};
|
|
18
|
+
var requireMutationRecord = (value) => {
|
|
19
|
+
const record = requireRecord(value, "mutation record");
|
|
20
|
+
if (typeof record.operationId !== "string" || record.operationId.length === 0 || record.operationId.length > 512 || typeof record.name !== "string" || record.name.length === 0 || record.name.length > 512 || typeof record.createdAt !== "number" || !Number.isFinite(record.createdAt) || typeof record.attempts !== "number" || !Number.isSafeInteger(record.attempts) || record.attempts < 0 || !Array.isArray(record.optimistic) || !Array.isArray(record.inverse))
|
|
21
|
+
throw new TypeError("Expo Sync bridge mutation record is invalid.");
|
|
22
|
+
return structuredClone(record);
|
|
23
|
+
};
|
|
24
|
+
var rollbackMarker = Symbol("expo-sync-bridge-rollback");
|
|
25
|
+
var createExpoSyncBridgeHost = ({
|
|
26
|
+
store,
|
|
27
|
+
namespace,
|
|
28
|
+
transactionTimeoutMs = 8000,
|
|
29
|
+
createId = () => crypto.randomUUID()
|
|
30
|
+
}) => {
|
|
31
|
+
if (!namespace || namespace.length > 512)
|
|
32
|
+
throw new TypeError("Expo Sync bridge namespace is invalid.");
|
|
33
|
+
if (!Number.isSafeInteger(transactionTimeoutMs) || transactionTimeoutMs < 100 || transactionTimeoutMs > 30000)
|
|
34
|
+
throw new TypeError("Expo Sync bridge transactionTimeoutMs must be between 100 and 30000.");
|
|
35
|
+
const sessions = new Map;
|
|
36
|
+
const begin = async (mode) => {
|
|
37
|
+
if (sessions.size >= 8)
|
|
38
|
+
throw new Error("Expo Sync bridge has too many open transactions.");
|
|
39
|
+
const id = createId();
|
|
40
|
+
if (!/^[A-Za-z0-9._:-]{1,160}$/u.test(id) || sessions.has(id))
|
|
41
|
+
throw new Error("Expo Sync bridge generated an invalid transaction id.");
|
|
42
|
+
let readyResolve = () => {
|
|
43
|
+
return;
|
|
44
|
+
};
|
|
45
|
+
let readyReject = () => {
|
|
46
|
+
return;
|
|
47
|
+
};
|
|
48
|
+
const ready = new Promise((resolve, reject) => {
|
|
49
|
+
readyResolve = resolve;
|
|
50
|
+
readyReject = reject;
|
|
51
|
+
});
|
|
52
|
+
let finish = () => {
|
|
53
|
+
return;
|
|
54
|
+
};
|
|
55
|
+
const decision = new Promise((resolve) => {
|
|
56
|
+
finish = resolve;
|
|
57
|
+
});
|
|
58
|
+
const complete = store.transaction(namespace, mode, async (transaction2) => {
|
|
59
|
+
readyResolve(transaction2);
|
|
60
|
+
if (!await decision)
|
|
61
|
+
throw rollbackMarker;
|
|
62
|
+
}).catch((error) => {
|
|
63
|
+
readyReject(error);
|
|
64
|
+
if (error !== rollbackMarker)
|
|
65
|
+
throw error;
|
|
66
|
+
});
|
|
67
|
+
const transaction = await ready;
|
|
68
|
+
const timer = setTimeout(() => {
|
|
69
|
+
sessions.delete(id);
|
|
70
|
+
finish(false);
|
|
71
|
+
}, transactionTimeoutMs);
|
|
72
|
+
sessions.set(id, { complete, finish, timer, transaction });
|
|
73
|
+
return id;
|
|
74
|
+
};
|
|
75
|
+
const session = (params) => {
|
|
76
|
+
const id = requireString(params.transactionId, "transaction id");
|
|
77
|
+
const value = sessions.get(id);
|
|
78
|
+
if (!value)
|
|
79
|
+
throw new Error("Expo Sync bridge transaction is closed or unknown.");
|
|
80
|
+
return { id, value };
|
|
81
|
+
};
|
|
82
|
+
const end = async (params) => {
|
|
83
|
+
const { id, value } = session(params);
|
|
84
|
+
if (typeof params.commit !== "boolean")
|
|
85
|
+
throw new TypeError("Expo Sync bridge commit decision is invalid.");
|
|
86
|
+
sessions.delete(id);
|
|
87
|
+
clearTimeout(value.timer);
|
|
88
|
+
value.finish(params.commit);
|
|
89
|
+
await value.complete;
|
|
90
|
+
return null;
|
|
91
|
+
};
|
|
92
|
+
const operation = async (method, params) => {
|
|
93
|
+
const { value } = session(params);
|
|
94
|
+
const transaction = value.transaction;
|
|
95
|
+
if (method === "sync.tx.getInstallationId")
|
|
96
|
+
return await transaction.getInstallationId() ?? null;
|
|
97
|
+
if (method === "sync.tx.setInstallationId") {
|
|
98
|
+
await transaction.setInstallationId(requireString(params.installationId, "installation id"));
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
if (method === "sync.tx.getCollection")
|
|
102
|
+
return await transaction.getCollection(requireString(params.key, "collection key")) ?? null;
|
|
103
|
+
if (method === "sync.tx.listCollections")
|
|
104
|
+
return transaction.listCollections();
|
|
105
|
+
if (method === "sync.tx.putCollection") {
|
|
106
|
+
await transaction.putCollection(requireString(params.key, "collection key"), requireCollectionRecord(params.record));
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
if (method === "sync.tx.deleteCollection") {
|
|
110
|
+
await transaction.deleteCollection(requireString(params.key, "collection key"));
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
if (method === "sync.tx.listMutations")
|
|
114
|
+
return transaction.listMutations();
|
|
115
|
+
if (method === "sync.tx.getMutation")
|
|
116
|
+
return await transaction.getMutation(requireString(params.operationId, "operation id")) ?? null;
|
|
117
|
+
if (method === "sync.tx.putMutation") {
|
|
118
|
+
await transaction.putMutation(requireMutationRecord(params.record));
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
if (method === "sync.tx.deleteMutation") {
|
|
122
|
+
await transaction.deleteMutation(requireString(params.operationId, "operation id"));
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
if (method === "sync.tx.resolveMutationPolicy")
|
|
126
|
+
return transaction.resolveMutationPolicy?.(requireString(params.name, "mutation name")) ?? null;
|
|
127
|
+
throw new Error("Expo Sync bridge transaction method is not allowed.");
|
|
128
|
+
};
|
|
129
|
+
return {
|
|
130
|
+
close: async () => {
|
|
131
|
+
const active = [...sessions.values()];
|
|
132
|
+
sessions.clear();
|
|
133
|
+
for (const value of active) {
|
|
134
|
+
clearTimeout(value.timer);
|
|
135
|
+
value.finish(false);
|
|
136
|
+
}
|
|
137
|
+
await Promise.allSettled(active.map((value) => value.complete));
|
|
138
|
+
},
|
|
139
|
+
request: async (method, rawParams) => {
|
|
140
|
+
const params = requireRecord(rawParams, "params");
|
|
141
|
+
if (method === "sync.store.begin") {
|
|
142
|
+
if (params.mode !== "readonly" && params.mode !== "readwrite")
|
|
143
|
+
throw new TypeError("Expo Sync bridge transaction mode is invalid.");
|
|
144
|
+
return { transactionId: await begin(params.mode) };
|
|
145
|
+
}
|
|
146
|
+
if (method === "sync.store.end")
|
|
147
|
+
return end(params);
|
|
148
|
+
if (method === "sync.store.schema")
|
|
149
|
+
return await store.getSchemaStatus?.() ?? null;
|
|
150
|
+
if (method === "sync.store.deleteNamespace") {
|
|
151
|
+
await store.deleteNamespace?.(namespace);
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
if (method.startsWith("sync.tx."))
|
|
155
|
+
return operation(method, params);
|
|
156
|
+
throw new Error("Expo Sync bridge method is not allowed.");
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
};
|
|
160
|
+
var websocketOrigin = (url) => {
|
|
161
|
+
const protocol = url.protocol === "wss:" ? "https:" : "http:";
|
|
162
|
+
return `${protocol}//${url.host}`;
|
|
163
|
+
};
|
|
164
|
+
var ticketSocketUrl = (url) => {
|
|
165
|
+
if (url.searchParams.has("__absolute_auth"))
|
|
166
|
+
throw new TypeError("Expo Sync socket URL contains reserved authentication.");
|
|
167
|
+
url.searchParams.set("__absolute_auth", "ticket");
|
|
168
|
+
return url.href;
|
|
169
|
+
};
|
|
170
|
+
var SOCKET_CHUNK_BYTES = 24 * 1024;
|
|
171
|
+
var SOCKET_UPLOAD_TIMEOUT_MS = 1e4;
|
|
172
|
+
var encodeBase64 = (value) => {
|
|
173
|
+
let binary = "";
|
|
174
|
+
for (const byte of value)
|
|
175
|
+
binary += String.fromCharCode(byte);
|
|
176
|
+
return btoa(binary);
|
|
177
|
+
};
|
|
178
|
+
var decodeBase64 = (value) => {
|
|
179
|
+
if (value.length === 0 || value.length > Math.ceil(SOCKET_CHUNK_BYTES / 3) * 4 + 4 || !/^[A-Za-z0-9+/]+={0,2}$/u.test(value))
|
|
180
|
+
throw new TypeError("Expo Sync socket chunk is invalid.");
|
|
181
|
+
return Uint8Array.from(atob(value), (character) => character.charCodeAt(0));
|
|
182
|
+
};
|
|
183
|
+
var createExpoSyncSocketBridgeHost = ({
|
|
184
|
+
allowedOrigin,
|
|
185
|
+
socketTicket,
|
|
186
|
+
emit,
|
|
187
|
+
webSocketImpl = globalThis.WebSocket,
|
|
188
|
+
maxSockets = 4,
|
|
189
|
+
maxFrameBytes = 4 * 1024 * 1024
|
|
190
|
+
}) => {
|
|
191
|
+
const origin = new URL(allowedOrigin);
|
|
192
|
+
if (origin.protocol !== "https:" || origin.username || origin.password || origin.pathname !== "/" || origin.search || origin.hash)
|
|
193
|
+
throw new TypeError("Expo Sync socket allowedOrigin must be an HTTPS origin.");
|
|
194
|
+
if (!webSocketImpl)
|
|
195
|
+
throw new Error("Expo Sync socket bridge requires WebSocket support.");
|
|
196
|
+
if (!Number.isSafeInteger(maxSockets) || maxSockets < 1 || maxSockets > 16)
|
|
197
|
+
throw new TypeError("Expo Sync maxSockets must be between 1 and 16.");
|
|
198
|
+
if (!Number.isSafeInteger(maxFrameBytes) || maxFrameBytes < SOCKET_CHUNK_BYTES || maxFrameBytes > 16 * 1024 * 1024)
|
|
199
|
+
throw new TypeError("Expo Sync maxFrameBytes must be between 24 KiB and 16 MiB.");
|
|
200
|
+
const sockets = new Map;
|
|
201
|
+
const uploads = new Map;
|
|
202
|
+
let messageSequence = 0;
|
|
203
|
+
const socketId = (value) => {
|
|
204
|
+
const id = requireString(value, "socket id");
|
|
205
|
+
if (!/^[A-Za-z0-9._:-]{1,160}$/u.test(id))
|
|
206
|
+
throw new TypeError("Expo Sync bridge socket id is invalid.");
|
|
207
|
+
return id;
|
|
208
|
+
};
|
|
209
|
+
const close = (id, code, reason) => {
|
|
210
|
+
const socket = sockets.get(id);
|
|
211
|
+
if (!socket)
|
|
212
|
+
return;
|
|
213
|
+
sockets.delete(id);
|
|
214
|
+
for (const [key, upload] of uploads)
|
|
215
|
+
if (key.startsWith(`${id}:\x00`)) {
|
|
216
|
+
clearTimeout(upload.timer);
|
|
217
|
+
uploads.delete(key);
|
|
218
|
+
}
|
|
219
|
+
socket.close(code, reason);
|
|
220
|
+
};
|
|
221
|
+
const emitMessage = (id, data) => {
|
|
222
|
+
const bytes = new TextEncoder().encode(data);
|
|
223
|
+
if (bytes.byteLength > maxFrameBytes) {
|
|
224
|
+
emit({ socketId: id, type: "error" });
|
|
225
|
+
close(id, 1009, "Sync frame is too large");
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
const total = Math.max(1, Math.ceil(bytes.byteLength / SOCKET_CHUNK_BYTES));
|
|
229
|
+
const messageId = `native_${(messageSequence += 1).toString(36)}`;
|
|
230
|
+
for (let index = 0;index < total; index += 1)
|
|
231
|
+
emit({
|
|
232
|
+
data: encodeBase64(bytes.slice(index * SOCKET_CHUNK_BYTES, Math.min(bytes.byteLength, (index + 1) * SOCKET_CHUNK_BYTES))),
|
|
233
|
+
index,
|
|
234
|
+
messageId,
|
|
235
|
+
socketId: id,
|
|
236
|
+
total,
|
|
237
|
+
type: "message-chunk"
|
|
238
|
+
});
|
|
239
|
+
};
|
|
240
|
+
return {
|
|
241
|
+
close: () => {
|
|
242
|
+
for (const id of [...sockets.keys()])
|
|
243
|
+
close(id, 1000, "Host closed");
|
|
244
|
+
},
|
|
245
|
+
request: async (method, rawParams) => {
|
|
246
|
+
const params = requireRecord(rawParams, "socket params");
|
|
247
|
+
const id = socketId(params.socketId);
|
|
248
|
+
if (method === "sync.socket.open") {
|
|
249
|
+
if (sockets.has(id))
|
|
250
|
+
throw new Error("Expo Sync bridge socket id is already open.");
|
|
251
|
+
if (sockets.size >= maxSockets)
|
|
252
|
+
throw new Error("Expo Sync bridge socket limit exceeded.");
|
|
253
|
+
const url = new URL(requireString(params.url, "socket URL"));
|
|
254
|
+
if (url.protocol !== "wss:" || url.username || url.password || websocketOrigin(url) !== origin.origin)
|
|
255
|
+
throw new Error("Expo Sync socket must use WSS on the configured production origin.");
|
|
256
|
+
const socket = new webSocketImpl(ticketSocketUrl(url));
|
|
257
|
+
sockets.set(id, socket);
|
|
258
|
+
socket.onopen = () => {
|
|
259
|
+
socketTicket(origin.origin).then((ticket) => {
|
|
260
|
+
if (sockets.get(id) !== socket)
|
|
261
|
+
return;
|
|
262
|
+
socket.send(JSON.stringify({ ticket, type: "authenticate" }));
|
|
263
|
+
emit({ socketId: id, type: "open" });
|
|
264
|
+
}).catch(() => {
|
|
265
|
+
if (sockets.get(id) !== socket)
|
|
266
|
+
return;
|
|
267
|
+
emit({ socketId: id, type: "error" });
|
|
268
|
+
close(id, 1008, "Authentication failed");
|
|
269
|
+
});
|
|
270
|
+
};
|
|
271
|
+
socket.onmessage = (event) => {
|
|
272
|
+
if (sockets.get(id) !== socket)
|
|
273
|
+
return;
|
|
274
|
+
if (typeof event.data !== "string") {
|
|
275
|
+
emit({ socketId: id, type: "error" });
|
|
276
|
+
close(id, 1003, "Binary frames are not supported");
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
emitMessage(id, event.data);
|
|
280
|
+
};
|
|
281
|
+
socket.onerror = () => {
|
|
282
|
+
if (sockets.get(id) === socket)
|
|
283
|
+
emit({ socketId: id, type: "error" });
|
|
284
|
+
};
|
|
285
|
+
socket.onclose = (event) => {
|
|
286
|
+
if (sockets.get(id) === socket)
|
|
287
|
+
sockets.delete(id);
|
|
288
|
+
emit({
|
|
289
|
+
code: event.code,
|
|
290
|
+
reason: event.reason,
|
|
291
|
+
socketId: id,
|
|
292
|
+
type: "close"
|
|
293
|
+
});
|
|
294
|
+
};
|
|
295
|
+
return null;
|
|
296
|
+
}
|
|
297
|
+
if (method === "sync.socket.sendChunk") {
|
|
298
|
+
const socket = sockets.get(id);
|
|
299
|
+
if (!socket || socket.readyState !== webSocketImpl.OPEN)
|
|
300
|
+
throw new Error("Expo Sync bridge socket is not open.");
|
|
301
|
+
const messageId = requireString(params.messageId, "message id");
|
|
302
|
+
const index = params.index;
|
|
303
|
+
const total = params.total;
|
|
304
|
+
if (typeof index !== "number" || !Number.isSafeInteger(index) || typeof total !== "number" || !Number.isSafeInteger(total) || index < 0 || total < 1 || index >= total || total > Math.ceil(maxFrameBytes / SOCKET_CHUNK_BYTES))
|
|
305
|
+
throw new TypeError("Expo Sync socket chunk position is invalid.");
|
|
306
|
+
if (typeof params.data !== "string")
|
|
307
|
+
throw new TypeError("Expo Sync socket chunk data is invalid.");
|
|
308
|
+
const key = `${id}:\x00${messageId}`;
|
|
309
|
+
let upload = uploads.get(key);
|
|
310
|
+
if (!upload) {
|
|
311
|
+
const timer = setTimeout(() => uploads.delete(key), SOCKET_UPLOAD_TIMEOUT_MS);
|
|
312
|
+
upload = { chunks: Array.from({ length: total }), timer };
|
|
313
|
+
uploads.set(key, upload);
|
|
314
|
+
}
|
|
315
|
+
if (upload.chunks.length !== total || upload.chunks[index])
|
|
316
|
+
throw new Error("Expo Sync socket chunk sequence is invalid.");
|
|
317
|
+
upload.chunks[index] = decodeBase64(params.data);
|
|
318
|
+
if (upload.chunks.every((chunk) => chunk !== undefined)) {
|
|
319
|
+
clearTimeout(upload.timer);
|
|
320
|
+
uploads.delete(key);
|
|
321
|
+
const size = upload.chunks.reduce((sum, chunk) => sum + (chunk?.byteLength ?? 0), 0);
|
|
322
|
+
if (size > maxFrameBytes)
|
|
323
|
+
throw new Error("Expo Sync socket frame exceeds its byte limit.");
|
|
324
|
+
const bytes = new Uint8Array(size);
|
|
325
|
+
let offset = 0;
|
|
326
|
+
for (const chunk of upload.chunks) {
|
|
327
|
+
bytes.set(chunk, offset);
|
|
328
|
+
offset += chunk.byteLength;
|
|
329
|
+
}
|
|
330
|
+
socket.send(new TextDecoder().decode(bytes));
|
|
331
|
+
}
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
334
|
+
if (method === "sync.socket.close") {
|
|
335
|
+
const code = params.code === undefined ? undefined : typeof params.code === "number" && Number.isSafeInteger(params.code) && params.code >= 1000 && params.code <= 4999 ? params.code : null;
|
|
336
|
+
if (code === null)
|
|
337
|
+
throw new TypeError("Expo Sync bridge close code is invalid.");
|
|
338
|
+
const reason = params.reason === undefined ? undefined : requireString(params.reason, "close reason");
|
|
339
|
+
close(id, code, reason);
|
|
340
|
+
return null;
|
|
341
|
+
}
|
|
342
|
+
throw new Error("Expo Sync socket bridge method is not allowed.");
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
};
|
|
346
|
+
export {
|
|
347
|
+
createExpoSyncBridgeHost,
|
|
348
|
+
createExpoSyncSocketBridgeHost
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
//# debugId=7A0999003788222864756E2164756E21
|
|
352
|
+
//# sourceMappingURL=bridge.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/bridge.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import type {\n LocalCollectionRecord,\n LocalMutationRecord,\n SyncLocalStore,\n SyncLocalStoreMode,\n SyncLocalTransaction,\n} from \"@absolutejs/sync/client\";\n\nexport type ExpoSyncBridgeHostOptions = {\n store: SyncLocalStore;\n namespace: string;\n /** Maximum time one WebView may hold an atomic transaction. Defaults to 8s. */\n transactionTimeoutMs?: number;\n createId?: () => string;\n};\n\nexport type ExpoSyncSocketBridgeEvent = {\n socketId: string;\n type: \"close\" | \"error\" | \"message-chunk\" | \"open\";\n code?: number;\n data?: string;\n index?: number;\n messageId?: string;\n reason?: string;\n total?: number;\n};\n\nexport type ExpoSyncSocketBridgeHostOptions = {\n allowedOrigin: string;\n socketTicket: (audience?: string) => Promise<string>;\n emit(event: ExpoSyncSocketBridgeEvent): void;\n webSocketImpl?: typeof WebSocket;\n maxSockets?: number;\n /** Maximum encoded Sync frame size. Defaults to 4 MiB. */\n maxFrameBytes?: number;\n};\n\ntype TransactionSession = {\n complete: Promise<void>;\n finish(commit: boolean): void;\n timer: ReturnType<typeof setTimeout>;\n transaction: SyncLocalTransaction;\n};\n\nconst requireRecord = (value: unknown, label: string) => {\n if (typeof value !== \"object\" || value === null || Array.isArray(value))\n throw new TypeError(`Expo Sync bridge ${label} is invalid.`);\n\n return value as Record<string, unknown>;\n};\n\nconst requireString = (value: unknown, label: string) => {\n if (typeof value !== \"string\" || value.length === 0 || value.length > 512)\n throw new TypeError(`Expo Sync bridge ${label} is invalid.`);\n\n return value;\n};\n\nconst requireCollectionRecord = (value: unknown): LocalCollectionRecord => {\n const record = requireRecord(value, \"collection record\");\n if (\n !Array.isArray(record.rows) ||\n typeof record.version !== \"number\" ||\n !Number.isSafeInteger(record.version) ||\n record.version < 0\n )\n throw new TypeError(\"Expo Sync bridge collection record is invalid.\");\n\n return structuredClone(record) as LocalCollectionRecord;\n};\n\nconst requireMutationRecord = (value: unknown): LocalMutationRecord => {\n const record = requireRecord(value, \"mutation record\");\n if (\n typeof record.operationId !== \"string\" ||\n record.operationId.length === 0 ||\n record.operationId.length > 512 ||\n typeof record.name !== \"string\" ||\n record.name.length === 0 ||\n record.name.length > 512 ||\n typeof record.createdAt !== \"number\" ||\n !Number.isFinite(record.createdAt) ||\n typeof record.attempts !== \"number\" ||\n !Number.isSafeInteger(record.attempts) ||\n record.attempts < 0 ||\n !Array.isArray(record.optimistic) ||\n !Array.isArray(record.inverse)\n )\n throw new TypeError(\"Expo Sync bridge mutation record is invalid.\");\n\n return structuredClone(record) as LocalMutationRecord;\n};\n\nconst rollbackMarker = Symbol(\"expo-sync-bridge-rollback\");\n\n/**\n * Native owner for WebView local-store transactions. It exposes only Sync's\n * typed persistence contract and never accepts a namespace from page code.\n */\nexport const createExpoSyncBridgeHost = ({\n store,\n namespace,\n transactionTimeoutMs = 8_000,\n createId = () => crypto.randomUUID(),\n}: ExpoSyncBridgeHostOptions) => {\n if (!namespace || namespace.length > 512)\n throw new TypeError(\"Expo Sync bridge namespace is invalid.\");\n if (\n !Number.isSafeInteger(transactionTimeoutMs) ||\n transactionTimeoutMs < 100 ||\n transactionTimeoutMs > 30_000\n )\n throw new TypeError(\n \"Expo Sync bridge transactionTimeoutMs must be between 100 and 30000.\",\n );\n const sessions = new Map<string, TransactionSession>();\n\n const begin = async (mode: SyncLocalStoreMode) => {\n if (sessions.size >= 8)\n throw new Error(\"Expo Sync bridge has too many open transactions.\");\n const id = createId();\n if (!/^[A-Za-z0-9._:-]{1,160}$/u.test(id) || sessions.has(id))\n throw new Error(\"Expo Sync bridge generated an invalid transaction id.\");\n let readyResolve: (transaction: SyncLocalTransaction) => void = () =>\n undefined;\n let readyReject: (error: unknown) => void = () => undefined;\n const ready = new Promise<SyncLocalTransaction>((resolve, reject) => {\n readyResolve = resolve;\n readyReject = reject;\n });\n let finish: (commit: boolean) => void = () => undefined;\n const decision = new Promise<boolean>((resolve) => {\n finish = resolve;\n });\n const complete = store\n .transaction(namespace, mode, async (transaction) => {\n readyResolve(transaction);\n if (!(await decision)) throw rollbackMarker;\n })\n .catch((error) => {\n readyReject(error);\n if (error !== rollbackMarker) throw error;\n });\n const transaction = await ready;\n const timer = setTimeout(() => {\n sessions.delete(id);\n finish(false);\n }, transactionTimeoutMs);\n sessions.set(id, { complete, finish, timer, transaction });\n\n return id;\n };\n\n const session = (params: Record<string, unknown>) => {\n const id = requireString(params.transactionId, \"transaction id\");\n const value = sessions.get(id);\n if (!value)\n throw new Error(\"Expo Sync bridge transaction is closed or unknown.\");\n\n return { id, value };\n };\n\n const end = async (params: Record<string, unknown>) => {\n const { id, value } = session(params);\n if (typeof params.commit !== \"boolean\")\n throw new TypeError(\"Expo Sync bridge commit decision is invalid.\");\n sessions.delete(id);\n clearTimeout(value.timer);\n value.finish(params.commit);\n await value.complete;\n\n return null;\n };\n\n const operation = async (\n method: string,\n params: Record<string, unknown>,\n ): Promise<unknown> => {\n const { value } = session(params);\n const transaction = value.transaction;\n if (method === \"sync.tx.getInstallationId\")\n return (await transaction.getInstallationId()) ?? null;\n if (method === \"sync.tx.setInstallationId\") {\n await transaction.setInstallationId(\n requireString(params.installationId, \"installation id\"),\n );\n\n return null;\n }\n if (method === \"sync.tx.getCollection\")\n return (\n (await transaction.getCollection(\n requireString(params.key, \"collection key\"),\n )) ?? null\n );\n if (method === \"sync.tx.listCollections\")\n return transaction.listCollections();\n if (method === \"sync.tx.putCollection\") {\n await transaction.putCollection(\n requireString(params.key, \"collection key\"),\n requireCollectionRecord(params.record),\n );\n\n return null;\n }\n if (method === \"sync.tx.deleteCollection\") {\n await transaction.deleteCollection(\n requireString(params.key, \"collection key\"),\n );\n\n return null;\n }\n if (method === \"sync.tx.listMutations\") return transaction.listMutations();\n if (method === \"sync.tx.getMutation\")\n return (\n (await transaction.getMutation(\n requireString(params.operationId, \"operation id\"),\n )) ?? null\n );\n if (method === \"sync.tx.putMutation\") {\n await transaction.putMutation(requireMutationRecord(params.record));\n\n return null;\n }\n if (method === \"sync.tx.deleteMutation\") {\n await transaction.deleteMutation(\n requireString(params.operationId, \"operation id\"),\n );\n\n return null;\n }\n if (method === \"sync.tx.resolveMutationPolicy\")\n return (\n transaction.resolveMutationPolicy?.(\n requireString(params.name, \"mutation name\"),\n ) ?? null\n );\n throw new Error(\"Expo Sync bridge transaction method is not allowed.\");\n };\n\n return {\n close: async () => {\n const active = [...sessions.values()];\n sessions.clear();\n for (const value of active) {\n clearTimeout(value.timer);\n value.finish(false);\n }\n await Promise.allSettled(active.map((value) => value.complete));\n },\n request: async (method: string, rawParams: unknown): Promise<unknown> => {\n const params = requireRecord(rawParams, \"params\");\n if (method === \"sync.store.begin\") {\n if (params.mode !== \"readonly\" && params.mode !== \"readwrite\")\n throw new TypeError(\"Expo Sync bridge transaction mode is invalid.\");\n\n return { transactionId: await begin(params.mode) };\n }\n if (method === \"sync.store.end\") return end(params);\n if (method === \"sync.store.schema\")\n return (await store.getSchemaStatus?.()) ?? null;\n if (method === \"sync.store.deleteNamespace\") {\n await store.deleteNamespace?.(namespace);\n\n return null;\n }\n if (method.startsWith(\"sync.tx.\")) return operation(method, params);\n throw new Error(\"Expo Sync bridge method is not allowed.\");\n },\n };\n};\n\nconst websocketOrigin = (url: URL) => {\n const protocol = url.protocol === \"wss:\" ? \"https:\" : \"http:\";\n\n return `${protocol}//${url.host}`;\n};\n\nconst ticketSocketUrl = (url: URL) => {\n if (url.searchParams.has(\"__absolute_auth\"))\n throw new TypeError(\n \"Expo Sync socket URL contains reserved authentication.\",\n );\n url.searchParams.set(\"__absolute_auth\", \"ticket\");\n\n return url.href;\n};\n\nconst SOCKET_CHUNK_BYTES = 24 * 1024;\nconst SOCKET_UPLOAD_TIMEOUT_MS = 10_000;\nconst encodeBase64 = (value: Uint8Array) => {\n let binary = \"\";\n for (const byte of value) binary += String.fromCharCode(byte);\n\n return btoa(binary);\n};\nconst decodeBase64 = (value: string) => {\n if (\n value.length === 0 ||\n value.length > Math.ceil(SOCKET_CHUNK_BYTES / 3) * 4 + 4 ||\n !/^[A-Za-z0-9+/]+={0,2}$/u.test(value)\n )\n throw new TypeError(\"Expo Sync socket chunk is invalid.\");\n\n return Uint8Array.from(atob(value), (character) => character.charCodeAt(0));\n};\n\n/**\n * Owns authenticated sockets in native JavaScript. Only ordinary string Sync\n * frames cross the WebView bridge; the single-use ticket is consumed here.\n */\nexport const createExpoSyncSocketBridgeHost = ({\n allowedOrigin,\n socketTicket,\n emit,\n webSocketImpl = globalThis.WebSocket,\n maxSockets = 4,\n maxFrameBytes = 4 * 1024 * 1024,\n}: ExpoSyncSocketBridgeHostOptions) => {\n const origin = new URL(allowedOrigin);\n if (\n origin.protocol !== \"https:\" ||\n origin.username ||\n origin.password ||\n origin.pathname !== \"/\" ||\n origin.search ||\n origin.hash\n )\n throw new TypeError(\n \"Expo Sync socket allowedOrigin must be an HTTPS origin.\",\n );\n if (!webSocketImpl)\n throw new Error(\"Expo Sync socket bridge requires WebSocket support.\");\n if (!Number.isSafeInteger(maxSockets) || maxSockets < 1 || maxSockets > 16)\n throw new TypeError(\"Expo Sync maxSockets must be between 1 and 16.\");\n if (\n !Number.isSafeInteger(maxFrameBytes) ||\n maxFrameBytes < SOCKET_CHUNK_BYTES ||\n maxFrameBytes > 16 * 1024 * 1024\n )\n throw new TypeError(\n \"Expo Sync maxFrameBytes must be between 24 KiB and 16 MiB.\",\n );\n const sockets = new Map<string, WebSocket>();\n const uploads = new Map<\n string,\n {\n chunks: Array<Uint8Array | undefined>;\n timer: ReturnType<typeof setTimeout>;\n }\n >();\n let messageSequence = 0;\n const socketId = (value: unknown) => {\n const id = requireString(value, \"socket id\");\n if (!/^[A-Za-z0-9._:-]{1,160}$/u.test(id))\n throw new TypeError(\"Expo Sync bridge socket id is invalid.\");\n\n return id;\n };\n const close = (id: string, code?: number, reason?: string) => {\n const socket = sockets.get(id);\n if (!socket) return;\n sockets.delete(id);\n for (const [key, upload] of uploads)\n if (key.startsWith(`${id}:\\u0000`)) {\n clearTimeout(upload.timer);\n uploads.delete(key);\n }\n socket.close(code, reason);\n };\n const emitMessage = (id: string, data: string) => {\n const bytes = new TextEncoder().encode(data);\n if (bytes.byteLength > maxFrameBytes) {\n emit({ socketId: id, type: \"error\" });\n close(id, 1009, \"Sync frame is too large\");\n\n return;\n }\n const total = Math.max(1, Math.ceil(bytes.byteLength / SOCKET_CHUNK_BYTES));\n const messageId = `native_${(messageSequence += 1).toString(36)}`;\n for (let index = 0; index < total; index += 1)\n emit({\n data: encodeBase64(\n bytes.slice(\n index * SOCKET_CHUNK_BYTES,\n Math.min(bytes.byteLength, (index + 1) * SOCKET_CHUNK_BYTES),\n ),\n ),\n index,\n messageId,\n socketId: id,\n total,\n type: \"message-chunk\",\n });\n };\n\n return {\n close: () => {\n for (const id of [...sockets.keys()]) close(id, 1000, \"Host closed\");\n },\n request: async (method: string, rawParams: unknown): Promise<unknown> => {\n const params = requireRecord(rawParams, \"socket params\");\n const id = socketId(params.socketId);\n if (method === \"sync.socket.open\") {\n if (sockets.has(id))\n throw new Error(\"Expo Sync bridge socket id is already open.\");\n if (sockets.size >= maxSockets)\n throw new Error(\"Expo Sync bridge socket limit exceeded.\");\n const url = new URL(requireString(params.url, \"socket URL\"));\n if (\n url.protocol !== \"wss:\" ||\n url.username ||\n url.password ||\n websocketOrigin(url) !== origin.origin\n )\n throw new Error(\n \"Expo Sync socket must use WSS on the configured production origin.\",\n );\n const socket = new webSocketImpl(ticketSocketUrl(url));\n sockets.set(id, socket);\n socket.onopen = () => {\n void socketTicket(origin.origin)\n .then((ticket) => {\n if (sockets.get(id) !== socket) return;\n socket.send(JSON.stringify({ ticket, type: \"authenticate\" }));\n emit({ socketId: id, type: \"open\" });\n })\n .catch(() => {\n if (sockets.get(id) !== socket) return;\n emit({ socketId: id, type: \"error\" });\n close(id, 1008, \"Authentication failed\");\n });\n };\n socket.onmessage = (event) => {\n if (sockets.get(id) !== socket) return;\n if (typeof event.data !== \"string\") {\n emit({ socketId: id, type: \"error\" });\n close(id, 1003, \"Binary frames are not supported\");\n\n return;\n }\n emitMessage(id, event.data);\n };\n socket.onerror = () => {\n if (sockets.get(id) === socket) emit({ socketId: id, type: \"error\" });\n };\n socket.onclose = (event) => {\n if (sockets.get(id) === socket) sockets.delete(id);\n emit({\n code: event.code,\n reason: event.reason,\n socketId: id,\n type: \"close\",\n });\n };\n\n return null;\n }\n if (method === \"sync.socket.sendChunk\") {\n const socket = sockets.get(id);\n if (!socket || socket.readyState !== webSocketImpl.OPEN)\n throw new Error(\"Expo Sync bridge socket is not open.\");\n const messageId = requireString(params.messageId, \"message id\");\n const index = params.index;\n const total = params.total;\n if (\n typeof index !== \"number\" ||\n !Number.isSafeInteger(index) ||\n typeof total !== \"number\" ||\n !Number.isSafeInteger(total) ||\n index < 0 ||\n total < 1 ||\n index >= total ||\n total > Math.ceil(maxFrameBytes / SOCKET_CHUNK_BYTES)\n )\n throw new TypeError(\"Expo Sync socket chunk position is invalid.\");\n if (typeof params.data !== \"string\")\n throw new TypeError(\"Expo Sync socket chunk data is invalid.\");\n const key = `${id}:\\u0000${messageId}`;\n let upload = uploads.get(key);\n if (!upload) {\n const timer = setTimeout(\n () => uploads.delete(key),\n SOCKET_UPLOAD_TIMEOUT_MS,\n );\n upload = { chunks: Array.from({ length: total }), timer };\n uploads.set(key, upload);\n }\n if (upload.chunks.length !== total || upload.chunks[index])\n throw new Error(\"Expo Sync socket chunk sequence is invalid.\");\n upload.chunks[index] = decodeBase64(params.data);\n if (upload.chunks.every((chunk) => chunk !== undefined)) {\n clearTimeout(upload.timer);\n uploads.delete(key);\n const size = upload.chunks.reduce(\n (sum, chunk) => sum + (chunk?.byteLength ?? 0),\n 0,\n );\n if (size > maxFrameBytes)\n throw new Error(\"Expo Sync socket frame exceeds its byte limit.\");\n const bytes = new Uint8Array(size);\n let offset = 0;\n for (const chunk of upload.chunks) {\n bytes.set(chunk!, offset);\n offset += chunk!.byteLength;\n }\n socket.send(new TextDecoder().decode(bytes));\n }\n\n return null;\n }\n if (method === \"sync.socket.close\") {\n const code =\n params.code === undefined\n ? undefined\n : typeof params.code === \"number\" &&\n Number.isSafeInteger(params.code) &&\n params.code >= 1000 &&\n params.code <= 4999\n ? params.code\n : null;\n if (code === null)\n throw new TypeError(\"Expo Sync bridge close code is invalid.\");\n const reason =\n params.reason === undefined\n ? undefined\n : requireString(params.reason, \"close reason\");\n close(id, code, reason);\n\n return null;\n }\n throw new Error(\"Expo Sync socket bridge method is not allowed.\");\n },\n };\n};\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": ";AA4CA,IAAM,gBAAgB,CAAC,OAAgB,UAAkB;AAAA,EACvD,IAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,MAAM,QAAQ,KAAK;AAAA,IACpE,MAAM,IAAI,UAAU,oBAAoB,mBAAmB;AAAA,EAE7D,OAAO;AAAA;AAGT,IAAM,gBAAgB,CAAC,OAAgB,UAAkB;AAAA,EACvD,IAAI,OAAO,UAAU,YAAY,MAAM,WAAW,KAAK,MAAM,SAAS;AAAA,IACpE,MAAM,IAAI,UAAU,oBAAoB,mBAAmB;AAAA,EAE7D,OAAO;AAAA;AAGT,IAAM,0BAA0B,CAAC,UAA0C;AAAA,EACzE,MAAM,SAAS,cAAc,OAAO,mBAAmB;AAAA,EACvD,IACE,CAAC,MAAM,QAAQ,OAAO,IAAI,KAC1B,OAAO,OAAO,YAAY,YAC1B,CAAC,OAAO,cAAc,OAAO,OAAO,KACpC,OAAO,UAAU;AAAA,IAEjB,MAAM,IAAI,UAAU,gDAAgD;AAAA,EAEtE,OAAO,gBAAgB,MAAM;AAAA;AAG/B,IAAM,wBAAwB,CAAC,UAAwC;AAAA,EACrE,MAAM,SAAS,cAAc,OAAO,iBAAiB;AAAA,EACrD,IACE,OAAO,OAAO,gBAAgB,YAC9B,OAAO,YAAY,WAAW,KAC9B,OAAO,YAAY,SAAS,OAC5B,OAAO,OAAO,SAAS,YACvB,OAAO,KAAK,WAAW,KACvB,OAAO,KAAK,SAAS,OACrB,OAAO,OAAO,cAAc,YAC5B,CAAC,OAAO,SAAS,OAAO,SAAS,KACjC,OAAO,OAAO,aAAa,YAC3B,CAAC,OAAO,cAAc,OAAO,QAAQ,KACrC,OAAO,WAAW,KAClB,CAAC,MAAM,QAAQ,OAAO,UAAU,KAChC,CAAC,MAAM,QAAQ,OAAO,OAAO;AAAA,IAE7B,MAAM,IAAI,UAAU,8CAA8C;AAAA,EAEpE,OAAO,gBAAgB,MAAM;AAAA;AAG/B,IAAM,iBAAiB,OAAO,2BAA2B;AAMlD,IAAM,2BAA2B;AAAA,EACtC;AAAA,EACA;AAAA,EACA,uBAAuB;AAAA,EACvB,WAAW,MAAM,OAAO,WAAW;AAAA,MACJ;AAAA,EAC/B,IAAI,CAAC,aAAa,UAAU,SAAS;AAAA,IACnC,MAAM,IAAI,UAAU,wCAAwC;AAAA,EAC9D,IACE,CAAC,OAAO,cAAc,oBAAoB,KAC1C,uBAAuB,OACvB,uBAAuB;AAAA,IAEvB,MAAM,IAAI,UACR,sEACF;AAAA,EACF,MAAM,WAAW,IAAI;AAAA,EAErB,MAAM,QAAQ,OAAO,SAA6B;AAAA,IAChD,IAAI,SAAS,QAAQ;AAAA,MACnB,MAAM,IAAI,MAAM,kDAAkD;AAAA,IACpE,MAAM,KAAK,SAAS;AAAA,IACpB,IAAI,CAAC,4BAA4B,KAAK,EAAE,KAAK,SAAS,IAAI,EAAE;AAAA,MAC1D,MAAM,IAAI,MAAM,uDAAuD;AAAA,IACzE,IAAI,eAA4D,MAAG;AAAA,MACjE;AAAA;AAAA,IACF,IAAI,cAAwC,MAAG;AAAA,MAAG;AAAA;AAAA,IAClD,MAAM,QAAQ,IAAI,QAA8B,CAAC,SAAS,WAAW;AAAA,MACnE,eAAe;AAAA,MACf,cAAc;AAAA,KACf;AAAA,IACD,IAAI,SAAoC,MAAG;AAAA,MAAG;AAAA;AAAA,IAC9C,MAAM,WAAW,IAAI,QAAiB,CAAC,YAAY;AAAA,MACjD,SAAS;AAAA,KACV;AAAA,IACD,MAAM,WAAW,MACd,YAAY,WAAW,MAAM,OAAO,iBAAgB;AAAA,MACnD,aAAa,YAAW;AAAA,MACxB,IAAI,CAAE,MAAM;AAAA,QAAW,MAAM;AAAA,KAC9B,EACA,MAAM,CAAC,UAAU;AAAA,MAChB,YAAY,KAAK;AAAA,MACjB,IAAI,UAAU;AAAA,QAAgB,MAAM;AAAA,KACrC;AAAA,IACH,MAAM,cAAc,MAAM;AAAA,IAC1B,MAAM,QAAQ,WAAW,MAAM;AAAA,MAC7B,SAAS,OAAO,EAAE;AAAA,MAClB,OAAO,KAAK;AAAA,OACX,oBAAoB;AAAA,IACvB,SAAS,IAAI,IAAI,EAAE,UAAU,QAAQ,OAAO,YAAY,CAAC;AAAA,IAEzD,OAAO;AAAA;AAAA,EAGT,MAAM,UAAU,CAAC,WAAoC;AAAA,IACnD,MAAM,KAAK,cAAc,OAAO,eAAe,gBAAgB;AAAA,IAC/D,MAAM,QAAQ,SAAS,IAAI,EAAE;AAAA,IAC7B,IAAI,CAAC;AAAA,MACH,MAAM,IAAI,MAAM,oDAAoD;AAAA,IAEtE,OAAO,EAAE,IAAI,MAAM;AAAA;AAAA,EAGrB,MAAM,MAAM,OAAO,WAAoC;AAAA,IACrD,QAAQ,IAAI,UAAU,QAAQ,MAAM;AAAA,IACpC,IAAI,OAAO,OAAO,WAAW;AAAA,MAC3B,MAAM,IAAI,UAAU,8CAA8C;AAAA,IACpE,SAAS,OAAO,EAAE;AAAA,IAClB,aAAa,MAAM,KAAK;AAAA,IACxB,MAAM,OAAO,OAAO,MAAM;AAAA,IAC1B,MAAM,MAAM;AAAA,IAEZ,OAAO;AAAA;AAAA,EAGT,MAAM,YAAY,OAChB,QACA,WACqB;AAAA,IACrB,QAAQ,UAAU,QAAQ,MAAM;AAAA,IAChC,MAAM,cAAc,MAAM;AAAA,IAC1B,IAAI,WAAW;AAAA,MACb,OAAQ,MAAM,YAAY,kBAAkB,KAAM;AAAA,IACpD,IAAI,WAAW,6BAA6B;AAAA,MAC1C,MAAM,YAAY,kBAChB,cAAc,OAAO,gBAAgB,iBAAiB,CACxD;AAAA,MAEA,OAAO;AAAA,IACT;AAAA,IACA,IAAI,WAAW;AAAA,MACb,OACG,MAAM,YAAY,cACjB,cAAc,OAAO,KAAK,gBAAgB,CAC5C,KAAM;AAAA,IAEV,IAAI,WAAW;AAAA,MACb,OAAO,YAAY,gBAAgB;AAAA,IACrC,IAAI,WAAW,yBAAyB;AAAA,MACtC,MAAM,YAAY,cAChB,cAAc,OAAO,KAAK,gBAAgB,GAC1C,wBAAwB,OAAO,MAAM,CACvC;AAAA,MAEA,OAAO;AAAA,IACT;AAAA,IACA,IAAI,WAAW,4BAA4B;AAAA,MACzC,MAAM,YAAY,iBAChB,cAAc,OAAO,KAAK,gBAAgB,CAC5C;AAAA,MAEA,OAAO;AAAA,IACT;AAAA,IACA,IAAI,WAAW;AAAA,MAAyB,OAAO,YAAY,cAAc;AAAA,IACzE,IAAI,WAAW;AAAA,MACb,OACG,MAAM,YAAY,YACjB,cAAc,OAAO,aAAa,cAAc,CAClD,KAAM;AAAA,IAEV,IAAI,WAAW,uBAAuB;AAAA,MACpC,MAAM,YAAY,YAAY,sBAAsB,OAAO,MAAM,CAAC;AAAA,MAElE,OAAO;AAAA,IACT;AAAA,IACA,IAAI,WAAW,0BAA0B;AAAA,MACvC,MAAM,YAAY,eAChB,cAAc,OAAO,aAAa,cAAc,CAClD;AAAA,MAEA,OAAO;AAAA,IACT;AAAA,IACA,IAAI,WAAW;AAAA,MACb,OACE,YAAY,wBACV,cAAc,OAAO,MAAM,eAAe,CAC5C,KAAK;AAAA,IAET,MAAM,IAAI,MAAM,qDAAqD;AAAA;AAAA,EAGvE,OAAO;AAAA,IACL,OAAO,YAAY;AAAA,MACjB,MAAM,SAAS,CAAC,GAAG,SAAS,OAAO,CAAC;AAAA,MACpC,SAAS,MAAM;AAAA,MACf,WAAW,SAAS,QAAQ;AAAA,QAC1B,aAAa,MAAM,KAAK;AAAA,QACxB,MAAM,OAAO,KAAK;AAAA,MACpB;AAAA,MACA,MAAM,QAAQ,WAAW,OAAO,IAAI,CAAC,UAAU,MAAM,QAAQ,CAAC;AAAA;AAAA,IAEhE,SAAS,OAAO,QAAgB,cAAyC;AAAA,MACvE,MAAM,SAAS,cAAc,WAAW,QAAQ;AAAA,MAChD,IAAI,WAAW,oBAAoB;AAAA,QACjC,IAAI,OAAO,SAAS,cAAc,OAAO,SAAS;AAAA,UAChD,MAAM,IAAI,UAAU,+CAA+C;AAAA,QAErE,OAAO,EAAE,eAAe,MAAM,MAAM,OAAO,IAAI,EAAE;AAAA,MACnD;AAAA,MACA,IAAI,WAAW;AAAA,QAAkB,OAAO,IAAI,MAAM;AAAA,MAClD,IAAI,WAAW;AAAA,QACb,OAAQ,MAAM,MAAM,kBAAkB,KAAM;AAAA,MAC9C,IAAI,WAAW,8BAA8B;AAAA,QAC3C,MAAM,MAAM,kBAAkB,SAAS;AAAA,QAEvC,OAAO;AAAA,MACT;AAAA,MACA,IAAI,OAAO,WAAW,UAAU;AAAA,QAAG,OAAO,UAAU,QAAQ,MAAM;AAAA,MAClE,MAAM,IAAI,MAAM,yCAAyC;AAAA;AAAA,EAE7D;AAAA;AAGF,IAAM,kBAAkB,CAAC,QAAa;AAAA,EACpC,MAAM,WAAW,IAAI,aAAa,SAAS,WAAW;AAAA,EAEtD,OAAO,GAAG,aAAa,IAAI;AAAA;AAG7B,IAAM,kBAAkB,CAAC,QAAa;AAAA,EACpC,IAAI,IAAI,aAAa,IAAI,iBAAiB;AAAA,IACxC,MAAM,IAAI,UACR,wDACF;AAAA,EACF,IAAI,aAAa,IAAI,mBAAmB,QAAQ;AAAA,EAEhD,OAAO,IAAI;AAAA;AAGb,IAAM,qBAAqB,KAAK;AAChC,IAAM,2BAA2B;AACjC,IAAM,eAAe,CAAC,UAAsB;AAAA,EAC1C,IAAI,SAAS;AAAA,EACb,WAAW,QAAQ;AAAA,IAAO,UAAU,OAAO,aAAa,IAAI;AAAA,EAE5D,OAAO,KAAK,MAAM;AAAA;AAEpB,IAAM,eAAe,CAAC,UAAkB;AAAA,EACtC,IACE,MAAM,WAAW,KACjB,MAAM,SAAS,KAAK,KAAK,qBAAqB,CAAC,IAAI,IAAI,KACvD,CAAC,0BAA0B,KAAK,KAAK;AAAA,IAErC,MAAM,IAAI,UAAU,oCAAoC;AAAA,EAE1D,OAAO,WAAW,KAAK,KAAK,KAAK,GAAG,CAAC,cAAc,UAAU,WAAW,CAAC,CAAC;AAAA;AAOrE,IAAM,iCAAiC;AAAA,EAC5C;AAAA,EACA;AAAA,EACA;AAAA,EACA,gBAAgB,WAAW;AAAA,EAC3B,aAAa;AAAA,EACb,gBAAgB,IAAI,OAAO;AAAA,MACU;AAAA,EACrC,MAAM,SAAS,IAAI,IAAI,aAAa;AAAA,EACpC,IACE,OAAO,aAAa,YACpB,OAAO,YACP,OAAO,YACP,OAAO,aAAa,OACpB,OAAO,UACP,OAAO;AAAA,IAEP,MAAM,IAAI,UACR,yDACF;AAAA,EACF,IAAI,CAAC;AAAA,IACH,MAAM,IAAI,MAAM,qDAAqD;AAAA,EACvE,IAAI,CAAC,OAAO,cAAc,UAAU,KAAK,aAAa,KAAK,aAAa;AAAA,IACtE,MAAM,IAAI,UAAU,gDAAgD;AAAA,EACtE,IACE,CAAC,OAAO,cAAc,aAAa,KACnC,gBAAgB,sBAChB,gBAAgB,KAAK,OAAO;AAAA,IAE5B,MAAM,IAAI,UACR,4DACF;AAAA,EACF,MAAM,UAAU,IAAI;AAAA,EACpB,MAAM,UAAU,IAAI;AAAA,EAOpB,IAAI,kBAAkB;AAAA,EACtB,MAAM,WAAW,CAAC,UAAmB;AAAA,IACnC,MAAM,KAAK,cAAc,OAAO,WAAW;AAAA,IAC3C,IAAI,CAAC,4BAA4B,KAAK,EAAE;AAAA,MACtC,MAAM,IAAI,UAAU,wCAAwC;AAAA,IAE9D,OAAO;AAAA;AAAA,EAET,MAAM,QAAQ,CAAC,IAAY,MAAe,WAAoB;AAAA,IAC5D,MAAM,SAAS,QAAQ,IAAI,EAAE;AAAA,IAC7B,IAAI,CAAC;AAAA,MAAQ;AAAA,IACb,QAAQ,OAAO,EAAE;AAAA,IACjB,YAAY,KAAK,WAAW;AAAA,MAC1B,IAAI,IAAI,WAAW,GAAG,SAAW,GAAG;AAAA,QAClC,aAAa,OAAO,KAAK;AAAA,QACzB,QAAQ,OAAO,GAAG;AAAA,MACpB;AAAA,IACF,OAAO,MAAM,MAAM,MAAM;AAAA;AAAA,EAE3B,MAAM,cAAc,CAAC,IAAY,SAAiB;AAAA,IAChD,MAAM,QAAQ,IAAI,YAAY,EAAE,OAAO,IAAI;AAAA,IAC3C,IAAI,MAAM,aAAa,eAAe;AAAA,MACpC,KAAK,EAAE,UAAU,IAAI,MAAM,QAAQ,CAAC;AAAA,MACpC,MAAM,IAAI,MAAM,yBAAyB;AAAA,MAEzC;AAAA,IACF;AAAA,IACA,MAAM,QAAQ,KAAK,IAAI,GAAG,KAAK,KAAK,MAAM,aAAa,kBAAkB,CAAC;AAAA,IAC1E,MAAM,YAAY,WAAW,mBAAmB,GAAG,SAAS,EAAE;AAAA,IAC9D,SAAS,QAAQ,EAAG,QAAQ,OAAO,SAAS;AAAA,MAC1C,KAAK;AAAA,QACH,MAAM,aACJ,MAAM,MACJ,QAAQ,oBACR,KAAK,IAAI,MAAM,aAAa,QAAQ,KAAK,kBAAkB,CAC7D,CACF;AAAA,QACA;AAAA,QACA;AAAA,QACA,UAAU;AAAA,QACV;AAAA,QACA,MAAM;AAAA,MACR,CAAC;AAAA;AAAA,EAGL,OAAO;AAAA,IACL,OAAO,MAAM;AAAA,MACX,WAAW,MAAM,CAAC,GAAG,QAAQ,KAAK,CAAC;AAAA,QAAG,MAAM,IAAI,MAAM,aAAa;AAAA;AAAA,IAErE,SAAS,OAAO,QAAgB,cAAyC;AAAA,MACvE,MAAM,SAAS,cAAc,WAAW,eAAe;AAAA,MACvD,MAAM,KAAK,SAAS,OAAO,QAAQ;AAAA,MACnC,IAAI,WAAW,oBAAoB;AAAA,QACjC,IAAI,QAAQ,IAAI,EAAE;AAAA,UAChB,MAAM,IAAI,MAAM,6CAA6C;AAAA,QAC/D,IAAI,QAAQ,QAAQ;AAAA,UAClB,MAAM,IAAI,MAAM,yCAAyC;AAAA,QAC3D,MAAM,MAAM,IAAI,IAAI,cAAc,OAAO,KAAK,YAAY,CAAC;AAAA,QAC3D,IACE,IAAI,aAAa,UACjB,IAAI,YACJ,IAAI,YACJ,gBAAgB,GAAG,MAAM,OAAO;AAAA,UAEhC,MAAM,IAAI,MACR,oEACF;AAAA,QACF,MAAM,SAAS,IAAI,cAAc,gBAAgB,GAAG,CAAC;AAAA,QACrD,QAAQ,IAAI,IAAI,MAAM;AAAA,QACtB,OAAO,SAAS,MAAM;AAAA,UACf,aAAa,OAAO,MAAM,EAC5B,KAAK,CAAC,WAAW;AAAA,YAChB,IAAI,QAAQ,IAAI,EAAE,MAAM;AAAA,cAAQ;AAAA,YAChC,OAAO,KAAK,KAAK,UAAU,EAAE,QAAQ,MAAM,eAAe,CAAC,CAAC;AAAA,YAC5D,KAAK,EAAE,UAAU,IAAI,MAAM,OAAO,CAAC;AAAA,WACpC,EACA,MAAM,MAAM;AAAA,YACX,IAAI,QAAQ,IAAI,EAAE,MAAM;AAAA,cAAQ;AAAA,YAChC,KAAK,EAAE,UAAU,IAAI,MAAM,QAAQ,CAAC;AAAA,YACpC,MAAM,IAAI,MAAM,uBAAuB;AAAA,WACxC;AAAA;AAAA,QAEL,OAAO,YAAY,CAAC,UAAU;AAAA,UAC5B,IAAI,QAAQ,IAAI,EAAE,MAAM;AAAA,YAAQ;AAAA,UAChC,IAAI,OAAO,MAAM,SAAS,UAAU;AAAA,YAClC,KAAK,EAAE,UAAU,IAAI,MAAM,QAAQ,CAAC;AAAA,YACpC,MAAM,IAAI,MAAM,iCAAiC;AAAA,YAEjD;AAAA,UACF;AAAA,UACA,YAAY,IAAI,MAAM,IAAI;AAAA;AAAA,QAE5B,OAAO,UAAU,MAAM;AAAA,UACrB,IAAI,QAAQ,IAAI,EAAE,MAAM;AAAA,YAAQ,KAAK,EAAE,UAAU,IAAI,MAAM,QAAQ,CAAC;AAAA;AAAA,QAEtE,OAAO,UAAU,CAAC,UAAU;AAAA,UAC1B,IAAI,QAAQ,IAAI,EAAE,MAAM;AAAA,YAAQ,QAAQ,OAAO,EAAE;AAAA,UACjD,KAAK;AAAA,YACH,MAAM,MAAM;AAAA,YACZ,QAAQ,MAAM;AAAA,YACd,UAAU;AAAA,YACV,MAAM;AAAA,UACR,CAAC;AAAA;AAAA,QAGH,OAAO;AAAA,MACT;AAAA,MACA,IAAI,WAAW,yBAAyB;AAAA,QACtC,MAAM,SAAS,QAAQ,IAAI,EAAE;AAAA,QAC7B,IAAI,CAAC,UAAU,OAAO,eAAe,cAAc;AAAA,UACjD,MAAM,IAAI,MAAM,sCAAsC;AAAA,QACxD,MAAM,YAAY,cAAc,OAAO,WAAW,YAAY;AAAA,QAC9D,MAAM,QAAQ,OAAO;AAAA,QACrB,MAAM,QAAQ,OAAO;AAAA,QACrB,IACE,OAAO,UAAU,YACjB,CAAC,OAAO,cAAc,KAAK,KAC3B,OAAO,UAAU,YACjB,CAAC,OAAO,cAAc,KAAK,KAC3B,QAAQ,KACR,QAAQ,KACR,SAAS,SACT,QAAQ,KAAK,KAAK,gBAAgB,kBAAkB;AAAA,UAEpD,MAAM,IAAI,UAAU,6CAA6C;AAAA,QACnE,IAAI,OAAO,OAAO,SAAS;AAAA,UACzB,MAAM,IAAI,UAAU,yCAAyC;AAAA,QAC/D,MAAM,MAAM,GAAG,UAAY;AAAA,QAC3B,IAAI,SAAS,QAAQ,IAAI,GAAG;AAAA,QAC5B,IAAI,CAAC,QAAQ;AAAA,UACX,MAAM,QAAQ,WACZ,MAAM,QAAQ,OAAO,GAAG,GACxB,wBACF;AAAA,UACA,SAAS,EAAE,QAAQ,MAAM,KAAK,EAAE,QAAQ,MAAM,CAAC,GAAG,MAAM;AAAA,UACxD,QAAQ,IAAI,KAAK,MAAM;AAAA,QACzB;AAAA,QACA,IAAI,OAAO,OAAO,WAAW,SAAS,OAAO,OAAO;AAAA,UAClD,MAAM,IAAI,MAAM,6CAA6C;AAAA,QAC/D,OAAO,OAAO,SAAS,aAAa,OAAO,IAAI;AAAA,QAC/C,IAAI,OAAO,OAAO,MAAM,CAAC,UAAU,UAAU,SAAS,GAAG;AAAA,UACvD,aAAa,OAAO,KAAK;AAAA,UACzB,QAAQ,OAAO,GAAG;AAAA,UAClB,MAAM,OAAO,OAAO,OAAO,OACzB,CAAC,KAAK,UAAU,OAAO,OAAO,cAAc,IAC5C,CACF;AAAA,UACA,IAAI,OAAO;AAAA,YACT,MAAM,IAAI,MAAM,gDAAgD;AAAA,UAClE,MAAM,QAAQ,IAAI,WAAW,IAAI;AAAA,UACjC,IAAI,SAAS;AAAA,UACb,WAAW,SAAS,OAAO,QAAQ;AAAA,YACjC,MAAM,IAAI,OAAQ,MAAM;AAAA,YACxB,UAAU,MAAO;AAAA,UACnB;AAAA,UACA,OAAO,KAAK,IAAI,YAAY,EAAE,OAAO,KAAK,CAAC;AAAA,QAC7C;AAAA,QAEA,OAAO;AAAA,MACT;AAAA,MACA,IAAI,WAAW,qBAAqB;AAAA,QAClC,MAAM,OACJ,OAAO,SAAS,YACZ,YACA,OAAO,OAAO,SAAS,YACrB,OAAO,cAAc,OAAO,IAAI,KAChC,OAAO,QAAQ,QACf,OAAO,QAAQ,OACf,OAAO,OACP;AAAA,QACR,IAAI,SAAS;AAAA,UACX,MAAM,IAAI,UAAU,yCAAyC;AAAA,QAC/D,MAAM,SACJ,OAAO,WAAW,YACd,YACA,cAAc,OAAO,QAAQ,cAAc;AAAA,QACjD,MAAM,IAAI,MAAM,MAAM;AAAA,QAEtB,OAAO;AAAA,MACT;AAAA,MACA,MAAM,IAAI,MAAM,gDAAgD;AAAA;AAAA,EAEpE;AAAA;",
|
|
8
|
+
"debugId": "7A0999003788222864756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|