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

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 (33) hide show
  1. package/_package.json +2 -1
  2. package/dist/app/accounts/createMetadataTableColumns.js +12 -0
  3. package/dist/app/accounts/generateSession.js +2 -4
  4. package/dist/app/accounts/getBrowserSafeUser.js +2 -1
  5. package/dist/app/accounts/hasLoginTokenExpired.js +1 -2
  6. package/dist/app/accounts/signup.js +30 -4
  7. package/dist/app/databases/mongodb/index.js +1 -1
  8. package/dist/app/databases/mongodb/queries/accounts.js +2 -0
  9. package/dist/app/databases/stringToSnakeCase.js +6 -0
  10. package/dist/app/getBrowserSafeRequest.js +3 -2
  11. package/dist/app/index.js +61 -12
  12. package/dist/app/middleware/getTranslations.js +64 -0
  13. package/dist/app/middleware/index.js +4 -5
  14. package/dist/app/middleware/render.js +11 -68
  15. package/dist/app/sanitizeAPIResponse.js +1 -6
  16. package/dist/app/validateSession.js +3 -0
  17. package/dist/email/templates/reset-password.js +0 -1
  18. package/dist/index.js +7 -2
  19. package/dist/lib/escapeHTML.js +9 -0
  20. package/dist/lib/escapeKeyValuePair.js +13 -0
  21. package/dist/lib/getBuildPath.js +1 -1
  22. package/dist/lib/getSSLCertificates.js +1 -1
  23. package/dist/lib/importFile.js +7 -0
  24. package/dist/lib/log.js +0 -3
  25. package/dist/lib/nodeUrlPolyfills.js +7 -14
  26. package/dist/ssr/getAPIForDataFunctions.js +33 -0
  27. package/dist/ssr/getDataFromComponent.js +14 -0
  28. package/dist/ssr/index.js +5 -41
  29. package/package.json +2 -1
  30. package/dist/app/accounts/roles/index.test.js +0 -123
  31. package/dist/app/index.test.js +0 -575
  32. package/dist/app/middleware/sanitizeQueryParameters.js +0 -16
  33. 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
+ };
@@ -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;
@@ -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;
@@ -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,13 @@ 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";
37
42
  process.setMaxListeners(0);
38
43
  class App {
39
44
  constructor(options = {}) {
@@ -53,6 +58,7 @@ class App {
53
58
  this.initWebsockets(options?.websockets || {});
54
59
  this.initDevelopmentRoutes();
55
60
  this.initAccounts();
61
+ this.initTests();
56
62
  this.initDeploy();
57
63
  this.initAPI(options?.api);
58
64
  this.initRoutes(options?.routes);
@@ -143,7 +149,9 @@ class App {
143
149
  process.BUILD_ERROR = JSON.parse(message);
144
150
  }
145
151
  });
146
- console.log(`App running at: http://localhost:${express.port}`);
152
+ if (process.env.NODE_ENV !== "test") {
153
+ console.log(`App running at: http://localhost:${express.port}`);
154
+ }
147
155
  }
148
156
  setMachineId() {
149
157
  generateMachineId();
@@ -156,6 +164,29 @@ class App {
156
164
  fs.writeFileSync("./.joystick/PROCESS_ID", `${generateId(32)}`);
157
165
  }
158
166
  }
