@joystick.js/node-canary 0.0.0-canary.7 → 0.0.0-canary.71

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.
Files changed (36) hide show
  1. package/_package.json +2 -1
  2. package/dist/app/accounts/createMetadataTableColumns.js +12 -0
  3. package/dist/app/accounts/deleteUser.js +7 -0
  4. package/dist/app/accounts/generateSession.js +2 -4
  5. package/dist/app/accounts/getBrowserSafeUser.js +2 -1
  6. package/dist/app/accounts/hasLoginTokenExpired.js +1 -2
  7. package/dist/app/accounts/index.js +2 -0
  8. package/dist/app/accounts/signup.js +30 -4
  9. package/dist/app/databases/mongodb/index.js +1 -1
  10. package/dist/app/databases/mongodb/queries/accounts.js +7 -0
  11. package/dist/app/databases/postgresql/queries/accounts.js +3 -0
  12. package/dist/app/databases/stringToSnakeCase.js +6 -0
  13. package/dist/app/getBrowserSafeRequest.js +3 -2
  14. package/dist/app/index.js +66 -12
  15. package/dist/app/middleware/getTranslations.js +64 -0
  16. package/dist/app/middleware/index.js +4 -5
  17. package/dist/app/middleware/render.js +11 -68
  18. package/dist/app/sanitizeAPIResponse.js +1 -6
  19. package/dist/app/validateSession.js +3 -0
  20. package/dist/email/templates/reset-password.js +0 -1
  21. package/dist/index.js +7 -2
  22. package/dist/lib/escapeHTML.js +9 -0
  23. package/dist/lib/escapeKeyValuePair.js +13 -0
  24. package/dist/lib/getBuildPath.js +1 -1
  25. package/dist/lib/getSSLCertificates.js +1 -1
  26. package/dist/lib/importFile.js +7 -0
  27. package/dist/lib/log.js +0 -3
  28. package/dist/lib/nodeUrlPolyfills.js +7 -14
  29. package/dist/ssr/getAPIForDataFunctions.js +33 -0
  30. package/dist/ssr/getDataFromComponent.js +14 -0
  31. package/dist/ssr/index.js +5 -41
  32. package/package.json +2 -1
  33. package/dist/app/accounts/roles/index.test.js +0 -123
  34. package/dist/app/index.test.js +0 -575
  35. package/dist/app/middleware/sanitizeQueryParameters.js +0 -16
  36. package/dist/email/send.test.js +0 -37
package/_package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@joystick.js/node",
3
- "version": "1.0.0-beta.165",
3
+ "version": "1.0.0-beta.166",
4
4
  "developmentVersion": "1.0.0-beta.1419",
5
5
  "type": "module",
6
6
  "description": "A Node.js framework for building web apps.",
@@ -39,6 +39,7 @@
39
39
  "node-html-parser": "^5.1.0",
40
40
  "nodemailer": "^6.7.0",
41
41
  "pg": "^8.7.3",
42
+ "pg-escape": "^0.2.0",
42
43
  "process": "^0.11.10",
43
44
  "query-string": "^7.0.1",
44
45
  "sanitize-html": "^2.7.3",
@@ -0,0 +1,12 @@
1
+ import escape from "pg-escape";
2
+ var createMetadataTableColumns_default = async (usersDatabase = "", sqlizedMetadata = {}) => {
3
+ if (usersDatabase === "postgresql") {
4
+ const columns = Object.keys(sqlizedMetadata);
5
+ for (let i = 0; i < columns?.length; i += 1) {
6
+ await process.databases.postgresql.query(escape(`ALTER TABLE users ADD COLUMN IF NOT EXISTS %I TEXT;`, columns[i]));
7
+ }
8
+ }
9
+ };
10
+ export {
11
+ createMetadataTableColumns_default as default
12
+ };
@@ -0,0 +1,7 @@
1
+ import runUserQuery from "./runUserQuery.js";
2
+ var deleteUser_default = (userId = "") => {
3
+ return runUserQuery("deleteUser", { userId });
4
+ };
5
+ export {
6
+ deleteUser_default as default
7
+ };
@@ -1,9 +1,7 @@
1
1
  import dayjs from "dayjs";
2
+ import utc from "dayjs/plugin/utc.js";
2
3
  import generateId from "../../lib/generateId";
