@proseql/rpc 0.1.0
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 +21 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +61 -0
- package/dist/index.js.map +1 -0
- package/dist/rpc-errors.d.ts +208 -0
- package/dist/rpc-errors.d.ts.map +1 -0
- package/dist/rpc-errors.js +159 -0
- package/dist/rpc-errors.js.map +1 -0
- package/dist/rpc-group.d.ts +819 -0
- package/dist/rpc-group.d.ts.map +1 -0
- package/dist/rpc-group.js +614 -0
- package/dist/rpc-group.js.map +1 -0
- package/dist/rpc-handlers.d.ts +247 -0
- package/dist/rpc-handlers.d.ts.map +1 -0
- package/dist/rpc-handlers.js +266 -0
- package/dist/rpc-handlers.js.map +1 -0
- package/dist/rpc-schemas.d.ts +382 -0
- package/dist/rpc-schemas.d.ts.map +1 -0
- package/dist/rpc-schemas.js +382 -0
- package/dist/rpc-schemas.js.map +1 -0
- package/package.json +50 -0
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RPC Handler Layer implementation.
|
|
3
|
+
*
|
|
4
|
+
* Creates an Effect Layer that provides handlers for all RPC procedures
|
|
5
|
+
* derived from a DatabaseConfig. The layer internally creates an EffectDatabase
|
|
6
|
+
* and wires each handler to the appropriate collection method.
|
|
7
|
+
*/
|
|
8
|
+
import { type DatabaseConfig, type DatasetFor, type GenerateDatabase, type GenerateDatabaseWithPersistence, type MigrationError, type PluginError } from "@proseql/core";
|
|
9
|
+
import { Context, Effect, Layer, Stream } from "effect";
|
|
10
|
+
/**
|
|
11
|
+
* Service tag for providing the database instance to handlers.
|
|
12
|
+
* This allows handlers to access the database without creating it themselves.
|
|
13
|
+
*/
|
|
14
|
+
export interface DatabaseContext<Config extends DatabaseConfig> {
|
|
15
|
+
readonly db: GenerateDatabase<Config>;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Create a Context.Tag for a specific database configuration.
|
|
19
|
+
* Each config type gets its own unique service identifier.
|
|
20
|
+
*/
|
|
21
|
+
export declare const makeDatabaseContextTag: <Config extends DatabaseConfig>() => Context.Tag<DatabaseContext<Config>, DatabaseContext<Config>>;
|
|
22
|
+
/**
|
|
23
|
+
* Internal function to create handlers for a single collection.
|
|
24
|
+
* Returns an object with handler functions for each RPC operation.
|
|
25
|
+
*/
|
|
26
|
+
declare const createCollectionHandlers: <Config extends DatabaseConfig>(collectionName: keyof Config, db: GenerateDatabase<Config>) => {
|
|
27
|
+
findById: ({ id }: {
|
|
28
|
+
readonly id: string;
|
|
29
|
+
}) => any;
|
|
30
|
+
query: (config: {
|
|
31
|
+
readonly where?: Record<string, unknown>;
|
|
32
|
+
readonly populate?: Record<string, unknown>;
|
|
33
|
+
readonly sort?: Record<string, "asc" | "desc">;
|
|
34
|
+
readonly select?: Record<string, unknown> | ReadonlyArray<string>;
|
|
35
|
+
readonly limit?: number;
|
|
36
|
+
readonly offset?: number;
|
|
37
|
+
}) => Effect.Effect<readonly Record<string, unknown>[], unknown, never>;
|
|
38
|
+
queryStream: (config: {
|
|
39
|
+
readonly where?: Record<string, unknown>;
|
|
40
|
+
readonly populate?: Record<string, unknown>;
|
|
41
|
+
readonly sort?: Record<string, "asc" | "desc">;
|
|
42
|
+
readonly select?: Record<string, unknown> | ReadonlyArray<string>;
|
|
43
|
+
readonly limit?: number;
|
|
44
|
+
readonly offset?: number;
|
|
45
|
+
readonly streamingOptions?: {
|
|
46
|
+
readonly chunkSize?: number;
|
|
47
|
+
readonly bufferSize?: number;
|
|
48
|
+
};
|
|
49
|
+
}) => Stream.Stream<Record<string, unknown>, unknown, never>;
|
|
50
|
+
create: ({ data }: {
|
|
51
|
+
readonly data: Record<string, unknown>;
|
|
52
|
+
}) => any;
|
|
53
|
+
createMany: ({ data, options, }: {
|
|
54
|
+
readonly data: ReadonlyArray<Record<string, unknown>>;
|
|
55
|
+
readonly options?: {
|
|
56
|
+
readonly skipDuplicates?: boolean;
|
|
57
|
+
readonly validateRelationships?: boolean;
|
|
58
|
+
};
|
|
59
|
+
}) => any;
|
|
60
|
+
update: ({ id, updates, }: {
|
|
61
|
+
readonly id: string;
|
|
62
|
+
readonly updates: Record<string, unknown>;
|
|
63
|
+
}) => any;
|
|
64
|
+
updateMany: ({ where, updates, }: {
|
|
65
|
+
readonly where: Record<string, unknown>;
|
|
66
|
+
readonly updates: Record<string, unknown>;
|
|
67
|
+
}) => any;
|
|
68
|
+
delete: ({ id }: {
|
|
69
|
+
readonly id: string;
|
|
70
|
+
}) => any;
|
|
71
|
+
deleteMany: ({ where, options, }: {
|
|
72
|
+
readonly where: Record<string, unknown>;
|
|
73
|
+
readonly options?: {
|
|
74
|
+
readonly limit?: number;
|
|
75
|
+
};
|
|
76
|
+
}) => any;
|
|
77
|
+
aggregate: (config: {
|
|
78
|
+
readonly count?: boolean;
|
|
79
|
+
readonly sum?: string | ReadonlyArray<string>;
|
|
80
|
+
readonly avg?: string | ReadonlyArray<string>;
|
|
81
|
+
readonly min?: string | ReadonlyArray<string>;
|
|
82
|
+
readonly max?: string | ReadonlyArray<string>;
|
|
83
|
+
readonly groupBy?: string | ReadonlyArray<string>;
|
|
84
|
+
readonly where?: Record<string, unknown>;
|
|
85
|
+
}) => any;
|
|
86
|
+
upsert: ({ where, create: createData, update: updateData, }: {
|
|
87
|
+
readonly where: Record<string, unknown>;
|
|
88
|
+
readonly create: Record<string, unknown>;
|
|
89
|
+
readonly update: Record<string, unknown>;
|
|
90
|
+
}) => any;
|
|
91
|
+
upsertMany: ({ data, }: {
|
|
92
|
+
readonly data: ReadonlyArray<{
|
|
93
|
+
readonly where: Record<string, unknown>;
|
|
94
|
+
readonly create: Record<string, unknown>;
|
|
95
|
+
readonly update: Record<string, unknown>;
|
|
96
|
+
}>;
|
|
97
|
+
}) => any;
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* Handler type for the RPC layer.
|
|
101
|
+
* This is the shape of handlers that need to be provided to the RpcGroup.
|
|
102
|
+
*/
|
|
103
|
+
export type RpcHandlers<Config extends DatabaseConfig> = {
|
|
104
|
+
readonly [K in keyof Config & string]: ReturnType<typeof createCollectionHandlers<Config>>;
|
|
105
|
+
};
|
|
106
|
+
/**
|
|
107
|
+
* Create an Effect Layer that provides handlers for all RPC procedures.
|
|
108
|
+
*
|
|
109
|
+
* The layer:
|
|
110
|
+
* 1. Creates an in-memory EffectDatabase from the config and optional initial data
|
|
111
|
+
* 2. For each collection, wires handlers to the appropriate database methods
|
|
112
|
+
* 3. Returns a Layer that provides the handler implementations
|
|
113
|
+
*
|
|
114
|
+
* @param config - The database configuration defining collections and schemas
|
|
115
|
+
* @param initialData - Optional initial data to seed the database
|
|
116
|
+
* @returns An Effect that produces the handler implementations for use with RpcGroup.toLayer
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* ```typescript
|
|
120
|
+
* import { Layer } from "effect"
|
|
121
|
+
* import { makeRpcHandlers, makeRpcGroup } from "@proseql/rpc"
|
|
122
|
+
*
|
|
123
|
+
* const config = {
|
|
124
|
+
* books: { schema: BookSchema, relationships: {} },
|
|
125
|
+
* } as const
|
|
126
|
+
*
|
|
127
|
+
* const rpcs = makeRpcGroup(config)
|
|
128
|
+
*
|
|
129
|
+
* // Create handler implementations
|
|
130
|
+
* const handlerEffect = makeRpcHandlers(config, {
|
|
131
|
+
* books: [{ id: "1", title: "Dune" }],
|
|
132
|
+
* })
|
|
133
|
+
*
|
|
134
|
+
* // Use with RpcGroup.toLayer for the complete RPC server layer
|
|
135
|
+
* ```
|
|
136
|
+
*/
|
|
137
|
+
export declare const makeRpcHandlers: <Config extends DatabaseConfig>(config: Config, initialData?: Partial<DatasetFor<Config>>) => Effect.Effect<RpcHandlers<Config>, MigrationError | PluginError>;
|
|
138
|
+
/**
|
|
139
|
+
* Create an Effect Layer that provides RPC handlers for all collections.
|
|
140
|
+
*
|
|
141
|
+
* This is a convenience wrapper that combines makeRpcHandlers with makeRpcGroup
|
|
142
|
+
* to produce a complete Layer ready for use with Effect RPC server.
|
|
143
|
+
*
|
|
144
|
+
* @param config - The database configuration
|
|
145
|
+
* @param initialData - Optional initial data to seed the database
|
|
146
|
+
* @returns A Layer providing all RPC handlers
|
|
147
|
+
*
|
|
148
|
+
* @example
|
|
149
|
+
* ```typescript
|
|
150
|
+
* import { Effect } from "effect"
|
|
151
|
+
* import { makeRpcHandlersLayer } from "@proseql/rpc"
|
|
152
|
+
*
|
|
153
|
+
* const config = {
|
|
154
|
+
* books: { schema: BookSchema, relationships: {} },
|
|
155
|
+
* } as const
|
|
156
|
+
*
|
|
157
|
+
* // Create handler layer (can be composed with other layers)
|
|
158
|
+
* const handlersLayer = makeRpcHandlersLayer(config, {
|
|
159
|
+
* books: [{ id: "1", title: "Dune" }],
|
|
160
|
+
* })
|
|
161
|
+
* ```
|
|
162
|
+
*/
|
|
163
|
+
export declare const makeRpcHandlersLayer: <Config extends DatabaseConfig>(config: Config, initialData?: Partial<DatasetFor<Config>>) => Layer.Layer<DatabaseContext<Config>, MigrationError | PluginError>;
|
|
164
|
+
/**
|
|
165
|
+
* Create RPC handlers from an existing database instance.
|
|
166
|
+
*
|
|
167
|
+
* This function accepts any EffectDatabase or EffectDatabaseWithPersistence
|
|
168
|
+
* and wires handlers to delegate to the collection methods. When the database
|
|
169
|
+
* is a persistent database (created via createPersistentEffectDatabase),
|
|
170
|
+
* mutations automatically trigger persistence as normal.
|
|
171
|
+
*
|
|
172
|
+
* This is the recommended approach for production use cases where you need:
|
|
173
|
+
* - File-based persistence with debounced writes
|
|
174
|
+
* - Control over the database lifecycle
|
|
175
|
+
* - Multiple transports (RPC, REST) sharing the same database instance
|
|
176
|
+
*
|
|
177
|
+
* @param config - The database configuration (used to enumerate collections)
|
|
178
|
+
* @param db - An existing EffectDatabase or EffectDatabaseWithPersistence instance
|
|
179
|
+
* @returns The RPC handler implementations
|
|
180
|
+
*
|
|
181
|
+
* @example
|
|
182
|
+
* ```typescript
|
|
183
|
+
* import { Effect, Layer } from "effect"
|
|
184
|
+
* import { createPersistentEffectDatabase, NodeStorageLayer, makeSerializerLayer, jsonCodec } from "@proseql/node"
|
|
185
|
+
* import { makeRpcHandlersFromDatabase } from "@proseql/rpc"
|
|
186
|
+
*
|
|
187
|
+
* const config = {
|
|
188
|
+
* books: {
|
|
189
|
+
* schema: BookSchema,
|
|
190
|
+
* file: "./data/books.json", // persistence enabled
|
|
191
|
+
* relationships: {},
|
|
192
|
+
* },
|
|
193
|
+
* } as const
|
|
194
|
+
*
|
|
195
|
+
* const program = Effect.gen(function* () {
|
|
196
|
+
* // Create persistent database
|
|
197
|
+
* const db = yield* createPersistentEffectDatabase(config, { books: [] })
|
|
198
|
+
*
|
|
199
|
+
* // Wire RPC handlers to the persistent database
|
|
200
|
+
* const handlers = makeRpcHandlersFromDatabase(config, db)
|
|
201
|
+
*
|
|
202
|
+
* // Mutations through RPC now trigger persistence automatically
|
|
203
|
+
* yield* handlers.books.create({ data: { id: "1", title: "Dune" } })
|
|
204
|
+
*
|
|
205
|
+
* // Flush to ensure data is written
|
|
206
|
+
* await db.flush()
|
|
207
|
+
* })
|
|
208
|
+
*
|
|
209
|
+
* const PersistenceLayer = Layer.merge(
|
|
210
|
+
* NodeStorageLayer,
|
|
211
|
+
* makeSerializerLayer([jsonCodec()]),
|
|
212
|
+
* )
|
|
213
|
+
*
|
|
214
|
+
* await Effect.runPromise(
|
|
215
|
+
* program.pipe(Effect.provide(PersistenceLayer), Effect.scoped),
|
|
216
|
+
* )
|
|
217
|
+
* ```
|
|
218
|
+
*/
|
|
219
|
+
export declare const makeRpcHandlersFromDatabase: <Config extends DatabaseConfig>(config: Config, db: GenerateDatabase<Config> | GenerateDatabaseWithPersistence<Config>) => RpcHandlers<Config>;
|
|
220
|
+
/**
|
|
221
|
+
* Create an Effect Layer that provides RPC handlers from an existing database.
|
|
222
|
+
*
|
|
223
|
+
* Similar to makeRpcHandlersLayer, but accepts an existing database instance
|
|
224
|
+
* instead of creating one internally. This allows you to use a persistent
|
|
225
|
+
* database with the RPC layer.
|
|
226
|
+
*
|
|
227
|
+
* @param db - An existing EffectDatabase or EffectDatabaseWithPersistence instance
|
|
228
|
+
* @returns A Layer providing all RPC handlers via DatabaseContext
|
|
229
|
+
*
|
|
230
|
+
* @example
|
|
231
|
+
* ```typescript
|
|
232
|
+
* import { Effect, Layer } from "effect"
|
|
233
|
+
* import { createPersistentEffectDatabase } from "@proseql/node"
|
|
234
|
+
* import { makeRpcHandlersLayerFromDatabase, makeDatabaseContextTag } from "@proseql/rpc"
|
|
235
|
+
*
|
|
236
|
+
* const program = Effect.gen(function* () {
|
|
237
|
+
* const db = yield* createPersistentEffectDatabase(config, initialData)
|
|
238
|
+
* const handlerLayer = makeRpcHandlersLayerFromDatabase(db)
|
|
239
|
+
*
|
|
240
|
+
* // Use the layer with your RPC server
|
|
241
|
+
* // ...
|
|
242
|
+
* })
|
|
243
|
+
* ```
|
|
244
|
+
*/
|
|
245
|
+
export declare const makeRpcHandlersLayerFromDatabase: <Config extends DatabaseConfig>(db: GenerateDatabase<Config> | GenerateDatabaseWithPersistence<Config>) => Layer.Layer<DatabaseContext<Config>>;
|
|
246
|
+
export {};
|
|
247
|
+
//# sourceMappingURL=rpc-handlers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rpc-handlers.d.ts","sourceRoot":"","sources":["../src/rpc-handlers.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RPC Handler Layer implementation.
|
|
3
|
+
*
|
|
4
|
+
* Creates an Effect Layer that provides handlers for all RPC procedures
|
|
5
|
+
* derived from a DatabaseConfig. The layer internally creates an EffectDatabase
|
|
6
|
+
* and wires each handler to the appropriate collection method.
|
|
7
|
+
*/
|
|
8
|
+
import { createEffectDatabase, } from "@proseql/core";
|
|
9
|
+
import { Chunk, Context, Effect, Layer, Stream } from "effect";
|
|
10
|
+
/**
|
|
11
|
+
* Create a Context.Tag for a specific database configuration.
|
|
12
|
+
* Each config type gets its own unique service identifier.
|
|
13
|
+
*/
|
|
14
|
+
export const makeDatabaseContextTag = () => Context.GenericTag("@proseql/rpc/DatabaseContext");
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// Handler Implementations
|
|
17
|
+
// ============================================================================
|
|
18
|
+
/**
|
|
19
|
+
* Internal function to create handlers for a single collection.
|
|
20
|
+
* Returns an object with handler functions for each RPC operation.
|
|
21
|
+
*/
|
|
22
|
+
const createCollectionHandlers = (collectionName, db) => {
|
|
23
|
+
// biome-ignore lint/suspicious/noExplicitAny: Collection type is dynamic based on config
|
|
24
|
+
const collection = db[collectionName];
|
|
25
|
+
return {
|
|
26
|
+
findById: ({ id }) => collection.findById(id),
|
|
27
|
+
query: (config) => {
|
|
28
|
+
// Query returns a RunnableStream; collect it to an array for RPC response
|
|
29
|
+
const stream = collection.query(config);
|
|
30
|
+
// The stream is a Stream.Stream at runtime (RunnableStream wrapper)
|
|
31
|
+
return Stream.runCollect(stream).pipe(Effect.map(Chunk.toReadonlyArray));
|
|
32
|
+
},
|
|
33
|
+
queryStream: (config) => {
|
|
34
|
+
// Return the stream directly for incremental delivery over RPC transport
|
|
35
|
+
// The RPC layer will serialize stream items as they are emitted
|
|
36
|
+
const baseStream = collection.query(config);
|
|
37
|
+
// Apply rechunking if streamingOptions.chunkSize is specified
|
|
38
|
+
// This batches items into chunks of the specified size before they are
|
|
39
|
+
// sent over the RPC transport, reducing overhead at the cost of increased
|
|
40
|
+
// latency to first item. The RPC layer transmits items in chunks, so this
|
|
41
|
+
// ensures each transmission contains up to `chunkSize` items.
|
|
42
|
+
//
|
|
43
|
+
// Note: bufferSize is a client-side hint that should be passed to the
|
|
44
|
+
// RPC client's streamBufferSize option when making the call.
|
|
45
|
+
const chunkSize = config.streamingOptions?.chunkSize;
|
|
46
|
+
if (chunkSize && chunkSize > 1) {
|
|
47
|
+
return Stream.rechunk(baseStream, chunkSize);
|
|
48
|
+
}
|
|
49
|
+
return baseStream;
|
|
50
|
+
},
|
|
51
|
+
create: ({ data }) =>
|
|
52
|
+
// biome-ignore lint/suspicious/noExplicitAny: Data type is dynamic based on schema
|
|
53
|
+
collection.create(data),
|
|
54
|
+
createMany: ({ data, options, }) =>
|
|
55
|
+
// biome-ignore lint/suspicious/noExplicitAny: Data type is dynamic based on schema
|
|
56
|
+
collection.createMany(data, options),
|
|
57
|
+
update: ({ id, updates, }) =>
|
|
58
|
+
// biome-ignore lint/suspicious/noExplicitAny: Updates type is dynamic based on schema
|
|
59
|
+
collection.update(id, updates),
|
|
60
|
+
updateMany: ({ where, updates, }) => collection.updateMany(
|
|
61
|
+
// For RPC we receive where clause, convert to predicate that matches records
|
|
62
|
+
// biome-ignore lint/suspicious/noExplicitAny: Dynamic predicate - entity type unknown at runtime
|
|
63
|
+
(entity) => {
|
|
64
|
+
for (const [key, value] of Object.entries(where)) {
|
|
65
|
+
if (entity[key] !== value)
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
return true;
|
|
69
|
+
},
|
|
70
|
+
// biome-ignore lint/suspicious/noExplicitAny: Updates type is dynamic based on collection schema
|
|
71
|
+
updates),
|
|
72
|
+
delete: ({ id }) => collection.delete(id),
|
|
73
|
+
deleteMany: ({ where, options, }) =>
|
|
74
|
+
// biome-ignore lint/suspicious/noExplicitAny: Predicate is dynamic
|
|
75
|
+
collection.deleteMany((entity) => {
|
|
76
|
+
for (const [key, value] of Object.entries(where)) {
|
|
77
|
+
if (entity[key] !== value)
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
return true;
|
|
81
|
+
}, options),
|
|
82
|
+
aggregate: (config) =>
|
|
83
|
+
// biome-ignore lint/suspicious/noExplicitAny: Aggregate config is dynamic
|
|
84
|
+
collection.aggregate(config),
|
|
85
|
+
upsert: ({ where, create: createData, update: updateData, }) => collection.upsert({
|
|
86
|
+
where,
|
|
87
|
+
create: createData,
|
|
88
|
+
update: updateData,
|
|
89
|
+
// biome-ignore lint/suspicious/noExplicitAny: Upsert data is dynamic - types unknown at runtime
|
|
90
|
+
}),
|
|
91
|
+
upsertMany: ({ data, }) =>
|
|
92
|
+
// biome-ignore lint/suspicious/noExplicitAny: Upsert data is dynamic
|
|
93
|
+
collection.upsertMany(data),
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
/**
|
|
97
|
+
* Create an Effect Layer that provides handlers for all RPC procedures.
|
|
98
|
+
*
|
|
99
|
+
* The layer:
|
|
100
|
+
* 1. Creates an in-memory EffectDatabase from the config and optional initial data
|
|
101
|
+
* 2. For each collection, wires handlers to the appropriate database methods
|
|
102
|
+
* 3. Returns a Layer that provides the handler implementations
|
|
103
|
+
*
|
|
104
|
+
* @param config - The database configuration defining collections and schemas
|
|
105
|
+
* @param initialData - Optional initial data to seed the database
|
|
106
|
+
* @returns An Effect that produces the handler implementations for use with RpcGroup.toLayer
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* ```typescript
|
|
110
|
+
* import { Layer } from "effect"
|
|
111
|
+
* import { makeRpcHandlers, makeRpcGroup } from "@proseql/rpc"
|
|
112
|
+
*
|
|
113
|
+
* const config = {
|
|
114
|
+
* books: { schema: BookSchema, relationships: {} },
|
|
115
|
+
* } as const
|
|
116
|
+
*
|
|
117
|
+
* const rpcs = makeRpcGroup(config)
|
|
118
|
+
*
|
|
119
|
+
* // Create handler implementations
|
|
120
|
+
* const handlerEffect = makeRpcHandlers(config, {
|
|
121
|
+
* books: [{ id: "1", title: "Dune" }],
|
|
122
|
+
* })
|
|
123
|
+
*
|
|
124
|
+
* // Use with RpcGroup.toLayer for the complete RPC server layer
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
127
|
+
export const makeRpcHandlers = (config, initialData) => Effect.gen(function* () {
|
|
128
|
+
// Create the in-memory database with optional initial data
|
|
129
|
+
// biome-ignore lint/suspicious/noExplicitAny: DatasetFor type is complex and requires casting
|
|
130
|
+
const db = yield* createEffectDatabase(config, initialData);
|
|
131
|
+
// Build handlers for each collection
|
|
132
|
+
const handlers = {};
|
|
133
|
+
for (const collectionName of Object.keys(config)) {
|
|
134
|
+
handlers[collectionName] = createCollectionHandlers(collectionName, db);
|
|
135
|
+
}
|
|
136
|
+
return handlers;
|
|
137
|
+
});
|
|
138
|
+
/**
|
|
139
|
+
* Create an Effect Layer that provides RPC handlers for all collections.
|
|
140
|
+
*
|
|
141
|
+
* This is a convenience wrapper that combines makeRpcHandlers with makeRpcGroup
|
|
142
|
+
* to produce a complete Layer ready for use with Effect RPC server.
|
|
143
|
+
*
|
|
144
|
+
* @param config - The database configuration
|
|
145
|
+
* @param initialData - Optional initial data to seed the database
|
|
146
|
+
* @returns A Layer providing all RPC handlers
|
|
147
|
+
*
|
|
148
|
+
* @example
|
|
149
|
+
* ```typescript
|
|
150
|
+
* import { Effect } from "effect"
|
|
151
|
+
* import { makeRpcHandlersLayer } from "@proseql/rpc"
|
|
152
|
+
*
|
|
153
|
+
* const config = {
|
|
154
|
+
* books: { schema: BookSchema, relationships: {} },
|
|
155
|
+
* } as const
|
|
156
|
+
*
|
|
157
|
+
* // Create handler layer (can be composed with other layers)
|
|
158
|
+
* const handlersLayer = makeRpcHandlersLayer(config, {
|
|
159
|
+
* books: [{ id: "1", title: "Dune" }],
|
|
160
|
+
* })
|
|
161
|
+
* ```
|
|
162
|
+
*/
|
|
163
|
+
export const makeRpcHandlersLayer = (config, initialData) => {
|
|
164
|
+
const DatabaseContextTag = makeDatabaseContextTag();
|
|
165
|
+
return Layer.effect(DatabaseContextTag, Effect.gen(function* () {
|
|
166
|
+
// biome-ignore lint/suspicious/noExplicitAny: DatasetFor type is complex and requires casting
|
|
167
|
+
const db = yield* createEffectDatabase(config, initialData);
|
|
168
|
+
return { db };
|
|
169
|
+
}));
|
|
170
|
+
};
|
|
171
|
+
// ============================================================================
|
|
172
|
+
// Database-First Handler Factory
|
|
173
|
+
// ============================================================================
|
|
174
|
+
/**
|
|
175
|
+
* Create RPC handlers from an existing database instance.
|
|
176
|
+
*
|
|
177
|
+
* This function accepts any EffectDatabase or EffectDatabaseWithPersistence
|
|
178
|
+
* and wires handlers to delegate to the collection methods. When the database
|
|
179
|
+
* is a persistent database (created via createPersistentEffectDatabase),
|
|
180
|
+
* mutations automatically trigger persistence as normal.
|
|
181
|
+
*
|
|
182
|
+
* This is the recommended approach for production use cases where you need:
|
|
183
|
+
* - File-based persistence with debounced writes
|
|
184
|
+
* - Control over the database lifecycle
|
|
185
|
+
* - Multiple transports (RPC, REST) sharing the same database instance
|
|
186
|
+
*
|
|
187
|
+
* @param config - The database configuration (used to enumerate collections)
|
|
188
|
+
* @param db - An existing EffectDatabase or EffectDatabaseWithPersistence instance
|
|
189
|
+
* @returns The RPC handler implementations
|
|
190
|
+
*
|
|
191
|
+
* @example
|
|
192
|
+
* ```typescript
|
|
193
|
+
* import { Effect, Layer } from "effect"
|
|
194
|
+
* import { createPersistentEffectDatabase, NodeStorageLayer, makeSerializerLayer, jsonCodec } from "@proseql/node"
|
|
195
|
+
* import { makeRpcHandlersFromDatabase } from "@proseql/rpc"
|
|
196
|
+
*
|
|
197
|
+
* const config = {
|
|
198
|
+
* books: {
|
|
199
|
+
* schema: BookSchema,
|
|
200
|
+
* file: "./data/books.json", // persistence enabled
|
|
201
|
+
* relationships: {},
|
|
202
|
+
* },
|
|
203
|
+
* } as const
|
|
204
|
+
*
|
|
205
|
+
* const program = Effect.gen(function* () {
|
|
206
|
+
* // Create persistent database
|
|
207
|
+
* const db = yield* createPersistentEffectDatabase(config, { books: [] })
|
|
208
|
+
*
|
|
209
|
+
* // Wire RPC handlers to the persistent database
|
|
210
|
+
* const handlers = makeRpcHandlersFromDatabase(config, db)
|
|
211
|
+
*
|
|
212
|
+
* // Mutations through RPC now trigger persistence automatically
|
|
213
|
+
* yield* handlers.books.create({ data: { id: "1", title: "Dune" } })
|
|
214
|
+
*
|
|
215
|
+
* // Flush to ensure data is written
|
|
216
|
+
* await db.flush()
|
|
217
|
+
* })
|
|
218
|
+
*
|
|
219
|
+
* const PersistenceLayer = Layer.merge(
|
|
220
|
+
* NodeStorageLayer,
|
|
221
|
+
* makeSerializerLayer([jsonCodec()]),
|
|
222
|
+
* )
|
|
223
|
+
*
|
|
224
|
+
* await Effect.runPromise(
|
|
225
|
+
* program.pipe(Effect.provide(PersistenceLayer), Effect.scoped),
|
|
226
|
+
* )
|
|
227
|
+
* ```
|
|
228
|
+
*/
|
|
229
|
+
export const makeRpcHandlersFromDatabase = (config, db) => {
|
|
230
|
+
// Build handlers for each collection, delegating to the provided database
|
|
231
|
+
const handlers = {};
|
|
232
|
+
for (const collectionName of Object.keys(config)) {
|
|
233
|
+
handlers[collectionName] = createCollectionHandlers(collectionName, db);
|
|
234
|
+
}
|
|
235
|
+
return handlers;
|
|
236
|
+
};
|
|
237
|
+
/**
|
|
238
|
+
* Create an Effect Layer that provides RPC handlers from an existing database.
|
|
239
|
+
*
|
|
240
|
+
* Similar to makeRpcHandlersLayer, but accepts an existing database instance
|
|
241
|
+
* instead of creating one internally. This allows you to use a persistent
|
|
242
|
+
* database with the RPC layer.
|
|
243
|
+
*
|
|
244
|
+
* @param db - An existing EffectDatabase or EffectDatabaseWithPersistence instance
|
|
245
|
+
* @returns A Layer providing all RPC handlers via DatabaseContext
|
|
246
|
+
*
|
|
247
|
+
* @example
|
|
248
|
+
* ```typescript
|
|
249
|
+
* import { Effect, Layer } from "effect"
|
|
250
|
+
* import { createPersistentEffectDatabase } from "@proseql/node"
|
|
251
|
+
* import { makeRpcHandlersLayerFromDatabase, makeDatabaseContextTag } from "@proseql/rpc"
|
|
252
|
+
*
|
|
253
|
+
* const program = Effect.gen(function* () {
|
|
254
|
+
* const db = yield* createPersistentEffectDatabase(config, initialData)
|
|
255
|
+
* const handlerLayer = makeRpcHandlersLayerFromDatabase(db)
|
|
256
|
+
*
|
|
257
|
+
* // Use the layer with your RPC server
|
|
258
|
+
* // ...
|
|
259
|
+
* })
|
|
260
|
+
* ```
|
|
261
|
+
*/
|
|
262
|
+
export const makeRpcHandlersLayerFromDatabase = (db) => {
|
|
263
|
+
const DatabaseContextTag = makeDatabaseContextTag();
|
|
264
|
+
return Layer.succeed(DatabaseContextTag, { db });
|
|
265
|
+
};
|
|
266
|
+
//# sourceMappingURL=rpc-handlers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rpc-handlers.js","sourceRoot":"","sources":["../src/rpc-handlers.ts"],"names":[],"mappings":"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"}
|