@proseql/rest 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.
@@ -0,0 +1,273 @@
1
+ /**
2
+ * REST Relationship Route Generation for proseql databases.
3
+ *
4
+ * Generates framework-agnostic HTTP handlers for relationship sub-routes
5
+ * derived from collection relationship definitions in the DatabaseConfig.
6
+ *
7
+ * For `ref` relationships (e.g., books.author), generates:
8
+ * GET /books/:id/author — returns the related author entity
9
+ *
10
+ * For `inverse` relationships (e.g., authors.books), generates:
11
+ * GET /authors/:id/books — returns related book entities
12
+ *
13
+ * @module
14
+ */
15
+ import { Cause, Chunk, Effect, Option, Runtime, Stream } from "effect";
16
+ // ============================================================================
17
+ // Relationship Inspection
18
+ // ============================================================================
19
+ /**
20
+ * Extract all relationship definitions from a database configuration.
21
+ *
22
+ * Iterates over all collections in the config and extracts relationship
23
+ * metadata for each defined relationship.
24
+ *
25
+ * @param config - The database configuration
26
+ * @returns Array of relationship route info objects
27
+ */
28
+ export const extractRelationships = (config) => {
29
+ const relationships = [];
30
+ for (const [collectionName, collectionConfig] of Object.entries(config)) {
31
+ const collectionRelationships = collectionConfig.relationships;
32
+ if (!collectionRelationships)
33
+ continue;
34
+ for (const [relationshipName, relationship] of Object.entries(collectionRelationships)) {
35
+ relationships.push({
36
+ sourceCollection: collectionName,
37
+ relationshipName,
38
+ relationship: relationship,
39
+ });
40
+ }
41
+ }
42
+ return relationships;
43
+ };
44
+ // ============================================================================
45
+ // Handler Creation
46
+ // ============================================================================
47
+ /**
48
+ * Create a handler for a `ref` relationship route.
49
+ *
50
+ * For a `ref` relationship like `books.author`, this generates a handler for
51
+ * `GET /books/:id/author` that:
52
+ * 1. Finds the source entity (book) by ID
53
+ * 2. Follows the foreign key to the target collection (authors)
54
+ * 3. Returns the related entity
55
+ *
56
+ * @param sourceCollection - The collection object for the source entity
57
+ * @param targetCollection - The collection object for the target entity
58
+ * @param foreignKey - The foreign key field name on the source entity
59
+ * @returns A REST handler function
60
+ */
61
+ const createRefRelationshipHandler = (
62
+ // biome-ignore lint/suspicious/noExplicitAny: Collection type is dynamic
63
+ sourceCollection,
64
+ // biome-ignore lint/suspicious/noExplicitAny: Collection type is dynamic
65
+ targetCollection, foreignKey) => {
66
+ return async (req) => {
67
+ const { id } = req.params;
68
+ try {
69
+ // Find the source entity
70
+ const findSourceEffect = sourceCollection.findById(id);
71
+ const sourceEntity = await Effect.runPromise(findSourceEffect);
72
+ // Get the foreign key value
73
+ const targetId = sourceEntity[foreignKey];
74
+ if (targetId === null || targetId === undefined) {
75
+ // No related entity (foreign key is null/undefined)
76
+ return { status: 200, body: null };
77
+ }
78
+ // Find the related entity
79
+ const findTargetEffect = targetCollection.findById(targetId);
80
+ const targetEntity = await Effect.runPromise(findTargetEffect);
81
+ return { status: 200, body: targetEntity };
82
+ }
83
+ catch (error) {
84
+ if (isTaggedError(error, "NotFoundError")) {
85
+ return {
86
+ status: 404,
87
+ body: { error: "Not found", _tag: "NotFoundError" },
88
+ };
89
+ }
90
+ return { status: 500, body: { error: "Internal server error" } };
91
+ }
92
+ };
93
+ };
94
+ /**
95
+ * Create a handler for an `inverse` relationship route.
96
+ *
97
+ * For an `inverse` relationship like `authors.books`, this generates a handler
98
+ * for `GET /authors/:id/books` that:
99
+ * 1. Verifies the source entity (author) exists
100
+ * 2. Queries the target collection (books) filtered by the foreign key
101
+ * 3. Returns the related entities array
102
+ *
103
+ * @param sourceCollection - The collection object for the source entity
104
+ * @param targetCollection - The collection object for the target entity
105
+ * @param foreignKey - The foreign key field name on the target entity
106
+ * @returns A REST handler function
107
+ */
108
+ const createInverseRelationshipHandler = (
109
+ // biome-ignore lint/suspicious/noExplicitAny: Collection type is dynamic
110
+ sourceCollection,
111
+ // biome-ignore lint/suspicious/noExplicitAny: Collection type is dynamic
112
+ targetCollection, foreignKey) => {
113
+ return async (req) => {
114
+ const { id } = req.params;
115
+ try {
116
+ // Verify the source entity exists
117
+ const findSourceEffect = sourceCollection.findById(id);
118
+ await Effect.runPromise(findSourceEffect);
119
+ // Query the target collection for related entities
120
+ const queryConfig = {
121
+ where: { [foreignKey]: id },
122
+ };
123
+ const stream = targetCollection.query(queryConfig);
124
+ const results = await Effect.runPromise(Stream.runCollect(stream).pipe(Effect.map(Chunk.toReadonlyArray)));
125
+ return { status: 200, body: results };
126
+ }
127
+ catch (error) {
128
+ if (isTaggedError(error, "NotFoundError")) {
129
+ return {
130
+ status: 404,
131
+ body: { error: "Not found", _tag: "NotFoundError" },
132
+ };
133
+ }
134
+ return { status: 500, body: { error: "Internal server error" } };
135
+ }
136
+ };
137
+ };
138
+ // ============================================================================
139
+ // Route Generation
140
+ // ============================================================================
141
+ /**
142
+ * Create REST handlers for all relationship routes in a database.
143
+ *
144
+ * Generates sub-routes for navigating relationships:
145
+ * - `ref` relationships: `GET /:collection/:id/:relationshipName`
146
+ * - `inverse` relationships: `GET /:collection/:id/:relationshipName`
147
+ *
148
+ * @param config - The database configuration defining collections and relationships
149
+ * @param db - An EffectDatabase or EffectDatabaseWithPersistence instance
150
+ * @returns Array of route descriptors for relationship routes
151
+ *
152
+ * @example
153
+ * ```typescript
154
+ * const config = {
155
+ * books: {
156
+ * schema: BookSchema,
157
+ * relationships: {
158
+ * author: { type: "ref", target: "authors", foreignKey: "authorId" },
159
+ * },
160
+ * },
161
+ * authors: {
162
+ * schema: AuthorSchema,
163
+ * relationships: {
164
+ * books: { type: "inverse", target: "books", foreignKey: "authorId" },
165
+ * },
166
+ * },
167
+ * } as const
168
+ *
169
+ * const routes = createRelationshipRoutes(config, db)
170
+ * // Generates:
171
+ * // GET /books/:id/author — returns the author of a book
172
+ * // GET /authors/:id/books — returns all books by an author
173
+ * ```
174
+ */
175
+ export const createRelationshipRoutes = (config, db) => {
176
+ const routes = [];
177
+ const relationships = extractRelationships(config);
178
+ for (const { sourceCollection, relationshipName, relationship, } of relationships) {
179
+ // Get the source and target collections from the database
180
+ // biome-ignore lint/suspicious/noExplicitAny: Collection type is dynamic
181
+ const source = db[sourceCollection];
182
+ // biome-ignore lint/suspicious/noExplicitAny: Collection type is dynamic
183
+ const target = db[relationship.target];
184
+ if (!source || !target) {
185
+ // Skip if collections don't exist (shouldn't happen with valid config)
186
+ continue;
187
+ }
188
+ const path = `/${sourceCollection}/:id/${relationshipName}`;
189
+ if (relationship.type === "ref") {
190
+ // For ref relationships, the foreign key is on the source entity
191
+ const foreignKey = relationship.foreignKey || `${relationshipName}Id`;
192
+ routes.push({
193
+ method: "GET",
194
+ path,
195
+ handler: createRefRelationshipHandler(source, target, foreignKey),
196
+ });
197
+ }
198
+ else if (relationship.type === "inverse") {
199
+ // For inverse relationships, the foreign key is on the target entity
200
+ // The foreignKey in the config specifies the field on the target that points back
201
+ const foreignKey = relationship.foreignKey || deriveForeignKey(sourceCollection);
202
+ routes.push({
203
+ method: "GET",
204
+ path,
205
+ handler: createInverseRelationshipHandler(source, target, foreignKey),
206
+ });
207
+ }
208
+ }
209
+ return routes;
210
+ };
211
+ // ============================================================================
212
+ // Utility Functions
213
+ // ============================================================================
214
+ /**
215
+ * Derive a default foreign key name from a collection name.
216
+ *
217
+ * Converts plural collection names to singular + "Id":
218
+ * - "users" → "userId"
219
+ * - "companies" → "companyId"
220
+ * - "categories" → "categoryId"
221
+ *
222
+ * @param collectionName - The collection name (typically plural)
223
+ * @returns The derived foreign key field name
224
+ */
225
+ const deriveForeignKey = (collectionName) => {
226
+ // Handle "-ies" plural (companies → companyId)
227
+ if (collectionName.endsWith("ies")) {
228
+ return `${collectionName.slice(0, -3)}yId`;
229
+ }
230
+ // Handle regular "-s" plural (users → userId)
231
+ if (collectionName.endsWith("s")) {
232
+ return `${collectionName.slice(0, -1)}Id`;
233
+ }
234
+ // Fallback: just append "Id"
235
+ return `${collectionName}Id`;
236
+ };
237
+ /**
238
+ * Extract a tagged error from an unknown error value.
239
+ *
240
+ * Effect.runPromise throws a FiberFailure when the Effect fails.
241
+ * This function extracts the underlying tagged error from the FiberFailure
242
+ * or returns the error directly if it's already a tagged error.
243
+ */
244
+ const extractTaggedError = (error) => {
245
+ // Check if it's a FiberFailure (from Effect.runPromise)
246
+ if (Runtime.isFiberFailure(error)) {
247
+ // Get the cause from the FiberFailure using the well-known symbol
248
+ const causeSymbol = Symbol.for("effect/Runtime/FiberFailure/Cause");
249
+ const cause = error[causeSymbol];
250
+ // Extract the failure from the cause
251
+ const failure = Cause.failureOption(cause);
252
+ if (Option.isSome(failure)) {
253
+ const value = failure.value;
254
+ if (value !== null && typeof value === "object" && "_tag" in value) {
255
+ return value;
256
+ }
257
+ }
258
+ }
259
+ // Check if it's already a tagged error
260
+ if (error !== null && typeof error === "object" && "_tag" in error) {
261
+ return error;
262
+ }
263
+ return null;
264
+ };
265
+ /**
266
+ * Type guard to check if an error has a specific _tag.
267
+ * Handles both direct tagged errors and FiberFailure wrappers.
268
+ */
269
+ const isTaggedError = (error, tag) => {
270
+ const taggedError = extractTaggedError(error);
271
+ return taggedError !== null && taggedError._tag === tag;
272
+ };
273
+ //# sourceMappingURL=relationship-routes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relationship-routes.js","sourceRoot":"","sources":["../src/relationship-routes.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@proseql/rest",
3
+ "version": "0.1.0",
4
+ "description": "REST API handlers for ProseQL databases, framework-agnostic",
5
+ "type": "module",
6
+ "scripts": {
7
+ "build": "bunx tsc --build",
8
+ "clean": "rm -rf dist *.tsbuildinfo",
9
+ "prepublishOnly": "bun run build && bun test"
10
+ },
11
+ "main": "dist/index.js",
12
+ "types": "dist/index.d.ts",
13
+ "exports": {
14
+ ".": {
15
+ "types": "./dist/index.d.ts",
16
+ "import": "./dist/index.js"
17
+ }
18
+ },
19
+ "files": [
20
+ "dist",
21
+ "LICENSE",
22
+ "README.md"
23
+ ],
24
+ "license": "MIT",
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "https://github.com/simonwjackson/proseql.git",
28
+ "directory": "packages/rest"
29
+ },
30
+ "keywords": [
31
+ "database",
32
+ "typescript",
33
+ "effect",
34
+ "rest",
35
+ "api",
36
+ "plain-text",
37
+ "type-safe"
38
+ ],
39
+ "engines": {
40
+ "node": ">=18"
41
+ },
42
+ "sideEffects": false,
43
+ "dependencies": {
44
+ "@proseql/core": "workspace:*"
45
+ },
46
+ "peerDependencies": {
47
+ "effect": "^3.15.0"
48
+ }
49
+ }