@mengine/storage 1.1.0 → 1.2.1-alpha.0

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.
@@ -72,6 +72,37 @@ interface DocUpdate {
72
72
  docId: string;
73
73
  data: Uint8Array;
74
74
  }
75
+ /**
76
+ * What a peer says about ITSELF after accepting a push.
77
+ *
78
+ * Every field is optional because most peers report nothing: the local pg/idb
79
+ * write paths never decode Loro, so they have no version to give and return an
80
+ * empty receipt. Only a peer that answers with its own state — today the
81
+ * mengine-server HTTP path — fills this in.
82
+ */
83
+ interface DocPushReceipt {
84
+ /**
85
+ * The encoded version vector the peer holds AFTER this write.
86
+ *
87
+ * Same class of evidence as {@link DocDiff.version}: a complete statement
88
+ * about the peer, so a caller tracking "what the remote has" may adopt it
89
+ * outright — including downward. A peer that lost an op it once acked reports
90
+ * a version below the caller's belief, and copying that lower version is what
91
+ * makes the caller re-send. Taking a per-peer max instead would make the
92
+ * belief monotonic and destroy that healing path.
93
+ *
94
+ * It may LEAD the caller's doc on OTHER peers, covering concurrent ops the
95
+ * caller has not seen yet; adopting that is safe and is the point, because
96
+ * those ops are then never re-sent. It must NOT be believed where it leads on
97
+ * the caller's OWN peer: `export({ from })` skips every local op the version
98
+ * already covers, so a claim beyond what the caller actually sent makes those
99
+ * ops unsendable while the push still reports success. Callers are expected to
100
+ * clamp their own entry to what the request provably carried — the bytes here
101
+ * are a claim, not a proof, and `VersionVector.decode` accepts inputs that are
102
+ * not version vectors at all.
103
+ */
104
+ version?: Uint8Array;
105
+ }
75
106
  interface DocUpdateRecord extends DocUpdate {
76
107
  seq: number;
77
108
  createdAt: Date;
@@ -105,9 +136,21 @@ interface DocStorage {
105
136
  */
106
137
  getDocDiff(docId: string, knownVersion?: Uint8Array): Promise<DocDiff | null>;
107
138
  /**
108
- * Push updates into storage
139
+ * Push updates into storage.
140
+ *
141
+ * Every field of the {@link DocPushReceipt} is optional, so a peer with nothing
142
+ * to report answers `{}`. A peer that CAN state its resulting version should,
143
+ * because that statement covers other peers' ops too and lets the caller stop
144
+ * re-sending them.
145
+ *
146
+ * Resolving means the bytes are durable at this peer, so a peer that would drop
147
+ * the write must reject instead — callers read "resolved" as proof of a write,
148
+ * and a silent drop is indistinguishable from success. A peer that refuses ALL
149
+ * writes is different in kind: it is a standing condition, not a failed request,
150
+ * so it reports itself through {@link DocStorage.isReadonly} and callers are
151
+ * expected to check that rather than to discover it one rejection at a time.
109
152
  */
110
- pushDocUpdate(update: DocUpdate, origin: unknown): Promise<void>;
153
+ pushDocUpdate(update: DocUpdate, origin: unknown): Promise<DocPushReceipt>;
111
154
  /**
112
155
  * Delete a specific doc data with all snapshots and updates
113
156
  */
@@ -140,7 +183,7 @@ declare abstract class BaseDocStorage<Opts = {}> implements DocStorage {
140
183
  constructor(options: Opts & DocStorageOptions);
141
184
  getDoc(docId: string): Promise<DocSnapshotRecord | null>;
142
185
  getDocDiff(docId: string, knownVersion?: Uint8Array): Promise<DocDiff | null>;
143
- abstract pushDocUpdate(update: DocUpdate, origin: unknown): Promise<void>;
186
+ abstract pushDocUpdate(update: DocUpdate, origin: unknown): Promise<DocPushReceipt>;
144
187
  abstract deleteDoc(docId: string): Promise<void>;
145
188
  subscribeDocUpdate(callback: (update: DocUpdate, origin: unknown) => void): () => void;
146
189
  /**
@@ -188,4 +231,4 @@ declare abstract class BaseDocStorage<Opts = {}> implements DocStorage {
188
231
  protected lockDocForUpdate(docId: string): Promise<AsyncDisposable>;
189
232
  }
190
233
  //#endregion
191
- export { DocStorage as a, DocUpdateRecord as c, Connection as d, ConnectionStatus as f, DocSnapshotRecord as i, share as l, Locker as n, DocStorageOptions as o, DummyConnection as p, DocDiff as r, DocUpdate as s, BaseDocStorage as t, AutoReconnectConnection as u };
234
+ export { DocSnapshotRecord as a, DocUpdate as c, AutoReconnectConnection as d, Connection as f, DocPushReceipt as i, DocUpdateRecord as l, DummyConnection as m, Locker as n, DocStorage as o, ConnectionStatus as p, DocDiff as r, DocStorageOptions as s, BaseDocStorage as t, share as u };
package/dist/idb.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { c as DocUpdateRecord, i as DocSnapshotRecord, n as Locker, s as DocUpdate, t as BaseDocStorage, u as AutoReconnectConnection } from "./base-B0LSXWTK.js";
1
+ import { a as DocSnapshotRecord, c as DocUpdate, d as AutoReconnectConnection, i as DocPushReceipt, l as DocUpdateRecord, n as Locker, t as BaseDocStorage } from "./base-CPnEm_ZJ.js";
2
2
  import { DBSchema, IDBPDatabase } from "idb";
3
3
 
4
4
  //#region src/impls/idb/schema.d.ts
@@ -105,7 +105,7 @@ declare class IndexedDBDocStorage extends BaseDocStorage<{
105
105
  protected readonly locker: IndexedDBLocker;
106
106
  get db(): import("idb").IDBPDatabase<DocStorageSchema>;
107
107
  get channel(): BroadcastChannel;
108
- pushDocUpdate(update: DocUpdate, origin: unknown): Promise<void>;
108
+ pushDocUpdate(update: DocUpdate, origin: unknown): Promise<DocPushReceipt>;
109
109
  protected getDocSnapshot(docId: string): Promise<DocSnapshotRecord | null>;
110
110
  deleteDoc(docId: string): Promise<void>;
111
111
  protected setDocSnapshot(snapshot: DocSnapshotRecord): Promise<boolean>;
package/dist/idb.js CHANGED
@@ -125,7 +125,7 @@ var IndexedDBDocStorage = class extends BaseDocStorage {
125
125
  createdAt: /* @__PURE__ */ new Date(),
126
126
  updatedAt: /* @__PURE__ */ new Date()
127
127
  });
128
- return;
128
+ return {};
129
129
  }
