@dcl/sdk 7.26.1-32785242405.commit-c8d1817 → 7.26.1-32860802198.commit-dae48fb
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/atom.d.ts +19 -0
- package/atom.js +83 -0
- package/future.d.ts +8 -0
- package/future.js +26 -0
- package/network/binary-message-bus.d.ts +6 -3
- package/network/binary-message-bus.js +9 -5
- package/network/chunking.d.ts +5 -0
- package/network/chunking.js +38 -0
- package/network/events/implementation.d.ts +93 -0
- package/network/events/implementation.js +221 -0
- package/network/events/index.d.ts +42 -0
- package/network/events/index.js +43 -0
- package/network/events/protocol.d.ts +27 -0
- package/network/events/protocol.js +66 -0
- package/network/events/registry.d.ts +8 -0
- package/network/events/registry.js +3 -0
- package/network/index.d.ts +8 -2
- package/network/index.js +16 -3
- package/network/message-bus-sync.d.ts +16 -3
- package/network/message-bus-sync.js +161 -103
- package/network/server/index.d.ts +14 -0
- package/network/server/index.js +219 -0
- package/network/server/utils.d.ts +18 -0
- package/network/server/utils.js +135 -0
- package/network/state.js +3 -5
- package/observables.js +2 -8
- package/package.json +6 -6
- package/server/env-var.d.ts +15 -0
- package/server/env-var.js +31 -0
- package/server/index.d.ts +2 -0
- package/server/index.js +3 -0
- package/server/storage/constants.d.ts +79 -0
- package/server/storage/constants.js +19 -0
- package/server/storage/index.d.ts +45 -0
- package/server/storage/index.js +51 -0
- package/server/storage/player.d.ts +45 -0
- package/server/storage/player.js +198 -0
- package/server/storage/scene.d.ts +40 -0
- package/server/storage/scene.js +188 -0
- package/server/storage/value-cache.d.ts +1 -0
- package/server/storage/value-cache.js +52 -0
- package/server/storage/write-queue.d.ts +1 -0
- package/server/storage/write-queue.js +71 -0
- package/server/storage-url.d.ts +13 -0
- package/server/storage-url.js +42 -0
- package/server/utils.d.ts +35 -0
- package/server/utils.js +61 -0
- package/src/atom.ts +98 -0
- package/src/future.ts +38 -0
- package/src/network/binary-message-bus.ts +9 -4
- package/src/network/chunking.ts +45 -0
- package/src/network/events/implementation.ts +271 -0
- package/src/network/events/index.ts +48 -0
- package/src/network/events/protocol.ts +94 -0
- package/src/network/events/registry.ts +18 -0
- package/src/network/index.ts +40 -3
- package/src/network/message-bus-sync.ts +176 -112
- package/src/network/server/index.ts +301 -0
- package/src/network/server/utils.ts +189 -0
- package/src/network/state.ts +3 -4
- package/src/observables.ts +1 -7
- package/src/server/env-var.ts +36 -0
- package/src/server/index.ts +12 -0
- package/src/server/storage/constants.ts +111 -0
- package/src/server/storage/index.ts +74 -0
- package/src/server/storage/player.ts +289 -0
- package/src/server/storage/scene.ts +270 -0
- package/src/server/storage/value-cache.ts +92 -0
- package/src/server/storage/write-queue.ts +131 -0
- package/src/server/storage-url.ts +49 -0
- package/src/server/utils.ts +76 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates the per-key write serializer shared by a storage scope.
|
|
3
|
+
* @internal
|
|
4
|
+
*/
|
|
5
|
+
export function createWriteQueue() {
|
|
6
|
+
const keys = new Map();
|
|
7
|
+
function makeOp(body, execute) {
|
|
8
|
+
let resolve;
|
|
9
|
+
const promise = new Promise((r) => (resolve = r));
|
|
10
|
+
return { body, execute, promise, resolve };
|
|
11
|
+
}
|
|
12
|
+
async function drain(key, state) {
|
|
13
|
+
for (;;) {
|
|
14
|
+
const op = state.active;
|
|
15
|
+
let result = false;
|
|
16
|
+
try {
|
|
17
|
+
result = await op.execute(op.body);
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
// Executors report failures via their boolean result; a throw is
|
|
21
|
+
// unexpected but must not wedge the queue.
|
|
22
|
+
}
|
|
23
|
+
op.resolve(result);
|
|
24
|
+
if (state.queued) {
|
|
25
|
+
state.active = state.queued;
|
|
26
|
+
state.queued = undefined;
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
keys.delete(key);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
pending(key) {
|
|
36
|
+
const state = keys.get(key);
|
|
37
|
+
if (!state)
|
|
38
|
+
return undefined;
|
|
39
|
+
return (state.queued ?? state.active).body;
|
|
40
|
+
},
|
|
41
|
+
isPending(key) {
|
|
42
|
+
return keys.has(key);
|
|
43
|
+
},
|
|
44
|
+
enqueue(key, body, execute, joinActive) {
|
|
45
|
+
const state = keys.get(key);
|
|
46
|
+
if (!state) {
|
|
47
|
+
const op = makeOp(body, execute);
|
|
48
|
+
const newState = { active: op };
|
|
49
|
+
keys.set(key, newState);
|
|
50
|
+
void drain(key, newState);
|
|
51
|
+
return op.promise;
|
|
52
|
+
}
|
|
53
|
+
if (state.queued) {
|
|
54
|
+
// A queued op has not started, so it is issued "after" this caller
|
|
55
|
+
// either way: join it when identical, supersede it otherwise.
|
|
56
|
+
if (state.queued.body !== body) {
|
|
57
|
+
state.queued.body = body;
|
|
58
|
+
state.queued.execute = execute;
|
|
59
|
+
}
|
|
60
|
+
return state.queued.promise;
|
|
61
|
+
}
|
|
62
|
+
if (joinActive && state.active.body === body) {
|
|
63
|
+
return state.active.promise;
|
|
64
|
+
}
|
|
65
|
+
const op = makeOp(body, execute);
|
|
66
|
+
state.queued = op;
|
|
67
|
+
return op.promise;
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid3JpdGUtcXVldWUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvc2VydmVyL3N0b3JhZ2Uvd3JpdGUtcXVldWUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBc0RBOzs7R0FHRztBQUNILE1BQU0sVUFBVSxnQkFBZ0I7SUFDOUIsTUFBTSxJQUFJLEdBQUcsSUFBSSxHQUFHLEVBQW9CLENBQUE7SUFFeEMsU0FBUyxNQUFNLENBQUMsSUFBbUIsRUFBRSxPQUE2QjtRQUNoRSxJQUFJLE9BQW1DLENBQUE7UUFDdkMsTUFBTSxPQUFPLEdBQUcsSUFBSSxPQUFPLENBQVUsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsT0FBTyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUE7UUFDMUQsT0FBTyxFQUFFLElBQUksRUFBRSxPQUFPLEVBQUUsT0FBTyxFQUFFLE9BQU8sRUFBRSxDQUFBO0lBQzVDLENBQUM7SUFFRCxLQUFLLFVBQVUsS0FBSyxDQUFDLEdBQVcsRUFBRSxLQUFlO1FBQy9DLFNBQVM7WUFDUCxNQUFNLEVBQUUsR0FBRyxLQUFLLENBQUMsTUFBTSxDQUFBO1lBQ3ZCLElBQUksTUFBTSxHQUFHLEtBQUssQ0FBQTtZQUNsQixJQUFJO2dCQUNGLE1BQU0sR0FBRyxNQUFNLEVBQUUsQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxDQUFBO2FBQ25DO1lBQUMsTUFBTTtnQkFDTixpRUFBaUU7Z0JBQ2pFLDJDQUEyQzthQUM1QztZQUNELEVBQUUsQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLENBQUE7WUFFbEIsSUFBSSxLQUFLLENBQUMsTUFBTSxFQUFFO2dCQUNoQixLQUFLLENBQUMsTUFBTSxHQUFHLEtBQUssQ0FBQyxNQUFNLENBQUE7Z0JBQzNCLEtBQUssQ0FBQyxNQUFNLEdBQUcsU0FBUyxDQUFBO2FBQ3pCO2lCQUFNO2dCQUNMLElBQUksQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUE7Z0JBQ2hCLE9BQU07YUFDUDtTQUNGO0lBQ0gsQ0FBQztJQUVELE9BQU87UUFDTCxPQUFPLENBQUMsR0FBVztZQUNqQixNQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFBO1lBQzNCLElBQUksQ0FBQyxLQUFLO2dCQUFFLE9BQU8sU0FBUyxDQUFBO1lBQzVCLE9BQU8sQ0FBQyxLQUFLLENBQUMsTUFBTSxJQUFJLEtBQUssQ0FBQyxNQUFNLENBQUMsQ0FBQyxJQUFJLENBQUE7UUFDNUMsQ0FBQztRQUVELFNBQVMsQ0FBQyxHQUFXO1lBQ25CLE9BQU8sSUFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQTtRQUN0QixDQUFDO1FBRUQsT0FBTyxDQUFDLEdBQVcsRUFBRSxJQUFtQixFQUFFLE9BQTZCLEVBQUUsVUFBbUI7WUFDMUYsTUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQTtZQUUzQixJQUFJLENBQUMsS0FBSyxFQUFFO2dCQUNWLE1BQU0sRUFBRSxHQUFHLE1BQU0sQ0FBQyxJQUFJLEVBQUUsT0FBTyxDQUFDLENBQUE7Z0JBQ2hDLE1BQU0sUUFBUSxHQUFhLEVBQUUsTUFBTSxFQUFFLEVBQUUsRUFBRSxDQUFBO2dCQUN6QyxJQUFJLENBQUMsR0FBRyxDQUFDLEdBQUcsRUFBRSxRQUFRLENBQUMsQ0FBQTtnQkFDdkIsS0FBSyxLQUFLLENBQUMsR0FBRyxFQUFFLFFBQVEsQ0FBQyxDQUFBO2dCQUN6QixPQUFPLEVBQUUsQ0FBQyxPQUFPLENBQUE7YUFDbEI7WUFFRCxJQUFJLEtBQUssQ0FBQyxNQUFNLEVBQUU7Z0JBQ2hCLG1FQUFtRTtnQkFDbkUsOERBQThEO2dCQUM5RCxJQUFJLEtBQUssQ0FBQyxNQUFNLENBQUMsSUFBSSxLQUFLLElBQUksRUFBRTtvQkFDOUIsS0FBSyxDQUFDLE1BQU0sQ0FBQyxJQUFJLEdBQUcsSUFBSSxDQUFBO29CQUN4QixLQUFLLENBQUMsTUFBTSxDQUFDLE9BQU8sR0FBRyxPQUFPLENBQUE7aUJBQy9CO2dCQUNELE9BQU8sS0FBSyxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUE7YUFDNUI7WUFFRCxJQUFJLFVBQVUsSUFBSSxLQUFLLENBQUMsTUFBTSxDQUFDLElBQUksS0FBSyxJQUFJLEVBQUU7Z0JBQzVDLE9BQU8sS0FBSyxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUE7YUFDNUI7WUFFRCxNQUFNLEVBQUUsR0FBRyxNQUFNLENBQUMsSUFBSSxFQUFFLE9BQU8sQ0FBQyxDQUFBO1lBQ2hDLEtBQUssQ0FBQyxNQUFNLEdBQUcsRUFBRSxDQUFBO1lBQ2pCLE9BQU8sRUFBRSxDQUFDLE9BQU8sQ0FBQTtRQUNuQixDQUFDO0tBQ0YsQ0FBQTtBQUNILENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEEgcGVuZGluZyB3cml0ZSBvcGVyYXRpb24uIGBib2R5YCBpcyB0aGUgc2VyaWFsaXplZCBQVVQgcGF5bG9hZCwgb3IgbnVsbFxuICogZm9yIGEgREVMRVRFLiBDYWxsZXJzIGNvYWxlc2NlZCBpbnRvIHRoZSBvcCBzaGFyZSBpdHMgcHJvbWlzZS5cbiAqIEBpbnRlcm5hbFxuICovXG5pbnRlcmZhY2UgUGVuZGluZ09wIHtcbiAgYm9keTogc3RyaW5nIHwgbnVsbFxuICBleGVjdXRlOiAoYm9keTogc3RyaW5nIHwgbnVsbCkgPT4gUHJvbWlzZTxib29sZWFuPlxuICBwcm9taXNlOiBQcm9taXNlPGJvb2xlYW4+XG4gIHJlc29sdmU6IChyZXN1bHQ6IGJvb2xlYW4pID0+IHZvaWRcbn1cblxuaW50ZXJmYWNlIEtleVN0YXRlIHtcbiAgLyoqIFRoZSBvcCBjdXJyZW50bHkgb24gdGhlIG5ldHdvcmsuICovXG4gIGFjdGl2ZTogUGVuZGluZ09wXG4gIC8qKiBBdCBtb3N0IG9uZSBxdWV1ZWQgb3A7IGxhdGVyIHdyaXRlcyByZXBsYWNlIGl0cyBwYXlsb2FkIChsYXRlc3Qgd2lucykuICovXG4gIHF1ZXVlZD86IFBlbmRpbmdPcFxufVxuXG4vKipcbiAqIFNlcmlhbGl6ZXMgd3JpdGVzIHBlciBrZXkgc28gdGhlIHNlcnZpY2UgY29tbWl0cyB0aGVtIGluIGlzc3VlIG9yZGVyLlxuICogT3ZlcmxhcHBpbmcgUFVUcyBmcm9tIHRoZSBzaW5nbGUgc2NlbmUgc2VydmVyIHdvdWxkIG90aGVyd2lzZSByYWNlOiB0aGVcbiAqIHNlcnZlciBrZWVwcyB3aGljaGV2ZXIgcmVxdWVzdCBpdCBwcm9jZXNzZXMgbGFzdCwgd2hpbGUgdGhlIGxvY2FsIGNhY2hlXG4gKiBrZWVwcyB3aGljaGV2ZXIgcmVzcG9uc2UgYXJyaXZlcyBsYXN0IOKAlCBlaXRoZXIgY2FuIGRpc2FncmVlIHdpdGggdGhlIGxhc3RcbiAqIHNldCgpIGlzc3VlZC4gV2l0aCBhdCBtb3N0IG9uZSBpbi1mbGlnaHQgb3AgcGVyIGtleSBhbmQgYSBzaW5nbGUgcXVldWVkXG4gKiBcImxhdGVzdCB2YWx1ZVwiIHNsb3QsIHRoZSBzZXJ2ZXIncyBmaW5hbCBzdGF0ZSBhbHdheXMgbWF0Y2hlcyB0aGUgbGFzdFxuICogd3JpdGUgaXNzdWVkLCBhbmQgTiByYXBpZCB3cml0ZXMgY29sbGFwc2UgaW50byBhdCBtb3N0IDIgbmV0d29yayBjYWxscy5cbiAqIEBpbnRlcm5hbFxuICovXG5leHBvcnQgaW50ZXJmYWNlIFdyaXRlUXVldWUge1xuICAvKipcbiAgICogQm9keSBvZiB0aGUgbGF0ZXN0IGlzc3VlZCB3cml0ZSBmb3IgdGhlIGtleSAodGhlIHF1ZXVlZCBvcCBpZiBwcmVzZW50LFxuICAgKiBlbHNlIHRoZSBpbi1mbGlnaHQgb25lKTogYSBzdHJpbmcgZm9yIGEgUFVULCBudWxsIGZvciBhIERFTEVURSxcbiAgICogdW5kZWZpbmVkIHdoZW4gbm8gd3JpdGUgaXMgcGVuZGluZy5cbiAgICovXG4gIHBlbmRpbmcoa2V5OiBzdHJpbmcpOiBzdHJpbmcgfCBudWxsIHwgdW5kZWZpbmVkXG4gIC8qKiBUcnVlIHdoaWxlIGFueSB3cml0ZSBmb3IgdGhlIGtleSBpcyBpbiBmbGlnaHQgb3IgcXVldWVkLiAqL1xuICBpc1BlbmRpbmcoa2V5OiBzdHJpbmcpOiBib29sZWFuXG4gIC8qKlxuICAgKiBJc3N1ZXMgYSB3cml0ZS4gSWYgb25lIGlzIGluIGZsaWdodCwgdGhlIG5ldyBvcCBpcyBxdWV1ZWQg4oCUIHJlcGxhY2luZyBhbnlcbiAgICogYWxyZWFkeS1xdWV1ZWQgb3AsIHdob3NlIGNhbGxlcnMgdGhlbiBmb2xsb3cgdGhpcyBvcCdzIG91dGNvbWUgKHRoZWlyXG4gICAqIHZhbHVlIHdhcyBzdXBlcnNlZGVkIGJlZm9yZSBpdCBjb3VsZCBldmVyIGJlIG9ic2VydmVkKS4gQW4gb3AgaWRlbnRpY2FsXG4gICAqIHRvIHRoZSBxdWV1ZWQgb25lIGpvaW5zIGl0OyBgam9pbkFjdGl2ZWAgYWRkaXRpb25hbGx5IGFsbG93cyBqb2luaW5nIGFuXG4gICAqIGlkZW50aWNhbCBpbi1mbGlnaHQgb3AgKG9ubHkgdmFsaWQgZm9yIGRlZHVwLXRvbGVyYW50IGNhbGxlcnMsIHNpbmNlIHRoYXRcbiAgICogb3Agd2FzIGlzc3VlZCBiZWZvcmUgdGhpcyBjYWxsKS5cbiAgICovXG4gIGVucXVldWUoXG4gICAga2V5OiBzdHJpbmcsXG4gICAgYm9keTogc3RyaW5nIHwgbnVsbCxcbiAgICBleGVjdXRlOiAoYm9keTogc3RyaW5nIHwgbnVsbCkgPT4gUHJvbWlzZTxib29sZWFuPixcbiAgICBqb2luQWN0aXZlOiBib29sZWFuXG4gICk6IFByb21pc2U8Ym9vbGVhbj5cbn1cblxuLyoqXG4gKiBDcmVhdGVzIHRoZSBwZXIta2V5IHdyaXRlIHNlcmlhbGl6ZXIgc2hhcmVkIGJ5IGEgc3RvcmFnZSBzY29wZS5cbiAqIEBpbnRlcm5hbFxuICovXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlV3JpdGVRdWV1ZSgpOiBXcml0ZVF1ZXVlIHtcbiAgY29uc3Qga2V5cyA9IG5ldyBNYXA8c3RyaW5nLCBLZXlTdGF0ZT4oKVxuXG4gIGZ1bmN0aW9uIG1ha2VPcChib2R5OiBzdHJpbmcgfCBudWxsLCBleGVjdXRlOiBQZW5kaW5nT3BbJ2V4ZWN1dGUnXSk6IFBlbmRpbmdPcCB7XG4gICAgbGV0IHJlc29sdmUhOiAocmVzdWx0OiBib29sZWFuKSA9PiB2b2lkXG4gICAgY29uc3QgcHJvbWlzZSA9IG5ldyBQcm9taXNlPGJvb2xlYW4+KChyKSA9PiAocmVzb2x2ZSA9IHIpKVxuICAgIHJldHVybiB7IGJvZHksIGV4ZWN1dGUsIHByb21pc2UsIHJlc29sdmUgfVxuICB9XG5cbiAgYXN5bmMgZnVuY3Rpb24gZHJhaW4oa2V5OiBzdHJpbmcsIHN0YXRlOiBLZXlTdGF0ZSk6IFByb21pc2U8dm9pZD4ge1xuICAgIGZvciAoOzspIHtcbiAgICAgIGNvbnN0IG9wID0gc3RhdGUuYWN0aXZlXG4gICAgICBsZXQgcmVzdWx0ID0gZmFsc2VcbiAgICAgIHRyeSB7XG4gICAgICAgIHJlc3VsdCA9IGF3YWl0IG9wLmV4ZWN1dGUob3AuYm9keSlcbiAgICAgIH0gY2F0Y2gge1xuICAgICAgICAvLyBFeGVjdXRvcnMgcmVwb3J0IGZhaWx1cmVzIHZpYSB0aGVpciBib29sZWFuIHJlc3VsdDsgYSB0aHJvdyBpc1xuICAgICAgICAvLyB1bmV4cGVjdGVkIGJ1dCBtdXN0IG5vdCB3ZWRnZSB0aGUgcXVldWUuXG4gICAgICB9XG4gICAgICBvcC5yZXNvbHZlKHJlc3VsdClcblxuICAgICAgaWYgKHN0YXRlLnF1ZXVlZCkge1xuICAgICAgICBzdGF0ZS5hY3RpdmUgPSBzdGF0ZS5xdWV1ZWRcbiAgICAgICAgc3RhdGUucXVldWVkID0gdW5kZWZpbmVkXG4gICAgICB9IGVsc2Uge1xuICAgICAgICBrZXlzLmRlbGV0ZShrZXkpXG4gICAgICAgIHJldHVyblxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIHJldHVybiB7XG4gICAgcGVuZGluZyhrZXk6IHN0cmluZyk6IHN0cmluZyB8IG51bGwgfCB1bmRlZmluZWQge1xuICAgICAgY29uc3Qgc3RhdGUgPSBrZXlzLmdldChrZXkpXG4gICAgICBpZiAoIXN0YXRlKSByZXR1cm4gdW5kZWZpbmVkXG4gICAgICByZXR1cm4gKHN0YXRlLnF1ZXVlZCA/PyBzdGF0ZS5hY3RpdmUpLmJvZHlcbiAgICB9LFxuXG4gICAgaXNQZW5kaW5nKGtleTogc3RyaW5nKTogYm9vbGVhbiB7XG4gICAgICByZXR1cm4ga2V5cy5oYXMoa2V5KVxuICAgIH0sXG5cbiAgICBlbnF1ZXVlKGtleTogc3RyaW5nLCBib2R5OiBzdHJpbmcgfCBudWxsLCBleGVjdXRlOiBQZW5kaW5nT3BbJ2V4ZWN1dGUnXSwgam9pbkFjdGl2ZTogYm9vbGVhbik6IFByb21pc2U8Ym9vbGVhbj4ge1xuICAgICAgY29uc3Qgc3RhdGUgPSBrZXlzLmdldChrZXkpXG5cbiAgICAgIGlmICghc3RhdGUpIHtcbiAgICAgICAgY29uc3Qgb3AgPSBtYWtlT3AoYm9keSwgZXhlY3V0ZSlcbiAgICAgICAgY29uc3QgbmV3U3RhdGU6IEtleVN0YXRlID0geyBhY3RpdmU6IG9wIH1cbiAgICAgICAga2V5cy5zZXQoa2V5LCBuZXdTdGF0ZSlcbiAgICAgICAgdm9pZCBkcmFpbihrZXksIG5ld1N0YXRlKVxuICAgICAgICByZXR1cm4gb3AucHJvbWlzZVxuICAgICAgfVxuXG4gICAgICBpZiAoc3RhdGUucXVldWVkKSB7XG4gICAgICAgIC8vIEEgcXVldWVkIG9wIGhhcyBub3Qgc3RhcnRlZCwgc28gaXQgaXMgaXNzdWVkIFwiYWZ0ZXJcIiB0aGlzIGNhbGxlclxuICAgICAgICAvLyBlaXRoZXIgd2F5OiBqb2luIGl0IHdoZW4gaWRlbnRpY2FsLCBzdXBlcnNlZGUgaXQgb3RoZXJ3aXNlLlxuICAgICAgICBpZiAoc3RhdGUucXVldWVkLmJvZHkgIT09IGJvZHkpIHtcbiAgICAgICAgICBzdGF0ZS5xdWV1ZWQuYm9keSA9IGJvZHlcbiAgICAgICAgICBzdGF0ZS5xdWV1ZWQuZXhlY3V0ZSA9IGV4ZWN1dGVcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gc3RhdGUucXVldWVkLnByb21pc2VcbiAgICAgIH1cblxuICAgICAgaWYgKGpvaW5BY3RpdmUgJiYgc3RhdGUuYWN0aXZlLmJvZHkgPT09IGJvZHkpIHtcbiAgICAgICAgcmV0dXJuIHN0YXRlLmFjdGl2ZS5wcm9taXNlXG4gICAgICB9XG5cbiAgICAgIGNvbnN0IG9wID0gbWFrZU9wKGJvZHksIGV4ZWN1dGUpXG4gICAgICBzdGF0ZS5xdWV1ZWQgPSBvcFxuICAgICAgcmV0dXJuIG9wLnByb21pc2VcbiAgICB9XG4gIH1cbn1cbiJdfQ==
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Determines the correct storage server URL based on the current realm.
|
|
3
|
+
*
|
|
4
|
+
* - If `isPreview` is true, uses the realm's baseUrl (localhost)
|
|
5
|
+
* - If the realm's baseUrl contains `.zone`, uses storage.decentraland.zone
|
|
6
|
+
* - Otherwise, uses storage.decentraland.org (production)
|
|
7
|
+
*
|
|
8
|
+
* The realm never changes mid-session, so the result is memoized; a failed
|
|
9
|
+
* resolution is not memoized so transient getRealm errors can be retried.
|
|
10
|
+
*
|
|
11
|
+
* @returns The storage server base URL
|
|
12
|
+
*/
|
|
13
|
+
export declare function getStorageServerUrl(): Promise<string>;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { getRealm } from '~system/Runtime';
|
|
2
|
+
const STORAGE_SERVER_ORG = 'https://storage.decentraland.org';
|
|
3
|
+
const STORAGE_SERVER_ZONE = 'https://storage.decentraland.zone';
|
|
4
|
+
async function resolveStorageServerUrl() {
|
|
5
|
+
const { realmInfo } = await getRealm({});
|
|
6
|
+
if (!realmInfo) {
|
|
7
|
+
throw new Error('Unable to retrieve realm information');
|
|
8
|
+
}
|
|
9
|
+
// Local development / preview mode
|
|
10
|
+
if (realmInfo.isPreview) {
|
|
11
|
+
return realmInfo.baseUrl;
|
|
12
|
+
}
|
|
13
|
+
// Staging / testing environment
|
|
14
|
+
if (realmInfo.baseUrl.includes('.zone')) {
|
|
15
|
+
return STORAGE_SERVER_ZONE;
|
|
16
|
+
}
|
|
17
|
+
// Production environment
|
|
18
|
+
return STORAGE_SERVER_ORG;
|
|
19
|
+
}
|
|
20
|
+
let memoizedUrl = null;
|
|
21
|
+
/**
|
|
22
|
+
* Determines the correct storage server URL based on the current realm.
|
|
23
|
+
*
|
|
24
|
+
* - If `isPreview` is true, uses the realm's baseUrl (localhost)
|
|
25
|
+
* - If the realm's baseUrl contains `.zone`, uses storage.decentraland.zone
|
|
26
|
+
* - Otherwise, uses storage.decentraland.org (production)
|
|
27
|
+
*
|
|
28
|
+
* The realm never changes mid-session, so the result is memoized; a failed
|
|
29
|
+
* resolution is not memoized so transient getRealm errors can be retried.
|
|
30
|
+
*
|
|
31
|
+
* @returns The storage server base URL
|
|
32
|
+
*/
|
|
33
|
+
export function getStorageServerUrl() {
|
|
34
|
+
if (!memoizedUrl) {
|
|
35
|
+
memoizedUrl = resolveStorageServerUrl();
|
|
36
|
+
memoizedUrl.catch(() => {
|
|
37
|
+
memoizedUrl = null;
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
return memoizedUrl;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RvcmFnZS11cmwuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvc2VydmVyL3N0b3JhZ2UtdXJsLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQTtBQUUxQyxNQUFNLGtCQUFrQixHQUFHLGtDQUFrQyxDQUFBO0FBQzdELE1BQU0sbUJBQW1CLEdBQUcsbUNBQW1DLENBQUE7QUFFL0QsS0FBSyxVQUFVLHVCQUF1QjtJQUNwQyxNQUFNLEVBQUUsU0FBUyxFQUFFLEdBQUcsTUFBTSxRQUFRLENBQUMsRUFBRSxDQUFDLENBQUE7SUFFeEMsSUFBSSxDQUFDLFNBQVMsRUFBRTtRQUNkLE1BQU0sSUFBSSxLQUFLLENBQUMsc0NBQXNDLENBQUMsQ0FBQTtLQUN4RDtJQUVELG1DQUFtQztJQUNuQyxJQUFJLFNBQVMsQ0FBQyxTQUFTLEVBQUU7UUFDdkIsT0FBTyxTQUFTLENBQUMsT0FBTyxDQUFBO0tBQ3pCO0lBRUQsZ0NBQWdDO0lBQ2hDLElBQUksU0FBUyxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUMsT0FBTyxDQUFDLEVBQUU7UUFDdkMsT0FBTyxtQkFBbUIsQ0FBQTtLQUMzQjtJQUVELHlCQUF5QjtJQUN6QixPQUFPLGtCQUFrQixDQUFBO0FBQzNCLENBQUM7QUFFRCxJQUFJLFdBQVcsR0FBMkIsSUFBSSxDQUFBO0FBRTlDOzs7Ozs7Ozs7OztHQVdHO0FBQ0gsTUFBTSxVQUFVLG1CQUFtQjtJQUNqQyxJQUFJLENBQUMsV0FBVyxFQUFFO1FBQ2hCLFdBQVcsR0FBRyx1QkFBdUIsRUFBRSxDQUFBO1FBQ3ZDLFdBQVcsQ0FBQyxLQUFLLENBQUMsR0FBRyxFQUFFO1lBQ3JCLFdBQVcsR0FBRyxJQUFJLENBQUE7UUFDcEIsQ0FBQyxDQUFDLENBQUE7S0FDSDtJQUNELE9BQU8sV0FBVyxDQUFBO0FBQ3BCLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBnZXRSZWFsbSB9IGZyb20gJ35zeXN0ZW0vUnVudGltZSdcblxuY29uc3QgU1RPUkFHRV9TRVJWRVJfT1JHID0gJ2h0dHBzOi8vc3RvcmFnZS5kZWNlbnRyYWxhbmQub3JnJ1xuY29uc3QgU1RPUkFHRV9TRVJWRVJfWk9ORSA9ICdodHRwczovL3N0b3JhZ2UuZGVjZW50cmFsYW5kLnpvbmUnXG5cbmFzeW5jIGZ1bmN0aW9uIHJlc29sdmVTdG9yYWdlU2VydmVyVXJsKCk6IFByb21pc2U8c3RyaW5nPiB7XG4gIGNvbnN0IHsgcmVhbG1JbmZvIH0gPSBhd2FpdCBnZXRSZWFsbSh7fSlcblxuICBpZiAoIXJlYWxtSW5mbykge1xuICAgIHRocm93IG5ldyBFcnJvcignVW5hYmxlIHRvIHJldHJpZXZlIHJlYWxtIGluZm9ybWF0aW9uJylcbiAgfVxuXG4gIC8vIExvY2FsIGRldmVsb3BtZW50IC8gcHJldmlldyBtb2RlXG4gIGlmIChyZWFsbUluZm8uaXNQcmV2aWV3KSB7XG4gICAgcmV0dXJuIHJlYWxtSW5mby5iYXNlVXJsXG4gIH1cblxuICAvLyBTdGFnaW5nIC8gdGVzdGluZyBlbnZpcm9ubWVudFxuICBpZiAocmVhbG1JbmZvLmJhc2VVcmwuaW5jbHVkZXMoJy56b25lJykpIHtcbiAgICByZXR1cm4gU1RPUkFHRV9TRVJWRVJfWk9ORVxuICB9XG5cbiAgLy8gUHJvZHVjdGlvbiBlbnZpcm9ubWVudFxuICByZXR1cm4gU1RPUkFHRV9TRVJWRVJfT1JHXG59XG5cbmxldCBtZW1vaXplZFVybDogUHJvbWlzZTxzdHJpbmc+IHwgbnVsbCA9IG51bGxcblxuLyoqXG4gKiBEZXRlcm1pbmVzIHRoZSBjb3JyZWN0IHN0b3JhZ2Ugc2VydmVyIFVSTCBiYXNlZCBvbiB0aGUgY3VycmVudCByZWFsbS5cbiAqXG4gKiAtIElmIGBpc1ByZXZpZXdgIGlzIHRydWUsIHVzZXMgdGhlIHJlYWxtJ3MgYmFzZVVybCAobG9jYWxob3N0KVxuICogLSBJZiB0aGUgcmVhbG0ncyBiYXNlVXJsIGNvbnRhaW5zIGAuem9uZWAsIHVzZXMgc3RvcmFnZS5kZWNlbnRyYWxhbmQuem9uZVxuICogLSBPdGhlcndpc2UsIHVzZXMgc3RvcmFnZS5kZWNlbnRyYWxhbmQub3JnIChwcm9kdWN0aW9uKVxuICpcbiAqIFRoZSByZWFsbSBuZXZlciBjaGFuZ2VzIG1pZC1zZXNzaW9uLCBzbyB0aGUgcmVzdWx0IGlzIG1lbW9pemVkOyBhIGZhaWxlZFxuICogcmVzb2x1dGlvbiBpcyBub3QgbWVtb2l6ZWQgc28gdHJhbnNpZW50IGdldFJlYWxtIGVycm9ycyBjYW4gYmUgcmV0cmllZC5cbiAqXG4gKiBAcmV0dXJucyBUaGUgc3RvcmFnZSBzZXJ2ZXIgYmFzZSBVUkxcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldFN0b3JhZ2VTZXJ2ZXJVcmwoKTogUHJvbWlzZTxzdHJpbmc+IHtcbiAgaWYgKCFtZW1vaXplZFVybCkge1xuICAgIG1lbW9pemVkVXJsID0gcmVzb2x2ZVN0b3JhZ2VTZXJ2ZXJVcmwoKVxuICAgIG1lbW9pemVkVXJsLmNhdGNoKCgpID0+IHtcbiAgICAgIG1lbW9pemVkVXJsID0gbnVsbFxuICAgIH0pXG4gIH1cbiAgcmV0dXJuIG1lbW9pemVkVXJsXG59XG4iXX0=
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/// <reference types="@dcl/js-runtime" />
|
|
2
|
+
import { SignedFetchRequest } from '~system/SignedFetch';
|
|
3
|
+
/**
|
|
4
|
+
* Validates that the code is running on a server-side scene.
|
|
5
|
+
* Throws an error if called from a client-side context.
|
|
6
|
+
*
|
|
7
|
+
* @param moduleName - The name of the module for the error message
|
|
8
|
+
* @throws Error if not running on a server-side scene
|
|
9
|
+
*/
|
|
10
|
+
export declare function assertIsServer(moduleName: string): void;
|
|
11
|
+
/**
|
|
12
|
+
* Result type for operations that can fail.
|
|
13
|
+
* Returns a tuple of [error, null] on failure or [null, data] on success.
|
|
14
|
+
*/
|
|
15
|
+
export type Result<T, E = string> = [E, null] | [null, T];
|
|
16
|
+
/**
|
|
17
|
+
* Extended result type that includes HTTP status code information.
|
|
18
|
+
*/
|
|
19
|
+
export type FetchResult<T> = [string, null, number?] | [null, T, number];
|
|
20
|
+
/**
|
|
21
|
+
* Wraps a promise to catch errors and return a Result tuple.
|
|
22
|
+
* This allows for cleaner error handling without try-catch blocks.
|
|
23
|
+
*
|
|
24
|
+
* @param promise - The promise to wrap
|
|
25
|
+
* @returns A tuple of [error, null] on failure or [null, data] on success
|
|
26
|
+
*/
|
|
27
|
+
export declare function tryCatch<T, E = Error>(promise: Promise<T>): Promise<Result<T, E>>;
|
|
28
|
+
/**
|
|
29
|
+
* Wraps signedFetch with automatic error handling and JSON parsing.
|
|
30
|
+
* Returns a FetchResult tuple with parsed JSON data or error message and status code.
|
|
31
|
+
*
|
|
32
|
+
* @param signedFetchBody - The signedFetch request configuration
|
|
33
|
+
* @returns A tuple of [error, null, statusCode?] on failure or [null, data, statusCode] on success
|
|
34
|
+
*/
|
|
35
|
+
export declare function wrapSignedFetch<T = unknown>(signedFetchBody: SignedFetchRequest): Promise<FetchResult<T>>;
|
package/server/utils.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { signedFetch } from '~system/SignedFetch';
|
|
2
|
+
import { isServer } from '../network';
|
|
3
|
+
/**
|
|
4
|
+
* Validates that the code is running on a server-side scene.
|
|
5
|
+
* Throws an error if called from a client-side context.
|
|
6
|
+
*
|
|
7
|
+
* @param moduleName - The name of the module for the error message
|
|
8
|
+
* @throws Error if not running on a server-side scene
|
|
9
|
+
*/
|
|
10
|
+
export function assertIsServer(moduleName) {
|
|
11
|
+
if (!isServer()) {
|
|
12
|
+
throw new Error(`${moduleName} is only available on server-side scenes`);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Wraps a promise to catch errors and return a Result tuple.
|
|
17
|
+
* This allows for cleaner error handling without try-catch blocks.
|
|
18
|
+
*
|
|
19
|
+
* @param promise - The promise to wrap
|
|
20
|
+
* @returns A tuple of [error, null] on failure or [null, data] on success
|
|
21
|
+
*/
|
|
22
|
+
export async function tryCatch(promise) {
|
|
23
|
+
try {
|
|
24
|
+
const data = await promise;
|
|
25
|
+
return [null, data];
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
return [error, null];
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Wraps signedFetch with automatic error handling and JSON parsing.
|
|
33
|
+
* Returns a FetchResult tuple with parsed JSON data or error message and status code.
|
|
34
|
+
*
|
|
35
|
+
* @param signedFetchBody - The signedFetch request configuration
|
|
36
|
+
* @returns A tuple of [error, null, statusCode?] on failure or [null, data, statusCode] on success
|
|
37
|
+
*/
|
|
38
|
+
export async function wrapSignedFetch(signedFetchBody) {
|
|
39
|
+
const [error, response] = await tryCatch(signedFetch(signedFetchBody));
|
|
40
|
+
if (error) {
|
|
41
|
+
console.error(`Error in ${signedFetchBody.url} endpoint`, { error });
|
|
42
|
+
return [error.message, null, undefined];
|
|
43
|
+
}
|
|
44
|
+
if (!response.ok) {
|
|
45
|
+
const errorMessage = `${response.status} ${response.statusText}`;
|
|
46
|
+
console.error(`Error in ${signedFetchBody.url} endpoint`, { response });
|
|
47
|
+
return [errorMessage, null, response.status];
|
|
48
|
+
}
|
|
49
|
+
let body;
|
|
50
|
+
try {
|
|
51
|
+
// JSON.parse throws synchronously, so it can't be wrapped with tryCatch (the
|
|
52
|
+
// throw would happen while evaluating the argument, rejecting this promise).
|
|
53
|
+
body = JSON.parse(response.body || '{}');
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
console.error(`Failed to parse response from ${signedFetchBody.url}`);
|
|
57
|
+
return ['Failed to parse response', null, response.status];
|
|
58
|
+
}
|
|
59
|
+
return [null, (body ?? {}), response.status];
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXRpbHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvc2VydmVyL3V0aWxzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxXQUFXLEVBQXNCLE1BQU0scUJBQXFCLENBQUE7QUFDckUsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLFlBQVksQ0FBQTtBQUVyQzs7Ozs7O0dBTUc7QUFDSCxNQUFNLFVBQVUsY0FBYyxDQUFDLFVBQWtCO0lBQy9DLElBQUksQ0FBQyxRQUFRLEVBQUUsRUFBRTtRQUNmLE1BQU0sSUFBSSxLQUFLLENBQUMsR0FBRyxVQUFVLDBDQUEwQyxDQUFDLENBQUE7S0FDekU7QUFDSCxDQUFDO0FBYUQ7Ozs7OztHQU1HO0FBQ0gsTUFBTSxDQUFDLEtBQUssVUFBVSxRQUFRLENBQWUsT0FBbUI7SUFDOUQsSUFBSTtRQUNGLE1BQU0sSUFBSSxHQUFHLE1BQU0sT0FBTyxDQUFBO1FBQzFCLE9BQU8sQ0FBQyxJQUFJLEVBQUUsSUFBSSxDQUFDLENBQUE7S0FDcEI7SUFBQyxPQUFPLEtBQUssRUFBRTtRQUNkLE9BQU8sQ0FBQyxLQUFVLEVBQUUsSUFBSSxDQUFDLENBQUE7S0FDMUI7QUFDSCxDQUFDO0FBRUQ7Ozs7OztHQU1HO0FBQ0gsTUFBTSxDQUFDLEtBQUssVUFBVSxlQUFlLENBQWMsZUFBbUM7SUFDcEYsTUFBTSxDQUFDLEtBQUssRUFBRSxRQUFRLENBQUMsR0FBRyxNQUFNLFFBQVEsQ0FBQyxXQUFXLENBQUMsZUFBZSxDQUFDLENBQUMsQ0FBQTtJQUV0RSxJQUFJLEtBQUssRUFBRTtRQUNULE9BQU8sQ0FBQyxLQUFLLENBQUMsWUFBWSxlQUFlLENBQUMsR0FBRyxXQUFXLEVBQUUsRUFBRSxLQUFLLEVBQUUsQ0FBQyxDQUFBO1FBQ3BFLE9BQU8sQ0FBQyxLQUFLLENBQUMsT0FBTyxFQUFFLElBQUksRUFBRSxTQUFTLENBQUMsQ0FBQTtLQUN4QztJQUVELElBQUksQ0FBQyxRQUFRLENBQUMsRUFBRSxFQUFFO1FBQ2hCLE1BQU0sWUFBWSxHQUFHLEdBQUcsUUFBUSxDQUFDLE1BQU0sSUFBSSxRQUFRLENBQUMsVUFBVSxFQUFFLENBQUE7UUFDaEUsT0FBTyxDQUFDLEtBQUssQ0FBQyxZQUFZLGVBQWUsQ0FBQyxHQUFHLFdBQVcsRUFBRSxFQUFFLFFBQVEsRUFBRSxDQUFDLENBQUE7UUFDdkUsT0FBTyxDQUFDLFlBQVksRUFBRSxJQUFJLEVBQUUsUUFBUSxDQUFDLE1BQU0sQ0FBQyxDQUFBO0tBQzdDO0lBRUQsSUFBSSxJQUFPLENBQUE7SUFDWCxJQUFJO1FBQ0YsNkVBQTZFO1FBQzdFLDZFQUE2RTtRQUM3RSxJQUFJLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxRQUFRLENBQUMsSUFBSSxJQUFJLElBQUksQ0FBQyxDQUFBO0tBQ3pDO0lBQUMsTUFBTTtRQUNOLE9BQU8sQ0FBQyxLQUFLLENBQUMsaUNBQWlDLGVBQWUsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxDQUFBO1FBQ3JFLE9BQU8sQ0FBQywwQkFBMEIsRUFBRSxJQUFJLEVBQUUsUUFBUSxDQUFDLE1BQU0sQ0FBQyxDQUFBO0tBQzNEO0lBRUQsT0FBTyxDQUFDLElBQUksRUFBRSxDQUFDLElBQUksSUFBSSxFQUFFLENBQU0sRUFBRSxRQUFRLENBQUMsTUFBTSxDQUFDLENBQUE7QUFDbkQsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IHNpZ25lZEZldGNoLCBTaWduZWRGZXRjaFJlcXVlc3QgfSBmcm9tICd+c3lzdGVtL1NpZ25lZEZldGNoJ1xuaW1wb3J0IHsgaXNTZXJ2ZXIgfSBmcm9tICcuLi9uZXR3b3JrJ1xuXG4vKipcbiAqIFZhbGlkYXRlcyB0aGF0IHRoZSBjb2RlIGlzIHJ1bm5pbmcgb24gYSBzZXJ2ZXItc2lkZSBzY2VuZS5cbiAqIFRocm93cyBhbiBlcnJvciBpZiBjYWxsZWQgZnJvbSBhIGNsaWVudC1zaWRlIGNvbnRleHQuXG4gKlxuICogQHBhcmFtIG1vZHVsZU5hbWUgLSBUaGUgbmFtZSBvZiB0aGUgbW9kdWxlIGZvciB0aGUgZXJyb3IgbWVzc2FnZVxuICogQHRocm93cyBFcnJvciBpZiBub3QgcnVubmluZyBvbiBhIHNlcnZlci1zaWRlIHNjZW5lXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBhc3NlcnRJc1NlcnZlcihtb2R1bGVOYW1lOiBzdHJpbmcpOiB2b2lkIHtcbiAgaWYgKCFpc1NlcnZlcigpKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGAke21vZHVsZU5hbWV9IGlzIG9ubHkgYXZhaWxhYmxlIG9uIHNlcnZlci1zaWRlIHNjZW5lc2ApXG4gIH1cbn1cblxuLyoqXG4gKiBSZXN1bHQgdHlwZSBmb3Igb3BlcmF0aW9ucyB0aGF0IGNhbiBmYWlsLlxuICogUmV0dXJucyBhIHR1cGxlIG9mIFtlcnJvciwgbnVsbF0gb24gZmFpbHVyZSBvciBbbnVsbCwgZGF0YV0gb24gc3VjY2Vzcy5cbiAqL1xuZXhwb3J0IHR5cGUgUmVzdWx0PFQsIEUgPSBzdHJpbmc+ID0gW0UsIG51bGxdIHwgW251bGwsIFRdXG5cbi8qKlxuICogRXh0ZW5kZWQgcmVzdWx0IHR5cGUgdGhhdCBpbmNsdWRlcyBIVFRQIHN0YXR1cyBjb2RlIGluZm9ybWF0aW9uLlxuICovXG5leHBvcnQgdHlwZSBGZXRjaFJlc3VsdDxUPiA9IFtzdHJpbmcsIG51bGwsIG51bWJlcj9dIHwgW251bGwsIFQsIG51bWJlcl1cblxuLyoqXG4gKiBXcmFwcyBhIHByb21pc2UgdG8gY2F0Y2ggZXJyb3JzIGFuZCByZXR1cm4gYSBSZXN1bHQgdHVwbGUuXG4gKiBUaGlzIGFsbG93cyBmb3IgY2xlYW5lciBlcnJvciBoYW5kbGluZyB3aXRob3V0IHRyeS1jYXRjaCBibG9ja3MuXG4gKlxuICogQHBhcmFtIHByb21pc2UgLSBUaGUgcHJvbWlzZSB0byB3cmFwXG4gKiBAcmV0dXJucyBBIHR1cGxlIG9mIFtlcnJvciwgbnVsbF0gb24gZmFpbHVyZSBvciBbbnVsbCwgZGF0YV0gb24gc3VjY2Vzc1xuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gdHJ5Q2F0Y2g8VCwgRSA9IEVycm9yPihwcm9taXNlOiBQcm9taXNlPFQ+KTogUHJvbWlzZTxSZXN1bHQ8VCwgRT4+IHtcbiAgdHJ5IHtcbiAgICBjb25zdCBkYXRhID0gYXdhaXQgcHJvbWlzZVxuICAgIHJldHVybiBbbnVsbCwgZGF0YV1cbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICByZXR1cm4gW2Vycm9yIGFzIEUsIG51bGxdXG4gIH1cbn1cblxuLyoqXG4gKiBXcmFwcyBzaWduZWRGZXRjaCB3aXRoIGF1dG9tYXRpYyBlcnJvciBoYW5kbGluZyBhbmQgSlNPTiBwYXJzaW5nLlxuICogUmV0dXJucyBhIEZldGNoUmVzdWx0IHR1cGxlIHdpdGggcGFyc2VkIEpTT04gZGF0YSBvciBlcnJvciBtZXNzYWdlIGFuZCBzdGF0dXMgY29kZS5cbiAqXG4gKiBAcGFyYW0gc2lnbmVkRmV0Y2hCb2R5IC0gVGhlIHNpZ25lZEZldGNoIHJlcXVlc3QgY29uZmlndXJhdGlvblxuICogQHJldHVybnMgQSB0dXBsZSBvZiBbZXJyb3IsIG51bGwsIHN0YXR1c0NvZGU/XSBvbiBmYWlsdXJlIG9yIFtudWxsLCBkYXRhLCBzdGF0dXNDb2RlXSBvbiBzdWNjZXNzXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiB3cmFwU2lnbmVkRmV0Y2g8VCA9IHVua25vd24+KHNpZ25lZEZldGNoQm9keTogU2lnbmVkRmV0Y2hSZXF1ZXN0KTogUHJvbWlzZTxGZXRjaFJlc3VsdDxUPj4ge1xuICBjb25zdCBbZXJyb3IsIHJlc3BvbnNlXSA9IGF3YWl0IHRyeUNhdGNoKHNpZ25lZEZldGNoKHNpZ25lZEZldGNoQm9keSkpXG5cbiAgaWYgKGVycm9yKSB7XG4gICAgY29uc29sZS5lcnJvcihgRXJyb3IgaW4gJHtzaWduZWRGZXRjaEJvZHkudXJsfSBlbmRwb2ludGAsIHsgZXJyb3IgfSlcbiAgICByZXR1cm4gW2Vycm9yLm1lc3NhZ2UsIG51bGwsIHVuZGVmaW5lZF1cbiAgfVxuXG4gIGlmICghcmVzcG9uc2Uub2spIHtcbiAgICBjb25zdCBlcnJvck1lc3NhZ2UgPSBgJHtyZXNwb25zZS5zdGF0dXN9ICR7cmVzcG9uc2Uuc3RhdHVzVGV4dH1gXG4gICAgY29uc29sZS5lcnJvcihgRXJyb3IgaW4gJHtzaWduZWRGZXRjaEJvZHkudXJsfSBlbmRwb2ludGAsIHsgcmVzcG9uc2UgfSlcbiAgICByZXR1cm4gW2Vycm9yTWVzc2FnZSwgbnVsbCwgcmVzcG9uc2Uuc3RhdHVzXVxuICB9XG5cbiAgbGV0IGJvZHk6IFRcbiAgdHJ5IHtcbiAgICAvLyBKU09OLnBhcnNlIHRocm93cyBzeW5jaHJvbm91c2x5LCBzbyBpdCBjYW4ndCBiZSB3cmFwcGVkIHdpdGggdHJ5Q2F0Y2ggKHRoZVxuICAgIC8vIHRocm93IHdvdWxkIGhhcHBlbiB3aGlsZSBldmFsdWF0aW5nIHRoZSBhcmd1bWVudCwgcmVqZWN0aW5nIHRoaXMgcHJvbWlzZSkuXG4gICAgYm9keSA9IEpTT04ucGFyc2UocmVzcG9uc2UuYm9keSB8fCAne30nKVxuICB9IGNhdGNoIHtcbiAgICBjb25zb2xlLmVycm9yKGBGYWlsZWQgdG8gcGFyc2UgcmVzcG9uc2UgZnJvbSAke3NpZ25lZEZldGNoQm9keS51cmx9YClcbiAgICByZXR1cm4gWydGYWlsZWQgdG8gcGFyc2UgcmVzcG9uc2UnLCBudWxsLCByZXNwb25zZS5zdGF0dXNdXG4gIH1cblxuICByZXR1cm4gW251bGwsIChib2R5ID8/IHt9KSBhcyBULCByZXNwb25zZS5zdGF0dXNdXG59XG4iXX0=
|
package/src/atom.ts
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import future, { IFuture } from './future'
|
|
2
|
+
|
|
3
|
+
// atom value wrapper like in clojure
|
|
4
|
+
|
|
5
|
+
// Simple Observable implementation to replace Babylon.js dependency
|
|
6
|
+
class SimpleObservable<T> {
|
|
7
|
+
private observers: ((value: T) => void)[] = []
|
|
8
|
+
private onceObservers: ((value: T) => void)[] = []
|
|
9
|
+
|
|
10
|
+
add(callback: (value: T) => void): (value: T) => void {
|
|
11
|
+
this.observers.push(callback)
|
|
12
|
+
return callback
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
addOnce(callback: (value: T) => void): void {
|
|
16
|
+
this.onceObservers.push(callback)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
remove(callback: (value: T) => void): void {
|
|
20
|
+
const index = this.observers.indexOf(callback)
|
|
21
|
+
if (index > -1) {
|
|
22
|
+
this.observers.splice(index, 1)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
notifyObservers(value: T): void {
|
|
27
|
+
this.observers.forEach((observer) => observer(value))
|
|
28
|
+
|
|
29
|
+
if (this.onceObservers.length > 0) {
|
|
30
|
+
this.onceObservers.forEach((observer) => observer(value))
|
|
31
|
+
this.onceObservers.length = 0
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const EMPTY = Symbol('empty')
|
|
37
|
+
type EMPTY = typeof EMPTY
|
|
38
|
+
|
|
39
|
+
export type Atom<T> = {
|
|
40
|
+
deref(): Promise<T>
|
|
41
|
+
getOrNull(): T | null
|
|
42
|
+
observable: SimpleObservable<T>
|
|
43
|
+
swap(value: T): T | void
|
|
44
|
+
pipe(fn: (value: T) => void | Promise<void>): Promise<void>
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function Atom<T>(initialValue: T | EMPTY = EMPTY): Atom<T> {
|
|
48
|
+
const observable = new SimpleObservable<T>()
|
|
49
|
+
let value: T | EMPTY = initialValue
|
|
50
|
+
const valueFutures: IFuture<T>[] = []
|
|
51
|
+
|
|
52
|
+
observable.addOnce((value) => {
|
|
53
|
+
valueFutures.forEach(($) => $.resolve(value))
|
|
54
|
+
valueFutures.length = 0
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
async pipe(fn) {
|
|
59
|
+
observable.add(async (t) => {
|
|
60
|
+
try {
|
|
61
|
+
await fn(t)
|
|
62
|
+
} catch (err) {
|
|
63
|
+
console.error(err)
|
|
64
|
+
}
|
|
65
|
+
})
|
|
66
|
+
if (value !== EMPTY) {
|
|
67
|
+
try {
|
|
68
|
+
await fn(value)
|
|
69
|
+
} catch (err) {
|
|
70
|
+
console.error(err)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
deref() {
|
|
75
|
+
if (value === EMPTY) {
|
|
76
|
+
const ret = future<T>()
|
|
77
|
+
valueFutures.push(ret)
|
|
78
|
+
return ret
|
|
79
|
+
}
|
|
80
|
+
return Promise.resolve(value)
|
|
81
|
+
},
|
|
82
|
+
getOrNull() {
|
|
83
|
+
if (value === EMPTY) {
|
|
84
|
+
return null
|
|
85
|
+
}
|
|
86
|
+
return value
|
|
87
|
+
},
|
|
88
|
+
observable,
|
|
89
|
+
swap(newValue) {
|
|
90
|
+
const oldValue = value
|
|
91
|
+
if (newValue !== value) {
|
|
92
|
+
value = newValue
|
|
93
|
+
observable.notifyObservers(value)
|
|
94
|
+
}
|
|
95
|
+
return oldValue === EMPTY ? undefined : oldValue
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
package/src/future.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export type IFuture<T> = Promise<T> & {
|
|
2
|
+
resolve: (x: T) => void
|
|
3
|
+
reject: (x: Error) => void
|
|
4
|
+
finally: (fn: () => void) => void
|
|
5
|
+
isPending: boolean
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function future<T = any>(): IFuture<T> {
|
|
9
|
+
let resolver: (x: T) => void
|
|
10
|
+
let rejecter: (x: Error) => void
|
|
11
|
+
|
|
12
|
+
const promise: any = new Promise((ok, err) => {
|
|
13
|
+
resolver = (x: T) => {
|
|
14
|
+
ok(x)
|
|
15
|
+
promise.isPending = false
|
|
16
|
+
}
|
|
17
|
+
rejecter = (x: Error) => {
|
|
18
|
+
err(x)
|
|
19
|
+
promise.isPending = false
|
|
20
|
+
}
|
|
21
|
+
}).catch((e) => Promise.reject(e))
|
|
22
|
+
|
|
23
|
+
promise.resolve = resolver!
|
|
24
|
+
promise.reject = rejecter!
|
|
25
|
+
|
|
26
|
+
if (!('finally' in promise)) {
|
|
27
|
+
promise.finally = (fn: any) => {
|
|
28
|
+
promise.then(fn)
|
|
29
|
+
promise.catch(fn)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
promise.isPending = true
|
|
34
|
+
|
|
35
|
+
return promise as IFuture<T>
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export default future
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { ReadWriteByteBuffer } from '@dcl/ecs/dist/serialization/ByteBuffer'
|
|
2
2
|
|
|
3
3
|
export enum CommsMessage {
|
|
4
|
-
CRDT =
|
|
5
|
-
REQ_CRDT_STATE =
|
|
6
|
-
RES_CRDT_STATE =
|
|
4
|
+
CRDT = 7,
|
|
5
|
+
REQ_CRDT_STATE = 8,
|
|
6
|
+
RES_CRDT_STATE = 9,
|
|
7
|
+
CRDT_SERVER = 4,
|
|
8
|
+
CRDT_AUTHORITATIVE = 5,
|
|
9
|
+
CUSTOM_EVENT = 6
|
|
7
10
|
}
|
|
8
11
|
|
|
9
12
|
export function BinaryMessageBus<T extends CommsMessage>(
|
|
@@ -20,7 +23,9 @@ export function BinaryMessageBus<T extends CommsMessage>(
|
|
|
20
23
|
__processMessages: (messages: Uint8Array[]) => {
|
|
21
24
|
for (const message of messages) {
|
|
22
25
|
const commsMsg = decodeCommsMessage<T>(message)
|
|
23
|
-
if (!commsMsg)
|
|
26
|
+
if (!commsMsg) {
|
|
27
|
+
continue
|
|
28
|
+
}
|
|
24
29
|
const { sender, messageType, data } = commsMsg
|
|
25
30
|
const fn = mapping.get(messageType)
|
|
26
31
|
if (fn) fn(data, sender)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { ReadWriteByteBuffer } from '@dcl/ecs/dist/serialization/ByteBuffer'
|
|
2
|
+
import { readMessages } from './server/utils'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Chunks CRDT messages from a Uint8Array buffer, respecting message boundaries
|
|
6
|
+
* Uses the comprehensive readMessages function that handles all message types
|
|
7
|
+
*/
|
|
8
|
+
export function chunkCrdtMessages(data: Uint8Array, maxSizeKB: number = 12): Uint8Array[] {
|
|
9
|
+
if (data.length === 0) {
|
|
10
|
+
return []
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const networkBuffer = new ReadWriteByteBuffer()
|
|
14
|
+
const chunks: Uint8Array[] = []
|
|
15
|
+
|
|
16
|
+
for (const message of readMessages(data)) {
|
|
17
|
+
// Check if adding this message would exceed the size limit
|
|
18
|
+
const currentBufferSize = networkBuffer.toBinary().byteLength
|
|
19
|
+
const messageSize = message.messageBuffer.byteLength
|
|
20
|
+
|
|
21
|
+
if ((currentBufferSize + messageSize) / 1024 > maxSizeKB) {
|
|
22
|
+
// If the current buffer has content, save it as a chunk
|
|
23
|
+
if (currentBufferSize > 0) {
|
|
24
|
+
chunks.push(networkBuffer.toCopiedBinary())
|
|
25
|
+
networkBuffer.resetBuffer()
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// If the message itself is larger than the limit, skip it
|
|
29
|
+
if (messageSize / 1024 > maxSizeKB) {
|
|
30
|
+
console.error(`Message too large (${messageSize} bytes), skipping CRDT message`)
|
|
31
|
+
continue
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Add message to current buffer
|
|
36
|
+
networkBuffer.writeBuffer(message.messageBuffer, false)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Add any remaining data as the final chunk
|
|
40
|
+
if (networkBuffer.currentWriteOffset() > 0) {
|
|
41
|
+
chunks.push(networkBuffer.toBinary())
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return chunks
|
|
45
|
+
}
|