@joystick.js/node-canary 0.0.0-canary.33 → 0.0.0-canary.331

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 (92) hide show
  1. package/_package.json +2 -3
  2. package/dist/action/class.js +21 -0
  3. package/dist/api/get.js +15 -13
  4. package/dist/api/set.js +15 -13
  5. package/dist/app/accounts/createMetadataTableColumns.js +12 -0
  6. package/dist/app/accounts/deleteUser.js +7 -0
  7. package/dist/app/accounts/generateSession.js +2 -4
  8. package/dist/app/accounts/getBrowserSafeUser.js +5 -2
  9. package/dist/app/accounts/hasLoginTokenExpired.js +1 -2
  10. package/dist/app/accounts/index.js +2 -0
  11. package/dist/app/accounts/login.js +6 -0
  12. package/dist/app/accounts/recoverPassword.js +3 -0
  13. package/dist/app/accounts/resetPassword.js +6 -0
  14. package/dist/app/accounts/signup.js +50 -7
  15. package/dist/app/accounts/verifyEmail.js +3 -0
  16. package/dist/app/cluster.js +26 -0
  17. package/dist/app/databases/generate_sql_from_object.js +60 -0
  18. package/dist/app/databases/mongodb/buildConnectionString.js +1 -1
  19. package/dist/app/databases/mongodb/createAccountsIndexes.js +18 -0
  20. package/dist/app/databases/mongodb/createSessionsIndexes.js +10 -0
  21. package/dist/app/databases/mongodb/index.js +1 -1
  22. package/dist/app/databases/mongodb/queries/accounts.js +8 -1
  23. package/dist/app/databases/mongodb/queries/queues.js +51 -28
  24. package/dist/app/databases/mongodb/queries/sessions.js +26 -0
  25. package/dist/app/databases/postgresql/createSessionsIndexes.js +10 -0
  26. package/dist/app/databases/postgresql/createSessionsTables.js +14 -0
  27. package/dist/app/databases/postgresql/handleCleanupQueues.js +36 -0
  28. package/dist/app/databases/postgresql/index.js +88 -2
  29. package/dist/app/databases/postgresql/queries/accounts.js +5 -2
  30. package/dist/app/databases/postgresql/queries/queues.js +83 -38
  31. package/dist/app/databases/postgresql/queries/sessions.js +43 -0
  32. package/dist/app/databases/queryMap.js +6 -2
  33. package/dist/app/databases/stringToSnakeCase.js +6 -0
  34. package/dist/app/getBrowserSafeRequest.js +3 -2
  35. package/dist/app/index.js +229 -78
  36. package/dist/app/initExpress.js +1 -1
  37. package/dist/app/middleware/csp.js +2 -2
  38. package/dist/app/middleware/getTranslations.js +64 -0
  39. package/dist/app/middleware/get_insecure_landing_page_html.js +71 -0
  40. package/dist/app/middleware/hmr/client.js +13 -9
  41. package/dist/app/middleware/index.js +6 -5
  42. package/dist/app/middleware/insecure.js +3 -4
  43. package/dist/app/middleware/render.js +8 -66
  44. package/dist/app/middleware/session.js +12 -11
  45. package/dist/app/queues/index.js +82 -32
  46. package/dist/app/registerGetters.js +5 -6
  47. package/dist/app/registerSetters.js +5 -6
  48. package/dist/app/runGetter.js +17 -5
  49. package/dist/app/runSessionQuery.js +15 -0
  50. package/dist/app/runSetter.js +17 -5
  51. package/dist/app/sanitizeAPIResponse.js +1 -1
  52. package/dist/app/validateInstanceToken.js +16 -0
  53. package/dist/app/validateSession.js +8 -3
  54. package/dist/app/validateUploaderOptions.js +3 -3
  55. package/dist/app/validateUploads.js +12 -1
  56. package/dist/email/send.js +7 -1
  57. package/dist/email/templates/reset-password.js +0 -1
  58. package/dist/fixture/index.js +40 -0
  59. package/dist/index.js +19 -0
  60. package/dist/lib/escapeKeyValuePair.js +13 -0
  61. package/dist/lib/formatAPIError.js +0 -1
  62. package/dist/lib/getBuildPath.js +1 -1
  63. package/dist/lib/getSSLCertificates.js +3 -3
  64. package/dist/lib/importFile.js +7 -0
  65. package/dist/lib/isValidJSONString.js +1 -1
  66. package/dist/lib/log.js +0 -3
  67. package/dist/lib/objectToSQLKeysString.js +1 -1
  68. package/dist/lib/objectToSQLValuesString.js +1 -1
  69. package/dist/lib/serializeQueryParameters.js +1 -1
  70. package/dist/lib/timestamps.js +47 -0
  71. package/dist/lib/wait.js +8 -0
  72. package/dist/push/logs/index.js +35 -17
  73. package/dist/settings/load.js +3 -5
  74. package/dist/ssr/compileCSS.js +4 -4
  75. package/dist/ssr/findComponentInTree.js +1 -1
  76. package/dist/ssr/getAPIForDataFunctions.js +35 -0
  77. package/dist/ssr/getDataFromComponent.js +15 -0
  78. package/dist/ssr/index.js +19 -45
  79. package/dist/ssr/replaceWhenTags.js +2 -3
  80. package/dist/ssr/setHeadTagsInHTML.js +3 -3
  81. package/dist/test/index.js +9 -0
  82. package/dist/test/trackFunctionCall.js +17 -0
  83. package/dist/validation/inputWithSchema/index.js +3 -3
  84. package/dist/validation/schema/index.js +5 -5
  85. package/dist/websockets/index.js +4 -0
  86. package/getSanitizedContext.js +43 -0
  87. package/package.json +2 -2
  88. package/dist/app/accounts/roles/index.test.js +0 -123
  89. package/dist/app/index.test.js +0 -575
  90. package/dist/app/middleware/sanitizeRequestParameters.js +0 -26
  91. package/dist/email/send.test.js +0 -37
  92. package/dist/validation/index.test.js +0 -463
