@live-state/sync 0.0.1-alpha.2 → 0.0.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.d.cts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { z, ZodTypeAny } from 'zod';
2
- import { LiveObjectAny, Schema, MaterializedLiveType, WhereClause } from './index.cjs';
2
+ import { LiveObjectAny, Schema, MaterializedLiveType, WhereClause, IncludeClause } 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,29 +125,36 @@ 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 {
141
148
  abstract updateSchema(opts: Schema<any>): Promise<void>;
142
149
  abstract findById<T extends LiveObjectAny>(resourceName: string, id: string): Promise<MaterializedLiveType<T> | undefined>;
143
- abstract find<T extends LiveObjectAny>(resourceName: string, where?: Record<string, any>): Promise<Record<string, MaterializedLiveType<T>>>;
150
+ abstract find<T extends LiveObjectAny>(resourceName: string, where?: Record<string, any>, include?: Record<string, any>): Promise<Record<string, MaterializedLiveType<T>>>;
144
151
  abstract upsert<T extends LiveObjectAny>(resourceName: string, resourceId: string, value: MaterializedLiveType<T>): Promise<MaterializedLiveType<T>>;
145
152
  }
146
153
  declare class InMemoryStorage extends Storage {
147
154
  private storage;
148
155
  updateSchema(opts: Schema<any>): Promise<void>;
149
156
  findById<T extends LiveObjectAny>(resourceName: string, id: string): Promise<MaterializedLiveType<T> | undefined>;
150
- find<T extends LiveObjectAny>(resourceName: string, where?: Record<string, any>): Promise<Record<string, MaterializedLiveType<T>>>;
157
+ find<T extends LiveObjectAny>(resourceName: string, where?: Record<string, any>, include?: Record<string, any>): Promise<Record<string, MaterializedLiveType<T>>>;
151
158
  upsert<T extends LiveObjectAny>(resourceName: string, resourceId: string, value: MaterializedLiveType<T>): Promise<MaterializedLiveType<T>>;
152
159
  }
