@balena/pinejs 16.0.0-build--batch-09b8c466600d7df13e6df3eacabaf463d96f652f-1 → 16.0.0-build-fisehara-update-sbvr-types-b58e72aca3193964afac96c955fde178fe39d077-1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (141) hide show
  1. package/.pinejs-cache.json +1 -1
  2. package/.versionbot/CHANGELOG.yml +2164 -15
  3. package/CHANGELOG.md +815 -3
  4. package/Gruntfile.ts +9 -6
  5. package/README.md +10 -0
  6. package/build/browser.ts +2 -2
  7. package/build/config.ts +1 -1
  8. package/build/module.ts +2 -2
  9. package/build/server.ts +2 -2
  10. package/docker-compose.npm-test.yml +21 -3
  11. package/out/bin/abstract-sql-compiler.js +5 -5
  12. package/out/bin/abstract-sql-compiler.js.map +1 -1
  13. package/out/bin/odata-compiler.js +10 -10
  14. package/out/bin/odata-compiler.js.map +1 -1
  15. package/out/bin/sbvr-compiler.js +34 -11
  16. package/out/bin/sbvr-compiler.js.map +1 -1
  17. package/out/bin/utils.js +25 -2
  18. package/out/bin/utils.js.map +1 -1
  19. package/out/config-loader/config-loader.d.ts +4 -2
  20. package/out/config-loader/config-loader.js +54 -13
  21. package/out/config-loader/config-loader.js.map +1 -1
  22. package/out/config-loader/env.d.ts +2 -1
  23. package/out/config-loader/env.js +5 -2
  24. package/out/config-loader/env.js.map +1 -1
  25. package/out/data-server/sbvr-server.d.ts +1 -1
  26. package/out/data-server/sbvr-server.js +3 -1
  27. package/out/data-server/sbvr-server.js.map +1 -1
  28. package/out/database-layer/db.js +40 -14
  29. package/out/database-layer/db.js.map +1 -1
  30. package/out/express-emulator/express.js +5 -3
  31. package/out/express-emulator/express.js.map +1 -1
  32. package/out/http-transactions/transactions.d.ts +1 -1
  33. package/out/http-transactions/transactions.js +10 -5
  34. package/out/http-transactions/transactions.js.map +1 -1
  35. package/out/migrator/async.js +32 -5
  36. package/out/migrator/async.js.map +1 -1
  37. package/out/migrator/sync.d.ts +2 -1
  38. package/out/migrator/sync.js +29 -3
  39. package/out/migrator/sync.js.map +1 -1
  40. package/out/migrator/utils.d.ts +6 -3
  41. package/out/migrator/utils.js +30 -4
  42. package/out/migrator/utils.js.map +1 -1
  43. package/out/odata-metadata/odata-metadata-generator.js +4 -1
  44. package/out/odata-metadata/odata-metadata-generator.js.map +1 -1
  45. package/out/passport-pinejs/mount-login-router.d.ts +3 -0
  46. package/out/passport-pinejs/mount-login-router.js +65 -0
  47. package/out/passport-pinejs/mount-login-router.js.map +1 -0
  48. package/out/passport-pinejs/passport-pinejs.d.ts +2 -1
  49. package/out/passport-pinejs/passport-pinejs.js +28 -2
  50. package/out/passport-pinejs/passport-pinejs.js.map +1 -1
  51. package/out/pinejs-session-store/pinejs-session-store.js +30 -7
  52. package/out/pinejs-session-store/pinejs-session-store.js.map +1 -1
  53. package/out/sbvr-api/abstract-sql.d.ts +2 -2
  54. package/out/sbvr-api/abstract-sql.js +35 -9
  55. package/out/sbvr-api/abstract-sql.js.map +1 -1
  56. package/out/sbvr-api/cached-compile.js +9 -6
  57. package/out/sbvr-api/cached-compile.js.map +1 -1
  58. package/out/sbvr-api/common-types.d.ts +1 -1
  59. package/out/sbvr-api/control-flow.js +5 -2
  60. package/out/sbvr-api/control-flow.js.map +1 -1
  61. package/out/sbvr-api/express-extension.d.ts +10 -7
  62. package/out/sbvr-api/express-extension.js +1 -0
  63. package/out/sbvr-api/hooks.d.ts +5 -1
  64. package/out/sbvr-api/hooks.js +12 -10
  65. package/out/sbvr-api/hooks.js.map +1 -1
  66. package/out/sbvr-api/odata-response.d.ts +5 -2
  67. package/out/sbvr-api/odata-response.js +36 -6
  68. package/out/sbvr-api/odata-response.js.map +1 -1
  69. package/out/sbvr-api/permissions.d.ts +6 -7
  70. package/out/sbvr-api/permissions.js +69 -38
  71. package/out/sbvr-api/permissions.js.map +1 -1
  72. package/out/sbvr-api/sbvr-utils.d.ts +21 -10
  73. package/out/sbvr-api/sbvr-utils.js +128 -124
  74. package/out/sbvr-api/sbvr-utils.js.map +1 -1
  75. package/out/sbvr-api/translations.d.ts +2 -2
  76. package/out/sbvr-api/translations.js +17 -10
  77. package/out/sbvr-api/translations.js.map +1 -1
  78. package/out/sbvr-api/uri-parser.d.ts +10 -12
  79. package/out/sbvr-api/uri-parser.js +46 -19
  80. package/out/sbvr-api/uri-parser.js.map +1 -1
  81. package/out/server-glue/global-ext.d.ts +2 -1
  82. package/out/server-glue/module.d.ts +3 -1
  83. package/out/server-glue/module.js +40 -13
  84. package/out/server-glue/module.js.map +1 -1
  85. package/out/server-glue/sbvr-loader.js.map +1 -1
  86. package/out/server-glue/server.js +31 -39
  87. package/out/server-glue/server.js.map +1 -1
  88. package/out/webresource-handler/handlers/NoopHandler.d.ts +7 -0
  89. package/out/webresource-handler/handlers/NoopHandler.js +20 -0
  90. package/out/webresource-handler/handlers/NoopHandler.js.map +1 -0
  91. package/out/webresource-handler/handlers/S3Handler.d.ts +28 -0
  92. package/out/webresource-handler/handlers/S3Handler.js +97 -0
  93. package/out/webresource-handler/handlers/S3Handler.js.map +1 -0
  94. package/out/webresource-handler/handlers/index.d.ts +2 -0
  95. package/out/webresource-handler/handlers/index.js +19 -0
  96. package/out/webresource-handler/handlers/index.js.map +1 -0
  97. package/out/webresource-handler/index.d.ts +34 -0
  98. package/out/webresource-handler/index.js +307 -0
  99. package/out/webresource-handler/index.js.map +1 -0
  100. package/package.json +68 -62
  101. package/src/bin/abstract-sql-compiler.ts +7 -9
  102. package/src/bin/odata-compiler.ts +12 -15
  103. package/src/bin/sbvr-compiler.ts +14 -18
  104. package/src/bin/utils.ts +1 -1
  105. package/src/config-loader/config-loader.ts +44 -10
  106. package/src/config-loader/env.ts +1 -1
  107. package/src/data-server/sbvr-server.js +3 -1
  108. package/src/database-layer/db.ts +23 -19
  109. package/src/express-emulator/express.js +5 -3
  110. package/src/extended-sbvr-parser/extended-sbvr-parser.ts +1 -1
  111. package/src/http-transactions/transactions.js +10 -5
  112. package/src/migrator/async.ts +7 -6
  113. package/src/migrator/sync.ts +10 -7
  114. package/src/migrator/utils.ts +11 -5
  115. package/src/odata-metadata/odata-metadata-generator.ts +2 -2
  116. package/src/passport-pinejs/mount-login-router.ts +46 -0
  117. package/src/passport-pinejs/passport-pinejs.ts +7 -3
  118. package/src/pinejs-session-store/pinejs-session-store.ts +6 -6
  119. package/src/sbvr-api/abstract-sql.ts +5 -5
  120. package/src/sbvr-api/cached-compile.ts +1 -2
  121. package/src/sbvr-api/common-types.ts +1 -1
  122. package/src/sbvr-api/control-flow.ts +1 -1
  123. package/src/sbvr-api/express-extension.ts +12 -8
  124. package/src/sbvr-api/hooks.ts +11 -11
  125. package/src/sbvr-api/odata-response.ts +56 -9
  126. package/src/sbvr-api/permissions.ts +44 -35
  127. package/src/sbvr-api/sbvr-utils.ts +117 -165
  128. package/src/sbvr-api/translations.ts +9 -6
  129. package/src/sbvr-api/uri-parser.ts +25 -30
  130. package/src/server-glue/global-ext.d.ts +2 -1
  131. package/src/server-glue/module.ts +8 -2
  132. package/src/server-glue/sbvr-loader.ts +1 -1
  133. package/src/server-glue/server.ts +11 -49
  134. package/src/webresource-handler/handlers/NoopHandler.ts +21 -0
  135. package/src/webresource-handler/handlers/S3Handler.ts +143 -0
  136. package/src/webresource-handler/handlers/index.ts +2 -0
  137. package/src/webresource-handler/index.ts +450 -0
  138. package/tsconfig.dev.json +2 -1
  139. package/tsconfig.json +1 -1
  140. package/typings/lf-to-abstract-sql.d.ts +1 -1
  141. package/typings/memoizee.d.ts +3 -4
