@zeronsh/orbit 0.3.4 → 0.3.6
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/dist/{chunk-LUFJQUF4.js → chunk-B575ZNM2.js} +89 -20
- package/dist/chunk-B575ZNM2.js.map +1 -0
- package/dist/{chunk-KFX7HOCV.js → chunk-K4VPAXH7.js} +3 -3
- package/dist/{chunk-KFX7HOCV.js.map → chunk-K4VPAXH7.js.map} +1 -1
- package/dist/{chunk-RP6SPOCI.js → chunk-ZYDDINVC.js} +3 -3
- package/dist/{chunk-RP6SPOCI.js.map → chunk-ZYDDINVC.js.map} +1 -1
- package/dist/client.d.ts +23 -0
- package/dist/client.js +1 -1
- package/dist/drizzle/cli/bin.js +3 -3
- package/dist/drizzle/cli.js +3 -3
- package/dist/drizzle.js +2 -2
- package/dist/orm-core.js +2 -2
- package/dist/server.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-LUFJQUF4.js.map +0 -1
|
@@ -407,7 +407,7 @@ var Store = class {
|
|
|
407
407
|
// persistence
|
|
408
408
|
#kv;
|
|
409
409
|
#dirtyRows = /* @__PURE__ */ new Set();
|
|
410
|
-
//
|
|
410
|
+
// `${table}\u0000${pkKey}` (NUL separator: cannot appear in a table name)
|
|
411
411
|
#dirtyPending = /* @__PURE__ */ new Set();
|
|
412
412
|
#cleared = false;
|
|
413
413
|
#flushTimer;
|
|
@@ -606,29 +606,36 @@ var Store = class {
|
|
|
606
606
|
}
|
|
607
607
|
const kv = this.#kv;
|
|
608
608
|
if (!kv) return;
|
|
609
|
+
const clearOps = [];
|
|
609
610
|
if (this.#cleared) {
|
|
610
|
-
const
|
|
611
|
-
for (const [k] of await kv.entries("e/")) dels.push(kv.del(k));
|
|
612
|
-
await Promise.all(dels);
|
|
611
|
+
for (const [k] of await kv.entries("e/")) clearOps.push({ type: "del", key: k });
|
|
613
612
|
this.#cleared = false;
|
|
614
613
|
}
|
|
615
|
-
const
|
|
614
|
+
const ops = [];
|
|
616
615
|
for (const dk of this.#dirtyRows) {
|
|
617
616
|
const sep = dk.indexOf("\0");
|
|
618
617
|
const table2 = dk.slice(0, sep);
|
|
619
618
|
const pkKey2 = dk.slice(sep + 1);
|
|
620
619
|
const row = this.#tables.get(table2)?.get(pkKey2);
|
|
621
620
|
const key = `e/${table2}/${pkKey2}`;
|
|
622
|
-
|
|
621
|
+
ops.push(row ? { type: "set", key, value: { t: table2, k: pkKey2, v: row } } : { type: "del", key });
|
|
623
622
|
}
|
|
624
623
|
this.#dirtyRows.clear();
|
|
625
624
|
for (const id of this.#dirtyPending) {
|
|
626
625
|
const p = this.#pending.find((x) => x.id === id);
|
|
627
|
-
|
|
626
|
+
ops.push(p ? { type: "set", key: `p/${id}`, value: p } : { type: "del", key: `p/${id}` });
|
|
628
627
|
}
|
|
629
628
|
this.#dirtyPending.clear();
|
|
630
|
-
|
|
631
|
-
if (
|
|
629
|
+
const cookieDirty = this.#cookieDirty && this.#cookie !== void 0;
|
|
630
|
+
if (kv.batch) {
|
|
631
|
+
if (cookieDirty) ops.push({ type: "set", key: "cookie", value: this.#cookie });
|
|
632
|
+
await kv.batch([...clearOps, ...ops]);
|
|
633
|
+
if (cookieDirty) this.#cookieDirty = false;
|
|
634
|
+
return;
|
|
635
|
+
}
|
|
636
|
+
await Promise.all(clearOps.map((o) => kv.del(o.key)));
|
|
637
|
+
await Promise.all(ops.map((o) => o.type === "set" ? kv.set(o.key, o.value) : kv.del(o.key)));
|
|
638
|
+
if (cookieDirty) {
|
|
632
639
|
await kv.set("cookie", this.#cookie);
|
|
633
640
|
this.#cookieDirty = false;
|
|
634
641
|
}
|
|
@@ -1709,6 +1716,8 @@ var Orbit = class {
|
|
|
1709
1716
|
#onError;
|
|
1710
1717
|
/** Whether the id was supplied explicitly (then we never override it from the KV). */
|
|
1711
1718
|
#idFromOpts;
|
|
1719
|
+
/** Releases the Web-Locks persistence leadership (held by the first tab). */
|
|
1720
|
+
#releasePersistLock;
|
|
1712
1721
|
constructor(opts) {
|
|
1713
1722
|
this.#opts = opts;
|
|
1714
1723
|
this.#schema = opts.schema;
|
|
@@ -1827,6 +1836,7 @@ var Orbit = class {
|
|
|
1827
1836
|
close() {
|
|
1828
1837
|
this.#closed = true;
|
|
1829
1838
|
this.#ws?.close();
|
|
1839
|
+
this.#releasePersistLock?.();
|
|
1830
1840
|
}
|
|
1831
1841
|
// --- internals ----------------------------------------------------------
|
|
1832
1842
|
/** Resolve the client context (a value or sync getter); `{}` when unset. */
|
|
@@ -1834,8 +1844,39 @@ var Orbit = class {
|
|
|
1834
1844
|
const c = this.#context;
|
|
1835
1845
|
return typeof c === "function" ? c() : c ?? {};
|
|
1836
1846
|
}
|
|
1847
|
+
/**
|
|
1848
|
+
* Web-Locks single-writer election for the persisted cache. Two tabs sharing one
|
|
1849
|
+
* IndexedDB (and the restored clientID) would each flush their own dirty rows,
|
|
1850
|
+
* pending mutations, and cookie into the same flat keyspace with no coordination —
|
|
1851
|
+
* last-writer-wins can persist a cookie covering rows only the *other* tab wrote,
|
|
1852
|
+
* and both tabs would drive the same server-side CVR. Only the first tab (the
|
|
1853
|
+
* lock holder) gets persistence; later tabs run memory-only with their own fresh
|
|
1854
|
+
* clientID (a full server sync — correct, just uncached). The lock auto-releases
|
|
1855
|
+
* when the tab dies, so the next reload elects a new leader. Environments without
|
|
1856
|
+
* Web Locks (Node, tests, old browsers) keep today's behavior.
|
|
1857
|
+
*/
|
|
1858
|
+
async #acquirePersistLock() {
|
|
1859
|
+
const locks = globalThis.navigator?.locks;
|
|
1860
|
+
if (!locks) return true;
|
|
1861
|
+
return new Promise((resolve2) => {
|
|
1862
|
+
const req = locks.request("zeronsh-orbit-persist", { ifAvailable: true }, (lock) => {
|
|
1863
|
+
if (lock === null) {
|
|
1864
|
+
resolve2(false);
|
|
1865
|
+
return;
|
|
1866
|
+
}
|
|
1867
|
+
resolve2(true);
|
|
1868
|
+
return new Promise((release) => {
|
|
1869
|
+
this.#releasePersistLock = release;
|
|
1870
|
+
});
|
|
1871
|
+
});
|
|
1872
|
+
void Promise.resolve(req).catch(() => resolve2(true));
|
|
1873
|
+
});
|
|
1874
|
+
}
|
|
1837
1875
|
/** Hydrate persisted state (if any), restore unconfirmed mutations, then connect. */
|
|
1838
1876
|
async #init() {
|
|
1877
|
+
if (this.#kv && !await this.#acquirePersistLock()) {
|
|
1878
|
+
this.#kv = void 0;
|
|
1879
|
+
}
|
|
1839
1880
|
if (this.#kv) {
|
|
1840
1881
|
try {
|
|
1841
1882
|
await this.#store.hydrate(this.#kv);
|
|
@@ -1881,8 +1922,13 @@ var Orbit = class {
|
|
|
1881
1922
|
requestID: Math.random().toString(36).slice(2)
|
|
1882
1923
|
}];
|
|
1883
1924
|
this.#unconfirmedPushes.set(id, msg);
|
|
1884
|
-
|
|
1885
|
-
|
|
1925
|
+
const kv = this.#kv;
|
|
1926
|
+
if (kv) {
|
|
1927
|
+
void kv.set("nextMutationID", this.#nextMutationID).catch(() => {
|
|
1928
|
+
}).then(() => this.#send(msg));
|
|
1929
|
+
} else {
|
|
1930
|
+
this.#send(msg);
|
|
1931
|
+
}
|
|
1886
1932
|
}
|
|
1887
1933
|
async #connect() {
|
|
1888
1934
|
if (this.#closed || this.#connecting) return;
|
|
@@ -2004,6 +2050,12 @@ var MemoryKV = class {
|
|
|
2004
2050
|
async entries(prefix) {
|
|
2005
2051
|
return [...this.#m].filter(([k]) => k.startsWith(prefix));
|
|
2006
2052
|
}
|
|
2053
|
+
async batch(ops) {
|
|
2054
|
+
for (const op of ops) {
|
|
2055
|
+
if (op.type === "set") this.#m.set(op.key, op.value);
|
|
2056
|
+
else this.#m.delete(op.key);
|
|
2057
|
+
}
|
|
2058
|
+
}
|
|
2007
2059
|
};
|
|
2008
2060
|
function wrap(req) {
|
|
2009
2061
|
return new Promise((resolve2, reject) => {
|
|
@@ -2036,16 +2088,33 @@ var IDBKV = class {
|
|
|
2036
2088
|
}
|
|
2037
2089
|
async entries(prefix) {
|
|
2038
2090
|
const s = await this.#store("readonly");
|
|
2039
|
-
const
|
|
2040
|
-
const
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2091
|
+
const successor = prefix.slice(0, -1) + String.fromCharCode(prefix.charCodeAt(prefix.length - 1) + 1);
|
|
2092
|
+
const range = IDBKeyRange.bound(prefix, successor, false, true);
|
|
2093
|
+
const [keys, vals] = await Promise.all([wrap(s.getAllKeys(range)), wrap(s.getAll(range))]);
|
|
2094
|
+
return keys.map((k, i) => [String(k), vals[i]]);
|
|
2095
|
+
}
|
|
2096
|
+
/**
|
|
2097
|
+
* All ops in ONE readwrite transaction: atomic under a crash (IndexedDB
|
|
2098
|
+
* transactions are all-or-nothing) and one commit instead of one per key —
|
|
2099
|
+
* the flush of a large poke goes from N transactions to 1.
|
|
2100
|
+
*/
|
|
2101
|
+
async batch(ops) {
|
|
2102
|
+
if (ops.length === 0) return;
|
|
2103
|
+
const db = await this.#dbp;
|
|
2104
|
+
await new Promise((resolve2, reject) => {
|
|
2105
|
+
const tx = db.transaction("kv", "readwrite");
|
|
2106
|
+
const s = tx.objectStore("kv");
|
|
2107
|
+
for (const op of ops) {
|
|
2108
|
+
if (op.type === "set") s.put(op.value, op.key);
|
|
2109
|
+
else s.delete(op.key);
|
|
2110
|
+
}
|
|
2111
|
+
tx.oncomplete = () => resolve2();
|
|
2112
|
+
tx.onerror = () => reject(tx.error);
|
|
2113
|
+
tx.onabort = () => reject(tx.error);
|
|
2114
|
+
});
|
|
2046
2115
|
}
|
|
2047
2116
|
};
|
|
2048
2117
|
|
|
2049
2118
|
export { IDBKV, MaterializedView, MemoryKV, MemorySource, MemorySourceProvider, Orbit, PROTOCOL_VERSION, Query, QueryManager, SchemaQuery, SourceConnection, Store, StoreProvider, TypedQuery, View, boolean, buildPipeline, buildSchemaQueries, collectOps, compareValues, createBuilder, createOrbitApi, createSchema, defineMutation, defineQuery, evaluate, hashAST, hashString, json, nodeToRow, number, optional, parseTTL, relationships, string, table, tablesOf, unwrapSingular, validateArgs, valuesEqual };
|
|
2050
|
-
//# sourceMappingURL=chunk-
|
|
2051
|
-
//# sourceMappingURL=chunk-
|
|
2119
|
+
//# sourceMappingURL=chunk-B575ZNM2.js.map
|
|
2120
|
+
//# sourceMappingURL=chunk-B575ZNM2.js.map
|