@mojojs/core 0.5.3 → 0.5.7

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.
@@ -3,7 +3,7 @@ import * as util from './../util.js';
3
3
  export default async function genLiteAppCommand(app, args) {
4
4
  const name = args[1] ?? 'index.js';
5
5
  process.stdout.write('Generating application directory struture:\n');
6
- await util.cliCreateFile('config.json', jsonConfig, { secret: crypto.randomBytes(16).toString('hex') });
6
+ await util.cliCreateFile('config.yml', yamlConfig, { secret: crypto.randomBytes(16).toString('hex') });
7
7
  await util.cliCreateFile(name, jsIndex);
8
8
  await util.cliCreateDir('controllers');
9
9
  await util.cliCreateFile('controllers/example.js', jsController);
@@ -27,17 +27,14 @@ genLiteAppCommand.usage = `Usage: APPLICATION gen-full-app [OPTIONS] [NAME]
27
27
  Options:
28
28
  -h, --help Show this summary of available options
29
29
  `;
30
- const jsonConfig = `{
31
- "secrets": [
32
- "<%= secret %>"
33
- ]
34
- }
30
+ const yamlConfig = `---
31
+ secrets: [<%= secret %>]
35
32
  `;
36
- const jsIndex = `import mojo, {jsonConfigPlugin} from '@mojojs/core';
33
+ const jsIndex = `import mojo, {yamlConfigPlugin} from '@mojojs/core';
37
34
 
38
35
  export const app = mojo();
39
36
 
40
- app.plugin(jsonConfigPlugin);
37
+ app.plugin(yamlConfigPlugin);
41
38
  app.secrets = [app.config.secrets];
42
39
 
43
40
  app.get('/').to('example#welcome');
@@ -1,4 +1,4 @@
1
- import escapeStringRegexp from 'escape-string-regexp';
1
+ import { escapeRegExp } from '../util.js';
2
2
  const OP = {
3
3
  placeholder: Symbol('placeholder'),
4
4
  relaxed: Symbol('relaxed'),
@@ -85,7 +85,7 @@ export class Pattern {
85
85
  continue;
86
86
  }
87
87
  else if (token[0] === OP.text) {
88
- part = escapeStringRegexp(token[1]);
88
+ part = escapeRegExp(token[1]);
89
89
  optional = false;
90
90
  }
91
91
  else {
@@ -128,7 +128,7 @@ export class Pattern {
128
128
  .slice()
129
129
  .sort()
130
130
  .reverse()
131
- .map(val => escapeStringRegexp(val))
131
+ .map(val => escapeRegExp(val))
132
132
  .join('|') +
133
133
  ')');
134
134
  }
package/lib/server.d.ts CHANGED
@@ -4,7 +4,7 @@ import type { Socket } from 'net';
4
4
  import http from 'http';
5
5
  import https from 'https';
6
6
  import { URL } from 'url';
7
- import WS from 'ws';
7
+ import { WebSocketServer } from 'ws';
8
8
  declare type ListenArgs = any[];
9
9
  interface ServerOptions {
10
10
  cluster?: boolean;
@@ -28,6 +28,6 @@ export declare class Server {
28
28
  stop(): Promise<void>;
29
29
  _createServer(location: string): Promise<void>;
30
30
  _handleRequest(req: http.IncomingMessage, res: http.ServerResponse): void;
31
- _handleUpgrade(wss: WS.Server, req: http.IncomingMessage, socket: Socket, head: Buffer): void;
31
+ _handleUpgrade(wss: WebSocketServer, req: http.IncomingMessage, socket: Socket, head: Buffer): void;
32
32
  }
33
33
  export {};
package/lib/server.js CHANGED
@@ -5,7 +5,7 @@ import os from 'os';
5
5
  import { URL } from 'url';
6
6
  import { WebSocket } from './websocket.js';
7
7
  import Path from '@mojojs/path';
8
- import WS from 'ws';
8
+ import { WebSocketServer } from 'ws';
9
9
  export class Server {
10
10
  constructor(app, options = {}) {
11
11
  this.urls = [];
@@ -38,7 +38,7 @@ export class Server {
38
38
  }
39
39
  async start() {
40
40
  await this.app.hooks.runHook('start', this.app);
41
- if (this._cluster && cluster.isMaster) {
41
+ if (this._cluster && cluster.isPrimary) {
42
42
  for (let i = 0; i < this._workers; i++) {
43
43
  cluster.fork();
44
44
  }
@@ -64,7 +64,7 @@ export class Server {
64
64
  isHttps = true;
65
65
  }
66
66
  await this.app.warmup();
67
- const wss = new WS.Server({ noServer: true });
67
+ const wss = new WebSocketServer({ noServer: true });
68
68
  const server = (isHttps ? https : http).createServer(options, this._handleRequest.bind(this));
69
69
  this._servers.push(server);
70
70
  server.on('upgrade', this._handleUpgrade.bind(this, wss));
@@ -35,7 +35,7 @@ export declare class TestUserAgent extends MockUserAgent {
35
35
  headerExistsNot(name: string): this;
36
36
  headerIs(name: string, value: string): this;
37
37
  headerLike(name: string, regex: RegExp): this;
38
- jsonIs(value: JSONValue): this;
38
+ jsonIs(value: JSONValue, pointer?: string): this;
39
39
  messageOk(): Promise<JSONValue>;
40
40
  static newTestUserAgent(app: App, options?: TestUserAgentOptions): Promise<TestUserAgent>;
41
41
  optionsOk(url: string | URL, options?: UserAgentRequestOptions): Promise<this>;
@@ -49,7 +49,7 @@ export declare class TestUserAgent extends MockUserAgent {
49
49
  typeLike(regex: RegExp): this;
50
50
  websocketOk(url: string | URL, options?: UserAgentWebSocketOptions): Promise<void>;
51
51
  get ws(): WebSocket;
52
- yamlIs(value: any): this;
52
+ yamlIs(value: JSONValue, pointer?: string): this;
53
53
  get _cheerio(): cheerio.Root;
54
54
  _prepareTap(tap: Tap.Tap): void;
55
55
  _requestOk(skip: SkipFunction, method: string, url: string | URL, options?: UserAgentRequestOptions): Promise<this>;
@@ -1,5 +1,6 @@
1
1
  import assert from 'assert/strict';
2
2
  import { on } from 'events';
3
+ import { jsonPointer } from '../util.js';
3
4
  import { MockUserAgent } from './mock.js';
4
5
  import cheerio from 'cheerio';
5
6
  import yaml from 'js-yaml';
@@ -79,8 +80,9 @@ export class TestUserAgent extends MockUserAgent {
79
80
  this.assert('match', [this.res.get(name), regex], `${name} is similar`, this.headerLike);
80
81
  return this;
81
82
  }
82
- jsonIs(value) {
83
- this.assert('same', [JSON.parse(this.body.toString()), value], 'JSON body is equal', this.jsonIs);
83
+ jsonIs(value, pointer = '') {
84
+ const expected = jsonPointer(JSON.parse(this.body.toString()), pointer);
85
+ this.assert('same', [expected, value], `exact match for JSON Pointer "${pointer}" (JSON)`, this.jsonIs);
84
86
  return this;
85
87
  }
86
88
  async messageOk() {
@@ -142,8 +144,9 @@ export class TestUserAgent extends MockUserAgent {
142
144
  throw new Error('No active WebSocket connection');
143
145
  return ws;
144
146
  }
145
- yamlIs(value) {
146
- this.assert('same', [yaml.load(this.body.toString()), value], 'YAML body is equal', this.yamlIs);
147
+ yamlIs(value, pointer = '') {
148
+ const expected = jsonPointer(yaml.load(this.body.toString()), pointer);
149
+ this.assert('same', [expected, value], `exact match for JSON Pointer "${pointer}" (YAML)`, this.yamlIs);
147
150
  return this;
148
151
  }
149
152
  get _cheerio() {
package/lib/user-agent.js CHANGED
@@ -74,6 +74,7 @@ class UserAgent extends EventEmitter {
74
74
  const filtered = this._filterSharedConfig(options);
75
75
  await this._loadCookies(filtered.url, filtered);
76
76
  this.emit('websocket', filtered);
77
+ filtered.url.protocol = filtered.url.protocol === 'https:' ? 'wss:' : 'ws:';
77
78
  const ws = new WS(filtered.url, filtered.protocols, { headers: filtered.headers });
78
79
  return await new Promise((resolve, reject) => {
79
80
  let handshake;
package/lib/util.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  /// <reference types="node" />
2
+ import type { JSONValue } from './types.js';
2
3
  import type { Mode } from 'fs';
3
4
  import { setTimeout } from 'timers/promises';
4
5
  export declare function captureOutput(fn: () => void, options?: {
@@ -11,6 +12,7 @@ export declare function cliCreateFile(path: string, template: string, values?: R
11
12
  }): Promise<void>;
12
13
  export declare function cliFixPackage(): Promise<void>;
13
14
  export declare function decodeURIComponentSafe(value: string): string | null;
15
+ export declare function escapeRegExp(string: string): string;
14
16
  export declare function exceptionContext(error: Error, options?: {
15
17
  lines?: number;
16
18
  }): Promise<{
@@ -24,6 +26,7 @@ export declare function exceptionContext(error: Error, options?: {
24
26
  } | null>;
25
27
  export declare function htmlEscape(value: string | SafeString): string;
26
28
  export declare function htmlTag(name: string, attrs?: Record<string, string> | string | SafeString, content?: string | SafeString): SafeString;
29
+ export declare function jsonPointer(value: JSONValue, pointer: string): JSONValue | undefined;
27
30
  export declare function loadModules(dirs: string[]): Promise<Record<string, any>>;
28
31
  export declare const sleep: typeof setTimeout;
29
32
  export declare function tablify(rows?: string[][]): string;
package/lib/util.js CHANGED
@@ -106,6 +106,9 @@ export function decodeURIComponentSafe(value) {
106
106
  return null;
107
107
  }
108
108
  }
109
+ export function escapeRegExp(string) {
110
+ return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
111
+ }
109
112
  export async function exceptionContext(error, options = {}) {
110
113
  const stack = error.stack ?? '';
111
114
  const match = stack.split('\n')[1].match(/^\s*at .+ \(([^)]+):(\d+):(\d+)\)\s*$/);
@@ -152,6 +155,24 @@ export function htmlTag(name, attrs = {}, content = '') {
152
155
  }
153
156
  return new SafeString(result.join(''));
154
157
  }
158
+ export function jsonPointer(value, pointer) {
159
+ if (!pointer.startsWith('/'))
160
+ return pointer.length > 0 ? null : value;
161
+ let data = value;
162
+ for (const part of pointer.replace(/^\//, '').split('/')) {
163
+ const unescaped = part.replaceAll('~1', '/').replaceAll('~0', '~');
164
+ if (typeof data === 'object' && data[unescaped] !== undefined) {
165
+ data = data[unescaped];
166
+ }
167
+ else if (Array.isArray(data) && /^\d+$/.test(unescaped)) {
168
+ data = data[parseInt(unescaped)];
169
+ }
170
+ else {
171
+ return undefined;
172
+ }
173
+ }
174
+ return data;
175
+ }
155
176
  export async function loadModules(dirs) {
156
177
  const modules = {};
157
178
  for (const dir of dirs.map(path => new Path(path))) {
@@ -29,6 +29,6 @@ declare class WebSocket extends EventEmitter {
29
29
  send(message: JSONValue | Buffer): Promise<void>;
30
30
  _messageIterator(): AsyncIterableIterator<Array<JSONValue | Buffer>>;
31
31
  _safeHandler<T extends keyof WebSocketControlEvents>(event: T, ...args: Parameters<WebSocketControlEvents[T]>): void;
32
- _safeMessageHandler(message: string | Buffer): void;
32
+ _safeMessageHandler(message: Buffer, isBinary: boolean): void;
33
33
  }
34
34
  export { WebSocket };
package/lib/websocket.js CHANGED
@@ -48,10 +48,10 @@ class WebSocket extends EventEmitter {
48
48
  this.emit('error', error);
49
49
  }
50
50
  }
51
- _safeMessageHandler(message) {
51
+ _safeMessageHandler(message, isBinary) {
52
52
  try {
53
53
  if (!this.jsonMode) {
54
- this.emit('message', message);
54
+ this.emit('message', isBinary ? message : message.toString());
55
55
  }
56
56
  else {
57
57
  this.emit('message', JSON.parse(message.toString()));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mojojs/core",
3
- "version": "0.5.3",
3
+ "version": "0.5.7",
4
4
  "description": "Real-time web framework",
5
5
  "keywords": [
6
6
  "web",
@@ -61,7 +61,7 @@
61
61
  "@types/stack-utils": "^2.0.0",
62
62
  "@types/tap": "^15.0.3",
63
63
  "@types/tough-cookie": "^4.0.0",
64
- "@types/ws": "^7.4.5",
64
+ "@types/ws": "^8.2.0",
65
65
  "@typescript-eslint/eslint-plugin": "^5.1.0",
66
66
  "autocannon": "^7.3.0",
67
67
  "concurrently": "^6.2.0",
@@ -71,6 +71,7 @@
71
71
  "eslint-plugin-node": "^11.1.0",
72
72
  "eslint-plugin-prettier": "^4.0.0",
73
73
  "prettier": "2.4.1",
74
+ "tap": "^15.0.6",
74
75
  "typescript": "^=4.3.2"
75
76
  },
76
77
  "engines": {
@@ -84,15 +85,17 @@
84
85
  "cheerio": "^1.0.0-rc.10",
85
86
  "cookie": "^0.4.1",
86
87
  "ejs": "^3.1.6",
87
- "escape-string-regexp": "^5.0.0",
88
88
  "form-data": "^4.0.0",
89
89
  "js-yaml": "^4.1.0",
90
90
  "lru-cache": "^6.0.0",
91
91
  "mime-types": "^2.1.30",
92
92
  "nopt": "^5.0.0",
93
93
  "stack-utils": "^2.0.3",
94
- "tap": "^15.0.6",
95
94
  "tough-cookie": "^4.0.0",
96
- "ws": "^7.4.5"
95
+ "ws": "^8.2.3"
96
+ },
97
+ "optionalDependencies": {
98
+ "bufferutil": "^4.0.5",
99
+ "utf-8-validate": "^5.0.7"
97
100
  }
98
101
  }