encore.dev 1.44.9 → 1.45.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.
@@ -1,9 +1,15 @@
1
1
  import { Gateway } from "../../api/gateway";
2
+ import { Middleware, MiddlewareRequest, HandlerResponse } from "../../api/mod";
3
+ import { IterableSocket, IterableStream, Sink } from "../../api/stream";
2
4
  import { RawRequest, RawResponse } from "../api/node_http";
3
5
  import { setCurrentRequest } from "../reqtrack/mod";
4
6
  import * as runtime from "../runtime/mod";
5
7
 
6
- export type Handler = runtime.ApiRoute;
8
+ export type Handler = {
9
+ apiRoute: runtime.ApiRoute;
10
+ middlewares: Middleware[];
11
+ endpointOptions: EndpointOptions;
12
+ };
7
13
 
8
14
  export function registerHandlers(handlers: Handler[]) {
9
15
  runtime.RT.registerHandlers(handlers.map((h) => transformHandler(h)));
@@ -22,104 +28,201 @@ export async function run() {
22
28
  return runtime.RT.runForever();
23
29
  }
24
30
 
25
- class IterableStream {
26
- private stream: runtime.Stream;
27
-
28
- constructor(stream: runtime.Stream) {
29
- this.stream = stream;
30
- }
31
+ interface EndpointOptions {
32
+ expose: boolean;
33
+ auth: boolean;
34
+ isRaw: boolean;
35
+ isStream: boolean;
36
+ }
31
37
 
32
- recv(): Promise<Record<string, any>> {
33
- return this.stream.recv();
34
- }
38
+ export interface InternalHandlerResponse {
39
+ payload: any;
40
+ extraHeaders?: Record<string, string | string[]>;
41
+ }
35
42
 
36
- async *[Symbol.asyncIterator]() {
37
- while (true) {
38
- try {
39
- yield await this.stream.recv();
40
- } catch (e) {
41
- break;
42
- }
43
+ // recursively calls all middlewares
44
+ async function invokeMiddlewareChain(
45
+ req: MiddlewareRequest,
46
+ chain: Middleware[],
47
+ handler: () => Promise<any>
48
+ ): Promise<InternalHandlerResponse> {
49
+ const execute = async (
50
+ index: number,
51
+ req: MiddlewareRequest
52
+ ): Promise<HandlerResponse> => {
53
+ const currentMiddleware = chain.at(index);
54
+
55
+ // no more middlewares, execute the handler
56
+ if (currentMiddleware === undefined) {
57
+ return new HandlerResponse(await handler());
43
58
  }
44
- }
45
- }
46
59
 
47
- class IterableSocket {
48
- private socket: runtime.Socket;
60
+ // execute current middleware
61
+ return currentMiddleware(req, (req) => {
62
+ return execute(index + 1, req);
63
+ });
64
+ };
49
65
 
50
- constructor(socket: runtime.Socket) {
51
- this.socket = socket;
52
- }
66
+ return (await execute(0, req)).__internalToResponse();
67
+ }
53
68
 
54
- send(msg: Record<string, any>): void {
55
- return this.socket.send(msg);
56
- }
57
- recv(): Promise<Record<string, any>> {
58
- return this.socket.recv();
59
- }
69
+ // calculate what middlewares should run for an endpoint
70
+ function calculateMiddlewareChain(
71
+ endpointOptions: EndpointOptions,
72
+ ms: Middleware[]
73
+ ): Middleware[] {
74
+ let middlewares = [];
75
+
76
+ for (const m of ms) {
77
+ if (m.options === undefined || m.options.target === undefined) {
78
+ middlewares.push(m);
79
+ } else {
80
+ const target = m.options.target;
81
+ // check if options are set and if they match the endpoint options
82
+ if (target.auth !== undefined && target.auth !== endpointOptions.auth) {
83
+ continue;
84
+ }
60
85
 
61
- close(): void {
62
- this.socket.close();
63
- }
86
+ if (
87
+ target.expose !== undefined &&
88
+ target.expose !== endpointOptions.expose
89
+ ) {
90
+ continue;
91
+ }
64
92
 
65
- async *[Symbol.asyncIterator]() {
66
- while (true) {
67
- try {
68
- yield await this.socket.recv();
69
- } catch (e) {
70
- break;
93
+ if (
94
+ target.isRaw !== undefined &&
95
+ target.isRaw !== endpointOptions.isRaw
96
+ ) {
97
+ continue;
71
98
  }
99
+
100
+ if (
101
+ target.isStream !== undefined &&
102
+ target.isStream !== endpointOptions.isStream
103
+ ) {
104
+ continue;
105
+ }
106
+
107
+ middlewares.push(m);
72
108
  }
73
109
  }
110
+
111
+ return middlewares;
74
112
  }
75
113
 
76
- function transformHandler(h: Handler): Handler {
77
- if (h.streamingResponse || h.streamingRequest) {
114
+ function transformHandler(h: Handler): runtime.ApiRoute {
115
+ const middlewares = calculateMiddlewareChain(
116
+ h.endpointOptions,
117
+ h.middlewares
118
+ );
119
+
120
+ if (h.apiRoute.streamingResponse || h.apiRoute.streamingRequest) {
78
121
  return {
79
- ...h,
122
+ ...h.apiRoute,
80
123
  // req is the upgrade request.
81
124
  // stream is either a bidirectional stream, in stream or out stream.
82
- handler: (req: runtime.Request, stream: unknown) => {
125
+ handler: (
126
+ req: runtime.Request,
127
+ stream: runtime.Sink | runtime.Stream | runtime.Socket
128
+ ) => {
83
129
  setCurrentRequest(req);
84
130
 
85
131
  // make readable streams async iterators
86
- if (stream instanceof runtime.Stream) {
87
- stream = new IterableStream(stream);
88
- }
89
- if (stream instanceof runtime.Socket) {
90
- stream = new IterableSocket(stream);
132
+ const streamArg: IterableStream | IterableSocket | Sink =
133
+ stream instanceof runtime.Stream
134
+ ? new IterableStream(stream)
135
+ : stream instanceof runtime.Socket
136
+ ? new IterableSocket(stream)
137
+ : new Sink(stream);
138
+
139
+ if (middlewares.length === 0) {
140
+ const payload = req.payload();
141
+ return toResponse(
142
+ payload !== null
143
+ ? h.apiRoute.handler(payload, streamArg)
144
+ : h.apiRoute.handler(streamArg)
145
+ );
91
146
  }
92
147
 
93
- // handshake payload
94
- const payload = req.payload();
95
- return payload !== null
96
- ? h.handler(payload, stream)
97
- : h.handler(stream);
148
+ const handler = async () => {
149
+ // handshake payload
150
+ const payload = req.payload();
151
+ return payload !== null
152
+ ? h.apiRoute.handler(payload, streamArg)
153
+ : h.apiRoute.handler(streamArg);
154
+ };
155
+
156
+ const mwRequest = new MiddlewareRequest(
157
+ streamArg,
158
+ undefined,
159
+ undefined
160
+ );
161
+ return invokeMiddlewareChain(mwRequest, middlewares, handler);
98
162
  }
99
163
  };
100
164
  }
101
165
 
102
- if (h.raw) {
166
+ if (h.apiRoute.raw) {
103
167
  return {
104
- ...h,
168
+ ...h.apiRoute,
105
169
  handler: (
106
170
  req: runtime.Request,
107
171
  resp: runtime.ResponseWriter,
108
172
  body: runtime.BodyReader
109
173
  ) => {
110
174
  setCurrentRequest(req);
175
+
111
176
  const rawReq = new RawRequest(req, body);
112
177
  const rawResp = new RawResponse(rawReq, resp);
113
- return h.handler(rawReq, rawResp);
178
+
179
+ if (middlewares.length === 0) {
180
+ return toResponse(h.apiRoute.handler(rawReq, rawResp));
181
+ }
182
+
183
+ const handler = async () => {
184
+ return h.apiRoute.handler(rawReq, rawResp);
185
+ };
186
+
187
+ const mwRequest = new MiddlewareRequest(undefined, rawReq, rawResp);
188
+ return invokeMiddlewareChain(mwRequest, middlewares, handler);
114
189
  }
115
190
  };
116
191
  }
192
+
117
193
  return {
118
- ...h,
194
+ ...h.apiRoute,
119
195
  handler: (req: runtime.Request) => {
120
196
  setCurrentRequest(req);
121
- const payload = req.payload();
122
- return payload !== null ? h.handler(payload) : h.handler();
197
+
198
+ if (middlewares.length === 0) {
199
+ const payload = req.payload();
200
+ return toResponse(
201
+ payload !== null ? h.apiRoute.handler(payload) : h.apiRoute.handler()
202
+ );
203
+ }
204
+
205
+ const handler = async () => {
206
+ const payload = req.payload();
207
+ return payload !== null
208
+ ? h.apiRoute.handler(payload)
209
+ : h.apiRoute.handler();
210
+ };
211
+
212
+ const mwRequest = new MiddlewareRequest(undefined, undefined, undefined);
213
+ return invokeMiddlewareChain(mwRequest, middlewares, handler);
123
214
  }
124
215
  };
125
216
  }
217
+
218
+ function toResponse(
219
+ payload: any
220
+ ): InternalHandlerResponse | Promise<InternalHandlerResponse> {
221
+ if (payload instanceof Promise) {
222
+ return payload.then((payload) => {
223
+ return new HandlerResponse(payload).__internalToResponse();
224
+ });
225
+ } else {
226
+ return new HandlerResponse(payload).__internalToResponse();
227
+ }
228
+ }
@@ -1,5 +1,5 @@
1
1
  // The version of the runtime this JS bundle was generated for.
2
- const version = "v1.44.9";
2
+ const version = "v1.45.0";
3
3
 
4
4
  // Load the native module.
5
5
  const nativeModulePath = process.env.ENCORE_RUNTIME_LIB;
@@ -23,6 +23,7 @@ export interface ApiDesc {
23
23
  service: string
24
24
  endpoint: string
25
25
  raw: boolean
26
+ requiresAuth: boolean
26
27
  }
27
28
 
28
29
  export interface ApiRoute {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "encore.dev",
3
3
  "description": "Encore's JavaScript/TypeScript SDK",
4
- "version": "1.44.9",
4
+ "version": "1.45.0",
5
5
  "license": "MPL-2.0",
6
6
  "bugs": {
7
7
  "url": "https://github.com/encoredev/encore/issues"
package/req_meta.ts CHANGED
@@ -10,6 +10,9 @@ export interface APIDesc {
10
10
 
11
11
  /** Whether the endpoint is a raw endpoint. */
12
12
  raw: boolean;
13
+
14
+ /** Whether the endpoint requires auth. */
15
+ auth: boolean;
13
16
  }
14
17
 
15
18
  export type Method =
@@ -153,7 +156,7 @@ export function currentRequest(): RequestMeta | undefined {
153
156
  const meta = req.meta();
154
157
 
155
158
  const base: BaseRequestMeta = {
156
- trace: meta.trace,
159
+ trace: meta.trace
157
160
  };
158
161
 
159
162
  if (meta.apiCall) {
@@ -163,13 +166,14 @@ export function currentRequest(): RequestMeta | undefined {
163
166
  service: meta.apiCall.api.service,
164
167
  endpoint: meta.apiCall.api.endpoint,
165
168
  raw: meta.apiCall.api.raw,
169
+ auth: meta.apiCall.api.requiresAuth
166
170
  },
167
171
  method: meta.apiCall.method as Method,
168
172
  path: meta.apiCall.path,
169
173
  pathAndQuery: meta.apiCall.pathAndQuery,
170
174
  pathParams: meta.apiCall.pathParams ?? {},
171
175
  parsedPayload: meta.apiCall.parsedPayload,
172
- headers: meta.apiCall.headers,
176
+ headers: meta.apiCall.headers
173
177
  };
174
178
  return { ...base, ...api };
175
179
  } else if (meta.pubsubMessage) {
@@ -180,7 +184,7 @@ export function currentRequest(): RequestMeta | undefined {
180
184
  subscription: meta.pubsubMessage.subscription,
181
185
  messageId: meta.pubsubMessage.id,
182
186
  deliveryAttempt: meta.pubsubMessage.deliveryAttempt,
183
- parsedPayload: meta.pubsubMessage.parsedPayload,
187
+ parsedPayload: meta.pubsubMessage.parsedPayload
184
188
  };
185
189
  return { ...base, ...msg };
186
190
  } else {
package/service/mod.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { Middleware } from "../api/mod";
2
+
1
3
  /**
2
4
  * Defines an Encore backend service.
3
5
  *
@@ -17,4 +19,6 @@ export class Service {
17
19
  }
18
20
  }
19
21
 
20
- export interface ServiceConfig {}
22
+ export interface ServiceConfig {
23
+ middlewares?: Middleware[];
24
+ }
@@ -19,7 +19,7 @@ const driverName = "node-pg";
19
19
  export type Row = Record<string, any>;
20
20
 
21
21
  /** Represents a type that can be used in query template literals */
22
- export type Primitive = string | number | boolean | Buffer | null;
22
+ export type Primitive = string | number | boolean | Buffer | Date | null;
23
23
 
24
24
  /**
25
25
  * Constructing a new database object will result in Encore provisioning a database with