diff --git a/.eslintrc.js b/.eslintrc.js index a7b20048d9..7431172f95 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -108,7 +108,7 @@ module.exports = { }, overrides: [ { - files: ['**/*/*/test/**/*.js'], + files: ['**/*/*/test/**/*.js', '**/*/*/test/**/*.ts'], extends: ['plugin:ava/recommended'], env: { browser: true, diff --git a/packages/core/src/options.js b/packages/core/src/options.js new file mode 100644 index 0000000000..496001d48b --- /dev/null +++ b/packages/core/src/options.js @@ -0,0 +1,30 @@ +// @ts-check + +/** + * Value of the {@link LavaMoatOpts.scuttleGlobalThis} option. + * + * @typedef LavaMoatScuttleOpts + * @property {boolean} [enabled] + * @property {string[]} [exceptions] + * @property {string} [scuttlerName] + */ + +/** + * Options for LavaMoat + * + * @typedef LavaMoatOpts + * @property {LavaMoatScuttleOpts} [scuttleGlobalThis] Enable or disable + * scuttling of `globalThis` + * @property {string[]} [scuttleGlobalThisExceptions] + * @property {boolean} [writeAutoPolicy] Automatically write a policy file + * @property {boolean} [writeAutoPolicyDebug] Automatically write a debug policy + * file + * @property {boolean} [writeAutoPolicyAndRun] Automatically write a policy file + * and run the application + * @property {string} [policyPath] Path to policy file + * @property {string} [policyDebugPath] Path to policy debug file + * @property {string} [policyOverridePath] Path to policy override file + * @property {string} [projectRoot] Path to project root + * @property {boolean} [debugMode] Enable debug mode + * @property {boolean} [statsMode] Enable stats mode + */ diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index e64d55de01..3d1ba72ff7 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -1,5 +1,6 @@ +export type * from './generatePolicy' export * from './index' export type * from './moduleRecord' +export type * from './options' export type * from './parseForPolicy' export type * from './schema' -export type * from './generatePolicy' diff --git a/packages/core/test/scenarios/scenario.d.ts b/packages/core/test/scenario.ts similarity index 79% rename from packages/core/test/scenarios/scenario.d.ts rename to packages/core/test/scenario.ts index 3619a9b9c4..2cc1581954 100644 --- a/packages/core/test/scenarios/scenario.d.ts +++ b/packages/core/test/scenario.ts @@ -1,8 +1,7 @@ -// @ts-check - import type { ExecutionContext } from 'ava' -import type { LavaMoatOpts } from '../../../node/src/defaults' -import type { LavaMoatPolicySchema } from '../../schema' +import { ModuleInitializer } from '../src/moduleRecord' +import type { LavaMoatOpts } from '../src/options' +import { LavaMoatPolicy, LavaMoatPolicyOverrides } from '../src/schema' export type ScenarioType = 'truthy' | 'falsy' | 'deepEqual' @@ -31,11 +30,7 @@ export interface NormalizedScenarioJSFile extends ScenarioFile { export interface NormalizedBuiltin extends NormalizedScenarioJSFile { specifier: string - moduleInitializer: ( - exports: Record, - require: (id: string) => any, - module: Record - ) => void + moduleInitializer: ModuleInitializer } /** * Scenario file as provided by user @@ -54,21 +49,17 @@ export interface ScenarioFile { type?: ScenarioFileType entry?: boolean importMap?: Record - moduleInitializer?: ( - exports: Record, - require: (id: string) => any, - module: Record - ) => void + moduleInitializer?: ModuleInitializer } export type ScenarioSourceFn = () => void export interface Scenario { name?: string - config?: LavaMoatPolicySchema - configOverride?: LavaMoatPolicySchema + config?: LavaMoatPolicy + configOverride?: LavaMoatPolicyOverrides expectedFailure?: boolean - expectedResult?: any + expectedResult?: Result defineEntry?: ScenarioSourceFn defineOne?: ScenarioSourceFn defineTwo?: ScenarioSourceFn @@ -77,14 +68,14 @@ export interface Scenario { expectedFailureMessageRegex?: RegExp files?: Record defaultPolicy?: boolean - builtin?: Record - context?: Record + builtin?: Record + context?: Record opts?: LavaMoatOpts dir?: string checkPostRun?: ScenarioCheckPostRunFn checkError?: ScenarioCheckErrorFn checkResult?: ScenarioCheckResultFn - kernelArgs?: Record + kernelArgs?: Record beforeCreateKernel?: (scenario: NormalizedScenario) => void } @@ -109,8 +100,8 @@ export type NormalizedScenario = Required< > & Pick, 'dir' | 'kernelArgs' | 'beforeCreateKernel'> & { entries: string[] - globalThis?: Record - vmContext?: Record + globalThis?: Record + vmContext?: Record } export type ScenarioCheckPostRunFn = ( diff --git a/packages/core/test/tsconfig.json b/packages/core/test/tsconfig.json index 0634246577..6159e87b9c 100644 --- a/packages/core/test/tsconfig.json +++ b/packages/core/test/tsconfig.json @@ -1,10 +1,12 @@ { - "extends": "../../../.config/tsconfig.test.json", + // not extending the test config b/c we must output some types + "extends": "../../../.config/tsconfig.src.json", "compilerOptions": { "checkJs": false, - "rootDir": "." + "rootDir": "..", + "outDir": "../types" }, - "include": ["**/*"], + "include": ["**/*.js", "**/*.ts"], "references": [ { "path": "../src/tsconfig.json" diff --git a/packages/core/test/util.js b/packages/core/test/util.js index a9ad3189a0..a4e292d3f5 100644 --- a/packages/core/test/util.js +++ b/packages/core/test/util.js @@ -30,7 +30,7 @@ module.exports = { /** * @typedef {Partial & { - * files: import('./scenarios/scenario').NormalizedScenarioJSFile[] + * files: import('./scenario').NormalizedScenarioJSFile[] * }} GeneratePolicyFromFilesOpts */ @@ -41,17 +41,17 @@ module.exports = { async function generatePolicyFromFiles({ files, ...opts }) { const config = await parseForPolicy({ moduleSpecifier: - /** @type {import('./scenarios/scenario').NormalizedScenarioJSFile} */ ( + /** @type {import('./scenario').NormalizedScenarioJSFile} */ ( files.find((file) => file.entry) ).specifier, resolveHook: (requestedName, parentAddress) => { - return /** @type {import('./scenarios/scenario').NormalizedScenarioJSFile} */ ( + return /** @type {import('./scenario').NormalizedScenarioJSFile} */ ( files.find((file) => file.specifier === parentAddress) ).importMap[requestedName] }, importHook: async (address) => { return new LavamoatModuleRecord( - /** @type {import('./scenarios/scenario').NormalizedBuiltin} */ ( + /** @type {import('./scenario').NormalizedBuiltin} */ ( files.find((file) => file.specifier === address) ) ) @@ -69,8 +69,8 @@ async function generatePolicyFromFiles({ files, ...opts }) { * running. * * @template [Result=unknown] Default is `unknown` - * @param {import('./scenarios/scenario').Scenario} scenario - * @returns {import('./scenarios/scenario').NormalizedScenario} + * @param {import('./scenario').Scenario} scenario + * @returns {import('./scenario').NormalizedScenario<{ value: string }>} */ function createScenarioFromScaffold({ name = 'template scenario', @@ -80,12 +80,12 @@ function createScenarioFromScaffold({ testType = 'deepEqual', checkPostRun = async (t, result, err, scenario) => { if (err) { - await /** @type {import('./scenarios/scenario').ScenarioCheckErrorFn} */ ( + await /** @type {import('./scenario').ScenarioCheckErrorFn} */ ( scenario.checkError )(t, err, scenario) } else { // assumes `result` is not undefined - await /** @type {import('./scenarios/scenario').ScenarioCheckResultFn} */ ( + await /** @type {import('./scenario').ScenarioCheckResultFn} */ ( scenario.checkResult )(t, /** @type {Result} */ (result), scenario) } @@ -298,8 +298,11 @@ function createScenarioFromScaffold({ expectedFailureMessageRegex, entries: ['entry.js'], files: _files, - config: _config, - configOverride: _configOverride, + config: /** @type {import('../src/schema').LavaMoatPolicy} */ (_config), + configOverride: + /** @type {import('../src/schema').LavaMoatPolicyOverrides} */ ( + _configOverride + ), context, opts, dir, @@ -342,7 +345,7 @@ function createHookedConsole() { * * @template [Result=unknown] Default is `unknown` * @typedef PlatformRunScenarioOpts - * @property {import('./scenarios/scenario').NormalizedScenario} scenario + * @property {import('./scenario').NormalizedScenario} scenario * @property {boolean} [runWithPrecompiledModules] * @property {(...args: any[]) => void} [log] */ @@ -446,9 +449,8 @@ async function runScenario({ scenario, runWithPrecompiledModules = false }) { */ getRelativeModuleId: (id, relative) => { return ( - /** @type {import('./scenarios/scenario').NormalizedScenarioJSFile} */ ( - files[id] - ).importMap[relative] || relative + /** @type {import('./scenario').NormalizedScenarioJSFile} */ (files[id]) + .importMap[relative] || relative ) }, prepareModuleInitializerArgs, @@ -479,8 +481,8 @@ async function runScenario({ scenario, runWithPrecompiledModules = false }) { * @param {Object} options - The options for preparing the scenario. * @param {FsPromiseApi} [options.fs] - The file system module to use (default: * `node:fs/promises`). - * @param {import('./scenarios/scenario').Scenario} options.scenario - The - * scenario object containing the files to write. + * @param {import('./scenario').Scenario} options.scenario - The scenario object + * containing the files to write. * @param {string} [options.policyName='policies'] - The name of the policy * directory (default: 'policies'). Default is `'policies'` * @param {string} [options.projectDir] - The project directory path. @@ -522,9 +524,7 @@ async function prepareScenarioOnDisk({ filesToWrite.map(async (file) => { const fullPath = path.join( /** @type {string} */ (projectDir), - /** @type {import('./scenarios/scenario').NormalizedScenarioJSFile} */ ( - file - ).file + /** @type {import('./scenario').NormalizedScenarioJSFile} */ (file).file ) const dirname = path.dirname(fullPath) await fs.mkdir(dirname, { recursive: true }) @@ -538,7 +538,7 @@ async function prepareScenarioOnDisk({ } /** - * @param {Record} files + * @param {Record} files * @returns */ function fillInFileDetails(files) { @@ -566,7 +566,7 @@ function moduleDataForBuiltin(builtinObj, name) { file: name, package: name, type: 'builtin', - /** @type {import('./scenarios/scenario').ScenarioFile['moduleInitializer']} */ + /** @type {import('./scenario').ScenarioFile['moduleInitializer']} */ moduleInitializer: (_, _2, module) => { module.exports = builtinObj[name] }, @@ -647,7 +647,7 @@ function evaluateWithSourceUrl(filename, content, context) { * @returns {Promise} */ async function createConfigForTest(testFn, opts = {}) { - /** @type {import('./scenarios/scenario').NormalizedScenarioJSFile[]} */ + /** @type {import('./scenario').NormalizedScenarioJSFile[]} */ const files = [ { type: 'js', @@ -676,21 +676,20 @@ async function createConfigForTest(testFn, opts = {}) { /** * @param {object} opts - * @param {import('./scenarios/scenario').Scenario} opts.scenario + * @param {import('./scenario').Scenario} opts.scenario * @param {Partial} [opts.opts] */ async function autoConfigForScenario({ scenario, opts = {} }) { - const files = - /** @type {import('./scenarios/scenario').NormalizedScenarioJSFile[]} */ ( - Object.values(scenario.files ?? {}) - ) + const files = /** @type {import('./scenario').NormalizedScenarioJSFile[]} */ ( + Object.values(scenario.files ?? {}) + ) const policy = await generatePolicyFromFiles({ files, ...opts }) scenario.config = policy } /** * @param {object} opts - * @param {import('./scenarios/scenario').NormalizedScenario} opts.scenario + * @param {import('./scenario').NormalizedScenario} opts.scenario */ function convertOptsToArgs({ scenario }) { const { entries } = scenario @@ -712,7 +711,7 @@ function functionToString(func) { /** * @template [Result=unknown] Default is `unknown` * @param {import('ava').ExecutionContext} t - * @param {import('./scenarios/scenario').NormalizedScenario} scenario + * @param {import('./scenario').NormalizedScenario} scenario * @param {PlatformRunScenario} platformRunScenario * @returns {Promise} */ diff --git a/packages/node/src/defaults.js b/packages/node/src/defaults.js index 18a83a11fc..8965474314 100644 --- a/packages/node/src/defaults.js +++ b/packages/node/src/defaults.js @@ -2,32 +2,7 @@ const { getDefaultPaths } = require('lavamoat-core') const defaultPaths = getDefaultPaths('node') -/** - * @typedef LavaMoatScuttleOpts - * @property {boolean} [enabled] - * @property {string[]} [exceptions] - * @property {string} [scuttlerName] - */ - -/** - * @typedef LavaMoatOpts - * @property {LavaMoatScuttleOpts} [scuttleGlobalThis] Enable or disable - * scuttling of `globalThis` - * @property {string[]} [scuttleGlobalThisExceptions] - * @property {boolean} [writeAutoPolicy] Automatically write a policy file - * @property {boolean} [writeAutoPolicyDebug] Automatically write a debug policy - * file - * @property {boolean} [writeAutoPolicyAndRun] Automatically write a policy file - * and run the application - * @property {string} [policyPath] Path to policy file - * @property {string} [policyDebugPath] Path to policy debug file - * @property {string} [policyOverridePath] Path to policy override file - * @property {string} [projectRoot] Path to project root - * @property {boolean} [debugMode] Enable debug mode - * @property {boolean} [statsMode] Enable stats mode - */ - -/** @type {LavaMoatOpts} */ +/** @type {import('lavamoat-core').LavaMoatOpts} */ const defaults = { scuttleGlobalThis: {}, scuttleGlobalThisExceptions: [],