@live-state/sync 0.0.1-alpha.1

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/server.js ADDED
@@ -0,0 +1 @@
1
+ import {nanoid}from'nanoid';import {z}from'zod';var y=class{},S=class extends y{storage={};async updateSchema(e){console.log("Updating schema",e),this.storage=Object.entries(e).reduce((r,[s,i])=>(r[i.name]={},r),{});}async findById(e,r){var s;return (s=this.storage[e])==null?void 0:s[r]}async find(e,r){return this.storage[e]??{}}async upsert(e,r,s){return this.storage[e]??={},this.storage[e][r]=s,s}};var d=z.string().nanoid(),v=z.object({_id:d,type:z.literal("SUBSCRIBE"),resource:z.string()}),q=z.object({_id:d,type:z.literal("SYNC"),lastSyncedAt:z.string().optional(),resources:z.string().array().optional()}),R=z.record(z.object({value:z.string().or(z.number()).or(z.boolean()).or(z.date()),_meta:z.object({timestamp:z.string().optional()}).optional()})).superRefine((a,e)=>{a.id&&e.addIssue({code:z.ZodIssueCode.custom,message:"Payload cannot have an id"});}),T=z.object({_id:d,type:z.literal("MUTATE"),resource:z.string(),resourceId:z.string(),payload:R}),b=z.union([v,T,q]),x=z.object({_id:d,type:z.literal("SYNC"),resource:z.string(),data:z.record(R)}),j=z.object({_id:d,type:z.literal("REJECT"),resource:z.string()});z.union([T,x,j]);var _=a=>{let e={},r={};return a.subscribeToMutations(s=>{console.log("Mutation propagated:",s),Object.entries(r[s.resource]??{}).forEach(([i,p])=>{var o;(o=e[i])==null||o.send(JSON.stringify(s));});}),s=>{let i=nanoid();e[i]=s,console.log("Client connected:",i),s.on("message",async p=>{try{console.log("Message received from the client:",p);let o=b.parse(JSON.parse(p.toString()));if(o.type==="SUBSCRIBE"){let{resource:n}=o;r[n]||(r[n]={}),r[n][i]={};}else if(o.type==="SYNC"){let{resources:n}=o,c=n??Object.keys(a.schema);console.log("Syncing resources:",c),await Promise.all(c.map(async u=>{let l=await a.handleRequest({req:{type:"FIND",resourceName:u,context:{}}});if(!l||!l.data)throw new Error("Invalid resource");s.send(JSON.stringify({_id:o._id,type:"SYNC",resource:u,data:Object.fromEntries(Object.entries(l.data??{}).map(([f,M])=>[f,M.value]))}));}));}else if(o.type==="MUTATE"){let{resource:n}=o;console.log("Received mutation from client:",o);try{let c=await a.handleRequest({req:{type:"SET",resourceName:n,payload:o.payload,context:{messageId:o._id},resourceId:o.resourceId}}).catch(u=>(console.error("Error handling mutation from the client:",u),null));(!c||!c.acceptedValues||Object.keys(c.acceptedValues).length===0)&&s.send(JSON.stringify({_id:o._id,type:"REJECT",resource:n}));}catch(c){s.send(JSON.stringify({_id:o._id,type:"REJECT",resource:n})),console.error("Error parsing mutation from the client:",c);}}}catch(o){console.error("Error handling message from the client:",o);}}),s.on("close",()=>{console.log("Connection closed",i),delete e[i];});}};var g=class a{routes;constructor(e){this.routes=e.routes;}static create(e){return new a(e)}},V=a=>g.create({...a}),h=class{shape;constructor(e){this.shape=e;}async handleFind(e){return {data:await e.db.find(e.req.resourceName,e.req.where),acceptedValues:null}}async handleSet(e){if(!e.req.payload)throw new Error("Payload is required");if(!e.req.resourceId)throw new Error("ResourceId is required");let r=await e.db.findById(e.req.resourceName,e.req.resourceId),[s,i]=this.shape.mergeMutation("set",e.req.payload,r);if(!i){if(!r)throw new Error("Mutation rejected");return {data:r,acceptedValues:null}}return {data:await e.db.upsert(e.req.resourceName,e.req.resourceId,s),acceptedValues:i}}async handleRequest(e){switch(e.req.type){case "FIND":return this.handleFind(e);case "SET":return this.handleSet(e);default:throw new Error("Invalid request type")}}},Z=()=>a=>new h(a),m=class a{router;storage;schema;mutationSubscriptions=new Set;constructor(e){this.router=e.router,this.storage=e.storage,this.schema=e.schema,this.storage.updateSchema(this.schema);}static create(e){return new a(e)}subscribeToMutations(e){return this.mutationSubscriptions.add(e),()=>{this.mutationSubscriptions.delete(e);}}async handleRequest(e){var s;let r=await((s=this.router.routes[e.req.resourceName])==null?void 0:s.handleRequest({req:e.req,db:this.storage}));return r&&e.req.type==="SET"&&r.acceptedValues&&Object.keys(r.acceptedValues).length>0&&this.mutationSubscriptions.forEach(i=>{i({_id:e.req.context.messageId??nanoid(),type:"MUTATE",resource:e.req.resourceName,payload:r.acceptedValues??{},resourceId:e.req.resourceId});}),r}},k=m.create;export{S as InMemoryStorage,h as Route,g as Router,m as Server,y as Storage,Z as routeFactory,V as router,k as server,_ as webSocketAdapter};
package/package.json ADDED
@@ -0,0 +1,76 @@
1
+ {
2
+ "name": "@live-state/sync",
3
+ "version": "0.0.1-alpha.1",
4
+ "private": false,
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "description": "A sync engine with built-in client store and ORM.",
9
+ "author": "Pedro Costa",
10
+ "license": "Apache-2.0",
11
+ "homepage": "https://github.com/pedroscosta/live-state",
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/pedroscosta/live-state.git",
15
+ "directory": "packages/live-state"
16
+ },
17
+ "main": "./dist/index.js",
18
+ "types": "./dist/index.d.ts",
19
+ "files": [
20
+ "dist/**"
21
+ ],
22
+ "type": "module",
23
+ "scripts": {
24
+ "build": "tsup",
25
+ "dev": "tsup --watch",
26
+ "lint": "eslint src/",
27
+ "typecheck": "tsc --noEmit",
28
+ "test": "jest"
29
+ },
30
+ "devDependencies": {
31
+ "@jest/globals": "^29.7.0",
32
+ "@repo/typescript-config": "workspace:*",
33
+ "@types/express-ws": "^3.0.5",
34
+ "@types/node": "^20.11.24",
35
+ "@types/ws": "^8.5.13",
36
+ "jest": "^29.7.0",
37
+ "react": "18.0.0",
38
+ "tsup": "^8.0.2",
39
+ "typescript": "5.5.4"
40
+ },
41
+ "dependencies": {
42
+ "express-ws": "^5.0.2",
43
+ "nanoid": "^5.0.9",
44
+ "ws": "^8.18.0",
45
+ "zod": "^3.24.1"
46
+ },
47
+ "exports": {
48
+ ".": {
49
+ "require": "./dist/index.js",
50
+ "types": "./src/index.ts",
51
+ "import": "./dist/index.js"
52
+ },
53
+ "./server": {
54
+ "types": "./src/server/index.ts",
55
+ "import": "./dist/server.js",
56
+ "require": "./dist/server.js"
57
+ },
58
+ "./client": {
59
+ "types": "./src/client/index.ts",
60
+ "import": "./dist/client.js",
61
+ "require": "./dist/client.js"
62
+ }
63
+ },
64
+ "peerDependencies": {
65
+ "@types/react": ">=18.0.0",
66
+ "react": ">=18.0.0"
67
+ },
68
+ "peerDependenciesMeta": {
69
+ "react": {
70
+ "optional": true
71
+ },
72
+ "@types/react": {
73
+ "optional": true
74
+ }
75
+ }
76
+ }