@@ -1,15 +1,19 @@
1
1
  import mongodbAccountsQueries from "./mongodb/queries/accounts";
2
2
  import mongodbQueuesQueries from "./mongodb/queries/queues";
3
+ import mongodbSessionsQueries from "./mongodb/queries/sessions";
3
4
  import postgresqlAccountsQueries from "./postgresql/queries/accounts";
4
5
  import postgresqlQueuesQueries from "./postgresql/queries/queues";
6
+ import postgresqlSessionsQueries from "./postgresql/queries/sessions";
5
7
  var queryMap_default = {
6
8
  mongodb: {
7
9
  accounts: mongodbAccountsQueries,
8
- queues: mongodbQueuesQueries
10
+ queues: mongodbQueuesQueries,
11
+ sessions: mongodbSessionsQueries
9
12
  },
10
13
  postgresql: {
11
14
  accounts: postgresqlAccountsQueries,
12
- queues: postgresqlQueuesQueries
15
+ queues: postgresqlQueuesQueries,
16
+ sessions: postgresqlSessionsQueries
13
17
  }
14
18
  };
15
19
  export {
@@ -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
@@ -6,6 +6,8 @@ import queryString from "query-string";
6
6
  import multer from "multer";
7
7
  import cron from "node-cron";
8
8
  import { execSync } from "child_process";
9
+ import dayjs from "dayjs";
10
+ import cluster from "./cluster.js";
9
11
  import initExpress from "./initExpress.js";
10
12
  import handleProcessErrors from "./handleProcessErrors";
11
13
  import registerGetters from "./registerGetters.js";
@@ -25,49 +27,58 @@ import runUploader from "./runUploader";
25
27
  import generateId from "../lib/generateId.js";
26
28
  import getOutput from "./getOutput.js";
27
29
  import defaultUserOutputFields from "./accounts/defaultUserOutputFields.js";
30
+ import createMongoDBAccountsIndexes from "./databases/mongodb/createAccountsIndexes";
31
+ import createMongoDBSessionsIndexes from "./databases/mongodb/createSessionsIndexes";
28
32
  import createPostgreSQLAccountsTables from "./databases/postgresql/createAccountsTables";
29
33
  import createPostgreSQLAccountsIndexes from "./databases/postgresql/createAccountsIndexes";
34
+ import createPostgreSQLSessionsTables from "./databases/postgresql/createSessionsTables";
35
+ import createPostgreSQLSessionsIndexes from "./databases/postgresql/createSessionsIndexes";
30
36
  import loadSettings from "../settings/load.js";
31
37
  import Queue from "./queues/index.js";
32
38
  import readDirectory from "../lib/readDirectory.js";
33
39
  import getBuildPath from "../lib/getBuildPath.js";
34
40
  import generateMachineId from "../lib/generateMachineId.js";
41
+ import importFile from "../lib/importFile.js";
35
42
  import emitWebsocketEvent from "../websockets/emitWebsocketEvent.js";
36
43
  import getTargetDatabaseConnection from "./databases/getTargetDatabaseConnection.js";
44
+ import getAPIForDataFunctions from "../ssr/getAPIForDataFunctions.js";
45
+ import getBrowserSafeRequest from "./getBrowserSafeRequest.js";
46
+ import getDataFromComponent from "../ssr/getDataFromComponent.js";
47
+ import getTranslations from "./middleware/getTranslations.js";
48
+ import runUserQuery from "./accounts/runUserQuery.js";
49
+ import wait from "../lib/wait.js";
50
+ import trackFunctionCall from "../test/trackFunctionCall.js";
51
+ import getBrowserSafeUser from "./accounts/getBrowserSafeUser.js";
37
52
  process.setMaxListeners(0);
38
53
  class App {
39
54
  constructor(options = {}) {
40
55
  this.setMachineId();
41
56
  this.setJoystickProcessId();
42
57
  handleProcessErrors(options?.events);
43
- const HMRSessions = JSON.parse(process.env.HMR_SESSIONS || "{}");
44
- this.sessions = new Map(HMRSessions ? Object.entries(HMRSessions) : []);
45
58
  this.databases = [];
46
59
  this.express = {};
47
60
  this.options = options || {};
61
+ process.joystick = {
62
+ _app: {
63
+ options
64
+ }
65
+ };
48
66
  }
49
67
  async start(options = {}) {
50
68
  await this.invalidateCache();
51
69
  this.databases = await this.loadDatabases();
52
70
  this.express = initExpress(this.onStartApp, options, this);
53
71
  this.initWebsockets(options?.websockets || {});
54
- this.initDevelopmentRoutes();
55
- this.initAccounts();
56
- this.initDeploy();
72
+ this.initAccounts(options?.accounts);
73
+ this.initTests();
74
+ this.initPush();
57
75
  this.initAPI(options?.api);
58
- this.initRoutes(options?.routes);
59
76
  this.initUploaders(options?.uploaders);
77
+ this.initIndexes(options?.indexes);
60
78
  this.initFixtures(options?.fixtures);
61
79
  this.initQueues(options?.queues);
62
80
  this.initCronJobs(options?.cronJobs);
63
- if (process.env.NODE_ENV === "development") {
64
- process.on("message", (message) => {
65
- const parsedMessage = typeof message === "string" ? JSON.parse(message) : message;
66
- if (parsedMessage?.type === " RESTART_SERVER") {
67
- this.express?.server?.close();
68
- }
69
- });
70
- }
81
+ this.initRoutes(options?.routes);
71
82
  }
72
83
  async invalidateCache() {
73
84
  const uiFiles = fs.existsSync(`${getBuildPath()}ui`) ? await readDirectory(`${getBuildPath()}ui`) : [];
@@ -85,9 +96,21 @@ class App {
85
96
  const hasQueuesDatabase = settings?.config?.databases?.some((database = {}) => {
86
97
  return !!database?.queues;
87
98
  });
99
+ const hasSessionsDatabase = settings?.config?.databases?.some((database = {}) => {
100
+ return !!database?.sessions;
101
+ });
102
+ const hasMongoDBUsersDatabase = settings?.config?.databases?.some((database = {}) => {
103
+ return database?.provider === "mongodb" && database?.users;
104
+ });
105
+ const hasMongoDBSessionsDatabase = settings?.config?.databases?.some((database = {}) => {
106
+ return database?.provider === "mongodb" && database?.sessions;
107
+ });
88
108
  const hasPostgreSQLUsersDatabase = settings?.config?.databases?.some((database = {}) => {
89
109
  return database?.provider === "postgresql" && database?.users;
90
110
  });
111
+ const hasPostgreSQLSessionsDatabase = settings?.config?.databases?.some((database = {}) => {
112
+ return database?.provider === "postgresql" && database?.sessions;
113
+ });
91
114
  const databases = settings?.config?.databases?.map((database) => {
92
115
  return {
93
116
  provider: database?.provider,
@@ -114,12 +137,22 @@ class App {
114
137
  ...process.databases || {},
115
138
  postgresql: !hasMultipleOfProvider ? {
116
139
  ...postgresql?.pool,
117
- query: postgresql?.query
140
+ add_column: postgresql?.add_column,
141
+ create_table: postgresql?.create_table,
142
+ insert: postgresql?.insert,
143
+ query: postgresql?.query,
144
+ select: postgresql?.select,
145
+ update: postgresql?.update
118
146
  } : {
119
147
  ...process?.databases?.postgresql || {},
120
148
  [database?.settings?.name || `postgresql_${databasePort}`]: {
121
149
  ...postgresql?.pool,
122
- query: postgresql?.query
150
+ add_column: postgresql?.add_column,
151
+ create_table: postgresql?.create_table,
152
+ insert: postgresql?.insert,
153
+ query: postgresql?.query,
154
+ select: postgresql?.select,
155
+ update: postgresql?.update
123
156
  }
124
157
  }
125
158
  };
@@ -131,19 +164,40 @@ class App {
131
164
  if (hasQueuesDatabase) {
132
165
  process.databases._queues = getTargetDatabaseConnection("queues")?.connection;
133
166
  }
167
+ if (hasSessionsDatabase) {
168
+ process.databases._sessions = getTargetDatabaseConnection("sessions")?.connection;
169
+ }
170
+ if (hasMongoDBUsersDatabase) {
171
+ await createMongoDBAccountsIndexes();
172
+ }
173
+ if (hasMongoDBSessionsDatabase) {
174
+ await createMongoDBSessionsIndexes();
175
+ }
134
176
  if (hasPostgreSQLUsersDatabase) {
135
177
  await createPostgreSQLAccountsTables();
136
178
  await createPostgreSQLAccountsIndexes();
137
179
  }
180
+ if (hasPostgreSQLSessionsDatabase) {
181
+ await createPostgreSQLSessionsTables();
182
+ await createPostgreSQLSessionsIndexes();
183
+ }
138
184
  return process.databases;
139
185
  }
140
- onStartApp(express = {}) {
186
+ onStartApp(express = {}, joystick_app_instance = {}) {
141
187
  process.on("message", (message) => {
142
188
  if (typeof message === "string") {
143
- process.BUILD_ERROR = JSON.parse(message);
189
+ const parsed_message = JSON.parse(message);
190
+ if (parsed_message?.type === "BUILD_ERROR") {
191
+ process.BUILD_ERROR = JSON.parse(message);
192
+ }
193
+ if (parsed_message?.type === "RESTART_SERVER") {
194
+ console.log("HANDLE RESTART");
195
+ }
144
196
  }
145
197
  });
146
- console.log(`App running at: http://localhost:${express.port}`);
198
+ if (process.env.NODE_ENV !== "test") {
199
+ console.log(`App running at: http://localhost:${express.port}`);
200
+ }
147
201
  }
148
202
  setMachineId() {
149
203
  generateMachineId();
@@ -156,10 +210,78 @@ class App {
156
210
  fs.writeFileSync("./.joystick/PROCESS_ID", `${generateId(32)}`);
157
211
  }
158
212
  }
159
- initDeploy() {
160
- if (process.env.NODE_ENV === "production" && process.env.IS_PUSH_DEPLOYED) {
213
+ initTests() {
214
+ if (process.env.NODE_ENV === "test") {
215
+ this.express.app.get("/api/_test/bootstrap", async (req, res) => {
216
+ const buildPath = `${process.cwd()}/.joystick/build`;
217
+ const Component = req?.query?.pathToComponent ? await importFile(`${buildPath}/${req?.query?.pathToComponent}`) : null;
218
+ if (Component) {
219
+ const componentInstance = Component();
220
+ const apiForDataFunctions = await getAPIForDataFunctions(req, this?.options?.api);
221
+ const browserSafeRequest = getBrowserSafeRequest(req);
222
+ const browserSafeUser = getBrowserSafeUser(req?.context?.user);
223
+ const data = await getDataFromComponent(componentInstance, apiForDataFunctions, browserSafeUser, browserSafeRequest);
224
+ const translations = await getTranslations({ build: buildPath, page: req?.query?.pathToComponent }, req);
225
+ const settings = loadSettings();
226
+ return res.status(200).send({
227
+ data: {
228
+ [data?.componentId]: data?.data
229
+ },
230
+ req: browserSafeRequest,
231
+ settings,
232
+ translations
233
+ });
234
+ }
235
+ res.status(200).send({ data: {}, translations: {} });
236
+ });
237
+ this.express.app.get("/api/_test/process", async (req, res) => {
238
+ res.status(200).send({
239
+ test: process?.test || {}
240
+ });
241
+ });
242
+ this.express.app.post("/api/_test/accounts/signup", async (req, res) => {
243
+ const existingUser = await runUserQuery("user", { emailAddress: req?.body?.emailAddress });
244
+ if (existingUser) {
245
+ await runUserQuery("deleteUser", { userId: existingUser?._id || existingUser?.user_id });
246
+ }
247
+ const signup = await accounts.signup({
248
+ emailAddress: req?.body?.emailAddress,
249
+ password: req?.body?.password,
250
+ metadata: req?.body?.metadata,
251
+ output: req?.body?.output || defaultUserOutputFields
252
+ });
253
+ res.status(200).send(JSON.stringify({
254
+ ...signup?.user || {},
255
+ joystickLoginToken: signup?.token,
256
+ joystickLoginTokenExpiresAt: signup?.tokenExpiresAt
257
+ }));
258
+ });
259
+ this.express.app.delete("/api/_test/accounts", async (req, res) => {
260
+ await runUserQuery("deleteUser", { userId: req?.body?.userId });
261
+ res.status(200).send({ data: {} });
262
+ });
263
+ this.express.app.post("/api/_test/queues", async (req, res) => {
264
+ const queue = process?.queues[req?.body?.queue];
265
+ const job = this?.options?.queues[req?.body?.queue]?.jobs[req?.body?.job];
266
+ if (!queue) {
267
+ return res.status(404).send({ status: 404, error: `Queue ${req?.body?.queue} not found.` });
268
+ }
269
+ if (!job) {
270
+ return res.status(400).send({ status: 400, error: `Couldn't find a job called ${req?.body?.job} for the ${req?.body?.queue} queue.` });
271
+ }
272
+ await queue.handleNextJob({
273
+ _id: "joystick_test",
274
+ job: req?.body?.job,
275
+ payload: req?.body?.payload
276
+ });
277
+ res.status(200).send({ data: {} });
278
+ });
279
+ }
280
+ }
281
+ initPush() {
282
+ if (process.env.NODE_ENV !== "development" && process.env.IS_PUSH_DEPLOYED) {
161
283
  this.express.app.get("/api/_push/pre-version", async (req, res) => {
162
- const instanceToken = fs.readFileSync("/root/token.txt", "utf-8");
284
+ const instanceToken = fs.readFileSync("/root/push/instance_token.txt", "utf-8");
163
285
  if (req?.headers["x-instance-token"] === instanceToken?.replace("\n", "")) {
164
286
  if (this.options?.events?.onBeforeDeployment && typeof this.options?.events?.onBeforeDeployment === "function") {
165
287
  await this.options.events.onBeforeDeployment(req?.query?.instance || "", req?.query?.version);
@@ -170,28 +292,12 @@ class App {
170
292
  return res.status(403).send("Sorry, you must pass a valid instance token to access this endpoint.");
171
293
  });
172
294
  this.express.app.get("/api/_push/health", async (req, res) => {
173
- const instanceToken = fs.readFileSync("/root/token.txt", "utf-8");
295
+ const instanceToken = fs.readFileSync("/root/push/instance_token.txt", "utf-8");
174
296
  if (req?.headers["x-instance-token"] === instanceToken?.replace("\n", "")) {
175
297
  return res.status(200).send("ok");
176
298
  }
177
299
  return res.status(403).send("Sorry, you must pass a valid instance token to access this endpoint.");
178
300
  });
179
- this.express.app.get("/api/_push/logs", async (req, res) => {
180
- const instanceToken = fs.readFileSync("/root/token.txt", "utf-8");
181
- if (req?.headers["x-instance-token"] === instanceToken?.replace("\n", "")) {
182
- const logs = execSync(`export NODE_ENV=production && instance logs${req?.query?.before ? ` --before ${req?.query?.before}` : ""}${req?.query?.after ? ` --after ${req?.query?.after}` : ""}`);
183
- return res.status(200).send(logs);
184
- }
185
- return res.status(403).send("Sorry, you must pass a valid instance token to access this endpoint.");
186
- });
187
- this.express.app.get("/api/_push/metrics", async (req, res) => {
188
- const instanceToken = fs.readFileSync("/root/token.txt", "utf-8");
189
- if (req?.headers["x-instance-token"] === instanceToken?.replace("\n", "")) {
190
- const metrics = execSync(`export NODE_ENV=production && instance metrics`);
191
- return res.status(200).send(metrics);
192
- }
193
- return res.status(403).send("Sorry, you must pass a valid instance token to access this endpoint.");
194
- });
195
301
  }
196
302
  }
197
303
  initAPI(api = {}) {
@@ -199,15 +305,15 @@ class App {
199
305
  const setters = api?.setters;
200
306
  const options = api?.options;
201
307
  const context = api?.context;
202
- if (getters && isObject(getters) && Object.keys(getters).length > 0) {
203
- registerGetters(this.express, Object.entries(getters), context, options, this);
308
+ if (getters && isObject(getters) && Object.keys(getters || {}).length > 0) {
309
+ registerGetters(this.express, Object.entries(getters || {}), context, options, this);
204
310
  }
205
- if (setters && isObject(setters) && Object.keys(setters).length > 0) {
206
- registerSetters(this.express, Object.entries(setters), context, options, this);
311
+ if (setters && isObject(setters) && Object.keys(setters || {}).length > 0) {
312
+ registerSetters(this.express, Object.entries(setters || {}), context, options, this);
207
313
  }
208
314
  }
209
315
  initRoutes(routes = {}) {
210
- Object.entries(routes).forEach(([path, callback]) => {
316
+ Object.entries(routes || {}).forEach(([path, callback]) => {
211
317
  const isObjectBasedRoute = path && callback && typeof callback === "object";
212
318
  const isFunctionBasedRoute = path && callback && typeof callback === "function";
213
319
  const method = callback?.method?.toLowerCase();
@@ -331,20 +437,29 @@ class App {
331
437
  path: "/api/_websockets/uploaders"
332
438
  })
333
439
  },
334
- ...Object.entries(userWebsockets).reduce((definitions = {}, [userWebsocketName, userWebsocketDefinition]) => {
440
+ ...Object.entries(userWebsockets || {}).reduce((definitions = {}, [userWebsocketName, userWebsocketDefinition]) => {
335
441
  definitions[userWebsocketName] = {
336
442
  server: new WebSocket.WebSocketServer({
337
443
  noServer: true,
338
444
  path: `/api/_websockets/${userWebsocketName}`
339
445
  }),
340
- onOpen: userWebsocketDefinition?.onOpen || null,
341
- onMessage: userWebsocketDefinition?.onMessage || null,
342
- onClose: userWebsocketDefinition?.onClose || null
446
+ onOpen: (...args) => {
447
+ trackFunctionCall(`node.websockets.${userWebsocketName}.onOpen`, args);
448
+ return userWebsocketDefinition?.onOpen ? userWebsocketDefinition?.onOpen(...args) : null;
449
+ },
450
+ onMessage: (...args) => {
451
+ trackFunctionCall(`node.websockets.${userWebsocketName}.onMessage`, args);
452
+ return userWebsocketDefinition?.onMessage ? userWebsocketDefinition?.onMessage(...args) : null;
453
+ },
454
+ onClose: (...args) => {
455
+ trackFunctionCall(`node.websockets.${userWebsocketName}.onClose`, args);
456
+ return userWebsocketDefinition?.onClose ? userWebsocketDefinition.onClose(...args) : null;
457
+ }
343
458
  };
344
459
  return definitions;
345
460
  }, {})
346
461
  };
347
- Object.entries(websocketServers).forEach(([websocketName, websocketDefinition]) => {
462
+ Object.entries(websocketServers || {}).forEach(([websocketName, websocketDefinition]) => {
348
463
  websocketDefinition.server.on("connection", function connection(websocketConnection, connectionRequest) {
349
464
  try {
350
465
  const [_path, params] = connectionRequest?.url?.split("?");
@@ -400,18 +515,7 @@ class App {
400
515
  }
401
516
  });
402
517
  }
403
- initDevelopmentRoutes() {
404
- if (process.env.NODE_ENV === "development") {
405
- this.express.app.get("/api/_joystick/sessions", async (req, res) => {
406
- const sessions = Array.from(this.sessions.entries())?.reduce((acc = {}, [key, value]) => {
407
- acc[key] = value;
408
- return acc;
409
- }, {});
410
- res.status(200).send(JSON.stringify(sessions));
411
- });
412
- }
413
- }
414
- initAccounts() {
518
+ initAccounts(options = {}) {
415
519
  this.express.app.get("/api/_accounts/authenticated", async (req, res) => {
416
520
  const loginTokenHasExpired = await hasLoginTokenExpired(res, req?.cookies?.joystickLoginToken, req?.cookies?.joystickLoginTokenExpiresAt);
417
521
  const status = !loginTokenHasExpired ? 200 : 401;
@@ -431,11 +535,20 @@ class App {
431
535
  metadata: req?.body?.metadata,
432
536
  output: req?.body?.output || defaultUserOutputFields
433
537
  });
434
- accounts._setAuthenticationCookie(res, {
435
- token: signup?.token,
436
- tokenExpiresAt: signup?.tokenExpiresAt
437
- });
438
- res.status(200).send(JSON.stringify(signup?.user || {}));
538
+ if (!process.env.NODE_ENV !== "test") {
539
+ accounts._setAuthenticationCookie(res, {
540
+ token: signup?.token,
541
+ tokenExpiresAt: signup?.tokenExpiresAt
542
+ });
543
+ }
544
+ const response = {
545
+ ...signup?.user || {}
546
+ };
547
+ if (process.env.NODE_ENV === "test") {
548
+ response.joystickToken = signup?.token;
549
+ response.joystickLoginTokenExpiresAt = signup?.tokenExpiresAt;
550
+ }
551
+ res.status(200).send(JSON.stringify(response));
439
552
  } catch (exception) {
440
553
  console.log(exception);
441
554
  return res.status(500).send(JSON.stringify({
@@ -451,11 +564,20 @@ class App {
451
564
  password: req?.body?.password,
452
565
  output: req?.body?.output || defaultUserOutputFields
453
566
  });
454
- accounts._setAuthenticationCookie(res, {
455
- token: login?.token,
456
- tokenExpiresAt: login?.tokenExpiresAt
457
- });
458
- res.status(200).send(JSON.stringify(login?.user || {}));
567
+ if (!process.env.NODE_ENV !== "test") {
568
+ accounts._setAuthenticationCookie(res, {
569
+ token: login?.token,
570
+ tokenExpiresAt: login?.tokenExpiresAt
571
+ });
572
+ }
573
+ const response = {
574
+ ...login?.user || {}
575
+ };
576
+ if (process.env.NODE_ENV === "test") {
577
+ response.joystickToken = login?.token;
578
+ response.joystickLoginTokenExpiresAt = login?.tokenExpiresAt;
579
+ }
580
+ res.status(200).send(JSON.stringify(response));
459
581
  } catch (exception) {
460
582
  console.log(exception);
461
583
  return res.status(500).send(JSON.stringify({
@@ -465,8 +587,10 @@ class App {
465
587
  });
466
588
  this.express.app.post("/api/_accounts/logout", async (req, res) => {
467
589
  try {
468
- this.sessions.delete(req?.context?.user?._id || req?.context?.user?.user_id);
469
590
  accounts._unsetAuthenticationCookie(res);
591
+ if (typeof options?.onLogout === "function") {
592
+ options.onLogout(req?.context?.user);
593
+ }
470
594
  res.status(200).send(JSON.stringify({}));
471
595
  } catch (exception) {
472
596
  console.log(exception);
@@ -523,7 +647,7 @@ class App {
523
647
  }
524
648
  initUploaders(uploaders = {}) {
525
649
  const { app } = this.express;
526
- Object.entries(uploaders).forEach(([uploaderName, uploaderOptions]) => {
650
+ Object.entries(uploaders || {}).forEach(([uploaderName, uploaderOptions]) => {
527
651
  const errors = validateUploaderOptions(uploaderOptions);
528
652
  if (errors?.length > 0) {
529
653
  log(errors, {
@@ -561,6 +685,11 @@ class App {
561
685
  req,
562
686
  uploads: validatedUploads
563
687
  });
688
+ trackFunctionCall(`node.uploaders.${uploaderName}.onBeforeUpload`, [{
689
+ input,
690
+ req,
691
+ uploads: validatedUploads
692
+ }]);
564
693
  }
565
694
  const fileSize = parseInt(req.headers["content-length"], 10);
566
695
  const providers = uploaderOptions?.providers?.includes("local") ? uploaderOptions?.providers.length : uploaderOptions?.providers?.length + 1;
@@ -578,6 +707,11 @@ class App {
578
707
  req,
579
708
  uploads
580
709
  });
710
+ trackFunctionCall(`node.uploaders.${uploaderName}.onAfterUpload`, [{
711
+ input,
712
+ req,
713
+ uploads
714
+ }]);
581
715
  }
582
716
  res.status(200).send(JSON.stringify({
583
717
  status: 200,
@@ -604,6 +738,11 @@ class App {
604
738
  }
605
739
  });
606
740
  }
741
+ initIndexes(indexes = null) {
742
+ if (indexes && typeof indexes === "function") {
743
+ indexes();
744
+ }
745
+ }
607
746
  initFixtures(fixtures = null) {
608
747
  if (fixtures && typeof fixtures === "function") {
609
748
  fixtures();
@@ -611,7 +750,7 @@ class App {
611
750
  }
612
751
  initQueues(queues = null) {
613
752
  if (queues && typeof queues === "object" && !Array.isArray(queues)) {
614
- const queueDefinitions = Object.entries(queues);
753
+ const queueDefinitions = Object.entries(queues || {});
615
754
  for (let i = 0; i < queueDefinitions.length; i += 1) {
616
755
  const [queueName, queueOptions] = queueDefinitions[i];
617
756
  process.queues = {
@@ -623,7 +762,7 @@ class App {
623
762
  }
624
763
  initCronJobs(cronJobs = null) {
625
764
  if (cronJobs && typeof cronJobs === "object" && !Array.isArray(cronJobs)) {
626
- const cronJobDefinitions = Object.entries(cronJobs);
765
+ const cronJobDefinitions = Object.entries(cronJobs || {});
627
766
  for (let i = 0; i < cronJobDefinitions.length; i += 1) {
628
767
  const [cronJobName, cronJobOptions] = cronJobDefinitions[i];
629
768
  if (cronJobOptions?.schedule && cronJobOptions?.run && typeof cronJobOptions?.run === "function") {
@@ -641,11 +780,23 @@ class App {
641
780
  }
642
781
  }
643
782
  }
783
+ ;
784
+ const handleStartApp = async (options = {}) => {
785
+ const app = new App(options);
786
+ await app.start(options);
787
+ return app;
788
+ };
644
789
  var app_default = (options = {}) => {
645
790
  return new Promise(async (resolve) => {
646
- const app = new App(options);
647
- await app.start(options);
648
- return resolve(app.express);
791
+ if (options?.cluster) {
792
+ cluster(async () => {
793
+ const app = await handleStartApp(options);
794
+ return resolve(app.express);
795
+ });
796
+ } else {
797
+ const app = await handleStartApp(options);
798
+ return resolve(app.express);
799
+ }
649
800
  });
650
801
  };
651
802
  export {
@@ -29,7 +29,7 @@ var initExpress_default = (onInit = () => {
29
29
  server
30
30
  };
31
31
  if (onInit) {
32
- onInit(instance);
32
+ onInit(instance, appInstance);
33
33
  }
34
34
  return instance;
35
35
  } catch (exception) {
@@ -27,7 +27,7 @@ var csp_default = (req, res, next, config = null) => {
27
27
  directiveDefaults["script-src"].push("'unsafe-eval'");
28
28
  directiveDefaults["connect-src"].push("ws:");
29
29
  }
30
- const directiveNames = Object.keys(directiveDefaults);
30
+ const directiveNames = Object.keys(directiveDefaults || {});
31
31
  for (let i = 0; i < directiveNames?.length; i += 1) {
32
32
  const directive = directiveNames[i];
33
33
  directiveDefaults[directive] = [
@@ -36,7 +36,7 @@ var csp_default = (req, res, next, config = null) => {
36
36
  ...directives[directive] || []
37
37
  ];
38
38
  }
39
- const csp = Object.keys(directiveDefaults).map((source) => {
39
+ const csp = Object.keys(directiveDefaults || {}).map((source) => {
40
40
  return `${source} ${directiveDefaults[source].join(" ")}`;
41
41
  })?.join("; ");
42
42
  res.setHeader("Content-Security-Policy", csp);
@@ -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
+ };