3
- if (process.env.NODE_ENV !== "test") {
4
- const utc = await import("dayjs/plugin/utc");
5
- dayjs.extend(utc.default);
6
- }
4
+ dayjs.extend(utc);
7
5
  var generateSession_default = () => {
8
6
  return {
9
7
  token: generateId(64),
@@ -1,4 +1,5 @@
1
1
  import { isObject } from "../../validation/lib/typeValidators";
2
+ import escapeKeyValuePair from "../../lib/escapeKeyValuePair.js";
2
3
  var getBrowserSafeUser_default = (user = null) => {
3
4
  if (!user || !isObject(user)) {
4
5
  return null;
@@ -16,7 +17,7 @@ var getBrowserSafeUser_default = (user = null) => {
16
17
  return fields;
17
18
  }
18
19
  }, {});
19
- return browserSafeUser;
20
+ return escapeKeyValuePair(browserSafeUser);
20
21
  };
21
22
  export {
22
23
  getBrowserSafeUser_default as default
@@ -5,8 +5,7 @@ var hasLoginTokenExpired_default = async (res, token = null, tokenExpiresAt = nu
5
5
  unsetAuthenticationCookie(res);
6
6
  return true;
7
7
  }
8
- const _dayjs = process.env.NODE_ENV === "test" ? (await import("../../tests/mocks/dayjs")).default : null;
9
- const hasExpired = process.env.NODE_ENV === "test" ? _dayjs().isAfter(_dayjs(tokenExpiresAt)) : dayjs().isAfter(dayjs(tokenExpiresAt));
8
+ const hasExpired = dayjs().isAfter(dayjs(tokenExpiresAt));
10
9
  if (hasExpired) {
11
10
  unsetAuthenticationCookie(res);
12
11
  return true;
@@ -1,6 +1,7 @@
1
1
  import _setAuthenticationCookie from "./setAuthenticationCookie.js";
2
2
  import _unsetAuthenticationCookie from "./unsetAuthenticationCookie.js";
3
3
  import defaultUserOutputFields from "./defaultUserOutputFields";
4
+ import deleteUser from "./deleteUser.js";
4
5
  import getBrowserSafeUser from "./getBrowserSafeUser";
5
6
  import login from "./login";
6
7
  import recoverPassword from "./recoverPassword";
@@ -14,6 +15,7 @@ var accounts_default = {
14
15
  _setAuthenticationCookie,
15
16
  _unsetAuthenticationCookie,
16
17
  defaultUserOutputFields,
18
+ deleteUser,
17
19
  getBrowserSafeUser,
18
20
  login,
19
21
  recoverPassword,
@@ -6,6 +6,9 @@ import { isObject } from "../../validation/lib/typeValidators";
6
6
  import getOutput from "../getOutput";
7
7
  import typesMap from "../databases/typesMap";
8
8
  import getTargetDatabaseProvider from "../databases/getTargetDatabaseProvider.js";
9
+ import stringToSnakeCase from "../databases/stringToSnakeCase.js";
10
+ import createMetadataTableColumns from "./createMetadataTableColumns.js";
11
+ import roles from "./roles/index.js";
9
12
  const addSessionToUser = (userId = null, session = null) => {
10
13
  try {
11
14
  return runUserQuery("addSession", { userId, session });
@@ -27,6 +30,16 @@ const insertUserInDatabase = async (user = {}) => {
27
30
  throw new Error(formatErrorString("signup.insertUserInDatabase", exception));
28
31
  }
29
32
  };
33
+ const sqlizeMetadata = (metadata = {}) => {
34
+ try {
35
+ return Object.entries(metadata).reduce((sqlized = {}, [key, value]) => {
36
+ sqlized[stringToSnakeCase(key)] = value;
37
+ return sqlized;
38
+ }, {});
39
+ } catch (exception) {
40
+ throw new Error(`[actionName.sqlizeMetadata] ${exception.message}`);
41
+ }
42
+ };
30
43
  const getUserToCreate = async (options = {}) => {
31
44
  try {
32
45
  const usersDatabase = getTargetDatabaseProvider("users");
@@ -40,12 +53,19 @@ const getUserToCreate = async (options = {}) => {
40
53
  if (options?.username) {
41
54
  user.username = options?.username;
42
55
  }
43
- if (options?.metadata && isObject(options.metadata) && usersDatabaseType === "sql" && options?.metadata?.language) {
44
- user.language = options?.metadata?.language;
56
+ if (options?.metadata && isObject(options.metadata) && usersDatabaseType === "sql") {
57
+ const sqlizedMetadata = sqlizeMetadata(options.metadata);
58
+ await createMetadataTableColumns(usersDatabase, sqlizedMetadata);
59
+ const { roles: [], ...restOfMetadata } = options?.metadata;
60
+ user = {
61
+ ...sqlizeMetadata(restOfMetadata),
62
+ ...user
63
+ };
45
64
  }
46
65
  if (options?.metadata && isObject(options.metadata) && usersDatabaseType === "nosql") {
66
+ const { roles: [], ...restOfMetadata } = options?.metadata;
47
67
  user = {
48
- ...options.metadata,
68
+ ...restOfMetadata || {},
49
69
  ...user
50
70
  };
51
71
  }
@@ -78,7 +98,13 @@ const signup = async (options, { resolve, reject }) => {
78
98
  const user = await getUserByUserId(userId);
79
99
  const session = generateSession();
80
100
  if (user?._id || user?.user_id) {
81
- await addSessionToUser(user._id || user?.user_id, session);
101
+ await addSessionToUser(user?._id || user?.user_id, session);
102
+ }
103
+ if (options?.metadata?.roles?.length > 0 && process.env.NODE_ENV === "test") {
104
+ for (let i = 0; i < options?.metadata?.roles?.length; i += 1) {
105
+ const role = options?.metadata?.roles[i];
106
+ roles.grant(user?._id || user?.user_id, role);
107
+ }
82
108
  }
83
109
  return resolve({
84
110
  ...session,
@@ -17,7 +17,7 @@ var mongodb_default = async (settings = {}, databasePort = 2610) => {
17
17
  const connectionOptions = {
18
18
  useNewUrlParser: true,
19
19
  useUnifiedTopology: true,
20
- ssl: !process.env.NODE_ENV?.includes("development"),
20
+ ssl: !["development", "test"].includes(process.env.NODE_ENV),
21
21
  ...settings?.options || {}
22
22
  };
23
23
  if (settings?.options?.ca) {
@@ -1,5 +1,7 @@
1
1
  import generateId from "../../../../lib/generateId";
2
2
  import dayjs from "dayjs";
3
+ import utc from "dayjs/plugin/utc.js";
4
+ dayjs.extend(utc);
3
5
  var accounts_default = {
4
6
  existingUser: async (input = {}) => {
5
7
  let existingUserWithEmailAddress;
@@ -35,6 +37,11 @@ var accounts_default = {
35
37
  }
36
38
  return null;
37
39
  },
40
+ deleteUser: async (input = {}) => {
41
+ return process.databases._users?.collection("users").deleteOne({
42
+ _id: input?.userId
43
+ });
44
+ },
38
45
  deleteOldSessions: async (input = {}) => {
39
46
  const user = await process.databases._users?.collection("users").findOne({
40
47
  _id: input?.userId
@@ -43,6 +43,9 @@ var accounts_default = {
43
43
  }
44
44
  return null;
45
45
  },
46
+ deleteUser: async (input = {}) => {
47
+ await process.databases._users?.query(`DELETE FROM users WHERE user_id = $1`, [input?.userId]);
48
+ },
46
49
  deleteOldSessions: async (input = {}) => {
47
50
  await process.databases._users?.query(`DELETE FROM users_sessions WHERE user_id = $1 AND token_expires_at::date < NOW()`, [input?.userId]);
48
51
  },
@@ -0,0 +1,6 @@
1
+ var stringToSnakeCase_default = (string = "") => {
2
+ return string?.split(/\.?(?=[A-Z])/).join("_").toLowerCase();
3
+ };
4
+ export {
5
+ stringToSnakeCase_default as default
6
+ };
@@ -1,8 +1,9 @@
1
1
  import getBrowserSafeUser from "./accounts/getBrowserSafeUser";
2
+ import escapeKeyValuePair from "../lib/escapeKeyValuePair.js";
2
3
  var getBrowserSafeRequest_default = (req = {}) => {
3
4
  const browserSafeRequest = {};
4
- browserSafeRequest.params = req.params;
5
- browserSafeRequest.query = req.query;
5
+ browserSafeRequest.params = escapeKeyValuePair(req.params);
6
+ browserSafeRequest.query = escapeKeyValuePair(req.query);
6
7
  browserSafeRequest.context = {
7
8
  user: getBrowserSafeUser(req.context.user)
8
9
  };
package/dist/app/index.js CHANGED
@@ -32,8 +32,14 @@ import Queue from "./queues/index.js";
32
32
  import readDirectory from "../lib/readDirectory.js";
33
33
  import getBuildPath from "../lib/getBuildPath.js";
34
34
  import generateMachineId from "../lib/generateMachineId.js";
35
+ import importFile from "../lib/importFile.js";
35
36
  import emitWebsocketEvent from "../websockets/emitWebsocketEvent.js";
36
37
  import getTargetDatabaseConnection from "./databases/getTargetDatabaseConnection.js";
38
+ import getAPIForDataFunctions from "../ssr/getAPIForDataFunctions.js";
39
+ import getBrowserSafeRequest from "./getBrowserSafeRequest.js";
40
+ import getDataFromComponent from "../ssr/getDataFromComponent.js";
41
+ import getTranslations from "./middleware/getTranslations.js";
42
+ import runUserQuery from "./accounts/runUserQuery.js";
37
43
  process.setMaxListeners(0);
38
44
  class App {
39
45
  constructor(options = {}) {
@@ -53,6 +59,7 @@ class App {
53
59
  this.initWebsockets(options?.websockets || {});
54
60
  this.initDevelopmentRoutes();
55
61
  this.initAccounts();
62
+ this.initTests();
56
63
  this.initDeploy();
57
64
  this.initAPI(options?.api);
58
65
  this.initRoutes(options?.routes);
@@ -143,7 +150,9 @@ class App {
143
150
  process.BUILD_ERROR = JSON.parse(message);
144
151
  }
145
152
  });
146
- console.log(`App running at: http://localhost:${express.port}`);
153
+ if (process.env.NODE_ENV !== "test") {
154
+ console.log(`App running at: http://localhost:${express.port}`);
155
+ }
147
156
  }
148
157
  setMachineId() {
149
158
  generateMachineId();
@@ -156,6 +165,33 @@ class App {
156
165
  fs.writeFileSync("./.joystick/PROCESS_ID", `${generateId(32)}`);
157
166
  }
158
167
  }
168
+ initTests() {
169
+ if (process.env.NODE_ENV === "test") {
170
+ this.express.app.get("/api/_test/bootstrap", async (req, res) => {
171
+ const buildPath = `${process.cwd()}/.joystick/build`;
172
+ const Component = req?.query?.pathToComponent ? await importFile(`${buildPath}/${req?.query?.pathToComponent}`) : null;
173
+ if (Component) {
174
+ const componentInstance = Component();
175
+ const apiForDataFunctions = await getAPIForDataFunctions(req, this?.options?.api);
176
+ const browserSafeRequest = getBrowserSafeRequest(req);
177
+ const data = await getDataFromComponent(componentInstance, apiForDataFunctions, browserSafeRequest);
178
+ const translations = await getTranslations({ build: buildPath, page: req?.query?.pathToComponent }, req);
179
+ return res.status(200).send({
180
+ data: {
181
+ [data?.componentId]: data?.data
182
+ },
183
+ translations,
184
+ req: browserSafeRequest
185
+ });
186
+ }
187
+ res.status(200).send({ data: {}, translations: {} });
188
+ });
189
+ this.express.app.delete("/api/_test/accounts", async (req, res) => {
190
+ await runUserQuery("deleteUser", { userId: req?.body?.userId });
191
+ res.status(200).send({ data: {} });
192
+ });
193
+ }
194
+ }
159
195
  initDeploy() {
160
196
  if (process.env.NODE_ENV === "production" && process.env.IS_PUSH_DEPLOYED) {
161
197
  this.express.app.get("/api/_push/pre-version", async (req, res) => {
@@ -401,7 +437,7 @@ class App {
401
437
  });
402
438
  }
403
439
  initDevelopmentRoutes() {
404
- if (process.env.NODE_ENV === "development") {
440
+ if (["development", "test"].includes(process.env.NODE_ENV)) {
405
441
  this.express.app.get("/api/_joystick/sessions", async (req, res) => {
406
442
  const sessions = Array.from(this.sessions.entries())?.reduce((acc = {}, [key, value]) => {
407
443
  acc[key] = value;
@@ -431,11 +467,20 @@ class App {
431
467
  metadata: req?.body?.metadata,
432
468
  output: req?.body?.output || defaultUserOutputFields
433
469
  });
434
- accounts._setAuthenticationCookie(res, {
435
- token: signup?.token,
436
- tokenExpiresAt: signup?.tokenExpiresAt
437
- });
438
- res.status(200).send(JSON.stringify(signup?.user || {}));
470
+ if (!process.env.NODE_ENV !== "test") {
471
+ accounts._setAuthenticationCookie(res, {
472
+ token: signup?.token,
473
+ tokenExpiresAt: signup?.tokenExpiresAt
474
+ });
475
+ }
476
+ const response = {
477
+ ...signup?.user || {}
478
+ };
479
+ if (process.env.NODE_ENV === "test") {
480
+ response.joystickToken = signup?.token;
481
+ response.joystickLoginTokenExpiresAt = signup?.tokenExpiresAt;
482
+ }
483
+ res.status(200).send(JSON.stringify(response));
439
484
  } catch (exception) {
440
485
  console.log(exception);
441
486
  return res.status(500).send(JSON.stringify({
@@ -451,11 +496,20 @@ class App {
451
496
  password: req?.body?.password,
452
497
  output: req?.body?.output || defaultUserOutputFields
453
498
  });
454
- accounts._setAuthenticationCookie(res, {
455
- token: login?.token,
456
- tokenExpiresAt: login?.tokenExpiresAt
457
- });
458
- res.status(200).send(JSON.stringify(login?.user || {}));
499
+ if (!process.env.NODE_ENV !== "test") {
500
+ accounts._setAuthenticationCookie(res, {
501
+ token: login?.token,
502
+ tokenExpiresAt: login?.tokenExpiresAt
503
+ });
504
+ }
505
+ const response = {
506
+ ...login?.user || {}
507
+ };
508
+ if (process.env.NODE_ENV === "test") {
509
+ response.joystickToken = login?.token;
510
+ response.joystickLoginTokenExpiresAt = login?.tokenExpiresAt;
511
+ }
512
+ res.status(200).send(JSON.stringify(response));
459
513
  } catch (exception) {
460
514
  console.log(exception);
461
515
  return res.status(500).send(JSON.stringify({
@@ -0,0 +1,64 @@
1
+ import fs from "fs";
2
+ import importFile from "../../lib/importFile.js";
3
+ import { isObject } from "../../validation/lib/typeValidators.js";
4
+ import settings from "../../settings/index.js";
5
+ const getTranslationsFile = async (languageFilePath = "", paths = "") => {
6
+ const languageFile = await importFile(`${paths.build}/i18n/${languageFilePath}`);
7
+ const isValidLanguageFile = languageFile && isObject(languageFile);
8
+ if (isValidLanguageFile) {
9
+ const translationsForPage = languageFile[paths.page];
10
+ return translationsForPage ? translationsForPage : languageFile;
11
+ }
12
+ return {};
13
+ };
14
+ const getLanguagePreferenceRegexes = (userLanguage = "", browserLanguages = []) => {
15
+ let languagePreferences = [];
16
+ if (userLanguage) {
17
+ languagePreferences.push(userLanguage);
18
+ }
19
+ const filteredBrowserLanguages = browserLanguages?.filter((language) => {
20
+ return !language?.includes("*");
21
+ });
22
+ languagePreferences.push(...filteredBrowserLanguages);
23
+ languagePreferences.push(settings?.config?.i18n?.defaultLanguage);
24
+ return languagePreferences?.flatMap((language) => {
25
+ const variants = [language];
26
+ if (language?.length === 2) {
27
+ variants.push(`${language.substring(0, 2)}-`);
28
+ }
29
+ if (language?.length > 2) {
30
+ variants.push(`${language?.split("-")[0]}`);
31
+ variants.push(`${language?.split("-")[0]}-`);
32
+ }
33
+ return variants;
34
+ })?.map((languageString) => {
35
+ const lastCharacter = languageString[languageString.length - 1];
36
+ if (lastCharacter === "-") {
37
+ return new RegExp(`^${languageString}[A-Z]+.js`, "g");
38
+ }
39
+ return new RegExp(`^${languageString}.js`, "g");
40
+ });
41
+ };
42
+ const parseBrowserLanguages = (languages = "") => {
43
+ const rawLanguages = languages.split(",");
44
+ return rawLanguages?.map((rawLanguage) => rawLanguage.split(";")[0]);
45
+ };
46
+ var getTranslations_default = async (paths = {}, req = {}) => {
47
+ const languageFiles = fs.readdirSync(`${paths.build}/i18n`);
48
+ const browserLanguages = parseBrowserLanguages(req?.headers["accept-language"]);
49
+ const languagePreferences = getLanguagePreferenceRegexes(req?.context?.user?.language, browserLanguages);
50
+ let matchingFile = null;
51
+ for (let i = 0; i < languagePreferences.length; i += 1) {
52
+ const languageRegex = languagePreferences[i];
53
+ const match = languageFiles.find((languageFile) => !!languageFile.match(languageRegex));
54
+ if (match) {
55
+ matchingFile = match;
56
+ break;
57
+ }
58
+ }
59
+ const translationsFile = await getTranslationsFile(matchingFile, paths);
60
+ return translationsFile;
61
+ };
62
+ export {
63
+ getTranslations_default as default
64
+ };
@@ -3,6 +3,7 @@ import compression from "compression";
3
3
  import cookieParser from "cookie-parser";
4
4
  import favicon from "serve-favicon";
5
5
  import fs from "fs";
6
+ import { __package } from "../../index.js";
6
7
  import insecure from "./insecure.js";
7
8
  import requestMethods from "./requestMethods.js";
8
9
  import bodyParser from "./bodyParser.js";
@@ -14,11 +15,10 @@ import runUserQuery from "../accounts/runUserQuery.js";
14
15
  import replaceBackslashesWithForwardSlashes from "../../lib/replaceBackslashesWithForwardSlashes.js";
15
16
  import replaceFileProtocol from "../../lib/replaceFileProtocol.js";
16
17
  import getBuildPath from "../../lib/getBuildPath.js";
17
- import sanitizeQueryParameters from "./sanitizeQueryParameters.js";
18
18
  import session from "./session.js";
19
19
  import csp from "./csp.js";
20
20
  const cwd = replaceFileProtocol(replaceBackslashesWithForwardSlashes(process.cwd()));
21
- const faviconPath = process.env.NODE_ENV === "test" ? `${cwd}/src/tests/mocks/app/public/favicon.ico` : "public/favicon.ico";
21
+ const faviconPath = "public/favicon.ico";
22
22
  var middleware_default = ({
23
23
  app,
24
24
  port,
@@ -41,7 +41,6 @@ var middleware_default = ({
41
41
  }
42
42
  next();
43
43
  });
44
- app.use(sanitizeQueryParameters);
45
44
  app.use(requestMethods);
46
45
  if (cspConfig) {
47
46
  app.use((req, res, next) => csp(req, res, next, cspConfig));
@@ -54,7 +53,7 @@ var middleware_default = ({
54
53
  });
55
54
  app.use("/_joystick/utils/process.js", (_req, res) => {
56
55
  res.set("Content-Type", "text/javascript");
57
- const processPolyfill = fs.readFileSync(`${cwd}/node_modules/@joystick.js/node/dist/app/utils/process.js`, "utf-8");
56
+ const processPolyfill = fs.readFileSync(`${__package}/app/utils/process.js`, "utf-8");
58
57
  res.send(processPolyfill.replace("${NODE_ENV}", process.env.NODE_ENV));
59
58
  });
60
59
  app.use("/_joystick/index.client.js", express.static(`${buildPath}index.client.js`, {
@@ -65,7 +64,7 @@ var middleware_default = ({
65
64
  app.use("/_joystick/ui", express.static(`${buildPath}ui`, { eTag: false, maxAge: "0" }));
66
65
  app.use("/_joystick/hmr/client.js", (_req, res) => {
67
66
  res.set("Content-Type", "text/javascript");
68
- const hmrClient = fs.readFileSync(`${cwd}/node_modules/@joystick.js/node/dist/app/middleware/hmr/client.js`, "utf-8");
67
+ const hmrClient = fs.readFileSync(`${__package}/app/middleware/hmr/client.js`, "utf-8");
69
68
  res.send(hmrClient.replace("${process.env.PORT}", parseInt(process.env.PORT, 10) + 1));
70
69
  });
71
70
  app.use(favicon(faviconPath));
@@ -8,6 +8,10 @@ import generateErrorPage from "../../lib/generateErrorPage.js";
8
8
  import replaceFileProtocol from "../../lib/replaceFileProtocol.js";
9
9
  import replaceBackslashesWithForwardSlashes from "../../lib/replaceBackslashesWithForwardSlashes.js";
10
10
  import getBuildPath from "../../lib/getBuildPath.js";
11
+ import escapeHTML from "../../lib/escapeHTML.js";
12
+ import escapeKeyValuePair from "../../lib/escapeKeyValuePair.js";
13
+ import importFile from "../../lib/importFile.js";
14
+ import getTranslations from "./getTranslations.js";
11
15
  const generateHash = (input = "") => {
12
16
  return crypto.createHash("sha256").update(input).digest("hex");
13
17
  };
@@ -65,71 +69,12 @@ const getCachedHTML = ({ cachePath, cacheFileName, currentDiff }) => {
65
69
  const getUrl = (request = {}) => {
66
70
  const [path = null] = request.url?.split("?");
67
71
  return {
68
- params: request.params,
69
- query: request.query,
70
- route: request.route.path,
71
- path
72
+ params: escapeKeyValuePair(request.params),
73
+ query: escapeKeyValuePair(request.query),
74
+ route: escapeHTML(request.route.path),
75
+ path: escapeHTML(path)
72
76
  };
73
77
  };
74
- const getFile = async (buildPath = "") => {
75
- const file = await import(buildPath);
76
- return file.default;
77
- };
78
- const getTranslationsFile = async (languageFilePath = "", paths = "") => {
79
- const languageFile = await getFile(`${paths.build}/i18n/${languageFilePath}`);
80
- const isValidLanguageFile = languageFile && isObject(languageFile);
81
- if (isValidLanguageFile) {
82
- const translationsForPage = languageFile[paths.page];
83
- return translationsForPage ? translationsForPage : languageFile;
84
- }
85
- return {};
86
- };
87
- const getTranslations = async (paths = {}, languagePreferences = []) => {
88
- const languageFiles = fs.readdirSync(`${paths.build}/i18n`);
89
- let matchingFile = null;
90
- for (let i = 0; i < languagePreferences.length; i += 1) {
91
- const languageRegex = languagePreferences[i];
92
- const match = languageFiles.find((languageFile) => !!languageFile.match(languageRegex));
93
- if (match) {
94
- matchingFile = match;
95
- break;
96
- }
97
- }
98
- const translationsFile = await getTranslationsFile(matchingFile, paths);
99
- return translationsFile;
100
- };
101
- const getLanguagePreferenceRegexes = (userLanguage = "", browserLanguages = []) => {
102
- let languagePreferences = [];
103
- if (userLanguage) {
104
- languagePreferences.push(userLanguage);
105
- }
106
- const filteredBrowserLanguages = browserLanguages?.filter((language) => {
107
- return !language?.includes("*");
108
- });
109
- languagePreferences.push(...filteredBrowserLanguages);
110
- languagePreferences.push(settings?.config?.i18n?.defaultLanguage);
111
- return languagePreferences?.flatMap((language) => {
112
- const variants = [language];
113
- if (language?.length === 2) {
114
- variants.push(`${language.substring(0, 2)}-`);
115
- }
116
- if (language?.length > 2) {
117
- variants.push(`${language?.split("-")[0]}`);
118
- variants.push(`${language?.split("-")[0]}-`);
119
- }
120
- return variants;
121
- })?.map((languageString) => {
122
- const lastCharacter = languageString[languageString.length - 1];
123
- if (lastCharacter === "-") {
124
- return new RegExp(`^${languageString}[A-Z]+.js`, "g");
125
- }
126
- return new RegExp(`^${languageString}.js`, "g");
127
- });
128
- };
129
- const parseBrowserLanguages = (languages = "") => {
130
- const rawLanguages = languages.split(",");
131
- return rawLanguages?.map((rawLanguage) => rawLanguage.split(";")[0]);
132
- };
133
78
  var render_default = (req, res, next, appInstance = {}) => {
134
79
  res.render = async function(path = "", options = {}) {
135
80
  const urlFormattedForCache = req?.url?.split("/")?.filter((part) => !!part)?.join("_");
@@ -167,13 +112,11 @@ var render_default = (req, res, next, appInstance = {}) => {
167
112
  return res.send(cachedHTML);
168
113
  }
169
114
  }
170
- const pageFile = await getFile(pagePath);
115
+ const pageFile = await importFile(pagePath);
171
116
  const Page = pageFile;
172
- const layoutFile = layoutPath ? await getFile(layoutPath) : null;
117
+ const layoutFile = layoutPath ? await importFile(layoutPath) : null;
173
118
  const Layout = layoutFile;
174
- const browserLanguages = parseBrowserLanguages(req?.headers["accept-language"]);
175
- const languagePreferenceRegexes = getLanguagePreferenceRegexes(req?.context?.user?.language, browserLanguages);
176
- const translations = await getTranslations({ build: buildPath, page: path }, languagePreferenceRegexes);
119
+ const translations = await getTranslations({ build: buildPath, page: path }, req);
177
120
  const url = getUrl(req);
178
121
  const props = { ...options?.props || {} };
179
122
  if (layoutPath && fs.existsSync(layoutPath)) {
@@ -1,10 +1,5 @@
1
1
  import util from "util";
2
- import { HTML_ENTITY_MAP } from "../lib/constants.js";
3
- const escapeHTML = (string = "") => {
4
- return String(string).replace(/[&<>"'`=\/]/g, function(match) {
5
- return HTML_ENTITY_MAP[match];
6
- });
7
- };
2
+ import escapeHTML from "../lib/escapeHTML.js";
8
3
  const sanitizeAPIResponse = (data = null) => {
9
4
  let sanitizedData = data;
10
5
  if (!util.isString(sanitizedData) && !util.isObject(sanitizedData) && !Array.isArray(sanitizedData)) {
@@ -3,6 +3,9 @@ var validateSession_default = (req = null, res = null, sessions = null) => {
3
3
  const sessionToken = req?.cookies?.joystickSession;
4
4
  const csrfToken = req?.headers["x-joystick-csrf"];
5
5
  const session = sessions?.get(sessionToken);
6
+ if (csrfToken === "joystick_test") {
7
+ return true;
8
+ }
6
9
  if (!session || session && session.csrf !== csrfToken) {
7
10
  res.status(403).send(JSON.stringify({
8
11
  errors: [formatAPIError(new Error("Unauthorized request."))]
@@ -1,6 +1,5 @@
1
1
  import ui from "@joystick.js/ui";
2
2
  const ResetPassword = ui.component({
3
- id: process.env.NODE_ENV === "test" ? "testComponent1234" : null,
4
3
  render: ({ props }) => {
5
4
  return `
6
5
  <p>A password reset was requested for this email address (${props.emailAddress}). If you requested this reset, click the link below to reset your password:</p>
package/dist/index.js CHANGED
@@ -1,5 +1,7 @@
1
- import sanitizeHTML from "sanitize-html";
2
1
  import fs from "fs";
2
+ import { fileURLToPath } from "url";
3
+ import { dirname } from "path";
4
+ import sanitizeHTML from "sanitize-html";
3
5
  import _accounts from "./app/accounts";
4
6
  import _action from "./action/index.js";
5
7
  import _websockets from "./websockets";
@@ -27,16 +29,18 @@ const sanitize = {
27
29
  allowedTags: sanitizeHTML.defaults.allowedTags
28
30
  }
29
31
  };
32
+ const currentFilePath = fileURLToPath(import.meta.url);
33
+ const __package = dirname(currentFilePath);
30
34
  const __filename = nodeUrlPolyfills.__filename;
31
35
  const __dirname = nodeUrlPolyfills.__dirname;
32
36
  const id = generateId;
33
37
  const origin = getOrigin();
34
38
  const settings = loadSettings();
35
- console.log("HERE", fs.readFileSync(__dirname("dist/app/utils/process.js")?.replace("file://", ""), "utf-8"));
36
39
  global.joystick = {
37
40
  id: generateId,
38
41
  emitters: {},
39
42
  settings,
43
+ __package,
40
44
  __dirname,
41
45
  __filename
42
46
  };
@@ -58,6 +62,7 @@ var src_default = {
58
62
  export {
59
63
  __dirname,
60
64
  __filename,
65
+ __package,
61
66
  accounts,
62
67
  action,
63
68
  src_default as default,
@@ -0,0 +1,9 @@
1
+ import { HTML_ENTITY_MAP } from "./constants.js";
2
+ var escapeHTML_default = (string = "") => {
3
+ return String(string).replace(/[&<>"'`=]/g, function(match) {
4
+ return HTML_ENTITY_MAP[match];
5
+ });
6
+ };
7
+ export {
8
+ escapeHTML_default as default
9
+ };
@@ -0,0 +1,13 @@
1
+ import escapeHTML from "./escapeHTML.js";
2
+ var escapeKeyValuePair_default = (target = {}) => {
3
+ const parameters = Object.entries(target || {});
4
+ for (let i = 0; i < parameters?.length; i += 1) {
5
+ const [key, value] = parameters[i];
6
+ delete target[key];
7
+ target[escapeHTML(key)] = escapeHTML(value);
8
+ }
9
+ return target;
10
+ };
11
+ export {
12
+ escapeKeyValuePair_default as default
13
+ };