167
+ initTests() {
168
+ if (process.env.NODE_ENV === "test") {
169
+ this.express.app.get("/api/_test/bootstrap", async (req, res) => {
170
+ const buildPath = `${process.cwd()}/.joystick/build`;
171
+ const Component = req?.query?.pathToComponent ? await importFile(`${buildPath}/${req?.query?.pathToComponent}`) : null;
172
+ if (Component) {
173
+ const componentInstance = Component();
174
+ const apiForDataFunctions = await getAPIForDataFunctions(req, this?.options?.api);
175
+ const browserSafeRequest = getBrowserSafeRequest(req);
176
+ const data = await getDataFromComponent(componentInstance, apiForDataFunctions, browserSafeRequest);
177
+ const translations = await getTranslations({ build: buildPath, page: req?.query?.pathToComponent }, req);
178
+ return res.status(200).send({
179
+ data: {
180
+ [data?.componentId]: data?.data
181
+ },
182
+ translations,
183
+ req: browserSafeRequest
184
+ });
185
+ }
186
+ res.status(200).send({ data: {}, translations: {} });
187
+ });
188
+ }
189
+ }
159
190
  initDeploy() {
160
191
  if (process.env.NODE_ENV === "production" && process.env.IS_PUSH_DEPLOYED) {
161
192
  this.express.app.get("/api/_push/pre-version", async (req, res) => {
@@ -401,7 +432,7 @@ class App {
401
432
  });
402
433
  }
403
434
  initDevelopmentRoutes() {
404
- if (process.env.NODE_ENV === "development") {
435
+ if (["development", "test"].includes(process.env.NODE_ENV)) {
405
436
  this.express.app.get("/api/_joystick/sessions", async (req, res) => {
406
437
  const sessions = Array.from(this.sessions.entries())?.reduce((acc = {}, [key, value]) => {
407
438
  acc[key] = value;
@@ -431,11 +462,20 @@ class App {
431
462
  metadata: req?.body?.metadata,
432
463
  output: req?.body?.output || defaultUserOutputFields
433
464
  });
434
- accounts._setAuthenticationCookie(res, {
435
- token: signup?.token,
436
- tokenExpiresAt: signup?.tokenExpiresAt
437
- });
438
- res.status(200).send(JSON.stringify(signup?.user || {}));
465
+ if (!process.env.NODE_ENV !== "test") {
466
+ accounts._setAuthenticationCookie(res, {
467
+ token: signup?.token,
468
+ tokenExpiresAt: signup?.tokenExpiresAt
469
+ });
470
+ }
471
+ const response = {
472
+ ...signup?.user || {}
473
+ };
474
+ if (process.env.NODE_ENV === "test") {
475
+ response.joystickToken = signup?.token;
476
+ response.joystickLoginTokenExpiresAt = signup?.tokenExpiresAt;
477
+ }
478
+ res.status(200).send(JSON.stringify(response));
439
479
  } catch (exception) {
440
480
  console.log(exception);
441
481
  return res.status(500).send(JSON.stringify({
@@ -451,11 +491,20 @@ class App {
451
491
  password: req?.body?.password,
452
492
  output: req?.body?.output || defaultUserOutputFields
453
493
  });
454
- accounts._setAuthenticationCookie(res, {
455
- token: login?.token,
456
- tokenExpiresAt: login?.tokenExpiresAt
457
- });
458
- res.status(200).send(JSON.stringify(login?.user || {}));
494
+ if (!process.env.NODE_ENV !== "test") {
495
+ accounts._setAuthenticationCookie(res, {
496
+ token: login?.token,
497
+ tokenExpiresAt: login?.tokenExpiresAt
498
+ });
499
+ }
500
+ const response = {
501
+ ...login?.user || {}
502
+ };
503
+ if (process.env.NODE_ENV === "test") {
504
+ response.joystickToken = login?.token;
505
+ response.joystickLoginTokenExpiresAt = login?.tokenExpiresAt;
506
+ }
507
+ res.status(200).send(JSON.stringify(response));
459
508
  } catch (exception) {
460
509
  console.log(exception);
461
510
  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
+ };
@@ -1,6 +1,6 @@
1
1
  import fs from "fs";
2
2
  var getBuildPath_default = () => {
3
- if (process.env.NODE_ENV === "development" || fs.existsSync(".joystick/build")) {
3
+ if (["development", "test"].includes(process.env.NODE_ENV) || fs.existsSync(".joystick/build")) {
4
4
  return ".joystick/build/";
5
5
  }
6
6
  return "";
@@ -6,7 +6,7 @@ var getSSLCertificates_default = (ssl = null) => {
6
6
  const keyPath = process.env.IS_PUSH_DEPLOYED ? pushKeyPath : ssl?.key || null;
7
7
  const certExists = fs.existsSync(certPath);
8
8
  const keyExists = fs.existsSync(keyPath);
9
- if (process.env.NODE_ENV === "development" || !certExists || !keyExists) {
9
+ if (["development", "test"].includes(process.env.NODE_ENV) || !certExists || !keyExists) {
10
10
  return null;
11
11
  }
12
12
  return {
@@ -0,0 +1,7 @@
1
+ var importFile_default = async (buildPath = "") => {
2
+ const file = await import(buildPath);
3
+ return file?.default;
4
+ };
5
+ export {
6
+ importFile_default as default
7
+ };
package/dist/lib/log.js CHANGED
@@ -1,9 +1,6 @@
1
1
  import chalk from "chalk";
2
2
  import rainbowRoad from "./rainbowRoad.js";
3
3
  var log_default = (message = "", options = {}) => {
4
- if (process.env.NODE_ENV === "test") {
5
- return;
6
- }
7
4
  const colors = {
8
5
  info: "blue",
9
6
  success: "green",