@live-state/sync 0.0.1-alpha.1 → 0.0.1-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/dist/chunk-EK7ODJWE.js +1 -0
- package/dist/client.d.ts +122 -72
- package/dist/client.js +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +199 -2
- package/dist/index.d.ts +199 -2
- package/dist/index.js +1 -1
- package/dist/server.cjs +1 -1
- package/dist/server.d.cts +166 -46
- package/dist/server.d.ts +166 -46
- package/dist/server.js +1 -1
- package/package.json +13 -16
- package/dist/index-sSVirsfN.d.cts +0 -458
- package/dist/index-sSVirsfN.d.ts +0 -458
package/dist/server.d.cts
CHANGED
|
@@ -1,6 +1,141 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { z, ZodTypeAny } from 'zod';
|
|
2
|
+
import { LiveObjectAny, Schema, MaterializedLiveType, WhereClause } from './index.cjs';
|
|
3
|
+
import { PostgresPool } from 'kysely';
|
|
2
4
|
import WebSocket from 'ws';
|
|
3
|
-
|
|
5
|
+
|
|
6
|
+
declare const mutationSchema: z.ZodUnion<[z.ZodObject<z.objectUtil.extendShape<{
|
|
7
|
+
id: z.ZodOptional<z.ZodString>;
|
|
8
|
+
type: z.ZodLiteral<"MUTATE">;
|
|
9
|
+
resource: z.ZodString;
|
|
10
|
+
}, {
|
|
11
|
+
procedure: z.ZodString;
|
|
12
|
+
payload: z.ZodAny;
|
|
13
|
+
}>, "strip", z.ZodTypeAny, {
|
|
14
|
+
type: "MUTATE";
|
|
15
|
+
resource: string;
|
|
16
|
+
procedure: string;
|
|
17
|
+
id?: string | undefined;
|
|
18
|
+
payload?: any;
|
|
19
|
+
}, {
|
|
20
|
+
type: "MUTATE";
|
|
21
|
+
resource: string;
|
|
22
|
+
procedure: string;
|
|
23
|
+
id?: string | undefined;
|
|
24
|
+
payload?: any;
|
|
25
|
+
}>, z.ZodObject<z.objectUtil.extendShape<{
|
|
26
|
+
id: z.ZodOptional<z.ZodString>;
|
|
27
|
+
type: z.ZodLiteral<"MUTATE">;
|
|
28
|
+
resource: z.ZodString;
|
|
29
|
+
}, {
|
|
30
|
+
resourceId: z.ZodString;
|
|
31
|
+
payload: z.ZodEffects<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
32
|
+
value: z.ZodUnion<[z.ZodUnion<[z.ZodUnion<[z.ZodString, z.ZodNumber]>, z.ZodBoolean]>, z.ZodDate]>;
|
|
33
|
+
_meta: z.ZodOptional<z.ZodObject<{
|
|
34
|
+
timestamp: z.ZodOptional<z.ZodString>;
|
|
35
|
+
}, "strip", z.ZodTypeAny, {
|
|
36
|
+
timestamp?: string | undefined;
|
|
37
|
+
}, {
|
|
38
|
+
timestamp?: string | undefined;
|
|
39
|
+
}>>;
|
|
40
|
+
}, "strip", z.ZodTypeAny, {
|
|
41
|
+
value: string | number | boolean | Date;
|
|
42
|
+
_meta?: {
|
|
43
|
+
timestamp?: string | undefined;
|
|
44
|
+
} | undefined;
|
|
45
|
+
}, {
|
|
46
|
+
value: string | number | boolean | Date;
|
|
47
|
+
_meta?: {
|
|
48
|
+
timestamp?: string | undefined;
|
|
49
|
+
} | undefined;
|
|
50
|
+
}>>, Record<string, {
|
|
51
|
+
value: string | number | boolean | Date;
|
|
52
|
+
_meta?: {
|
|
53
|
+
timestamp?: string | undefined;
|
|
54
|
+
} | undefined;
|
|
55
|
+
}>, Record<string, {
|
|
56
|
+
value: string | number | boolean | Date;
|
|
57
|
+
_meta?: {
|
|
58
|
+
timestamp?: string | undefined;
|
|
59
|
+
} | undefined;
|
|
60
|
+
}>>;
|
|
61
|
+
}>, "strip", z.ZodTypeAny, {
|
|
62
|
+
type: "MUTATE";
|
|
63
|
+
resource: string;
|
|
64
|
+
payload: Record<string, {
|
|
65
|
+
value: string | number | boolean | Date;
|
|
66
|
+
_meta?: {
|
|
67
|
+
timestamp?: string | undefined;
|
|
68
|
+
} | undefined;
|
|
69
|
+
}>;
|
|
70
|
+
resourceId: string;
|
|
71
|
+
id?: string | undefined;
|
|
72
|
+
}, {
|
|
73
|
+
type: "MUTATE";
|
|
74
|
+
resource: string;
|
|
75
|
+
payload: Record<string, {
|
|
76
|
+
value: string | number | boolean | Date;
|
|
77
|
+
_meta?: {
|
|
78
|
+
timestamp?: string | undefined;
|
|
79
|
+
} | undefined;
|
|
80
|
+
}>;
|
|
81
|
+
resourceId: string;
|
|
82
|
+
id?: string | undefined;
|
|
83
|
+
}>]>;
|
|
84
|
+
type RawMutationRequest = z.infer<typeof mutationSchema>;
|
|
85
|
+
|
|
86
|
+
type RouteRecord = Record<string, AnyRoute>;
|
|
87
|
+
declare class Router<TRoutes extends RouteRecord> {
|
|
88
|
+
readonly routes: TRoutes;
|
|
89
|
+
private constructor();
|
|
90
|
+
static create<TRoutes extends RouteRecord>(opts: {
|
|
91
|
+
routes: TRoutes;
|
|
92
|
+
}): Router<TRoutes>;
|
|
93
|
+
}
|
|
94
|
+
declare const router: <TSchema extends Schema<any>, TRoutes extends Record<keyof TSchema, AnyRoute>>(opts: {
|
|
95
|
+
schema: TSchema;
|
|
96
|
+
routes: TRoutes;
|
|
97
|
+
}) => Router<TRoutes>;
|
|
98
|
+
type AnyRouter = Router<RouteRecord>;
|
|
99
|
+
type QueryResult<TShape extends LiveObjectAny> = {
|
|
100
|
+
data: Record<string, MaterializedLiveType<TShape>>;
|
|
101
|
+
};
|
|
102
|
+
type MutationResult<TShape extends LiveObjectAny> = {
|
|
103
|
+
data: MaterializedLiveType<TShape>;
|
|
104
|
+
acceptedValues: Record<string, any> | null;
|
|
105
|
+
};
|
|
106
|
+
type RequestHandler<TInput, TResult, TSchema extends Schema<any> = Schema<any>> = (opts: {
|
|
107
|
+
req: Request<TInput>;
|
|
108
|
+
db: Storage;
|
|
109
|
+
schema: TSchema;
|
|
110
|
+
}) => Promise<TResult>;
|
|
111
|
+
type Mutation<TInputValidator extends ZodTypeAny, // TODO use StandardSchema instead
|
|
112
|
+
THandler extends RequestHandler<z.infer<TInputValidator>, any, any>> = {
|
|
113
|
+
inputValidator: TInputValidator;
|
|
114
|
+
handler: THandler;
|
|
115
|
+
};
|
|
116
|
+
declare const mutationCreator: <TInputValidator extends ZodTypeAny>(validator?: TInputValidator) => {
|
|
117
|
+
handler: <THandler extends RequestHandler<z.infer<TInputValidator>, any, any>>(handler: THandler) => Mutation<TInputValidator, THandler>;
|
|
118
|
+
};
|
|
119
|
+
declare class Route<TResourceSchema extends LiveObjectAny, TMiddleware extends Middleware<any>, TCustomMutations extends Record<string, Mutation<any, RequestHandler<any, any>>>> {
|
|
120
|
+
readonly _resourceSchema: TResourceSchema;
|
|
121
|
+
readonly resourceName: TResourceSchema["name"];
|
|
122
|
+
readonly middlewares: Set<TMiddleware>;
|
|
123
|
+
readonly customMutations: TCustomMutations;
|
|
124
|
+
constructor(resourceName: TResourceSchema["name"], customMutations?: TCustomMutations);
|
|
125
|
+
private handleFind;
|
|
126
|
+
private handleSet;
|
|
127
|
+
handleRequest(opts: {
|
|
128
|
+
req: Request;
|
|
129
|
+
db: Storage;
|
|
130
|
+
schema: Schema<any>;
|
|
131
|
+
}): Promise<any>;
|
|
132
|
+
use(middleware: TMiddleware): this;
|
|
133
|
+
withMutations<T extends Record<string, Mutation<any, RequestHandler<any, any>>>>(mutationFactory: (opts: {
|
|
134
|
+
mutation: typeof mutationCreator;
|
|
135
|
+
}) => T): Route<TResourceSchema, TMiddleware, T>;
|
|
136
|
+
}
|
|
137
|
+
declare const routeFactory: () => <T extends LiveObjectAny>(shape: T) => Route<T, Middleware<any>, Record<string, never>>;
|
|
138
|
+
type AnyRoute = Route<LiveObjectAny, Middleware<any>, Record<string, any>>;
|
|
4
139
|
|
|
5
140
|
declare abstract class Storage {
|
|
6
141
|
abstract updateSchema(opts: Schema<any>): Promise<void>;
|
|
@@ -15,62 +150,46 @@ declare class InMemoryStorage extends Storage {
|
|
|
15
150
|
find<T extends LiveObjectAny>(resourceName: string, where?: Record<string, any>): Promise<Record<string, MaterializedLiveType<T>>>;
|
|
16
151
|
upsert<T extends LiveObjectAny>(resourceName: string, resourceId: string, value: MaterializedLiveType<T>): Promise<MaterializedLiveType<T>>;
|
|
17
152
|
}
|
|
153
|
+
declare class SQLStorage extends Storage {
|
|
154
|
+
private db;
|
|
155
|
+
private schema?;
|
|
156
|
+
constructor(pool: PostgresPool);
|
|
157
|
+
updateSchema(opts: Schema<any>): Promise<void>;
|
|
158
|
+
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>>>;
|
|
160
|
+
upsert<T extends LiveObjectAny>(resourceName: string, resourceId: string, value: MaterializedLiveType<T>): Promise<MaterializedLiveType<T>>;
|
|
161
|
+
private convertToMaterializedLiveType;
|
|
162
|
+
private applyWhere;
|
|
163
|
+
}
|
|
18
164
|
|
|
19
165
|
type Subscription = {
|
|
20
166
|
filters?: Record<string, any>;
|
|
21
167
|
};
|
|
22
|
-
declare const webSocketAdapter: (server: Server<AnyRouter>) => (ws: WebSocket) => void;
|
|
168
|
+
declare const webSocketAdapter: (server: Server<AnyRouter>) => (ws: WebSocket, request: any) => void;
|
|
23
169
|
|
|
24
|
-
type
|
|
170
|
+
type Request<TInput = any> = {
|
|
171
|
+
headers: Record<string, string>;
|
|
172
|
+
cookies: Record<string, string>;
|
|
25
173
|
resourceName: string;
|
|
174
|
+
procedure?: string;
|
|
26
175
|
context: Record<string, any>;
|
|
27
176
|
where?: Record<string, any>;
|
|
177
|
+
type: "QUERY" | "MUTATE";
|
|
178
|
+
resourceId?: string;
|
|
179
|
+
input?: TInput;
|
|
28
180
|
};
|
|
29
|
-
type FindRequest = RequestBase & {
|
|
30
|
-
type: "FIND";
|
|
31
|
-
};
|
|
32
|
-
type SetRequest = RequestBase & {
|
|
33
|
-
type: "SET";
|
|
34
|
-
resourceId: string;
|
|
35
|
-
payload: Record<string, any>;
|
|
36
|
-
};
|
|
37
|
-
type Request = FindRequest | SetRequest;
|
|
38
181
|
type RequestType = Request["type"];
|
|
39
|
-
type
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}): Router<TSchema, TRoutes>;
|
|
46
|
-
}
|
|
47
|
-
declare const router: <TSchema extends Schema<any>, TRoutes extends Record<keyof TSchema, Route<LiveObjectAny>>>(opts: {
|
|
48
|
-
schema: TSchema;
|
|
49
|
-
routes: TRoutes;
|
|
50
|
-
}) => Router<Schema<any>, TRoutes>;
|
|
51
|
-
type AnyRouter = Router<Schema<any>, RouteRecord>;
|
|
52
|
-
type RouteResult<TShape extends LiveObjectAny> = {
|
|
53
|
-
data: MaterializedLiveType<TShape> | Record<string, MaterializedLiveType<TShape>>;
|
|
54
|
-
acceptedValues: Record<string, any> | null;
|
|
55
|
-
};
|
|
56
|
-
declare class Route<TShape extends LiveObjectAny> {
|
|
57
|
-
readonly shape: TShape;
|
|
58
|
-
constructor(shape: TShape);
|
|
59
|
-
private handleFind;
|
|
60
|
-
private handleSet;
|
|
61
|
-
handleRequest(opts: {
|
|
62
|
-
req: Request;
|
|
63
|
-
db: Storage;
|
|
64
|
-
}): Promise<RouteResult<TShape>>;
|
|
65
|
-
}
|
|
66
|
-
declare const routeFactory: () => <T extends LiveObjectAny>(shape: T) => Route<T>;
|
|
67
|
-
type AnyRoute = Route<LiveObjectAny>;
|
|
68
|
-
type ClientId = string;
|
|
69
|
-
type MutationHandler = (mutation: MutationMessage) => void;
|
|
182
|
+
type MutationHandler = (mutation: RawMutationRequest) => void;
|
|
183
|
+
type NextFunction<T> = (req: Request) => Promise<T> | T;
|
|
184
|
+
type Middleware<T> = (opts: {
|
|
185
|
+
req: Request;
|
|
186
|
+
next: NextFunction<T>;
|
|
187
|
+
}) => ReturnType<NextFunction<T>>;
|
|
70
188
|
declare class Server<TRouter extends AnyRouter> {
|
|
71
189
|
readonly router: TRouter;
|
|
72
190
|
readonly storage: Storage;
|
|
73
191
|
readonly schema: Schema<any>;
|
|
192
|
+
readonly middlewares: Set<Middleware<any>>;
|
|
74
193
|
private mutationSubscriptions;
|
|
75
194
|
private constructor();
|
|
76
195
|
static create<TRouter extends AnyRouter>(opts: {
|
|
@@ -81,8 +200,9 @@ declare class Server<TRouter extends AnyRouter> {
|
|
|
81
200
|
subscribeToMutations(handler: MutationHandler): () => void;
|
|
82
201
|
handleRequest(opts: {
|
|
83
202
|
req: Request;
|
|
84
|
-
}): Promise<
|
|
203
|
+
}): Promise<any>;
|
|
204
|
+
use(middleware: Middleware<any>): this;
|
|
85
205
|
}
|
|
86
206
|
declare const server: typeof Server.create;
|
|
87
207
|
|
|
88
|
-
export { type AnyRoute, type AnyRouter, type
|
|
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 };
|
package/dist/server.d.ts
CHANGED
|
@@ -1,6 +1,141 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { z, ZodTypeAny } from 'zod';
|
|
2
|
+
import { LiveObjectAny, Schema, MaterializedLiveType, WhereClause } from './index.js';
|
|
3
|
+
import { PostgresPool } from 'kysely';
|
|
2
4
|
import WebSocket from 'ws';
|
|
3
|
-
|
|
5
|
+
|
|
6
|
+
declare const mutationSchema: z.ZodUnion<[z.ZodObject<z.objectUtil.extendShape<{
|
|
7
|
+
id: z.ZodOptional<z.ZodString>;
|
|
8
|
+
type: z.ZodLiteral<"MUTATE">;
|
|
9
|
+
resource: z.ZodString;
|
|
10
|
+
}, {
|
|
11
|
+
procedure: z.ZodString;
|
|
12
|
+
payload: z.ZodAny;
|
|
13
|
+
}>, "strip", z.ZodTypeAny, {
|
|
14
|
+
type: "MUTATE";
|
|
15
|
+
resource: string;
|
|
16
|
+
procedure: string;
|
|
17
|
+
id?: string | undefined;
|
|
18
|
+
payload?: any;
|
|
19
|
+
}, {
|
|
20
|
+
type: "MUTATE";
|
|
21
|
+
resource: string;
|
|
22
|
+
procedure: string;
|
|
23
|
+
id?: string | undefined;
|
|
24
|
+
payload?: any;
|
|
25
|
+
}>, z.ZodObject<z.objectUtil.extendShape<{
|
|
26
|
+
id: z.ZodOptional<z.ZodString>;
|
|
27
|
+
type: z.ZodLiteral<"MUTATE">;
|
|
28
|
+
resource: z.ZodString;
|
|
29
|
+
}, {
|
|
30
|
+
resourceId: z.ZodString;
|
|
31
|
+
payload: z.ZodEffects<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
32
|
+
value: z.ZodUnion<[z.ZodUnion<[z.ZodUnion<[z.ZodString, z.ZodNumber]>, z.ZodBoolean]>, z.ZodDate]>;
|
|
33
|
+
_meta: z.ZodOptional<z.ZodObject<{
|
|
34
|
+
timestamp: z.ZodOptional<z.ZodString>;
|
|
35
|
+
}, "strip", z.ZodTypeAny, {
|
|
36
|
+
timestamp?: string | undefined;
|
|
37
|
+
}, {
|
|
38
|
+
timestamp?: string | undefined;
|
|
39
|
+
}>>;
|
|
40
|
+
}, "strip", z.ZodTypeAny, {
|
|
41
|
+
value: string | number | boolean | Date;
|
|
42
|
+
_meta?: {
|
|
43
|
+
timestamp?: string | undefined;
|
|
44
|
+
} | undefined;
|
|
45
|
+
}, {
|
|
46
|
+
value: string | number | boolean | Date;
|
|
47
|
+
_meta?: {
|
|
48
|
+
timestamp?: string | undefined;
|
|
49
|
+
} | undefined;
|
|
50
|
+
}>>, Record<string, {
|
|
51
|
+
value: string | number | boolean | Date;
|
|
52
|
+
_meta?: {
|
|
53
|
+
timestamp?: string | undefined;
|
|
54
|
+
} | undefined;
|
|
55
|
+
}>, Record<string, {
|
|
56
|
+
value: string | number | boolean | Date;
|
|
57
|
+
_meta?: {
|
|
58
|
+
timestamp?: string | undefined;
|
|
59
|
+
} | undefined;
|
|
60
|
+
}>>;
|
|
61
|
+
}>, "strip", z.ZodTypeAny, {
|
|
62
|
+
type: "MUTATE";
|
|
63
|
+
resource: string;
|
|
64
|
+
payload: Record<string, {
|
|
65
|
+
value: string | number | boolean | Date;
|
|
66
|
+
_meta?: {
|
|
67
|
+
timestamp?: string | undefined;
|
|
68
|
+
} | undefined;
|
|
69
|
+
}>;
|
|
70
|
+
resourceId: string;
|
|
71
|
+
id?: string | undefined;
|
|
72
|
+
}, {
|
|
73
|
+
type: "MUTATE";
|
|
74
|
+
resource: string;
|
|
75
|
+
payload: Record<string, {
|
|
76
|
+
value: string | number | boolean | Date;
|
|
77
|
+
_meta?: {
|
|
78
|
+
timestamp?: string | undefined;
|
|
79
|
+
} | undefined;
|
|
80
|
+
}>;
|
|
81
|
+
resourceId: string;
|
|
82
|
+
id?: string | undefined;
|
|
83
|
+
}>]>;
|
|
84
|
+
type RawMutationRequest = z.infer<typeof mutationSchema>;
|
|
85
|
+
|
|
86
|
+
type RouteRecord = Record<string, AnyRoute>;
|
|
87
|
+
declare class Router<TRoutes extends RouteRecord> {
|
|
88
|
+
readonly routes: TRoutes;
|
|
89
|
+
private constructor();
|
|
90
|
+
static create<TRoutes extends RouteRecord>(opts: {
|
|
91
|
+
routes: TRoutes;
|
|
92
|
+
}): Router<TRoutes>;
|
|
93
|
+
}
|
|
94
|
+
declare const router: <TSchema extends Schema<any>, TRoutes extends Record<keyof TSchema, AnyRoute>>(opts: {
|
|
95
|
+
schema: TSchema;
|
|
96
|
+
routes: TRoutes;
|
|
97
|
+
}) => Router<TRoutes>;
|
|
98
|
+
type AnyRouter = Router<RouteRecord>;
|
|
99
|
+
type QueryResult<TShape extends LiveObjectAny> = {
|
|
100
|
+
data: Record<string, MaterializedLiveType<TShape>>;
|
|
101
|
+
};
|
|
102
|
+
type MutationResult<TShape extends LiveObjectAny> = {
|
|
103
|
+
data: MaterializedLiveType<TShape>;
|
|
104
|
+
acceptedValues: Record<string, any> | null;
|
|
105
|
+
};
|
|
106
|
+
type RequestHandler<TInput, TResult, TSchema extends Schema<any> = Schema<any>> = (opts: {
|
|
107
|
+
req: Request<TInput>;
|
|
108
|
+
db: Storage;
|
|
109
|
+
schema: TSchema;
|
|
110
|
+
}) => Promise<TResult>;
|
|
111
|
+
type Mutation<TInputValidator extends ZodTypeAny, // TODO use StandardSchema instead
|
|
112
|
+
THandler extends RequestHandler<z.infer<TInputValidator>, any, any>> = {
|
|
113
|
+
inputValidator: TInputValidator;
|
|
114
|
+
handler: THandler;
|
|
115
|
+
};
|
|
116
|
+
declare const mutationCreator: <TInputValidator extends ZodTypeAny>(validator?: TInputValidator) => {
|
|
117
|
+
handler: <THandler extends RequestHandler<z.infer<TInputValidator>, any, any>>(handler: THandler) => Mutation<TInputValidator, THandler>;
|
|
118
|
+
};
|
|
119
|
+
declare class Route<TResourceSchema extends LiveObjectAny, TMiddleware extends Middleware<any>, TCustomMutations extends Record<string, Mutation<any, RequestHandler<any, any>>>> {
|
|
120
|
+
readonly _resourceSchema: TResourceSchema;
|
|
121
|
+
readonly resourceName: TResourceSchema["name"];
|
|
122
|
+
readonly middlewares: Set<TMiddleware>;
|
|
123
|
+
readonly customMutations: TCustomMutations;
|
|
124
|
+
constructor(resourceName: TResourceSchema["name"], customMutations?: TCustomMutations);
|
|
125
|
+
private handleFind;
|
|
126
|
+
private handleSet;
|
|
127
|
+
handleRequest(opts: {
|
|
128
|
+
req: Request;
|
|
129
|
+
db: Storage;
|
|
130
|
+
schema: Schema<any>;
|
|
131
|
+
}): Promise<any>;
|
|
132
|
+
use(middleware: TMiddleware): this;
|
|
133
|
+
withMutations<T extends Record<string, Mutation<any, RequestHandler<any, any>>>>(mutationFactory: (opts: {
|
|
134
|
+
mutation: typeof mutationCreator;
|
|
135
|
+
}) => T): Route<TResourceSchema, TMiddleware, T>;
|
|
136
|
+
}
|
|
137
|
+
declare const routeFactory: () => <T extends LiveObjectAny>(shape: T) => Route<T, Middleware<any>, Record<string, never>>;
|
|
138
|
+
type AnyRoute = Route<LiveObjectAny, Middleware<any>, Record<string, any>>;
|
|
4
139
|
|
|
5
140
|
declare abstract class Storage {
|
|
6
141
|
abstract updateSchema(opts: Schema<any>): Promise<void>;
|
|
@@ -15,62 +150,46 @@ declare class InMemoryStorage extends Storage {
|
|
|
15
150
|
find<T extends LiveObjectAny>(resourceName: string, where?: Record<string, any>): Promise<Record<string, MaterializedLiveType<T>>>;
|
|
16
151
|
upsert<T extends LiveObjectAny>(resourceName: string, resourceId: string, value: MaterializedLiveType<T>): Promise<MaterializedLiveType<T>>;
|
|
17
152
|
}
|
|
153
|
+
declare class SQLStorage extends Storage {
|
|
154
|
+
private db;
|
|
155
|
+
private schema?;
|
|
156
|
+
constructor(pool: PostgresPool);
|
|
157
|
+
updateSchema(opts: Schema<any>): Promise<void>;
|
|
158
|
+
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>>>;
|
|
160
|
+
upsert<T extends LiveObjectAny>(resourceName: string, resourceId: string, value: MaterializedLiveType<T>): Promise<MaterializedLiveType<T>>;
|
|
161
|
+
private convertToMaterializedLiveType;
|
|
162
|
+
private applyWhere;
|
|
163
|
+
}
|
|
18
164
|
|
|
19
165
|
type Subscription = {
|
|
20
166
|
filters?: Record<string, any>;
|
|
21
167
|
};
|
|
22
|
-
declare const webSocketAdapter: (server: Server<AnyRouter>) => (ws: WebSocket) => void;
|
|
168
|
+
declare const webSocketAdapter: (server: Server<AnyRouter>) => (ws: WebSocket, request: any) => void;
|
|
23
169
|
|
|
24
|
-
type
|
|
170
|
+
type Request<TInput = any> = {
|
|
171
|
+
headers: Record<string, string>;
|
|
172
|
+
cookies: Record<string, string>;
|
|
25
173
|
resourceName: string;
|
|
174
|
+
procedure?: string;
|
|
26
175
|
context: Record<string, any>;
|
|
27
176
|
where?: Record<string, any>;
|
|
177
|
+
type: "QUERY" | "MUTATE";
|
|
178
|
+
resourceId?: string;
|
|
179
|
+
input?: TInput;
|
|
28
180
|
};
|
|
29
|
-
type FindRequest = RequestBase & {
|
|
30
|
-
type: "FIND";
|
|
31
|
-
};
|
|
32
|
-
type SetRequest = RequestBase & {
|
|
33
|
-
type: "SET";
|
|
34
|
-
resourceId: string;
|
|
35
|
-
payload: Record<string, any>;
|
|
36
|
-
};
|
|
37
|
-
type Request = FindRequest | SetRequest;
|
|
38
181
|
type RequestType = Request["type"];
|
|
39
|
-
type
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}): Router<TSchema, TRoutes>;
|
|
46
|
-
}
|
|
47
|
-
declare const router: <TSchema extends Schema<any>, TRoutes extends Record<keyof TSchema, Route<LiveObjectAny>>>(opts: {
|
|
48
|
-
schema: TSchema;
|
|
49
|
-
routes: TRoutes;
|
|
50
|
-
}) => Router<Schema<any>, TRoutes>;
|
|
51
|
-
type AnyRouter = Router<Schema<any>, RouteRecord>;
|
|
52
|
-
type RouteResult<TShape extends LiveObjectAny> = {
|
|
53
|
-
data: MaterializedLiveType<TShape> | Record<string, MaterializedLiveType<TShape>>;
|
|
54
|
-
acceptedValues: Record<string, any> | null;
|
|
55
|
-
};
|
|
56
|
-
declare class Route<TShape extends LiveObjectAny> {
|
|
57
|
-
readonly shape: TShape;
|
|
58
|
-
constructor(shape: TShape);
|
|
59
|
-
private handleFind;
|
|
60
|
-
private handleSet;
|
|
61
|
-
handleRequest(opts: {
|
|
62
|
-
req: Request;
|
|
63
|
-
db: Storage;
|
|
64
|
-
}): Promise<RouteResult<TShape>>;
|
|
65
|
-
}
|
|
66
|
-
declare const routeFactory: () => <T extends LiveObjectAny>(shape: T) => Route<T>;
|
|
67
|
-
type AnyRoute = Route<LiveObjectAny>;
|
|
68
|
-
type ClientId = string;
|
|
69
|
-
type MutationHandler = (mutation: MutationMessage) => void;
|
|
182
|
+
type MutationHandler = (mutation: RawMutationRequest) => void;
|
|
183
|
+
type NextFunction<T> = (req: Request) => Promise<T> | T;
|
|
184
|
+
type Middleware<T> = (opts: {
|
|
185
|
+
req: Request;
|
|
186
|
+
next: NextFunction<T>;
|
|
187
|
+
}) => ReturnType<NextFunction<T>>;
|
|
70
188
|
declare class Server<TRouter extends AnyRouter> {
|
|
71
189
|
readonly router: TRouter;
|
|
72
190
|
readonly storage: Storage;
|
|
73
191
|
readonly schema: Schema<any>;
|
|
192
|
+
readonly middlewares: Set<Middleware<any>>;
|
|
74
193
|
private mutationSubscriptions;
|
|
75
194
|
private constructor();
|
|
76
195
|
static create<TRouter extends AnyRouter>(opts: {
|
|
@@ -81,8 +200,9 @@ declare class Server<TRouter extends AnyRouter> {
|
|
|
81
200
|
subscribeToMutations(handler: MutationHandler): () => void;
|
|
82
201
|
handleRequest(opts: {
|
|
83
202
|
req: Request;
|
|
84
|
-
}): Promise<
|
|
203
|
+
}): Promise<any>;
|
|
204
|
+
use(middleware: Middleware<any>): this;
|
|
85
205
|
}
|
|
86
206
|
declare const server: typeof Server.create;
|
|
87
207
|
|
|
88
|
-
export { type AnyRoute, type AnyRouter, type
|
|
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 };
|
package/dist/server.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import {nanoid}from'nanoid';import {z}from'zod';var y=class{},S=class extends y{storage={};async updateSchema(e){console.log("Updating schema",e),this.storage=Object.entries(e).reduce((r,[s,i])=>(r[i.name]={},r),{});}async findById(e,r){var s;return (s=this.storage[e])==null?void 0:s[r]}async find(e,r){return this.storage[e]??{}}async upsert(e,r,s){return this.storage[e]??={},this.storage[e][r]=s,s}};var d=z.string().nanoid(),v=z.object({_id:d,type:z.literal("SUBSCRIBE"),resource:z.string()}),q=z.object({_id:d,type:z.literal("SYNC"),lastSyncedAt:z.string().optional(),resources:z.string().array().optional()}),R=z.record(z.object({value:z.string().or(z.number()).or(z.boolean()).or(z.date()),_meta:z.object({timestamp:z.string().optional()}).optional()})).superRefine((a,e)=>{a.id&&e.addIssue({code:z.ZodIssueCode.custom,message:"Payload cannot have an id"});}),T=z.object({_id:d,type:z.literal("MUTATE"),resource:z.string(),resourceId:z.string(),payload:R}),b=z.union([v,T,q]),x=z.object({_id:d,type:z.literal("SYNC"),resource:z.string(),data:z.record(R)}),j=z.object({_id:d,type:z.literal("REJECT"),resource:z.string()});z.union([T,x,j]);var _=a=>{let e={},r={};return a.subscribeToMutations(s=>{console.log("Mutation propagated:",s),Object.entries(r[s.resource]??{}).forEach(([i,p])=>{var o;(o=e[i])==null||o.send(JSON.stringify(s));});}),s=>{let i=nanoid();e[i]=s,console.log("Client connected:",i),s.on("message",async p=>{try{console.log("Message received from the client:",p);let o=b.parse(JSON.parse(p.toString()));if(o.type==="SUBSCRIBE"){let{resource:n}=o;r[n]||(r[n]={}),r[n][i]={};}else if(o.type==="SYNC"){let{resources:n}=o,c=n??Object.keys(a.schema);console.log("Syncing resources:",c),await Promise.all(c.map(async u=>{let l=await a.handleRequest({req:{type:"FIND",resourceName:u,context:{}}});if(!l||!l.data)throw new Error("Invalid resource");s.send(JSON.stringify({_id:o._id,type:"SYNC",resource:u,data:Object.fromEntries(Object.entries(l.data??{}).map(([f,M])=>[f,M.value]))}));}));}else if(o.type==="MUTATE"){let{resource:n}=o;console.log("Received mutation from client:",o);try{let c=await a.handleRequest({req:{type:"SET",resourceName:n,payload:o.payload,context:{messageId:o._id},resourceId:o.resourceId}}).catch(u=>(console.error("Error handling mutation from the client:",u),null));(!c||!c.acceptedValues||Object.keys(c.acceptedValues).length===0)&&s.send(JSON.stringify({_id:o._id,type:"REJECT",resource:n}));}catch(c){s.send(JSON.stringify({_id:o._id,type:"REJECT",resource:n})),console.error("Error parsing mutation from the client:",c);}}}catch(o){console.error("Error handling message from the client:",o);}}),s.on("close",()=>{console.log("Connection closed",i),delete e[i];});}};var g=class a{routes;constructor(e){this.routes=e.routes;}static create(e){return new a(e)}},V=a=>g.create({...a}),h=class{shape;constructor(e){this.shape=e;}async handleFind(e){return {data:await e.db.find(e.req.resourceName,e.req.where),acceptedValues:null}}async handleSet(e){if(!e.req.payload)throw new Error("Payload is required");if(!e.req.resourceId)throw new Error("ResourceId is required");let r=await e.db.findById(e.req.resourceName,e.req.resourceId),[s,i]=this.shape.mergeMutation("set",e.req.payload,r);if(!i){if(!r)throw new Error("Mutation rejected");return {data:r,acceptedValues:null}}return {data:await e.db.upsert(e.req.resourceName,e.req.resourceId,s),acceptedValues:i}}async handleRequest(e){switch(e.req.type){case "FIND":return this.handleFind(e);case "SET":return this.handleSet(e);default:throw new Error("Invalid request type")}}},Z=()=>a=>new h(a),m=class a{router;storage;schema;mutationSubscriptions=new Set;constructor(e){this.router=e.router,this.storage=e.storage,this.schema=e.schema,this.storage.updateSchema(this.schema);}static create(e){return new a(e)}subscribeToMutations(e){return this.mutationSubscriptions.add(e),()=>{this.mutationSubscriptions.delete(e);}}async handleRequest(e){var s;let r=await((s=this.router.routes[e.req.resourceName])==null?void 0:s.handleRequest({req:e.req,db:this.storage}));return r&&e.req.type==="SET"&&r.acceptedValues&&Object.keys(r.acceptedValues).length>0&&this.mutationSubscriptions.forEach(i=>{i({_id:e.req.context.messageId??nanoid(),type:"MUTATE",resource:e.req.resourceName,payload:r.acceptedValues??{},resourceId:e.req.resourceId});}),r}},k=m.create;export{S as InMemoryStorage,h as Route,g as Router,m as Server,y as Storage,Z as routeFactory,V as router,k as server,_ as webSocketAdapter};
|
|
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};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@live-state/sync",
|
|
3
|
-
"version": "0.0.1-alpha.
|
|
3
|
+
"version": "0.0.1-alpha.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -20,27 +20,18 @@
|
|
|
20
20
|
"dist/**"
|
|
21
21
|
],
|
|
22
22
|
"type": "module",
|
|
23
|
-
"scripts": {
|
|
24
|
-
"build": "tsup",
|
|
25
|
-
"dev": "tsup --watch",
|
|
26
|
-
"lint": "eslint src/",
|
|
27
|
-
"typecheck": "tsc --noEmit",
|
|
28
|
-
"test": "jest"
|
|
29
|
-
},
|
|
30
23
|
"devDependencies": {
|
|
31
|
-
"@jest/globals": "^29.7.0",
|
|
32
|
-
"@repo/typescript-config": "workspace:*",
|
|
33
|
-
"@types/express-ws": "^3.0.5",
|
|
34
24
|
"@types/node": "^20.11.24",
|
|
35
25
|
"@types/ws": "^8.5.13",
|
|
36
|
-
"
|
|
26
|
+
"cookie": "^1.0.2",
|
|
37
27
|
"react": "18.0.0",
|
|
38
28
|
"tsup": "^8.0.2",
|
|
39
|
-
"typescript": "5.5.4"
|
|
29
|
+
"typescript": "5.5.4",
|
|
30
|
+
"ulid": "^3.0.0",
|
|
31
|
+
"@repo/typescript-config": "0.0.0"
|
|
40
32
|
},
|
|
41
33
|
"dependencies": {
|
|
42
|
-
"
|
|
43
|
-
"nanoid": "^5.0.9",
|
|
34
|
+
"kysely": "^0.28.2",
|
|
44
35
|
"ws": "^8.18.0",
|
|
45
36
|
"zod": "^3.24.1"
|
|
46
37
|
},
|
|
@@ -72,5 +63,11 @@
|
|
|
72
63
|
"@types/react": {
|
|
73
64
|
"optional": true
|
|
74
65
|
}
|
|
66
|
+
},
|
|
67
|
+
"scripts": {
|
|
68
|
+
"build": "tsup",
|
|
69
|
+
"dev": "tsup --watch",
|
|
70
|
+
"lint": "eslint src/",
|
|
71
|
+
"typecheck": "tsc --noEmit"
|
|
75
72
|
}
|
|
76
|
-
}
|
|
73
|
+
}
|