@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/chunk-2W2QGOPU.js +1 -0
- package/dist/client.d.ts +3 -333
- package/dist/client.js +1 -1
- package/dist/fetch-client.d.ts +18 -0
- package/dist/fetch-client.js +1 -0
- package/dist/index-BS5EvFM-.d.ts +345 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +14 -1
- package/dist/index.d.ts +14 -1
- package/dist/index.js +1 -1
- package/dist/server.cjs +2 -1
- package/dist/server.d.cts +41 -25
- package/dist/server.d.ts +41 -25
- package/dist/server.js +2 -1
- package/package.json +14 -3
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
|
|
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:
|
|
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:
|
|
128
|
+
req: ParsedRequest;
|
|
129
129
|
db: Storage;
|
|
130
130
|
schema: Schema<any>;
|
|
131
131
|
}): Promise<any>;
|
|
132
|
-
use(
|
|
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
|
|
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
|
-
|
|
166
|
-
|
|
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
|
|
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
|
|
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:
|
|
184
|
-
type Middleware<T> = (opts: {
|
|
185
|
-
req:
|
|
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:
|
|
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
|
|
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
|
|
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:
|
|
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:
|
|
128
|
+
req: ParsedRequest;
|
|
129
129
|
db: Storage;
|
|
130
130
|
schema: Schema<any>;
|
|
131
131
|
}): Promise<any>;
|
|
132
|
-
use(
|
|
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
|
|
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
|
-
|
|
166
|
-
|
|
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
|
|
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
|
|
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:
|
|
184
|
-
type Middleware<T> = (opts: {
|
|
185
|
-
req:
|
|
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:
|
|
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
|
|
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
|
|
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
|
}
|