@@ -1,24 +1,21 @@
1
1
  import { version, writeAll, writeSqlModel } from './utils';
2
-
3
- import type * as SbvrUtils from '../sbvr-api/sbvr-utils';
4
-
5
2
  import { program } from 'commander';
6
3
  import * as fs from 'fs';
7
4
 
8
5
  const getSE = (inputFile: string) => fs.readFileSync(inputFile, 'utf8');
9
6
 
10
- const parse = (inputFile: string, outputFile?: string) => {
11
- const { generateLfModel } =
12
- require('../sbvr-api/sbvr-utils') as typeof SbvrUtils;
7
+ const parse = async (inputFile: string, outputFile?: string) => {
8
+ const { generateLfModel } = await import('../sbvr-api/sbvr-utils.js');
13
9
  const seModel = getSE(inputFile);
14
10
  const result = generateLfModel(seModel);
15
11
  const json = JSON.stringify(result, null, 2);
16
12
  writeAll(json, outputFile);
17
13
  };
18
14
 
19
- const transform = (inputFile: string, outputFile?: string) => {
20
- const { generateLfModel, generateAbstractSqlModel } =
21
- require('../sbvr-api/sbvr-utils') as typeof SbvrUtils;
15
+ const transform = async (inputFile: string, outputFile?: string) => {
16
+ const { generateLfModel, generateAbstractSqlModel } = await import(
17
+ '../sbvr-api/sbvr-utils.js'
18
+ );
22
19
  const seModel = getSE(inputFile);
23
20
  const lfModel = generateLfModel(seModel);
24
21
  const result = generateAbstractSqlModel(lfModel);
@@ -26,9 +23,8 @@ const transform = (inputFile: string, outputFile?: string) => {
26
23
  writeAll(json, outputFile);
27
24
  };
28
25
 
29
- const runCompile = (inputFile: string, outputFile?: string) => {
30
- const { generateModels } =
31
- require('../sbvr-api/sbvr-utils') as typeof SbvrUtils;
26
+ const runCompile = async (inputFile: string, outputFile?: string) => {
27
+ const { generateModels } = await import('../sbvr-api/sbvr-utils.js');
32
28
  const seModel = getSE(inputFile);
33
29
  const models = generateModels(
34
30
  { apiRoot: 'sbvr-compiler', modelText: seModel },
@@ -38,16 +34,16 @@ const runCompile = (inputFile: string, outputFile?: string) => {
38
34
  writeSqlModel(models.sql, outputFile);
39
35
  };
40
36
 
41
- const generateTypes = (inputFile: string, outputFile?: string) => {
42
- const { generateModels } =
43
- require('../sbvr-api/sbvr-utils') as typeof SbvrUtils;
37
+ const generateTypes = async (inputFile: string, outputFile?: string) => {
38
+ const { generateModels } = await import('../sbvr-api/sbvr-utils.js');
44
39
  const seModel = getSE(inputFile);
45
40
  const models = generateModels(
46
41
  { apiRoot: 'sbvr-compiler', modelText: seModel },
47
42
  program.opts().engine,
48
43
  );
49
- const { abstractSqlToTypescriptTypes } =
50
- require('@balena/abstract-sql-to-typescript') as typeof import('@balena/abstract-sql-to-typescript');
44
+ const { abstractSqlToTypescriptTypes } = await import(
45
+ '@balena/abstract-sql-to-typescript'
46
+ );
51
47
  const types = abstractSqlToTypescriptTypes(models.abstractSql);
52
48
 
53
49
  writeAll(types, outputFile);
@@ -93,4 +89,4 @@ if (process.argv.length === 2) {
93
89
  program.help();
94
90
  }
95
91
 
96
- program.parse(process.argv);
92
+ void program.parseAsync(process.argv);
package/src/bin/utils.ts CHANGED
@@ -10,7 +10,6 @@ import * as fs from 'fs';
10
10
  import * as path from 'path';
11
11
  import '../server-glue/sbvr-loader';
12
12
 
13
- // tslint:disable:no-var-requires
14
13
  export const { version } = JSON.parse(
15
14
  fs.readFileSync(require.resolve('../../package.json'), 'utf8'),
16
15
  );
@@ -83,6 +82,7 @@ export const getAbstractSqlModelFromFile = (
83
82
  throw new Error('Unrecognised config file');
84
83
  }
85
84
  const { generateLfModel, generateAbstractSqlModel } =
85
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
86
86
  require('../sbvr-api/sbvr-utils') as typeof SbvrUtils;
87
87
  let lfModel;
88
88
  try {
@@ -12,8 +12,8 @@ import type {
12
12
  } from '../sbvr-api/common-types';
13
13
 
14
14
  import {
15
- Migration,
16
- Migrations,
15
+ type Migration,
16
+ type Migrations,
17
17
  defaultMigrationCategory,
18
18
  MigrationCategories,
19
19
  isSyncMigration,
@@ -21,13 +21,20 @@ import {
21
21
  } from '../migrator/utils';
22
22
 
23
23
  import * as fs from 'fs';
24
- import * as _ from 'lodash';
24
+ import _ from 'lodash';
25
25
  import * as path from 'path';
26
26
 
27
27
  import * as sbvrUtils from '../sbvr-api/sbvr-utils';
28
28
 
29
29
  import * as permissions from '../sbvr-api/permissions';
30
- import { AliasValidNodeType } from '../sbvr-api/translations';
30
+ import {
31
+ getDefaultHandler,
32
+ getUploaderMiddlware,
33
+ type WebResourceHandler,
34
+ setupUploadHooks,
35
+ setupWebresourceHandler,
36
+ } from '../webresource-handler';
37
+ import type { AliasValidNodeType } from '../sbvr-api/translations';
31
38
 
32
39
  export type SetupFunction = (
33
40
  app: Express.Application,
@@ -64,6 +71,7 @@ export interface User {
64
71
  export interface Config {
65
72
  models: Model[];
66
73
  users?: User[];
74
+ webResourceHandler?: WebResourceHandler;
67
75
  }
68
76
 
69
77
  const getOrCreate = async (
@@ -195,16 +203,37 @@ export const setup = (app: Express.Application) => {
195
203
  );
196
204
 
197
205
  const apiRoute = `/${model.apiRoot}/*`;
206
+ const webResourceHandler =
207
+ data.webResourceHandler ?? getDefaultHandler();
208
+
209
+ setupWebresourceHandler(webResourceHandler);
210
+ const fileUploadMiddleware =
211
+ getUploaderMiddlware(webResourceHandler);
198
212
  app
199
213
  .route(apiRoute)
200
214
  .options((_req, res) => res.status(200).end())
201
215
  .get(sbvrUtils.handleODataRequest)
202
216
  .put(sbvrUtils.handleODataRequest)
203
- .post(sbvrUtils.handleODataRequest)
204
- .patch(sbvrUtils.handleODataRequest)
217
+ .post(fileUploadMiddleware, sbvrUtils.handleODataRequest)
218
+ .patch(fileUploadMiddleware, sbvrUtils.handleODataRequest)
205
219
  .merge(sbvrUtils.handleODataRequest)
206
220
  .delete(sbvrUtils.handleODataRequest);
207
221
 
222
+ const loadedModel = sbvrUtils.getModel(model.apiRoot!);
223
+ if (translateTo == null) {
224
+ const resourceNames = Object.values(
225
+ loadedModel.abstractSql.tables,
226
+ )
227
+ .filter((table) =>
228
+ table.fields.some((f) => f.dataType === 'WebResource'),
229
+ )
230
+ .map((table) => table.name);
231
+
232
+ for (const resource of new Set(resourceNames)) {
233
+ setupUploadHooks(webResourceHandler, model.apiRoot!, resource);
234
+ }
235
+ }
236
+
208
237
  console.info(
209
238
  'Successfully executed ' + model.modelName + ' model.',
210
239
  );
@@ -251,7 +280,8 @@ export const setup = (app: Express.Application) => {
251
280
  };
252
281
  const loadConfigFile = async (configPath: string): Promise<Config> => {
253
282
  console.info('Loading config:', configPath);
254
- return await import(configPath);
283
+ const { default: configObj } = await import(configPath);
284
+ return configObj;
255
285
  };
256
286
 
257
287
  const loadApplicationConfig = async (config: string | Config | undefined) => {
@@ -350,9 +380,12 @@ export const setup = (app: Express.Application) => {
350
380
  switch (fileExtension) {
351
381
  case '.coffee':
352
382
  case '.ts':
353
- case '.js':
354
- const loadeMigration = nodeRequire(filePath);
355
- const migration = loadeMigration.default ?? loadeMigration;
383
+ case '.js': {
384
+ const { default: loadedMigration } = await import(filePath);
385
+ const migration =
386
+ // This second check for `.default` is because it might be a commonjs file that was compiled from typescript with `export default`
387
+ // and typescript will add the `.default` but so will the `import()`
388
+ loadedMigration.default ?? loadedMigration;
356
389
 
357
390
  if (
358
391
  !isAsyncMigration(migration) &&
@@ -365,6 +398,7 @@ export const setup = (app: Express.Application) => {
365
398
 
366
399
  assignMigrationWithCategory(migrationKey, migration);
367
400
  break;
401
+ }
368
402
  case '.sql':
369
403
  if (migrationCategory === MigrationCategories.async) {
370
404
  console.error(
@@ -50,7 +50,7 @@ export const cache = {
50
50
  };
51
51
 
52
52
  import { boolVar } from '@balena/env-parsing';
53
- import * as memoize from 'memoizee';
53
+ import memoize from 'memoizee';
54
54
  import memoizeWeak = require('memoizee/weak');
55
55
  export const createCache = <T extends (...args: any[]) => any>(
56
56
  cacheName: keyof typeof cache,
@@ -48,7 +48,7 @@ const serverIsOnAir = async (_req, _res, next) => {
48
48
  };
49
49
 
50
50
  /** @type {import('../config-loader/config-loader').Config} */
51
- export let config = {
51
+ export const config = {
52
52
  models: [
53
53
  {
54
54
  modelName: 'ui',
@@ -267,6 +267,8 @@ export async function setup(app, sbvrUtils, db) {
267
267
  try {
268
268
  await tx.executeSql(query);
269
269
  } catch (err) {
270
+ // TODO: Consider changing this to a custom Error
271
+ // eslint-disable-next-line no-throw-literal
270
272
  throw [query, err];
271
273
  }
272
274
  }
@@ -6,7 +6,7 @@ import type { Dictionary, Resolvable } from '../sbvr-api/common-types';
6
6
 
7
7
  import { Engines } from '@balena/abstract-sql-compiler';
8
8
  import { EventEmitter } from 'eventemitter3';
9
- import * as _ from 'lodash';
9
+ import _ from 'lodash';
10
10
  import { TypedError } from 'typed-error';
11
11
  import * as env from '../config-loader/env';
12
12
  import { fromCallback, timeout } from '../sbvr-api/control-flow';
@@ -168,7 +168,7 @@ const atomicExecuteSql: Database['executeSql'] = async function (
168
168
  };
169
169
 
170
170
  const asyncTryFn = (fn: () => any) => {
171
- Promise.resolve().then(fn);
171
+ void Promise.resolve().then(fn);
172
172
  };
173
173
 
174
174
  type RejectedFunctions = (message: string) => {
@@ -189,7 +189,7 @@ const getRejectedFunctions: RejectedFunctions = env.DEBUG
189
189
  executeSql: rejectFn,
190
190
  rollback: rejectFn,
191
191
  };
192
- }
192
+ }
193
193
  : (message) => {
194
194
  const rejectFn = async () => {
195
195
  throw new TransactionClosedError(message);
@@ -198,7 +198,7 @@ const getRejectedFunctions: RejectedFunctions = env.DEBUG
198
198
  executeSql: rejectFn,
199
199
  rollback: rejectFn,
200
200
  };
201
- };
201
+ };
202
202
 
203
203
  const onEnd: Tx['on'] = (name: string, fn: () => void) => {
204
204
  if (name === 'end') {
@@ -215,7 +215,10 @@ class AutomaticClose {
215
215
  private automaticCloseTimeout: ReturnType<typeof setTimeout>;
216
216
  private automaticClose: () => void;
217
217
  private pending: false | number = 0;
218
- constructor(tx: Tx, private stackTraceErr?: Error) {
218
+ constructor(
219
+ tx: Tx,
220
+ private stackTraceErr?: Error,
221
+ ) {
219
222
  this.automaticClose = () => {
220
223
  console.error(
221
224
  `Transaction still open after ${env.db.timeoutMS}ms without an execute call.`,
@@ -223,7 +226,7 @@ class AutomaticClose {
223
226
  if (this.stackTraceErr) {
224
227
  console.error(this.stackTraceErr.stack);
225
228
  }
226
- tx.rollback();
229
+ void tx.rollback();
227
230
  };
228
231
  this.automaticCloseTimeout = setTimeout(
229
232
  this.automaticClose,
@@ -400,10 +403,13 @@ export abstract class Tx {
400
403
  protected abstract _rollback(): Promise<void>;
401
404
  protected abstract _commit(): Promise<void>;
402
405
 
406
+ // TODO: Re-enable the lint rule once eslint properly supports abstract class base implementations
403
407
  public async getTxLevelLock(
408
+ /* eslint-disable @typescript-eslint/no-unused-vars */
404
409
  _namespaceKey: string,
405
410
  _key: number,
406
411
  _blocking: boolean = true,
412
+ /* eslint-enable @typescript-eslint/no-unused-vars */
407
413
  ): Promise<boolean> {
408
414
  throw new Error(
409
415
  'The getTxLevelLock method is not implemented for the current engine.',
@@ -462,7 +468,6 @@ const createTransaction = (createFunc: CreateTransactionFn): TransactionFn => {
462
468
 
463
469
  let maybePg: typeof Pg | undefined;
464
470
  try {
465
- // tslint:disable-next-line:no-var-requires
466
471
  maybePg = require('pg');
467
472
  } catch (e) {
468
473
  // Ignore errors
@@ -497,7 +502,7 @@ if (maybePg != null) {
497
502
  const p = new pg.Pool(config);
498
503
  if (PG_SCHEMA != null) {
499
504
  p.on('connect', (client) => {
500
- client.query({ text: `SET search_path TO "${PG_SCHEMA}"` });
505
+ void client.query({ text: `SET search_path TO "${PG_SCHEMA}"` });
501
506
  });
502
507
  }
503
508
  p.on('connect', (client) => {
@@ -519,6 +524,7 @@ if (maybePg != null) {
519
524
  let pool: Pg.Pool;
520
525
  let replica: Pg.Pool;
521
526
  if (typeof connectString === 'string') {
527
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
522
528
  const pgConnectionString: typeof PgConnectionString = require('pg-connection-string');
523
529
  // We have to cast because of the use of null vs undefined
524
530
  const config = pgConnectionString.parse(connectString) as Pg.PoolConfig;
@@ -540,12 +546,12 @@ if (maybePg != null) {
540
546
  rowCount,
541
547
  rows,
542
548
  }: {
543
- rowCount: number;
549
+ rowCount: number | null;
544
550
  rows: Row[];
545
551
  }): Result => {
546
552
  return {
547
553
  rows,
548
- rowsAffected: rowCount,
554
+ rowsAffected: rowCount ?? 0,
549
555
  insertId: rows?.[0]?.id,
550
556
  };
551
557
  };
@@ -617,9 +623,9 @@ if (maybePg != null) {
617
623
  );
618
624
  this.db.release();
619
625
  } catch (err: any) {
620
- err = wrapDatabaseError(err);
621
- this.db.release(err);
622
- throw err;
626
+ const errorToReturn = wrapDatabaseError(err);
627
+ this.db.release(errorToReturn);
628
+ throw errorToReturn;
623
629
  }
624
630
  }
625
631
 
@@ -686,13 +692,13 @@ if (maybePg != null) {
686
692
  transaction: createTransaction(async (stackTraceErr) => {
687
693
  const client = await pool.connect();
688
694
  const tx = new PostgresTx(client, false, stackTraceErr);
689
- tx.executeSql('START TRANSACTION;');
695
+ void tx.executeSql('START TRANSACTION;');
690
696
  return tx;
691
697
  }),
692
698
  readTransaction: createTransaction(async (stackTraceErr) => {
693
699
  const client = await replica.connect();
694
700
  const tx = new PostgresTx(client, false, stackTraceErr);
695
- tx.executeSql('START TRANSACTION READ ONLY;');
701
+ void tx.executeSql('START TRANSACTION READ ONLY;');
696
702
  return tx.asReadOnly();
697
703
  }),
698
704
  ...alwaysExport,
@@ -702,7 +708,6 @@ if (maybePg != null) {
702
708
 
703
709
  let maybeMysql: typeof Mysql | undefined;
704
710
  try {
705
- // tslint:disable-next-line:no-var-requires
706
711
  maybeMysql = require('mysql');
707
712
  } catch (e) {
708
713
  // Ignore errors
@@ -809,14 +814,14 @@ if (maybeMysql != null) {
809
814
  const client = await getConnectionAsync();
810
815
  const close = () => client.release();
811
816
  const tx = new MySqlTx(client, close, false, stackTraceErr);
812
- tx.executeSql('START TRANSACTION;');
817
+ void tx.executeSql('START TRANSACTION;');
813
818
  return tx;
814
819
  }),
815
820
  readTransaction: createTransaction(async (stackTraceErr) => {
816
821
  const client = await getConnectionAsync();
817
822
  const close = () => client.release();
818
823
  const tx = new MySqlTx(client, close, false, stackTraceErr);
819
- tx.executeSql('START TRANSACTION READ ONLY;');
824
+ void tx.executeSql('START TRANSACTION READ ONLY;');
820
825
  return tx.asReadOnly();
821
826
  }),
822
827
  ...alwaysExport,
@@ -935,7 +940,6 @@ if (typeof window !== 'undefined' && window.openDatabase != null) {
935
940
  // allowing us to use async calls within the API.
936
941
  private asyncRecurse = () => {
937
942
  let args: AsyncQuery | undefined;
938
- // tslint:disable-next-line no-conditional-assignment
939
943
  while ((args = this.queue.pop())) {
940
944
  console.debug('Running', args[0]);
941
945
  this.tx.executeSql(args[0], args[1], args[2], args[3]);
@@ -1,4 +1,4 @@
1
- import * as _ from 'lodash';
1
+ import _ from 'lodash';
2
2
 
3
3
  /**
4
4
  * @typedef {Partial<Window> & {GLOBAL_PERMISSIONS?: string[]}} ExtendedWindow
@@ -34,10 +34,10 @@ const app = (function () {
34
34
  /** @type {string} */ match,
35
35
  /** @type import('express').Handler[] */ ...middleware
36
36
  ) {
37
- //Strip wildcard
37
+ // Strip wildcard
38
38
  let paramName;
39
39
  match = match.toLowerCase();
40
- const newMatch = match.replace(/[\/\*]*$/, '');
40
+ const newMatch = match.replace(/[/*]*$/, '');
41
41
  if (newMatch !== match) {
42
42
  match = newMatch;
43
43
  paramName = '*';
@@ -59,6 +59,8 @@ const app = (function () {
59
59
  /** @type any */ body = '',
60
60
  ) {
61
61
  if (!handlers[method]) {
62
+ // TODO: Consider changing this to a custom Error
63
+ // eslint-disable-next-line no-throw-literal
62
64
  throw [404, null, null];
63
65
  }
64
66
  const req = {
@@ -1,5 +1,5 @@
1
1
  import { SBVRParser } from '@balena/sbvr-parser';
2
- // tslint:disable-next-line:no-var-requires
2
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
3
3
  const Types: string = require('@balena/sbvr-types/Type.sbvr');
4
4
  import { version as sbvrParserVersion } from '@balena/sbvr-parser/package.json';
5
5
  import { version } from '@balena/sbvr-parser/package.json';
@@ -1,10 +1,10 @@
1
- import * as _ from 'lodash';
2
1
  import { odataNameToSqlName } from '@balena/odata-to-abstract-sql';
3
- // @ts-ignore
2
+ // @ts-expect-error b/c TS doesn't know what the result of requiring an sbvr file would be
3
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
4
4
  const transactionModel = require('./transaction.sbvr');
5
5
 
6
6
  /** @type {import('../config-loader/config-loader').Config} */
7
- export let config = {
7
+ export const config = {
8
8
  models: [
9
9
  {
10
10
  apiRoot: 'transaction',
@@ -130,7 +130,12 @@ WHERE "conditional field"."conditional resource" = ?;`,
130
130
  modelField.dataType === 'ForeignKey' &&
131
131
  Number.isNaN(Number(fieldValue))
132
132
  ) {
133
- if (!placeholders.hasOwnProperty(fieldValue)) {
133
+ if (
134
+ !Object.prototype.hasOwnProperty.call(
135
+ placeholders,
136
+ fieldValue,
137
+ )
138
+ ) {
134
139
  throw new Error('Cannot resolve placeholder' + fieldValue);
135
140
  } else {
136
141
  try {
@@ -170,7 +175,7 @@ WHERE "conditional resource"."transaction" = ?;\
170
175
  resolve = $resolve;
171
176
  reject = $reject;
172
177
  });
173
- // @ts-ignore
178
+ // @ts-expect-error we use resolve & reject before they are assigned b/c we treat them as a deferred.
174
179
  placeholders[placeholder] = { promise, resolve, reject };
175
180
  }
176
181
  }
@@ -1,7 +1,7 @@
1
1
  import type { Tx } from '../database-layer/db';
2
2
  import type { Model } from '../config-loader/config-loader';
3
3
 
4
- import * as _ from 'lodash';
4
+ import _ from 'lodash';
5
5
  import * as sbvrUtils from '../sbvr-api/sbvr-utils';
6
6
 
7
7
  type ApiRootModel = Model & { apiRoot: string };
@@ -12,18 +12,18 @@ type InitialMigrationStatus = MigrationStatus &
12
12
  >;
13
13
 
14
14
  import {
15
- MigrationTuple,
15
+ type MigrationTuple,
16
16
  getExecutedMigrations,
17
17
  migratorEnv,
18
18
  lockMigrations,
19
19
  initMigrationStatus,
20
20
  readMigrationStatus,
21
21
  updateMigrationStatus,
22
- RunnableAsyncMigrations,
22
+ type RunnableAsyncMigrations,
23
23
  getRunnableAsyncMigrations,
24
24
  filterAndSortPendingMigrations,
25
- MigrationStatus,
26
- BaseAsyncMigration,
25
+ type MigrationStatus,
26
+ type BaseAsyncMigration,
27
27
  } from './utils';
28
28
  import { booleanToEnabledString } from '../config-loader/env';
29
29
 
@@ -220,7 +220,8 @@ const $run = async (
220
220
  // skip execution
221
221
  if (migrationState.last_run_time) {
222
222
  const durationSinceLastRun =
223
- Date.now() - migrationState.last_run_time.getTime();
223
+ Date.now() -
224
+ new Date(migrationState.last_run_time).getTime();
224
225
  const delayMs = migrationState.is_backing_off
225
226
  ? initMigrationState.backoffDelayMS
226
227
  : initMigrationState.delayMS;
@@ -1,20 +1,21 @@
1
1
  import {
2
- MigrationTuple,
2
+ type MigrationTuple,
3
3
  MigrationError,
4
+ type MigrationExecutionResult,
4
5
  setExecutedMigrations,
5
6
  getExecutedMigrations,
6
7
  lockMigrations,
7
- RunnableMigrations,
8
+ type RunnableMigrations,
8
9
  filterAndSortPendingMigrations,
9
10
  getRunnableSyncMigrations,
10
11
  } from './utils';
11
12
  import type { Tx } from '../database-layer/db';
12
13
  import type { Config, Model } from '../config-loader/config-loader';
13
14
 
14
- import * as _ from 'lodash';
15
+ import _ from 'lodash';
15
16
  import * as sbvrUtils from '../sbvr-api/sbvr-utils';
16
17
 
17
- // tslint:disable-next-line:no-var-requires
18
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
18
19
  const modelText = require('./migrations.sbvr');
19
20
 
20
21
  type ApiRootModel = Model & { apiRoot: string };
@@ -45,7 +46,10 @@ export const postRun = async (tx: Tx, model: ApiRootModel): Promise<void> => {
45
46
  }
46
47
  };
47
48
 
48
- export const run = async (tx: Tx, model: ApiRootModel): Promise<void> => {
49
+ export const run = async (
50
+ tx: Tx,
51
+ model: ApiRootModel,
52
+ ): Promise<MigrationExecutionResult> => {
49
53
  const { migrations } = model;
50
54
  if (migrations == null || _.isEmpty(migrations)) {
51
55
  return;
@@ -58,7 +62,7 @@ const $run = async (
58
62
  tx: Tx,
59
63
  model: ApiRootModel,
60
64
  migrations: RunnableMigrations,
61
- ): Promise<void> => {
65
+ ): Promise<MigrationExecutionResult> => {
62
66
  const modelName = model.apiRoot;
63
67
 
64
68
  // migrations only run if the model has been executed before,
@@ -68,7 +72,6 @@ const $run = async (
68
72
  (sbvrUtils.api.migrations?.logger.info ?? console.info)(
69
73
  `First time model '${modelName}' has executed, skipping migrations`,
70
74
  );
71
-
72
75
  return await setExecutedMigrations(tx, modelName, Object.keys(migrations));
73
76
  }
74
77
  await lockMigrations({ tx, modelName, blocking: true }, async () => {
@@ -3,7 +3,7 @@ import type { Resolvable } from '../sbvr-api/common-types';
3
3
 
4
4
  import { createHash } from 'crypto';
5
5
  import { Engines } from '@balena/abstract-sql-compiler';
6
- import * as _ from 'lodash';
6
+ import _ from 'lodash';
7
7
  import { TypedError } from 'typed-error';
8
8
  import { migrator as migratorEnv } from '../config-loader/env';
9
9
  export { migrator as migratorEnv } from '../config-loader/env';
@@ -32,12 +32,12 @@ export type AsyncMigrationFn = (
32
32
  sbvrUtils: SbvrUtils,
33
33
  ) => Resolvable<number>;
34
34
 
35
- type AddFn<T extends {}, x extends 'sync' | 'async'> = T & {
35
+ type AddFn<T extends object, x extends 'sync' | 'async'> = T & {
36
36
  [key in `${x}Fn`]: key extends 'syncFn' ? MigrationFn : AsyncMigrationFn;
37
37
  } & {
38
38
  [key in `${x}Sql`]?: undefined;
39
39
  };
40
- type AddSql<T extends {}, x extends 'sync' | 'async'> = T & {
40
+ type AddSql<T extends object, x extends 'sync' | 'async'> = T & {
41
41
  [key in `${x}Fn`]?: undefined;
42
42
  } & {
43
43
  [key in `${x}Sql`]: string;
@@ -105,6 +105,12 @@ export type MigrationStatus = {
105
105
  is_backing_off: boolean;
106
106
  };
107
107
 
108
+ export type MigrationExecutionResult =
109
+ | undefined
110
+ | {
111
+ pendingUnsetMigrations: string[];
112
+ };
113
+
108
114
  export const getRunnableAsyncMigrations = (
109
115
  $migrations: Migrations,
110
116
  ): RunnableAsyncMigrations | undefined => {
@@ -270,9 +276,9 @@ export const setExecutedMigrations = async (
270
276
  tx: Tx,
271
277
  modelName: string,
272
278
  executedMigrations: string[],
273
- ): Promise<void> => {
279
+ ): Promise<MigrationExecutionResult> => {
274
280
  if (!(await migrationTablesExist(tx))) {
275
- return;
281
+ return { pendingUnsetMigrations: executedMigrations };
276
282
  }
277
283
 
278
284
  const stringifiedMigrations = await sbvrUtils.sbvrTypes.JSON.validate(
@@ -3,9 +3,9 @@ import type {
3
3
  AbstractSqlTable,
4
4
  } from '@balena/abstract-sql-compiler';
5
5
 
6
- import sbvrTypes, { SbvrType } from '@balena/sbvr-types';
6
+ import sbvrTypes, { type SbvrType } from '@balena/sbvr-types';
7
7
 
8
- // tslint:disable-next-line:no-var-requires
8
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
9
9
  const { version }: { version: string } = require('../../package.json');
10
10
 
11
11
  const getResourceName = (resourceName: string): string =>
@@ -0,0 +1,46 @@
1
+ import * as passportPinejs from './passport-pinejs';
2
+ import type { Express } from 'express';
3
+ import { PinejsSessionStore } from '../pinejs-session-store/pinejs-session-store';
4
+ import type { setup } from '../config-loader/config-loader';
5
+
6
+ export const mountLoginRouter = async (
7
+ configLoader: ReturnType<typeof setup>,
8
+ expressApp: Express,
9
+ ) => {
10
+ await Promise.all([
11
+ configLoader.loadConfig(passportPinejs.config),
12
+ configLoader.loadConfig(PinejsSessionStore.config),
13
+ ]);
14
+
15
+ if (
16
+ typeof process === 'undefined' ||
17
+ process == null ||
18
+ !process.env.DISABLE_DEFAULT_AUTH
19
+ ) {
20
+ expressApp.post(
21
+ '/login',
22
+ passportPinejs.login((err, user, req, res) => {
23
+ if (err) {
24
+ console.error('Error logging in', err);
25
+ res.status(500).end();
26
+ } else if (user === false) {
27
+ if (req.xhr === true) {
28
+ res.status(401).end();
29
+ } else {
30
+ res.redirect('/login.html');
31
+ }
32
+ } else {
33
+ if (req.xhr === true) {
34
+ res.status(200).end();
35
+ } else {
36
+ res.redirect('/');
37
+ }
38
+ }
39
+ }),
40
+ );
41
+
42
+ expressApp.get('/logout', passportPinejs.logout, (_req, res) => {
43
+ res.redirect('/');
44
+ });
45
+ }
46
+ };