diff --git a/Gruntfile.ts b/Gruntfile.cts similarity index 85% rename from Gruntfile.ts rename to Gruntfile.cts index 31368f659..a54b7f4c6 100644 --- a/Gruntfile.ts +++ b/Gruntfile.cts @@ -3,9 +3,9 @@ import type { WebpackPluginInstance } from 'webpack'; import _ from 'lodash'; import TerserPlugin from 'terser-webpack-plugin'; -import browserConfig from './build/browser'; -import moduleConfig from './build/module'; -import serverConfig from './build/server'; +import browserConfig from './build/browser.cts'; +import moduleConfig from './build/module.cts'; +import serverConfig from './build/server.cts'; const serverConfigs = { browser: browserConfig, @@ -110,30 +110,18 @@ export = (grunt: typeof Grunt) => { return renames; })(), - replace: { - 'pine.js': { + replace: _.mapValues(serverConfigs, (_config, task) => { + return { src: 'out/pine.js', overwrite: true, replacements: [ { - from: /nodeRequire/g, - to: 'require', + from: /sourceMappingURL=pine.js.map/g, + to: `sourceMappingURL=pine-${task}-<%= grunt.option('version') %>.js.map`, }, ], - }, - ..._.mapValues(serverConfigs, (_config, task) => { - return { - src: 'out/pine.js', - overwrite: true, - replacements: [ - { - from: /sourceMappingURL=pine.js.map/g, - to: `sourceMappingURL=pine-${task}-<%= grunt.option('version') %>.js.map`, - }, - ], - }; - }), - }, + }; + }), webpack: serverConfigs, @@ -167,7 +155,6 @@ export = (grunt: typeof Grunt) => { 'webpack:' + task, 'gitinfo:describe', 'version', - 'replace:pine.js', `replace:${task}`, `concat:${task}`, `rename:${task}`, diff --git a/bin/abstract-sql-compiler.js b/bin/abstract-sql-compiler.js index 594fd1936..67781b1a1 100755 --- a/bin/abstract-sql-compiler.js +++ b/bin/abstract-sql-compiler.js @@ -1,2 +1,2 @@ #!/usr/bin/env node -require('../out/bin/abstract-sql-compiler'); +import '../out/bin/abstract-sql-compiler.js'; diff --git a/bin/odata-compiler.js b/bin/odata-compiler.js index e58f90d2d..8082df797 100755 --- a/bin/odata-compiler.js +++ b/bin/odata-compiler.js @@ -1,2 +1,2 @@ #!/usr/bin/env node -require('../out/bin/odata-compiler'); +import '../out/bin/odata-compiler.js'; diff --git a/bin/sbvr-compiler.js b/bin/sbvr-compiler.js index e9b0e2dc6..547bb82ba 100755 --- a/bin/sbvr-compiler.js +++ b/bin/sbvr-compiler.js @@ -1,2 +1,2 @@ #!/usr/bin/env node -require('../out/bin/sbvr-compiler'); +import '../out/bin/sbvr-compiler.js'; diff --git a/build/browser.ts b/build/browser.cts similarity index 95% rename from build/browser.ts rename to build/browser.cts index e2364d522..9007344e9 100644 --- a/build/browser.ts +++ b/build/browser.cts @@ -1,6 +1,6 @@ import * as webpack from 'webpack'; import type { Configuration } from 'webpack'; -import sharedConfig from './config'; +import sharedConfig from './config.cts'; if (typeof sharedConfig.externals !== 'object') { throw new Error('Expected externals to be an object'); diff --git a/build/config.ts b/build/config.cts similarity index 91% rename from build/config.ts rename to build/config.cts index 8d900c3a5..2a4159df1 100644 --- a/build/config.ts +++ b/build/config.cts @@ -1,4 +1,4 @@ -import type { RequiredField } from '../src/sbvr-api/common-types'; +import type { RequiredField } from '../src/sbvr-api/common-types.js' with { 'resolution-mode': 'import' }; import * as path from 'path'; import * as webpack from 'webpack'; @@ -46,6 +46,10 @@ const config: RequiredField< }, resolve: { extensions: ['.js', '.ts'], + extensionAlias: { + '.js': ['.ts', '.js'], + '.mjs': ['.mts', '.mjs'], + }, }, plugins: [new webpack.optimize.LimitChunkCountPlugin({ maxChunks: 1 })], module: { diff --git a/build/module.ts b/build/module.cts similarity index 93% rename from build/module.ts rename to build/module.cts index eafb8d19d..25f924781 100644 --- a/build/module.ts +++ b/build/module.cts @@ -1,6 +1,6 @@ import * as webpack from 'webpack'; import type { Configuration } from 'webpack'; -import sharedConfig from './config'; +import sharedConfig from './config.cts'; const config: Configuration = { ...sharedConfig, diff --git a/build/server.ts b/build/server.cts similarity index 91% rename from build/server.ts rename to build/server.cts index e5d4c58c2..6154b34b7 100644 --- a/build/server.ts +++ b/build/server.cts @@ -1,6 +1,6 @@ import * as webpack from 'webpack'; import type { Configuration } from 'webpack'; -import sharedConfig from './config'; +import sharedConfig from './config.cts'; const config: Configuration = { ...sharedConfig, diff --git a/package.json b/package.json index 455207415..bd660c105 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { "name": "@balena/pinejs", "version": "19.7.5", - "main": "out/server-glue/module", - "type": "commonjs", + "main": "out/server-glue/module.js", + "type": "module", "repository": "git@github.com:balena-io/pinejs.git", "license": "Apache-2.0", "bin": { @@ -13,16 +13,16 @@ "scripts": { "prepublish": "require-npm4-to-publish", "prepare": "node -e \"try { (await import('husky')).default() } catch (e) { if (e.code !== 'ERR_MODULE_NOT_FOUND') throw e }\" --input-type module && npm run build", - "build": "grunt build", - "webpack-browser": "grunt browser", - "webpack-module": "grunt module", - "webpack-server": "grunt server", + "build": "grunt --preload ts-node/register/transpile-only --gruntfile Gruntfile.cts build", + "webpack-browser": "grunt --preload ts-node/register/transpile-only --gruntfile Gruntfile.cts browser", + "webpack-module": "grunt --preload ts-node/register/transpile-only --gruntfile Gruntfile.cts module", + "webpack-server": "grunt --preload ts-node/register/transpile-only --gruntfile Gruntfile.cts server", "webpack-build": "npm run webpack-browser && npm run webpack-module && npm run webpack-server", - "lint": "balena-lint -t tsconfig.dev.json -e js -e ts src build typings Gruntfile.ts && npx tsc --project tsconfig.dev.json --noEmit", + "lint": "balena-lint -t tsconfig.dev.json -e js -e ts src build typings Gruntfile.cts && npx tsc --project tsconfig.dev.json --noEmit", "test": "npm run build && npm run lint && npm run webpack-build && npm run test:compose && npm run test:generated-types", "test:compose": "trap 'docker compose -f docker-compose.npm-test.yml down ; echo Stopped ; exit 0' INT; docker compose -f docker-compose.npm-test.yml up -d && sleep 2 && DATABASE_URL=postgres://docker:docker@localhost:5431/postgres PINEJS_WEBRESOURCE_MAXFILESIZE=1000000000 S3_ENDPOINT=http://localhost:43680 S3_ACCESS_KEY=USERNAME S3_SECRET_KEY=PASSWORD S3_STORAGE_ADAPTER_BUCKET=balena-pine-web-resources S3_REGION=us-east-1 PINEJS_QUEUE_CONCURRENCY=1 TZ=UTC npx mocha", "test:generated-types": "npm run generate-types && git diff --exit-code ./src/sbvr-api/user.ts ./src/migrator/migrations.ts ./src/sbvr-api/dev.ts", - "lint-fix": "balena-lint -t tsconfig.dev.json -e js -e ts --fix src test build typings Gruntfile.ts", + "lint-fix": "balena-lint -t tsconfig.dev.json -e js -e ts --fix src test build typings Gruntfile.cts", "generate-types": "node ./bin/sbvr-compiler.js generate-types ./src/sbvr-api/user.sbvr ./src/sbvr-api/user.ts && node ./bin/sbvr-compiler.js generate-types ./src/migrator/migrations.sbvr ./src/migrator/migrations.ts && node ./bin/sbvr-compiler.js generate-types ./src/sbvr-api/dev.sbvr ./src/sbvr-api/dev.ts && node ./bin/sbvr-compiler.js generate-types ./src/tasks/tasks.sbvr ./src/tasks/tasks.ts && balena-lint -t tsconfig.dev.json --fix ./src/sbvr-api/user.ts ./src/migrator/migrations.ts ./src/sbvr-api/dev.ts" }, "dependencies": { @@ -54,7 +54,7 @@ "@types/websql": "^0.0.30", "ajv": "^8.17.1", "busboy": "^1.6.0", - "commander": "^12.1.0", + "commander": "^13.0.0", "cron-parser": "^4.9.0", "deep-freeze": "^0.0.1", "eventemitter3": "^5.0.1", @@ -69,9 +69,10 @@ "devDependencies": { "@balena/lint": "^8.2.8", "@balena/pinejs": "file:./", + "@balena/pinejs-webresource-s3": "^1.0.2", "@faker-js/faker": "^9.3.0", "@types/busboy": "^1.5.4", - "@types/chai": "^4.3.20", + "@types/chai": "^5.0.1", "@types/grunt": "^0.4.31", "@types/mocha": "^10.0.10", "@types/on-finished": "^2.3.4", @@ -79,7 +80,7 @@ "@types/supertest": "^6.0.2", "@types/terser-webpack-plugin": "^5.2.0", "@types/webpack": "^5.28.5", - "chai": "^4.5.0", + "chai": "^5.1.2", "grunt": "^1.6.1", "grunt-check-dependencies": "^1.0.0", "grunt-cli": "^1.5.0", @@ -126,7 +127,7 @@ "serve-static": "^1.16.2" }, "engines": { - "node": ">=20.14.0", + "node": "^20.14.0 || ^22.0.0", "npm": ">=10.7.0" }, "lint-staged": { @@ -141,7 +142,7 @@ "extension": [ ".test.ts" ], - "require": "ts-node/register/transpile-only", + "loader": "ts-node/esm/transpile-only", "exit": true, "timeout": 60000, "recursive": true diff --git a/src/bin/abstract-sql-compiler.ts b/src/bin/abstract-sql-compiler.ts index 6bc65575b..1e3d12bbf 100644 --- a/src/bin/abstract-sql-compiler.ts +++ b/src/bin/abstract-sql-compiler.ts @@ -3,13 +3,13 @@ import { version, writeAll, writeSqlModel, -} from './utils'; +} from './utils.js'; import { program } from 'commander'; const runCompile = async (inputFile: string, outputFile?: string) => { const { generateSqlModel } = await import('../sbvr-api/sbvr-utils.js'); - const abstractSql = getAbstractSqlModelFromFile( + const abstractSql = await getAbstractSqlModelFromFile( inputFile, program.opts().model, ); @@ -28,7 +28,7 @@ const generateTypes = async ( const { abstractSqlToTypescriptTypes } = await import( '@balena/abstract-sql-to-typescript/generate' ); - const abstractSql = getAbstractSqlModelFromFile( + const abstractSql = await getAbstractSqlModelFromFile( inputFile, program.opts().model, ); diff --git a/src/bin/odata-compiler.ts b/src/bin/odata-compiler.ts index 935ece8fd..d94e84e7a 100644 --- a/src/bin/odata-compiler.ts +++ b/src/bin/odata-compiler.ts @@ -1,4 +1,4 @@ -import { getAbstractSqlModelFromFile, version, writeAll } from './utils'; +import { getAbstractSqlModelFromFile, version, writeAll } from './utils.js'; import type { AbstractSqlModel, SqlResult, @@ -44,7 +44,7 @@ const translateOData = async ( outputFile?: string, ) => { const request = await generateAbstractSqlQuery( - getAbstractSqlModelFromFile(modelFile, program.opts().model), + await getAbstractSqlModelFromFile(modelFile, program.opts().model), odata, ); const json = JSON.stringify(request.abstractSqlQuery, null, 2); @@ -68,7 +68,7 @@ const compileOData = async ( outputFile?: string, ) => { const translatedRequest = await generateAbstractSqlQuery( - getAbstractSqlModelFromFile(modelFile, program.opts().model), + await getAbstractSqlModelFromFile(modelFile, program.opts().model), odata, ); const { compileRequest } = await import('../sbvr-api/abstract-sql.js'); diff --git a/src/bin/sbvr-compiler.ts b/src/bin/sbvr-compiler.ts index fa174b445..8de00ae3a 100644 --- a/src/bin/sbvr-compiler.ts +++ b/src/bin/sbvr-compiler.ts @@ -1,6 +1,6 @@ -import { version, writeAll, writeSqlModel } from './utils'; +import { version, writeAll, writeSqlModel } from './utils.js'; import { program } from 'commander'; -import * as fs from 'fs'; +import fs from 'fs'; const getSE = (inputFile: string) => fs.readFileSync(inputFile, 'utf8'); diff --git a/src/bin/utils.ts b/src/bin/utils.ts index 4da558b89..5ebc8010c 100644 --- a/src/bin/utils.ts +++ b/src/bin/utils.ts @@ -1,16 +1,18 @@ process.env.PINEJS_CACHE_FILE = - process.env.PINEJS_CACHE_FILE || __dirname + '/.pinejs-cache.json'; + process.env.PINEJS_CACHE_FILE || + fileURLToPath(new URL('.pinejs-cache.json', import.meta.url)); import type { SqlModel } from '@balena/abstract-sql-compiler'; -import type { Config, Model } from '../config-loader/config-loader'; -import type * as SbvrUtils from '../sbvr-api/sbvr-utils'; +import type { Config, Model } from '../config-loader/config-loader.js'; import type { AbstractSqlModel } from '@balena/abstract-sql-compiler'; -import * as fs from 'fs'; -import * as path from 'path'; -import '../server-glue/sbvr-loader'; +import fs from 'fs'; +import path from 'path'; +import '../server-glue/sbvr-loader.js'; +import { fileURLToPath } from 'url'; +import { loadSBVR } from '../server-glue/sbvr-loader.js'; -export { version } from '../config-loader/env'; +export { version } from '../config-loader/env.js'; export const writeAll = (output: string, outputFile?: string): void => { if (outputFile) { @@ -68,15 +70,21 @@ const getConfigModel = ( return fileContents; }; -export const getAbstractSqlModelFromFile = ( +export const getAbstractSqlModelFromFile = async ( modelFile: string, modelName: string | undefined, -): AbstractSqlModel => { +): Promise => { let fileContents: string | Model | AbstractSqlModel | Config; try { - fileContents = require(path.resolve(modelFile)); + fileContents = await import(path.resolve(modelFile)); } catch { - fileContents = fs.readFileSync(require.resolve(modelFile), 'utf8'); + fileContents = await fs.promises.readFile(path.resolve(modelFile), 'utf8'); + try { + // Try to parse the file as JSON + fileContents = JSON.parse(fileContents); + } catch { + // Ignore error as it's likely just a text sbvr file + } } let seModel: string; if (fileContents == null) { @@ -94,16 +102,16 @@ export const getAbstractSqlModelFromFile = ( } else if ('modelText' in configModel && configModel.modelText != null) { seModel = configModel.modelText; } else if ('modelFile' in configModel && configModel.modelFile != null) { - seModel = fs.readFileSync(require.resolve(configModel.modelFile), 'utf8'); + seModel = await loadSBVR(configModel.modelFile, import.meta); } else { throw new Error('Unrecognized config file'); } } else { throw new Error('Unrecognized config file'); } - const { generateLfModel, generateAbstractSqlModel } = - // eslint-disable-next-line @typescript-eslint/no-var-requires - require('../sbvr-api/sbvr-utils') as typeof SbvrUtils; + const { generateLfModel, generateAbstractSqlModel } = await import( + '../sbvr-api/sbvr-utils.js' + ); let lfModel; try { lfModel = generateLfModel(seModel); diff --git a/src/config-loader/config-loader.ts b/src/config-loader/config-loader.ts index c13ec35da..e9b1a7596 100644 --- a/src/config-loader/config-loader.ts +++ b/src/config-loader/config-loader.ts @@ -1,15 +1,15 @@ -import type * as Express from 'express'; +import type Express from 'express'; import type { AbstractSqlModel, Definition, } from '@balena/abstract-sql-compiler'; -import type { Database } from '../database-layer/db'; +import type { Database } from '../database-layer/db.js'; import type { AnyObject, Dictionary, RequiredField, Resolvable, -} from '../sbvr-api/common-types'; +} from '../sbvr-api/common-types.js'; import { type Migration, @@ -18,23 +18,23 @@ import { MigrationCategories, isSyncMigration, isAsyncMigration, -} from '../migrator/utils'; +} from '../migrator/utils.js'; -import * as fs from 'fs'; +import fs from 'fs'; import _ from 'lodash'; -import * as path from 'path'; +import path from 'path'; -import * as sbvrUtils from '../sbvr-api/sbvr-utils'; +import * as sbvrUtils from '../sbvr-api/sbvr-utils.js'; -import * as permissions from '../sbvr-api/permissions'; +import * as permissions from '../sbvr-api/permissions.js'; import { getDefaultHandler, getUploaderMiddlware, type WebResourceHandler, setupUploadHooks, setupWebresourceHandler, -} from '../webresource-handler'; -import type { AliasValidNodeType } from '../sbvr-api/translations'; +} from '../webresource-handler/index.js'; +import type { AliasValidNodeType } from '../sbvr-api/translations.js'; export type SetupFunction = ( app: Express.Application, @@ -240,7 +240,7 @@ export const setup = (app: Express.Application) => { ); } catch (err: any) { const message = `Failed to execute '${model.modelName}' model from '${model.modelFile}'`; - if (_.isError(err)) { + if (err instanceof Error) { err.message = `${message} due to: ${err.message}`; throw err; } @@ -256,7 +256,7 @@ export const setup = (app: Express.Application) => { let customCode: SetupFunction; if (typeof model.customServerCode === 'string') { try { - customCode = nodeRequire(model.customServerCode).setup; + customCode = (await import(model.customServerCode)).setup; } catch (e: any) { e.message = `Error loading custom server code: '${e.message}'`; throw e; @@ -291,7 +291,7 @@ export const setup = (app: Express.Application) => { let root: string; let configObj: Config; if (config == null) { - root = path.resolve(process.argv[2]) || __dirname; + root = path.resolve(process.argv[2]) || import.meta.dirname; configObj = await loadConfigFile(path.join(root, 'config.json')); } else if (typeof config === 'string') { root = path.dirname(config); diff --git a/src/config-loader/env.ts b/src/config-loader/env.ts index 4832a6bf6..4c970b4f3 100644 --- a/src/config-loader/env.ts +++ b/src/config-loader/env.ts @@ -1,6 +1,6 @@ -import * as fs from 'fs'; +import { promises as fs } from 'fs'; export const { version } = JSON.parse( - fs.readFileSync(require.resolve('../../package.json'), 'utf8'), + await fs.readFile(new URL(import.meta.resolve('../../package.json')), 'utf8'), ); const { PINEJS_DEBUG } = process.env; @@ -56,7 +56,7 @@ export const cache = { import { boolVar, intVar } from '@balena/env-parsing'; import memoize from 'memoizee'; -import memoizeWeak from 'memoizee/weak'; +import memoizeWeak from 'memoizee/weak.js'; export const createCache = any>( cacheName: keyof typeof cache, fn: T, diff --git a/src/data-server/sbvr-server.ts b/src/data-server/sbvr-server.ts index 391c087ef..3ad9e28c9 100644 --- a/src/data-server/sbvr-server.ts +++ b/src/data-server/sbvr-server.ts @@ -1,8 +1,8 @@ -import * as permissions from '../sbvr-api/permissions'; -import type { Resolvable } from '../sbvr-api/common-types'; +import * as permissions from '../sbvr-api/permissions.js'; +import type { Resolvable } from '../sbvr-api/common-types.js'; import type { Handler } from 'express'; -import type { Config, SetupFunction } from '../config-loader/config-loader'; -import type { Tx } from '../database-layer/db'; +import type { Config, SetupFunction } from '../config-loader/config-loader.js'; +import type { Tx } from '../database-layer/db.js'; const uiModel = `\ Vocabulary: ui diff --git a/src/database-layer/db.ts b/src/database-layer/db.ts index 5c5109e2f..97ec5dc5f 100644 --- a/src/database-layer/db.ts +++ b/src/database-layer/db.ts @@ -1,15 +1,15 @@ /// -import type * as Mysql from 'mysql'; -import type * as Pg from 'pg'; -import type * as PgConnectionString from 'pg-connection-string'; -import type { Dictionary, Resolvable } from '../sbvr-api/common-types'; +import type Mysql from 'mysql'; +import type Pg from 'pg'; +import type PgConnectionString from 'pg-connection-string'; +import type { Dictionary, Resolvable } from '../sbvr-api/common-types.js'; import { Engines } from '@balena/abstract-sql-compiler'; import { EventEmitter } from 'eventemitter3'; import _ from 'lodash'; import { TypedError } from 'typed-error'; -import * as env from '../config-loader/env'; -import { fromCallback, timeout } from '../sbvr-api/control-flow'; +import * as env from '../config-loader/env.js'; +import { fromCallback, timeout } from '../sbvr-api/control-flow.js'; export const metrics = new EventEmitter(); @@ -487,8 +487,10 @@ const createTransaction = (createFunc: CreateTransactionFn): TransactionFn => { }; let maybePg: typeof Pg | undefined; +let maybePgConnectionString: typeof PgConnectionString | undefined; try { - maybePg = require('pg'); + maybePg = (await import('pg')).default; + maybePgConnectionString = (await import('pg-connection-string')).default; } catch { // Ignore errors } @@ -543,10 +545,15 @@ if (maybePg != null) { let pool: Pg.Pool; let replica: Pg.Pool; if (typeof connectString === 'string') { - // eslint-disable-next-line @typescript-eslint/no-var-requires - const pgConnectionString: typeof PgConnectionString = require('pg-connection-string'); + if (maybePgConnectionString == null) { + throw new Error( + 'pg-connection-string is required for string connection strings', + ); + } // We have to cast because of the use of null vs undefined - const config = pgConnectionString.parse(connectString) as Pg.PoolConfig; + const config = maybePgConnectionString.parse( + connectString, + ) as Pg.PoolConfig; pool = initPool(config); } else { const config = connectString; @@ -728,7 +735,7 @@ if (maybePg != null) { let maybeMysql: typeof Mysql | undefined; try { - maybeMysql = require('mysql'); + maybeMysql = (await import('mysql')).default; } catch { // Ignore errors } diff --git a/src/extended-sbvr-parser/extended-sbvr-parser.ts b/src/extended-sbvr-parser/extended-sbvr-parser.ts index f1c710f97..de85ea3da 100644 --- a/src/extended-sbvr-parser/extended-sbvr-parser.ts +++ b/src/extended-sbvr-parser/extended-sbvr-parser.ts @@ -1,9 +1,9 @@ import { SBVRParser } from '@balena/sbvr-parser'; -import { requireSBVR } from '../server-glue/sbvr-loader'; -import { version as sbvrParserVersion } from '@balena/sbvr-parser/package.json'; -import { version } from '../config-loader/env'; +import { importSBVR } from '../server-glue/sbvr-loader.js'; +import SbvrParserPackage from '@balena/sbvr-parser/package.json' with { type: 'json' }; +import { version } from '../config-loader/env.js'; -const Types = requireSBVR('@balena/sbvr-types/Type.sbvr', require); +const Types = await importSBVR('@balena/sbvr-types/Type.sbvr', import.meta); export const ExtendedSBVRParser = SBVRParser._extend({ initialize() { @@ -13,5 +13,5 @@ export const ExtendedSBVRParser = SBVRParser._extend({ this.AddBuiltInVocab(Types); return this; }, - version: sbvrParserVersion + '+' + version, + version: SbvrParserPackage.version + '+' + version, }); diff --git a/src/http-transactions/transactions.js b/src/http-transactions/transactions.js index 34a297ec5..c861fde34 100644 --- a/src/http-transactions/transactions.js +++ b/src/http-transactions/transactions.js @@ -1,8 +1,8 @@ import { odataNameToSqlName } from '@balena/odata-to-abstract-sql'; -import { requireSBVR } from '../server-glue/sbvr-loader'; -const transactionModel = requireSBVR('./transaction.sbvr', require); +import { importSBVR } from '../server-glue/sbvr-loader.js'; +const transactionModel = await importSBVR('./transaction.sbvr', import.meta); -/** @type {import('../config-loader/config-loader').Config} */ +/** @type {import('../config-loader/config-loader.js').Config} */ export const config = { models: [ { @@ -53,7 +53,7 @@ ADD COLUMN IF NOT EXISTS "modified at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT N /** @type {(modelName: string) => void} */ export let addModelHooks; -/** @type { import('../config-loader/config-loader').SetupFunction } */ +/** @type { import('../config-loader/config-loader.js').SetupFunction } */ export function setup(app, sbvrUtils) { addModelHooks = (modelName) => { // TODO: Add checks on POST/PATCH requests as well. diff --git a/src/migrator/async.ts b/src/migrator/async.ts index 219fa0b51..053fc2856 100644 --- a/src/migrator/async.ts +++ b/src/migrator/async.ts @@ -1,8 +1,8 @@ -import type { Tx } from '../database-layer/db'; -import type { Model } from '../config-loader/config-loader'; +import type { Tx } from '../database-layer/db.js'; +import type { Model } from '../config-loader/config-loader.js'; import _ from 'lodash'; -import * as sbvrUtils from '../sbvr-api/sbvr-utils'; +import * as sbvrUtils from '../sbvr-api/sbvr-utils.js'; type ApiRootModel = Model & { apiRoot: string }; @@ -24,8 +24,8 @@ import { filterAndSortPendingMigrations, type MigrationStatus, type BaseAsyncMigration, -} from './utils'; -import { booleanToEnabledString } from '../config-loader/env'; +} from './utils.js'; +import { booleanToEnabledString } from '../config-loader/env.js'; export const run = async (tx: Tx, model: ApiRootModel): Promise => { const { migrations } = model; diff --git a/src/migrator/sync.ts b/src/migrator/sync.ts index 282b02df1..ff2cf62b0 100644 --- a/src/migrator/sync.ts +++ b/src/migrator/sync.ts @@ -1,4 +1,4 @@ -import type MigrationsModel from './migrations'; +import type MigrationsModel from './migrations.js'; import { type MigrationTuple, MigrationError, @@ -9,15 +9,15 @@ import { type RunnableMigrations, filterAndSortPendingMigrations, getRunnableSyncMigrations, -} from './utils'; -import type { Tx } from '../database-layer/db'; -import type { Config, Model } from '../config-loader/config-loader'; +} from './utils.js'; +import type { Tx } from '../database-layer/db.js'; +import type { Config, Model } from '../config-loader/config-loader.js'; import _ from 'lodash'; -import * as sbvrUtils from '../sbvr-api/sbvr-utils'; -import { requireSBVR } from '../server-glue/sbvr-loader'; +import * as sbvrUtils from '../sbvr-api/sbvr-utils.js'; +import { importSBVR } from '../server-glue/sbvr-loader.js'; -const migrationsModel = requireSBVR('./migrations.sbvr', require); +const migrationsModel = await importSBVR('./migrations.sbvr', import.meta); type ApiRootModel = Model & { apiRoot: string }; @@ -137,7 +137,7 @@ const executeMigration = async ( } }; -declare module '../sbvr-api/sbvr-utils' { +declare module '../sbvr-api/sbvr-utils.js' { export interface API { [migrationModelConfig.apiRoot]: PinejsClient; } diff --git a/src/migrator/utils.ts b/src/migrator/utils.ts index 061fcf530..cb934c056 100644 --- a/src/migrator/utils.ts +++ b/src/migrator/utils.ts @@ -1,16 +1,16 @@ -import type { Result, Tx } from '../database-layer/db'; -import type { Resolvable } from '../sbvr-api/common-types'; +import type { Result, Tx } from '../database-layer/db.js'; +import type { Resolvable } from '../sbvr-api/common-types.js'; import { createHash } from 'crypto'; import { Engines } from '@balena/abstract-sql-compiler'; import _ from 'lodash'; import { TypedError } from 'typed-error'; -import { migrator as migratorEnv } from '../config-loader/env'; -export { migrator as migratorEnv } from '../config-loader/env'; -import { PINEJS_ADVISORY_LOCK } from '../config-loader/env'; -import { delay } from '../sbvr-api/control-flow'; +import { migrator as migratorEnv } from '../config-loader/env.js'; +export { migrator as migratorEnv } from '../config-loader/env.js'; +import { PINEJS_ADVISORY_LOCK } from '../config-loader/env.js'; +import { delay } from '../sbvr-api/control-flow.js'; -import * as sbvrUtils from '../sbvr-api/sbvr-utils'; +import * as sbvrUtils from '../sbvr-api/sbvr-utils.js'; export enum MigrationCategories { 'sync' = 'sync', 'async' = 'async', diff --git a/src/odata-metadata/odata-metadata-generator.ts b/src/odata-metadata/odata-metadata-generator.ts index 3208067c1..efb674af0 100644 --- a/src/odata-metadata/odata-metadata-generator.ts +++ b/src/odata-metadata/odata-metadata-generator.ts @@ -3,8 +3,9 @@ import type { AbstractSqlTable, } from '@balena/abstract-sql-compiler'; -import sbvrTypes, { type SbvrType } from '@balena/sbvr-types'; -import { version } from '../config-loader/env'; +import type { SbvrType } from '@balena/sbvr-types'; +import { sbvrTypes } from '../sbvr-api/sbvr-utils.js'; +import { version } from '../config-loader/env.js'; const getResourceName = (resourceName: string): string => resourceName diff --git a/src/passport-pinejs/mount-login-router.ts b/src/passport-pinejs/mount-login-router.ts index a6c59bb96..aed8effed 100644 --- a/src/passport-pinejs/mount-login-router.ts +++ b/src/passport-pinejs/mount-login-router.ts @@ -1,7 +1,7 @@ -import * as passportPinejs from './passport-pinejs'; +import * as passportPinejs from './passport-pinejs.js'; import type { Express } from 'express'; -import { PinejsSessionStore } from '../pinejs-session-store/pinejs-session-store'; -import type { setup } from '../config-loader/config-loader'; +import { PinejsSessionStore } from '../pinejs-session-store/pinejs-session-store.js'; +import type { setup } from '../config-loader/config-loader.js'; export const mountLoginRouter = async ( configLoader: ReturnType, diff --git a/src/passport-pinejs/passport-pinejs.ts b/src/passport-pinejs/passport-pinejs.ts index ea3964f20..118bfac1c 100644 --- a/src/passport-pinejs/passport-pinejs.ts +++ b/src/passport-pinejs/passport-pinejs.ts @@ -1,10 +1,10 @@ -import type * as Express from 'express'; -import type * as Passport from 'passport'; -import type * as PassportLocal from 'passport-local'; -import type * as ConfigLoader from '../config-loader/config-loader'; -import type { User } from '../sbvr-api/sbvr-utils'; +import type Express from 'express'; +import type Passport from 'passport'; +import type PassportLocal from 'passport-local'; +import type * as ConfigLoader from '../config-loader/config-loader.js'; +import type { User } from '../sbvr-api/sbvr-utils.js'; -import * as permissions from '../sbvr-api/permissions'; +import * as permissions from '../sbvr-api/permissions.js'; // Returns a middleware that will handle logging in using `username` and `password` body properties export let login: ( diff --git a/src/pinejs-session-store/pinejs-session-store.ts b/src/pinejs-session-store/pinejs-session-store.ts index ed855ca52..40d9ace86 100644 --- a/src/pinejs-session-store/pinejs-session-store.ts +++ b/src/pinejs-session-store/pinejs-session-store.ts @@ -1,9 +1,9 @@ -import type { Config } from '../config-loader/config-loader'; -import type { AnyObject } from '../sbvr-api/common-types'; +import type { Config } from '../config-loader/config-loader.js'; +import type { AnyObject } from '../sbvr-api/common-types.js'; import { Store } from 'express-session'; -import * as permissions from '../sbvr-api/permissions'; -import { api } from '../sbvr-api/sbvr-utils'; +import * as permissions from '../sbvr-api/permissions.js'; +import { api } from '../sbvr-api/sbvr-utils.js'; export { Store }; diff --git a/src/sbvr-api/abstract-sql.ts b/src/sbvr-api/abstract-sql.ts index f9da809fb..bb309bd34 100644 --- a/src/sbvr-api/abstract-sql.ts +++ b/src/sbvr-api/abstract-sql.ts @@ -1,18 +1,18 @@ import _ from 'lodash'; -import * as AbstractSQLCompiler from '@balena/abstract-sql-compiler'; +import AbstractSQLCompiler from '@balena/abstract-sql-compiler'; import type { BindKey } from '@balena/odata-parser'; import { type ODataBinds, odataNameToSqlName, isBindReference, } from '@balena/odata-to-abstract-sql'; -import deepFreeze = require('deep-freeze'); +import deepFreeze from 'deep-freeze'; import memoize from 'memoizee'; -import * as env from '../config-loader/env'; -import { BadRequestError, SqlCompilationError } from './errors'; -import * as sbvrUtils from './sbvr-utils'; -import type { ODataRequest } from './uri-parser'; +import * as env from '../config-loader/env.js'; +import { BadRequestError, SqlCompilationError } from './errors.js'; +import * as sbvrUtils from './sbvr-utils.js'; +import type { ODataRequest } from './uri-parser.js'; const getMemoizedCompileRule = memoize( (engine: AbstractSQLCompiler.Engines) => diff --git a/src/sbvr-api/cached-compile.ts b/src/sbvr-api/cached-compile.ts index 5f5644585..3617ba218 100644 --- a/src/sbvr-api/cached-compile.ts +++ b/src/sbvr-api/cached-compile.ts @@ -1,4 +1,4 @@ -import type * as Fs from 'fs'; +import type Fs from 'fs'; import _ from 'lodash'; @@ -12,7 +12,7 @@ let cache: null | { } = null; let fs: undefined | typeof Fs; try { - fs = require('fs'); + fs = await import('fs'); } catch { // Ignore error } diff --git a/src/sbvr-api/control-flow.ts b/src/sbvr-api/control-flow.ts index c608ab014..d8231cb41 100644 --- a/src/sbvr-api/control-flow.ts +++ b/src/sbvr-api/control-flow.ts @@ -1,4 +1,4 @@ -import type { Resolvable } from './common-types'; +import type { Resolvable } from './common-types.js'; import _ from 'lodash'; import { TypedError } from 'typed-error'; diff --git a/src/sbvr-api/errors.ts b/src/sbvr-api/errors.ts index 2a3959f99..057cbe5c4 100644 --- a/src/sbvr-api/errors.ts +++ b/src/sbvr-api/errors.ts @@ -1,4 +1,4 @@ -import type { AnyObject, Tail } from './common-types'; +import type { AnyObject, Tail } from './common-types.js'; import { TypedError } from 'typed-error'; diff --git a/src/sbvr-api/express-extension.ts b/src/sbvr-api/express-extension.ts index 8f7405124..9912e4137 100644 --- a/src/sbvr-api/express-extension.ts +++ b/src/sbvr-api/express-extension.ts @@ -1,5 +1,5 @@ // Augment express.js with pinejs-specific attributes via declaration merging. -import type { User as PineUser } from './sbvr-utils'; +import type { User as PineUser } from './sbvr-utils.js'; declare global { // eslint-disable-next-line @typescript-eslint/no-namespace @@ -10,7 +10,7 @@ declare global { interface Request { user?: User; - apiKey?: import('./sbvr-utils').ApiKey; + apiKey?: import('./sbvr-utils.js').ApiKey; } } } diff --git a/src/sbvr-api/hooks.ts b/src/sbvr-api/hooks.ts index 1be74db6c..b3b3b95c8 100644 --- a/src/sbvr-api/hooks.ts +++ b/src/sbvr-api/hooks.ts @@ -1,12 +1,12 @@ -import type { OptionalField, Resolvable } from './common-types'; -import type { Result, Tx } from '../database-layer/db'; -import type { ODataRequest, ParsedODataRequest } from './uri-parser'; +import type { OptionalField, Resolvable } from './common-types.js'; +import type { Result, Tx } from '../database-layer/db.js'; +import type { ODataRequest, ParsedODataRequest } from './uri-parser.js'; import type { AnyObject } from 'pinejs-client-core'; import type { TypedError } from 'typed-error'; import type { SupportedMethod } from '@balena/odata-to-abstract-sql'; import _ from 'lodash'; -import { settleMapSeries } from './control-flow'; +import { settleMapSeries } from './control-flow.js'; import memoize from 'memoizee'; import { type User, @@ -15,7 +15,7 @@ import { getAbstractSqlModel, api, type Response, -} from './sbvr-utils'; +} from './sbvr-utils.js'; export interface HookReq { user?: User; diff --git a/src/sbvr-api/odata-response.ts b/src/sbvr-api/odata-response.ts index d7a407e65..0dbea2481 100644 --- a/src/sbvr-api/odata-response.ts +++ b/src/sbvr-api/odata-response.ts @@ -18,13 +18,17 @@ declare module '@balena/abstract-sql-compiler' { } } -import type { Result, Row } from '../database-layer/db'; +import type { Result, Row } from '../database-layer/db.js'; import { sqlNameToODataName } from '@balena/odata-to-abstract-sql'; -import sbvrTypes, { type SbvrType } from '@balena/sbvr-types'; +import type { SbvrType } from '@balena/sbvr-types'; import _ from 'lodash'; -import { resolveNavigationResource, resolveSynonym } from './sbvr-utils'; -import { getWebresourceHandler } from '../webresource-handler'; +import { + sbvrTypes, + resolveNavigationResource, + resolveSynonym, +} from './sbvr-utils.js'; +import { getWebresourceHandler } from '../webresource-handler/index.js'; const checkForExpansion = async ( vocab: string, diff --git a/src/sbvr-api/permissions.ts b/src/sbvr-api/permissions.ts index dea92be7b..91205a135 100644 --- a/src/sbvr-api/permissions.ts +++ b/src/sbvr-api/permissions.ts @@ -1,4 +1,4 @@ -import type AuthModel from './user'; +import type AuthModel from './user.js'; import type { AbstractSqlModel, AbstractSqlQuery, @@ -14,16 +14,16 @@ import type { SelectNode, SelectQueryNode, } from '@balena/abstract-sql-compiler'; -import './express-extension'; -import type * as Express from 'express'; +import './express-extension.js'; +import type Express from 'express'; import type { ODataBinds, ODataQuery, SupportedMethod, } from '@balena/odata-parser'; -import type { Tx } from '../database-layer/db'; -import type { ApiKey, User } from '../sbvr-api/sbvr-utils'; -import type { AnyObject, Dictionary } from './common-types'; +import type { Tx } from '../database-layer/db.js'; +import type { ApiKey, User } from '../sbvr-api/sbvr-utils.js'; +import type { AnyObject, Dictionary } from './common-types.js'; import { isBindReference, @@ -32,32 +32,32 @@ import { type ResourceFunction, sqlNameToODataName, } from '@balena/odata-to-abstract-sql'; -import * as ODataParser from '@balena/odata-parser'; +import ODataParser from '@balena/odata-parser'; import _ from 'lodash'; import memoize from 'memoizee'; -import * as randomstring from 'randomstring'; -import * as env from '../config-loader/env'; -import * as sbvrUtils from '../sbvr-api/sbvr-utils'; -import { type HookReq, addPureHook, addHook } from './hooks'; +import randomstring from 'randomstring'; +import * as env from '../config-loader/env.js'; +import * as sbvrUtils from '../sbvr-api/sbvr-utils.js'; +import { type HookReq, addPureHook, addHook } from './hooks.js'; import { BadRequestError, PermissionError, PermissionParsingError, -} from './errors'; +} from './errors.js'; import { memoizedGetOData2AbstractSQL, memoizedParseOdata, metadataEndpoints, type ODataRequest, -} from './uri-parser'; -import memoizeWeak from 'memoizee/weak'; -import type { Config } from '../config-loader/config-loader'; +} from './uri-parser.js'; +import memoizeWeak from 'memoizee/weak.js'; +import type { Config } from '../config-loader/config-loader.js'; import type { ODataOptions } from 'pinejs-client-core'; -import type { Permission } from './user'; -import { requireSBVR } from '../server-glue/sbvr-loader'; +import type { Permission } from './user.js'; +import { importSBVR } from '../server-glue/sbvr-loader.js'; -const userModel = requireSBVR('./user.sbvr', require); +const userModel = await importSBVR('./user.sbvr', import.meta); const DEFAULT_ACTOR_BIND = '@__ACTOR_ID'; const DEFAULT_ACTOR_BIND_REGEX = new RegExp( @@ -1779,7 +1779,7 @@ export const addPermissions = async ( } }; -declare module './sbvr-utils' { +declare module './sbvr-utils.js' { export interface API { [authModelConfig.apiRoot]: PinejsClient; } diff --git a/src/sbvr-api/sbvr-utils.ts b/src/sbvr-api/sbvr-utils.ts index bb42023ed..444da279a 100644 --- a/src/sbvr-api/sbvr-utils.ts +++ b/src/sbvr-api/sbvr-utils.ts @@ -1,7 +1,7 @@ -import type * as Express from 'express'; -import type * as Db from '../database-layer/db'; -import type { Model } from '../config-loader/config-loader'; -import type { AnyObject, RequiredField } from './common-types'; +import type Express from 'express'; +import type * as Db from '../database-layer/db.js'; +import type { Model } from '../config-loader/config-loader.js'; +import type { AnyObject, RequiredField } from './common-types.js'; import type { PickDeferred, Resource, @@ -21,12 +21,14 @@ declare global { import _ from 'lodash'; import type { TypedError } from 'typed-error'; -import { cachedCompile } from './cached-compile'; +import { cachedCompile } from './cached-compile.js'; type LFModel = any[]; -import * as AbstractSQLCompiler from '@balena/abstract-sql-compiler'; -import { version as AbstractSQLCompilerVersion } from '@balena/abstract-sql-compiler/package.json'; -import * as LF2AbstractSQL from '@balena/lf-to-abstract-sql'; +import AbstractSQLCompiler from '@balena/abstract-sql-compiler'; +import AbstractSqlCompilerPackage from '@balena/abstract-sql-compiler/package.json' with { type: 'json' }; +const { version: AbstractSQLCompilerVersion } = AbstractSqlCompilerPackage; + +import LF2AbstractSQL from '@balena/lf-to-abstract-sql'; import { type ODataBinds, @@ -34,21 +36,22 @@ import { sqlNameToODataName, type SupportedMethod, } from '@balena/odata-to-abstract-sql'; -import sbvrTypes from '@balena/sbvr-types'; -import deepFreeze = require('deep-freeze'); +import $sbvrTypes from '@balena/sbvr-types'; +const { default: sbvrTypes } = $sbvrTypes; +import deepFreeze from 'deep-freeze'; import type { ODataOptions, Params } from 'pinejs-client-core'; import { PinejsClientCore, type PromiseResultTypes } from 'pinejs-client-core'; -import { ExtendedSBVRParser } from '../extended-sbvr-parser/extended-sbvr-parser'; +import { ExtendedSBVRParser } from '../extended-sbvr-parser/extended-sbvr-parser.js'; -import * as asyncMigrator from '../migrator/async'; -import * as syncMigrator from '../migrator/sync'; -import { generateODataMetadata } from '../odata-metadata/odata-metadata-generator'; -import { requireSBVR } from '../server-glue/sbvr-loader'; +import * as asyncMigrator from '../migrator/async.js'; +import * as syncMigrator from '../migrator/sync.js'; +import { generateODataMetadata } from '../odata-metadata/odata-metadata-generator.js'; +import { importSBVR } from '../server-glue/sbvr-loader.js'; -import type DevModel from './dev'; -const devModel = requireSBVR('./dev.sbvr', require); -import * as permissions from './permissions'; +import type DevModel from './dev.js'; +const devModel = await importSBVR('./dev.sbvr', import.meta); +import * as permissions from './permissions.js'; import { BadRequestError, ConflictError, @@ -63,9 +66,9 @@ import { statusCodeToError, TranslationError, UnauthorizedError, -} from './errors'; -import * as uriParser from './uri-parser'; -export type { ODataRequest } from './uri-parser'; +} from './errors.js'; +import * as uriParser from './uri-parser.js'; +export type { ODataRequest } from './uri-parser.js'; import { type HookReq, type HookArgs, @@ -73,7 +76,7 @@ import { getHooks, runHooks, type InstantiatedHooks, -} from './hooks'; +} from './hooks.js'; export { type HookReq, type HookArgs, @@ -81,30 +84,33 @@ export { type Hooks, addPureHook, addSideEffectHook, -} from './hooks'; +} from './hooks.js'; -import memoizeWeak from 'memoizee/weak'; -import * as controlFlow from './control-flow'; +import memoizeWeak from 'memoizee/weak.js'; +import * as controlFlow from './control-flow.js'; export let db = undefined as any as Db.Database; export { sbvrTypes }; -import { version as LF2AbstractSQLVersion } from '@balena/lf-to-abstract-sql/package.json'; -import { version as sbvrTypesVersion } from '@balena/sbvr-types/package.json'; +import LF2AbstractSQLPackage from '@balena/lf-to-abstract-sql/package.json' with { type: 'json' }; +const { version: LF2AbstractSQLVersion } = LF2AbstractSQLPackage; +import SbvrTypesPackage from '@balena/sbvr-types/package.json' with { type: 'json' }; +const { version: sbvrTypesVersion } = SbvrTypesPackage; + import { compileRequest, getAndCheckBindValues, isRuleAffected, -} from './abstract-sql'; -export { resolveOdataBind } from './abstract-sql'; -import * as odataResponse from './odata-response'; -import { env } from '../server-glue/module'; -import { translateAbstractSqlModel } from './translations'; +} from './abstract-sql.js'; +export { resolveOdataBind } from './abstract-sql.js'; +import * as odataResponse from './odata-response.js'; +import { env } from '../server-glue/module.js'; +import { translateAbstractSqlModel } from './translations.js'; import { type MigrationExecutionResult, setExecutedMigrations, -} from '../migrator/utils'; +} from '../migrator/utils.js'; const LF2AbstractSQLTranslator = LF2AbstractSQL.createTranslator(sbvrTypes); const LF2AbstractSQLTranslatorVersion = `${LF2AbstractSQLVersion}+${sbvrTypesVersion}`; @@ -1151,7 +1157,7 @@ export const runURI = async ( const [response] = await promise; - if (_.isError(response)) { + if (response instanceof Error) { throw response; } @@ -1462,7 +1468,7 @@ const runODataRequest = (req: Express.Request, vocabulary: string) => { const responses = results.map( (result): Response | Response[] | HttpError => { - if (_.isError(result)) { + if (result instanceof Error) { return convertToHttpError(result); } else { if ( diff --git a/src/sbvr-api/translations.ts b/src/sbvr-api/translations.ts index 44aa6470b..734fe1e79 100644 --- a/src/sbvr-api/translations.ts +++ b/src/sbvr-api/translations.ts @@ -14,7 +14,7 @@ import type { UnknownTypeNodes, NullNode, } from '@balena/abstract-sql-compiler'; -import type { Dictionary } from './common-types'; +import type { Dictionary } from './common-types.js'; export type AliasValidNodeType = | ReferencedFieldNode diff --git a/src/sbvr-api/uri-parser.ts b/src/sbvr-api/uri-parser.ts index 548523f75..67d73199c 100644 --- a/src/sbvr-api/uri-parser.ts +++ b/src/sbvr-api/uri-parser.ts @@ -1,30 +1,30 @@ -import type * as AbstractSQLCompiler from '@balena/abstract-sql-compiler'; +import type AbstractSQLCompiler from '@balena/abstract-sql-compiler'; import type { ODataBinds, ODataOptions, ODataQuery, SupportedMethod, } from '@balena/odata-parser'; -import type { Tx } from '../database-layer/db'; -import type { InstantiatedHooks } from './hooks'; -import type { AnyObject } from './common-types'; +import type { Tx } from '../database-layer/db.js'; +import type { InstantiatedHooks } from './hooks.js'; +import type { AnyObject } from './common-types.js'; -import * as ODataParser from '@balena/odata-parser'; +import ODataParser from '@balena/odata-parser'; export const SyntaxError = ODataParser.SyntaxError; import { OData2AbstractSQL } from '@balena/odata-to-abstract-sql'; import _ from 'lodash'; -import memoizeWeak from 'memoizee/weak'; +import memoizeWeak from 'memoizee/weak.js'; -export { BadRequestError, ParsingError, TranslationError } from './errors'; +export { BadRequestError, ParsingError, TranslationError } from './errors.js'; import deepFreeze from 'deep-freeze'; -import * as env from '../config-loader/env'; +import * as env from '../config-loader/env.js'; import { BadRequestError, ParsingError, PermissionError, TranslationError, -} from './errors'; -import * as sbvrUtils from './sbvr-utils'; +} from './errors.js'; +import * as sbvrUtils from './sbvr-utils.js'; export type OdataBinds = ODataBinds; diff --git a/src/server-glue/global-ext.d.ts b/src/server-glue/global-ext.d.ts index 605910541..05434b7f5 100644 --- a/src/server-glue/global-ext.d.ts +++ b/src/server-glue/global-ext.d.ts @@ -5,7 +5,4 @@ declare namespace NodeJS { export interface Process { browser: boolean; } - export interface Global { - nodeRequire: NodeRequire; - } } diff --git a/src/server-glue/module.ts b/src/server-glue/module.ts index f022c8555..0a42885ff 100644 --- a/src/server-glue/module.ts +++ b/src/server-glue/module.ts @@ -1,27 +1,27 @@ -import type * as Express from 'express'; +import type Express from 'express'; -import './sbvr-loader'; +import './sbvr-loader.js'; -import * as dbModule from '../database-layer/db'; -import * as configLoader from '../config-loader/config-loader'; -import * as migrator from '../migrator/sync'; -import type * as migratorUtils from '../migrator/utils'; -import * as tasks from '../tasks'; +import * as dbModule from '../database-layer/db.js'; +import * as configLoader from '../config-loader/config-loader.js'; +import * as migrator from '../migrator/sync.js'; +import type * as migratorUtils from '../migrator/utils.js'; +import * as tasks from '../tasks/index.js'; -import * as sbvrUtils from '../sbvr-api/sbvr-utils'; -import { PINEJS_ADVISORY_LOCK } from '../config-loader/env'; +import * as sbvrUtils from '../sbvr-api/sbvr-utils.js'; +import { PINEJS_ADVISORY_LOCK } from '../config-loader/env.js'; -export * as dbModule from '../database-layer/db'; -export { PinejsSessionStore } from '../pinejs-session-store/pinejs-session-store'; -export { mountLoginRouter } from '../passport-pinejs/mount-login-router'; -export * as sbvrUtils from '../sbvr-api/sbvr-utils'; -export * as permissions from '../sbvr-api/permissions'; -export * as errors from '../sbvr-api/errors'; -export * as env from '../config-loader/env'; -export * as types from '../sbvr-api/common-types'; -export * as hooks from '../sbvr-api/hooks'; -export * as tasks from '../tasks'; -export * as webResourceHandler from '../webresource-handler'; +export * as dbModule from '../database-layer/db.js'; +export { PinejsSessionStore } from '../pinejs-session-store/pinejs-session-store.js'; +export { mountLoginRouter } from '../passport-pinejs/mount-login-router.js'; +export * as sbvrUtils from '../sbvr-api/sbvr-utils.js'; +export * as permissions from '../sbvr-api/permissions.js'; +export * as errors from '../sbvr-api/errors.js'; +export * as env from '../config-loader/env.js'; +export * as types from '../sbvr-api/common-types.js'; +export * as hooks from '../sbvr-api/hooks.js'; +export * as tasks from '../tasks/index.js'; +export * as webResourceHandler from '../webresource-handler/index.js'; export type { configLoader as ConfigLoader }; export type { migratorUtils as Migrator }; diff --git a/src/server-glue/sbvr-loader.ts b/src/server-glue/sbvr-loader.ts index 95f0c0d01..bc26953f5 100644 --- a/src/server-glue/sbvr-loader.ts +++ b/src/server-glue/sbvr-loader.ts @@ -1,35 +1,23 @@ -import type * as Fs from 'fs'; - -if (!process.browser) { - if (typeof nodeRequire === 'undefined' || nodeRequire == null) { - // `nodeRequire` is a special variable we use to bypass webpack's resolving of `require` - // statements on build for the cases where we need to always use the nodejs require, eg - // in the config-loader which dynamically loads code at runtime, and for adding custom - // filetype handlers - it works by being replaced with `require` after the webpack build - // finishes. - // In the case of `nodeRequire` being undefined it means we're being run in a nodejs - // environment directly, without a webpack build, and have to manually create it as an - // alias for the nodejs require so that things continue to work. - - // Alias require as nodeRequire for the config-loader hack. - global.nodeRequire = require; - } - // Register a .sbvr loader - // eslint-disable-next-line @typescript-eslint/no-var-requires - const fs: typeof Fs = require('fs'); - nodeRequire.extensions['.sbvr'] = (module: NodeModule, filename: string) => - (module.exports = fs.readFileSync(filename, 'utf8')); +/** + * + * @param filePath The module to load + * @param parentUrl Use import.meta.url + * @returns + */ +export async function loadSBVR(filePath: string, meta: ImportMeta) { + return await ( + await import('fs') + ).promises.readFile(new URL(meta.resolve(filePath)), 'utf8'); } /** * * @param filePath The module to load - * @param parentUrl Use `require` + * @param parentUrl Use `import.meta.url` * @returns The sbvr file contents */ -export function requireSBVR(filePath: string, require: NodeRequire) { - return (require('fs') as typeof import('fs')).readFileSync( - require.resolve(filePath), - 'utf8', - ); +export async function importSBVR(filePath: string, meta: ImportMeta) { + return await ( + await import('fs') + ).promises.readFile(new URL(meta.resolve(filePath)), 'utf8'); } diff --git a/src/server-glue/server.ts b/src/server-glue/server.ts index 5ce1e6089..1b054a09b 100644 --- a/src/server-glue/server.ts +++ b/src/server-glue/server.ts @@ -1,18 +1,9 @@ -import type BodyParser from 'body-parser'; -import type Compression from 'compression'; -import type CookieParser from 'cookie-parser'; -import type ExpressSession from 'express-session'; -import type MethodOverride from 'method-override'; -import type * as Passport from 'passport'; -import type * as Path from 'path'; -import type ServeStatic from 'serve-static'; +import * as Pinejs from './module.js'; +export { sbvrUtils, PinejsSessionStore } from './module.js'; -import * as Pinejs from './module'; -export { sbvrUtils, PinejsSessionStore } from './module'; +export { ExtendedSBVRParser } from '../extended-sbvr-parser/extended-sbvr-parser.js'; -export { ExtendedSBVRParser } from '../extended-sbvr-parser/extended-sbvr-parser'; - -import { mountLoginRouter } from '../passport-pinejs/mount-login-router'; +import { mountLoginRouter } from '../passport-pinejs/mount-login-router.js'; import express from 'express'; @@ -27,20 +18,18 @@ switch (app.get('env')) { } if (!process.browser) { - /* eslint-disable @typescript-eslint/no-var-requires */ - const passport: typeof Passport = require('passport'); - const path: typeof Path = require('path'); - const compression: typeof Compression = require('compression'); - const serveStatic: typeof ServeStatic = require('serve-static'); - const cookieParser: typeof CookieParser = require('cookie-parser'); - const bodyParser: typeof BodyParser = require('body-parser'); - const methodOverride: typeof MethodOverride = require('method-override'); - const expressSession: typeof ExpressSession = require('express-session'); - /* eslint-enable @typescript-eslint/no-var-requires */ + const { default: passport } = await import('passport'); + const { default: path } = await import('path'); + const { default: compression } = await import('compression'); + const { default: serveStatic } = await import('serve-static'); + const { default: cookieParser } = await import('cookie-parser'); + const { default: bodyParser } = await import('body-parser'); + const { default: methodOverride } = await import('method-override'); + const { default: expressSession } = await import('express-session'); app.use(compression()); - const root = process.argv[2] || __dirname; + const root = process.argv[2] || import.meta.dirname; app.use('/', serveStatic(path.join(root, 'static'))); app.use(cookieParser()); diff --git a/src/tasks/common.ts b/src/tasks/common.ts index f3faeb6b3..0ea86867b 100644 --- a/src/tasks/common.ts +++ b/src/tasks/common.ts @@ -3,4 +3,4 @@ import Ajv from 'ajv'; // Root path for the tasks API export const apiRoot = 'tasks'; -export const ajv = new Ajv(); +export const ajv = new Ajv.default(); diff --git a/src/tasks/index.ts b/src/tasks/index.ts index 96499d278..7ccaf3b85 100644 --- a/src/tasks/index.ts +++ b/src/tasks/index.ts @@ -1,20 +1,20 @@ import type { Schema } from 'ajv'; -import * as cronParser from 'cron-parser'; -import { tasks as tasksEnv } from '../config-loader/env'; -import { addPureHook } from '../sbvr-api/hooks'; -import * as sbvrUtils from '../sbvr-api/sbvr-utils'; -import type { ConfigLoader } from '../server-glue/module'; -import { ajv, apiRoot } from './common'; -import type { TaskHandler } from './worker'; -import { Worker } from './worker'; -import type TasksModel from './tasks'; -import type { Task } from './tasks'; +import cronParser from 'cron-parser'; +import { tasks as tasksEnv } from '../config-loader/env.js'; +import { addPureHook } from '../sbvr-api/hooks.js'; +import * as sbvrUtils from '../sbvr-api/sbvr-utils.js'; +import type { ConfigLoader } from '../server-glue/module.js'; +import { ajv, apiRoot } from './common.js'; +import type { TaskHandler } from './worker.js'; +import { Worker } from './worker.js'; +import type TasksModel from './tasks.js'; +import type { Task } from './tasks.js'; import type { FromSchema } from 'json-schema-to-ts'; -import { requireSBVR } from '../server-glue/sbvr-loader'; +import { importSBVR } from '../server-glue/sbvr-loader.js'; -export type * from './tasks'; +export type * from './tasks.js'; -const modelText = requireSBVR('./tasks.sbvr', require); +const modelText = await importSBVR('./tasks.sbvr', import.meta); // Create index for polling tasks table const initSql = ` @@ -25,7 +25,7 @@ CREATE INDEX IF NOT EXISTS idx_task_poll ON task USING btree ( ) WHERE status = 'queued'; `; -declare module '../sbvr-api/sbvr-utils' { +declare module '../sbvr-api/sbvr-utils.js' { export interface API { [apiRoot]: PinejsClient; } diff --git a/src/tasks/worker.ts b/src/tasks/worker.ts index 0f89ed00a..1d0ee05ac 100644 --- a/src/tasks/worker.ts +++ b/src/tasks/worker.ts @@ -1,14 +1,14 @@ import type { ValidateFunction } from 'ajv'; import { setTimeout } from 'node:timers/promises'; import type { AnyObject } from 'pinejs-client-core'; -import { tasks as tasksEnv } from '../config-loader/env'; -import type * as Db from '../database-layer/db'; -import * as permissions from '../sbvr-api/permissions'; -import { PinejsClient } from '../sbvr-api/sbvr-utils'; -import { sbvrUtils } from '../server-glue/module'; -import { ajv } from './common'; -import type { Task } from './tasks'; -import type TasksModel from './tasks'; +import { tasks as tasksEnv } from '../config-loader/env.js'; +import type * as Db from '../database-layer/db.js'; +import * as permissions from '../sbvr-api/permissions.js'; +import { PinejsClient } from '../sbvr-api/sbvr-utils.js'; +import { sbvrUtils } from '../server-glue/module.js'; +import { ajv } from './common.js'; +import type { Task } from './tasks.js'; +import type TasksModel from './tasks.js'; interface TaskArgs { api: PinejsClient; diff --git a/src/webresource-handler/handlers/NoopHandler.ts b/src/webresource-handler/handlers/NoopHandler.ts index 21f286fda..cb5fc6733 100644 --- a/src/webresource-handler/handlers/NoopHandler.ts +++ b/src/webresource-handler/handlers/NoopHandler.ts @@ -1,5 +1,9 @@ import type { WebResourceType as WebResource } from '@balena/sbvr-types'; -import type { IncomingFile, UploadResponse, WebResourceHandler } from '..'; +import type { + IncomingFile, + UploadResponse, + WebResourceHandler, +} from '../index.js'; export class NoopHandler implements WebResourceHandler { public async handleFile(resource: IncomingFile): Promise { diff --git a/src/webresource-handler/handlers/S3Handler.ts b/src/webresource-handler/handlers/S3Handler.ts deleted file mode 100644 index 222e4423a..000000000 --- a/src/webresource-handler/handlers/S3Handler.ts +++ /dev/null @@ -1,143 +0,0 @@ -import { - FileSizeExceededError, - type IncomingFile, - normalizeHref, - type UploadResponse, - WebResourceError, - type WebResourceHandler, -} from '..'; -import { - S3Client, - type S3ClientConfig, - DeleteObjectCommand, - type PutObjectCommandInput, - GetObjectCommand, -} from '@aws-sdk/client-s3'; -import { Upload } from '@aws-sdk/lib-storage'; -import { getSignedUrl } from '@aws-sdk/s3-request-presigner'; - -import { randomUUID } from 'crypto'; -import type { WebResourceType as WebResource } from '@balena/sbvr-types'; -import memoize from 'memoizee'; - -export interface S3HandlerProps { - region: string; - accessKey: string; - secretKey: string; - endpoint: string; - bucket: string; - maxSize?: number; - signedUrlExpireTimeSeconds?: number; - signedUrlCacheExpireTimeSeconds?: number; -} - -export class S3Handler implements WebResourceHandler { - private readonly config: S3ClientConfig; - private readonly bucket: string; - private readonly maxFileSize: number; - - protected readonly signedUrlExpireTimeSeconds: number; - protected readonly signedUrlCacheExpireTimeSeconds: number; - protected cachedGetSignedUrl: (fileKey: string) => Promise; - - private client: S3Client; - - constructor(config: S3HandlerProps) { - this.config = { - region: config.region, - credentials: { - accessKeyId: config.accessKey, - secretAccessKey: config.secretKey, - }, - endpoint: config.endpoint, - forcePathStyle: true, - }; - - this.signedUrlExpireTimeSeconds = - config.signedUrlExpireTimeSeconds ?? 86400; // 24h - this.signedUrlCacheExpireTimeSeconds = - config.signedUrlCacheExpireTimeSeconds ?? 82800; // 22h - - this.maxFileSize = config.maxSize ?? 52428800; - this.bucket = config.bucket; - this.client = new S3Client(this.config); - - // Memoize expects maxAge in MS and s3 signing method in seconds. - // Normalization to use only seconds and therefore convert here from seconds to MS - this.cachedGetSignedUrl = memoize(this.s3SignUrl, { - maxAge: this.signedUrlCacheExpireTimeSeconds * 1000, - }); - } - - public async handleFile(resource: IncomingFile): Promise { - let size = 0; - const key = `${resource.fieldname}_${randomUUID()}_${ - resource.originalname - }`; - const params: PutObjectCommandInput = { - Bucket: this.bucket, - Key: key, - Body: resource.stream, - ContentType: resource.mimetype, - }; - const upload = new Upload({ client: this.client, params }); - - upload.on('httpUploadProgress', async (ev) => { - size = ev.total ?? ev.loaded!; - if (size > this.maxFileSize) { - await upload.abort(); - } - }); - - try { - await upload.done(); - } catch (err: any) { - resource.stream.resume(); - if (size > this.maxFileSize) { - throw new FileSizeExceededError(this.maxFileSize); - } - throw new WebResourceError(err); - } - - const filename = this.getS3URL(key); - return { size, filename }; - } - - public async removeFile(href: string): Promise { - const fileKey = this.getKeyFromHref(href); - - const command = new DeleteObjectCommand({ - Bucket: this.bucket, - Key: fileKey, - }); - - await this.client.send(command); - } - - public async onPreRespond(webResource: WebResource): Promise { - if (webResource.href != null) { - const fileKey = this.getKeyFromHref(webResource.href); - webResource.href = await this.cachedGetSignedUrl(fileKey); - } - return webResource; - } - - private s3SignUrl(fileKey: string): Promise { - const command = new GetObjectCommand({ - Bucket: this.bucket, - Key: fileKey, - }); - return getSignedUrl(this.client, command, { - expiresIn: this.signedUrlExpireTimeSeconds, - }); - } - - private getS3URL(key: string): string { - return `${this.config.endpoint}/${this.bucket}/${key}`; - } - - private getKeyFromHref(href: string): string { - const hrefWithoutParams = normalizeHref(href); - return hrefWithoutParams.substring(hrefWithoutParams.lastIndexOf('/') + 1); - } -} diff --git a/src/webresource-handler/handlers/index.ts b/src/webresource-handler/handlers/index.ts index 39158864a..9db4bd6f8 100644 --- a/src/webresource-handler/handlers/index.ts +++ b/src/webresource-handler/handlers/index.ts @@ -1,2 +1 @@ -export * from './NoopHandler'; -export * from './S3Handler'; +export * from './NoopHandler.js'; diff --git a/src/webresource-handler/index.ts b/src/webresource-handler/index.ts index cf7968cfb..94f9b96d0 100644 --- a/src/webresource-handler/index.ts +++ b/src/webresource-handler/index.ts @@ -1,21 +1,21 @@ -import type * as Express from 'express'; +import type Express from 'express'; import busboy from 'busboy'; -import type * as stream from 'node:stream'; -import * as uriParser from '../sbvr-api/uri-parser'; -import * as sbvrUtils from '../sbvr-api/sbvr-utils'; -import type { HookArgs } from '../sbvr-api/hooks'; -import { getApiRoot, getModel } from '../sbvr-api/sbvr-utils'; -import { checkPermissions } from '../sbvr-api/permissions'; -import { NoopHandler } from './handlers/NoopHandler'; +import type stream from 'node:stream'; +import * as uriParser from '../sbvr-api/uri-parser.js'; +import * as sbvrUtils from '../sbvr-api/sbvr-utils.js'; +import type { HookArgs } from '../sbvr-api/hooks.js'; +import { getApiRoot, getModel } from '../sbvr-api/sbvr-utils.js'; +import { checkPermissions } from '../sbvr-api/permissions.js'; +import { NoopHandler } from './handlers/NoopHandler.js'; import { odataNameToSqlName, sqlNameToODataName, } from '@balena/odata-to-abstract-sql'; -import { errors, permissions } from '../server-glue/module'; +import { errors, permissions } from '../server-glue/module.js'; import type { WebResourceType as WebResource } from '@balena/sbvr-types'; import { TypedError } from 'typed-error'; -export * from './handlers'; +export * from './handlers/index.js'; export interface IncomingFile { fieldname: string; diff --git a/test/00-basic.test.ts b/test/00-basic.test.ts index 09b6ebe6e..fd50cb9f2 100644 --- a/test/00-basic.test.ts +++ b/test/00-basic.test.ts @@ -1,7 +1,7 @@ import supertest from 'supertest'; import { expect } from 'chai'; -const configPath = __dirname + '/fixtures/00-basic/config.js'; -import { testInit, testDeInit, testLocalServer } from './lib/test-init'; +const configPath = import.meta.dirname + '/fixtures/00-basic/config.js'; +import { testInit, testDeInit, testLocalServer } from './lib/test-init.js'; describe('00 basic tests', function () { let pineServer: Awaited>; diff --git a/test/01-constrain.test.ts b/test/01-constrain.test.ts index 9e432fdeb..f37170ff9 100644 --- a/test/01-constrain.test.ts +++ b/test/01-constrain.test.ts @@ -1,7 +1,7 @@ import supertest from 'supertest'; import { expect } from 'chai'; -const configPath = __dirname + '/fixtures/01-constrain/config.js'; -import { testInit, testDeInit, testLocalServer } from './lib/test-init'; +const configPath = import.meta.dirname + '/fixtures/01-constrain/config.js'; +import { testInit, testDeInit, testLocalServer } from './lib/test-init.js'; describe('01 basic constrain tests', function () { let pineServer: Awaited>; diff --git a/test/02-sync-migrator.test.ts b/test/02-sync-migrator.test.ts index ecd09e7e2..43f59b0b9 100644 --- a/test/02-sync-migrator.test.ts +++ b/test/02-sync-migrator.test.ts @@ -1,9 +1,9 @@ import supertest from 'supertest'; import type { ChildProcess } from 'child_process'; import { expect } from 'chai'; -import { testInit, testDeInit, testLocalServer } from './lib/test-init'; +import { testInit, testDeInit, testLocalServer } from './lib/test-init.js'; -const fixturesBasePath = __dirname + '/fixtures/02-sync-migrator/'; +const fixturesBasePath = import.meta.dirname + '/fixtures/02-sync-migrator/'; type TestDevice = { created_at: Date; diff --git a/test/03-async-migrator.test.ts b/test/03-async-migrator.test.ts index 10090860c..a6dd371f5 100644 --- a/test/03-async-migrator.test.ts +++ b/test/03-async-migrator.test.ts @@ -4,9 +4,9 @@ import { assert, expect } from 'chai'; import { setTimeout } from 'timers'; import type { Migrator } from '@balena/pinejs'; import { dbModule } from '@balena/pinejs'; -import { testInit, testDeInit, testLocalServer } from './lib/test-init'; +import { testInit, testDeInit, testLocalServer } from './lib/test-init.js'; -const fixturesBasePath = __dirname + '/fixtures/03-async-migrator/'; +const fixturesBasePath = import.meta.dirname + '/fixtures/03-async-migrator/'; type TestDevice = { created_at: Date; diff --git a/test/04-translations.test.ts b/test/04-translations.test.ts index 4bdfc3fdd..f59e664fd 100644 --- a/test/04-translations.test.ts +++ b/test/04-translations.test.ts @@ -1,12 +1,13 @@ -const configPath = __dirname + '/fixtures/04-translations/config.js'; -const hooksPath = __dirname + '/fixtures/04-translations/translations/hooks.js'; -import { testInit, testDeInit, testLocalServer } from './lib/test-init'; +const configPath = import.meta.dirname + '/fixtures/04-translations/config.js'; +const hooksPath = + import.meta.dirname + '/fixtures/04-translations/translations/hooks.js'; +import { testInit, testDeInit, testLocalServer } from './lib/test-init.js'; import { faker } from '@faker-js/faker'; import { expect } from 'chai'; import type { AnyObject } from 'pinejs-client-core'; import { PineTest } from 'pinejs-client-supertest'; -import { assertExists } from './lib/common'; +import { assertExists } from './lib/common.js'; describe('04 native translation tests', function () { let pineServer: Awaited>; diff --git a/test/05-request-cancellation.test.ts b/test/05-request-cancellation.test.ts index 1de34fe8e..03f7556c1 100644 --- a/test/05-request-cancellation.test.ts +++ b/test/05-request-cancellation.test.ts @@ -1,8 +1,11 @@ import { expect } from 'chai'; -const configPath = __dirname + '/fixtures/05-request-cancellation/config.js'; -const hooksPath = __dirname + '/fixtures/05-request-cancellation/hooks.js'; -const routesPath = __dirname + '/fixtures/05-request-cancellation/routes.js'; -import { testInit, testDeInit, testLocalServer } from './lib/test-init'; +const configPath = + import.meta.dirname + '/fixtures/05-request-cancellation/config.js'; +const hooksPath = + import.meta.dirname + '/fixtures/05-request-cancellation/hooks.js'; +const routesPath = + import.meta.dirname + '/fixtures/05-request-cancellation/routes.js'; +import { testInit, testDeInit, testLocalServer } from './lib/test-init.js'; import { PineTest } from 'pinejs-client-supertest'; import request from 'request'; import { setTimeout } from 'timers/promises'; diff --git a/test/06-webresource.test.ts b/test/06-webresource.test.ts index 3be3cb5f5..7a49e5119 100644 --- a/test/06-webresource.test.ts +++ b/test/06-webresource.test.ts @@ -1,27 +1,28 @@ import supertest from 'supertest'; import { expect } from 'chai'; -const configPath = __dirname + '/fixtures/06-webresource/config.js'; -const hooksPath = __dirname + '/fixtures/06-webresource/translations/hooks.js'; -const testResourcePath = __dirname + '/fixtures/06-webresource/resources/'; +const configPath = import.meta.dirname + '/fixtures/06-webresource/config.js'; +const hooksPath = + import.meta.dirname + '/fixtures/06-webresource/translations/hooks.js'; +const testResourcePath = + import.meta.dirname + '/fixtures/06-webresource/resources/'; -import * as fsBase from 'fs'; +import { promises as fs } from 'fs'; import { createReadStream, createWriteStream } from 'fs'; import { pipeline as pipelineRaw, Readable } from 'stream'; -import * as util from 'util'; +import util from 'util'; import { randomUUID } from 'crypto'; import { tmpdir } from 'os'; -import * as path from 'path'; -import { testInit, testDeInit, testLocalServer } from './lib/test-init'; +import path from 'path'; +import { testInit, testDeInit, testLocalServer } from './lib/test-init.js'; import { ListObjectsV2Command, S3Client, DeleteObjectCommand, } from '@aws-sdk/client-s3'; import { intVar, requiredVar } from '@balena/env-parsing'; -import { assertExists } from './lib/common'; +import { assertExists } from './lib/common.js'; const pipeline = util.promisify(pipelineRaw); -const fs = fsBase.promises; describe('06 webresources tests', function () { let pineServer: Awaited>; diff --git a/test/07-permissions.test.ts b/test/07-permissions.test.ts index fdbc935a4..32b0ef1a7 100644 --- a/test/07-permissions.test.ts +++ b/test/07-permissions.test.ts @@ -1,9 +1,9 @@ import supertest from 'supertest'; import { expect } from 'chai'; -const configPath = __dirname + '/fixtures/07-permissions/config.js'; -import { testInit, testDeInit, testLocalServer } from './lib/test-init'; +const configPath = import.meta.dirname + '/fixtures/07-permissions/config.js'; +import { testInit, testDeInit, testLocalServer } from './lib/test-init.js'; import { sbvrUtils, permissions } from '@balena/pinejs'; -import type UserModel from '@balena/pinejs/out/sbvr-api/user'; +import type UserModel from '@balena/pinejs/out/sbvr-api/user.js'; describe('07 permissions tests', function () { let pineServer: Awaited>; diff --git a/test/08-tasks.test.ts b/test/08-tasks.test.ts index 3cd259707..c5fa1e5b3 100644 --- a/test/08-tasks.test.ts +++ b/test/08-tasks.test.ts @@ -3,14 +3,14 @@ import { strict } from 'node:assert'; import { randomUUID } from 'node:crypto'; import { setTimeout } from 'node:timers/promises'; import { PineTest } from 'pinejs-client-supertest'; -import { testInit, testDeInit, testLocalServer } from './lib/test-init'; +import { testInit, testDeInit, testLocalServer } from './lib/test-init.js'; import { env } from '@balena/pinejs'; -import type Model from '@balena/pinejs/out/tasks/tasks'; -import * as cronParser from 'cron-parser'; -import { PINE_TEST_SIGNALS } from './lib/common'; +import type Model from '@balena/pinejs/out/tasks/tasks.js'; +import cronParser from 'cron-parser'; +import { PINE_TEST_SIGNALS } from './lib/common.js'; const actorId = 1; -const fixturesBasePath = __dirname + '/fixtures/08-tasks/'; +const fixturesBasePath = import.meta.dirname + '/fixtures/08-tasks/'; // Wait for a condition to be true, or throw an error if it doesn't happen in time. export async function waitFor(checkFn: () => Promise): Promise { diff --git a/test/fixtures/00-basic/config.ts b/test/fixtures/00-basic/config.ts index bfd7299ee..aad10f9b8 100644 --- a/test/fixtures/00-basic/config.ts +++ b/test/fixtures/00-basic/config.ts @@ -2,7 +2,7 @@ import type { ConfigLoader } from '@balena/pinejs'; const apiRoot = 'example'; const modelName = 'example'; -const modelFile = __dirname + '/example.sbvr'; +const modelFile = import.meta.dirname + '/example.sbvr'; export default { models: [ diff --git a/test/fixtures/01-constrain/config.ts b/test/fixtures/01-constrain/config.ts index 94ba9fa24..434ce8e64 100644 --- a/test/fixtures/01-constrain/config.ts +++ b/test/fixtures/01-constrain/config.ts @@ -2,7 +2,7 @@ import type { ConfigLoader } from '@balena/pinejs'; const apiRoot = 'university'; const modelName = 'university'; -const modelFile = __dirname + '/university.sbvr'; +const modelFile = import.meta.dirname + '/university.sbvr'; export default { models: [ diff --git a/test/fixtures/02-sync-migrator/00-execute-model.ts b/test/fixtures/02-sync-migrator/00-execute-model.ts index e9d87db63..d0723d523 100644 --- a/test/fixtures/02-sync-migrator/00-execute-model.ts +++ b/test/fixtures/02-sync-migrator/00-execute-model.ts @@ -2,8 +2,8 @@ import type { ConfigLoader } from '@balena/pinejs'; const apiRoot = 'example'; const modelName = 'example'; -const modelFile = __dirname + '/example.sbvr'; -const initSqlPath = __dirname + '/init-data.sql'; +const modelFile = import.meta.dirname + '/example.sbvr'; +const initSqlPath = import.meta.dirname + '/init-data.sql'; export default { models: [ diff --git a/test/fixtures/02-sync-migrator/01-migrations.ts b/test/fixtures/02-sync-migrator/01-migrations.ts index 3d848d8de..41606d510 100644 --- a/test/fixtures/02-sync-migrator/01-migrations.ts +++ b/test/fixtures/02-sync-migrator/01-migrations.ts @@ -2,8 +2,8 @@ import type { ConfigLoader } from '@balena/pinejs'; const apiRoot = 'example'; const modelName = 'example'; -const modelFile = __dirname + '/example.sbvr'; -const migrationsPath = __dirname + '/01-migrations'; +const modelFile = import.meta.dirname + '/example.sbvr'; +const migrationsPath = import.meta.dirname + '/01-migrations'; export default { models: [ diff --git a/test/fixtures/02-sync-migrator/02-migrations-error.ts b/test/fixtures/02-sync-migrator/02-migrations-error.ts index 5b96a72a5..871fcb5ae 100644 --- a/test/fixtures/02-sync-migrator/02-migrations-error.ts +++ b/test/fixtures/02-sync-migrator/02-migrations-error.ts @@ -2,8 +2,8 @@ import type { ConfigLoader } from '@balena/pinejs'; const apiRoot = 'example'; const modelName = 'example'; -const modelFile = __dirname + '/example.sbvr'; -const migrationsPath = __dirname + '/02-migrations-error'; +const modelFile = import.meta.dirname + '/example.sbvr'; +const migrationsPath = import.meta.dirname + '/02-migrations-error'; export default { models: [ diff --git a/test/fixtures/02-sync-migrator/03-exclusive-category.ts b/test/fixtures/02-sync-migrator/03-exclusive-category.ts index f24a0af7b..4ab82b9c4 100644 --- a/test/fixtures/02-sync-migrator/03-exclusive-category.ts +++ b/test/fixtures/02-sync-migrator/03-exclusive-category.ts @@ -2,8 +2,8 @@ import type { ConfigLoader } from '@balena/pinejs'; const apiRoot = 'example'; const modelName = 'example'; -const modelFile = __dirname + '/example.sbvr'; -const migrationsPath = __dirname + '/01-migrations'; +const modelFile = import.meta.dirname + '/example.sbvr'; +const migrationsPath = import.meta.dirname + '/01-migrations'; export default { models: [ diff --git a/test/fixtures/02-sync-migrator/04-new-model-with-init.ts b/test/fixtures/02-sync-migrator/04-new-model-with-init.ts index 2321b0ead..2db8161b3 100644 --- a/test/fixtures/02-sync-migrator/04-new-model-with-init.ts +++ b/test/fixtures/02-sync-migrator/04-new-model-with-init.ts @@ -2,7 +2,7 @@ import type { ConfigLoader } from '@balena/pinejs'; const apiRoot = 'example'; const modelName = 'example'; -const modelFile = __dirname + '/example.sbvr'; +const modelFile = import.meta.dirname + '/example.sbvr'; export default { models: [ diff --git a/test/fixtures/03-async-migrator/00-execute-model.ts b/test/fixtures/03-async-migrator/00-execute-model.ts index e9d87db63..d0723d523 100644 --- a/test/fixtures/03-async-migrator/00-execute-model.ts +++ b/test/fixtures/03-async-migrator/00-execute-model.ts @@ -2,8 +2,8 @@ import type { ConfigLoader } from '@balena/pinejs'; const apiRoot = 'example'; const modelName = 'example'; -const modelFile = __dirname + '/example.sbvr'; -const initSqlPath = __dirname + '/init-data.sql'; +const modelFile = import.meta.dirname + '/example.sbvr'; +const initSqlPath = import.meta.dirname + '/init-data.sql'; export default { models: [ diff --git a/test/fixtures/03-async-migrator/01-migrations.ts b/test/fixtures/03-async-migrator/01-migrations.ts index 6cf7137f2..757af0756 100644 --- a/test/fixtures/03-async-migrator/01-migrations.ts +++ b/test/fixtures/03-async-migrator/01-migrations.ts @@ -2,8 +2,8 @@ import type { ConfigLoader } from '@balena/pinejs'; const apiRoot = 'example'; const modelName = 'example'; -const modelFile = __dirname + '/example.sbvr'; -const migrationsPath = __dirname + '/01-migrations/migrations'; +const modelFile = import.meta.dirname + '/example.sbvr'; +const migrationsPath = import.meta.dirname + '/01-migrations/migrations'; export default { models: [ diff --git a/test/fixtures/03-async-migrator/02-parallel-migrations.ts b/test/fixtures/03-async-migrator/02-parallel-migrations.ts index 6f5a94ae6..2d1691c7a 100644 --- a/test/fixtures/03-async-migrator/02-parallel-migrations.ts +++ b/test/fixtures/03-async-migrator/02-parallel-migrations.ts @@ -2,8 +2,9 @@ import type { ConfigLoader } from '@balena/pinejs'; const apiRoot = 'example'; const modelName = 'example'; -const modelFile = __dirname + '/example.sbvr'; -const migrationsPath = __dirname + '/02-parallel-migrations/migrations'; +const modelFile = import.meta.dirname + '/example.sbvr'; +const migrationsPath = + import.meta.dirname + '/02-parallel-migrations/migrations'; export default { models: [ diff --git a/test/fixtures/03-async-migrator/03-finalize-async.ts b/test/fixtures/03-async-migrator/03-finalize-async.ts index e11459ed5..74e7fbdae 100644 --- a/test/fixtures/03-async-migrator/03-finalize-async.ts +++ b/test/fixtures/03-async-migrator/03-finalize-async.ts @@ -2,8 +2,8 @@ import type { ConfigLoader } from '@balena/pinejs'; const apiRoot = 'example'; const modelName = 'example'; -const modelFile = __dirname + '/example.sbvr'; -const migrationsPath = __dirname + '/03-finalize-async/migrations'; +const modelFile = import.meta.dirname + '/example.sbvr'; +const migrationsPath = import.meta.dirname + '/03-finalize-async/migrations'; export default { models: [ diff --git a/test/fixtures/03-async-migrator/04-migration-errors.ts b/test/fixtures/03-async-migrator/04-migration-errors.ts index 4eab4e43f..da14a9b11 100644 --- a/test/fixtures/03-async-migrator/04-migration-errors.ts +++ b/test/fixtures/03-async-migrator/04-migration-errors.ts @@ -2,8 +2,8 @@ import type { ConfigLoader } from '@balena/pinejs'; const apiRoot = 'example'; const modelName = 'example'; -const modelFile = __dirname + '/example.sbvr'; -const migrationsPath = __dirname + '/04-migration-errors/migrations'; +const modelFile = import.meta.dirname + '/example.sbvr'; +const migrationsPath = import.meta.dirname + '/04-migration-errors/migrations'; export default { models: [ diff --git a/test/fixtures/03-async-migrator/05-massive-data.ts b/test/fixtures/03-async-migrator/05-massive-data.ts index 80589f1d8..ba66547e7 100644 --- a/test/fixtures/03-async-migrator/05-massive-data.ts +++ b/test/fixtures/03-async-migrator/05-massive-data.ts @@ -2,8 +2,8 @@ import type { ConfigLoader } from '@balena/pinejs'; const apiRoot = 'example'; const modelName = 'example'; -const modelFile = __dirname + '/example.sbvr'; -const migrationsPath = __dirname + '/05-massive-data/migrations'; +const modelFile = import.meta.dirname + '/example.sbvr'; +const migrationsPath = import.meta.dirname + '/05-massive-data/migrations'; export default { models: [ diff --git a/test/fixtures/03-async-migrator/05-massive-data/00-execute-model.ts b/test/fixtures/03-async-migrator/05-massive-data/00-execute-model.ts index c59877355..208ab1270 100644 --- a/test/fixtures/03-async-migrator/05-massive-data/00-execute-model.ts +++ b/test/fixtures/03-async-migrator/05-massive-data/00-execute-model.ts @@ -3,8 +3,8 @@ import type { ConfigLoader } from '@balena/pinejs'; const apiRoot = 'example'; const modelName = 'example'; -const modelFile = resolve(__dirname, '../example.sbvr'); -const initSqlPath = __dirname + '/init-data.sql'; +const modelFile = resolve(import.meta.dirname, '../example.sbvr'); +const initSqlPath = import.meta.dirname + '/init-data.sql'; export default { models: [ diff --git a/test/fixtures/03-async-migrator/06-setup-errors.ts b/test/fixtures/03-async-migrator/06-setup-errors.ts index 5a0e4474a..320c0220b 100644 --- a/test/fixtures/03-async-migrator/06-setup-errors.ts +++ b/test/fixtures/03-async-migrator/06-setup-errors.ts @@ -2,8 +2,8 @@ import type { ConfigLoader } from '@balena/pinejs'; const apiRoot = 'example'; const modelName = 'example'; -const modelFile = __dirname + '/example.sbvr'; -const migrationsPath = __dirname + '/06-setup-errors'; +const modelFile = import.meta.dirname + '/example.sbvr'; +const migrationsPath = import.meta.dirname + '/06-setup-errors'; export default { models: [ diff --git a/test/fixtures/03-async-migrator/07-setup-error-mixed-migrations.ts b/test/fixtures/03-async-migrator/07-setup-error-mixed-migrations.ts index d8de91ff8..0308ce73d 100644 --- a/test/fixtures/03-async-migrator/07-setup-error-mixed-migrations.ts +++ b/test/fixtures/03-async-migrator/07-setup-error-mixed-migrations.ts @@ -1,6 +1,6 @@ const apiRoot = 'example'; const modelName = 'example'; -const modelFile = __dirname + '/example.sbvr'; +const modelFile = import.meta.dirname + '/example.sbvr'; export default { models: [ diff --git a/test/fixtures/03-async-migrator/08-01-async-lock-taker.ts b/test/fixtures/03-async-migrator/08-01-async-lock-taker.ts index a29576e08..492de7f27 100644 --- a/test/fixtures/03-async-migrator/08-01-async-lock-taker.ts +++ b/test/fixtures/03-async-migrator/08-01-async-lock-taker.ts @@ -2,7 +2,7 @@ import type { ConfigLoader } from '@balena/pinejs'; const apiRoot = 'example'; const modelName = 'example'; -const modelFile = __dirname + '/example.sbvr'; +const modelFile = import.meta.dirname + '/example.sbvr'; const asyncSpammer = { asyncFn: async (tx: any) => { diff --git a/test/fixtures/03-async-migrator/08-02-sync-lock-starvation.ts b/test/fixtures/03-async-migrator/08-02-sync-lock-starvation.ts index 39a96e6bf..392d318c3 100644 --- a/test/fixtures/03-async-migrator/08-02-sync-lock-starvation.ts +++ b/test/fixtures/03-async-migrator/08-02-sync-lock-starvation.ts @@ -2,7 +2,7 @@ import type { ConfigLoader } from '@balena/pinejs'; const apiRoot = 'example'; const modelName = 'example'; -const modelFile = __dirname + '/example.sbvr'; +const modelFile = import.meta.dirname + '/example.sbvr'; export default { models: [ diff --git a/test/fixtures/04-translations/config.ts b/test/fixtures/04-translations/config.ts index 491f7dc4b..ff04ecdc9 100644 --- a/test/fixtures/04-translations/config.ts +++ b/test/fixtures/04-translations/config.ts @@ -1,18 +1,21 @@ import type { AbstractSqlQuery } from '@balena/abstract-sql-compiler'; -import { getAbstractSqlModelFromFile } from '@balena/pinejs/out/bin/utils'; +import { getAbstractSqlModelFromFile } from '@balena/pinejs/out/bin/utils.js'; import type { ConfigLoader } from '@balena/pinejs'; const apiRoot = 'university'; const modelName = 'university'; -const modelFile = __dirname + '/university.sbvr'; +const modelFile = import.meta.dirname + '/university.sbvr'; -import { v1AbstractSqlModel, v1Translations } from './translations/v1'; -import { v2AbstractSqlModel, v2Translations } from './translations/v2'; -import { v3AbstractSqlModel, v3Translations } from './translations/v3'; -import { v4AbstractSqlModel } from './translations/v4'; -import { v5AbstractSqlModel } from './translations/v5'; +import { v1AbstractSqlModel, v1Translations } from './translations/v1/index.js'; +import { v2AbstractSqlModel, v2Translations } from './translations/v2/index.js'; +import { v3AbstractSqlModel, v3Translations } from './translations/v3/index.js'; +import { v4AbstractSqlModel } from './translations/v4/index.js'; +import { v5AbstractSqlModel } from './translations/v5/index.js'; -export const abstractSql = getAbstractSqlModelFromFile(modelFile, undefined); +export const abstractSql = await getAbstractSqlModelFromFile( + modelFile, + undefined, +); abstractSql.tables['student'].fields.push({ fieldName: 'computed field', diff --git a/test/fixtures/04-translations/translations/hooks.ts b/test/fixtures/04-translations/translations/hooks.ts index 5cdfb6446..3e0b64be4 100644 --- a/test/fixtures/04-translations/translations/hooks.ts +++ b/test/fixtures/04-translations/translations/hooks.ts @@ -1,3 +1,3 @@ -import './v1/hooks'; -import './v2/hooks'; -import './v3/hooks'; +import './v1/hooks.js'; +import './v2/hooks.js'; +import './v3/hooks.js'; diff --git a/test/fixtures/04-translations/translations/v1/index.ts b/test/fixtures/04-translations/translations/v1/index.ts index 2fd9a76c8..3b888a63e 100644 --- a/test/fixtures/04-translations/translations/v1/index.ts +++ b/test/fixtures/04-translations/translations/v1/index.ts @@ -1,11 +1,11 @@ import type { ConfigLoader } from '@balena/pinejs'; -import { getAbstractSqlModelFromFile } from '@balena/pinejs/out/bin/utils'; +import { getAbstractSqlModelFromFile } from '@balena/pinejs/out/bin/utils.js'; import type { AbstractSqlQuery } from '@balena/abstract-sql-compiler'; export const toVersion = 'v2'; -export const v1AbstractSqlModel = getAbstractSqlModelFromFile( - __dirname + '/university.sbvr', +export const v1AbstractSqlModel = await getAbstractSqlModelFromFile( + import.meta.dirname + '/university.sbvr', undefined, ); diff --git a/test/fixtures/04-translations/translations/v2/index.ts b/test/fixtures/04-translations/translations/v2/index.ts index 53b0d3459..a2acace8c 100644 --- a/test/fixtures/04-translations/translations/v2/index.ts +++ b/test/fixtures/04-translations/translations/v2/index.ts @@ -1,12 +1,12 @@ import type { ConfigLoader } from '@balena/pinejs'; -import { getAbstractSqlModelFromFile } from '@balena/pinejs/out/bin/utils'; +import { getAbstractSqlModelFromFile } from '@balena/pinejs/out/bin/utils.js'; import type { AbstractSqlQuery, SelectQueryNode, } from '@balena/abstract-sql-compiler'; -export const v2AbstractSqlModel = getAbstractSqlModelFromFile( - __dirname + '/university.sbvr', +export const v2AbstractSqlModel = await getAbstractSqlModelFromFile( + import.meta.dirname + '/university.sbvr', undefined, ); diff --git a/test/fixtures/04-translations/translations/v3/index.ts b/test/fixtures/04-translations/translations/v3/index.ts index cc333f754..96fc617ae 100644 --- a/test/fixtures/04-translations/translations/v3/index.ts +++ b/test/fixtures/04-translations/translations/v3/index.ts @@ -1,9 +1,9 @@ import type { ConfigLoader } from '@balena/pinejs'; -import { getAbstractSqlModelFromFile } from '@balena/pinejs/out/bin/utils'; +import { getAbstractSqlModelFromFile } from '@balena/pinejs/out/bin/utils.js'; import type { AbstractSqlQuery } from '@balena/abstract-sql-compiler'; -export const v3AbstractSqlModel = getAbstractSqlModelFromFile( - __dirname + '/university.sbvr', +export const v3AbstractSqlModel = await getAbstractSqlModelFromFile( + import.meta.dirname + '/university.sbvr', undefined, ); diff --git a/test/fixtures/04-translations/translations/v4/index.ts b/test/fixtures/04-translations/translations/v4/index.ts index 5023ca28f..752de141b 100644 --- a/test/fixtures/04-translations/translations/v4/index.ts +++ b/test/fixtures/04-translations/translations/v4/index.ts @@ -1,8 +1,8 @@ -import { getAbstractSqlModelFromFile } from '@balena/pinejs/out/bin/utils'; +import { getAbstractSqlModelFromFile } from '@balena/pinejs/out/bin/utils.js'; import type { AbstractSqlQuery } from '@balena/abstract-sql-compiler'; -export const v4AbstractSqlModel = getAbstractSqlModelFromFile( - __dirname + '/university.sbvr', +export const v4AbstractSqlModel = await getAbstractSqlModelFromFile( + import.meta.dirname + '/university.sbvr', undefined, ); diff --git a/test/fixtures/04-translations/translations/v5/index.ts b/test/fixtures/04-translations/translations/v5/index.ts index 18d657cc2..59babafd3 100644 --- a/test/fixtures/04-translations/translations/v5/index.ts +++ b/test/fixtures/04-translations/translations/v5/index.ts @@ -1,8 +1,8 @@ -import { getAbstractSqlModelFromFile } from '@balena/pinejs/out/bin/utils'; +import { getAbstractSqlModelFromFile } from '@balena/pinejs/out/bin/utils.js'; import type { AbstractSqlQuery } from '@balena/abstract-sql-compiler'; -export const v5AbstractSqlModel = getAbstractSqlModelFromFile( - __dirname + '/university.sbvr', +export const v5AbstractSqlModel = await getAbstractSqlModelFromFile( + import.meta.dirname + '/university.sbvr', undefined, ); diff --git a/test/fixtures/05-request-cancellation/config.ts b/test/fixtures/05-request-cancellation/config.ts index bfd7299ee..aad10f9b8 100644 --- a/test/fixtures/05-request-cancellation/config.ts +++ b/test/fixtures/05-request-cancellation/config.ts @@ -2,7 +2,7 @@ import type { ConfigLoader } from '@balena/pinejs'; const apiRoot = 'example'; const modelName = 'example'; -const modelFile = __dirname + '/example.sbvr'; +const modelFile = import.meta.dirname + '/example.sbvr'; export default { models: [ diff --git a/test/fixtures/05-request-cancellation/hooks.ts b/test/fixtures/05-request-cancellation/hooks.ts index 7e8709cce..6f55665c8 100644 --- a/test/fixtures/05-request-cancellation/hooks.ts +++ b/test/fixtures/05-request-cancellation/hooks.ts @@ -1,6 +1,6 @@ import { setTimeout } from 'timers/promises'; import { sbvrUtils } from '@balena/pinejs'; -import { track } from './util'; +import { track } from './util.js'; sbvrUtils.addPureHook('POST', 'example', 'slow_resource', { async POSTRUN({ request, api }) { diff --git a/test/fixtures/05-request-cancellation/routes.ts b/test/fixtures/05-request-cancellation/routes.ts index 8c4532e93..60029e5b8 100644 --- a/test/fixtures/05-request-cancellation/routes.ts +++ b/test/fixtures/05-request-cancellation/routes.ts @@ -2,7 +2,7 @@ import type express from 'express'; import onFinished from 'on-finished'; import { sbvrUtils, errors } from '@balena/pinejs'; import { setTimeout } from 'timers/promises'; -import { track } from './util'; +import { track } from './util.js'; export const initRoutes = (app: express.Express) => { app.post('/slow-custom-endpoint', async (req, res) => { diff --git a/test/fixtures/06-webresource/config.ts b/test/fixtures/06-webresource/config.ts index 26d93ff0f..8e0ed064a 100644 --- a/test/fixtures/06-webresource/config.ts +++ b/test/fixtures/06-webresource/config.ts @@ -1,21 +1,20 @@ import type { ConfigLoader } from '@balena/pinejs'; -import { webResourceHandler } from '@balena/pinejs'; -import { v1AbstractSqlModel, v1Translations } from './translations/v1'; +import { S3Handler } from '@balena/pinejs-webresource-s3'; +import { v1AbstractSqlModel, v1Translations } from './translations/v1/index.js'; import { requiredVar, intVar } from '@balena/env-parsing'; const apiRoot = 'example'; const modelName = 'example'; -const modelFile = __dirname + '/example.sbvr'; +const modelFile = import.meta.dirname + '/example.sbvr'; -const s3Handler: webResourceHandler.WebResourceHandler = - new webResourceHandler.S3Handler({ - bucket: requiredVar('S3_STORAGE_ADAPTER_BUCKET'), - region: requiredVar('S3_REGION'), - accessKey: requiredVar('S3_ACCESS_KEY'), - secretKey: requiredVar('S3_SECRET_KEY'), - endpoint: requiredVar('S3_ENDPOINT'), - maxSize: intVar('PINEJS_WEBRESOURCE_MAXFILESIZE'), - }); +const s3Handler = new S3Handler({ + bucket: requiredVar('S3_STORAGE_ADAPTER_BUCKET'), + region: requiredVar('S3_REGION'), + accessKey: requiredVar('S3_ACCESS_KEY'), + secretKey: requiredVar('S3_SECRET_KEY'), + endpoint: requiredVar('S3_ENDPOINT'), + maxSize: intVar('PINEJS_WEBRESOURCE_MAXFILESIZE'), +}); export default { models: [ diff --git a/test/fixtures/06-webresource/translations/hooks.ts b/test/fixtures/06-webresource/translations/hooks.ts index 59bac1f9a..636c3972a 100644 --- a/test/fixtures/06-webresource/translations/hooks.ts +++ b/test/fixtures/06-webresource/translations/hooks.ts @@ -1 +1 @@ -import './v1/hooks'; +import './v1/hooks.js'; diff --git a/test/fixtures/06-webresource/translations/v1/index.ts b/test/fixtures/06-webresource/translations/v1/index.ts index 65615bebb..2c8b918f8 100644 --- a/test/fixtures/06-webresource/translations/v1/index.ts +++ b/test/fixtures/06-webresource/translations/v1/index.ts @@ -1,10 +1,10 @@ import type { ConfigLoader } from '@balena/pinejs'; -import { getAbstractSqlModelFromFile } from '@balena/pinejs/out/bin/utils'; +import { getAbstractSqlModelFromFile } from '@balena/pinejs/out/bin/utils.js'; export const toVersion = 'example'; -export const v1AbstractSqlModel = getAbstractSqlModelFromFile( - __dirname + '/example.sbvr', +export const v1AbstractSqlModel = await getAbstractSqlModelFromFile( + import.meta.dirname + '/example.sbvr', undefined, ); diff --git a/test/fixtures/07-permissions/config.ts b/test/fixtures/07-permissions/config.ts index eddb49318..130fc877f 100644 --- a/test/fixtures/07-permissions/config.ts +++ b/test/fixtures/07-permissions/config.ts @@ -2,7 +2,7 @@ import type { ConfigLoader } from '@balena/pinejs'; const apiRoot = 'university'; const modelName = 'university'; -const modelFile = __dirname + '/university.sbvr'; +const modelFile = import.meta.dirname + '/university.sbvr'; export default { models: [ diff --git a/test/fixtures/08-tasks/config.ts b/test/fixtures/08-tasks/config.ts index d3ff58ab4..955c72360 100644 --- a/test/fixtures/08-tasks/config.ts +++ b/test/fixtures/08-tasks/config.ts @@ -1,20 +1,25 @@ import type { ConfigLoader } from '@balena/pinejs'; +import { fileURLToPath } from 'node:url'; export default { models: [ { modelName: 'Auth', - modelFile: require.resolve('@balena/pinejs/out/sbvr-api/user.sbvr'), + modelFile: fileURLToPath( + import.meta.resolve('@balena/pinejs/out/sbvr-api/user.sbvr'), + ), apiRoot: 'Auth', }, { modelName: 'tasks', - modelFile: require.resolve('@balena/pinejs/out/tasks/tasks.sbvr'), + modelFile: fileURLToPath( + import.meta.resolve('@balena/pinejs/out/tasks/tasks.sbvr'), + ), apiRoot: 'tasks', }, { modelName: 'example', - modelFile: __dirname + '/example.sbvr', + modelFile: import.meta.dirname + '/example.sbvr', apiRoot: 'example', }, ], diff --git a/test/lib/pine-in-process.ts b/test/lib/pine-in-process.ts index 50fbbe11b..cd9fd6c90 100644 --- a/test/lib/pine-in-process.ts +++ b/test/lib/pine-in-process.ts @@ -1,9 +1,9 @@ import { exit } from 'process'; import cluster from 'node:cluster'; -import type { PineTestOptions } from './pine-init'; -import { init } from './pine-init'; +import type { PineTestOptions } from './pine-init.js'; +import { init } from './pine-init.js'; import { tasks } from '@balena/pinejs'; -import { PINE_TEST_SIGNALS } from './common'; +import { PINE_TEST_SIGNALS } from './common.js'; import { type Serializable } from 'child_process'; const createWorker = ( @@ -67,7 +67,7 @@ async function runApp(processArgs: PineTestOptions) { const { default: initConfig } = await import(processArgs.configPath); console.info(`listenPort: ${processArgs.listenPort}`); const app = await init( - initConfig.default, + initConfig, processArgs.listenPort, processArgs.withLoginRoute, ); @@ -79,16 +79,12 @@ async function runApp(processArgs: PineTestOptions) { // load task handlers if (processArgs.taskHandlersPath) { - const { - default: { initTaskHandlers }, - } = await import(processArgs.taskHandlersPath); + const { initTaskHandlers } = await import(processArgs.taskHandlersPath); initTaskHandlers(); } if (processArgs.routesPath) { - const { - default: { initRoutes }, - } = await import(processArgs.routesPath); + const { initRoutes } = await import(processArgs.routesPath); initRoutes(app); } diff --git a/test/lib/test-init.ts b/test/lib/test-init.ts index ceef04605..62b9da152 100644 --- a/test/lib/test-init.ts +++ b/test/lib/test-init.ts @@ -3,7 +3,7 @@ import { fork } from 'child_process'; import { boolVar } from '@balena/env-parsing'; import type { types } from '@balena/pinejs'; import { dbModule } from '@balena/pinejs'; -import type { PineTestOptions } from './pine-init'; +import type { PineTestOptions } from './pine-init.js'; export const listenPortDefault = 1337; export const testLocalServer = `http://localhost:${listenPortDefault}`; @@ -25,7 +25,7 @@ export async function testInit( await cleanDb(); } const testServer = fork( - __dirname + '/pine-in-process.ts', + import.meta.dirname + '/pine-in-process.ts', [JSON.stringify(processArgs)], { detached: false, diff --git a/tsconfig.dev.json b/tsconfig.dev.json index f0cbfb7fd..baea09633 100644 --- a/tsconfig.dev.json +++ b/tsconfig.dev.json @@ -1,13 +1,15 @@ { "extends": "./tsconfig.json", "compilerOptions": { - "rootDir": "." + "rootDir": ".", + "allowImportingTsExtensions": true }, "include": [ "build/**/*", + "bin/**/*", "src/**/*", "test/**/*", "typings/**/*.d.ts", - "Gruntfile.ts" + "Gruntfile.cts" ] } diff --git a/tsconfig.json b/tsconfig.json index 03e9038c5..c12880529 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "module": "Node16", + "module": "NodeNext", "strict": true, "strictFunctionTypes": false, "noImplicitThis": false, diff --git a/typings/lf-to-abstract-sql.d.ts b/typings/lf-to-abstract-sql.d.ts index a1a728577..aa8ce3a53 100644 --- a/typings/lf-to-abstract-sql.d.ts +++ b/typings/lf-to-abstract-sql.d.ts @@ -5,7 +5,7 @@ declare module '@balena/lf-to-abstract-sql' { export const LF2AbstractSQL: { createInstance: () => { match: (lfModel: LFModel, rule: 'Process') => AbstractSqlModel; - addTypes: (types: typeof sbvrTypes) => void; + addTypes: (types: typeof sbvrTypes.default) => void; reset: () => void; }; }; @@ -14,6 +14,6 @@ declare module '@balena/lf-to-abstract-sql' { _extend(obj: object): typeof LF2AbstractSQLPrep; }; export const createTranslator: ( - types: typeof sbvrTypes, + types: typeof sbvrTypes.default, ) => (lfModel: LFModel, rule: 'Process') => AbstractSqlModel; } diff --git a/typings/memoizee.d.ts b/typings/memoizee.d.ts index f374180bf..4cbcdd6d0 100644 --- a/typings/memoizee.d.ts +++ b/typings/memoizee.d.ts @@ -1,5 +1,5 @@ -declare module 'memoizee/weak' { - import type * as Memoize from 'memoizee'; +declare module 'memoizee/weak.js' { + import type Memoize from 'memoizee'; type FirstArg = T extends (arg1: infer U) => any ? U : any; type RestArgs = T extends (arg1: any, ...args: infer U) => any ? U : any[];