5htp-core 0.5.0-9 → 0.5.1-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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "5htp-core",
3
3
  "description": "Convenient TypeScript framework designed for Performance and Productivity.",
4
- "version": "0.5.0-9",
4
+ "version": "0.5.1-1",
5
5
  "author": "Gaetan Le Gac (https://github.com/gaetanlegac)",
6
6
  "repository": "git://github.com/gaetanlegac/5htp-core.git",
7
7
  "license": "MIT",
@@ -80,6 +80,7 @@ export type TRouteOptions = {
80
80
  accept?: string,
81
81
  raw?: boolean, // true to return raw data
82
82
  auth?: TUserRole | boolean,
83
+ canonicalParams?: string[],
83
84
 
84
85
  // Rendering
85
86
  static?: boolean,
@@ -417,6 +417,7 @@ export class SchemaValidators {
417
417
  try {
418
418
  val = JSON.parse(val);
419
419
  } catch (error) {
420
+ console.error("Failed to parse rich text json:", error, val);
420
421
  throw new InputError("Invalid rich text format.");
421
422
  }
422
423
 
@@ -12,6 +12,7 @@
12
12
  // Node
13
13
  // Npm
14
14
  import type express from 'express';
15
+ import type { Request, Response, NextFunction } from 'express';
15
16
  import { v4 as uuid } from 'uuid';
16
17
  import type { GlobImportedWithMetas } from 'babel-plugin-glob-import';
17
18
 
@@ -265,7 +266,31 @@ export default class ServerRouter<
265
266
  public post = (...args: TApiRegisterArgs<this>) => this.registerApi('POST', ...args);
266
267
  public put = (...args: TApiRegisterArgs<this>) => this.registerApi('PUT', ...args);
267
268
  public patch = (...args: TApiRegisterArgs<this>) => this.registerApi('PATCH', ...args);
268
- public delete = (...args: TApiRegisterArgs<this>) => this.registerApi('DELETE', ...args)
269
+ public delete = (...args: TApiRegisterArgs<this>) => this.registerApi('DELETE', ...args);
270
+
271
+ public express(
272
+ middleware: (
273
+ req: Request,
274
+ res: Response,
275
+ next: NextFunction,
276
+ requestContext: TRouterContext
277
+ ) => void
278
+ ) {
279
+ return (context: TRouterContext) => new Promise((resolve) => {
280
+
281
+ context.request.res.on('finish', function() {
282
+ //console.log('the response has been sent', request.res.statusCode);
283
+ resolve(true);
284
+ });
285
+
286
+ middleware(
287
+ context.request.req,
288
+ context.request.res,
289
+ () => { resolve(true); },
290
+ context
291
+ )
292
+ })
293
+ }
269
294
 
270
295
  protected registerApi(method: TRouteHttpMethod, ...args: TApiRegisterArgs<this>): this {
271
296
 
@@ -432,12 +457,16 @@ declare type Routes = {
432
457
  response = await this.handleError(e, request);
433
458
  }
434
459
 
435
- // Status
436
- res.status(response.statusCode);
437
- // Headers
438
- res.header(response.headers);
439
- // Data
440
- res.send(response.data);
460
+ if (!res.headersSent) {
461
+ // Status
462
+ res.status(response.statusCode);
463
+ // Headers
464
+ res.header(response.headers);
465
+ // Data
466
+ res.send(response.data);
467
+ } else if (response.data !== 'true') {
468
+ throw new Error("Can't return data from the controller since response has already been sent via express.");
469
+ }
441
470
 
442
471
  });
443
472
  }
@@ -490,7 +519,6 @@ declare type Routes = {
490
519
 
491
520
  // Run on resolution hooks. Ex: authentication check
492
521
  await this.runHook('resolved', route);
493
-
494
522
  const timeEndResolving = Date.now();
495
523
 
496
524
  // Create response
@@ -82,6 +82,7 @@ export default class ServerResponse<
82
82
  public headers: {[cle: string]: string} = {}
83
83
  public cookie: express.Response["cookie"];
84
84
  public clearCookie: express.Response["clearCookie"];
85
+ public canonicalUrl: URL;
85
86
 
86
87
  // If data was provided by at lead one controller
87
88
  public wasProvided = false;
@@ -95,12 +96,18 @@ export default class ServerResponse<
95
96
 
96
97
  this.router = request.router;
97
98
  this.app = this.router.app;
99
+
100
+ this.canonicalUrl = new URL(request.url);
101
+ this.canonicalUrl.search = '';
98
102
  }
99
103
 
100
104
  public async runController( route: TAnyRoute, additionnalData: {} = {} ) {
101
105
 
102
106
  this.route = route;
103
107
 
108
+ // Update canonical url
109
+ this.updateCanonicalUrl(route);
110
+
104
111
  // Create response context for controllers
105
112
  const context = await this.createContext(route);
106
113
 
@@ -140,6 +147,18 @@ export default class ServerResponse<
140
147
  this.router.cache[ chunkId ] = this.data;
141
148
  }
142
149
 
150
+ private updateCanonicalUrl( route: TAnyRoute ) {
151
+
152
+ if (!route.options.canonicalParams)
153
+ return;
154
+
155
+ for (const key of route.options.canonicalParams) {
156
+ const paramValue = this.request.data[ key ];
157
+ if (paramValue !== undefined)
158
+ this.canonicalUrl.searchParams.set(key, paramValue);
159
+ }
160
+ }
161
+
143
162
  /*----------------------------------
144
163
  - INTERNAL
145
164
  ----------------------------------*/
@@ -57,9 +57,6 @@ export default class DocumentRenderer<TRouter extends Router> {
57
57
 
58
58
  public async page( html: string, page: Page, response: ServerResponse<TRouter> ) {
59
59
 
60
- // TODO: can be customized via page / route config
61
- const canonicalUrl = response.request.url;
62
-
63
60
  let attrsBody = {
64
61
  className: [...page.bodyClass].join(' '),
65
62
  };
@@ -94,7 +91,7 @@ export default class DocumentRenderer<TRouter extends Router> {
94
91
  {/* Page */}
95
92
  <title>{page.title}</title>
96
93
  <meta content={page.description} name="description" />
97
- <link rel="canonical" href={canonicalUrl} />
94
+ <link rel="canonical" href={response.canonicalUrl} />
98
95
 
99
96
  {/* SEO, social medias, OG tags, ... */}
100
97
  {page.head.map(({ $, ...attrs }) => (