@multitenantkit/adapter-transport-supabase-edge 0.2.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/README.md +180 -0
- package/dist/buildEdgeFunction.d.ts +29 -0
- package/dist/buildEdgeFunction.d.ts.map +1 -0
- package/dist/buildEdgeFunction.js +150 -0
- package/dist/buildEdgeFunction.js.map +1 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +34 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware/auth.d.ts +8 -0
- package/dist/middleware/auth.d.ts.map +1 -0
- package/dist/middleware/auth.js +27 -0
- package/dist/middleware/auth.js.map +1 -0
- package/dist/middleware/cors.d.ts +10 -0
- package/dist/middleware/cors.d.ts.map +1 -0
- package/dist/middleware/cors.js +30 -0
- package/dist/middleware/cors.js.map +1 -0
- package/dist/middleware/index.d.ts +5 -0
- package/dist/middleware/index.d.ts.map +1 -0
- package/dist/middleware/index.js +6 -0
- package/dist/middleware/index.js.map +1 -0
- package/dist/middleware/requestId.d.ts +6 -0
- package/dist/middleware/requestId.d.ts.map +1 -0
- package/dist/middleware/requestId.js +14 -0
- package/dist/middleware/requestId.js.map +1 -0
- package/dist/middleware/validation.d.ts +31 -0
- package/dist/middleware/validation.d.ts.map +1 -0
- package/dist/middleware/validation.js +104 -0
- package/dist/middleware/validation.js.map +1 -0
- package/dist/router.d.ts +53 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +96 -0
- package/dist/router.js.map +1 -0
- package/dist/types.d.ts +90 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/package.json +63 -0
package/README.md
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# @multitenantkit/adapter-transport-supabase-edge
|
|
2
|
+
|
|
3
|
+
Supabase Edge Functions transport adapter for MultiTenantKit HTTP API.
|
|
4
|
+
|
|
5
|
+
This adapter enables running MultiTenantKit API endpoints as a single Supabase Edge Function with internal routing (the "fat function" pattern).
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Single Edge Function**: All endpoints run in one function, minimizing cold starts
|
|
10
|
+
- **Native Deno**: Uses `Deno.serve()` directly without additional frameworks
|
|
11
|
+
- **Full Compatibility**: Works with all `@multitenantkit/api-handlers` HandlerPackages
|
|
12
|
+
- **Built-in CORS**: Configurable CORS handling for browser clients
|
|
13
|
+
- **Authentication**: Integrates with `@multitenantkit/adapter-auth-supabase` for JWT validation
|
|
14
|
+
- **Request Validation**: Zod-based validation for body, params, and query
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install @multitenantkit/adapter-transport-supabase-edge
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
### Basic Setup
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
// supabase/functions/multitenantkit/index.ts
|
|
28
|
+
|
|
29
|
+
import { buildEdgeFunction } from '@multitenantkit/adapter-transport-supabase-edge';
|
|
30
|
+
import { buildHandlers } from '@multitenantkit/api-handlers';
|
|
31
|
+
import { createUseCases } from '@multitenantkit/composition';
|
|
32
|
+
import { SupabaseAuthService } from '@multitenantkit/adapter-auth-supabase';
|
|
33
|
+
|
|
34
|
+
// Initialize your adapters and use cases
|
|
35
|
+
const useCases = createUseCases(adapters);
|
|
36
|
+
const handlers = buildHandlers(useCases);
|
|
37
|
+
|
|
38
|
+
// Create auth service
|
|
39
|
+
const authService = new SupabaseAuthService({
|
|
40
|
+
supabaseUrl: Deno.env.get('SUPABASE_URL')!,
|
|
41
|
+
supabaseServiceKey: Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// Build the Edge Function handler
|
|
45
|
+
const handler = buildEdgeFunction(handlers, authService, {
|
|
46
|
+
basePath: '/multitenantkit',
|
|
47
|
+
cors: {
|
|
48
|
+
allowOrigin: '*',
|
|
49
|
+
allowHeaders: ['authorization', 'x-client-info', 'apikey', 'content-type'],
|
|
50
|
+
allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS']
|
|
51
|
+
},
|
|
52
|
+
debug: true // Enable for development
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// Start the server
|
|
56
|
+
Deno.serve(handler);
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Configuration Options
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
interface EdgeFunctionOptions {
|
|
63
|
+
/** Base path prefix for all routes (usually the function name) */
|
|
64
|
+
basePath: string;
|
|
65
|
+
|
|
66
|
+
/** CORS configuration */
|
|
67
|
+
cors?: {
|
|
68
|
+
allowOrigin: string | string[];
|
|
69
|
+
allowHeaders: string[];
|
|
70
|
+
allowMethods: string[];
|
|
71
|
+
maxAge?: number;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
/** Enable debug logging */
|
|
75
|
+
debug?: boolean;
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Project Structure
|
|
80
|
+
|
|
81
|
+
For a Supabase project using this adapter:
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
supabase/
|
|
85
|
+
├── functions/
|
|
86
|
+
│ ├── import_map.json
|
|
87
|
+
│ ├── _shared/
|
|
88
|
+
│ │ └── multitenantkit.ts # Shared configuration
|
|
89
|
+
│ └── multitenantkit/
|
|
90
|
+
│ └── index.ts # Entry point
|
|
91
|
+
├── migrations/
|
|
92
|
+
└── config.toml
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Supabase Config (`config.toml`)
|
|
96
|
+
|
|
97
|
+
```toml
|
|
98
|
+
[functions.multitenantkit]
|
|
99
|
+
verify_jwt = false # We handle JWT verification ourselves
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Available Endpoints
|
|
103
|
+
|
|
104
|
+
Once deployed, the following endpoints are available (prefixed with your function path):
|
|
105
|
+
|
|
106
|
+
### Users
|
|
107
|
+
- `POST /users` - Create user
|
|
108
|
+
- `GET /users/me` - Get current user
|
|
109
|
+
- `PATCH /users/me` - Update current user
|
|
110
|
+
- `DELETE /users/me` - Delete current user
|
|
111
|
+
- `GET /users/me/organizations` - List user's organizations
|
|
112
|
+
|
|
113
|
+
### Organizations
|
|
114
|
+
- `POST /organizations` - Create organization
|
|
115
|
+
- `GET /organizations/:id` - Get organization
|
|
116
|
+
- `PATCH /organizations/:id` - Update organization
|
|
117
|
+
- `DELETE /organizations/:id` - Delete organization
|
|
118
|
+
|
|
119
|
+
### Memberships
|
|
120
|
+
- `POST /organizations/:organizationId/memberships` - Add member
|
|
121
|
+
- `GET /organizations/:organizationId/memberships` - List members
|
|
122
|
+
- `GET /organizations/:organizationId/memberships/:memberId` - Get member
|
|
123
|
+
- `PATCH /organizations/:organizationId/memberships/:memberId` - Update member
|
|
124
|
+
- `DELETE /organizations/:organizationId/memberships/:memberId` - Remove member
|
|
125
|
+
|
|
126
|
+
### Health
|
|
127
|
+
- `GET /health` - Health check endpoint (built-in)
|
|
128
|
+
|
|
129
|
+
## API Reference
|
|
130
|
+
|
|
131
|
+
### `buildEdgeFunction(handlers, authService, options)`
|
|
132
|
+
|
|
133
|
+
Creates a Deno.serve() compatible handler function.
|
|
134
|
+
|
|
135
|
+
**Parameters:**
|
|
136
|
+
- `handlers` - Array of HandlerPackages from `@multitenantkit/api-handlers`
|
|
137
|
+
- `authService` - AuthService implementation (e.g., SupabaseAuthService)
|
|
138
|
+
- `options` - Configuration options
|
|
139
|
+
|
|
140
|
+
**Returns:** `(request: Request) => Promise<Response>`
|
|
141
|
+
|
|
142
|
+
### `EdgeRouter`
|
|
143
|
+
|
|
144
|
+
Low-level router class for advanced use cases.
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
import { EdgeRouter } from '@multitenantkit/adapter-transport-supabase-edge';
|
|
148
|
+
|
|
149
|
+
const router = new EdgeRouter('/api', handlers);
|
|
150
|
+
const match = router.match('GET', '/api/users/me');
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Middleware Utilities
|
|
154
|
+
|
|
155
|
+
For custom implementations:
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
import {
|
|
159
|
+
authenticateRequest,
|
|
160
|
+
buildCorsHeaders,
|
|
161
|
+
handleCorsPreflightRequest,
|
|
162
|
+
getRequestId,
|
|
163
|
+
validateRequest
|
|
164
|
+
} from '@multitenantkit/adapter-transport-supabase-edge';
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Differences from Express Adapter
|
|
168
|
+
|
|
169
|
+
| Aspect | Express | Supabase Edge |
|
|
170
|
+
|--------|---------|---------------|
|
|
171
|
+
| Runtime | Node.js | Deno |
|
|
172
|
+
| Deploy | Manual | Automatic (Supabase) |
|
|
173
|
+
| Scaling | Manual | Automatic (edge) |
|
|
174
|
+
| CORS | `cors` middleware | Manual headers |
|
|
175
|
+
| Body parsing | `express.json()` | Native `request.json()` |
|
|
176
|
+
| Path params | Express router | Regex matching |
|
|
177
|
+
|
|
178
|
+
## License
|
|
179
|
+
|
|
180
|
+
MIT
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { AuthService } from '@multitenantkit/api-contracts/shared/ports';
|
|
2
|
+
import type { HandlerPackage } from '@multitenantkit/api-handlers';
|
|
3
|
+
import type { EdgeFunctionOptions } from './types';
|
|
4
|
+
/**
|
|
5
|
+
* Build the Edge Function handler from HandlerPackages
|
|
6
|
+
*
|
|
7
|
+
* This function creates a Deno.serve() compatible handler that:
|
|
8
|
+
* - Routes requests to appropriate handlers
|
|
9
|
+
* - Handles CORS
|
|
10
|
+
* - Authenticates requests
|
|
11
|
+
* - Validates input
|
|
12
|
+
* - Returns consistent responses
|
|
13
|
+
*
|
|
14
|
+
* @param handlers - Array of HandlerPackages from api-handlers
|
|
15
|
+
* @param authService - AuthService implementation (e.g., SupabaseAuthService)
|
|
16
|
+
* @param options - Configuration options
|
|
17
|
+
* @returns Handler function for Deno.serve()
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* const handler = buildEdgeFunction(handlers, authService, {
|
|
22
|
+
* basePath: '/multitenantkit'
|
|
23
|
+
* });
|
|
24
|
+
*
|
|
25
|
+
* Deno.serve(handler);
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export declare function buildEdgeFunction(handlers: HandlerPackage<unknown, unknown>[], authService: AuthService<unknown>, options: EdgeFunctionOptions): (request: Request) => Promise<Response>;
|
|
29
|
+
//# sourceMappingURL=buildEdgeFunction.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buildEdgeFunction.d.ts","sourceRoot":"","sources":["../src/buildEdgeFunction.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4CAA4C,CAAC;AAC9E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAQnE,OAAO,KAAK,EAAe,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAwDhE;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,iBAAiB,CAC7B,QAAQ,EAAE,cAAc,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,EAC5C,WAAW,EAAE,WAAW,CAAC,OAAO,CAAC,EACjC,OAAO,EAAE,mBAAmB,GAC7B,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CA0HzC"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
// Main builder for Supabase Edge Functions handler
|
|
2
|
+
import { ANONYMOUS_PRINCIPAL } from '@multitenantkit/domain-contracts/shared/auth';
|
|
3
|
+
import { authenticateRequest } from './middleware/auth';
|
|
4
|
+
import { buildCorsHeaders, handleCorsPreflightRequest } from './middleware/cors';
|
|
5
|
+
import { getRequestId } from './middleware/requestId';
|
|
6
|
+
import { validateRequest } from './middleware/validation';
|
|
7
|
+
import { EdgeRouter } from './router';
|
|
8
|
+
/**
|
|
9
|
+
* Default CORS configuration
|
|
10
|
+
*/
|
|
11
|
+
const DEFAULT_CORS = {
|
|
12
|
+
allowOrigin: '*',
|
|
13
|
+
allowHeaders: ['authorization', 'x-client-info', 'apikey', 'content-type', 'x-request-id'],
|
|
14
|
+
allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS']
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Default options for Edge Function
|
|
18
|
+
*/
|
|
19
|
+
const DEFAULT_OPTIONS = {
|
|
20
|
+
cors: DEFAULT_CORS,
|
|
21
|
+
debug: false
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Build error response with consistent format
|
|
25
|
+
*/
|
|
26
|
+
function buildErrorResponse(status, code, message, requestId, corsHeaders, details) {
|
|
27
|
+
const errorBody = {
|
|
28
|
+
code,
|
|
29
|
+
message,
|
|
30
|
+
requestId,
|
|
31
|
+
timestamp: new Date().toISOString()
|
|
32
|
+
};
|
|
33
|
+
if (details) {
|
|
34
|
+
errorBody.details = details;
|
|
35
|
+
}
|
|
36
|
+
return new Response(JSON.stringify({
|
|
37
|
+
error: errorBody
|
|
38
|
+
}), {
|
|
39
|
+
status,
|
|
40
|
+
headers: {
|
|
41
|
+
'Content-Type': 'application/json',
|
|
42
|
+
'X-Request-ID': requestId,
|
|
43
|
+
...corsHeaders
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Build the Edge Function handler from HandlerPackages
|
|
49
|
+
*
|
|
50
|
+
* This function creates a Deno.serve() compatible handler that:
|
|
51
|
+
* - Routes requests to appropriate handlers
|
|
52
|
+
* - Handles CORS
|
|
53
|
+
* - Authenticates requests
|
|
54
|
+
* - Validates input
|
|
55
|
+
* - Returns consistent responses
|
|
56
|
+
*
|
|
57
|
+
* @param handlers - Array of HandlerPackages from api-handlers
|
|
58
|
+
* @param authService - AuthService implementation (e.g., SupabaseAuthService)
|
|
59
|
+
* @param options - Configuration options
|
|
60
|
+
* @returns Handler function for Deno.serve()
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```typescript
|
|
64
|
+
* const handler = buildEdgeFunction(handlers, authService, {
|
|
65
|
+
* basePath: '/multitenantkit'
|
|
66
|
+
* });
|
|
67
|
+
*
|
|
68
|
+
* Deno.serve(handler);
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
export function buildEdgeFunction(handlers, authService, options) {
|
|
72
|
+
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
73
|
+
const corsConfig = opts.cors ?? DEFAULT_CORS;
|
|
74
|
+
const corsHeaders = buildCorsHeaders(corsConfig);
|
|
75
|
+
const router = new EdgeRouter(opts.basePath, handlers);
|
|
76
|
+
return async (request) => {
|
|
77
|
+
const requestId = getRequestId(request);
|
|
78
|
+
try {
|
|
79
|
+
// Handle CORS preflight
|
|
80
|
+
if (request.method === 'OPTIONS') {
|
|
81
|
+
return handleCorsPreflightRequest(corsHeaders);
|
|
82
|
+
}
|
|
83
|
+
const url = new URL(request.url);
|
|
84
|
+
const pathname = url.pathname;
|
|
85
|
+
const method = request.method;
|
|
86
|
+
if (opts.debug) {
|
|
87
|
+
console.log(`📍 ${method} ${pathname} [${requestId}]`);
|
|
88
|
+
}
|
|
89
|
+
// Health check endpoint
|
|
90
|
+
if (pathname === `${opts.basePath}/health` && method === 'GET') {
|
|
91
|
+
return new Response(JSON.stringify({
|
|
92
|
+
status: 'healthy',
|
|
93
|
+
timestamp: new Date().toISOString(),
|
|
94
|
+
requestId
|
|
95
|
+
}), {
|
|
96
|
+
status: 200,
|
|
97
|
+
headers: {
|
|
98
|
+
'Content-Type': 'application/json',
|
|
99
|
+
'X-Request-ID': requestId,
|
|
100
|
+
...corsHeaders
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
// Route matching
|
|
105
|
+
const routeMatch = router.match(method, pathname);
|
|
106
|
+
if (!routeMatch) {
|
|
107
|
+
return buildErrorResponse(404, 'NOT_FOUND', `Route ${method} ${pathname} not found`, requestId, corsHeaders);
|
|
108
|
+
}
|
|
109
|
+
const { handler: pkg, params } = routeMatch;
|
|
110
|
+
const handlerPackage = pkg;
|
|
111
|
+
const { route, schema, handler } = handlerPackage;
|
|
112
|
+
// Authentication
|
|
113
|
+
let principal = ANONYMOUS_PRINCIPAL;
|
|
114
|
+
if (route.auth === 'required' || route.auth === 'optional') {
|
|
115
|
+
principal = await authenticateRequest(request, authService);
|
|
116
|
+
// If auth is required but we got anonymous, return 401
|
|
117
|
+
if (route.auth === 'required' && principal === ANONYMOUS_PRINCIPAL) {
|
|
118
|
+
return buildErrorResponse(401, 'UNAUTHORIZED', 'Authentication required', requestId, corsHeaders);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// Validation
|
|
122
|
+
const validationResult = await validateRequest(request, url, params, schema);
|
|
123
|
+
if (!validationResult.success) {
|
|
124
|
+
return buildErrorResponse(400, 'VALIDATION_ERROR', 'Request validation failed', requestId, corsHeaders, { issues: validationResult.errors });
|
|
125
|
+
}
|
|
126
|
+
// Execute handler
|
|
127
|
+
const result = await handler({
|
|
128
|
+
input: validationResult.data,
|
|
129
|
+
principal,
|
|
130
|
+
requestId
|
|
131
|
+
});
|
|
132
|
+
// Build response
|
|
133
|
+
const responseHeaders = {
|
|
134
|
+
'Content-Type': 'application/json',
|
|
135
|
+
'X-Request-ID': requestId,
|
|
136
|
+
...corsHeaders,
|
|
137
|
+
...(result.headers || {})
|
|
138
|
+
};
|
|
139
|
+
return new Response(JSON.stringify(result.body), {
|
|
140
|
+
status: result.status,
|
|
141
|
+
headers: responseHeaders
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
console.error('Edge Function error:', error);
|
|
146
|
+
return buildErrorResponse(500, 'INTERNAL_SERVER_ERROR', 'An unexpected error occurred', requestId, corsHeaders);
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=buildEdgeFunction.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buildEdgeFunction.js","sourceRoot":"","sources":["../src/buildEdgeFunction.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @multitenantkit/adapter-transport-supabase-edge
|
|
3
|
+
*
|
|
4
|
+
* Supabase Edge Functions Transport Adapter for MultiTenantKit
|
|
5
|
+
*
|
|
6
|
+
* This adapter enables running MultiTenantKit API endpoints as a single
|
|
7
|
+
* Supabase Edge Function with internal routing (the "fat function" pattern).
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import { buildEdgeFunction } from '@multitenantkit/adapter-transport-supabase-edge';
|
|
12
|
+
* import { buildHandlers } from '@multitenantkit/api-handlers';
|
|
13
|
+
* import { SupabaseAuthService } from '@multitenantkit/adapter-auth-supabase';
|
|
14
|
+
*
|
|
15
|
+
* const handlers = buildHandlers(useCases);
|
|
16
|
+
* const authService = new SupabaseAuthService({ supabaseUrl, supabaseServiceKey });
|
|
17
|
+
*
|
|
18
|
+
* const handler = buildEdgeFunction(handlers, authService, {
|
|
19
|
+
* basePath: '/multitenantkit'
|
|
20
|
+
* });
|
|
21
|
+
*
|
|
22
|
+
* Deno.serve(handler);
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export { buildEdgeFunction } from './buildEdgeFunction';
|
|
26
|
+
export { authenticateRequest } from './middleware/auth';
|
|
27
|
+
export { buildCorsHeaders, handleCorsPreflightRequest } from './middleware/cors';
|
|
28
|
+
export { getRequestId } from './middleware/requestId';
|
|
29
|
+
export { validateRequest } from './middleware/validation';
|
|
30
|
+
export { EdgeRouter } from './router';
|
|
31
|
+
export type { CorsOptions, EdgeContext, EdgeFunctionOptions, RouteMatch } from './types';
|
|
32
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAGH,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,0BAA0B,EAAE,MAAM,mBAAmB,CAAC;AACjF,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAE1D,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAGtC,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @multitenantkit/adapter-transport-supabase-edge
|
|
3
|
+
*
|
|
4
|
+
* Supabase Edge Functions Transport Adapter for MultiTenantKit
|
|
5
|
+
*
|
|
6
|
+
* This adapter enables running MultiTenantKit API endpoints as a single
|
|
7
|
+
* Supabase Edge Function with internal routing (the "fat function" pattern).
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import { buildEdgeFunction } from '@multitenantkit/adapter-transport-supabase-edge';
|
|
12
|
+
* import { buildHandlers } from '@multitenantkit/api-handlers';
|
|
13
|
+
* import { SupabaseAuthService } from '@multitenantkit/adapter-auth-supabase';
|
|
14
|
+
*
|
|
15
|
+
* const handlers = buildHandlers(useCases);
|
|
16
|
+
* const authService = new SupabaseAuthService({ supabaseUrl, supabaseServiceKey });
|
|
17
|
+
*
|
|
18
|
+
* const handler = buildEdgeFunction(handlers, authService, {
|
|
19
|
+
* basePath: '/multitenantkit'
|
|
20
|
+
* });
|
|
21
|
+
*
|
|
22
|
+
* Deno.serve(handler);
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
// Main builder
|
|
26
|
+
export { buildEdgeFunction } from './buildEdgeFunction';
|
|
27
|
+
// Middleware utilities (for custom implementations)
|
|
28
|
+
export { authenticateRequest } from './middleware/auth';
|
|
29
|
+
export { buildCorsHeaders, handleCorsPreflightRequest } from './middleware/cors';
|
|
30
|
+
export { getRequestId } from './middleware/requestId';
|
|
31
|
+
export { validateRequest } from './middleware/validation';
|
|
32
|
+
// Router (for advanced use cases)
|
|
33
|
+
export { EdgeRouter } from './router';
|
|
34
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,eAAe;AACf,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,oDAAoD;AACpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,0BAA0B,EAAE,MAAM,mBAAmB,CAAC;AACjF,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,kCAAkC;AAClC,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { AuthService } from '@multitenantkit/api-contracts/shared/ports';
|
|
2
|
+
import { type Principal } from '@multitenantkit/domain-contracts/shared/auth';
|
|
3
|
+
/**
|
|
4
|
+
* Authenticate a request using the provided AuthService
|
|
5
|
+
* Adapts Deno Request headers to the format expected by AuthService
|
|
6
|
+
*/
|
|
7
|
+
export declare function authenticateRequest(request: Request, authService: AuthService<unknown>): Promise<Principal>;
|
|
8
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/middleware/auth.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4CAA4C,CAAC;AAC9E,OAAO,EAAuB,KAAK,SAAS,EAAE,MAAM,8CAA8C,CAAC;AAEnG;;;GAGG;AACH,wBAAsB,mBAAmB,CACrC,OAAO,EAAE,OAAO,EAChB,WAAW,EAAE,WAAW,CAAC,OAAO,CAAC,GAClC,OAAO,CAAC,SAAS,CAAC,CAqBpB"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// Authentication middleware for Supabase Edge Functions
|
|
2
|
+
import { ANONYMOUS_PRINCIPAL } from '@multitenantkit/domain-contracts/shared/auth';
|
|
3
|
+
/**
|
|
4
|
+
* Authenticate a request using the provided AuthService
|
|
5
|
+
* Adapts Deno Request headers to the format expected by AuthService
|
|
6
|
+
*/
|
|
7
|
+
export async function authenticateRequest(request, authService) {
|
|
8
|
+
try {
|
|
9
|
+
// Convert Deno Headers to Record<string, string>
|
|
10
|
+
const headers = {};
|
|
11
|
+
request.headers.forEach((value, key) => {
|
|
12
|
+
headers[key.toLowerCase()] = value;
|
|
13
|
+
});
|
|
14
|
+
// Build auth input (compatible with SupabaseAuthService)
|
|
15
|
+
const authInput = {
|
|
16
|
+
headers,
|
|
17
|
+
cookies: {} // Edge Functions typically use header-based auth
|
|
18
|
+
};
|
|
19
|
+
const principal = await authService.authenticate(authInput);
|
|
20
|
+
return principal || ANONYMOUS_PRINCIPAL;
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
console.error('Authentication error:', error);
|
|
24
|
+
return ANONYMOUS_PRINCIPAL;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/middleware/auth.ts"],"names":[],"mappings":"AAAA,wDAAwD;AAGxD,OAAO,EAAE,mBAAmB,EAAkB,MAAM,8CAA8C,CAAC;AAEnG;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACrC,OAAgB,EAChB,WAAiC;IAEjC,IAAI,CAAC;QACD,iDAAiD;QACjD,MAAM,OAAO,GAA2B,EAAE,CAAC;QAC3C,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;YACnC,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,GAAG,KAAK,CAAC;QACvC,CAAC,CAAC,CAAC;QAEH,yDAAyD;QACzD,MAAM,SAAS,GAAG;YACd,OAAO;YACP,OAAO,EAAE,EAAE,CAAC,iDAAiD;SAChE,CAAC;QAEF,MAAM,SAAS,GAAG,MAAM,WAAW,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC;QAE5D,OAAO,SAAS,IAAI,mBAAmB,CAAC;IAC5C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,uBAAuB,EAAE,KAAK,CAAC,CAAC;QAC9C,OAAO,mBAAmB,CAAC;IAC/B,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { CorsOptions } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Build CORS headers object
|
|
4
|
+
*/
|
|
5
|
+
export declare function buildCorsHeaders(options?: CorsOptions): Record<string, string>;
|
|
6
|
+
/**
|
|
7
|
+
* Handle CORS preflight (OPTIONS) request
|
|
8
|
+
*/
|
|
9
|
+
export declare function handleCorsPreflightRequest(corsHeaders: Record<string, string>): Response;
|
|
10
|
+
//# sourceMappingURL=cors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cors.d.ts","sourceRoot":"","sources":["../../src/middleware/cors.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAW5C;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,GAAE,WAA0B,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAW5F;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,QAAQ,CAExF"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// CORS Handler for Supabase Edge Functions
|
|
2
|
+
/**
|
|
3
|
+
* Default CORS configuration
|
|
4
|
+
*/
|
|
5
|
+
const DEFAULT_CORS = {
|
|
6
|
+
allowOrigin: '*',
|
|
7
|
+
allowHeaders: ['authorization', 'x-client-info', 'apikey', 'content-type', 'x-request-id'],
|
|
8
|
+
allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS']
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Build CORS headers object
|
|
12
|
+
*/
|
|
13
|
+
export function buildCorsHeaders(options = DEFAULT_CORS) {
|
|
14
|
+
const origin = Array.isArray(options.allowOrigin)
|
|
15
|
+
? options.allowOrigin.join(', ')
|
|
16
|
+
: options.allowOrigin;
|
|
17
|
+
return {
|
|
18
|
+
'Access-Control-Allow-Origin': origin,
|
|
19
|
+
'Access-Control-Allow-Headers': options.allowHeaders.join(', '),
|
|
20
|
+
'Access-Control-Allow-Methods': options.allowMethods.join(', '),
|
|
21
|
+
...(options.maxAge && { 'Access-Control-Max-Age': String(options.maxAge) })
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Handle CORS preflight (OPTIONS) request
|
|
26
|
+
*/
|
|
27
|
+
export function handleCorsPreflightRequest(corsHeaders) {
|
|
28
|
+
return new Response('ok', { headers: corsHeaders });
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=cors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cors.js","sourceRoot":"","sources":["../../src/middleware/cors.ts"],"names":[],"mappings":"AAAA,2CAA2C;AAI3C;;GAEG;AACH,MAAM,YAAY,GAAgB;IAC9B,WAAW,EAAE,GAAG;IAChB,YAAY,EAAE,CAAC,eAAe,EAAE,eAAe,EAAE,QAAQ,EAAE,cAAc,EAAE,cAAc,CAAC;IAC1F,YAAY,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,CAAC;CACrE,CAAC;AAEF;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,UAAuB,YAAY;IAChE,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC;QAC7C,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC;QAChC,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC;IAE1B,OAAO;QACH,6BAA6B,EAAE,MAAM;QACrC,8BAA8B,EAAE,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC;QAC/D,8BAA8B,EAAE,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC;QAC/D,GAAG,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,wBAAwB,EAAE,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;KAC9E,CAAC;AACN,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,0BAA0B,CAAC,WAAmC;IAC1E,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC,CAAC;AACxD,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/middleware/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAAE,MAAM,QAAQ,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,0BAA0B,EAAE,MAAM,QAAQ,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// Middleware exports for Supabase Edge Functions adapter
|
|
2
|
+
export { authenticateRequest } from './auth';
|
|
3
|
+
export { buildCorsHeaders, handleCorsPreflightRequest } from './cors';
|
|
4
|
+
export { getRequestId } from './requestId';
|
|
5
|
+
export { validateRequest } from './validation';
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/middleware/index.ts"],"names":[],"mappings":"AAAA,yDAAyD;AAEzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,QAAQ,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,0BAA0B,EAAE,MAAM,QAAQ,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"requestId.d.ts","sourceRoot":"","sources":["../../src/middleware/requestId.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CASrD"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Request ID middleware for Supabase Edge Functions
|
|
2
|
+
/**
|
|
3
|
+
* Generate or extract request ID from headers
|
|
4
|
+
* Uses the Web Crypto API (available in Deno) to generate UUIDs
|
|
5
|
+
*/
|
|
6
|
+
export function getRequestId(request) {
|
|
7
|
+
const existingId = request.headers.get('x-request-id');
|
|
8
|
+
if (existingId) {
|
|
9
|
+
return existingId;
|
|
10
|
+
}
|
|
11
|
+
// Generate new UUID using Web Crypto API (available in Deno)
|
|
12
|
+
return crypto.randomUUID();
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=requestId.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"requestId.js","sourceRoot":"","sources":["../../src/middleware/requestId.ts"],"names":[],"mappings":"AAAA,oDAAoD;AAEpD;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,OAAgB;IACzC,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAEvD,IAAI,UAAU,EAAE,CAAC;QACb,OAAO,UAAU,CAAC;IACtB,CAAC;IAED,6DAA6D;IAC7D,OAAO,MAAM,CAAC,UAAU,EAAE,CAAC;AAC/B,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { ZodSchema } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* Validation schema configuration
|
|
4
|
+
*/
|
|
5
|
+
interface ValidationSchemaConfig {
|
|
6
|
+
body?: ZodSchema;
|
|
7
|
+
params?: ZodSchema;
|
|
8
|
+
query?: ZodSchema;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Validation error detail
|
|
12
|
+
*/
|
|
13
|
+
interface ValidationError {
|
|
14
|
+
field: string;
|
|
15
|
+
message: string;
|
|
16
|
+
code: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Result of validation
|
|
20
|
+
*/
|
|
21
|
+
interface ValidationResult {
|
|
22
|
+
success: boolean;
|
|
23
|
+
data?: unknown;
|
|
24
|
+
errors?: ValidationError[];
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Validate request using Zod schema
|
|
28
|
+
*/
|
|
29
|
+
export declare function validateRequest(request: Request, url: URL, params: Record<string, string>, schema: ZodSchema | ValidationSchemaConfig): Promise<ValidationResult>;
|
|
30
|
+
export {};
|
|
31
|
+
//# sourceMappingURL=validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/middleware/validation.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,KAAK,CAAC;AAErC;;GAEG;AACH,UAAU,sBAAsB;IAC5B,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,KAAK,CAAC,EAAE,SAAS,CAAC;CACrB;AAED;;GAEG;AACH,UAAU,eAAe;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,UAAU,gBAAgB;IACtB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,eAAe,EAAE,CAAC;CAC9B;AA8BD;;GAEG;AACH,wBAAsB,eAAe,CACjC,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,GAAG,EACR,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC9B,MAAM,EAAE,SAAS,GAAG,sBAAsB,GAC3C,OAAO,CAAC,gBAAgB,CAAC,CAuF3B"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
// Request validation middleware for Supabase Edge Functions
|
|
2
|
+
/**
|
|
3
|
+
* Parse request body as JSON
|
|
4
|
+
*/
|
|
5
|
+
async function parseBody(request) {
|
|
6
|
+
try {
|
|
7
|
+
const contentType = request.headers.get('content-type');
|
|
8
|
+
if (contentType?.includes('application/json')) {
|
|
9
|
+
return await request.json();
|
|
10
|
+
}
|
|
11
|
+
return {};
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return {};
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Parse query parameters from URL
|
|
19
|
+
*/
|
|
20
|
+
function parseQuery(url) {
|
|
21
|
+
const query = {};
|
|
22
|
+
url.searchParams.forEach((value, key) => {
|
|
23
|
+
query[key] = value;
|
|
24
|
+
});
|
|
25
|
+
return query;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Validate request using Zod schema
|
|
29
|
+
*/
|
|
30
|
+
export async function validateRequest(request, url, params, schema) {
|
|
31
|
+
const errors = [];
|
|
32
|
+
let validatedInput = {};
|
|
33
|
+
const body = await parseBody(request);
|
|
34
|
+
const query = parseQuery(url);
|
|
35
|
+
// Handle simple schema (validates entire request object)
|
|
36
|
+
if ('safeParse' in schema && typeof schema.safeParse === 'function') {
|
|
37
|
+
const requestData = {
|
|
38
|
+
body,
|
|
39
|
+
params,
|
|
40
|
+
query
|
|
41
|
+
};
|
|
42
|
+
const result = schema.safeParse(requestData);
|
|
43
|
+
if (!result.success) {
|
|
44
|
+
return {
|
|
45
|
+
success: false,
|
|
46
|
+
errors: result.error.errors.map((err) => ({
|
|
47
|
+
field: err.path.join('.'),
|
|
48
|
+
message: err.message,
|
|
49
|
+
code: err.code
|
|
50
|
+
}))
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
success: true,
|
|
55
|
+
data: result.data
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
// Handle complex schema configuration
|
|
59
|
+
const config = schema;
|
|
60
|
+
if (config.body) {
|
|
61
|
+
const bodyResult = config.body.safeParse(body);
|
|
62
|
+
if (!bodyResult.success) {
|
|
63
|
+
errors.push(...bodyResult.error.errors.map((err) => ({
|
|
64
|
+
field: `body.${err.path.join('.')}`,
|
|
65
|
+
message: err.message,
|
|
66
|
+
code: err.code
|
|
67
|
+
})));
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
validatedInput.body = bodyResult.data;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (config.params) {
|
|
74
|
+
const paramsResult = config.params.safeParse(params);
|
|
75
|
+
if (!paramsResult.success) {
|
|
76
|
+
errors.push(...paramsResult.error.errors.map((err) => ({
|
|
77
|
+
field: `params.${err.path.join('.')}`,
|
|
78
|
+
message: err.message,
|
|
79
|
+
code: err.code
|
|
80
|
+
})));
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
validatedInput = { ...validatedInput, ...paramsResult.data };
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (config.query) {
|
|
87
|
+
const queryResult = config.query.safeParse(query);
|
|
88
|
+
if (!queryResult.success) {
|
|
89
|
+
errors.push(...queryResult.error.errors.map((err) => ({
|
|
90
|
+
field: `query.${err.path.join('.')}`,
|
|
91
|
+
message: err.message,
|
|
92
|
+
code: err.code
|
|
93
|
+
})));
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
validatedInput = { ...validatedInput, ...queryResult.data };
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (errors.length > 0) {
|
|
100
|
+
return { success: false, errors };
|
|
101
|
+
}
|
|
102
|
+
return { success: true, data: validatedInput };
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/middleware/validation.ts"],"names":[],"mappings":"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"}
|
package/dist/router.d.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Edge Function Router
|
|
3
|
+
*
|
|
4
|
+
* Provides Express-style routing for Supabase Edge Functions.
|
|
5
|
+
* Converts HandlerPackages to a routing table with regex-based path matching.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
import type { HandlerPackage } from '@multitenantkit/api-handlers';
|
|
10
|
+
import type { RouteMatch } from './types';
|
|
11
|
+
/**
|
|
12
|
+
* Route matcher for Supabase Edge Functions.
|
|
13
|
+
*
|
|
14
|
+
* Converts Express-style paths (e.g., `/users/:id`) to regex patterns
|
|
15
|
+
* and matches incoming requests to the appropriate handlers.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* import { EdgeRouter } from '@multitenantkit/adapter-transport-supabase-edge';
|
|
20
|
+
*
|
|
21
|
+
* const router = new EdgeRouter('/api', handlers);
|
|
22
|
+
*
|
|
23
|
+
* // Match a request
|
|
24
|
+
* const match = router.match('GET', '/api/organizations/123');
|
|
25
|
+
* if (match) {
|
|
26
|
+
* console.log(match.params); // { id: '123' }
|
|
27
|
+
* // Call match.handler...
|
|
28
|
+
* }
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export declare class EdgeRouter {
|
|
32
|
+
private routes;
|
|
33
|
+
private readonly basePath;
|
|
34
|
+
constructor(basePath: string, handlers: HandlerPackage<unknown, unknown>[]);
|
|
35
|
+
/**
|
|
36
|
+
* Register all handler packages as routes
|
|
37
|
+
*/
|
|
38
|
+
private registerHandlers;
|
|
39
|
+
/**
|
|
40
|
+
* Convert Express-style path to regex
|
|
41
|
+
* Example: /users/:id → /^\/users\/([^/]+)$/
|
|
42
|
+
*/
|
|
43
|
+
private pathToRegex;
|
|
44
|
+
/**
|
|
45
|
+
* Match an incoming request to a registered handler.
|
|
46
|
+
*
|
|
47
|
+
* @param method - HTTP method (GET, POST, PUT, DELETE, PATCH)
|
|
48
|
+
* @param pathname - Full pathname including base path (e.g., '/api/users/123')
|
|
49
|
+
* @returns RouteMatch with handler and extracted params, or null if no match
|
|
50
|
+
*/
|
|
51
|
+
match(method: string, pathname: string): RouteMatch | null;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=router.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAY1C;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,UAAU;IACnB,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;gBAEtB,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE;IAK1E;;OAEG;IAEH,OAAO,CAAC,gBAAgB;IAcxB;;;OAGG;IACH,OAAO,CAAC,WAAW;IAiBnB;;;;;;OAMG;IACH,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI;CAoB7D"}
|
package/dist/router.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Edge Function Router
|
|
3
|
+
*
|
|
4
|
+
* Provides Express-style routing for Supabase Edge Functions.
|
|
5
|
+
* Converts HandlerPackages to a routing table with regex-based path matching.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Route matcher for Supabase Edge Functions.
|
|
11
|
+
*
|
|
12
|
+
* Converts Express-style paths (e.g., `/users/:id`) to regex patterns
|
|
13
|
+
* and matches incoming requests to the appropriate handlers.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* import { EdgeRouter } from '@multitenantkit/adapter-transport-supabase-edge';
|
|
18
|
+
*
|
|
19
|
+
* const router = new EdgeRouter('/api', handlers);
|
|
20
|
+
*
|
|
21
|
+
* // Match a request
|
|
22
|
+
* const match = router.match('GET', '/api/organizations/123');
|
|
23
|
+
* if (match) {
|
|
24
|
+
* console.log(match.params); // { id: '123' }
|
|
25
|
+
* // Call match.handler...
|
|
26
|
+
* }
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export class EdgeRouter {
|
|
30
|
+
routes = [];
|
|
31
|
+
basePath;
|
|
32
|
+
constructor(basePath, handlers) {
|
|
33
|
+
this.basePath = basePath;
|
|
34
|
+
this.registerHandlers(handlers);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Register all handler packages as routes
|
|
38
|
+
*/
|
|
39
|
+
// biome-ignore lint/correctness/noUnusedPrivateClassMembers: Called in constructor - false positive
|
|
40
|
+
registerHandlers(handlers) {
|
|
41
|
+
for (const handler of handlers) {
|
|
42
|
+
const { method, path } = handler.route;
|
|
43
|
+
const { pattern, paramNames } = this.pathToRegex(path);
|
|
44
|
+
this.routes.push({
|
|
45
|
+
method: method.toUpperCase(),
|
|
46
|
+
pattern,
|
|
47
|
+
paramNames,
|
|
48
|
+
handler
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Convert Express-style path to regex
|
|
54
|
+
* Example: /users/:id → /^\/users\/([^/]+)$/
|
|
55
|
+
*/
|
|
56
|
+
pathToRegex(path) {
|
|
57
|
+
const paramNames = [];
|
|
58
|
+
const fullPath = this.basePath + path;
|
|
59
|
+
const regexStr = fullPath
|
|
60
|
+
.replace(/:[a-zA-Z]+/g, (match) => {
|
|
61
|
+
paramNames.push(match.slice(1)); // Remove ':'
|
|
62
|
+
return '([^/]+)';
|
|
63
|
+
})
|
|
64
|
+
.replace(/\//g, '\\/');
|
|
65
|
+
return {
|
|
66
|
+
pattern: new RegExp(`^${regexStr}$`),
|
|
67
|
+
paramNames
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Match an incoming request to a registered handler.
|
|
72
|
+
*
|
|
73
|
+
* @param method - HTTP method (GET, POST, PUT, DELETE, PATCH)
|
|
74
|
+
* @param pathname - Full pathname including base path (e.g., '/api/users/123')
|
|
75
|
+
* @returns RouteMatch with handler and extracted params, or null if no match
|
|
76
|
+
*/
|
|
77
|
+
match(method, pathname) {
|
|
78
|
+
for (const route of this.routes) {
|
|
79
|
+
if (route.method !== method.toUpperCase())
|
|
80
|
+
continue;
|
|
81
|
+
const match = pathname.match(route.pattern);
|
|
82
|
+
if (match) {
|
|
83
|
+
const params = {};
|
|
84
|
+
route.paramNames.forEach((name, i) => {
|
|
85
|
+
params[name] = match[i + 1];
|
|
86
|
+
});
|
|
87
|
+
return {
|
|
88
|
+
handler: route.handler,
|
|
89
|
+
params
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=router.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"router.js","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAeH;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,OAAO,UAAU;IACX,MAAM,GAAiB,EAAE,CAAC;IACjB,QAAQ,CAAS;IAElC,YAAY,QAAgB,EAAE,QAA4C;QACtE,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IACpC,CAAC;IAED;;OAEG;IACH,oGAAoG;IAC5F,gBAAgB,CAAC,QAA4C;QACjE,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC7B,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC;YACvC,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;YAEvD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;gBACb,MAAM,EAAE,MAAM,CAAC,WAAW,EAAE;gBAC5B,OAAO;gBACP,UAAU;gBACV,OAAO;aACV,CAAC,CAAC;QACP,CAAC;IACL,CAAC;IAED;;;OAGG;IACK,WAAW,CAAC,IAAY;QAC5B,MAAM,UAAU,GAAa,EAAE,CAAC;QAChC,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QAEtC,MAAM,QAAQ,GAAG,QAAQ;aACpB,OAAO,CAAC,aAAa,EAAE,CAAC,KAAK,EAAE,EAAE;YAC9B,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa;YAC9C,OAAO,SAAS,CAAC;QACrB,CAAC,CAAC;aACD,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QAE3B,OAAO;YACH,OAAO,EAAE,IAAI,MAAM,CAAC,IAAI,QAAQ,GAAG,CAAC;YACpC,UAAU;SACb,CAAC;IACN,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,MAAc,EAAE,QAAgB;QAClC,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAC9B,IAAI,KAAK,CAAC,MAAM,KAAK,MAAM,CAAC,WAAW,EAAE;gBAAE,SAAS;YAEpD,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAC5C,IAAI,KAAK,EAAE,CAAC;gBACR,MAAM,MAAM,GAA2B,EAAE,CAAC;gBAC1C,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE;oBACjC,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;gBAChC,CAAC,CAAC,CAAC;gBAEH,OAAO;oBACH,OAAO,EAAE,KAAK,CAAC,OAAO;oBACtB,MAAM;iBACT,CAAC;YACN,CAAC;QACL,CAAC;QAED,OAAO,IAAI,CAAC;IAChB,CAAC;CACJ"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types specific to Supabase Edge Functions adapter
|
|
3
|
+
* @module
|
|
4
|
+
*/
|
|
5
|
+
import type { Principal } from '@multitenantkit/domain-contracts/shared/auth';
|
|
6
|
+
/**
|
|
7
|
+
* Context passed through the Edge Function pipeline.
|
|
8
|
+
* Contains all request information after parsing and authentication.
|
|
9
|
+
*/
|
|
10
|
+
export interface EdgeContext {
|
|
11
|
+
/** The original Deno Request object */
|
|
12
|
+
request: Request;
|
|
13
|
+
/** Unique identifier for this request (from header or generated) */
|
|
14
|
+
requestId: string;
|
|
15
|
+
/** Authenticated principal, or null if not authenticated */
|
|
16
|
+
principal: Principal | null;
|
|
17
|
+
/** Path parameters extracted from the URL (e.g., { id: '123' }) */
|
|
18
|
+
params: Record<string, string>;
|
|
19
|
+
/** Query parameters from the URL */
|
|
20
|
+
query: Record<string, string>;
|
|
21
|
+
/** Parsed request body */
|
|
22
|
+
body: unknown;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* CORS (Cross-Origin Resource Sharing) configuration options.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```typescript
|
|
29
|
+
* const cors: CorsOptions = {
|
|
30
|
+
* allowOrigin: 'https://myapp.com',
|
|
31
|
+
* allowHeaders: ['authorization', 'content-type'],
|
|
32
|
+
* allowMethods: ['GET', 'POST', 'PUT', 'DELETE'],
|
|
33
|
+
* maxAge: 86400 // 24 hours
|
|
34
|
+
* };
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export interface CorsOptions {
|
|
38
|
+
/** Allowed origins - can be '*' for all, a single origin, or array of origins */
|
|
39
|
+
allowOrigin: string | string[];
|
|
40
|
+
/** Headers that can be used in the actual request */
|
|
41
|
+
allowHeaders: string[];
|
|
42
|
+
/** HTTP methods allowed when accessing the resource */
|
|
43
|
+
allowMethods: string[];
|
|
44
|
+
/** How long (in seconds) the results of a preflight request can be cached */
|
|
45
|
+
maxAge?: number;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Configuration options for the Edge Function builder.
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```typescript
|
|
52
|
+
* const options: EdgeFunctionOptions = {
|
|
53
|
+
* basePath: '/multitenantkit',
|
|
54
|
+
* cors: {
|
|
55
|
+
* allowOrigin: '*',
|
|
56
|
+
* allowHeaders: ['authorization', 'content-type'],
|
|
57
|
+
* allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH']
|
|
58
|
+
* },
|
|
59
|
+
* debug: true
|
|
60
|
+
* };
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
export interface EdgeFunctionOptions {
|
|
64
|
+
/**
|
|
65
|
+
* Base path prefix for all routes.
|
|
66
|
+
* This is typically the Edge Function name (e.g., '/multitenantkit').
|
|
67
|
+
* All handler routes will be prefixed with this path.
|
|
68
|
+
*/
|
|
69
|
+
basePath: string;
|
|
70
|
+
/**
|
|
71
|
+
* CORS configuration. If not provided, defaults to allowing all origins.
|
|
72
|
+
*/
|
|
73
|
+
cors?: CorsOptions;
|
|
74
|
+
/**
|
|
75
|
+
* Enable debug logging. Logs request method, path, and requestId.
|
|
76
|
+
* @default false
|
|
77
|
+
*/
|
|
78
|
+
debug?: boolean;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Result of matching a request to a registered route.
|
|
82
|
+
* Used internally by the router.
|
|
83
|
+
*/
|
|
84
|
+
export interface RouteMatch {
|
|
85
|
+
/** The matched HandlerPackage */
|
|
86
|
+
handler: unknown;
|
|
87
|
+
/** Extracted path parameters */
|
|
88
|
+
params: Record<string, string>;
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8CAA8C,CAAC;AAE9E;;;GAGG;AACH,MAAM,WAAW,WAAW;IACxB,uCAAuC;IACvC,OAAO,EAAE,OAAO,CAAC;IACjB,oEAAoE;IACpE,SAAS,EAAE,MAAM,CAAC;IAClB,4DAA4D;IAC5D,SAAS,EAAE,SAAS,GAAG,IAAI,CAAC;IAC5B,mEAAmE;IACnE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,oCAAoC;IACpC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,0BAA0B;IAC1B,IAAI,EAAE,OAAO,CAAC;CACjB;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,WAAW;IACxB,iFAAiF;IACjF,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC/B,qDAAqD;IACrD,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,uDAAuD;IACvD,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,6EAA6E;IAC7E,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,mBAAmB;IAChC;;;;OAIG;IACH,QAAQ,EAAE,MAAM,CAAC;IACjB;;OAEG;IACH,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB;;;OAGG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;GAGG;AACH,MAAM,WAAW,UAAU;IACvB,iCAAiC;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,gCAAgC;IAChC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@multitenantkit/adapter-transport-supabase-edge",
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Supabase Edge Functions transport adapter for MultiTenantKit HTTP API",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"multitenantkit",
|
|
8
|
+
"saas",
|
|
9
|
+
"multi-tenant",
|
|
10
|
+
"multitenancy",
|
|
11
|
+
"adapter",
|
|
12
|
+
"supabase",
|
|
13
|
+
"edge-functions",
|
|
14
|
+
"deno",
|
|
15
|
+
"rest-api",
|
|
16
|
+
"api-server",
|
|
17
|
+
"http",
|
|
18
|
+
"serverless",
|
|
19
|
+
"organizations",
|
|
20
|
+
"teams",
|
|
21
|
+
"users",
|
|
22
|
+
"typescript"
|
|
23
|
+
],
|
|
24
|
+
"author": "",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "https://github.com/multitenantkit/multitenantkit.git",
|
|
29
|
+
"directory": "packages/adapters/transport/supabase-edge"
|
|
30
|
+
},
|
|
31
|
+
"bugs": {
|
|
32
|
+
"url": "https://github.com/multitenantkit/multitenantkit/issues"
|
|
33
|
+
},
|
|
34
|
+
"homepage": "https://github.com/multitenantkit/multitenantkit#readme",
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"access": "public"
|
|
37
|
+
},
|
|
38
|
+
"main": "./dist/index.js",
|
|
39
|
+
"types": "./dist/index.d.ts",
|
|
40
|
+
"files": [
|
|
41
|
+
"dist",
|
|
42
|
+
"README.md"
|
|
43
|
+
],
|
|
44
|
+
"scripts": {
|
|
45
|
+
"build": "tsc --build",
|
|
46
|
+
"clean": "rm -rf dist",
|
|
47
|
+
"type-check": "tsc --noEmit",
|
|
48
|
+
"test": "vitest run",
|
|
49
|
+
"test:watch": "vitest",
|
|
50
|
+
"test:coverage": "vitest run --coverage"
|
|
51
|
+
},
|
|
52
|
+
"dependencies": {
|
|
53
|
+
"@multitenantkit/api-contracts": "^0.2.0",
|
|
54
|
+
"@multitenantkit/domain-contracts": "^0.2.0",
|
|
55
|
+
"@multitenantkit/api-handlers": "^0.2.0",
|
|
56
|
+
"zod": "^3.22.0"
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"@types/node": "^20.0.0",
|
|
60
|
+
"typescript": "^5.0.0",
|
|
61
|
+
"vitest": "^1.0.0"
|
|
62
|
+
}
|
|
63
|
+
}
|