@live-state/sync 0.0.1-alpha.2 → 0.0.1-alpha.3

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.d.cts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { z, ZodTypeAny } from 'zod';
2
2
  import { LiveObjectAny, Schema, MaterializedLiveType, WhereClause } from './index.cjs';
3
3
  import { PostgresPool } from 'kysely';
4
- import WebSocket from 'ws';
4
+ import { Application } from 'express-ws';
5
5
 
6
6
  declare const mutationSchema: z.ZodUnion<[z.ZodObject<z.objectUtil.extendShape<{
7
7
  id: z.ZodOptional<z.ZodString>;
@@ -9,17 +9,17 @@ declare const mutationSchema: z.ZodUnion<[z.ZodObject<z.objectUtil.extendShape<{
9
9
  resource: z.ZodString;
10
10
  }, {
11
11
  procedure: z.ZodString;
12
- payload: z.ZodAny;
12
+ payload: z.ZodOptional<z.ZodAny>;
13
13
  }>, "strip", z.ZodTypeAny, {
14
+ procedure: string;
14
15
  type: "MUTATE";
15
16
  resource: string;
16
- procedure: string;
17
17
  id?: string | undefined;
18
18
  payload?: any;
19
19
  }, {
20
+ procedure: string;
20
21
  type: "MUTATE";
21
22
  resource: string;
22
- procedure: string;
23
23
  id?: string | undefined;
24
24
  payload?: any;
25
25
  }>, z.ZodObject<z.objectUtil.extendShape<{
@@ -60,6 +60,7 @@ declare const mutationSchema: z.ZodUnion<[z.ZodObject<z.objectUtil.extendShape<{
60
60
  }>>;
61
61
  }>, "strip", z.ZodTypeAny, {
62
62
  type: "MUTATE";
63
+ resourceId: string;
63
64
  resource: string;
64
65
  payload: Record<string, {
65
66
  value: string | number | boolean | Date;
@@ -67,10 +68,10 @@ declare const mutationSchema: z.ZodUnion<[z.ZodObject<z.objectUtil.extendShape<{
67
68
  timestamp?: string | undefined;
68
69
  } | undefined;
69
70
  }>;
70
- resourceId: string;
71
71
  id?: string | undefined;
72
72
  }, {
73
73
  type: "MUTATE";
74
+ resourceId: string;
74
75
  resource: string;
75
76
  payload: Record<string, {
76
77
  value: string | number | boolean | Date;
@@ -78,7 +79,6 @@ declare const mutationSchema: z.ZodUnion<[z.ZodObject<z.objectUtil.extendShape<{
78
79
  timestamp?: string | undefined;
79
80
  } | undefined;
80
81
  }>;
81
- resourceId: string;
82
82
  id?: string | undefined;
83
83
  }>]>;
84
84
  type RawMutationRequest = z.infer<typeof mutationSchema>;
@@ -104,7 +104,7 @@ type MutationResult<TShape extends LiveObjectAny> = {
104
104
  acceptedValues: Record<string, any> | null;
105
105
  };
106
106
  type RequestHandler<TInput, TResult, TSchema extends Schema<any> = Schema<any>> = (opts: {
107
- req: Request<TInput>;
107
+ req: ParsedRequest<TInput>;
108
108
  db: Storage;
109
109
  schema: TSchema;
110
110
  }) => Promise<TResult>;
@@ -125,16 +125,23 @@ declare class Route<TResourceSchema extends LiveObjectAny, TMiddleware extends M
125
125
  private handleFind;
126
126
  private handleSet;
127
127
  handleRequest(opts: {
128
- req: Request;
128
+ req: ParsedRequest;
129
129
  db: Storage;
130
130
  schema: Schema<any>;
131
131
  }): Promise<any>;
132
- use(middleware: TMiddleware): this;
132
+ use(...middlewares: TMiddleware[]): this;
133
133
  withMutations<T extends Record<string, Mutation<any, RequestHandler<any, any>>>>(mutationFactory: (opts: {
134
134
  mutation: typeof mutationCreator;
135
135
  }) => T): Route<TResourceSchema, TMiddleware, T>;
136
136
  }
137
- declare const routeFactory: () => <T extends LiveObjectAny>(shape: T) => Route<T, Middleware<any>, Record<string, never>>;
137
+ declare class RouteFactory {
138
+ private middlewares;
139
+ private constructor();
140
+ createBasicRoute<T extends LiveObjectAny>(shape: T): Route<T, Middleware<any>, Record<string, never>>;
141
+ use(...middlewares: Middleware<any>[]): RouteFactory;
142
+ static create(): RouteFactory;
143
+ }
144
+ declare const routeFactory: typeof RouteFactory.create;
138
145
  type AnyRoute = Route<LiveObjectAny, Middleware<any>, Record<string, any>>;
139
146
 
140
147
  declare abstract class Storage {
@@ -162,14 +169,14 @@ declare class SQLStorage extends Storage {
162
169
  private applyWhere;
163
170
  }
164
171
 
165
- type Subscription = {
166
- filters?: Record<string, any>;
167
- };
168
- declare const webSocketAdapter: (server: Server<AnyRouter>) => (ws: WebSocket, request: any) => void;
172
+ declare const expressAdapter: (app: Application, server: Server<AnyRouter>, options?: {
173
+ basePath?: string;
174
+ }) => void;
169
175
 
170
- type Request<TInput = any> = {
176
+ type ParsedRequest<TInput = any> = {
171
177
  headers: Record<string, string>;
172
178
  cookies: Record<string, string>;
179
+ query: Record<string, string>;
173
180
  resourceName: string;
174
181
  procedure?: string;
175
182
  context: Record<string, any>;
@@ -178,11 +185,14 @@ type Request<TInput = any> = {
178
185
  resourceId?: string;
179
186
  input?: TInput;
180
187
  };
181
- type RequestType = Request["type"];
188
+ type ContextProvider = (req: Pick<ParsedRequest, "headers" | "cookies" | "query"> & {
189
+ transport: "HTTP" | "WEBSOCKET";
190
+ }) => Record<string, any>;
191
+ type RequestType = ParsedRequest["type"];
182
192
  type MutationHandler = (mutation: RawMutationRequest) => void;
183
- type NextFunction<T> = (req: Request) => Promise<T> | T;
184
- type Middleware<T> = (opts: {
185
- req: Request;
193
+ type NextFunction<T> = (req: ParsedRequest) => Promise<T> | T;
194
+ type Middleware<T = any> = (opts: {
195
+ req: ParsedRequest;
186
196
  next: NextFunction<T>;
187
197
  }) => ReturnType<NextFunction<T>>;
188
198
  declare class Server<TRouter extends AnyRouter> {
@@ -190,19 +200,23 @@ declare class Server<TRouter extends AnyRouter> {
190
200
  readonly storage: Storage;
191
201
  readonly schema: Schema<any>;
192
202
  readonly middlewares: Set<Middleware<any>>;
203
+ contextProvider?: ContextProvider;
193
204
  private mutationSubscriptions;
194
205
  private constructor();
195
206
  static create<TRouter extends AnyRouter>(opts: {
196
207
  router: TRouter;
197
208
  storage: Storage;
198
209
  schema: Schema<any>;
210
+ middlewares?: Middleware<any>[];
211
+ contextProvider?: ContextProvider;
199
212
  }): Server<TRouter>;
200
213
  subscribeToMutations(handler: MutationHandler): () => void;
201
214
  handleRequest(opts: {
202
- req: Request;
215
+ req: ParsedRequest;
203
216
  }): Promise<any>;
204
217
  use(middleware: Middleware<any>): this;
218
+ context(contextProvider: ContextProvider): this;
205
219
  }
206
220
  declare const server: typeof Server.create;
207
221
 
208
- export { type AnyRoute, type AnyRouter, InMemoryStorage, type Middleware, type Mutation, type MutationHandler, type MutationResult, type NextFunction, type QueryResult, type Request, type RequestHandler, type RequestType, Route, type RouteRecord, Router, SQLStorage, Server, Storage, type Subscription, routeFactory, router, server, webSocketAdapter };
222
+ export { type AnyRoute, type AnyRouter, type ContextProvider, InMemoryStorage, type Middleware, type Mutation, type MutationHandler, type MutationResult, type NextFunction, type ParsedRequest, type QueryResult, type RequestHandler, type RequestType, Route, RouteFactory, type RouteRecord, Router, SQLStorage, Server, Storage, expressAdapter, routeFactory, router, server };
package/dist/server.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { z, ZodTypeAny } from 'zod';
2
2
  import { LiveObjectAny, Schema, MaterializedLiveType, WhereClause } from './index.js';
3
3
  import { PostgresPool } from 'kysely';
4
- import WebSocket from 'ws';
4
+ import { Application } from 'express-ws';
5
5
 
6
6
  declare const mutationSchema: z.ZodUnion<[z.ZodObject<z.objectUtil.extendShape<{
7
7
  id: z.ZodOptional<z.ZodString>;
@@ -9,17 +9,17 @@ declare const mutationSchema: z.ZodUnion<[z.ZodObject<z.objectUtil.extendShape<{
9
9
  resource: z.ZodString;
10
10
  }, {
11
11
  procedure: z.ZodString;
12
- payload: z.ZodAny;
12
+ payload: z.ZodOptional<z.ZodAny>;
13
13
  }>, "strip", z.ZodTypeAny, {
14
+ procedure: string;
14
15
  type: "MUTATE";
15
16
  resource: string;
16
- procedure: string;
17
17
  id?: string | undefined;
18
18
  payload?: any;
19
19
  }, {
20
+ procedure: string;
20
21
  type: "MUTATE";
21
22
  resource: string;
22
- procedure: string;
23
23
  id?: string | undefined;
24
24
  payload?: any;
25
25
  }>, z.ZodObject<z.objectUtil.extendShape<{
@@ -60,6 +60,7 @@ declare const mutationSchema: z.ZodUnion<[z.ZodObject<z.objectUtil.extendShape<{
60
60
  }>>;
61
61
  }>, "strip", z.ZodTypeAny, {
62
62
  type: "MUTATE";
63
+ resourceId: string;
63
64
  resource: string;
64
65
  payload: Record<string, {
65
66
  value: string | number | boolean | Date;
@@ -67,10 +68,10 @@ declare const mutationSchema: z.ZodUnion<[z.ZodObject<z.objectUtil.extendShape<{
67
68
  timestamp?: string | undefined;
68
69
  } | undefined;
69
70
  }>;
70
- resourceId: string;
71
71
  id?: string | undefined;
72
72
  }, {
73
73
  type: "MUTATE";
74
+ resourceId: string;
74
75
  resource: string;
75
76
  payload: Record<string, {
76
77
  value: string | number | boolean | Date;
@@ -78,7 +79,6 @@ declare const mutationSchema: z.ZodUnion<[z.ZodObject<z.objectUtil.extendShape<{
78
79
  timestamp?: string | undefined;
79
80
  } | undefined;
80
81
  }>;
81
- resourceId: string;
82
82
  id?: string | undefined;
83
83
  }>]>;
84
84
  type RawMutationRequest = z.infer<typeof mutationSchema>;
@@ -104,7 +104,7 @@ type MutationResult<TShape extends LiveObjectAny> = {
104
104
  acceptedValues: Record<string, any> | null;
105
105
  };
106
106
  type RequestHandler<TInput, TResult, TSchema extends Schema<any> = Schema<any>> = (opts: {
107
- req: Request<TInput>;
107
+ req: ParsedRequest<TInput>;
108
108
  db: Storage;
109
109
  schema: TSchema;
110
110
  }) => Promise<TResult>;
@@ -125,16 +125,23 @@ declare class Route<TResourceSchema extends LiveObjectAny, TMiddleware extends M
125
125
  private handleFind;
126
126
  private handleSet;
127
127
  handleRequest(opts: {
128
- req: Request;
128
+ req: ParsedRequest;
129
129
  db: Storage;
130
130
  schema: Schema<any>;
131
131
  }): Promise<any>;
132
- use(middleware: TMiddleware): this;
132
+ use(...middlewares: TMiddleware[]): this;
133
133
  withMutations<T extends Record<string, Mutation<any, RequestHandler<any, any>>>>(mutationFactory: (opts: {
134
134
  mutation: typeof mutationCreator;
135
135
  }) => T): Route<TResourceSchema, TMiddleware, T>;
136
136
  }
137
- declare const routeFactory: () => <T extends LiveObjectAny>(shape: T) => Route<T, Middleware<any>, Record<string, never>>;
137
+ declare class RouteFactory {
138
+ private middlewares;
139
+ private constructor();
140
+ createBasicRoute<T extends LiveObjectAny>(shape: T): Route<T, Middleware<any>, Record<string, never>>;
141
+ use(...middlewares: Middleware<any>[]): RouteFactory;
142
+ static create(): RouteFactory;
143
+ }
144
+ declare const routeFactory: typeof RouteFactory.create;
138
145
  type AnyRoute = Route<LiveObjectAny, Middleware<any>, Record<string, any>>;
139
146
 
140
147
  declare abstract class Storage {
@@ -162,14 +169,14 @@ declare class SQLStorage extends Storage {
162
169
  private applyWhere;
163
170
  }
164
171
 
165
- type Subscription = {
166
- filters?: Record<string, any>;
167
- };
168
- declare const webSocketAdapter: (server: Server<AnyRouter>) => (ws: WebSocket, request: any) => void;
172
+ declare const expressAdapter: (app: Application, server: Server<AnyRouter>, options?: {
173
+ basePath?: string;
174
+ }) => void;
169
175
 
170
- type Request<TInput = any> = {
176
+ type ParsedRequest<TInput = any> = {
171
177
  headers: Record<string, string>;
172
178
  cookies: Record<string, string>;
179
+ query: Record<string, string>;
173
180
  resourceName: string;
174
181
  procedure?: string;
175
182
  context: Record<string, any>;
@@ -178,11 +185,14 @@ type Request<TInput = any> = {
178
185
  resourceId?: string;
179
186
  input?: TInput;
180
187
  };
181
- type RequestType = Request["type"];
188
+ type ContextProvider = (req: Pick<ParsedRequest, "headers" | "cookies" | "query"> & {
189
+ transport: "HTTP" | "WEBSOCKET";
190
+ }) => Record<string, any>;
191
+ type RequestType = ParsedRequest["type"];
182
192
  type MutationHandler = (mutation: RawMutationRequest) => void;
183
- type NextFunction<T> = (req: Request) => Promise<T> | T;
184
- type Middleware<T> = (opts: {
185
- req: Request;
193
+ type NextFunction<T> = (req: ParsedRequest) => Promise<T> | T;
194
+ type Middleware<T = any> = (opts: {
195
+ req: ParsedRequest;
186
196
  next: NextFunction<T>;
187
197
  }) => ReturnType<NextFunction<T>>;
188
198
  declare class Server<TRouter extends AnyRouter> {
@@ -190,19 +200,23 @@ declare class Server<TRouter extends AnyRouter> {
190
200
  readonly storage: Storage;
191
201
  readonly schema: Schema<any>;
192
202
  readonly middlewares: Set<Middleware<any>>;
203
+ contextProvider?: ContextProvider;
193
204
  private mutationSubscriptions;
194
205
  private constructor();
195
206
  static create<TRouter extends AnyRouter>(opts: {
196
207
  router: TRouter;
197
208
  storage: Storage;
198
209
  schema: Schema<any>;
210
+ middlewares?: Middleware<any>[];
211
+ contextProvider?: ContextProvider;
199
212
  }): Server<TRouter>;
200
213
  subscribeToMutations(handler: MutationHandler): () => void;
201
214
  handleRequest(opts: {
202
- req: Request;
215
+ req: ParsedRequest;
203
216
  }): Promise<any>;
204
217
  use(middleware: Middleware<any>): this;
218
+ context(contextProvider: ContextProvider): this;
205
219
  }
206
220
  declare const server: typeof Server.create;
207
221
 
208
- export { type AnyRoute, type AnyRouter, InMemoryStorage, type Middleware, type Mutation, type MutationHandler, type MutationResult, type NextFunction, type QueryResult, type Request, type RequestHandler, type RequestType, Route, type RouteRecord, Router, SQLStorage, Server, Storage, type Subscription, routeFactory, router, server, webSocketAdapter };
222
+ export { type AnyRoute, type AnyRouter, type ContextProvider, InMemoryStorage, type Middleware, type Mutation, type MutationHandler, type MutationResult, type NextFunction, type ParsedRequest, type QueryResult, type RequestHandler, type RequestType, Route, RouteFactory, type RouteRecord, Router, SQLStorage, Server, Storage, expressAdapter, routeFactory, router, server };
package/dist/server.js CHANGED
@@ -1 +1,2 @@
1
- import {a,b}from'./chunk-EK7ODJWE.js';import {z}from'zod';import {Kysely,PostgresDialect}from'kysely';import S from'node:crypto';var P=a(M=>{Object.defineProperty(M,"__esModule",{value:true});M.parse=ne;M.serialize=ie;var Y=/^[\u0021-\u003A\u003C\u003E-\u007E]+$/,J=/^[\u0021-\u003A\u003C-\u007E]*$/,X=/^([.]?[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)([.][a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)*$/i,ee=/^[\u0020-\u003A\u003D-\u007E]*$/,te=Object.prototype.toString,re=(()=>{let r=function(){};return r.prototype=Object.create(null),r})();function ne(r,e){let t=new re,n=r.length;if(n<2)return t;let i=(e==null?void 0:e.decode)||ae,a=0;do{let c=r.indexOf("=",a);if(c===-1)break;let u=r.indexOf(";",a),o=u===-1?n:u;if(c>o){a=r.lastIndexOf(";",c-1)+1;continue}let s=C(r,a,c),p=z(r,c,s),m=r.slice(s,p);if(t[m]===void 0){let b=C(r,c+1,o),y=z(r,o,b),f=i(r.slice(b,y));t[m]=f;}a=o+1;}while(a<n);return t}function C(r,e,t){do{let n=r.charCodeAt(e);if(n!==32&&n!==9)return e}while(++e<t);return t}function z(r,e,t){for(;e>t;){let n=r.charCodeAt(--e);if(n!==32&&n!==9)return e+1}return t}function ie(r,e,t){let n=(t==null?void 0:t.encode)||encodeURIComponent;if(!Y.test(r))throw new TypeError(`argument name is invalid: ${r}`);let i=n(e);if(!J.test(i))throw new TypeError(`argument val is invalid: ${e}`);let a=r+"="+i;if(!t)return a;if(t.maxAge!==void 0){if(!Number.isInteger(t.maxAge))throw new TypeError(`option maxAge is invalid: ${t.maxAge}`);a+="; Max-Age="+t.maxAge;}if(t.domain){if(!X.test(t.domain))throw new TypeError(`option domain is invalid: ${t.domain}`);a+="; Domain="+t.domain;}if(t.path){if(!ee.test(t.path))throw new TypeError(`option path is invalid: ${t.path}`);a+="; Path="+t.path;}if(t.expires){if(!oe(t.expires)||!Number.isFinite(t.expires.valueOf()))throw new TypeError(`option expires is invalid: ${t.expires}`);a+="; Expires="+t.expires.toUTCString();}if(t.httpOnly&&(a+="; HttpOnly"),t.secure&&(a+="; Secure"),t.partitioned&&(a+="; Partitioned"),t.priority)switch(typeof t.priority=="string"?t.priority.toLowerCase():void 0){case "low":a+="; Priority=Low";break;case "medium":a+="; Priority=Medium";break;case "high":a+="; Priority=High";break;default:throw new TypeError(`option priority is invalid: ${t.priority}`)}if(t.sameSite)switch(typeof t.sameSite=="string"?t.sameSite.toLowerCase():t.sameSite){case true:case "strict":a+="; SameSite=Strict";break;case "lax":a+="; SameSite=Lax";break;case "none":a+="; SameSite=None";break;default:throw new TypeError(`option sameSite is invalid: ${t.sameSite}`)}return a}function ae(r){if(r.indexOf("%")===-1)return r;try{return decodeURIComponent(r)}catch{return r}}function oe(r){return te.call(r)==="[object Date]"}});var w=class r{routes;constructor(e){this.routes=e.routes;}static create(e){return new r(e)}},Se=r=>w.create({...r}),W=r=>({handler:e=>({inputValidator:r??z.undefined(),handler:e})}),v=class r{_resourceSchema;resourceName;middlewares;customMutations;constructor(e,t){this.resourceName=e,this.middlewares=new Set,this.customMutations=t??{};}handleFind=async({req:e,db:t})=>({data:await t.find(e.resourceName,e.where),acceptedValues:null});handleSet=async({req:e,db:t,schema:n})=>{if(!e.input)throw new Error("Payload is required");if(!e.resourceId)throw new Error("ResourceId is required");let i=await t.findById(e.resourceName,e.resourceId),[a,c]=n[this.resourceName].mergeMutation("set",e.input,i);if(!c)throw new Error("Mutation rejected");return {data:await t.upsert(e.resourceName,e.resourceId,a),acceptedValues:c}};async handleRequest(e){let t=n=>(()=>{if(n.type==="QUERY")return this.handleFind({req:n,db:e.db,schema:e.schema});if(n.type==="MUTATE")if(n.procedure){if(this.customMutations[n.procedure]){let i=this.customMutations[n.procedure].inputValidator.parse(n.input);return n.input=i,this.customMutations[n.procedure].handler({req:n,db:e.db,schema:e.schema})}}else return this.handleSet({req:n,db:e.db,schema:e.schema});throw new Error("Invalid request")})();return await Array.from(this.middlewares.values()).reduceRight((n,i)=>a=>i({req:a,next:n}),async n=>t(n))(e.req)}use(e){return this.middlewares.add(e),this}withMutations(e){return new r(this.resourceName,e({mutation:W}))}},we=()=>r=>new v(r.name);var R=class{},O=class extends R{storage={};async updateSchema(e){this.storage=Object.entries(e).reduce((t,[n,i])=>(t[i.name]={},t),{});}async findById(e,t){var n;return (n=this.storage[e])==null?void 0:n[t]}async find(e,t){return this.storage[e]??{}}async upsert(e,t,n){return this.storage[e]??={},this.storage[e][t]=n,n}},j=class extends R{db;schema;constructor(e){super(),this.db=new Kysely({dialect:new PostgresDialect({pool:e})});}async updateSchema(e){this.schema=e;let t=await this.db.introspection.getTables();for(let[n,i]of Object.entries(e)){let a=t.find(o=>o.name===n);a||await this.db.schema.createTable(n).ifNotExists().execute();let c=`${n}_meta`,u=t.find(o=>o.name===c);u||await this.db.schema.createTable(c).ifNotExists().execute();for(let[o,s]of Object.entries(i.fields)){let p=a==null?void 0:a.columns.find(y=>y.name===o),m=s.getStorageFieldType();p?p.dataType!==m.type&&console.error("Column type mismatch:",o,"expected to have type:",m.type,"but has type:",p.dataType):(await this.db.schema.alterTable(n).addColumn(o,m.type,y=>{let f=y;return m.unique&&(f=f.unique()),m.nullable||(f=f.notNull()),m.references&&(f=f.references(m.references)),m.primary&&(f=f.primaryKey()),f}).execute().catch(y=>{throw console.error("Error adding column",o,y),y}),m.index&&await this.db.schema.createIndex(`${n}_${o}_index`).on(n).column(o).execute().catch(y=>{})),(u==null?void 0:u.columns.find(y=>y.name===o))||await this.db.schema.alterTable(c).addColumn(o,"varchar",y=>{let f=y;return m.primary&&(f=f.primaryKey().references(`${n}.${o}`)),f}).execute();}}}async findById(e,t){let n=await this.db.selectFrom(e).where("id","=",t).selectAll(e).executeTakeFirst(),i=await this.db.selectFrom(`${e}_meta`).where("id","=",t).selectAll(`${e}_meta`).executeTakeFirst();if(!(!n||!i))return this.convertToMaterializedLiveType(n,i)}async find(e,t){let i=await this.applyWhere(e,this.db.selectFrom(e).selectAll(e),t).execute(),a=Object.fromEntries(i.map(o=>{let{id:s,...p}=o;return [s,p]}));if(Object.keys(a).length===0)return {};let c=Object.fromEntries((await this.db.selectFrom(`${e}_meta`).selectAll().where("id","in",Object.keys(a)).execute()).map(o=>{let{id:s,...p}=o;return [s,p]}));return Object.entries(a).reduce((o,[s,p])=>(c[s]&&(o[s]=this.convertToMaterializedLiveType(p,c[s])),o),{})}async upsert(e,t,n){return await this.db.transaction().execute(async i=>{let a=!!await i.selectFrom(e).select("id").where("id","=",t).executeTakeFirst(),c={},u={};for(let[o,s]of Object.entries(n.value))c[o]=s.value,u[o]=s._meta.timestamp;a?await Promise.all([i.updateTable(e).set(c).where("id","=",t).execute(),i.updateTable(`${e}_meta`).set(u).where("id","=",t).execute()]):await Promise.all([i.insertInto(e).values({...c,id:t}).execute(),i.insertInto(`${e}_meta`).values({...u,id:t}).execute()]);}),n}convertToMaterializedLiveType(e,t){return {value:Object.fromEntries(Object.entries(e).flatMap(([n,i])=>t?[[n,{value:i,_meta:{timestamp:t==null?void 0:t[n]}}]]:[]))}}applyWhere(e,t,n){if(!n)return t;if(!this.schema)throw new Error("Schema not initialized");let i=this.schema[e];if(!i)throw new Error("Resource not found");for(let[a,c]of Object.entries(n))if(i.fields[a])t=t.where(`${e}.${a}`,"=",c);else if(i.relations[a]){let u=i.relations[a],o=u.entity.name,s=u.type==="one"?"id":u.foreignColumn,p=u.type==="one"?u.relationalColumn:"id";t=t.leftJoin(o,`${o}.${s}`,`${e}.${p}`),t=this.applyWhere(o,t,c);}return t}};var Z=b(P(),1);z.object({type:z.literal("QUERY"),resource:z.string(),where:z.record(z.any()).optional(),include:z.record(z.any()).optional()});var I=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((r,e)=>{r.id&&e.addIssue({code:z.ZodIssueCode.custom,message:"Payload cannot have an id"});}),U=z.object({id:z.string().optional(),type:z.literal("MUTATE"),resource:z.string()}),E=U.extend({procedure:z.string(),payload:z.any()}),A=U.extend({resourceId:z.string(),payload:I});z.union([E,A]);var h=z.string(),se=z.object({id:h,type:z.literal("SUBSCRIBE"),resource:z.string()}),ce=z.object({id:h,type:z.literal("SYNC"),lastSyncedAt:z.string().optional(),resources:z.string().array().optional(),where:z.record(z.any()).optional()}),D=A.extend({id:h}),ue=E.extend({id:h}),de=z.union([ue,D]),V=z.union([se,ce,de]),le=z.object({id:h,type:z.literal("SYNC"),resource:z.string(),data:z.record(I)}),pe=z.object({id:h,type:z.literal("REJECT"),resource:z.string(),message:z.string().optional()}),me=z.object({id:h,type:z.literal("REPLY"),data:z.any()});z.union([le,pe,me,D]);var k="0123456789ABCDEFGHJKMNPQRSTVWXYZ",x=32;var ye=16,_=10,$=0xffffffffffff;var g;(function(r){r.Base32IncorrectEncoding="B32_ENC_INVALID",r.DecodeTimeInvalidCharacter="DEC_TIME_CHAR",r.DecodeTimeValueMalformed="DEC_TIME_MALFORMED",r.EncodeTimeNegative="ENC_TIME_NEG",r.EncodeTimeSizeExceeded="ENC_TIME_SIZE_EXCEED",r.EncodeTimeValueMalformed="ENC_TIME_MALFORMED",r.PRNGDetectFailure="PRNG_DETECT",r.ULIDInvalid="ULID_INVALID",r.Unexpected="UNEXPECTED",r.UUIDInvalid="UUID_INVALID";})(g||(g={}));var T=class extends Error{constructor(e,t){super(`${t} (${e})`),this.name="ULIDError",this.code=e;}};function fe(r){let e=Math.floor(r()*x);return e===x&&(e=x-1),k.charAt(e)}function he(r){var n;let e=ge(),t=e&&(e.crypto||e.msCrypto)||(typeof S<"u"?S:null);if(typeof(t==null?void 0:t.getRandomValues)=="function")return ()=>{let i=new Uint8Array(1);return t.getRandomValues(i),i[0]/255};if(typeof(t==null?void 0:t.randomBytes)=="function")return ()=>t.randomBytes(1).readUInt8()/255;if((n=S)!=null&&n.randomBytes)return ()=>S.randomBytes(1).readUInt8()/255;throw new T(g.PRNGDetectFailure,"Failed to find a reliable PRNG")}function ge(){return xe()?self:typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:null}function Te(r,e){let t="";for(;r>0;r--)t=fe(e)+t;return t}function be(r,e=_){if(isNaN(r))throw new T(g.EncodeTimeValueMalformed,`Time must be a number: ${r}`);if(r>$)throw new T(g.EncodeTimeSizeExceeded,`Cannot encode a time larger than ${$}: ${r}`);if(r<0)throw new T(g.EncodeTimeNegative,`Time must be positive: ${r}`);if(Number.isInteger(r)===false)throw new T(g.EncodeTimeValueMalformed,`Time must be an integer: ${r}`);let t,n="";for(let i=e;i>0;i--)t=r%x,n=k.charAt(t)+n,r=(r-t)/x;return n}function xe(){return typeof WorkerGlobalScope<"u"&&self instanceof WorkerGlobalScope}function F(r,e){let t=he(),n=Date.now();return be(n,_)+Te(ye,t)}var L=()=>F().toLowerCase();var Ge=r=>{let e={},t={};return r.subscribeToMutations(n=>{let i=n;!i.resourceId||!i.payload||(console.log("Mutation propagated:",i),Object.entries(t[i.resource]??{}).forEach(([a,c])=>{var u;(u=e[a])==null||u.send(JSON.stringify({...i,id:i.id??L()}));}));}),(n,i)=>{let a=o=>{n.send(JSON.stringify(o));},c=L(),u={headers:i.headers,cookies:typeof i.headers.cookie=="string"?Z.default.parse(i.headers.cookie):{}};e[c]=n,console.log("Client connected:",c),n.on("message",async o=>{try{console.log("Message received from the client:",o);let s=V.parse(JSON.parse(o.toString()));if(s.type==="SUBSCRIBE"){let{resource:p}=s;t[p]||(t[p]={}),t[p][c]={};}else if(s.type==="SYNC"){let{resources:p}=s,m=p??Object.keys(r.schema);console.log("Syncing resources:",m),await Promise.all(m.map(async b=>{let y=await r.handleRequest({req:{...u,type:"QUERY",resourceName:b,context:{}}});if(!y||!y.data)throw new Error("Invalid resource");a({id:s.id,type:"SYNC",resource:b,data:Object.fromEntries(Object.entries(y.data??{}).map(([f,q])=>[f,q.value]))});}));}else if(s.type==="MUTATE"){let{resource:p}=s;console.log("Received mutation from client:",s);try{let m=await r.handleRequest({req:{...u,type:"MUTATE",resourceName:p,input:s.payload,context:{messageId:s.id},resourceId:s.resourceId,procedure:s.procedure}});s.procedure&&a({id:s.id,type:"REPLY",data:m});}catch(m){a({id:s.id,type:"REJECT",resource:p,message:m.message}),console.error("Error parsing mutation from the client:",m);}}}catch(s){console.error("Error handling message from the client:",s);}}),n.on("close",()=>{console.log("Connection closed",c),delete e[c];});}};var N=class r{router;storage;schema;middlewares=new Set;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 r(e)}subscribeToMutations(e){return this.mutationSubscriptions.add(e),()=>{this.mutationSubscriptions.delete(e);}}async handleRequest(e){if(!this.router.routes[e.req.resourceName])throw new Error("Invalid resource");let t=await Array.from(this.middlewares.values()).reduceRight((n,i)=>a=>i({req:a,next:n}),async n=>this.router.routes[e.req.resourceName].handleRequest({req:n,db:this.storage,schema:this.schema}))(e.req);return t&&e.req.type==="MUTATE"&&t.acceptedValues&&Object.keys(t.acceptedValues).length>0&&this.mutationSubscriptions.forEach(n=>{n({id:e.req.context.messageId,type:"MUTATE",resource:e.req.resourceName,payload:t.acceptedValues??{},resourceId:e.req.resourceId});}),t}use(e){return this.middlewares.add(e),this}},Qe=N.create;export{O as InMemoryStorage,v as Route,w as Router,j as SQLStorage,N as Server,R as Storage,we as routeFactory,Se as router,Qe as server,Ge as webSocketAdapter};
1
+ import {a,b}from'./chunk-EK7ODJWE.js';import he,{parse}from'qs';import {z as z$1}from'zod';import I from'node:crypto';import {Kysely,PostgresDialect}from'kysely';var P=a(E=>{Object.defineProperty(E,"__esModule",{value:true});E.parse=le;E.serialize=pe;var oe=/^[\u0021-\u003A\u003C\u003E-\u007E]+$/,ie=/^[\u0021-\u003A\u003C-\u007E]*$/,se=/^([.]?[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)([.][a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)*$/i,ce=/^[\u0020-\u003A\u003D-\u007E]*$/,ue=Object.prototype.toString,de=(()=>{let r=function(){};return r.prototype=Object.create(null),r})();function le(r,e){let t=new de,n=r.length;if(n<2)return t;let a=(e==null?void 0:e.decode)||me,o=0;do{let i=r.indexOf("=",o);if(i===-1)break;let u=r.indexOf(";",o),s=u===-1?n:u;if(i>s){o=r.lastIndexOf(";",i-1)+1;continue}let h=V(r,o,i),m=_(r,i,h),l=r.slice(h,m);if(t[l]===void 0){let p=V(r,i+1,s),d=_(r,s,p),c=a(r.slice(p,d));t[l]=c;}o=s+1;}while(o<n);return t}function V(r,e,t){do{let n=r.charCodeAt(e);if(n!==32&&n!==9)return e}while(++e<t);return t}function _(r,e,t){for(;e>t;){let n=r.charCodeAt(--e);if(n!==32&&n!==9)return e+1}return t}function pe(r,e,t){let n=(t==null?void 0:t.encode)||encodeURIComponent;if(!oe.test(r))throw new TypeError(`argument name is invalid: ${r}`);let a=n(e);if(!ie.test(a))throw new TypeError(`argument val is invalid: ${e}`);let o=r+"="+a;if(!t)return o;if(t.maxAge!==void 0){if(!Number.isInteger(t.maxAge))throw new TypeError(`option maxAge is invalid: ${t.maxAge}`);o+="; Max-Age="+t.maxAge;}if(t.domain){if(!se.test(t.domain))throw new TypeError(`option domain is invalid: ${t.domain}`);o+="; Domain="+t.domain;}if(t.path){if(!ce.test(t.path))throw new TypeError(`option path is invalid: ${t.path}`);o+="; Path="+t.path;}if(t.expires){if(!ye(t.expires)||!Number.isFinite(t.expires.valueOf()))throw new TypeError(`option expires is invalid: ${t.expires}`);o+="; Expires="+t.expires.toUTCString();}if(t.httpOnly&&(o+="; HttpOnly"),t.secure&&(o+="; Secure"),t.partitioned&&(o+="; Partitioned"),t.priority)switch(typeof t.priority=="string"?t.priority.toLowerCase():void 0){case "low":o+="; Priority=Low";break;case "medium":o+="; Priority=Medium";break;case "high":o+="; Priority=High";break;default:throw new TypeError(`option priority is invalid: ${t.priority}`)}if(t.sameSite)switch(typeof t.sameSite=="string"?t.sameSite.toLowerCase():t.sameSite){case true:case "strict":o+="; SameSite=Strict";break;case "lax":o+="; SameSite=Lax";break;case "none":o+="; SameSite=None";break;default:throw new TypeError(`option sameSite is invalid: ${t.sameSite}`)}return o}function me(r){if(r.indexOf("%")===-1)return r;try{return decodeURIComponent(r)}catch{return r}}function ye(r){return ue.call(r)==="[object Date]"}});var Z=b(P(),1);var $=z$1.object({type:z$1.literal("QUERY"),resource:z$1.string(),where:z$1.record(z$1.any()).optional(),include:z$1.record(z$1.any()).optional()}),L=z$1.record(z$1.object({value:z$1.string().or(z$1.number()).or(z$1.boolean()).or(z$1.date()),_meta:z$1.object({timestamp:z$1.string().optional()}).optional()})).superRefine((r,e)=>{r.id&&e.addIssue({code:z$1.ZodIssueCode.custom,message:"Payload cannot have an id"});}),q=z$1.object({id:z$1.string().optional(),type:z$1.literal("MUTATE"),resource:z$1.string()}),S=q.extend({procedure:z$1.string(),payload:z$1.any().optional()}),M=q.extend({resourceId:z$1.string(),payload:L});z$1.union([S,M]);var F=$.omit({type:true,resource:true}),N=S.omit({id:true,type:true,resource:true,procedure:true}),j=M.omit({id:true,type:true,resource:true});z$1.union([j,N]);var H=r=>async e=>{var t;try{let n=typeof e.headers.getSetCookie=="function"?Object.fromEntries(e.headers):e.headers,a={headers:n,cookies:n.cookie?Z.default.parse(n.cookie):{}},o=new URL(e.url),i=o.pathname.split("/"),u=o.searchParams,s=he.parse(u.toString()),h=await((t=r.contextProvider)==null?void 0:t.call(r,{transport:"HTTP",headers:a.headers,cookies:a.cookies,query:s}))??{};if(e.method==="GET"){let m=i[i.length-1],{success:l,data:p,error:d}=F.safeParse(s);if(!l)return Response.json({message:"Invalid query",code:"INVALID_QUERY",details:d},{status:400});let c=await r.handleRequest({req:{...a,type:"QUERY",resourceName:m,context:h,where:p.where,query:s}});return !c||!c.data?Response.json({message:"Invalid resource",code:"INVALID_RESOURCE"},{status:400}):Response.json(c.data)}if(e.method==="POST")try{let m=i[i.length-1],l=i[i.length-2],p=e.body?await e.json():{},d;if(m==="set"){let{success:b,data:g,error:w}=j.safeParse(p);if(!b)return Response.json({message:"Invalid mutation",code:"INVALID_REQUEST",details:w},{status:400});d=g;}else {let{success:b,data:g,error:w}=N.safeParse(p);if(!b)return Response.json({message:"Invalid mutation",code:"INVALID_REQUEST",details:w},{status:400});d=g;}let c=await r.handleRequest({req:{...a,type:"MUTATE",resourceName:l,input:d.payload,context:h,resourceId:d.resourceId,procedure:m!=="set"?m:void 0,query:{}}});return Response.json(c)}catch(m){return console.error("Error parsing mutation from the client:",m),Response.json({message:"Internal server error",code:"INTERNAL_SERVER_ERROR"},{status:500})}return Response.json({message:"Not found",code:"NOT_FOUND"},{status:404})}catch(n){return console.error("Unexpected error:",n),Response.json({message:"Internal server error",code:"INTERNAL_SERVER_ERROR"},{status:500})}};var J=b(P(),1);var T=z$1.string(),ge=z$1.object({id:T,type:z$1.literal("SUBSCRIBE"),resource:z$1.string()}),Te=z$1.object({id:T,type:z$1.literal("SYNC"),lastSyncedAt:z$1.string().optional(),resources:z$1.string().array().optional(),where:z$1.record(z$1.any()).optional()}),G=M.extend({id:T}),Re=S.extend({id:T}),xe=z$1.union([Re,G]),B=z$1.union([ge,Te,xe]),be=z$1.object({id:T,type:z$1.literal("SYNC"),resource:z$1.string(),data:z$1.record(L)}),we=z$1.object({id:T,type:z$1.literal("REJECT"),resource:z$1.string(),message:z$1.string().optional()}),Se=z$1.object({id:T,type:z$1.literal("REPLY"),data:z$1.any()});z$1.union([be,we,Se,G]);var W="0123456789ABCDEFGHJKMNPQRSTVWXYZ",v=32;var Me=16,K=10,Q=0xffffffffffff;var R;(function(r){r.Base32IncorrectEncoding="B32_ENC_INVALID",r.DecodeTimeInvalidCharacter="DEC_TIME_CHAR",r.DecodeTimeValueMalformed="DEC_TIME_MALFORMED",r.EncodeTimeNegative="ENC_TIME_NEG",r.EncodeTimeSizeExceeded="ENC_TIME_SIZE_EXCEED",r.EncodeTimeValueMalformed="ENC_TIME_MALFORMED",r.PRNGDetectFailure="PRNG_DETECT",r.ULIDInvalid="ULID_INVALID",r.Unexpected="UNEXPECTED",r.UUIDInvalid="UUID_INVALID";})(R||(R={}));var x=class extends Error{constructor(e,t){super(`${t} (${e})`),this.name="ULIDError",this.code=e;}};function ve(r){let e=Math.floor(r()*v);return e===v&&(e=v-1),W.charAt(e)}function Ee(r){var n;let e=Ie(),t=e&&(e.crypto||e.msCrypto)||(typeof I<"u"?I:null);if(typeof(t==null?void 0:t.getRandomValues)=="function")return ()=>{let a=new Uint8Array(1);return t.getRandomValues(a),a[0]/255};if(typeof(t==null?void 0:t.randomBytes)=="function")return ()=>t.randomBytes(1).readUInt8()/255;if((n=I)!=null&&n.randomBytes)return ()=>I.randomBytes(1).readUInt8()/255;throw new x(R.PRNGDetectFailure,"Failed to find a reliable PRNG")}function Ie(){return Le()?self:typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:null}function Ae(r,e){let t="";for(;r>0;r--)t=ve(e)+t;return t}function Pe(r,e=K){if(isNaN(r))throw new x(R.EncodeTimeValueMalformed,`Time must be a number: ${r}`);if(r>Q)throw new x(R.EncodeTimeSizeExceeded,`Cannot encode a time larger than ${Q}: ${r}`);if(r<0)throw new x(R.EncodeTimeNegative,`Time must be positive: ${r}`);if(Number.isInteger(r)===false)throw new x(R.EncodeTimeValueMalformed,`Time must be an integer: ${r}`);let t,n="";for(let a=e;a>0;a--)t=r%v,n=W.charAt(t)+n,r=(r-t)/v;return n}function Le(){return typeof WorkerGlobalScope<"u"&&self instanceof WorkerGlobalScope}function Y(r,e){let t=Ee(),n=Date.now();return Pe(n,K)+Ae(Me,t)}var O=()=>Y().toLowerCase();var X=r=>{let e={},t={};return r.subscribeToMutations(n=>{let a=n;!a.resourceId||!a.payload||(console.log("Mutation propagated:",a),Object.entries(t[a.resource]??{}).forEach(([o,i])=>{var u;(u=e[o])==null||u.send(JSON.stringify({...a,id:a.id??O()}));}));}),(n,a)=>{var m;let o=l=>{n.send(JSON.stringify(l));},i=O(),u={headers:a.headers,cookies:typeof a.headers.cookie=="string"?J.default.parse(a.headers.cookie):{}},s=parse(a.url.split("?")[1]),h=(m=r.contextProvider)==null?void 0:m.call(r,{transport:"WEBSOCKET",headers:u.headers,cookies:u.cookies,query:s});e[i]=n,console.log("Client connected:",i),n.on("message",async l=>{try{console.log("Message received from the client:",l);let p=B.parse(JSON.parse(l.toString()));if(p.type==="SUBSCRIBE"){let{resource:d}=p;t[d]||(t[d]={}),t[d][i]={};}else if(p.type==="SYNC"){let{resources:d}=p,c=d??Object.keys(r.schema);console.log("Syncing resources:",c),await Promise.all(c.map(async b=>{let g=await r.handleRequest({req:{...u,type:"QUERY",resourceName:b,context:await h??{},query:s}});if(!g||!g.data)throw new Error("Invalid resource");o({id:p.id,type:"SYNC",resource:b,data:Object.fromEntries(Object.entries(g.data??{}).map(([w,ne])=>[w,ne.value]))});}));}else if(p.type==="MUTATE"){let{resource:d}=p;console.log("Received mutation from client:",p);try{let c=await r.handleRequest({req:{...u,type:"MUTATE",resourceName:d,input:p.payload,context:{messageId:p.id,...await h??{}},resourceId:p.resourceId,procedure:p.procedure,query:s}});p.procedure&&o({id:p.id,type:"REPLY",data:c});}catch(c){o({id:p.id,type:"REJECT",resource:d,message:c.message}),console.error("Error parsing mutation from the client:",c);}}}catch(p){console.error("Error handling message from the client:",p);}}),n.on("close",()=>{console.log("Connection closed",i),delete e[i];for(let l of Object.values(t))delete l[i];});}};function ee(r){let e=`${r.protocol}://${r.hostname}${r.url}`,t=new Headers;return Object.entries(r.headers).forEach(([n,a])=>{a&&t.set(n,Array.isArray(a)?a.join(","):a);}),new Request(e,{method:r.method,headers:t,body:r.body&&r.method!=="GET"?JSON.stringify(r.body):void 0})}var pt=(r,e,t)=>{r.ws(`${(t==null?void 0:t.basePath)??""}/ws`,X(e)),r.use(`${(t==null?void 0:t.basePath)??""}/`,(n,a)=>{H(e)(ee(n)).then(i=>i.json().then(u=>a.status(i.status).send(u)));});};var C=class r{routes;constructor(e){this.routes=e.routes;}static create(e){return new r(e)}},ht=r=>C.create({...r}),Oe=r=>({handler:e=>({inputValidator:r??z$1.undefined(),handler:e})}),z=class r{_resourceSchema;resourceName;middlewares;customMutations;constructor(e,t){this.resourceName=e,this.middlewares=new Set,this.customMutations=t??{};}handleFind=async({req:e,db:t})=>({data:await t.find(e.resourceName,e.where),acceptedValues:null});handleSet=async({req:e,db:t,schema:n})=>{if(!e.input)throw new Error("Payload is required");if(!e.resourceId)throw new Error("ResourceId is required");let a=await t.findById(e.resourceName,e.resourceId),[o,i]=n[this.resourceName].mergeMutation("set",e.input,a);if(!i)throw new Error("Mutation rejected");return {data:await t.upsert(e.resourceName,e.resourceId,o),acceptedValues:i}};async handleRequest(e){let t=n=>(()=>{if(n.type==="QUERY")return this.handleFind({req:n,db:e.db,schema:e.schema});if(n.type==="MUTATE")if(n.procedure){if(this.customMutations[n.procedure]){let a=this.customMutations[n.procedure].inputValidator.parse(n.input);return n.input=a,this.customMutations[n.procedure].handler({req:n,db:e.db,schema:e.schema})}}else return this.handleSet({req:n,db:e.db,schema:e.schema});throw new Error("Invalid request")})();return await Array.from(this.middlewares.values()).reduceRight((n,a)=>o=>a({req:o,next:n}),async n=>t(n))(e.req)}use(...e){for(let t of e)this.middlewares.add(t);return this}withMutations(e){return new r(this.resourceName,e({mutation:Oe}))}},k=class r{middlewares;constructor(e=[]){this.middlewares=e;}createBasicRoute(e){return new z(e.name).use(...this.middlewares)}use(...e){return new r([...this.middlewares,...e])}static create(){return new r}},gt=k.create;var A=class{},te=class extends A{storage={};async updateSchema(e){this.storage=Object.entries(e).reduce((t,[n,a])=>(t[a.name]={},t),{});}async findById(e,t){var n;return (n=this.storage[e])==null?void 0:n[t]}async find(e,t){return this.storage[e]??{}}async upsert(e,t,n){return this.storage[e]??={},this.storage[e][t]=n,n}},re=class extends A{db;schema;constructor(e){super(),this.db=new Kysely({dialect:new PostgresDialect({pool:e})});}async updateSchema(e){this.schema=e;let t=await this.db.introspection.getTables();for(let[n,a]of Object.entries(e)){let o=t.find(s=>s.name===n);o||await this.db.schema.createTable(n).ifNotExists().execute();let i=`${n}_meta`,u=t.find(s=>s.name===i);u||await this.db.schema.createTable(i).ifNotExists().execute();for(let[s,h]of Object.entries(a.fields)){let m=o==null?void 0:o.columns.find(d=>d.name===s),l=h.getStorageFieldType();m?m.dataType!==l.type&&console.error("Column type mismatch:",s,"expected to have type:",l.type,"but has type:",m.dataType):(await this.db.schema.alterTable(n).addColumn(s,l.type,d=>{let c=d;return l.unique&&(c=c.unique()),l.nullable||(c=c.notNull()),l.references&&(c=c.references(l.references)),l.primary&&(c=c.primaryKey()),l.default!==void 0&&(c=c.defaultTo(l.default)),c}).execute().catch(d=>{throw console.error("Error adding column",s,d),d}),l.index&&await this.db.schema.createIndex(`${n}_${s}_index`).on(n).column(s).execute().catch(d=>{})),(u==null?void 0:u.columns.find(d=>d.name===s))||await this.db.schema.alterTable(i).addColumn(s,"varchar",d=>{let c=d;return l.primary&&(c=c.primaryKey().references(`${n}.${s}`)),c}).execute();}}}async findById(e,t){let n=await this.db.selectFrom(e).where("id","=",t).selectAll(e).executeTakeFirst(),a=await this.db.selectFrom(`${e}_meta`).where("id","=",t).selectAll(`${e}_meta`).executeTakeFirst();if(!(!n||!a))return this.convertToMaterializedLiveType(n,a)}async find(e,t){let a=await this.applyWhere(e,this.db.selectFrom(e).selectAll(e),t).execute(),o=Object.fromEntries(a.map(s=>{let{id:h,...m}=s;return [h,m]}));if(Object.keys(o).length===0)return {};let i=Object.fromEntries((await this.db.selectFrom(`${e}_meta`).selectAll().where("id","in",Object.keys(o)).execute()).map(s=>{let{id:h,...m}=s;return [h,m]}));return Object.entries(o).reduce((s,[h,m])=>(i[h]&&(s[h]=this.convertToMaterializedLiveType(m,i[h])),s),{})}async upsert(e,t,n){return await this.db.transaction().execute(async a=>{let o=!!await a.selectFrom(e).select("id").where("id","=",t).executeTakeFirst(),i={},u={};for(let[s,h]of Object.entries(n.value))i[s]=h.value,u[s]=h._meta.timestamp;o?await Promise.all([a.updateTable(e).set(i).where("id","=",t).execute(),a.updateTable(`${e}_meta`).set(u).where("id","=",t).execute()]):await Promise.all([a.insertInto(e).values({...i,id:t}).execute(),a.insertInto(`${e}_meta`).values({...u,id:t}).execute()]);}),n}convertToMaterializedLiveType(e,t){return {value:Object.fromEntries(Object.entries(e).flatMap(([n,a])=>t?[[n,{value:a,_meta:{timestamp:t==null?void 0:t[n]}}]]:[]))}}applyWhere(e,t,n){if(!n)return t;if(!this.schema)throw new Error("Schema not initialized");let a=this.schema[e];if(!a)throw new Error("Resource not found");for(let[o,i]of Object.entries(n))if(a.fields[o])t=t.where(`${e}.${o}`,"=",i);else if(a.relations[o]){let u=a.relations[o],s=u.entity.name,h=u.type==="one"?"id":u.foreignColumn,m=u.type==="one"?u.relationalColumn:"id";t=t.leftJoin(s,`${s}.${h}`,`${e}.${m}`),t=this.applyWhere(s,t,i);}return t}};var D=class r{router;storage;schema;middlewares=new Set;contextProvider;mutationSubscriptions=new Set;constructor(e){var t;this.router=e.router,this.storage=e.storage,this.schema=e.schema,(t=e.middlewares)==null||t.forEach(n=>this.middlewares.add(n)),this.storage.updateSchema(this.schema),this.contextProvider=e.contextProvider;}static create(e){return new r(e)}subscribeToMutations(e){return this.mutationSubscriptions.add(e),()=>{this.mutationSubscriptions.delete(e);}}async handleRequest(e){if(!this.router.routes[e.req.resourceName])throw new Error("Invalid resource");let t=await Array.from(this.middlewares.values()).reduceRight((n,a)=>o=>a({req:o,next:n}),async n=>this.router.routes[e.req.resourceName].handleRequest({req:n,db:this.storage,schema:this.schema}))(e.req);return t&&e.req.type==="MUTATE"&&t.acceptedValues&&Object.keys(t.acceptedValues).length>0&&this.mutationSubscriptions.forEach(n=>{n({id:e.req.context.messageId,type:"MUTATE",resource:e.req.resourceName,payload:t.acceptedValues??{},resourceId:e.req.resourceId});}),t}use(e){return this.middlewares.add(e),this}context(e){return this.contextProvider=e,this}},St=D.create;
2
+ export{te as InMemoryStorage,z as Route,k as RouteFactory,C as Router,re as SQLStorage,D as Server,A as Storage,pt as expressAdapter,gt as routeFactory,ht as router,St as server};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@live-state/sync",
3
- "version": "0.0.1-alpha.2",
3
+ "version": "0.0.1-alpha.3",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -21,9 +21,13 @@
21
21
  ],
22
22
  "type": "module",
23
23
  "devDependencies": {
24
+ "@types/express": "^4.17.21",
25
+ "@types/express-ws": "^3.0.5",
24
26
  "@types/node": "^20.11.24",
27
+ "@types/qs": "^6.14.0",
25
28
  "@types/ws": "^8.5.13",
26
29
  "cookie": "^1.0.2",
30
+ "express": "^4.21.2",
27
31
  "react": "18.0.0",
28
32
  "tsup": "^8.0.2",
29
33
  "typescript": "5.5.4",
@@ -32,6 +36,7 @@
32
36
  },
33
37
  "dependencies": {
34
38
  "kysely": "^0.28.2",
39
+ "qs": "^6.14.0",
35
40
  "ws": "^8.18.0",
36
41
  "zod": "^3.24.1"
37
42
  },
@@ -50,6 +55,11 @@
50
55
  "types": "./src/client/index.ts",
51
56
  "import": "./dist/client.js",
52
57
  "require": "./dist/client.js"
58
+ },
59
+ "./client/fetch": {
60
+ "types": "./src/client/fetch-client.ts",
61
+ "import": "./dist/fetch-client.js",
62
+ "require": "./dist/fetch-client.js"
53
63
  }
54
64
  },
55
65
  "peerDependencies": {