@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/dist/index.js
CHANGED
|
@@ -1,1213 +1,1214 @@
|
|
|
1
|
-
//
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
}
|
|
6
|
-
var atitle = (title) => title ? `"${title}" ` : "";
|
|
7
|
-
function abool(value, title = "") {
|
|
8
|
-
if (typeof value !== "boolean")
|
|
9
|
-
throw new TypeError(atitle(title) + "expected boolean, got type=" + typeof value);
|
|
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.`);
|
|
10
5
|
return value;
|
|
11
|
-
}
|
|
12
|
-
function anumber(n, title = "") {
|
|
13
|
-
if (typeof n !== "number")
|
|
14
|
-
throw new TypeError(atitle(title) + "expected number, got " + typeof n);
|
|
15
|
-
if (!Number.isSafeInteger(n) || n < 0)
|
|
16
|
-
throw new RangeError(atitle(title) + "expected integer >= 0, got " + n);
|
|
17
|
-
return n;
|
|
18
|
-
}
|
|
19
|
-
function abytes(value, length, title = "") {
|
|
20
|
-
if (isBytes(value) && (length === undefined || value.length === length))
|
|
21
|
-
return value;
|
|
22
|
-
if (length !== undefined)
|
|
23
|
-
anumber(length, "length");
|
|
24
|
-
const bytes = isBytes(value);
|
|
25
|
-
const ofLen = length !== undefined ? ` of length ${length}` : "";
|
|
26
|
-
const got = bytes ? `length=${value.length}` : `type=${typeof value}`;
|
|
27
|
-
const message = atitle(title) + "expected Uint8Array" + ofLen + ", got " + got;
|
|
28
|
-
if (!bytes)
|
|
29
|
-
throw new TypeError(message);
|
|
30
|
-
throw new RangeError(message);
|
|
31
|
-
}
|
|
32
|
-
function aexists(instance, checkFinished = true) {
|
|
33
|
-
if (instance.destroyed)
|
|
34
|
-
throw new Error("hash was destroyed");
|
|
35
|
-
if (checkFinished && instance.finished)
|
|
36
|
-
throw new Error("digest() was already called");
|
|
37
|
-
}
|
|
38
|
-
function aoutput(out, instance) {
|
|
39
|
-
abytes(out, undefined, "output");
|
|
40
|
-
const min = instance.outputLen;
|
|
41
|
-
if (!(out.length >= min)) {
|
|
42
|
-
throw new RangeError('"output" expected length >= ' + min);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
function aoutput32(out, instance) {
|
|
46
|
-
aoutput(out, instance);
|
|
47
|
-
if (!isAligned32(out))
|
|
48
|
-
throw new Error("invalid output, must be aligned");
|
|
49
|
-
}
|
|
50
|
-
function u8(arr) {
|
|
51
|
-
return new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength);
|
|
52
|
-
}
|
|
53
|
-
function u32(arr) {
|
|
54
|
-
return new Uint32Array(arr.buffer, arr.byteOffset, Math.floor(arr.byteLength / 4));
|
|
55
|
-
}
|
|
56
|
-
function clean(...arrays) {
|
|
57
|
-
for (let i = 0;i < arrays.length; i++) {
|
|
58
|
-
arrays[i].fill(0);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
function createView(arr) {
|
|
62
|
-
return new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
|
|
63
|
-
}
|
|
64
|
-
var isLE = /* @__PURE__ */ (() => new Uint8Array(new Uint32Array([287454020]).buffer)[0] === 68)();
|
|
65
|
-
function byteSwap(word) {
|
|
66
|
-
return word << 24 & 4278190080 | word << 8 & 16711680 | word >>> 8 & 65280 | word >>> 24 & 255;
|
|
67
|
-
}
|
|
68
|
-
var swap8IfBE = isLE ? (n) => n : (n) => byteSwap(n) >>> 0;
|
|
69
|
-
function byteSwap32(arr) {
|
|
70
|
-
for (let i = 0;i < arr.length; i++) {
|
|
71
|
-
arr[i] = byteSwap(arr[i]);
|
|
72
|
-
}
|
|
73
|
-
return arr;
|
|
74
|
-
}
|
|
75
|
-
var swap32IfBE = isLE ? (u) => u : byteSwap32;
|
|
76
|
-
function equalBytes(a, b) {
|
|
77
|
-
a = abytes(a);
|
|
78
|
-
b = abytes(b);
|
|
79
|
-
if (a.length !== b.length)
|
|
80
|
-
return false;
|
|
81
|
-
let diff = 0;
|
|
82
|
-
for (let i = 0;i < a.length; i++)
|
|
83
|
-
diff |= a[i] ^ b[i];
|
|
84
|
-
return diff === 0;
|
|
85
|
-
}
|
|
86
|
-
function wrapMacConstructor(keyLen, macCons, fromMsg) {
|
|
87
|
-
const mac = macCons;
|
|
88
|
-
const getArgs = fromMsg || (() => []);
|
|
89
|
-
const macC = (msg, key) => mac(key, ...getArgs(msg)).update(msg).digest();
|
|
90
|
-
const tmp = mac(new Uint8Array(keyLen), ...getArgs(new Uint8Array(0)));
|
|
91
|
-
macC.outputLen = tmp.outputLen;
|
|
92
|
-
macC.blockLen = tmp.blockLen;
|
|
93
|
-
macC.create = (key, ...args) => mac(key, ...args);
|
|
94
|
-
return macC;
|
|
95
|
-
}
|
|
96
|
-
var wrapCipher = (params, constructor) => {
|
|
97
|
-
function wrappedCipher(key, ...args) {
|
|
98
|
-
abytes(key, undefined, "key");
|
|
99
|
-
if (params.nonceLength !== undefined) {
|
|
100
|
-
const nonce = args[0];
|
|
101
|
-
abytes(nonce, params.varSizeNonce ? undefined : params.nonceLength, "nonce");
|
|
102
|
-
}
|
|
103
|
-
const tagl = params.tagLength;
|
|
104
|
-
const aadStart = params.nonceLength !== undefined ? 1 : 0;
|
|
105
|
-
if (!params.withAAD) {
|
|
106
|
-
for (let i = aadStart;i < args.length; i++)
|
|
107
|
-
if (isBytes(args[i]))
|
|
108
|
-
throw new Error("AAD not supported");
|
|
109
|
-
}
|
|
110
|
-
if (params.withAAD && args[aadStart] !== undefined)
|
|
111
|
-
abytes(args[aadStart], undefined, "AAD");
|
|
112
|
-
const cipher = constructor(key, ...args);
|
|
113
|
-
const checkOutput = (fnLength, output) => {
|
|
114
|
-
if (output !== undefined) {
|
|
115
|
-
if (fnLength !== 2)
|
|
116
|
-
throw new Error("cipher output not supported");
|
|
117
|
-
abytes(output, undefined, "output");
|
|
118
|
-
}
|
|
119
|
-
};
|
|
120
|
-
let called = false;
|
|
121
|
-
const wrCipher = {
|
|
122
|
-
encrypt(data, output) {
|
|
123
|
-
if (called)
|
|
124
|
-
throw new Error("cannot encrypt() twice with same key + nonce");
|
|
125
|
-
called = true;
|
|
126
|
-
abytes(data, undefined, "data");
|
|
127
|
-
checkOutput(cipher.encrypt.length, output);
|
|
128
|
-
return cipher.encrypt(data, output);
|
|
129
|
-
},
|
|
130
|
-
decrypt(data, output) {
|
|
131
|
-
abytes(data, undefined, "data");
|
|
132
|
-
if (tagl && data.length < tagl)
|
|
133
|
-
throw new Error('"ciphertext" expected length >= tagLength=' + tagl);
|
|
134
|
-
checkOutput(cipher.decrypt.length, output);
|
|
135
|
-
return cipher.decrypt(data, output);
|
|
136
|
-
}
|
|
137
|
-
};
|
|
138
|
-
return wrCipher;
|
|
139
|
-
}
|
|
140
|
-
Object.assign(wrappedCipher, params);
|
|
141
|
-
return wrappedCipher;
|
|
142
6
|
};
|
|
143
|
-
|
|
144
|
-
if (
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
if (onlyAligned && !isAligned32(out))
|
|
148
|
-
throw new Error("invalid output, must be aligned");
|
|
149
|
-
return out;
|
|
150
|
-
}
|
|
151
|
-
function u64Lengths(dataLength, aadLength, isLE2) {
|
|
152
|
-
anumber(dataLength);
|
|
153
|
-
anumber(aadLength);
|
|
154
|
-
abool(isLE2);
|
|
155
|
-
const num = new Uint8Array(16);
|
|
156
|
-
const view = createView(num);
|
|
157
|
-
view.setBigUint64(0, BigInt(aadLength), isLE2);
|
|
158
|
-
view.setBigUint64(8, BigInt(dataLength), isLE2);
|
|
159
|
-
return num;
|
|
160
|
-
}
|
|
161
|
-
function isAligned32(bytes) {
|
|
162
|
-
return bytes.byteOffset % 4 === 0;
|
|
163
|
-
}
|
|
164
|
-
function copyBytes(bytes) {
|
|
165
|
-
return Uint8Array.from(abytes(bytes));
|
|
166
|
-
}
|
|
167
|
-
function randomBytes(bytesLength = 32) {
|
|
168
|
-
anumber(bytesLength, "bytesLength");
|
|
169
|
-
const cr = typeof globalThis === "object" ? globalThis.crypto : null;
|
|
170
|
-
if (typeof cr?.getRandomValues !== "function")
|
|
171
|
-
throw new Error("crypto.getRandomValues must be defined");
|
|
172
|
-
if (bytesLength > 65536)
|
|
173
|
-
throw new RangeError(`"bytesLength" expected <= 65536, got ${bytesLength}`);
|
|
174
|
-
return cr.getRandomValues(new Uint8Array(bytesLength));
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// node_modules/@noble/ciphers/_polyval.js
|
|
178
|
-
var BLOCK_SIZE = 16;
|
|
179
|
-
var ZEROS16 = /* @__PURE__ */ new Uint8Array(16);
|
|
180
|
-
var ZEROS32 = /* @__PURE__ */ u32(ZEROS16);
|
|
181
|
-
var POLY = 225;
|
|
182
|
-
var mul2 = (s0, s1, s2, s3) => {
|
|
183
|
-
const hiBit = s3 & 1;
|
|
184
|
-
return {
|
|
185
|
-
s3: s2 << 31 | s3 >>> 1,
|
|
186
|
-
s2: s1 << 31 | s2 >>> 1,
|
|
187
|
-
s1: s0 << 31 | s1 >>> 1,
|
|
188
|
-
s0: s0 >>> 1 ^ POLY << 24 & -(hiBit & 1)
|
|
189
|
-
};
|
|
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;
|
|
190
11
|
};
|
|
191
|
-
var
|
|
192
|
-
|
|
193
|
-
if (
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
return 4;
|
|
197
|
-
return 2;
|
|
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);
|
|
198
17
|
};
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
const
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
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;
|
|
264
100
|
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
abytes(data);
|
|
273
|
-
data = copyBytes(data);
|
|
274
|
-
const b32 = u32(data);
|
|
275
|
-
const blocks = Math.floor(data.length / BLOCK_SIZE);
|
|
276
|
-
const left = data.length % BLOCK_SIZE;
|
|
277
|
-
for (let i = 0;i < blocks; i++) {
|
|
278
|
-
this._updateBlock(swap8IfBE(b32[i * 4 + 0]), swap8IfBE(b32[i * 4 + 1]), swap8IfBE(b32[i * 4 + 2]), swap8IfBE(b32[i * 4 + 3]));
|
|
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;
|
|
279
108
|
}
|
|
280
|
-
if (
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
clean(ZEROS32);
|
|
109
|
+
if (method === "sync.tx.deleteCollection") {
|
|
110
|
+
await transaction.deleteCollection(requireString(params.key, "collection key"));
|
|
111
|
+
return null;
|
|
284
112
|
}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
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;
|
|
292
120
|
}
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
aoutput32(out, this);
|
|
297
|
-
this.finished = true;
|
|
298
|
-
const { s0, s1, s2, s3 } = this;
|
|
299
|
-
const o32 = u32(out);
|
|
300
|
-
o32[0] = s0;
|
|
301
|
-
o32[1] = s1;
|
|
302
|
-
o32[2] = s2;
|
|
303
|
-
o32[3] = s3;
|
|
304
|
-
if (!isLE)
|
|
305
|
-
swap32IfBE(o32.subarray(0, BLOCK_SIZE / 4));
|
|
306
|
-
}
|
|
307
|
-
digest() {
|
|
308
|
-
const res = new Uint8Array(BLOCK_SIZE);
|
|
309
|
-
this.digestInto(res);
|
|
310
|
-
this.destroy();
|
|
311
|
-
return res;
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
var ghash = /* @__PURE__ */ wrapMacConstructor(16, (key, expectedLength) => new GHASH(key, expectedLength), (msg) => [msg.length]);
|
|
315
|
-
|
|
316
|
-
// node_modules/@noble/ciphers/aes.js
|
|
317
|
-
var BLOCK_SIZE2 = 16;
|
|
318
|
-
var BLOCK_SIZE32 = 4;
|
|
319
|
-
var EMPTY_BLOCK = /* @__PURE__ */ new Uint8Array(BLOCK_SIZE2);
|
|
320
|
-
var POLY2 = 283;
|
|
321
|
-
function validateKeyLength(key) {
|
|
322
|
-
if (![16, 24, 32].includes(key.length))
|
|
323
|
-
throw new Error('"aes key" expected Uint8Array of length 16/24/32, got length=' + key.length);
|
|
324
|
-
}
|
|
325
|
-
function mul22(n) {
|
|
326
|
-
return n << 1 ^ POLY2 & -(n >> 7);
|
|
327
|
-
}
|
|
328
|
-
function mul(a, b) {
|
|
329
|
-
let res = 0;
|
|
330
|
-
for (;b > 0; b >>= 1) {
|
|
331
|
-
res ^= a & -(b & 1);
|
|
332
|
-
a = mul22(a);
|
|
333
|
-
}
|
|
334
|
-
return res;
|
|
335
|
-
}
|
|
336
|
-
var sbox = /* @__PURE__ */ (() => {
|
|
337
|
-
const t = new Uint8Array(256);
|
|
338
|
-
for (let i = 0, x = 1;i < 256; i++, x ^= mul22(x))
|
|
339
|
-
t[i] = x;
|
|
340
|
-
const box = new Uint8Array(256);
|
|
341
|
-
box[0] = 99;
|
|
342
|
-
for (let i = 0;i < 255; i++) {
|
|
343
|
-
let x = t[255 - i];
|
|
344
|
-
x |= x << 8;
|
|
345
|
-
box[t[i]] = (x ^ x >> 4 ^ x >> 5 ^ x >> 6 ^ x >> 7 ^ 99) & 255;
|
|
346
|
-
}
|
|
347
|
-
clean(t);
|
|
348
|
-
return box;
|
|
349
|
-
})();
|
|
350
|
-
var rotr32_8 = (n) => n << 24 | n >>> 8;
|
|
351
|
-
var rotl32_8 = (n) => n << 8 | n >>> 24;
|
|
352
|
-
function genTtable(sbox2, fn) {
|
|
353
|
-
if (sbox2.length !== 256)
|
|
354
|
-
throw new Error("wrong sbox length");
|
|
355
|
-
const T0 = new Uint32Array(256).map((_, j) => fn(sbox2[j]));
|
|
356
|
-
const T1 = T0.map(rotl32_8);
|
|
357
|
-
const T2 = T1.map(rotl32_8);
|
|
358
|
-
const T3 = T2.map(rotl32_8);
|
|
359
|
-
const T01 = new Uint32Array(256 * 256);
|
|
360
|
-
const T23 = new Uint32Array(256 * 256);
|
|
361
|
-
const sbox22 = new Uint16Array(256 * 256);
|
|
362
|
-
for (let i = 0;i < 256; i++) {
|
|
363
|
-
for (let j = 0;j < 256; j++) {
|
|
364
|
-
const idx = i * 256 + j;
|
|
365
|
-
T01[idx] = T0[i] ^ T1[j];
|
|
366
|
-
T23[idx] = T2[i] ^ T3[j];
|
|
367
|
-
sbox22[idx] = sbox2[i] << 8 | sbox2[j];
|
|
121
|
+
if (method === "sync.tx.deleteMutation") {
|
|
122
|
+
await transaction.deleteMutation(requireString(params.operationId, "operation id"));
|
|
123
|
+
return null;
|
|
368
124
|
}
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
return xk;
|
|
402
|
-
}
|
|
403
|
-
function apply0123(T01, T23, s0, s1, s2, s3) {
|
|
404
|
-
return T01[s0 << 8 & 65280 | s1 >>> 8 & 255] ^ T23[s2 >>> 8 & 65280 | s3 >>> 24 & 255];
|
|
405
|
-
}
|
|
406
|
-
function applySbox(sbox2, s0, s1, s2, s3) {
|
|
407
|
-
return sbox2[s0 & 255 | s1 & 65280] | sbox2[s2 >>> 16 & 255 | s3 >>> 16 & 65280] << 16;
|
|
408
|
-
}
|
|
409
|
-
function encrypt(xk, s0, s1, s2, s3) {
|
|
410
|
-
const { sbox2, T01, T23 } = tableEncoding;
|
|
411
|
-
let k = 0;
|
|
412
|
-
s0 ^= xk[k++], s1 ^= xk[k++], s2 ^= xk[k++], s3 ^= xk[k++];
|
|
413
|
-
const rounds = xk.length / 4 - 2;
|
|
414
|
-
for (let i = 0;i < rounds; i++) {
|
|
415
|
-
const t02 = xk[k++] ^ apply0123(T01, T23, s0, s1, s2, s3);
|
|
416
|
-
const t12 = xk[k++] ^ apply0123(T01, T23, s1, s2, s3, s0);
|
|
417
|
-
const t22 = xk[k++] ^ apply0123(T01, T23, s2, s3, s0, s1);
|
|
418
|
-
const t32 = xk[k++] ^ apply0123(T01, T23, s3, s0, s1, s2);
|
|
419
|
-
s0 = t02, s1 = t12, s2 = t22, s3 = t32;
|
|
420
|
-
}
|
|
421
|
-
const t0 = xk[k++] ^ applySbox(sbox2, s0, s1, s2, s3);
|
|
422
|
-
const t1 = xk[k++] ^ applySbox(sbox2, s1, s2, s3, s0);
|
|
423
|
-
const t2 = xk[k++] ^ applySbox(sbox2, s2, s3, s0, s1);
|
|
424
|
-
const t3 = xk[k++] ^ applySbox(sbox2, s3, s0, s1, s2);
|
|
425
|
-
return { s0: t0, s1: t1, s2: t2, s3: t3 };
|
|
426
|
-
}
|
|
427
|
-
function ctr32(xk, isLE2, nonce, src, dst) {
|
|
428
|
-
abytes(nonce, BLOCK_SIZE2, "nonce");
|
|
429
|
-
abytes(src);
|
|
430
|
-
dst = getOutput(src.length, dst);
|
|
431
|
-
const ctr = nonce;
|
|
432
|
-
const c32 = u32(ctr);
|
|
433
|
-
const view = createView(ctr);
|
|
434
|
-
const src32 = u32(src);
|
|
435
|
-
const dst32 = u32(dst);
|
|
436
|
-
const ctrPos = isLE2 ? 0 : 12;
|
|
437
|
-
const srcLen = src.length;
|
|
438
|
-
let ctrNum = view.getUint32(ctrPos, isLE2);
|
|
439
|
-
for (let i = 0;i + 4 <= src32.length; i += 4) {
|
|
440
|
-
const { s0, s1, s2, s3 } = encrypt(xk, swap8IfBE(c32[0]), swap8IfBE(c32[1]), swap8IfBE(c32[2]), swap8IfBE(c32[3]));
|
|
441
|
-
dst32[i + 0] = src32[i + 0] ^ swap8IfBE(s0);
|
|
442
|
-
dst32[i + 1] = src32[i + 1] ^ swap8IfBE(s1);
|
|
443
|
-
dst32[i + 2] = src32[i + 2] ^ swap8IfBE(s2);
|
|
444
|
-
dst32[i + 3] = src32[i + 3] ^ swap8IfBE(s3);
|
|
445
|
-
ctrNum = ctrNum + 1 >>> 0;
|
|
446
|
-
view.setUint32(ctrPos, ctrNum, isLE2);
|
|
447
|
-
}
|
|
448
|
-
const start = BLOCK_SIZE2 * Math.floor(src32.length / BLOCK_SIZE32);
|
|
449
|
-
if (start < srcLen) {
|
|
450
|
-
const { s0, s1, s2, s3 } = encrypt(xk, swap8IfBE(c32[0]), swap8IfBE(c32[1]), swap8IfBE(c32[2]), swap8IfBE(c32[3]));
|
|
451
|
-
const b32 = new Uint32Array([s0, s1, s2, s3]);
|
|
452
|
-
swap32IfBE(b32);
|
|
453
|
-
const buf = u8(b32);
|
|
454
|
-
for (let i = start, pos = 0;i < srcLen; i++, pos++)
|
|
455
|
-
dst[i] = src[i] ^ buf[pos];
|
|
456
|
-
clean(b32);
|
|
457
|
-
}
|
|
458
|
-
return dst;
|
|
459
|
-
}
|
|
460
|
-
function computeTag(fn, isLE2, key, data, AAD) {
|
|
461
|
-
const aadLength = AAD ? AAD.length : 0;
|
|
462
|
-
const h = fn.create(key, data.length + aadLength);
|
|
463
|
-
if (AAD)
|
|
464
|
-
h.update(AAD);
|
|
465
|
-
const num = u64Lengths(8 * data.length, 8 * aadLength, isLE2);
|
|
466
|
-
h.update(data);
|
|
467
|
-
h.update(num);
|
|
468
|
-
const res = h.digest();
|
|
469
|
-
clean(num);
|
|
470
|
-
return res;
|
|
471
|
-
}
|
|
472
|
-
var gcm = /* @__PURE__ */ wrapCipher({ blockSize: 16, nonceLength: 12, tagLength: 16, withAAD: true, varSizeNonce: true }, function aesgcm(key, nonce, AAD) {
|
|
473
|
-
if (nonce.length < 8)
|
|
474
|
-
throw new Error("aes/gcm: invalid nonce length");
|
|
475
|
-
const tagLength = 16;
|
|
476
|
-
function _computeTag(authKey, tagMask, data) {
|
|
477
|
-
const tag = computeTag(ghash, false, authKey, data, AAD);
|
|
478
|
-
for (let i = 0;i < tagMask.length; i++)
|
|
479
|
-
tag[i] ^= tagMask[i];
|
|
480
|
-
return tag;
|
|
481
|
-
}
|
|
482
|
-
function deriveKeys() {
|
|
483
|
-
const xk = expandKeyLE(key);
|
|
484
|
-
const authKey = EMPTY_BLOCK.slice();
|
|
485
|
-
const counter = EMPTY_BLOCK.slice();
|
|
486
|
-
ctr32(xk, false, counter, counter, authKey);
|
|
487
|
-
if (nonce.length === 12) {
|
|
488
|
-
counter.set(nonce);
|
|
489
|
-
} else {
|
|
490
|
-
const nonceLen = EMPTY_BLOCK.slice();
|
|
491
|
-
const view = createView(nonceLen);
|
|
492
|
-
view.setBigUint64(8, BigInt(nonce.length * 8), false);
|
|
493
|
-
const g = ghash.create(authKey).update(nonce).update(nonceLen);
|
|
494
|
-
g.digestInto(counter);
|
|
495
|
-
g.destroy();
|
|
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.");
|
|
496
157
|
}
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
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
|
+
};
|
|
500
240
|
return {
|
|
501
|
-
|
|
502
|
-
const
|
|
503
|
-
|
|
504
|
-
const toClean = [xk, authKey, counter, tagMask];
|
|
505
|
-
if (!isAligned32(plaintext))
|
|
506
|
-
toClean.push(plaintext = copyBytes(plaintext));
|
|
507
|
-
ctr32(xk, false, counter, plaintext, out.subarray(0, plaintext.length));
|
|
508
|
-
const tag = _computeTag(authKey, tagMask, out.subarray(0, out.length - tagLength));
|
|
509
|
-
toClean.push(tag);
|
|
510
|
-
out.set(tag, plaintext.length);
|
|
511
|
-
clean(...toClean);
|
|
512
|
-
return out;
|
|
241
|
+
close: () => {
|
|
242
|
+
for (const id of [...sockets.keys()])
|
|
243
|
+
close(id, 1000, "Host closed");
|
|
513
244
|
},
|
|
514
|
-
|
|
515
|
-
const
|
|
516
|
-
const
|
|
517
|
-
if (
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
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;
|
|
526
296
|
}
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
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.");
|
|
530
343
|
}
|
|
531
344
|
};
|
|
532
|
-
});
|
|
533
|
-
|
|
534
|
-
// src/index.ts
|
|
535
|
-
import * as BackgroundTask from "expo-background-task";
|
|
536
|
-
import * as Network from "expo-network";
|
|
537
|
-
import * as SecureStore from "expo-secure-store";
|
|
538
|
-
import * as TaskManager from "expo-task-manager";
|
|
539
|
-
import { AppState } from "react-native";
|
|
540
|
-
|
|
541
|
-
// src/store.ts
|
|
542
|
-
import {
|
|
543
|
-
createSyncLocalSchemaStatus,
|
|
544
|
-
migrateSyncLocalCollectionRecord,
|
|
545
|
-
migrateSyncLocalMutationRecord,
|
|
546
|
-
resolveSyncLocalDataPolicy,
|
|
547
|
-
resolveSyncLocalSchemaComponents,
|
|
548
|
-
runSyncLocalPolicyTransaction
|
|
549
|
-
} from "@absolutejs/sync/client";
|
|
550
|
-
import * as SQLite from "expo-sqlite";
|
|
551
|
-
var SCHEMA = [
|
|
552
|
-
`CREATE TABLE IF NOT EXISTS absolute_sync_schema (
|
|
553
|
-
singleton_id INTEGER PRIMARY KEY NOT NULL CHECK (singleton_id = 1),
|
|
554
|
-
logical_version INTEGER NOT NULL
|
|
555
|
-
)`,
|
|
556
|
-
`CREATE TABLE IF NOT EXISTS absolute_sync_schema_components (
|
|
557
|
-
component_id TEXT PRIMARY KEY NOT NULL,
|
|
558
|
-
logical_version INTEGER NOT NULL
|
|
559
|
-
)`,
|
|
560
|
-
`CREATE TABLE IF NOT EXISTS absolute_sync_metadata (
|
|
561
|
-
namespace TEXT PRIMARY KEY NOT NULL,
|
|
562
|
-
installation_id TEXT NOT NULL
|
|
563
|
-
)`,
|
|
564
|
-
`CREATE TABLE IF NOT EXISTS absolute_sync_collections (
|
|
565
|
-
namespace TEXT NOT NULL,
|
|
566
|
-
collection_key TEXT NOT NULL,
|
|
567
|
-
record_json TEXT NOT NULL,
|
|
568
|
-
PRIMARY KEY (namespace, collection_key)
|
|
569
|
-
)`,
|
|
570
|
-
`CREATE TABLE IF NOT EXISTS absolute_sync_mutations (
|
|
571
|
-
namespace TEXT NOT NULL,
|
|
572
|
-
operation_id TEXT NOT NULL,
|
|
573
|
-
created_at INTEGER NOT NULL,
|
|
574
|
-
record_json TEXT NOT NULL,
|
|
575
|
-
PRIMARY KEY (namespace, operation_id)
|
|
576
|
-
)`,
|
|
577
|
-
`CREATE INDEX IF NOT EXISTS absolute_sync_mutations_order
|
|
578
|
-
ON absolute_sync_mutations (namespace, created_at, operation_id)`
|
|
579
|
-
];
|
|
580
|
-
var executor = (value) => ({
|
|
581
|
-
execAsync: (source) => value.execAsync(source),
|
|
582
|
-
getAllAsync: (source, params = []) => value.getAllAsync(source, [...params]),
|
|
583
|
-
getFirstAsync: (source, params = []) => value.getFirstAsync(source, [...params]),
|
|
584
|
-
runAsync: (source, params = []) => value.runAsync(source, [...params])
|
|
585
|
-
});
|
|
586
|
-
var defaultDatabase = async (databaseName) => {
|
|
587
|
-
const database = await SQLite.openDatabaseAsync(databaseName);
|
|
588
|
-
const direct = executor(database);
|
|
589
|
-
return {
|
|
590
|
-
...direct,
|
|
591
|
-
withExclusiveTransactionAsync: (run) => database.withExclusiveTransactionAsync((transaction) => run(executor(transaction)))
|
|
592
|
-
};
|
|
593
345
|
};
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
346
|
+
|
|
347
|
+
// node_modules/@noble/ciphers/utils.js
|
|
348
|
+
/*! noble-ciphers - MIT License (c) 2023 Paul Miller (paulmillr.com) */
|
|
349
|
+
function isBytes(a) {
|
|
350
|
+
return a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array" && "BYTES_PER_ELEMENT" in a && a.BYTES_PER_ELEMENT === 1;
|
|
351
|
+
}
|
|
352
|
+
var atitle = (title) => title ? `"${title}" ` : "";
|
|
353
|
+
function abool(value, title = "") {
|
|
354
|
+
if (typeof value !== "boolean")
|
|
355
|
+
throw new TypeError(atitle(title) + "expected boolean, got type=" + typeof value);
|
|
356
|
+
return value;
|
|
357
|
+
}
|
|
358
|
+
function anumber(n, title = "") {
|
|
359
|
+
if (typeof n !== "number")
|
|
360
|
+
throw new TypeError(atitle(title) + "expected number, got " + typeof n);
|
|
361
|
+
if (!Number.isSafeInteger(n) || n < 0)
|
|
362
|
+
throw new RangeError(atitle(title) + "expected integer >= 0, got " + n);
|
|
363
|
+
return n;
|
|
364
|
+
}
|
|
365
|
+
function abytes(value, length, title = "") {
|
|
366
|
+
if (isBytes(value) && (length === undefined || value.length === length))
|
|
367
|
+
return value;
|
|
368
|
+
if (length !== undefined)
|
|
369
|
+
anumber(length, "length");
|
|
370
|
+
const bytes = isBytes(value);
|
|
371
|
+
const ofLen = length !== undefined ? ` of length ${length}` : "";
|
|
372
|
+
const got = bytes ? `length=${value.length}` : `type=${typeof value}`;
|
|
373
|
+
const message = atitle(title) + "expected Uint8Array" + ofLen + ", got " + got;
|
|
374
|
+
if (!bytes)
|
|
375
|
+
throw new TypeError(message);
|
|
376
|
+
throw new RangeError(message);
|
|
377
|
+
}
|
|
378
|
+
function aexists(instance, checkFinished = true) {
|
|
379
|
+
if (instance.destroyed)
|
|
380
|
+
throw new Error("hash was destroyed");
|
|
381
|
+
if (checkFinished && instance.finished)
|
|
382
|
+
throw new Error("digest() was already called");
|
|
383
|
+
}
|
|
384
|
+
function aoutput(out, instance) {
|
|
385
|
+
abytes(out, undefined, "output");
|
|
386
|
+
const min = instance.outputLen;
|
|
387
|
+
if (!(out.length >= min)) {
|
|
388
|
+
throw new RangeError('"output" expected length >= ' + min);
|
|
615
389
|
}
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
390
|
+
}
|
|
391
|
+
function aoutput32(out, instance) {
|
|
392
|
+
aoutput(out, instance);
|
|
393
|
+
if (!isAligned32(out))
|
|
394
|
+
throw new Error("invalid output, must be aligned");
|
|
395
|
+
}
|
|
396
|
+
function u8(arr) {
|
|
397
|
+
return new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength);
|
|
398
|
+
}
|
|
399
|
+
function u32(arr) {
|
|
400
|
+
return new Uint32Array(arr.buffer, arr.byteOffset, Math.floor(arr.byteLength / 4));
|
|
401
|
+
}
|
|
402
|
+
function clean(...arrays) {
|
|
403
|
+
for (let i = 0;i < arrays.length; i++) {
|
|
404
|
+
arrays[i].fill(0);
|
|
622
405
|
}
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
let
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
406
|
+
}
|
|
407
|
+
function createView(arr) {
|
|
408
|
+
return new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
|
|
409
|
+
}
|
|
410
|
+
var isLE = /* @__PURE__ */ (() => new Uint8Array(new Uint32Array([287454020]).buffer)[0] === 68)();
|
|
411
|
+
function byteSwap(word) {
|
|
412
|
+
return word << 24 & 4278190080 | word << 8 & 16711680 | word >>> 8 & 65280 | word >>> 24 & 255;
|
|
413
|
+
}
|
|
414
|
+
var swap8IfBE = isLE ? (n) => n : (n) => byteSwap(n) >>> 0;
|
|
415
|
+
function byteSwap32(arr) {
|
|
416
|
+
for (let i = 0;i < arr.length; i++) {
|
|
417
|
+
arr[i] = byteSwap(arr[i]);
|
|
418
|
+
}
|
|
419
|
+
return arr;
|
|
420
|
+
}
|
|
421
|
+
var swap32IfBE = isLE ? (u) => u : byteSwap32;
|
|
422
|
+
function equalBytes(a, b) {
|
|
423
|
+
a = abytes(a);
|
|
424
|
+
b = abytes(b);
|
|
425
|
+
if (a.length !== b.length)
|
|
426
|
+
return false;
|
|
427
|
+
let diff = 0;
|
|
428
|
+
for (let i = 0;i < a.length; i++)
|
|
429
|
+
diff |= a[i] ^ b[i];
|
|
430
|
+
return diff === 0;
|
|
431
|
+
}
|
|
432
|
+
function wrapMacConstructor(keyLen, macCons, fromMsg) {
|
|
433
|
+
const mac = macCons;
|
|
434
|
+
const getArgs = fromMsg || (() => []);
|
|
435
|
+
const macC = (msg, key) => mac(key, ...getArgs(msg)).update(msg).digest();
|
|
436
|
+
const tmp = mac(new Uint8Array(keyLen), ...getArgs(new Uint8Array(0)));
|
|
437
|
+
macC.outputLen = tmp.outputLen;
|
|
438
|
+
macC.blockLen = tmp.blockLen;
|
|
439
|
+
macC.create = (key, ...args) => mac(key, ...args);
|
|
440
|
+
return macC;
|
|
441
|
+
}
|
|
442
|
+
var wrapCipher = (params, constructor) => {
|
|
443
|
+
function wrappedCipher(key, ...args) {
|
|
444
|
+
abytes(key, undefined, "key");
|
|
445
|
+
if (params.nonceLength !== undefined) {
|
|
446
|
+
const nonce = args[0];
|
|
447
|
+
abytes(nonce, params.varSizeNonce ? undefined : params.nonceLength, "nonce");
|
|
642
448
|
}
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
if (
|
|
656
|
-
throw new Error("
|
|
657
|
-
|
|
658
|
-
if (migrated === null)
|
|
659
|
-
await transaction.runAsync("DELETE FROM absolute_sync_collections WHERE namespace = ? AND collection_key = ?", [namespace, key]);
|
|
660
|
-
else
|
|
661
|
-
await transaction.runAsync("UPDATE absolute_sync_collections SET record_json = ? WHERE namespace = ? AND collection_key = ?", [
|
|
662
|
-
serializeRecord(migrated, {
|
|
663
|
-
kind: "collection",
|
|
664
|
-
name: migrated.collection ?? key,
|
|
665
|
-
namespace
|
|
666
|
-
}, protector),
|
|
667
|
-
namespace,
|
|
668
|
-
key
|
|
669
|
-
]);
|
|
449
|
+
const tagl = params.tagLength;
|
|
450
|
+
const aadStart = params.nonceLength !== undefined ? 1 : 0;
|
|
451
|
+
if (!params.withAAD) {
|
|
452
|
+
for (let i = aadStart;i < args.length; i++)
|
|
453
|
+
if (isBytes(args[i]))
|
|
454
|
+
throw new Error("AAD not supported");
|
|
455
|
+
}
|
|
456
|
+
if (params.withAAD && args[aadStart] !== undefined)
|
|
457
|
+
abytes(args[aadStart], undefined, "AAD");
|
|
458
|
+
const cipher = constructor(key, ...args);
|
|
459
|
+
const checkOutput = (fnLength, output) => {
|
|
460
|
+
if (output !== undefined) {
|
|
461
|
+
if (fnLength !== 2)
|
|
462
|
+
throw new Error("cipher output not supported");
|
|
463
|
+
abytes(output, undefined, "output");
|
|
670
464
|
}
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
if (
|
|
676
|
-
throw new Error("
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
name: migrated.name,
|
|
689
|
-
namespace
|
|
690
|
-
}, protector),
|
|
691
|
-
namespace,
|
|
692
|
-
operationId
|
|
693
|
-
]);
|
|
465
|
+
};
|
|
466
|
+
let called = false;
|
|
467
|
+
const wrCipher = {
|
|
468
|
+
encrypt(data, output) {
|
|
469
|
+
if (called)
|
|
470
|
+
throw new Error("cannot encrypt() twice with same key + nonce");
|
|
471
|
+
called = true;
|
|
472
|
+
abytes(data, undefined, "data");
|
|
473
|
+
checkOutput(cipher.encrypt.length, output);
|
|
474
|
+
return cipher.encrypt(data, output);
|
|
475
|
+
},
|
|
476
|
+
decrypt(data, output) {
|
|
477
|
+
abytes(data, undefined, "data");
|
|
478
|
+
if (tagl && data.length < tagl)
|
|
479
|
+
throw new Error('"ciphertext" expected length >= tagLength=' + tagl);
|
|
480
|
+
checkOutput(cipher.decrypt.length, output);
|
|
481
|
+
return cipher.decrypt(data, output);
|
|
694
482
|
}
|
|
695
|
-
}
|
|
696
|
-
for (const component of resolved.components)
|
|
697
|
-
await transaction.runAsync("INSERT INTO absolute_sync_schema_components (component_id, logical_version) VALUES (?, ?) ON CONFLICT(component_id) DO UPDATE SET logical_version = excluded.logical_version", [component.id, component.targetVersion]);
|
|
698
|
-
const app = resolved.components.find((component) => component.id === "@absolutejs/app");
|
|
699
|
-
if (app)
|
|
700
|
-
await transaction.runAsync("INSERT INTO absolute_sync_schema (singleton_id, logical_version) VALUES (1, ?) ON CONFLICT(singleton_id) DO UPDATE SET logical_version = excluded.logical_version", [app.targetVersion]);
|
|
701
|
-
status = createSyncLocalSchemaStatus(resolved.components, resolved.orphanedComponents, "components" in storageSchema);
|
|
702
|
-
});
|
|
703
|
-
if (!status)
|
|
704
|
-
throw new Error("Expo Sync schema transaction did not run.");
|
|
705
|
-
return status;
|
|
706
|
-
};
|
|
707
|
-
var createExpoSyncLocalStore = ({
|
|
708
|
-
databaseName = "absolutejs-sync-local-v1.db",
|
|
709
|
-
database: createDatabase = () => defaultDatabase(databaseName),
|
|
710
|
-
storageSchema = { version: 1 },
|
|
711
|
-
protection,
|
|
712
|
-
now = Date.now
|
|
713
|
-
} = {}) => {
|
|
714
|
-
if (!/^[A-Za-z0-9._-]{1,120}$/u.test(databaseName))
|
|
715
|
-
throw new TypeError("Expo Sync databaseName is invalid.");
|
|
716
|
-
const localData = resolveSyncLocalDataPolicy(storageSchema);
|
|
717
|
-
let protectorPromise;
|
|
718
|
-
const prepareProtector = () => protectorPromise ??= protection?.prepare();
|
|
719
|
-
let schemaStatus;
|
|
720
|
-
let databasePromise;
|
|
721
|
-
const database = () => {
|
|
722
|
-
databasePromise ??= Promise.all([
|
|
723
|
-
Promise.resolve(createDatabase()),
|
|
724
|
-
prepareProtector()
|
|
725
|
-
]).then(async ([value, protector]) => {
|
|
726
|
-
await value.execAsync("PRAGMA journal_mode = WAL");
|
|
727
|
-
for (const statement of SCHEMA)
|
|
728
|
-
await value.execAsync(statement);
|
|
729
|
-
schemaStatus = await prepareSchema(value, storageSchema, protector);
|
|
730
|
-
return value;
|
|
731
|
-
});
|
|
732
|
-
return databasePromise;
|
|
733
|
-
};
|
|
734
|
-
let tail = Promise.resolve();
|
|
735
|
-
const locked = async (run) => {
|
|
736
|
-
let release = () => {
|
|
737
|
-
return;
|
|
738
483
|
};
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
return rows.map((row) => {
|
|
786
|
-
const key = row.collection_key;
|
|
787
|
-
const record = parseRecord(row.record_json, "collection", { kind: "collection", namespace }, protector);
|
|
788
|
-
return typeof key === "string" && record ? { key, record } : undefined;
|
|
789
|
-
}).filter((entry) => entry !== undefined);
|
|
790
|
-
},
|
|
791
|
-
listMutations: async () => {
|
|
792
|
-
const rows = await sqlite.getAllAsync("SELECT record_json FROM absolute_sync_mutations WHERE namespace = ? ORDER BY created_at ASC, operation_id ASC", [namespace]);
|
|
793
|
-
return rows.map((row) => parseRecord(row.record_json, "mutation", { kind: "mutation", namespace }, protector)).filter((record) => record !== undefined);
|
|
794
|
-
},
|
|
795
|
-
putCollection: async (key, record) => {
|
|
796
|
-
writable();
|
|
797
|
-
await sqlite.runAsync("INSERT INTO absolute_sync_collections (namespace, collection_key, record_json) VALUES (?, ?, ?) ON CONFLICT(namespace, collection_key) DO UPDATE SET record_json = excluded.record_json", [
|
|
798
|
-
namespace,
|
|
799
|
-
key,
|
|
800
|
-
serializeRecord(record, {
|
|
801
|
-
kind: "collection",
|
|
802
|
-
name: record.collection ?? key,
|
|
803
|
-
namespace
|
|
804
|
-
}, protector)
|
|
805
|
-
]);
|
|
806
|
-
},
|
|
807
|
-
putMutation: async (record) => {
|
|
808
|
-
writable();
|
|
809
|
-
await sqlite.runAsync("INSERT INTO absolute_sync_mutations (namespace, operation_id, created_at, record_json) VALUES (?, ?, ?, ?) ON CONFLICT(namespace, operation_id) DO UPDATE SET created_at = excluded.created_at, record_json = excluded.record_json", [
|
|
810
|
-
namespace,
|
|
811
|
-
record.operationId,
|
|
812
|
-
record.createdAt,
|
|
813
|
-
serializeRecord(record, {
|
|
814
|
-
kind: "mutation",
|
|
815
|
-
name: record.name,
|
|
816
|
-
namespace
|
|
817
|
-
}, protector)
|
|
818
|
-
]);
|
|
819
|
-
},
|
|
820
|
-
setInstallationId: async (installationId) => {
|
|
821
|
-
writable();
|
|
822
|
-
if (installationId.length === 0)
|
|
823
|
-
throw new TypeError("Sync installation id must not be empty.");
|
|
824
|
-
await sqlite.runAsync("INSERT INTO absolute_sync_metadata (namespace, installation_id) VALUES (?, ?) ON CONFLICT(namespace) DO UPDATE SET installation_id = excluded.installation_id", [namespace, installationId]);
|
|
825
|
-
}
|
|
826
|
-
};
|
|
827
|
-
result = await runSyncLocalPolicyTransaction({
|
|
828
|
-
mode,
|
|
829
|
-
now: now(),
|
|
830
|
-
policy: localData,
|
|
831
|
-
protected: protector !== undefined,
|
|
832
|
-
raw,
|
|
833
|
-
run
|
|
834
|
-
});
|
|
835
|
-
completed = true;
|
|
836
|
-
});
|
|
837
|
-
if (!completed)
|
|
838
|
-
throw new Error("Expo Sync transaction did not complete.");
|
|
839
|
-
return result;
|
|
840
|
-
});
|
|
841
|
-
};
|
|
484
|
+
return wrCipher;
|
|
485
|
+
}
|
|
486
|
+
Object.assign(wrappedCipher, params);
|
|
487
|
+
return wrappedCipher;
|
|
488
|
+
};
|
|
489
|
+
function getOutput(expectedLength, out, onlyAligned = true) {
|
|
490
|
+
if (out === undefined)
|
|
491
|
+
return new Uint8Array(expectedLength);
|
|
492
|
+
abytes(out, expectedLength, "output");
|
|
493
|
+
if (onlyAligned && !isAligned32(out))
|
|
494
|
+
throw new Error("invalid output, must be aligned");
|
|
495
|
+
return out;
|
|
496
|
+
}
|
|
497
|
+
function u64Lengths(dataLength, aadLength, isLE2) {
|
|
498
|
+
anumber(dataLength);
|
|
499
|
+
anumber(aadLength);
|
|
500
|
+
abool(isLE2);
|
|
501
|
+
const num = new Uint8Array(16);
|
|
502
|
+
const view = createView(num);
|
|
503
|
+
view.setBigUint64(0, BigInt(aadLength), isLE2);
|
|
504
|
+
view.setBigUint64(8, BigInt(dataLength), isLE2);
|
|
505
|
+
return num;
|
|
506
|
+
}
|
|
507
|
+
function isAligned32(bytes) {
|
|
508
|
+
return bytes.byteOffset % 4 === 0;
|
|
509
|
+
}
|
|
510
|
+
function copyBytes(bytes) {
|
|
511
|
+
return Uint8Array.from(abytes(bytes));
|
|
512
|
+
}
|
|
513
|
+
function randomBytes(bytesLength = 32) {
|
|
514
|
+
anumber(bytesLength, "bytesLength");
|
|
515
|
+
const cr = typeof globalThis === "object" ? globalThis.crypto : null;
|
|
516
|
+
if (typeof cr?.getRandomValues !== "function")
|
|
517
|
+
throw new Error("crypto.getRandomValues must be defined");
|
|
518
|
+
if (bytesLength > 65536)
|
|
519
|
+
throw new RangeError(`"bytesLength" expected <= 65536, got ${bytesLength}`);
|
|
520
|
+
return cr.getRandomValues(new Uint8Array(bytesLength));
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// node_modules/@noble/ciphers/_polyval.js
|
|
524
|
+
var BLOCK_SIZE = 16;
|
|
525
|
+
var ZEROS16 = /* @__PURE__ */ new Uint8Array(16);
|
|
526
|
+
var ZEROS32 = /* @__PURE__ */ u32(ZEROS16);
|
|
527
|
+
var POLY = 225;
|
|
528
|
+
var mul2 = (s0, s1, s2, s3) => {
|
|
529
|
+
const hiBit = s3 & 1;
|
|
842
530
|
return {
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
await value.withExclusiveTransactionAsync(async (sqlite) => {
|
|
848
|
-
for (const table of [
|
|
849
|
-
"absolute_sync_metadata",
|
|
850
|
-
"absolute_sync_collections",
|
|
851
|
-
"absolute_sync_mutations"
|
|
852
|
-
])
|
|
853
|
-
await sqlite.runAsync(`DELETE FROM ${table} WHERE namespace = ?`, [
|
|
854
|
-
namespace
|
|
855
|
-
]);
|
|
856
|
-
});
|
|
857
|
-
});
|
|
858
|
-
},
|
|
859
|
-
getSchemaStatus: async () => {
|
|
860
|
-
await database();
|
|
861
|
-
if (!schemaStatus)
|
|
862
|
-
throw new Error("Expo Sync schema was not prepared.");
|
|
863
|
-
return { ...schemaStatus };
|
|
864
|
-
},
|
|
865
|
-
transaction
|
|
531
|
+
s3: s2 << 31 | s3 >>> 1,
|
|
532
|
+
s2: s1 << 31 | s2 >>> 1,
|
|
533
|
+
s1: s0 << 31 | s1 >>> 1,
|
|
534
|
+
s0: s0 >>> 1 ^ POLY << 24 & -(hiBit & 1)
|
|
866
535
|
};
|
|
867
536
|
};
|
|
868
|
-
|
|
869
|
-
var
|
|
870
|
-
if (
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
if (typeof value !== "string" || value.length === 0 || value.length > 512)
|
|
876
|
-
throw new TypeError(`Expo Sync bridge ${label} is invalid.`);
|
|
877
|
-
return value;
|
|
878
|
-
};
|
|
879
|
-
var requireCollectionRecord = (value) => {
|
|
880
|
-
const record = requireRecord(value, "collection record");
|
|
881
|
-
if (!Array.isArray(record.rows) || typeof record.version !== "number" || !Number.isSafeInteger(record.version) || record.version < 0)
|
|
882
|
-
throw new TypeError("Expo Sync bridge collection record is invalid.");
|
|
883
|
-
return structuredClone(record);
|
|
884
|
-
};
|
|
885
|
-
var requireMutationRecord = (value) => {
|
|
886
|
-
const record = requireRecord(value, "mutation record");
|
|
887
|
-
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))
|
|
888
|
-
throw new TypeError("Expo Sync bridge mutation record is invalid.");
|
|
889
|
-
return structuredClone(record);
|
|
537
|
+
var swapLE = (n) => (n >>> 0 & 255) << 24 | (n >>> 8 & 255) << 16 | (n >>> 16 & 255) << 8 | n >>> 24 & 255 | 0;
|
|
538
|
+
var estimateWindow = (bytes) => {
|
|
539
|
+
if (bytes > 64 * 1024)
|
|
540
|
+
return 8;
|
|
541
|
+
if (bytes > 1024)
|
|
542
|
+
return 4;
|
|
543
|
+
return 2;
|
|
890
544
|
};
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
let
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
readyResolve = resolve;
|
|
917
|
-
readyReject = reject;
|
|
918
|
-
});
|
|
919
|
-
let finish = () => {
|
|
920
|
-
return;
|
|
921
|
-
};
|
|
922
|
-
const decision = new Promise((resolve) => {
|
|
923
|
-
finish = resolve;
|
|
924
|
-
});
|
|
925
|
-
const complete = store.transaction(namespace, mode, async (transaction2) => {
|
|
926
|
-
readyResolve(transaction2);
|
|
927
|
-
if (!await decision)
|
|
928
|
-
throw rollbackMarker;
|
|
929
|
-
}).catch((error) => {
|
|
930
|
-
readyReject(error);
|
|
931
|
-
if (error !== rollbackMarker)
|
|
932
|
-
throw error;
|
|
933
|
-
});
|
|
934
|
-
const transaction = await ready;
|
|
935
|
-
const timer = setTimeout(() => {
|
|
936
|
-
sessions.delete(id);
|
|
937
|
-
finish(false);
|
|
938
|
-
}, transactionTimeoutMs);
|
|
939
|
-
sessions.set(id, { complete, finish, timer, transaction });
|
|
940
|
-
return id;
|
|
941
|
-
};
|
|
942
|
-
const session = (params) => {
|
|
943
|
-
const id = requireString(params.transactionId, "transaction id");
|
|
944
|
-
const value = sessions.get(id);
|
|
945
|
-
if (!value)
|
|
946
|
-
throw new Error("Expo Sync bridge transaction is closed or unknown.");
|
|
947
|
-
return { id, value };
|
|
948
|
-
};
|
|
949
|
-
const end = async (params) => {
|
|
950
|
-
const { id, value } = session(params);
|
|
951
|
-
if (typeof params.commit !== "boolean")
|
|
952
|
-
throw new TypeError("Expo Sync bridge commit decision is invalid.");
|
|
953
|
-
sessions.delete(id);
|
|
954
|
-
clearTimeout(value.timer);
|
|
955
|
-
value.finish(params.commit);
|
|
956
|
-
await value.complete;
|
|
957
|
-
return null;
|
|
958
|
-
};
|
|
959
|
-
const operation = async (method, params) => {
|
|
960
|
-
const { value } = session(params);
|
|
961
|
-
const transaction = value.transaction;
|
|
962
|
-
if (method === "sync.tx.getInstallationId")
|
|
963
|
-
return await transaction.getInstallationId() ?? null;
|
|
964
|
-
if (method === "sync.tx.setInstallationId") {
|
|
965
|
-
await transaction.setInstallationId(requireString(params.installationId, "installation id"));
|
|
966
|
-
return null;
|
|
545
|
+
|
|
546
|
+
class GHASH {
|
|
547
|
+
blockLen = BLOCK_SIZE;
|
|
548
|
+
outputLen = BLOCK_SIZE;
|
|
549
|
+
s0 = 0;
|
|
550
|
+
s1 = 0;
|
|
551
|
+
s2 = 0;
|
|
552
|
+
s3 = 0;
|
|
553
|
+
finished = false;
|
|
554
|
+
destroyed = false;
|
|
555
|
+
t;
|
|
556
|
+
W;
|
|
557
|
+
windowSize;
|
|
558
|
+
constructor(key, expectedLength) {
|
|
559
|
+
abytes(key, 16, "key");
|
|
560
|
+
key = copyBytes(key);
|
|
561
|
+
const kView = createView(key);
|
|
562
|
+
let k0 = kView.getUint32(0, false);
|
|
563
|
+
let k1 = kView.getUint32(4, false);
|
|
564
|
+
let k2 = kView.getUint32(8, false);
|
|
565
|
+
let k3 = kView.getUint32(12, false);
|
|
566
|
+
const doubles = [];
|
|
567
|
+
for (let i = 0;i < 128; i++) {
|
|
568
|
+
doubles.push({ s0: swapLE(k0), s1: swapLE(k1), s2: swapLE(k2), s3: swapLE(k3) });
|
|
569
|
+
({ s0: k0, s1: k1, s2: k2, s3: k3 } = mul2(k0, k1, k2, k3));
|
|
967
570
|
}
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
571
|
+
const W = estimateWindow(expectedLength || 1024);
|
|
572
|
+
if (![1, 2, 4, 8].includes(W))
|
|
573
|
+
throw new Error("ghash: invalid window size, expected 2, 4 or 8");
|
|
574
|
+
this.W = W;
|
|
575
|
+
const bits = 128;
|
|
576
|
+
const windows = bits / W;
|
|
577
|
+
const windowSize = this.windowSize = 2 ** W;
|
|
578
|
+
const items = [];
|
|
579
|
+
for (let w = 0;w < windows; w++) {
|
|
580
|
+
for (let byte = 0;byte < windowSize; byte++) {
|
|
581
|
+
let s0 = 0, s1 = 0, s2 = 0, s3 = 0;
|
|
582
|
+
for (let j = 0;j < W; j++) {
|
|
583
|
+
const bit = byte >>> W - j - 1 & 1;
|
|
584
|
+
if (!bit)
|
|
585
|
+
continue;
|
|
586
|
+
const { s0: d0, s1: d1, s2: d2, s3: d3 } = doubles[W * w + j];
|
|
587
|
+
s0 ^= d0, s1 ^= d1, s2 ^= d2, s3 ^= d3;
|
|
588
|
+
}
|
|
589
|
+
items.push({ s0, s1, s2, s3 });
|
|
590
|
+
}
|
|
975
591
|
}
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
592
|
+
this.t = items;
|
|
593
|
+
}
|
|
594
|
+
_updateBlock(s0, s1, s2, s3) {
|
|
595
|
+
s0 ^= this.s0, s1 ^= this.s1, s2 ^= this.s2, s3 ^= this.s3;
|
|
596
|
+
const { W, t, windowSize } = this;
|
|
597
|
+
let o0 = 0, o1 = 0, o2 = 0, o3 = 0;
|
|
598
|
+
const mask = (1 << W) - 1;
|
|
599
|
+
let w = 0;
|
|
600
|
+
for (const num of [s0, s1, s2, s3]) {
|
|
601
|
+
for (let bytePos = 0;bytePos < 4; bytePos++) {
|
|
602
|
+
const byte = num >>> 8 * bytePos & 255;
|
|
603
|
+
for (let bitPos = 8 / W - 1;bitPos >= 0; bitPos--) {
|
|
604
|
+
const bit = byte >>> W * bitPos & mask;
|
|
605
|
+
const { s0: e0, s1: e1, s2: e2, s3: e3 } = t[w * windowSize + bit];
|
|
606
|
+
o0 ^= e0, o1 ^= e1, o2 ^= e2, o3 ^= e3;
|
|
607
|
+
w += 1;
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
this.s0 = o0;
|
|
612
|
+
this.s1 = o1;
|
|
613
|
+
this.s2 = o2;
|
|
614
|
+
this.s3 = o3;
|
|
615
|
+
}
|
|
616
|
+
update(data) {
|
|
617
|
+
aexists(this);
|
|
618
|
+
abytes(data);
|
|
619
|
+
data = copyBytes(data);
|
|
620
|
+
const b32 = u32(data);
|
|
621
|
+
const blocks = Math.floor(data.length / BLOCK_SIZE);
|
|
622
|
+
const left = data.length % BLOCK_SIZE;
|
|
623
|
+
for (let i = 0;i < blocks; i++) {
|
|
624
|
+
this._updateBlock(swap8IfBE(b32[i * 4 + 0]), swap8IfBE(b32[i * 4 + 1]), swap8IfBE(b32[i * 4 + 2]), swap8IfBE(b32[i * 4 + 3]));
|
|
625
|
+
}
|
|
626
|
+
if (left) {
|
|
627
|
+
ZEROS16.set(data.subarray(blocks * BLOCK_SIZE));
|
|
628
|
+
this._updateBlock(swap8IfBE(ZEROS32[0]), swap8IfBE(ZEROS32[1]), swap8IfBE(ZEROS32[2]), swap8IfBE(ZEROS32[3]));
|
|
629
|
+
clean(ZEROS32);
|
|
630
|
+
}
|
|
631
|
+
return this;
|
|
632
|
+
}
|
|
633
|
+
destroy() {
|
|
634
|
+
this.destroyed = true;
|
|
635
|
+
const { t } = this;
|
|
636
|
+
for (const elm of t) {
|
|
637
|
+
elm.s0 = 0, elm.s1 = 0, elm.s2 = 0, elm.s3 = 0;
|
|
979
638
|
}
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
639
|
+
}
|
|
640
|
+
digestInto(out) {
|
|
641
|
+
aexists(this);
|
|
642
|
+
aoutput32(out, this);
|
|
643
|
+
this.finished = true;
|
|
644
|
+
const { s0, s1, s2, s3 } = this;
|
|
645
|
+
const o32 = u32(out);
|
|
646
|
+
o32[0] = s0;
|
|
647
|
+
o32[1] = s1;
|
|
648
|
+
o32[2] = s2;
|
|
649
|
+
o32[3] = s3;
|
|
650
|
+
if (!isLE)
|
|
651
|
+
swap32IfBE(o32.subarray(0, BLOCK_SIZE / 4));
|
|
652
|
+
}
|
|
653
|
+
digest() {
|
|
654
|
+
const res = new Uint8Array(BLOCK_SIZE);
|
|
655
|
+
this.digestInto(res);
|
|
656
|
+
this.destroy();
|
|
657
|
+
return res;
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
var ghash = /* @__PURE__ */ wrapMacConstructor(16, (key, expectedLength) => new GHASH(key, expectedLength), (msg) => [msg.length]);
|
|
661
|
+
|
|
662
|
+
// node_modules/@noble/ciphers/aes.js
|
|
663
|
+
var BLOCK_SIZE2 = 16;
|
|
664
|
+
var BLOCK_SIZE32 = 4;
|
|
665
|
+
var EMPTY_BLOCK = /* @__PURE__ */ new Uint8Array(BLOCK_SIZE2);
|
|
666
|
+
var POLY2 = 283;
|
|
667
|
+
function validateKeyLength(key) {
|
|
668
|
+
if (![16, 24, 32].includes(key.length))
|
|
669
|
+
throw new Error('"aes key" expected Uint8Array of length 16/24/32, got length=' + key.length);
|
|
670
|
+
}
|
|
671
|
+
function mul22(n) {
|
|
672
|
+
return n << 1 ^ POLY2 & -(n >> 7);
|
|
673
|
+
}
|
|
674
|
+
function mul(a, b) {
|
|
675
|
+
let res = 0;
|
|
676
|
+
for (;b > 0; b >>= 1) {
|
|
677
|
+
res ^= a & -(b & 1);
|
|
678
|
+
a = mul22(a);
|
|
679
|
+
}
|
|
680
|
+
return res;
|
|
681
|
+
}
|
|
682
|
+
var sbox = /* @__PURE__ */ (() => {
|
|
683
|
+
const t = new Uint8Array(256);
|
|
684
|
+
for (let i = 0, x = 1;i < 256; i++, x ^= mul22(x))
|
|
685
|
+
t[i] = x;
|
|
686
|
+
const box = new Uint8Array(256);
|
|
687
|
+
box[0] = 99;
|
|
688
|
+
for (let i = 0;i < 255; i++) {
|
|
689
|
+
let x = t[255 - i];
|
|
690
|
+
x |= x << 8;
|
|
691
|
+
box[t[i]] = (x ^ x >> 4 ^ x >> 5 ^ x >> 6 ^ x >> 7 ^ 99) & 255;
|
|
692
|
+
}
|
|
693
|
+
clean(t);
|
|
694
|
+
return box;
|
|
695
|
+
})();
|
|
696
|
+
var rotr32_8 = (n) => n << 24 | n >>> 8;
|
|
697
|
+
var rotl32_8 = (n) => n << 8 | n >>> 24;
|
|
698
|
+
function genTtable(sbox2, fn) {
|
|
699
|
+
if (sbox2.length !== 256)
|
|
700
|
+
throw new Error("wrong sbox length");
|
|
701
|
+
const T0 = new Uint32Array(256).map((_, j) => fn(sbox2[j]));
|
|
702
|
+
const T1 = T0.map(rotl32_8);
|
|
703
|
+
const T2 = T1.map(rotl32_8);
|
|
704
|
+
const T3 = T2.map(rotl32_8);
|
|
705
|
+
const T01 = new Uint32Array(256 * 256);
|
|
706
|
+
const T23 = new Uint32Array(256 * 256);
|
|
707
|
+
const sbox22 = new Uint16Array(256 * 256);
|
|
708
|
+
for (let i = 0;i < 256; i++) {
|
|
709
|
+
for (let j = 0;j < 256; j++) {
|
|
710
|
+
const idx = i * 256 + j;
|
|
711
|
+
T01[idx] = T0[i] ^ T1[j];
|
|
712
|
+
T23[idx] = T2[i] ^ T3[j];
|
|
713
|
+
sbox22[idx] = sbox2[i] << 8 | sbox2[j];
|
|
987
714
|
}
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
715
|
+
}
|
|
716
|
+
return { sbox: sbox2, sbox2: sbox22, T0, T1, T2, T3, T01, T23 };
|
|
717
|
+
}
|
|
718
|
+
var tableEncoding = /* @__PURE__ */ genTtable(sbox, (s) => mul(s, 3) << 24 | s << 16 | s << 8 | mul(s, 2));
|
|
719
|
+
var xPowers = /* @__PURE__ */ (() => {
|
|
720
|
+
const p = new Uint8Array(16);
|
|
721
|
+
for (let i = 0, x = 1;i < 16; i++, x = mul22(x))
|
|
722
|
+
p[i] = x;
|
|
723
|
+
return p;
|
|
724
|
+
})();
|
|
725
|
+
function expandKeyLE(key) {
|
|
726
|
+
abytes(key);
|
|
727
|
+
const len = key.length;
|
|
728
|
+
validateKeyLength(key);
|
|
729
|
+
const { sbox2 } = tableEncoding;
|
|
730
|
+
const toClean = [];
|
|
731
|
+
if (!isLE || !isAligned32(key))
|
|
732
|
+
toClean.push(key = copyBytes(key));
|
|
733
|
+
const k32 = swap32IfBE(u32(key));
|
|
734
|
+
const Nk = k32.length;
|
|
735
|
+
const subByte = (n) => applySbox(sbox2, n, n, n, n);
|
|
736
|
+
const xk = new Uint32Array(len + 28);
|
|
737
|
+
xk.set(k32);
|
|
738
|
+
for (let i = Nk;i < xk.length; i++) {
|
|
739
|
+
let t = xk[i - 1];
|
|
740
|
+
if (i % Nk === 0)
|
|
741
|
+
t = subByte(rotr32_8(t)) ^ xPowers[i / Nk - 1];
|
|
742
|
+
else if (Nk > 6 && i % Nk === 4)
|
|
743
|
+
t = subByte(t);
|
|
744
|
+
xk[i] = xk[i - Nk] ^ t;
|
|
745
|
+
}
|
|
746
|
+
clean(...toClean);
|
|
747
|
+
return xk;
|
|
748
|
+
}
|
|
749
|
+
function apply0123(T01, T23, s0, s1, s2, s3) {
|
|
750
|
+
return T01[s0 << 8 & 65280 | s1 >>> 8 & 255] ^ T23[s2 >>> 8 & 65280 | s3 >>> 24 & 255];
|
|
751
|
+
}
|
|
752
|
+
function applySbox(sbox2, s0, s1, s2, s3) {
|
|
753
|
+
return sbox2[s0 & 255 | s1 & 65280] | sbox2[s2 >>> 16 & 255 | s3 >>> 16 & 65280] << 16;
|
|
754
|
+
}
|
|
755
|
+
function encrypt(xk, s0, s1, s2, s3) {
|
|
756
|
+
const { sbox2, T01, T23 } = tableEncoding;
|
|
757
|
+
let k = 0;
|
|
758
|
+
s0 ^= xk[k++], s1 ^= xk[k++], s2 ^= xk[k++], s3 ^= xk[k++];
|
|
759
|
+
const rounds = xk.length / 4 - 2;
|
|
760
|
+
for (let i = 0;i < rounds; i++) {
|
|
761
|
+
const t02 = xk[k++] ^ apply0123(T01, T23, s0, s1, s2, s3);
|
|
762
|
+
const t12 = xk[k++] ^ apply0123(T01, T23, s1, s2, s3, s0);
|
|
763
|
+
const t22 = xk[k++] ^ apply0123(T01, T23, s2, s3, s0, s1);
|
|
764
|
+
const t32 = xk[k++] ^ apply0123(T01, T23, s3, s0, s1, s2);
|
|
765
|
+
s0 = t02, s1 = t12, s2 = t22, s3 = t32;
|
|
766
|
+
}
|
|
767
|
+
const t0 = xk[k++] ^ applySbox(sbox2, s0, s1, s2, s3);
|
|
768
|
+
const t1 = xk[k++] ^ applySbox(sbox2, s1, s2, s3, s0);
|
|
769
|
+
const t2 = xk[k++] ^ applySbox(sbox2, s2, s3, s0, s1);
|
|
770
|
+
const t3 = xk[k++] ^ applySbox(sbox2, s3, s0, s1, s2);
|
|
771
|
+
return { s0: t0, s1: t1, s2: t2, s3: t3 };
|
|
772
|
+
}
|
|
773
|
+
function ctr32(xk, isLE2, nonce, src, dst) {
|
|
774
|
+
abytes(nonce, BLOCK_SIZE2, "nonce");
|
|
775
|
+
abytes(src);
|
|
776
|
+
dst = getOutput(src.length, dst);
|
|
777
|
+
const ctr = nonce;
|
|
778
|
+
const c32 = u32(ctr);
|
|
779
|
+
const view = createView(ctr);
|
|
780
|
+
const src32 = u32(src);
|
|
781
|
+
const dst32 = u32(dst);
|
|
782
|
+
const ctrPos = isLE2 ? 0 : 12;
|
|
783
|
+
const srcLen = src.length;
|
|
784
|
+
let ctrNum = view.getUint32(ctrPos, isLE2);
|
|
785
|
+
for (let i = 0;i + 4 <= src32.length; i += 4) {
|
|
786
|
+
const { s0, s1, s2, s3 } = encrypt(xk, swap8IfBE(c32[0]), swap8IfBE(c32[1]), swap8IfBE(c32[2]), swap8IfBE(c32[3]));
|
|
787
|
+
dst32[i + 0] = src32[i + 0] ^ swap8IfBE(s0);
|
|
788
|
+
dst32[i + 1] = src32[i + 1] ^ swap8IfBE(s1);
|
|
789
|
+
dst32[i + 2] = src32[i + 2] ^ swap8IfBE(s2);
|
|
790
|
+
dst32[i + 3] = src32[i + 3] ^ swap8IfBE(s3);
|
|
791
|
+
ctrNum = ctrNum + 1 >>> 0;
|
|
792
|
+
view.setUint32(ctrPos, ctrNum, isLE2);
|
|
793
|
+
}
|
|
794
|
+
const start = BLOCK_SIZE2 * Math.floor(src32.length / BLOCK_SIZE32);
|
|
795
|
+
if (start < srcLen) {
|
|
796
|
+
const { s0, s1, s2, s3 } = encrypt(xk, swap8IfBE(c32[0]), swap8IfBE(c32[1]), swap8IfBE(c32[2]), swap8IfBE(c32[3]));
|
|
797
|
+
const b32 = new Uint32Array([s0, s1, s2, s3]);
|
|
798
|
+
swap32IfBE(b32);
|
|
799
|
+
const buf = u8(b32);
|
|
800
|
+
for (let i = start, pos = 0;i < srcLen; i++, pos++)
|
|
801
|
+
dst[i] = src[i] ^ buf[pos];
|
|
802
|
+
clean(b32);
|
|
803
|
+
}
|
|
804
|
+
return dst;
|
|
805
|
+
}
|
|
806
|
+
function computeTag(fn, isLE2, key, data, AAD) {
|
|
807
|
+
const aadLength = AAD ? AAD.length : 0;
|
|
808
|
+
const h = fn.create(key, data.length + aadLength);
|
|
809
|
+
if (AAD)
|
|
810
|
+
h.update(AAD);
|
|
811
|
+
const num = u64Lengths(8 * data.length, 8 * aadLength, isLE2);
|
|
812
|
+
h.update(data);
|
|
813
|
+
h.update(num);
|
|
814
|
+
const res = h.digest();
|
|
815
|
+
clean(num);
|
|
816
|
+
return res;
|
|
817
|
+
}
|
|
818
|
+
var gcm = /* @__PURE__ */ wrapCipher({ blockSize: 16, nonceLength: 12, tagLength: 16, withAAD: true, varSizeNonce: true }, function aesgcm(key, nonce, AAD) {
|
|
819
|
+
if (nonce.length < 8)
|
|
820
|
+
throw new Error("aes/gcm: invalid nonce length");
|
|
821
|
+
const tagLength = 16;
|
|
822
|
+
function _computeTag(authKey, tagMask, data) {
|
|
823
|
+
const tag = computeTag(ghash, false, authKey, data, AAD);
|
|
824
|
+
for (let i = 0;i < tagMask.length; i++)
|
|
825
|
+
tag[i] ^= tagMask[i];
|
|
826
|
+
return tag;
|
|
827
|
+
}
|
|
828
|
+
function deriveKeys() {
|
|
829
|
+
const xk = expandKeyLE(key);
|
|
830
|
+
const authKey = EMPTY_BLOCK.slice();
|
|
831
|
+
const counter = EMPTY_BLOCK.slice();
|
|
832
|
+
ctr32(xk, false, counter, counter, authKey);
|
|
833
|
+
if (nonce.length === 12) {
|
|
834
|
+
counter.set(nonce);
|
|
835
|
+
} else {
|
|
836
|
+
const nonceLen = EMPTY_BLOCK.slice();
|
|
837
|
+
const view = createView(nonceLen);
|
|
838
|
+
view.setBigUint64(8, BigInt(nonce.length * 8), false);
|
|
839
|
+
const g = ghash.create(authKey).update(nonce).update(nonceLen);
|
|
840
|
+
g.digestInto(counter);
|
|
841
|
+
g.destroy();
|
|
991
842
|
}
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
};
|
|
843
|
+
const tagMask = ctr32(xk, false, counter, EMPTY_BLOCK);
|
|
844
|
+
return { xk, authKey, counter, tagMask };
|
|
845
|
+
}
|
|
996
846
|
return {
|
|
997
|
-
|
|
998
|
-
const
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
847
|
+
encrypt(plaintext) {
|
|
848
|
+
const { xk, authKey, counter, tagMask } = deriveKeys();
|
|
849
|
+
const out = new Uint8Array(plaintext.length + tagLength);
|
|
850
|
+
const toClean = [xk, authKey, counter, tagMask];
|
|
851
|
+
if (!isAligned32(plaintext))
|
|
852
|
+
toClean.push(plaintext = copyBytes(plaintext));
|
|
853
|
+
ctr32(xk, false, counter, plaintext, out.subarray(0, plaintext.length));
|
|
854
|
+
const tag = _computeTag(authKey, tagMask, out.subarray(0, out.length - tagLength));
|
|
855
|
+
toClean.push(tag);
|
|
856
|
+
out.set(tag, plaintext.length);
|
|
857
|
+
clean(...toClean);
|
|
858
|
+
return out;
|
|
1005
859
|
},
|
|
1006
|
-
|
|
1007
|
-
const
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
if (
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
await store.deleteNamespace?.(namespace);
|
|
1019
|
-
return null;
|
|
860
|
+
decrypt(ciphertext) {
|
|
861
|
+
const { xk, authKey, counter, tagMask } = deriveKeys();
|
|
862
|
+
const toClean = [xk, authKey, tagMask, counter];
|
|
863
|
+
if (!isAligned32(ciphertext))
|
|
864
|
+
toClean.push(ciphertext = copyBytes(ciphertext));
|
|
865
|
+
const data = ciphertext.subarray(0, -tagLength);
|
|
866
|
+
const passedTag = ciphertext.subarray(-tagLength);
|
|
867
|
+
const tag = _computeTag(authKey, tagMask, data);
|
|
868
|
+
toClean.push(tag);
|
|
869
|
+
if (!equalBytes(tag, passedTag)) {
|
|
870
|
+
clean(...toClean);
|
|
871
|
+
throw new Error("aes-gcm: invalid tag");
|
|
1020
872
|
}
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
873
|
+
const out = ctr32(xk, false, counter, data);
|
|
874
|
+
clean(...toClean);
|
|
875
|
+
return out;
|
|
1024
876
|
}
|
|
1025
877
|
};
|
|
878
|
+
});
|
|
879
|
+
|
|
880
|
+
// src/index.ts
|
|
881
|
+
import * as BackgroundTask from "expo-background-task";
|
|
882
|
+
import * as Network from "expo-network";
|
|
883
|
+
import * as SecureStore from "expo-secure-store";
|
|
884
|
+
import * as TaskManager from "expo-task-manager";
|
|
885
|
+
import { AppState } from "react-native";
|
|
886
|
+
|
|
887
|
+
// src/store.ts
|
|
888
|
+
import {
|
|
889
|
+
createSyncLocalSchemaStatus,
|
|
890
|
+
migrateSyncLocalCollectionRecord,
|
|
891
|
+
migrateSyncLocalMutationRecord,
|
|
892
|
+
resolveSyncLocalDataPolicy,
|
|
893
|
+
resolveSyncLocalSchemaComponents,
|
|
894
|
+
runSyncLocalPolicyTransaction
|
|
895
|
+
} from "@absolutejs/sync/client";
|
|
896
|
+
import * as SQLite from "expo-sqlite";
|
|
897
|
+
var SCHEMA = [
|
|
898
|
+
`CREATE TABLE IF NOT EXISTS absolute_sync_schema (
|
|
899
|
+
singleton_id INTEGER PRIMARY KEY NOT NULL CHECK (singleton_id = 1),
|
|
900
|
+
logical_version INTEGER NOT NULL
|
|
901
|
+
)`,
|
|
902
|
+
`CREATE TABLE IF NOT EXISTS absolute_sync_schema_components (
|
|
903
|
+
component_id TEXT PRIMARY KEY NOT NULL,
|
|
904
|
+
logical_version INTEGER NOT NULL
|
|
905
|
+
)`,
|
|
906
|
+
`CREATE TABLE IF NOT EXISTS absolute_sync_metadata (
|
|
907
|
+
namespace TEXT PRIMARY KEY NOT NULL,
|
|
908
|
+
installation_id TEXT NOT NULL
|
|
909
|
+
)`,
|
|
910
|
+
`CREATE TABLE IF NOT EXISTS absolute_sync_collections (
|
|
911
|
+
namespace TEXT NOT NULL,
|
|
912
|
+
collection_key TEXT NOT NULL,
|
|
913
|
+
record_json TEXT NOT NULL,
|
|
914
|
+
PRIMARY KEY (namespace, collection_key)
|
|
915
|
+
)`,
|
|
916
|
+
`CREATE TABLE IF NOT EXISTS absolute_sync_mutations (
|
|
917
|
+
namespace TEXT NOT NULL,
|
|
918
|
+
operation_id TEXT NOT NULL,
|
|
919
|
+
created_at INTEGER NOT NULL,
|
|
920
|
+
record_json TEXT NOT NULL,
|
|
921
|
+
PRIMARY KEY (namespace, operation_id)
|
|
922
|
+
)`,
|
|
923
|
+
`CREATE INDEX IF NOT EXISTS absolute_sync_mutations_order
|
|
924
|
+
ON absolute_sync_mutations (namespace, created_at, operation_id)`
|
|
925
|
+
];
|
|
926
|
+
var executor = (value) => ({
|
|
927
|
+
execAsync: (source) => value.execAsync(source),
|
|
928
|
+
getAllAsync: (source, params = []) => value.getAllAsync(source, [...params]),
|
|
929
|
+
getFirstAsync: (source, params = []) => value.getFirstAsync(source, [...params]),
|
|
930
|
+
runAsync: (source, params = []) => value.runAsync(source, [...params])
|
|
931
|
+
});
|
|
932
|
+
var defaultDatabase = async (databaseName) => {
|
|
933
|
+
const database = await SQLite.openDatabaseAsync(databaseName);
|
|
934
|
+
const direct = executor(database);
|
|
935
|
+
return {
|
|
936
|
+
...direct,
|
|
937
|
+
withExclusiveTransactionAsync: (run) => database.withExclusiveTransactionAsync((transaction) => run(executor(transaction)))
|
|
938
|
+
};
|
|
1026
939
|
};
|
|
1027
|
-
var
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
940
|
+
var parseRecord = (value, label, context, protector) => {
|
|
941
|
+
if (value === undefined || value === null)
|
|
942
|
+
return;
|
|
943
|
+
if (typeof value !== "string")
|
|
944
|
+
throw new Error(`Expo Sync SQLite returned invalid ${label} JSON.`);
|
|
945
|
+
try {
|
|
946
|
+
const parsed = JSON.parse(value);
|
|
947
|
+
if (typeof parsed === "object" && parsed !== null && "__absoluteSyncProtected" in parsed) {
|
|
948
|
+
const envelope = parsed.__absoluteSyncProtected;
|
|
949
|
+
if (!context || !protector || protector.id !== envelope.protector)
|
|
950
|
+
throw new Error(`Expo Sync ${label} requires unavailable protection provider "${envelope.protector}".`);
|
|
951
|
+
return JSON.parse(protector.open(envelope.value, {
|
|
952
|
+
...context,
|
|
953
|
+
name: envelope.name
|
|
954
|
+
}));
|
|
955
|
+
}
|
|
956
|
+
return parsed;
|
|
957
|
+
} catch (cause) {
|
|
958
|
+
throw new Error(`Expo Sync SQLite could not parse ${label} JSON.`, {
|
|
959
|
+
cause
|
|
960
|
+
});
|
|
961
|
+
}
|
|
1036
962
|
};
|
|
1037
|
-
var
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
963
|
+
var serializeRecord = (value, context, protector) => protector ? JSON.stringify({
|
|
964
|
+
__absoluteSyncProtected: {
|
|
965
|
+
name: context.name,
|
|
966
|
+
protector: protector.id,
|
|
967
|
+
value: protector.seal(JSON.stringify(value), context)
|
|
968
|
+
}
|
|
969
|
+
}) : JSON.stringify(value);
|
|
970
|
+
var requireNamespace = (namespace) => {
|
|
971
|
+
if (namespace.length === 0)
|
|
972
|
+
throw new TypeError("Sync local-store namespace must not be empty.");
|
|
1044
973
|
};
|
|
1045
|
-
var
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
return Uint8Array.from(atob(value), (character) => character.charCodeAt(0));
|
|
974
|
+
var rowString = (row, field) => {
|
|
975
|
+
const value = row?.[field];
|
|
976
|
+
return typeof value === "string" ? value : undefined;
|
|
1049
977
|
};
|
|
1050
|
-
var
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
978
|
+
var prepareSchema = async (database, storageSchema, protector) => {
|
|
979
|
+
let status;
|
|
980
|
+
await database.withExclusiveTransactionAsync(async (transaction) => {
|
|
981
|
+
const legacy = await transaction.getFirstAsync("SELECT logical_version FROM absolute_sync_schema WHERE singleton_id = 1 LIMIT 1");
|
|
982
|
+
const componentRows = await transaction.getAllAsync("SELECT component_id, logical_version FROM absolute_sync_schema_components ORDER BY component_id");
|
|
983
|
+
const storedVersions = {};
|
|
984
|
+
for (const row of componentRows) {
|
|
985
|
+
if (typeof row.component_id !== "string" || typeof row.logical_version !== "number")
|
|
986
|
+
throw new Error("Expo Sync SQLite returned an invalid schema component ledger.");
|
|
987
|
+
storedVersions[row.component_id] = row.logical_version;
|
|
988
|
+
}
|
|
989
|
+
if (storedVersions["@absolutejs/app"] === undefined && typeof legacy?.logical_version === "number")
|
|
990
|
+
storedVersions["@absolutejs/app"] = legacy.logical_version;
|
|
991
|
+
const resolved = resolveSyncLocalSchemaComponents(storedVersions, storageSchema);
|
|
992
|
+
const steps = resolved.components.flatMap((component) => component.steps);
|
|
993
|
+
if (steps.length > 0) {
|
|
994
|
+
const collections = await transaction.getAllAsync("SELECT namespace, collection_key, record_json FROM absolute_sync_collections ORDER BY namespace, collection_key");
|
|
995
|
+
for (const row of collections) {
|
|
996
|
+
const namespace = row.namespace;
|
|
997
|
+
const key = row.collection_key;
|
|
998
|
+
if (typeof namespace !== "string" || typeof key !== "string")
|
|
999
|
+
throw new Error("Expo Sync SQLite returned an invalid collection identity.");
|
|
1000
|
+
const record = parseRecord(row.record_json, "collection", { kind: "collection", namespace }, protector);
|
|
1001
|
+
if (!record)
|
|
1002
|
+
throw new Error("Expo Sync SQLite returned a missing collection record.");
|
|
1003
|
+
const migrated = migrateSyncLocalCollectionRecord(record, { key, namespace }, steps);
|
|
1004
|
+
if (migrated === null)
|
|
1005
|
+
await transaction.runAsync("DELETE FROM absolute_sync_collections WHERE namespace = ? AND collection_key = ?", [namespace, key]);
|
|
1006
|
+
else
|
|
1007
|
+
await transaction.runAsync("UPDATE absolute_sync_collections SET record_json = ? WHERE namespace = ? AND collection_key = ?", [
|
|
1008
|
+
serializeRecord(migrated, {
|
|
1009
|
+
kind: "collection",
|
|
1010
|
+
name: migrated.collection ?? key,
|
|
1011
|
+
namespace
|
|
1012
|
+
}, protector),
|
|
1013
|
+
namespace,
|
|
1014
|
+
key
|
|
1015
|
+
]);
|
|
1085
1016
|
}
|
|
1086
|
-
|
|
1017
|
+
const mutations = await transaction.getAllAsync("SELECT namespace, operation_id, record_json FROM absolute_sync_mutations ORDER BY namespace, operation_id");
|
|
1018
|
+
for (const row of mutations) {
|
|
1019
|
+
const namespace = row.namespace;
|
|
1020
|
+
const operationId = row.operation_id;
|
|
1021
|
+
if (typeof namespace !== "string" || typeof operationId !== "string")
|
|
1022
|
+
throw new Error("Expo Sync SQLite returned an invalid mutation identity.");
|
|
1023
|
+
const record = parseRecord(row.record_json, "mutation", { kind: "mutation", namespace }, protector);
|
|
1024
|
+
if (!record)
|
|
1025
|
+
throw new Error("Expo Sync SQLite returned a missing mutation record.");
|
|
1026
|
+
const migrated = migrateSyncLocalMutationRecord(record, { key: operationId, namespace }, steps);
|
|
1027
|
+
if (migrated === null)
|
|
1028
|
+
await transaction.runAsync("DELETE FROM absolute_sync_mutations WHERE namespace = ? AND operation_id = ?", [namespace, operationId]);
|
|
1029
|
+
else
|
|
1030
|
+
await transaction.runAsync("UPDATE absolute_sync_mutations SET created_at = ?, record_json = ? WHERE namespace = ? AND operation_id = ?", [
|
|
1031
|
+
migrated.createdAt,
|
|
1032
|
+
serializeRecord(migrated, {
|
|
1033
|
+
kind: "mutation",
|
|
1034
|
+
name: migrated.name,
|
|
1035
|
+
namespace
|
|
1036
|
+
}, protector),
|
|
1037
|
+
namespace,
|
|
1038
|
+
operationId
|
|
1039
|
+
]);
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
for (const component of resolved.components)
|
|
1043
|
+
await transaction.runAsync("INSERT INTO absolute_sync_schema_components (component_id, logical_version) VALUES (?, ?) ON CONFLICT(component_id) DO UPDATE SET logical_version = excluded.logical_version", [component.id, component.targetVersion]);
|
|
1044
|
+
const app = resolved.components.find((component) => component.id === "@absolutejs/app");
|
|
1045
|
+
if (app)
|
|
1046
|
+
await transaction.runAsync("INSERT INTO absolute_sync_schema (singleton_id, logical_version) VALUES (1, ?) ON CONFLICT(singleton_id) DO UPDATE SET logical_version = excluded.logical_version", [app.targetVersion]);
|
|
1047
|
+
status = createSyncLocalSchemaStatus(resolved.components, resolved.orphanedComponents, "components" in storageSchema);
|
|
1048
|
+
});
|
|
1049
|
+
if (!status)
|
|
1050
|
+
throw new Error("Expo Sync schema transaction did not run.");
|
|
1051
|
+
return status;
|
|
1052
|
+
};
|
|
1053
|
+
var createExpoSyncLocalStore = ({
|
|
1054
|
+
databaseName = "absolutejs-sync-local-v1.db",
|
|
1055
|
+
database: createDatabase = () => defaultDatabase(databaseName),
|
|
1056
|
+
storageSchema = { version: 1 },
|
|
1057
|
+
protection,
|
|
1058
|
+
now = Date.now
|
|
1059
|
+
} = {}) => {
|
|
1060
|
+
if (!/^[A-Za-z0-9._-]{1,120}$/u.test(databaseName))
|
|
1061
|
+
throw new TypeError("Expo Sync databaseName is invalid.");
|
|
1062
|
+
const localData = resolveSyncLocalDataPolicy(storageSchema);
|
|
1063
|
+
let protectorPromise;
|
|
1064
|
+
const prepareProtector = () => protectorPromise ??= protection?.prepare();
|
|
1065
|
+
let schemaStatus;
|
|
1066
|
+
let databasePromise;
|
|
1067
|
+
const database = () => {
|
|
1068
|
+
databasePromise ??= Promise.all([
|
|
1069
|
+
Promise.resolve(createDatabase()),
|
|
1070
|
+
prepareProtector()
|
|
1071
|
+
]).then(async ([value, protector]) => {
|
|
1072
|
+
await value.execAsync("PRAGMA journal_mode = WAL");
|
|
1073
|
+
for (const statement of SCHEMA)
|
|
1074
|
+
await value.execAsync(statement);
|
|
1075
|
+
schemaStatus = await prepareSchema(value, storageSchema, protector);
|
|
1076
|
+
return value;
|
|
1077
|
+
});
|
|
1078
|
+
return databasePromise;
|
|
1087
1079
|
};
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
emit({ socketId: id, type: "error" });
|
|
1092
|
-
close(id, 1009, "Sync frame is too large");
|
|
1080
|
+
let tail = Promise.resolve();
|
|
1081
|
+
const locked = async (run) => {
|
|
1082
|
+
let release = () => {
|
|
1093
1083
|
return;
|
|
1084
|
+
};
|
|
1085
|
+
const previous = tail;
|
|
1086
|
+
tail = new Promise((resolve) => {
|
|
1087
|
+
release = resolve;
|
|
1088
|
+
});
|
|
1089
|
+
await previous;
|
|
1090
|
+
try {
|
|
1091
|
+
return await run();
|
|
1092
|
+
} finally {
|
|
1093
|
+
release();
|
|
1094
1094
|
}
|
|
1095
|
-
const total = Math.max(1, Math.ceil(bytes.byteLength / SOCKET_CHUNK_BYTES));
|
|
1096
|
-
const messageId = `native_${(messageSequence += 1).toString(36)}`;
|
|
1097
|
-
for (let index = 0;index < total; index += 1)
|
|
1098
|
-
emit({
|
|
1099
|
-
data: encodeBase64(bytes.slice(index * SOCKET_CHUNK_BYTES, Math.min(bytes.byteLength, (index + 1) * SOCKET_CHUNK_BYTES))),
|
|
1100
|
-
index,
|
|
1101
|
-
messageId,
|
|
1102
|
-
socketId: id,
|
|
1103
|
-
total,
|
|
1104
|
-
type: "message-chunk"
|
|
1105
|
-
});
|
|
1106
1095
|
};
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
if (sockets.size >= maxSockets)
|
|
1119
|
-
throw new Error("Expo Sync bridge socket limit exceeded.");
|
|
1120
|
-
const url = new URL(requireString(params.url, "socket URL"));
|
|
1121
|
-
if (url.protocol !== "wss:" || url.username || url.password || websocketOrigin(url) !== origin.origin)
|
|
1122
|
-
throw new Error("Expo Sync socket must use WSS on the configured production origin.");
|
|
1123
|
-
const socket = new webSocketImpl(ticketSocketUrl(url));
|
|
1124
|
-
sockets.set(id, socket);
|
|
1125
|
-
socket.onopen = () => {
|
|
1126
|
-
socketTicket(origin.origin).then((ticket) => {
|
|
1127
|
-
if (sockets.get(id) !== socket)
|
|
1128
|
-
return;
|
|
1129
|
-
socket.send(JSON.stringify({ ticket, type: "authenticate" }));
|
|
1130
|
-
emit({ socketId: id, type: "open" });
|
|
1131
|
-
}).catch(() => {
|
|
1132
|
-
if (sockets.get(id) !== socket)
|
|
1133
|
-
return;
|
|
1134
|
-
emit({ socketId: id, type: "error" });
|
|
1135
|
-
close(id, 1008, "Authentication failed");
|
|
1136
|
-
});
|
|
1096
|
+
const transaction = async (namespace, mode, run) => {
|
|
1097
|
+
requireNamespace(namespace);
|
|
1098
|
+
return locked(async () => {
|
|
1099
|
+
const value = await database();
|
|
1100
|
+
const protector = await prepareProtector();
|
|
1101
|
+
let result;
|
|
1102
|
+
let completed = false;
|
|
1103
|
+
await value.withExclusiveTransactionAsync(async (sqlite) => {
|
|
1104
|
+
const writable = () => {
|
|
1105
|
+
if (mode !== "readwrite")
|
|
1106
|
+
throw new Error("Cannot write in a readonly Sync local transaction");
|
|
1137
1107
|
};
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1108
|
+
const raw = {
|
|
1109
|
+
deleteCollection: async (key) => {
|
|
1110
|
+
writable();
|
|
1111
|
+
await sqlite.runAsync("DELETE FROM absolute_sync_collections WHERE namespace = ? AND collection_key = ?", [namespace, key]);
|
|
1112
|
+
},
|
|
1113
|
+
deleteMutation: async (operationId) => {
|
|
1114
|
+
writable();
|
|
1115
|
+
await sqlite.runAsync("DELETE FROM absolute_sync_mutations WHERE namespace = ? AND operation_id = ?", [namespace, operationId]);
|
|
1116
|
+
},
|
|
1117
|
+
getCollection: async (key) => {
|
|
1118
|
+
const row = await sqlite.getFirstAsync("SELECT record_json FROM absolute_sync_collections WHERE namespace = ? AND collection_key = ? LIMIT 1", [namespace, key]);
|
|
1119
|
+
return parseRecord(row?.record_json, "collection", { kind: "collection", namespace }, protector);
|
|
1120
|
+
},
|
|
1121
|
+
getInstallationId: async () => {
|
|
1122
|
+
const row = await sqlite.getFirstAsync("SELECT installation_id FROM absolute_sync_metadata WHERE namespace = ? LIMIT 1", [namespace]);
|
|
1123
|
+
return rowString(row, "installation_id");
|
|
1124
|
+
},
|
|
1125
|
+
getMutation: async (operationId) => {
|
|
1126
|
+
const row = await sqlite.getFirstAsync("SELECT record_json FROM absolute_sync_mutations WHERE namespace = ? AND operation_id = ? LIMIT 1", [namespace, operationId]);
|
|
1127
|
+
return parseRecord(row?.record_json, "mutation", { kind: "mutation", namespace }, protector);
|
|
1128
|
+
},
|
|
1129
|
+
listCollections: async () => {
|
|
1130
|
+
const rows = await sqlite.getAllAsync("SELECT collection_key, record_json FROM absolute_sync_collections WHERE namespace = ? ORDER BY collection_key ASC", [namespace]);
|
|
1131
|
+
return rows.map((row) => {
|
|
1132
|
+
const key = row.collection_key;
|
|
1133
|
+
const record = parseRecord(row.record_json, "collection", { kind: "collection", namespace }, protector);
|
|
1134
|
+
return typeof key === "string" && record ? { key, record } : undefined;
|
|
1135
|
+
}).filter((entry) => entry !== undefined);
|
|
1136
|
+
},
|
|
1137
|
+
listMutations: async () => {
|
|
1138
|
+
const rows = await sqlite.getAllAsync("SELECT record_json FROM absolute_sync_mutations WHERE namespace = ? ORDER BY created_at ASC, operation_id ASC", [namespace]);
|
|
1139
|
+
return rows.map((row) => parseRecord(row.record_json, "mutation", { kind: "mutation", namespace }, protector)).filter((record) => record !== undefined);
|
|
1140
|
+
},
|
|
1141
|
+
putCollection: async (key, record) => {
|
|
1142
|
+
writable();
|
|
1143
|
+
await sqlite.runAsync("INSERT INTO absolute_sync_collections (namespace, collection_key, record_json) VALUES (?, ?, ?) ON CONFLICT(namespace, collection_key) DO UPDATE SET record_json = excluded.record_json", [
|
|
1144
|
+
namespace,
|
|
1145
|
+
key,
|
|
1146
|
+
serializeRecord(record, {
|
|
1147
|
+
kind: "collection",
|
|
1148
|
+
name: record.collection ?? key,
|
|
1149
|
+
namespace
|
|
1150
|
+
}, protector)
|
|
1151
|
+
]);
|
|
1152
|
+
},
|
|
1153
|
+
putMutation: async (record) => {
|
|
1154
|
+
writable();
|
|
1155
|
+
await sqlite.runAsync("INSERT INTO absolute_sync_mutations (namespace, operation_id, created_at, record_json) VALUES (?, ?, ?, ?) ON CONFLICT(namespace, operation_id) DO UPDATE SET created_at = excluded.created_at, record_json = excluded.record_json", [
|
|
1156
|
+
namespace,
|
|
1157
|
+
record.operationId,
|
|
1158
|
+
record.createdAt,
|
|
1159
|
+
serializeRecord(record, {
|
|
1160
|
+
kind: "mutation",
|
|
1161
|
+
name: record.name,
|
|
1162
|
+
namespace
|
|
1163
|
+
}, protector)
|
|
1164
|
+
]);
|
|
1165
|
+
},
|
|
1166
|
+
setInstallationId: async (installationId) => {
|
|
1167
|
+
writable();
|
|
1168
|
+
if (installationId.length === 0)
|
|
1169
|
+
throw new TypeError("Sync installation id must not be empty.");
|
|
1170
|
+
await sqlite.runAsync("INSERT INTO absolute_sync_metadata (namespace, installation_id) VALUES (?, ?) ON CONFLICT(namespace) DO UPDATE SET installation_id = excluded.installation_id", [namespace, installationId]);
|
|
1145
1171
|
}
|
|
1146
|
-
emitMessage(id, event.data);
|
|
1147
|
-
};
|
|
1148
|
-
socket.onerror = () => {
|
|
1149
|
-
if (sockets.get(id) === socket)
|
|
1150
|
-
emit({ socketId: id, type: "error" });
|
|
1151
|
-
};
|
|
1152
|
-
socket.onclose = (event) => {
|
|
1153
|
-
if (sockets.get(id) === socket)
|
|
1154
|
-
sockets.delete(id);
|
|
1155
|
-
emit({
|
|
1156
|
-
code: event.code,
|
|
1157
|
-
reason: event.reason,
|
|
1158
|
-
socketId: id,
|
|
1159
|
-
type: "close"
|
|
1160
|
-
});
|
|
1161
1172
|
};
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
if (method === "sync.socket.close") {
|
|
1202
|
-
const code = params.code === undefined ? undefined : typeof params.code === "number" && Number.isSafeInteger(params.code) && params.code >= 1000 && params.code <= 4999 ? params.code : null;
|
|
1203
|
-
if (code === null)
|
|
1204
|
-
throw new TypeError("Expo Sync bridge close code is invalid.");
|
|
1205
|
-
const reason = params.reason === undefined ? undefined : requireString(params.reason, "close reason");
|
|
1206
|
-
close(id, code, reason);
|
|
1207
|
-
return null;
|
|
1208
|
-
}
|
|
1209
|
-
throw new Error("Expo Sync socket bridge method is not allowed.");
|
|
1210
|
-
}
|
|
1173
|
+
result = await runSyncLocalPolicyTransaction({
|
|
1174
|
+
mode,
|
|
1175
|
+
now: now(),
|
|
1176
|
+
policy: localData,
|
|
1177
|
+
protected: protector !== undefined,
|
|
1178
|
+
raw,
|
|
1179
|
+
run
|
|
1180
|
+
});
|
|
1181
|
+
completed = true;
|
|
1182
|
+
});
|
|
1183
|
+
if (!completed)
|
|
1184
|
+
throw new Error("Expo Sync transaction did not complete.");
|
|
1185
|
+
return result;
|
|
1186
|
+
});
|
|
1187
|
+
};
|
|
1188
|
+
return {
|
|
1189
|
+
deleteNamespace: async (namespace) => {
|
|
1190
|
+
requireNamespace(namespace);
|
|
1191
|
+
await locked(async () => {
|
|
1192
|
+
const value = await database();
|
|
1193
|
+
await value.withExclusiveTransactionAsync(async (sqlite) => {
|
|
1194
|
+
for (const table of [
|
|
1195
|
+
"absolute_sync_metadata",
|
|
1196
|
+
"absolute_sync_collections",
|
|
1197
|
+
"absolute_sync_mutations"
|
|
1198
|
+
])
|
|
1199
|
+
await sqlite.runAsync(`DELETE FROM ${table} WHERE namespace = ?`, [
|
|
1200
|
+
namespace
|
|
1201
|
+
]);
|
|
1202
|
+
});
|
|
1203
|
+
});
|
|
1204
|
+
},
|
|
1205
|
+
getSchemaStatus: async () => {
|
|
1206
|
+
await database();
|
|
1207
|
+
if (!schemaStatus)
|
|
1208
|
+
throw new Error("Expo Sync schema was not prepared.");
|
|
1209
|
+
return { ...schemaStatus };
|
|
1210
|
+
},
|
|
1211
|
+
transaction
|
|
1211
1212
|
};
|
|
1212
1213
|
};
|
|
1213
1214
|
|
|
@@ -1388,5 +1389,5 @@ export {
|
|
|
1388
1389
|
unregisterExpoSyncBackgroundTask
|
|
1389
1390
|
};
|
|
1390
1391
|
|
|
1391
|
-
//# debugId=
|
|
1392
|
+
//# debugId=300EEDFDC3B21ACA64756E2164756E21
|
|
1392
1393
|
//# sourceMappingURL=index.js.map
|