zerodrift 1.0.0 → 1.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/NOTICE +26 -0
- package/README.md +27 -7
- package/package.json +3 -2
package/NOTICE
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
zerodrift
|
|
2
|
+
Copyright (c) 2026 selasijean
|
|
3
|
+
|
|
4
|
+
This product is licensed under the MIT License. See the LICENSE file.
|
|
5
|
+
|
|
6
|
+
Inspiration and attribution
|
|
7
|
+
---------------------------
|
|
8
|
+
|
|
9
|
+
zerodrift was shaped by public writing and talks about local-first sync engine
|
|
10
|
+
design, especially:
|
|
11
|
+
|
|
12
|
+
- Wenzhao Hu, "Reverse Engineering Linear's Sync Engine: A Detailed
|
|
13
|
+
Study" (https://github.com/wzhudev/reverse-linear-sync-engine)
|
|
14
|
+
- Tuomas Artman's React Helsinki talk on Linear's realtime sync
|
|
15
|
+
(https://www.youtube.com/watch?v=WxK11RsLqp4)
|
|
16
|
+
|
|
17
|
+
Wenzhao Hu's writeup is © 2025 Wenzhao Hu and licensed under the Creative
|
|
18
|
+
Commons Attribution 4.0 International License:
|
|
19
|
+
https://creativecommons.org/licenses/by/4.0/
|
|
20
|
+
|
|
21
|
+
zerodrift is an independent TypeScript implementation. It defines its own small
|
|
22
|
+
three-endpoint sync protocol and ships its own client/server reference code. It
|
|
23
|
+
contains no Linear or other third-party source code.
|
|
24
|
+
|
|
25
|
+
Linear is a trademark of its respective owner. zerodrift is not affiliated
|
|
26
|
+
with, endorsed by, or sponsored by Linear.
|
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# zerodrift
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/zerodrift)
|
|
4
|
+
|
|
3
5
|
A TypeScript local-first sync engine. Reads are synchronous from an in-memory pool, writes are optimistic, state stays current across tabs and clients via SSE, and everything persists locally so the app survives reloads and works offline. The same engine runs in Node so agents and background workers can hold a live model just like a browser tab.
|
|
4
6
|
|
|
5
7
|
You bring the backend. The client speaks a small three-endpoint protocol that can be implemented in any language. A reference Go backend and Next.js demo live in this repo so you can run the whole loop locally.
|
|
@@ -27,7 +29,7 @@ npm install zod # for entityFromZod(...) schema authoring
|
|
|
27
29
|
npm install eventsource # for Node/headless SSE clients
|
|
28
30
|
```
|
|
29
31
|
|
|
30
|
-
|
|
32
|
+
Decorator path: enable `experimentalDecorators` in your `tsconfig.json` (or the SWC/Babel equivalent). Unlike most decorator libraries, `reflect-metadata` is **not** needed.
|
|
31
33
|
|
|
32
34
|
## Import paths
|
|
33
35
|
|
|
@@ -35,7 +37,7 @@ If you use the decorator authoring path, enable `experimentalDecorators` in your
|
|
|
35
37
|
|---|---|
|
|
36
38
|
| `zerodrift` | `StoreManager`, `BaseModel`, decorators, `MemoryAdapter`, relation field types (`RefCollection`/`BackRef`/`OwnedRefs`), and the config / error / sync types. The curated, stable surface. |
|
|
37
39
|
| `zerodrift/schema` | `defineSchema`, `entityFromZod`, field builders, links, extensions, and typed `store.<entity>.*` APIs. |
|
|
38
|
-
| `zerodrift/react` | `<SyncProvider>` and React hooks: `useRecord`, `useRecords`, `useRecordsByIndex`, `useRelation`, `useBatch`, `useUndoRedo`. |
|
|
40
|
+
| `zerodrift/react` | `<SyncProvider>` and React hooks: `useRecord`, `useRecords`, `useRecordsByIndex`, `useRelation`, `useBatch`, `useUndoRedo`, `useBootstrapStatus`. |
|
|
39
41
|
| `zerodrift/internal` | Engine machinery (`ObjectPool`, `TransactionQueue`, `SyncConnection`, `ModelRegistry`, …) for tooling/tests. **No stability promise** — may change between releases. |
|
|
40
42
|
|
|
41
43
|
## Define your models
|
|
@@ -160,8 +162,9 @@ export default function Providers({ children }) {
|
|
|
160
162
|
workspaceId: "workspace-123",
|
|
161
163
|
transport: {
|
|
162
164
|
bootstrapFetcher: async (type, options) => {
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
+
const res = await fetch(
|
|
166
|
+
`/api/bootstrap?type=${type}&lastSyncId=${options?.sinceSyncId ?? 0}`,
|
|
167
|
+
);
|
|
165
168
|
return res.json();
|
|
166
169
|
},
|
|
167
170
|
transactionSender: async (batch) => {
|
|
@@ -295,9 +298,9 @@ SSE messages are delta packets:
|
|
|
295
298
|
|
|
296
299
|
```json
|
|
297
300
|
{
|
|
301
|
+
"syncId": 5206,
|
|
298
302
|
"syncActions": [
|
|
299
303
|
{
|
|
300
|
-
"id": 5206,
|
|
301
304
|
"modelName": "Issue",
|
|
302
305
|
"modelId": "uuid",
|
|
303
306
|
"action": "U",
|
|
@@ -309,7 +312,7 @@ SSE messages are delta packets:
|
|
|
309
312
|
}
|
|
310
313
|
```
|
|
311
314
|
|
|
312
|
-
The client reconnects with `?
|
|
315
|
+
The client reconnects with `?lastSyncId=<id>` so the server can replay missed events. See [agent-docs/07-realtime-sync.md](agent-docs/07-realtime-sync.md) for SSE details and [agent-docs/05-sync-groups.md](agent-docs/05-sync-groups.md) for scoped event delivery.
|
|
313
316
|
|
|
314
317
|
## Run the demo
|
|
315
318
|
|
|
@@ -341,7 +344,7 @@ Deeper material lives in [agent-docs/](agent-docs/):
|
|
|
341
344
|
## Project structure
|
|
342
345
|
|
|
343
346
|
```text
|
|
344
|
-
. # the publishable zerodrift package
|
|
347
|
+
. # the publishable zerodrift package
|
|
345
348
|
|-- src/
|
|
346
349
|
|-- agent-docs/ # architecture and API notes
|
|
347
350
|
`-- examples/ # self-contained runnable demo (own Makefile + compose)
|
|
@@ -356,3 +359,20 @@ Deeper material lives in [agent-docs/](agent-docs/):
|
|
|
356
359
|
- **Client**: TypeScript, MobX, IndexedDB, EventSource (SSE)
|
|
357
360
|
- **Reference server**: Go, Gin, Bun ORM, Postgres (LISTEN/NOTIFY), pgx
|
|
358
361
|
- **Protocol**: append-only changelog, monotonic sync id, sync group filtering
|
|
362
|
+
|
|
363
|
+
## Acknowledgments
|
|
364
|
+
|
|
365
|
+
zerodrift was informed by public writing and talks on local-first sync
|
|
366
|
+
engines. Two especially helpful references were Wenzhao Hu's "Reverse
|
|
367
|
+
Engineering Linear's Sync Engine: A Detailed Study"
|
|
368
|
+
([wzhudev/reverse-linear-sync-engine](https://github.com/wzhudev/reverse-linear-sync-engine))
|
|
369
|
+
and Tuomas Artman's React Helsinki talk on
|
|
370
|
+
[Linear's realtime sync](https://www.youtube.com/watch?v=WxK11RsLqp4).
|
|
371
|
+
|
|
372
|
+
This project is an independent TypeScript implementation and is not affiliated
|
|
373
|
+
with Linear.
|
|
374
|
+
|
|
375
|
+
## License
|
|
376
|
+
|
|
377
|
+
MIT — see [LICENSE](LICENSE). The MIT grant covers zerodrift's own code.
|
|
378
|
+
See [NOTICE](NOTICE) for inspiration and attribution notes.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zerodrift",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "A TypeScript local-first sync engine: synchronous in-memory reads, optimistic writes, realtime SSE sync, offline IndexedDB persistence. Runs in the browser and in Node.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -49,7 +49,8 @@
|
|
|
49
49
|
"./package.json": "./package.json"
|
|
50
50
|
},
|
|
51
51
|
"files": [
|
|
52
|
-
"dist"
|
|
52
|
+
"dist",
|
|
53
|
+
"NOTICE"
|
|
53
54
|
],
|
|
54
55
|
"engines": {
|
|
55
56
|
"node": ">=18"
|