@audiotool/nexus 0.0.10 → 0.0.12
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/LICENSE +201 -0
- package/dist/api.js +2 -2
- package/dist/{audiotool-api-D9u-oGp3.js → audiotool-api-CjHbqgj8.js} +880 -862
- package/dist/audiotool-client.d.ts +0 -12
- package/dist/document/backend/gateway.d.ts +5 -1
- package/dist/exports/index.d.ts +3 -1
- package/dist/index.js +124 -118
- package/dist/login-status.d.ts +7 -20
- package/dist/synced-document.d.ts +26 -3
- package/dist/utils/grpc/keepalive-transport.d.ts +1 -1
- package/dist/utils/platform.d.ts +4 -0
- package/package.json +8 -3
|
@@ -29,22 +29,10 @@ export type AudiotoolClient = {
|
|
|
29
29
|
* @returns Promise resolving to a SyncedDocument instance
|
|
30
30
|
*/
|
|
31
31
|
createSyncedDocument: (opts: {
|
|
32
|
-
/** Run in online mode, which means the document state is synced in real time. */
|
|
33
|
-
mode: "online";
|
|
34
32
|
/** The project to sync to; this can be anything containing a project's UUID, e.g. the URL of the studio
|
|
35
33
|
* when the project is open in the browser.
|
|
36
34
|
*/
|
|
37
35
|
project: string;
|
|
38
|
-
} | {
|
|
39
|
-
/** Run in offline mode, i.e. not synced to any particular project.
|
|
40
|
-
*
|
|
41
|
-
* All changes are discarded on reload.
|
|
42
|
-
*/
|
|
43
|
-
mode: "offline";
|
|
44
|
-
/** Whether transaction validation is on. If off, fewer transaction errors will occur,
|
|
45
|
-
* but the document might not get accepted by the backend if connecting to a project.
|
|
46
|
-
*/
|
|
47
|
-
validated: boolean;
|
|
48
36
|
}) => Promise<SyncedDocument>;
|
|
49
37
|
/**
|
|
50
38
|
* Collection of Audiotool API service clients.
|
|
@@ -41,8 +41,12 @@ export interface NexusGateway {
|
|
|
41
41
|
* This function must indicate that the current document state was loaded by returning at least
|
|
42
42
|
* one transaction. If the document state is empty, that transaction can be empty. The studio
|
|
43
43
|
* will show the loading spinner until the first transaction is received.
|
|
44
|
+
*
|
|
45
|
+
* After the first transaction, the gateway can return "done" to indicate that no further transactions
|
|
46
|
+
* will be returned, which the caller can use as a signal to stop polling. This is useful in situations
|
|
47
|
+
* where it's known that no synchronization transactions will ever be returned, such as when running in offline mode.
|
|
44
48
|
*/
|
|
45
|
-
synchronize(): ReadonlyTransaction[];
|
|
49
|
+
synchronize(): ReadonlyTransaction[] | "done";
|
|
46
50
|
/** This becomes true when the gateway is blocked in some way from syncing with upstream state.
|
|
47
51
|
* The document should be locked in this case to prevent data loss.
|
|
48
52
|
*/
|
package/dist/exports/index.d.ts
CHANGED
|
@@ -28,4 +28,6 @@
|
|
|
28
28
|
*/
|
|
29
29
|
export { createAudiotoolClient, type AudiotoolClient, } from '../audiotool-client';
|
|
30
30
|
export type { SyncedDocument } from '../synced-document';
|
|
31
|
-
export { getLoginStatus, type LoginStatus } from '../login-status';
|
|
31
|
+
export { getLoginStatus, type LoggedInStatus, type LoggedOutStatus, type LoginStatus, } from '../login-status';
|
|
32
|
+
export { createOfflineDocument } from '../synced-document';
|
|
33
|
+
export type { OfflineDocument } from '../synced-document';
|
package/dist/index.js
CHANGED
|
@@ -5,7 +5,7 @@ var jt = (i) => {
|
|
|
5
5
|
var Gt = (i, e, t) => e in i ? Wt(i, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : i[e] = t;
|
|
6
6
|
var a = (i, e, t) => Gt(i, typeof e != "symbol" ? e + "" : e, t), At = (i, e, t) => e.has(i) || jt("Cannot " + t);
|
|
7
7
|
var s = (i, e, t) => (At(i, e, "read from private field"), t ? t.call(i) : e.get(i)), h = (i, e, t) => e.has(i) ? jt("Cannot add the same private member more than once") : e instanceof WeakSet ? e.add(i) : e.set(i, t), g = (i, e, t, n) => (At(i, e, "write to private field"), n ? n.call(i, t) : e.set(i, t), t), y = (i, e, t) => (At(i, e, "access private method"), t);
|
|
8
|
-
import { aS as
|
|
8
|
+
import { aS as runningInBrowser, aT as entityMessageToTypeKey, aU as mustUnpackEntity, aV as packEntity, aW as getEntityTypeKeyFromProtoName, aX as unpackEntity, aY as Preset, aZ as anyEntityToTypeKey, a_ as createRetryingPromiseClient, a$ as createAuthorizedKeepaliveTransport, aR as createAudiotoolAPI, b0 as extractUuid, n as neverThrowingFetch } from "./audiotool-api-CjHbqgj8.js";
|
|
9
9
|
import { Message, proto3, protoInt64, Any, ScalarType, MethodKind } from "@bufbuild/protobuf";
|
|
10
10
|
import { P as Pointer, E as Empty, e as entityMessageTypes } from "./types-Cztu157p.js";
|
|
11
11
|
import { t as throw_, a as assert, b as asyncInterval, s as sleep } from "./lang-K-8hAzE4.js";
|
|
@@ -1276,7 +1276,7 @@ a(dt, "runtime", proto3), a(dt, "typeName", "audiotool.document.v1.GetTimeRespon
|
|
|
1276
1276
|
}
|
|
1277
1277
|
]));
|
|
1278
1278
|
let GetTimeResponse = dt;
|
|
1279
|
-
const wasmUrl = "/document_validator.wasm", wasmJsUrl = "/wasm_exec.js"
|
|
1279
|
+
const wasmUrl = "/document_validator.wasm", wasmJsUrl = "/wasm_exec.js";
|
|
1280
1280
|
let wasmDocumentStateBuilderCache;
|
|
1281
1281
|
const getWasmDocumentState = async () => {
|
|
1282
1282
|
if (wasmDocumentStateBuilderCache !== void 0)
|
|
@@ -1305,14 +1305,14 @@ const getWasmDocumentState = async () => {
|
|
|
1305
1305
|
const d = c();
|
|
1306
1306
|
let u = !1;
|
|
1307
1307
|
return {
|
|
1308
|
-
applyTransaction(
|
|
1308
|
+
applyTransaction(m) {
|
|
1309
1309
|
assert(
|
|
1310
1310
|
!u,
|
|
1311
1311
|
"tried applying a transaction after document state was terminated"
|
|
1312
1312
|
);
|
|
1313
|
-
const
|
|
1314
|
-
return
|
|
1315
|
-
modifications:
|
|
1313
|
+
const f = d.applyTransaction(m.toBinary());
|
|
1314
|
+
return f instanceof Object && "error" in f ? f.error : new Transaction({
|
|
1315
|
+
modifications: f.rollbacks.map((p) => Modification.fromBinary(p))
|
|
1316
1316
|
});
|
|
1317
1317
|
},
|
|
1318
1318
|
terminate() {
|
|
@@ -1321,7 +1321,7 @@ const getWasmDocumentState = async () => {
|
|
|
1321
1321
|
};
|
|
1322
1322
|
}), (await i)();
|
|
1323
1323
|
}, executeWrapperJs = async () => {
|
|
1324
|
-
if (
|
|
1324
|
+
if (runningInBrowser)
|
|
1325
1325
|
return import("https://cdn.audiotool.com/website-assets/document-service/5b85be5b151fe1c949845fe34b15f24ab059c736//wasm_exec.js");
|
|
1326
1326
|
const path = await import("node:path"), url = await import("node:url"), fs = await import("node:fs");
|
|
1327
1327
|
let filePath;
|
|
@@ -1339,7 +1339,7 @@ const getWasmDocumentState = async () => {
|
|
|
1339
1339
|
)
|
|
1340
1340
|
), await promise;
|
|
1341
1341
|
}, loadWasm = async () => {
|
|
1342
|
-
if (
|
|
1342
|
+
if (runningInBrowser)
|
|
1343
1343
|
return await fetch(
|
|
1344
1344
|
"https://cdn.audiotool.com/website-assets/document-service/5b85be5b151fe1c949845fe34b15f24ab059c736//document_validator.wasm.gz"
|
|
1345
1345
|
).then(
|
|
@@ -1392,22 +1392,22 @@ class NexusStateConsolidator {
|
|
|
1392
1392
|
if (t.size === 0)
|
|
1393
1393
|
return [];
|
|
1394
1394
|
const l = s(this, b).findIndex(
|
|
1395
|
-
([
|
|
1395
|
+
([f]) => t.has(f.id)
|
|
1396
1396
|
);
|
|
1397
1397
|
if (l === -1)
|
|
1398
1398
|
throw new Error(
|
|
1399
1399
|
"Invariant violation: expected to find rejected transaction in #pending, but didn't. This is a bug."
|
|
1400
1400
|
);
|
|
1401
|
-
const d = s(this, b).splice(l), u = d.map(([,
|
|
1402
|
-
if (!(s(this, I).applyTransaction(
|
|
1401
|
+
const d = s(this, b).splice(l), u = d.map(([, f]) => {
|
|
1402
|
+
if (!(s(this, I).applyTransaction(f) instanceof Transaction))
|
|
1403
1403
|
throw new Error("error applying reversal of transaction");
|
|
1404
|
-
return
|
|
1405
|
-
}).reverse(),
|
|
1406
|
-
const p = s(this, I).applyTransaction(
|
|
1404
|
+
return f;
|
|
1405
|
+
}).reverse(), m = d.filter(([f]) => !t.has(f.id)).map(([f]) => {
|
|
1406
|
+
const p = s(this, I).applyTransaction(f);
|
|
1407
1407
|
if (p instanceof Transaction)
|
|
1408
|
-
return [
|
|
1409
|
-
}).filter((
|
|
1410
|
-
return s(this, b).push(...
|
|
1408
|
+
return [f, p];
|
|
1409
|
+
}).filter((f) => f !== void 0);
|
|
1410
|
+
return s(this, b).push(...m), [...u, ...m.map(([f]) => f)];
|
|
1411
1411
|
}
|
|
1412
1412
|
const c = s(this, b).map((l) => l[1].clone()).reverse();
|
|
1413
1413
|
return c.forEach((l) => {
|
|
@@ -1420,7 +1420,7 @@ class NexusStateConsolidator {
|
|
|
1420
1420
|
throw new Error(`Error applying incoming transaction: ${d}`);
|
|
1421
1421
|
}), g(this, b, s(this, b).filter(([l]) => {
|
|
1422
1422
|
const d = t.has(l.id), u = e.some(
|
|
1423
|
-
(
|
|
1423
|
+
(m) => m.id === l.id
|
|
1424
1424
|
);
|
|
1425
1425
|
return !d && !u;
|
|
1426
1426
|
}).map(([l]) => {
|
|
@@ -1488,7 +1488,7 @@ const combinedValueNotifiersWithAnd = (...i) => {
|
|
|
1488
1488
|
}
|
|
1489
1489
|
)[Symbol.asyncIterator]());
|
|
1490
1490
|
let u = d();
|
|
1491
|
-
const
|
|
1491
|
+
const m = 1e3;
|
|
1492
1492
|
return {
|
|
1493
1493
|
nextTransactionIterator: async function* () {
|
|
1494
1494
|
for (; ; )
|
|
@@ -1525,7 +1525,7 @@ const combinedValueNotifiersWithAnd = (...i) => {
|
|
|
1525
1525
|
case Code.Unavailable:
|
|
1526
1526
|
case Code.Unknown: {
|
|
1527
1527
|
if (n.setValue(!1), await sleep(
|
|
1528
|
-
|
|
1528
|
+
m + Math.random() * m * 0.1,
|
|
1529
1529
|
// pass in master abort controller for early termination
|
|
1530
1530
|
r.signal
|
|
1531
1531
|
), r.signal.aborted)
|
|
@@ -1571,8 +1571,8 @@ const combinedValueNotifiersWithAnd = (...i) => {
|
|
|
1571
1571
|
);
|
|
1572
1572
|
if (d instanceof Error)
|
|
1573
1573
|
throw new Error("error sending transaction", { cause: d });
|
|
1574
|
-
l.forEach(([u,
|
|
1575
|
-
|
|
1574
|
+
l.forEach(([u, m]) => {
|
|
1575
|
+
m(d.errors[u.id] ?? void 0);
|
|
1576
1576
|
});
|
|
1577
1577
|
}
|
|
1578
1578
|
})(), {
|
|
@@ -1629,41 +1629,43 @@ const combinedValueNotifiersWithAnd = (...i) => {
|
|
|
1629
1629
|
}
|
|
1630
1630
|
};
|
|
1631
1631
|
}, createCollabGateway = (i, e, t, n) => {
|
|
1632
|
-
const o =
|
|
1632
|
+
const o = createDocumentServiceConnection(
|
|
1633
1633
|
i,
|
|
1634
1634
|
t
|
|
1635
|
-
),
|
|
1635
|
+
), r = new NexusStateConsolidator(e), c = [], l = [], d = /* @__PURE__ */ new Set();
|
|
1636
1636
|
(async () => {
|
|
1637
|
-
for await (const p of
|
|
1638
|
-
|
|
1639
|
-
})()
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1637
|
+
for await (const p of o.receiveNextTransaction)
|
|
1638
|
+
c.push(p);
|
|
1639
|
+
})();
|
|
1640
|
+
const m = new ValueNotifier(!0);
|
|
1641
|
+
o.connectionOk.subscribe((p) => {
|
|
1642
|
+
m.setValue(!p);
|
|
1643
|
+
}, !0);
|
|
1644
|
+
let f = !1;
|
|
1643
1645
|
return {
|
|
1644
|
-
blocked:
|
|
1646
|
+
blocked: m,
|
|
1645
1647
|
send: (p) => {
|
|
1646
|
-
if (
|
|
1648
|
+
if (f)
|
|
1647
1649
|
throw new Error(
|
|
1648
1650
|
"tried sending a transaction after gateway was terminated"
|
|
1649
1651
|
);
|
|
1650
|
-
p = p.clone(), p.id = crypto.randomUUID(),
|
|
1651
|
-
w !== void 0 &&
|
|
1652
|
+
p = p.clone(), p.id = crypto.randomUUID(), l.push(p), o.sendNextTransaction(p).then((w) => {
|
|
1653
|
+
w !== void 0 && d.add(p.id);
|
|
1652
1654
|
});
|
|
1653
1655
|
},
|
|
1654
1656
|
synchronize: () => {
|
|
1655
|
-
if (
|
|
1657
|
+
if (f)
|
|
1656
1658
|
return [];
|
|
1657
|
-
|
|
1658
|
-
const p =
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1659
|
+
c.length > 0;
|
|
1660
|
+
const p = r.consolidate(
|
|
1661
|
+
c,
|
|
1662
|
+
d,
|
|
1663
|
+
l
|
|
1662
1664
|
);
|
|
1663
|
-
return
|
|
1665
|
+
return c.length = 0, d.clear(), l.length = 0, p;
|
|
1664
1666
|
},
|
|
1665
1667
|
terminate: async () => {
|
|
1666
|
-
|
|
1668
|
+
f = !0, await o.terminate(), c.length = 0, d.clear(), l.length = 0, m.terminate(), e.terminate();
|
|
1667
1669
|
}
|
|
1668
1670
|
};
|
|
1669
1671
|
}, createWasmNexusValidator = async () => {
|
|
@@ -1902,11 +1904,11 @@ const createNexusFields = (i, e, t, n) => {
|
|
|
1902
1904
|
);
|
|
1903
1905
|
else {
|
|
1904
1906
|
const u = l.map(
|
|
1905
|
-
(
|
|
1907
|
+
(m, f) => createField(
|
|
1906
1908
|
i,
|
|
1907
1909
|
e,
|
|
1908
|
-
c.withAppendedFieldNumber(
|
|
1909
|
-
|
|
1910
|
+
c.withAppendedFieldNumber(f),
|
|
1911
|
+
m,
|
|
1910
1912
|
r
|
|
1911
1913
|
)
|
|
1912
1914
|
);
|
|
@@ -2053,50 +2055,50 @@ const createNexusFields = (i, e, t, n) => {
|
|
|
2053
2055
|
},
|
|
2054
2056
|
...(i == null ? void 0 : i.callbacks) ?? {}
|
|
2055
2057
|
}, o = /* @__PURE__ */ new Map(), r = (u) => {
|
|
2056
|
-
var
|
|
2057
|
-
return ((
|
|
2058
|
+
var m;
|
|
2059
|
+
return ((m = e.get(u)) == null ? void 0 : m.entityType) ?? o.get(u);
|
|
2058
2060
|
}, c = (u) => {
|
|
2059
|
-
const
|
|
2061
|
+
const m = createEntity(
|
|
2060
2062
|
r,
|
|
2061
2063
|
mustUnpackEntity(
|
|
2062
2064
|
u.entity ?? throw_("received empty create modification")
|
|
2063
2065
|
)
|
|
2064
2066
|
);
|
|
2065
|
-
e.set(
|
|
2066
|
-
addSourceTarget(t,
|
|
2067
|
-
}), n.onCreate(
|
|
2067
|
+
e.set(m.id, m), visitPointers$1(m, (f, p) => {
|
|
2068
|
+
addSourceTarget(t, f, p), n.onStartPointingTo(f, p);
|
|
2069
|
+
}), n.onCreate(m);
|
|
2068
2070
|
}, l = (u) => {
|
|
2069
|
-
const
|
|
2070
|
-
e.delete(
|
|
2071
|
+
const m = u.entityId, f = e.get(m) ?? throw_("can't find deleted entity");
|
|
2072
|
+
e.delete(m), visitPointers$1(f, (p, w) => {
|
|
2071
2073
|
removeSourceTarget(t, p, w), n.onStopPointingTo(p, w);
|
|
2072
|
-
}), n.onDelete(
|
|
2074
|
+
}), n.onDelete(f);
|
|
2073
2075
|
}, d = (u) => {
|
|
2074
|
-
const
|
|
2075
|
-
applyUpdate(p,
|
|
2076
|
+
const m = u.field ?? throw_("received update without pointer"), f = NexusLocation.fromPointerMessage(r, m), p = e.get(f.entityId) ?? throw_("can't find updated entity"), w = extractPbUpdateValue(u.value, r);
|
|
2077
|
+
applyUpdate(p, f, w, {
|
|
2076
2078
|
onStopPointingTo: (x, E) => {
|
|
2077
2079
|
removeSourceTarget(t, x, E) || throw_(), n.onStopPointingTo(x, E);
|
|
2078
2080
|
},
|
|
2079
2081
|
onStartPointingTo: (x, E) => {
|
|
2080
2082
|
addSourceTarget(t, x, E), n.onStartPointingTo(x, E);
|
|
2081
2083
|
}
|
|
2082
|
-
}), n.onUpdate(
|
|
2084
|
+
}), n.onUpdate(f, w);
|
|
2083
2085
|
};
|
|
2084
2086
|
return {
|
|
2085
2087
|
entities: e,
|
|
2086
2088
|
references: t,
|
|
2087
2089
|
applyModification(u) {
|
|
2088
|
-
const
|
|
2089
|
-
switch (
|
|
2090
|
+
const m = u.modification;
|
|
2091
|
+
switch (m.case) {
|
|
2090
2092
|
case "create": {
|
|
2091
|
-
c(
|
|
2093
|
+
c(m.value);
|
|
2092
2094
|
break;
|
|
2093
2095
|
}
|
|
2094
2096
|
case "delete": {
|
|
2095
|
-
l(
|
|
2097
|
+
l(m.value);
|
|
2096
2098
|
break;
|
|
2097
2099
|
}
|
|
2098
2100
|
case "update": {
|
|
2099
|
-
d(
|
|
2101
|
+
d(m.value);
|
|
2100
2102
|
break;
|
|
2101
2103
|
}
|
|
2102
2104
|
}
|
|
@@ -2104,11 +2106,11 @@ const createNexusFields = (i, e, t, n) => {
|
|
|
2104
2106
|
getStats() {
|
|
2105
2107
|
return {
|
|
2106
2108
|
entities: e.size,
|
|
2107
|
-
references: t.values().reduce((u,
|
|
2109
|
+
references: t.values().reduce((u, m) => u + m.length, 0)
|
|
2108
2110
|
};
|
|
2109
2111
|
},
|
|
2110
|
-
_addEntityTypeForId(u,
|
|
2111
|
-
o.set(u,
|
|
2112
|
+
_addEntityTypeForId(u, m) {
|
|
2113
|
+
o.set(u, m);
|
|
2112
2114
|
}
|
|
2113
2115
|
};
|
|
2114
2116
|
}, TerminableBuilder = {
|
|
@@ -2297,7 +2299,7 @@ let NexusEventManager = bt;
|
|
|
2297
2299
|
const sizeOf = (i) => i.values().reduce((e, t) => e + t.size, 0), getOrDefault = (i, e) => i.get(e) ?? i.set(e, /* @__PURE__ */ new Set()).get(e) ?? throw_(), mockNexusGateway = () => {
|
|
2298
2300
|
let i = !1, e = !1;
|
|
2299
2301
|
return {
|
|
2300
|
-
synchronize: () => e ?
|
|
2302
|
+
synchronize: () => e ? "done" : (e = !0, [new Transaction()]),
|
|
2301
2303
|
send: () => {
|
|
2302
2304
|
if (i)
|
|
2303
2305
|
throw new Error("Gateway terminated");
|
|
@@ -2767,15 +2769,15 @@ const filterByType = (i, e) => {
|
|
|
2767
2769
|
e.forEach((d) => n.set(d.entity.id, d));
|
|
2768
2770
|
const o = /* @__PURE__ */ new Map(), r = [];
|
|
2769
2771
|
[...n.values()].forEach(({ entity: d, overwrites: u }) => {
|
|
2770
|
-
const
|
|
2771
|
-
|
|
2772
|
-
const p = mapConstructorLocations(
|
|
2772
|
+
const m = entityToConstructorType(d), f = createDefaultEntityMessage(d.entityType);
|
|
2773
|
+
f.id = t.get(d.id) ?? throw_();
|
|
2774
|
+
const p = mapConstructorLocations(m, (w) => {
|
|
2773
2775
|
const x = t.get(w.entityId);
|
|
2774
|
-
return x !== void 0 ? (r.push([
|
|
2776
|
+
return x !== void 0 ? (r.push([f.id, x]), new NexusLocation(x, w.entityType, [
|
|
2775
2777
|
...w.fieldIndex
|
|
2776
2778
|
])) : w;
|
|
2777
2779
|
});
|
|
2778
|
-
updateEntityMessageWithConstructor(
|
|
2780
|
+
updateEntityMessageWithConstructor(f, p), u !== void 0 && updateEntityMessageWithConstructor(f, u), o.set(f.id, [d.entityType, f]);
|
|
2779
2781
|
});
|
|
2780
2782
|
const c = toposort(r).reverse(), l = [...o.keys()].filter(
|
|
2781
2783
|
(d) => !c.includes(d)
|
|
@@ -3306,12 +3308,16 @@ class NexusDocument {
|
|
|
3306
3308
|
resolve: r
|
|
3307
3309
|
} = Promise.withResolvers(), c = asyncInterval(async () => {
|
|
3308
3310
|
const u = (s(this, M) ?? throw_()).synchronize();
|
|
3311
|
+
if (u === "done")
|
|
3312
|
+
throw new Error(
|
|
3313
|
+
"Gateway returned 'done' before returning the initial transaction."
|
|
3314
|
+
);
|
|
3309
3315
|
if (u.length === 0)
|
|
3310
3316
|
return;
|
|
3311
|
-
const
|
|
3317
|
+
const m = u.flatMap((f) => f.modifications).length;
|
|
3312
3318
|
await this._applyIncomingTransactions(u, {
|
|
3313
3319
|
_takeTransactionLock: !1
|
|
3314
|
-
}), c.terminate(), r(
|
|
3320
|
+
}), c.terminate(), r(m > 0);
|
|
3315
3321
|
}, s(this, Nt)), l = await o;
|
|
3316
3322
|
if ((e == null ? void 0 : e.templateTransaction) !== void 0)
|
|
3317
3323
|
if (l)
|
|
@@ -3319,15 +3325,19 @@ class NexusDocument {
|
|
|
3319
3325
|
"Could not apply template transaction to document: Project state from backend is not empty."
|
|
3320
3326
|
);
|
|
3321
3327
|
else {
|
|
3322
|
-
const u = await e.templateTransaction,
|
|
3323
|
-
u.modifications.forEach((
|
|
3328
|
+
const u = await e.templateTransaction, m = await this.createTransaction(void 0, !1);
|
|
3329
|
+
u.modifications.forEach((f) => m._addModification(f)), m.send();
|
|
3324
3330
|
}
|
|
3325
3331
|
n.release();
|
|
3326
3332
|
const d = asyncInterval(async (u) => {
|
|
3327
3333
|
if (u.aborted)
|
|
3328
3334
|
return;
|
|
3329
|
-
const
|
|
3330
|
-
|
|
3335
|
+
const m = await s(this, S).acquire(), f = (s(this, M) ?? throw_()).synchronize();
|
|
3336
|
+
if (f === "done") {
|
|
3337
|
+
d.terminate(), m.release();
|
|
3338
|
+
return;
|
|
3339
|
+
}
|
|
3340
|
+
await this._applyIncomingTransactions(f, { _takeTransactionLock: !1 }), m.release();
|
|
3331
3341
|
}, s(this, Nt));
|
|
3332
3342
|
g(this, Bt, () => d.terminate());
|
|
3333
3343
|
}
|
|
@@ -3558,8 +3568,19 @@ const DocumentService = {
|
|
|
3558
3568
|
kind: MethodKind.Unary
|
|
3559
3569
|
}
|
|
3560
3570
|
}
|
|
3571
|
+
}, createSyncedDocument = (i, e, t, n) => ({
|
|
3572
|
+
connected: e,
|
|
3573
|
+
createTransaction: () => i.createTransaction(),
|
|
3574
|
+
modify: (o) => i.modify(o),
|
|
3575
|
+
queryEntities: i.queryEntitiesWithoutLock,
|
|
3576
|
+
events: i.events,
|
|
3577
|
+
start: async () => i.takeTransactions({ validator: t, gateway: n }),
|
|
3578
|
+
stop: async () => i.terminate()
|
|
3579
|
+
}), createOfflineDocument = async (i) => {
|
|
3580
|
+
const e = (i == null ? void 0 : i.validated) ?? !0 ? await createWasmNexusValidator() : mockNexusValidator(), t = mockNexusGateway(), n = new NexusDocument(), o = new ValueNotifier(!0), r = createSyncedDocument(n, o, e, t);
|
|
3581
|
+
return await r.start(), r;
|
|
3561
3582
|
}, createOnlineDocument = async (i, e, t) => {
|
|
3562
|
-
var
|
|
3583
|
+
var m;
|
|
3563
3584
|
let n = createWasmNexusValidator();
|
|
3564
3585
|
const o = await i.projectService.openSession({
|
|
3565
3586
|
projectName: e
|
|
@@ -3568,31 +3589,18 @@ const DocumentService = {
|
|
|
3568
3589
|
throw new Error("Couldn't open session", { cause: o });
|
|
3569
3590
|
const r = createRetryingPromiseClient(
|
|
3570
3591
|
DocumentService,
|
|
3571
|
-
createAuthorizedKeepaliveTransport({
|
|
3572
|
-
baseUrl: ((
|
|
3592
|
+
await createAuthorizedKeepaliveTransport({
|
|
3593
|
+
baseUrl: ((m = o.session) == null ? void 0 : m.documentServiceUrl) ?? throw_("backend returned no document service url"),
|
|
3573
3594
|
useBinaryFormat: !0,
|
|
3574
3595
|
getToken: t
|
|
3575
3596
|
})
|
|
3576
|
-
)
|
|
3577
|
-
await n;
|
|
3578
|
-
const c = createCollabGateway(
|
|
3597
|
+
), c = await n, l = createCollabGateway(
|
|
3579
3598
|
r,
|
|
3580
3599
|
await getWasmDocumentState(),
|
|
3581
3600
|
e
|
|
3582
|
-
),
|
|
3583
|
-
return
|
|
3584
|
-
},
|
|
3585
|
-
const e = i ? await createWasmNexusValidator() : mockNexusValidator(), t = mockNexusGateway(), n = new NexusDocument(), o = new ValueNotifier(!0);
|
|
3586
|
-
return createSyncedDocument(n, o, e, t);
|
|
3587
|
-
}, createSyncedDocument = (i, e, t, n) => ({
|
|
3588
|
-
connected: e,
|
|
3589
|
-
createTransaction: () => i.createTransaction(),
|
|
3590
|
-
modify: (o) => i.modify(o),
|
|
3591
|
-
queryEntities: i.queryEntitiesWithoutLock,
|
|
3592
|
-
events: i.events,
|
|
3593
|
-
start: async () => i.takeTransactions({ validator: t, gateway: n }),
|
|
3594
|
-
stop: async () => i.terminate()
|
|
3595
|
-
}), createAudiotoolClient = async ({
|
|
3601
|
+
), d = new NexusDocument(), u = new ValueNotifier(!1);
|
|
3602
|
+
return l.blocked.subscribe((f) => u.setValue(!f), !0), createSyncedDocument(d, u, c, l);
|
|
3603
|
+
}, createAudiotoolClient = async ({
|
|
3596
3604
|
authorization: i
|
|
3597
3605
|
}) => {
|
|
3598
3606
|
const e = async () => {
|
|
@@ -3607,12 +3615,9 @@ const DocumentService = {
|
|
|
3607
3615
|
}, t = await createAudiotoolAPI(e);
|
|
3608
3616
|
return {
|
|
3609
3617
|
api: t,
|
|
3610
|
-
createSyncedDocument: async (n) => {
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
return await createOnlineDocument(t, o, e);
|
|
3614
|
-
} else
|
|
3615
|
-
return await createOfflineDocument(n.validated);
|
|
3618
|
+
createSyncedDocument: async ({ project: n }) => {
|
|
3619
|
+
const o = extractProjectName(n);
|
|
3620
|
+
return await createOnlineDocument(t, o, e);
|
|
3616
3621
|
}
|
|
3617
3622
|
};
|
|
3618
3623
|
}, extractProjectName = (i) => {
|
|
@@ -3643,8 +3648,8 @@ const DocumentService = {
|
|
|
3643
3648
|
};
|
|
3644
3649
|
const d = r.get("code");
|
|
3645
3650
|
if (d != null) {
|
|
3646
|
-
const
|
|
3647
|
-
if (
|
|
3651
|
+
const f = r.get("state"), p = localStorage.getItem(n.state);
|
|
3652
|
+
if (f == null || p == null || f !== p)
|
|
3648
3653
|
return {
|
|
3649
3654
|
loggedIn: !1,
|
|
3650
3655
|
error: toError(
|
|
@@ -3693,26 +3698,26 @@ const DocumentService = {
|
|
|
3693
3698
|
}
|
|
3694
3699
|
localStorage.removeItem(n.state);
|
|
3695
3700
|
const u = await getOrFetchValidToken(n, i);
|
|
3696
|
-
let
|
|
3701
|
+
let m;
|
|
3697
3702
|
if (typeof u == "string") {
|
|
3698
|
-
const
|
|
3699
|
-
if (
|
|
3700
|
-
|
|
3701
|
-
const w = await
|
|
3702
|
-
return
|
|
3703
|
+
const f = async () => {
|
|
3704
|
+
if (m == null) {
|
|
3705
|
+
m = (async () => await getOrFetchValidToken(n, i) ?? new Error("User not logged in."))();
|
|
3706
|
+
const w = await m;
|
|
3707
|
+
return m = void 0, w;
|
|
3703
3708
|
}
|
|
3704
|
-
return await
|
|
3709
|
+
return await m;
|
|
3705
3710
|
};
|
|
3706
3711
|
let p;
|
|
3707
3712
|
return {
|
|
3708
3713
|
loggedIn: !0,
|
|
3709
|
-
getToken:
|
|
3714
|
+
getToken: f,
|
|
3710
3715
|
getUserName: async () => {
|
|
3711
3716
|
if (p !== void 0)
|
|
3712
3717
|
return await p;
|
|
3713
3718
|
const { promise: w, resolve: x } = Promise.withResolvers();
|
|
3714
3719
|
p = w;
|
|
3715
|
-
const E = await
|
|
3720
|
+
const E = await f();
|
|
3716
3721
|
if (E instanceof Error)
|
|
3717
3722
|
return x(E), await p;
|
|
3718
3723
|
const gt = await neverThrowingFetch(
|
|
@@ -3782,8 +3787,8 @@ const DocumentService = {
|
|
|
3782
3787
|
return;
|
|
3783
3788
|
let n = !0;
|
|
3784
3789
|
{
|
|
3785
|
-
const
|
|
3786
|
-
n =
|
|
3790
|
+
const f = localStorage.getItem(i.expiresAt);
|
|
3791
|
+
n = f == null ? !0 : Date.now() >= parseInt(f) - 6e4;
|
|
3787
3792
|
}
|
|
3788
3793
|
if (!n)
|
|
3789
3794
|
return t;
|
|
@@ -3807,13 +3812,14 @@ const DocumentService = {
|
|
|
3807
3812
|
return new Error(`Error during refresh token request: ${r.name}`, {
|
|
3808
3813
|
cause: r.message
|
|
3809
3814
|
});
|
|
3810
|
-
const { error: c, error_description: l, access_token: d, refresh_token: u, expires_in:
|
|
3815
|
+
const { error: c, error_description: l, access_token: d, refresh_token: u, expires_in: m } = await r.json();
|
|
3811
3816
|
return c ? toError(c, l) : (localStorage.setItem(i.accessToken, d), localStorage.setItem(i.refreshToken, u), localStorage.setItem(
|
|
3812
3817
|
i.expiresAt,
|
|
3813
|
-
(Date.now() +
|
|
3818
|
+
(Date.now() + m * 1e3).toString()
|
|
3814
3819
|
), d);
|
|
3815
3820
|
};
|
|
3816
3821
|
export {
|
|
3817
3822
|
createAudiotoolClient,
|
|
3823
|
+
createOfflineDocument,
|
|
3818
3824
|
getLoginStatus
|
|
3819
3825
|
};
|
package/dist/login-status.d.ts
CHANGED
|
@@ -1,23 +1,4 @@
|
|
|
1
1
|
export type LoggedInStatus = {
|
|
2
|
-
/** The app is authorized to make actions on a user's behalf. */
|
|
3
|
-
loggedIn: true;
|
|
4
|
-
/** Simple utility to get the current user display name / user name. The result of this function is cached. */
|
|
5
|
-
getUserInfo(): Promise<{
|
|
6
|
-
name: string;
|
|
7
|
-
displayName: string;
|
|
8
|
-
} | Error>;
|
|
9
|
-
/** Log the current user out and reload the page. */
|
|
10
|
-
logout: () => void;
|
|
11
|
-
/** Get the current authentication token. Might refresh the token if need be, but most often
|
|
12
|
-
* just returns the token.
|
|
13
|
-
*/
|
|
14
|
-
getToken: () => Promise<string | Error>;
|
|
15
|
-
};
|
|
16
|
-
/**
|
|
17
|
-
* The current authentication status of the user in this tab. Either logged in or logged out.
|
|
18
|
-
* To change the authentication status, call login or logout on this object.
|
|
19
|
-
*/
|
|
20
|
-
export type LoginStatus = {
|
|
21
2
|
/** The app is authorized to make actions on a user's behalf. */
|
|
22
3
|
loggedIn: true;
|
|
23
4
|
/** Simple utility to get the current user name. The result of this function is cached. */
|
|
@@ -28,7 +9,8 @@ export type LoginStatus = {
|
|
|
28
9
|
* just returns the token.
|
|
29
10
|
*/
|
|
30
11
|
getToken: () => Promise<string | Error>;
|
|
31
|
-
}
|
|
12
|
+
};
|
|
13
|
+
export type LoggedOutStatus = {
|
|
32
14
|
/** The app is not authorized to make actions on a user's behalf. Call login to authorize.*/
|
|
33
15
|
loggedIn: false;
|
|
34
16
|
/** If an error is the reason the user is logged out, this is set. Otherwise, the user just hasn't logged in yet. */
|
|
@@ -36,6 +18,11 @@ export type LoginStatus = {
|
|
|
36
18
|
/** start the authorization flow by redirecting the user to the login page of audiotool. */
|
|
37
19
|
login: () => Promise<void>;
|
|
38
20
|
};
|
|
21
|
+
/**
|
|
22
|
+
* The current authentication status of the user in this tab. Either logged in or logged out.
|
|
23
|
+
* To change the authentication status, call login or logout on this object.
|
|
24
|
+
*/
|
|
25
|
+
export type LoginStatus = LoggedInStatus | LoggedOutStatus;
|
|
39
26
|
/**
|
|
40
27
|
* This function allows to let arbitrary users use your app by letting them login/logout using the audiotool accounts system.
|
|
41
28
|
*
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
import { AudiotoolAPI } from './api/audiotool-api';
|
|
2
|
+
import { NexusGateway } from './document/backend/gateway';
|
|
3
|
+
import { NexusValidator } from './document/backend/validator';
|
|
4
|
+
import { NexusDocument } from './document/document';
|
|
1
5
|
import { NexusEventManager } from './document/event-manager';
|
|
2
6
|
import { EntityQuery } from './document/query/entity';
|
|
3
7
|
import { SafeTransactionBuilder, TransactionBuilder } from './document/transaction-builder';
|
|
4
8
|
import { ValueNotifier } from './utils/observable-notifier-value';
|
|
5
|
-
import { AudiotoolAPI } from './api/audiotool-api';
|
|
6
9
|
|
|
7
10
|
/**
|
|
8
11
|
* An Audiotool project document that synchronizes in real-time with the backend.
|
|
@@ -150,6 +153,26 @@ export type SyncedDocument = {
|
|
|
150
153
|
**/
|
|
151
154
|
stop: () => Promise<void>;
|
|
152
155
|
};
|
|
153
|
-
/** Create
|
|
156
|
+
/** Create a SyncedDocument wrapper from a NexusDocument. */
|
|
157
|
+
export declare const createSyncedDocument: (document: NexusDocument, connected: ValueNotifier<boolean>, validator: NexusValidator, gateway: NexusGateway) => SyncedDocument;
|
|
158
|
+
/** An offline version of a {@link SyncedDocument} - starts out completely empty,
|
|
159
|
+
* and all changes are discarded on reload/shutdown.
|
|
160
|
+
*
|
|
161
|
+
* Doesn't have start/stop methods since no syncing is happening.
|
|
162
|
+
*
|
|
163
|
+
* Can be safely thrown away after usage.
|
|
164
|
+
*/
|
|
165
|
+
export type OfflineDocument = Omit<SyncedDocument, "start" | "stop">;
|
|
166
|
+
/**
|
|
167
|
+
* Create a nexus document that is not synced to the backend; all changes are discarded on reload/shutdown.
|
|
168
|
+
*
|
|
169
|
+
* The returned document is ready to be modified immediately and can be thrown away without calling start/stop.
|
|
170
|
+
*
|
|
171
|
+
* To create a document that is synced with a state from the backend/DAW, use {@link AudiotoolClient.createSyncedDocument}.
|
|
172
|
+
* */
|
|
173
|
+
export declare const createOfflineDocument: (opts?: {
|
|
174
|
+
/** Whether validation is enabled. Turning that off results in fewer transaction errors, but can lead to invalid states. */
|
|
175
|
+
validated?: boolean;
|
|
176
|
+
}) => Promise<OfflineDocument>;
|
|
177
|
+
/** Create a SyncedDocument wrapper that's connected to the backend. */
|
|
154
178
|
export declare const createOnlineDocument: (api: AudiotoolAPI, projectName: string, getToken: () => Promise<string>) => Promise<SyncedDocument>;
|
|
155
|
-
export declare const createOfflineDocument: (validated: boolean) => Promise<SyncedDocument>;
|
|
@@ -22,7 +22,7 @@ export declare const createAuthorizedKeepaliveTransport: ({ baseUrl, useBinaryFo
|
|
|
22
22
|
useBinaryFormat: boolean;
|
|
23
23
|
typeRegistry?: IMessageTypeRegistry;
|
|
24
24
|
getToken: () => Promise<string>;
|
|
25
|
-
}) => KeepaliveTransport
|
|
25
|
+
}) => Promise<KeepaliveTransport>;
|
|
26
26
|
/** Takes the `keepalive` parameter out of `options`, packs it into the
|
|
27
27
|
* keepalive header.
|
|
28
28
|
*/
|