130
130
  let seq = await trx.objectStore("sequences").get(update.docId);
131
131
  if (!seq) seq = {
@@ -155,6 +155,7 @@ var IndexedDBDocStorage = class extends BaseDocStorage {
155
155
  },
156
156
  origin
157
157
  });
158
+ return {};
158
159
  }
159
160
  async getDocSnapshot(docId) {
160
161
  return await this.db.transaction("snapshots", "readonly").store.get(docId) ?? null;
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { a as DocStorage, c as DocUpdateRecord, d as Connection, f as ConnectionStatus, i as DocSnapshotRecord, l as share, o as DocStorageOptions, p as DummyConnection, r as DocDiff, s as DocUpdate, t as BaseDocStorage, u as AutoReconnectConnection } from "./base-B0LSXWTK.js";
2
- export { AutoReconnectConnection, BaseDocStorage, Connection, ConnectionStatus, DocDiff, DocSnapshotRecord, DocStorage, DocStorageOptions, DocUpdate, DocUpdateRecord, DummyConnection, share };
1
+ import { a as DocSnapshotRecord, c as DocUpdate, d as AutoReconnectConnection, f as Connection, i as DocPushReceipt, l as DocUpdateRecord, m as DummyConnection, o as DocStorage, p as ConnectionStatus, r as DocDiff, s as DocStorageOptions, t as BaseDocStorage, u as share } from "./base-CPnEm_ZJ.js";
2
+ export { AutoReconnectConnection, BaseDocStorage, Connection, ConnectionStatus, DocDiff, DocPushReceipt, DocSnapshotRecord, DocStorage, DocStorageOptions, DocUpdate, DocUpdateRecord, DummyConnection, share };
package/dist/pg.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { c as DocUpdateRecord, i as DocSnapshotRecord, s as DocUpdate, t as BaseDocStorage, u as AutoReconnectConnection } from "./base-B0LSXWTK.js";
1
+ import { a as DocSnapshotRecord, c as DocUpdate, d as AutoReconnectConnection, i as DocPushReceipt, l as DocUpdateRecord, t as BaseDocStorage } from "./base-CPnEm_ZJ.js";
2
2
  import { NodePgDatabase } from "drizzle-orm/node-postgres";
3
3
  import { Pool } from "pg";
4
4
 
@@ -659,7 +659,7 @@ declare class PgDocStorage extends BaseDocStorage<PgConnectionOptions> {
659
659
  }>;
660
660
  }>;
661
661
  getDoc(docId: string): Promise<DocSnapshotRecord | null>;
662
- pushDocUpdate(update: DocUpdate, _origin: unknown): Promise<void>;
662
+ pushDocUpdate(update: DocUpdate, _origin: unknown): Promise<DocPushReceipt>;
663
663
  deleteDoc(docId: string): Promise<void>;
664
664
  protected getDocSnapshot(docId: string): Promise<DocSnapshotRecord | null>;
665
665
  protected setDocSnapshot(snapshot: DocSnapshotRecord): Promise<boolean>;
package/dist/pg.js CHANGED
@@ -199,6 +199,7 @@ var PgDocStorage = class extends BaseDocStorage {
199
199
  createdAt: now
200
200
  });
201
201
  });
202
+ return {};
202
203
  }
203
204
  async deleteDoc(docId) {
204
205
  await this.db.transaction(async (tx) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mengine/storage",
3
- "version": "1.1.0",
3
+ "version": "1.2.1-alpha.0",
4
4
  "license": "UNLICENSED",
5
5
  "repository": {
6
6
  "type": "git",
@@ -25,7 +25,7 @@
25
25
  "drizzle-orm": "^0.45.2",
26
26
  "lodash-es": "^4.18.1",
27
27
  "loro-crdt": "^1.13.6",
28
- "@mengine/utils": "1.1.0"
28
+ "@mengine/utils": "1.2.1-alpha.0"
29
29
  },
30
30
  "devDependencies": {
31
31
  "@testcontainers/postgresql": "^12.0.1",