153
160
  declare class SQLStorage extends Storage {
@@ -156,33 +163,38 @@ declare class SQLStorage extends Storage {
156
163
  constructor(pool: PostgresPool);
157
164
  updateSchema(opts: Schema<any>): Promise<void>;
158
165
  findById<T extends LiveObjectAny>(resourceName: string, id: string): Promise<MaterializedLiveType<T> | undefined>;
159
- find<T extends LiveObjectAny>(resourceName: string, where?: WhereClause<T>): Promise<Record<string, MaterializedLiveType<T>>>;
166
+ find<T extends LiveObjectAny>(resourceName: string, where?: WhereClause<T>, include?: IncludeClause<T>): Promise<Record<string, MaterializedLiveType<T>>>;
160
167
  upsert<T extends LiveObjectAny>(resourceName: string, resourceId: string, value: MaterializedLiveType<T>): Promise<MaterializedLiveType<T>>;
161
168
  private convertToMaterializedLiveType;
162
169
  private applyWhere;
170
+ private applyInclude;
163
171
  }
164
172
 
165
- type Subscription = {
166
- filters?: Record<string, any>;
167
- };
168
- declare const webSocketAdapter: (server: Server<AnyRouter>) => (ws: WebSocket, request: any) => void;
173
+ declare const expressAdapter: (app: Application, server: Server<AnyRouter>, options?: {
174
+ basePath?: string;
175
+ }) => void;
169
176
 
170
- type Request<TInput = any> = {
177
+ type ParsedRequest<TInput = any> = {
171
178
  headers: Record<string, string>;
172
179
  cookies: Record<string, string>;
180
+ query: Record<string, string>;
173
181
  resourceName: string;
174
182
  procedure?: string;
175
183
  context: Record<string, any>;
176
184
  where?: Record<string, any>;
185
+ include?: Record<string, any>;
177
186
  type: "QUERY" | "MUTATE";
178
187
  resourceId?: string;
179
188
  input?: TInput;
180
189
  };
181
- type RequestType = Request["type"];
190
+ type ContextProvider = (req: Pick<ParsedRequest, "headers" | "cookies" | "query"> & {
191
+ transport: "HTTP" | "WEBSOCKET";
192
+ }) => Record<string, any>;
193
+ type RequestType = ParsedRequest["type"];
182
194
  type MutationHandler = (mutation: RawMutationRequest) => void;
183
- type NextFunction<T> = (req: Request) => Promise<T> | T;
184
- type Middleware<T> = (opts: {
185
- req: Request;
195
+ type NextFunction<T> = (req: ParsedRequest) => Promise<T> | T;
196
+ type Middleware<T = any> = (opts: {
197
+ req: ParsedRequest;
186
198
  next: NextFunction<T>;
187
199
  }) => ReturnType<NextFunction<T>>;
188
200
  declare class Server<TRouter extends AnyRouter> {
@@ -190,19 +202,23 @@ declare class Server<TRouter extends AnyRouter> {
190
202
  readonly storage: Storage;
191
203
  readonly schema: Schema<any>;
192
204
  readonly middlewares: Set<Middleware<any>>;
205
+ contextProvider?: ContextProvider;
193
206
  private mutationSubscriptions;
194
207
  private constructor();
195
208
  static create<TRouter extends AnyRouter>(opts: {
196
209
  router: TRouter;
197
210
  storage: Storage;
198
211
  schema: Schema<any>;
212
+ middlewares?: Middleware<any>[];
213
+ contextProvider?: ContextProvider;
199
214
  }): Server<TRouter>;
200
215
  subscribeToMutations(handler: MutationHandler): () => void;
201
216
  handleRequest(opts: {
202
- req: Request;
217
+ req: ParsedRequest;
203
218
  }): Promise<any>;
204
219
  use(middleware: Middleware<any>): this;
220
+ context(contextProvider: ContextProvider): this;
205
221
  }
206
222
  declare const server: typeof Server.create;
207
223
 
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 };
224
+ 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
- import { LiveObjectAny, Schema, MaterializedLiveType, WhereClause } from './index.js';
2
+ import { LiveObjectAny, Schema, MaterializedLiveType, WhereClause, IncludeClause } 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,29 +125,36 @@ 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 {
141
148
  abstract updateSchema(opts: Schema<any>): Promise<void>;
142
149
  abstract findById<T extends LiveObjectAny>(resourceName: string, id: string): Promise<MaterializedLiveType<T> | undefined>;
143
- abstract find<T extends LiveObjectAny>(resourceName: string, where?: Record<string, any>): Promise<Record<string, MaterializedLiveType<T>>>;
150
+ abstract find<T extends LiveObjectAny>(resourceName: string, where?: Record<string, any>, include?: Record<string, any>): Promise<Record<string, MaterializedLiveType<T>>>;
144
151
  abstract upsert<T extends LiveObjectAny>(resourceName: string, resourceId: string, value: MaterializedLiveType<T>): Promise<MaterializedLiveType<T>>;
145
152
  }
146
153
  declare class InMemoryStorage extends Storage {
147
154
  private storage;
148
155
  updateSchema(opts: Schema<any>): Promise<void>;
149
156
  findById<T extends LiveObjectAny>(resourceName: string, id: string): Promise<MaterializedLiveType<T> | undefined>;
150
- find<T extends LiveObjectAny>(resourceName: string, where?: Record<string, any>): Promise<Record<string, MaterializedLiveType<T>>>;
157
+ find<T extends LiveObjectAny>(resourceName: string, where?: Record<string, any>, include?: Record<string, any>): Promise<Record<string, MaterializedLiveType<T>>>;
151
158
  upsert<T extends LiveObjectAny>(resourceName: string, resourceId: string, value: MaterializedLiveType<T>): Promise<MaterializedLiveType<T>>;
152
159
  }
153
160
  declare class SQLStorage extends Storage {
@@ -156,33 +163,38 @@ declare class SQLStorage extends Storage {
156
163
  constructor(pool: PostgresPool);
157
164
  updateSchema(opts: Schema<any>): Promise<void>;
158
165
  findById<T extends LiveObjectAny>(resourceName: string, id: string): Promise<MaterializedLiveType<T> | undefined>;
159
- find<T extends LiveObjectAny>(resourceName: string, where?: WhereClause<T>): Promise<Record<string, MaterializedLiveType<T>>>;
166
+ find<T extends LiveObjectAny>(resourceName: string, where?: WhereClause<T>, include?: IncludeClause<T>): Promise<Record<string, MaterializedLiveType<T>>>;
160
167
  upsert<T extends LiveObjectAny>(resourceName: string, resourceId: string, value: MaterializedLiveType<T>): Promise<MaterializedLiveType<T>>;
161
168
  private convertToMaterializedLiveType;
162
169
  private applyWhere;
170
+ private applyInclude;
163
171
  }
164
172
 
165
- type Subscription = {
166
- filters?: Record<string, any>;
167
- };
168
- declare const webSocketAdapter: (server: Server<AnyRouter>) => (ws: WebSocket, request: any) => void;
173
+ declare const expressAdapter: (app: Application, server: Server<AnyRouter>, options?: {
174
+ basePath?: string;
175
+ }) => void;
169
176
 
170
- type Request<TInput = any> = {
177
+ type ParsedRequest<TInput = any> = {
171
178
  headers: Record<string, string>;
172
179
  cookies: Record<string, string>;
180
+ query: Record<string, string>;
173
181
  resourceName: string;
174
182
  procedure?: string;
175
183
  context: Record<string, any>;
176
184
  where?: Record<string, any>;
185
+ include?: Record<string, any>;
177
186
  type: "QUERY" | "MUTATE";
178
187
  resourceId?: string;
179
188
  input?: TInput;
180
189
  };
181
- type RequestType = Request["type"];
190
+ type ContextProvider = (req: Pick<ParsedRequest, "headers" | "cookies" | "query"> & {
191
+ transport: "HTTP" | "WEBSOCKET";
192
+ }) => Record<string, any>;
193
+ type RequestType = ParsedRequest["type"];
182
194
  type MutationHandler = (mutation: RawMutationRequest) => void;
183
- type NextFunction<T> = (req: Request) => Promise<T> | T;
184
- type Middleware<T> = (opts: {
185
- req: Request;
195
+ type NextFunction<T> = (req: ParsedRequest) => Promise<T> | T;
196
+ type Middleware<T = any> = (opts: {
197
+ req: ParsedRequest;
186
198
  next: NextFunction<T>;
187
199
  }) => ReturnType<NextFunction<T>>;
188
200
  declare class Server<TRouter extends AnyRouter> {
@@ -190,19 +202,23 @@ declare class Server<TRouter extends AnyRouter> {
190
202
  readonly storage: Storage;
191
203
  readonly schema: Schema<any>;
192
204
  readonly middlewares: Set<Middleware<any>>;
205
+ contextProvider?: ContextProvider;
193
206
  private mutationSubscriptions;
194
207
  private constructor();
195
208
  static create<TRouter extends AnyRouter>(opts: {
196
209
  router: TRouter;
197
210
  storage: Storage;
198
211
  schema: Schema<any>;
212
+ middlewares?: Middleware<any>[];
213
+ contextProvider?: ContextProvider;
199
214
  }): Server<TRouter>;
200
215
  subscribeToMutations(handler: MutationHandler): () => void;
201
216
  handleRequest(opts: {
202
- req: Request;
217
+ req: ParsedRequest;
203
218
  }): Promise<any>;
204
219
  use(middleware: Middleware<any>): this;
220
+ context(contextProvider: ContextProvider): this;
205
221
  }
206
222
  declare const server: typeof Server.create;
207
223
 
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 };
224
+ 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 E from'node:crypto';import {Kysely,PostgresDialect}from'kysely';import {jsonArrayFrom}from'kysely/helpers/postgres';var P=a(I=>{Object.defineProperty(I,"__esModule",{value:true});I.parse=le;I.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 s=r.indexOf(";",o),c=s===-1?n:s;if(i>c){o=r.lastIndexOf(";",i-1)+1;continue}let y=$(r,o,i),p=V(r,i,y),u=r.slice(y,p);if(t[u]===void 0){let m=$(r,i+1,c),l=V(r,c,m),d=a(r.slice(m,l));t[u]=d;}o=c+1;}while(o<n);return t}function $(r,e,t){do{let n=r.charCodeAt(e);if(n!==32&&n!==9)return e}while(++e<t);return t}function V(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}),j=S.omit({id:true,type:true,resource:true,procedure:true}),O=M.omit({id:true,type:true,resource:true});z$1.union([O,j]);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("/"),s=o.searchParams,c=he.parse(s.toString()),y=await((t=r.contextProvider)==null?void 0:t.call(r,{transport:"HTTP",headers:a.headers,cookies:a.cookies,query:c}))??{};if(e.method==="GET"){let p=i[i.length-1],{success:u,data:m,error:l}=F.safeParse(c);if(!u)return Response.json({message:"Invalid query",code:"INVALID_QUERY",details:l},{status:400});let d=await r.handleRequest({req:{...a,type:"QUERY",resourceName:p,context:y,where:m.where,include:m.include,query:c}});return !d||!d.data?Response.json({message:"Invalid resource",code:"INVALID_RESOURCE"},{status:400}):Response.json(d.data)}if(e.method==="POST")try{let p=i[i.length-1],u=i[i.length-2],m=e.body?await e.json():{},l;if(p==="set"){let{success:b,data:g,error:w}=O.safeParse(m);if(!b)return Response.json({message:"Invalid mutation",code:"INVALID_REQUEST",details:w},{status:400});l=g;}else {let{success:b,data:g,error:w}=j.safeParse(m);if(!b)return Response.json({message:"Invalid mutation",code:"INVALID_REQUEST",details:w},{status:400});l=g;}let d=await r.handleRequest({req:{...a,type:"MUTATE",resourceName:u,input:l.payload,context:y,resourceId:l.resourceId,procedure:p!=="set"?p:void 0,query:{}}});return Response.json(d)}catch(p){return console.error("Error parsing mutation from the client:",p),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 Ie(r){var n;let e=Ee(),t=e&&(e.crypto||e.msCrypto)||(typeof E<"u"?E: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=E)!=null&&n.randomBytes)return ()=>E.randomBytes(1).readUInt8()/255;throw new x(R.PRNGDetectFailure,"Failed to find a reliable PRNG")}function Ee(){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=Ie(),n=Date.now();return Pe(n,K)+Ae(Me,t)}var N=()=>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 s;(s=e[o])==null||s.send(JSON.stringify({...a,id:a.id??N()}));}));}),(n,a)=>{var p;let o=u=>{n.send(JSON.stringify(u));},i=N(),s={headers:a.headers,cookies:typeof a.headers.cookie=="string"?J.default.parse(a.headers.cookie):{}},c=parse(a.url.split("?")[1]),y=(p=r.contextProvider)==null?void 0:p.call(r,{transport:"WEBSOCKET",headers:s.headers,cookies:s.cookies,query:c});e[i]=n,console.log("Client connected:",i),n.on("message",async u=>{try{console.log("Message received from the client:",u);let m=B.parse(JSON.parse(u.toString()));if(m.type==="SUBSCRIBE"){let{resource:l}=m;t[l]||(t[l]={}),t[l][i]={};}else if(m.type==="SYNC"){let{resources:l}=m,d=l??Object.keys(r.schema);console.log("Syncing resources:",d),await Promise.all(d.map(async b=>{let g=await r.handleRequest({req:{...s,type:"QUERY",resourceName:b,context:await y??{},query:c}});if(!g||!g.data)throw new Error("Invalid resource");o({id:m.id,type:"SYNC",resource:b,data:Object.fromEntries(Object.entries(g.data??{}).map(([w,ne])=>[w,ne.value]))});}));}else if(m.type==="MUTATE"){let{resource:l}=m;console.log("Received mutation from client:",m);try{let d=await r.handleRequest({req:{...s,type:"MUTATE",resourceName:l,input:m.payload,context:{messageId:m.id,...await y??{}},resourceId:m.resourceId,procedure:m.procedure,query:c}});m.procedure&&o({id:m.id,type:"REPLY",data:d});}catch(d){o({id:m.id,type:"REJECT",resource:l,message:d.message}),console.error("Error parsing mutation from the client:",d);}}}catch(m){console.error("Error handling message from the client:",m);}}),n.on("close",()=>{console.log("Connection closed",i),delete e[i];for(let u of Object.values(t))delete u[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 mt=(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(s=>a.status(i.status).send(s)));});};var C=class r{routes;constructor(e){this.routes=e.routes;}static create(e){return new r(e)}},gt=r=>C.create({...r}),Ne=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,e.include),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:Ne}))}},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}},Tt=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,n){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(c=>c.name===n);o||await this.db.schema.createTable(n).ifNotExists().execute();let i=`${n}_meta`,s=t.find(c=>c.name===i);s||await this.db.schema.createTable(i).ifNotExists().execute();for(let[c,y]of Object.entries(a.fields)){let p=o==null?void 0:o.columns.find(l=>l.name===c),u=y.getStorageFieldType();p?p.dataType!==u.type&&console.error("Column type mismatch:",c,"expected to have type:",u.type,"but has type:",p.dataType):(await this.db.schema.alterTable(n).addColumn(c,u.type,l=>{let d=l;return u.unique&&(d=d.unique()),u.nullable||(d=d.notNull()),u.references&&(d=d.references(u.references)),u.primary&&(d=d.primaryKey()),u.default!==void 0&&(d=d.defaultTo(u.default)),d}).execute().catch(l=>{throw console.error("Error adding column",c,l),l}),u.index&&await this.db.schema.createIndex(`${n}_${c}_index`).on(n).column(c).execute().catch(l=>{})),(s==null?void 0:s.columns.find(l=>l.name===c))||await this.db.schema.alterTable(i).addColumn(c,"varchar",l=>{let d=l;return u.primary&&(d=d.primaryKey().references(`${n}.${c}`)),d}).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,n){let a=this.applyWhere(e,this.db.selectFrom(e).selectAll(e),t);a=this.applyInclude(e,a,n);let o=await a.execute(),i=Object.fromEntries(o.map(y=>{let{id:p,...u}=y;return [p,u]}));if(Object.keys(i).length===0)return {};let s=Object.fromEntries((await this.db.selectFrom(`${e}_meta`).selectAll().where("id","in",Object.keys(i)).execute()).map(y=>{let{id:p,...u}=y;return [p,u]}));return Object.entries(i).reduce((y,[p,u])=>(s[p]&&(y[p]=this.convertToMaterializedLiveType(u,s[p])),y),{})}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={},s={};for(let[c,y]of Object.entries(n.value))i[c]=y.value,s[c]=y._meta.timestamp;o?await Promise.all([a.updateTable(e).set(i).where("id","=",t).execute(),a.updateTable(`${e}_meta`).set(s).where("id","=",t).execute()]):await Promise.all([a.insertInto(e).values({...i,id:t}).execute(),a.insertInto(`${e}_meta`).values({...s,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 s=a.relations[o],c=s.entity.name,y=s.type==="one"?"id":s.foreignColumn,p=s.type==="one"?s.relationalColumn:"id";t=t.leftJoin(c,`${c}.${y}`,`${e}.${p}`),t=this.applyWhere(c,t,i);}return t}applyInclude(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: ${e}`);for(let[o,i]of Object.entries(n)){if(!a.relations[o])throw new Error(`Relation ${o} not found in resource ${e}`);let s=a.relations[o],c=s.entity.name,y=s.type==="one"?"id":s.foreignColumn,p=s.type==="one"?s.relationalColumn:"id";t=t.select(u=>jsonArrayFrom(u.selectFrom(c).selectAll(c).whereRef(`${c}.${y}`,"=",`${e}.${p}`)).as(o));}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}},vt=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,mt as expressAdapter,Tt as routeFactory,gt as router,vt 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",
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",
@@ -31,7 +35,9 @@
31
35
  "@repo/typescript-config": "0.0.0"
32
36
  },
33
37
  "dependencies": {
38
+ "idb": "^8.0.3",
34
39
  "kysely": "^0.28.2",
40
+ "qs": "^6.14.0",
35
41
  "ws": "^8.18.0",
36
42
  "zod": "^3.24.1"
37
43
  },
@@ -50,6 +56,11 @@
50
56
  "types": "./src/client/index.ts",
51
57
  "import": "./dist/client.js",
52
58
  "require": "./dist/client.js"
59
+ },
60
+ "./client/fetch": {
61
+ "types": "./src/client/fetch-client.ts",
62
+ "import": "./dist/fetch-client.js",
63
+ "require": "./dist/fetch-client.js"
53
64
  }
54
65
  },
55
66
  "peerDependencies": {
@@ -65,8 +76,8 @@
65
76
  }
66
77
  },
67
78
  "scripts": {
68
- "build": "tsup",
69
- "dev": "tsup --watch",
79
+ "build": "NODE_ENV=production tsup",
80
+ "dev": "NODE_ENV=development tsup --watch",
70
81
  "lint": "eslint src/",
71
82
  "typecheck": "tsc --noEmit"
72
83
  }