@true-sight/model-cli 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2 @@
1
+ import { type ManifestModel, type ModelManifest } from '@true-sight/model-schema';
2
+ export declare function assembleManifest(models: ManifestModel[]): ModelManifest;
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.assembleManifest = assembleManifest;
4
+ const model_schema_1 = require("@true-sight/model-schema");
5
+ function assembleManifest(models) {
6
+ return {
7
+ schemaVersion: model_schema_1.SCHEMA_VERSION,
8
+ generatedAt: new Date().toISOString(),
9
+ models,
10
+ };
11
+ }
@@ -0,0 +1,5 @@
1
+ export type CompiledEntry = {
2
+ jsPath: string;
3
+ cleanup: () => void;
4
+ };
5
+ export declare function compileEntryIfNeeded(absoluteEntry: string): Promise<CompiledEntry>;
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.compileEntryIfNeeded = compileEntryIfNeeded;
4
+ const node_child_process_1 = require("node:child_process");
5
+ const node_fs_1 = require("node:fs");
6
+ const node_os_1 = require("node:os");
7
+ const node_path_1 = require("node:path");
8
+ function findProjectTsconfig(absoluteEntry) {
9
+ let dir = (0, node_path_1.dirname)(absoluteEntry);
10
+ while (dir !== (0, node_path_1.dirname)(dir)) {
11
+ const candidate = (0, node_path_1.join)(dir, 'tsconfig.json');
12
+ if ((0, node_fs_1.existsSync)(candidate)) {
13
+ if (absoluteEntry.includes(`${(0, node_path_1.join)('packages', 'model-cli', 'test', 'fixtures')}`)) {
14
+ return undefined;
15
+ }
16
+ return candidate;
17
+ }
18
+ dir = (0, node_path_1.dirname)(dir);
19
+ }
20
+ return undefined;
21
+ }
22
+ function writeFixtureTsconfig(absoluteEntry, tempDir) {
23
+ const entryDir = (0, node_path_1.dirname)(absoluteEntry);
24
+ const compileConfigPath = (0, node_path_1.join)(tempDir, 'compile-tsconfig.json');
25
+ (0, node_fs_1.writeFileSync)(compileConfigPath, JSON.stringify({
26
+ compilerOptions: {
27
+ target: 'ES2023',
28
+ module: 'nodenext',
29
+ moduleResolution: 'nodenext',
30
+ strict: false,
31
+ experimentalDecorators: true,
32
+ emitDecoratorMetadata: true,
33
+ outDir: tempDir,
34
+ rootDir: entryDir,
35
+ },
36
+ include: [absoluteEntry],
37
+ }));
38
+ return compileConfigPath;
39
+ }
40
+ function readRootDir(tsconfigPath, absoluteEntry) {
41
+ const config = JSON.parse((0, node_fs_1.readFileSync)(tsconfigPath, 'utf8'));
42
+ if (config.compilerOptions?.rootDir) {
43
+ return (0, node_path_1.resolve)((0, node_path_1.dirname)(tsconfigPath), config.compilerOptions.rootDir);
44
+ }
45
+ return (0, node_path_1.dirname)(absoluteEntry);
46
+ }
47
+ async function compileEntryIfNeeded(absoluteEntry) {
48
+ if (!absoluteEntry.endsWith('.ts')) {
49
+ return { jsPath: absoluteEntry, cleanup: () => undefined };
50
+ }
51
+ const tempDir = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'model-cli-compile-'));
52
+ const projectTsconfig = findProjectTsconfig(absoluteEntry);
53
+ const compileConfigPath = projectTsconfig
54
+ ? projectTsconfig
55
+ : writeFixtureTsconfig(absoluteEntry, tempDir);
56
+ const tscArgs = projectTsconfig
57
+ ? ['tsc', '-p', compileConfigPath, '--outDir', tempDir, '--noCheck']
58
+ : ['tsc', '-p', compileConfigPath, '--noCheck'];
59
+ const result = (0, node_child_process_1.spawnSync)('npx', tscArgs, {
60
+ encoding: 'utf8',
61
+ cwd: projectTsconfig ? (0, node_path_1.dirname)(projectTsconfig) : undefined,
62
+ });
63
+ if (result.status !== 0) {
64
+ (0, node_fs_1.rmSync)(tempDir, { recursive: true, force: true });
65
+ const message = (result.stderr || result.stdout || 'tsc failed').trim();
66
+ throw new Error(message);
67
+ }
68
+ const rootDir = readRootDir(projectTsconfig ?? compileConfigPath, absoluteEntry);
69
+ const relJs = (0, node_path_1.relative)(rootDir, absoluteEntry).replace(/\.ts$/, '.js');
70
+ const jsPath = (0, node_path_1.join)(tempDir, relJs);
71
+ if (!(0, node_fs_1.existsSync)(jsPath)) {
72
+ (0, node_fs_1.rmSync)(tempDir, { recursive: true, force: true });
73
+ throw new Error(`Compiled entry not found: ${jsPath}`);
74
+ }
75
+ return {
76
+ jsPath,
77
+ cleanup: () => (0, node_fs_1.rmSync)(tempDir, { recursive: true, force: true }),
78
+ };
79
+ }
@@ -0,0 +1 @@
1
+ import 'reflect-metadata';
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ require("reflect-metadata");
4
+ const model_schema_1 = require("@true-sight/model-schema");
5
+ const node_module_1 = require("node:module");
6
+ const node_url_1 = require("node:url");
7
+ const requireEntry = (0, node_module_1.createRequire)(__filename);
8
+ function isModelConstructor(value) {
9
+ return typeof value === 'function';
10
+ }
11
+ async function loadEntry(entryPath) {
12
+ if (entryPath.endsWith('.cjs')) {
13
+ return requireEntry(entryPath);
14
+ }
15
+ return (await import((0, node_url_1.pathToFileURL)(entryPath).href));
16
+ }
17
+ async function main() {
18
+ const entryPath = process.argv[2];
19
+ if (!entryPath) {
20
+ console.error('Missing entry path');
21
+ process.exit(1);
22
+ }
23
+ try {
24
+ const loaded = await loadEntry(entryPath);
25
+ const models = loaded.models ?? loaded.default?.models;
26
+ if (!Array.isArray(models)) {
27
+ console.error('Entry must export `models` array of model classes');
28
+ process.exit(1);
29
+ }
30
+ if (!models.every(isModelConstructor)) {
31
+ console.error('Entry must export model class constructors in `models`');
32
+ process.exit(1);
33
+ }
34
+ process.stdout.write(JSON.stringify((0, model_schema_1.compileModels)(models)));
35
+ }
36
+ catch (error) {
37
+ const message = error instanceof Error ? error.message : String(error);
38
+ console.error(message);
39
+ process.exit(1);
40
+ }
41
+ }
42
+ main().catch((error) => {
43
+ const message = error instanceof Error ? error.message : String(error);
44
+ console.error(message);
45
+ process.exit(1);
46
+ });
@@ -0,0 +1,5 @@
1
+ import type { ManifestModel } from '@true-sight/model-schema';
2
+ export declare class LoadEntryError extends Error {
3
+ constructor(message: string);
4
+ }
5
+ export declare function loadModelsFromEntry(entryPath: string, cwd: string): Promise<ManifestModel[]>;
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LoadEntryError = void 0;
4
+ exports.loadModelsFromEntry = loadModelsFromEntry;
5
+ const node_child_process_1 = require("node:child_process");
6
+ const node_fs_1 = require("node:fs");
7
+ const node_path_1 = require("node:path");
8
+ const compile_entry_js_1 = require("./compile-entry.js");
9
+ class LoadEntryError extends Error {
10
+ constructor(message) {
11
+ super(message);
12
+ this.name = 'LoadEntryError';
13
+ }
14
+ }
15
+ exports.LoadEntryError = LoadEntryError;
16
+ function resolveNodePath(cwd) {
17
+ const candidates = [];
18
+ let dir = cwd;
19
+ while (dir !== (0, node_path_1.dirname)(dir)) {
20
+ candidates.push((0, node_path_1.join)(dir, 'node_modules'));
21
+ dir = (0, node_path_1.dirname)(dir);
22
+ }
23
+ return candidates.filter(node_fs_1.existsSync).join(process.platform === 'win32' ? ';' : ':');
24
+ }
25
+ function resolveRunnerPath() {
26
+ const adjacent = (0, node_path_1.resolve)((0, node_path_1.dirname)(__filename), 'load-entry-runner.js');
27
+ if ((0, node_fs_1.existsSync)(adjacent)) {
28
+ return adjacent;
29
+ }
30
+ const packageDist = (0, node_path_1.resolve)((0, node_path_1.dirname)(__filename), '../../dist/build/load-entry-runner.js');
31
+ if ((0, node_fs_1.existsSync)(packageDist)) {
32
+ return packageDist;
33
+ }
34
+ throw new LoadEntryError('Run npm run build -w @true-sight/model-cli first');
35
+ }
36
+ async function loadModelsFromEntry(entryPath, cwd) {
37
+ const absoluteEntry = (0, node_path_1.resolve)(cwd, entryPath);
38
+ if (!(0, node_fs_1.existsSync)(absoluteEntry)) {
39
+ throw new LoadEntryError(`Entry file not found: ${entryPath}`);
40
+ }
41
+ const { jsPath, cleanup } = await (0, compile_entry_js_1.compileEntryIfNeeded)(absoluteEntry);
42
+ const runnerPath = resolveRunnerPath();
43
+ try {
44
+ const result = (0, node_child_process_1.spawnSync)(process.execPath, [runnerPath, jsPath], {
45
+ encoding: 'utf8',
46
+ cwd,
47
+ maxBuffer: 10 * 1024 * 1024,
48
+ env: {
49
+ ...process.env,
50
+ NODE_PATH: resolveNodePath(cwd),
51
+ },
52
+ });
53
+ if (result.status !== 0) {
54
+ const message = (result.stderr || result.stdout || 'Failed to load entry').trim();
55
+ throw new LoadEntryError(message);
56
+ }
57
+ return JSON.parse(result.stdout);
58
+ }
59
+ finally {
60
+ cleanup();
61
+ }
62
+ }
@@ -0,0 +1,2 @@
1
+ import type { ModelManifest } from '@true-sight/model-schema';
2
+ export declare function writeManifest(manifest: ModelManifest, outPath: string, cwd: string): void;
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.writeManifest = writeManifest;
4
+ const node_fs_1 = require("node:fs");
5
+ const node_path_1 = require("node:path");
6
+ const MANIFEST_KEY_ORDER = [
7
+ 'schemaVersion',
8
+ 'generatedAt',
9
+ 'sourceCommit',
10
+ 'models',
11
+ ];
12
+ const MODEL_KEY_ORDER = ['name', 'label', 'connectionName', 'explores'];
13
+ const EXPLORE_KEY_ORDER = ['name', 'label', 'from', 'joins', 'views'];
14
+ const VIEW_KEY_ORDER = [
15
+ 'name',
16
+ 'label',
17
+ 'sql',
18
+ 'dimensions',
19
+ 'measurements',
20
+ ];
21
+ function orderKeys(value, keyOrder) {
22
+ const ordered = {};
23
+ for (const key of keyOrder) {
24
+ if (key in value) {
25
+ ordered[key] = value[key];
26
+ }
27
+ }
28
+ for (const key of Object.keys(value)) {
29
+ if (!(key in ordered)) {
30
+ ordered[key] = value[key];
31
+ }
32
+ }
33
+ return ordered;
34
+ }
35
+ function stableManifest(manifest) {
36
+ return orderKeys({
37
+ ...manifest,
38
+ models: manifest.models.map((model) => orderKeys({
39
+ ...model,
40
+ explores: model.explores.map((explore) => orderKeys({
41
+ ...explore,
42
+ views: explore.views.map((view) => orderKeys({ ...view }, VIEW_KEY_ORDER)),
43
+ }, EXPLORE_KEY_ORDER)),
44
+ }, MODEL_KEY_ORDER)),
45
+ }, MANIFEST_KEY_ORDER);
46
+ }
47
+ function writeManifest(manifest, outPath, cwd) {
48
+ const absoluteOut = (0, node_path_1.resolve)(cwd, outPath);
49
+ (0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(absoluteOut), { recursive: true });
50
+ const json = `${JSON.stringify(stableManifest(manifest), null, 2)}\n`;
51
+ (0, node_fs_1.writeFileSync)(absoluteOut, json, 'utf8');
52
+ }
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const commander_1 = require("commander");
5
+ const build_js_1 = require("./commands/build.js");
6
+ const sync_js_1 = require("./commands/sync.js");
7
+ const validate_js_1 = require("./commands/validate.js");
8
+ const program = new commander_1.Command();
9
+ program.name('truesight-model').description('True Sight model manifest CLI');
10
+ program
11
+ .command('build')
12
+ .description('Compile TS model definitions into manifest JSON')
13
+ .argument('[entry]', 'entry module path', 'src/index.ts')
14
+ .option('--out <path>', 'output manifest path', 'true-sight/manifest.json')
15
+ .action(async (entry, options) => {
16
+ const exitCode = await (0, build_js_1.runBuild)({ entry, out: options.out });
17
+ process.exit(exitCode);
18
+ });
19
+ program
20
+ .command('validate')
21
+ .description('Validate a pre-built manifest JSON file')
22
+ .argument('[path]', 'manifest path', 'true-sight/manifest.json')
23
+ .action(async (path) => {
24
+ const exitCode = await (0, validate_js_1.runValidate)({ path });
25
+ process.exit(exitCode);
26
+ });
27
+ program
28
+ .command('sync')
29
+ .description('Upload local manifest JSON to True Sight')
30
+ .argument('[path]', 'manifest path', 'true-sight/manifest.json')
31
+ .option('--url <url>', 'sync endpoint URL')
32
+ .option('--secret <secret>', 'HMAC signing secret')
33
+ .action(async (path, options) => {
34
+ const exitCode = await (0, sync_js_1.runSync)({
35
+ path,
36
+ url: options.url,
37
+ secret: options.secret,
38
+ });
39
+ process.exit(exitCode);
40
+ });
41
+ program.parseAsync(process.argv).catch((error) => {
42
+ const message = error instanceof Error ? error.message : String(error);
43
+ console.error(message);
44
+ process.exit(1);
45
+ });
@@ -0,0 +1,6 @@
1
+ export interface BuildOptions {
2
+ entry: string;
3
+ out: string;
4
+ cwd?: string;
5
+ }
6
+ export declare function runBuild(options: BuildOptions): Promise<number>;
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runBuild = runBuild;
4
+ const model_schema_1 = require("@true-sight/model-schema");
5
+ const assemble_manifest_js_1 = require("../build/assemble-manifest.js");
6
+ const load_entry_js_1 = require("../build/load-entry.js");
7
+ const write_manifest_js_1 = require("../build/write-manifest.js");
8
+ const print_errors_js_1 = require("../util/print-errors.js");
9
+ async function runBuild(options) {
10
+ const cwd = options.cwd ?? process.cwd();
11
+ try {
12
+ const models = await (0, load_entry_js_1.loadModelsFromEntry)(options.entry, cwd);
13
+ const manifest = (0, assemble_manifest_js_1.assembleManifest)(models);
14
+ const validation = (0, model_schema_1.validateManifest)(manifest);
15
+ if (!validation.ok) {
16
+ (0, print_errors_js_1.printValidationErrors)(validation.errors);
17
+ return 1;
18
+ }
19
+ (0, write_manifest_js_1.writeManifest)(manifest, options.out, cwd);
20
+ return 0;
21
+ }
22
+ catch (error) {
23
+ if (error instanceof load_entry_js_1.LoadEntryError) {
24
+ console.error(error.message);
25
+ return 1;
26
+ }
27
+ throw error;
28
+ }
29
+ }
@@ -0,0 +1,7 @@
1
+ export interface SyncOptions {
2
+ path: string;
3
+ url?: string;
4
+ secret?: string;
5
+ cwd?: string;
6
+ }
7
+ export declare function runSync(options: SyncOptions): Promise<number>;
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runSync = runSync;
4
+ const node_fs_1 = require("node:fs");
5
+ const node_path_1 = require("node:path");
6
+ const dotenv_1 = require("dotenv");
7
+ const model_schema_1 = require("@true-sight/model-schema");
8
+ const post_manifest_js_1 = require("../sync/post-manifest.js");
9
+ const sign_body_js_1 = require("../sync/sign-body.js");
10
+ const print_errors_js_1 = require("../util/print-errors.js");
11
+ async function runSync(options) {
12
+ const cwd = options.cwd ?? process.cwd();
13
+ (0, dotenv_1.config)({ path: (0, node_path_1.resolve)(cwd, '.env') });
14
+ const url = options.url ?? process.env.TRUESIGHT_SYNC_URL;
15
+ const secret = options.secret ?? process.env.TRUESIGHT_SYNC_SECRET;
16
+ if (!url || !secret) {
17
+ console.error('Sync requires TRUESIGHT_SYNC_URL and TRUESIGHT_SYNC_SECRET (or --url / --secret flags)');
18
+ return 1;
19
+ }
20
+ const absolutePath = (0, node_path_1.resolve)(cwd, options.path);
21
+ const rawBody = (0, node_fs_1.readFileSync)(absolutePath);
22
+ let parsed;
23
+ try {
24
+ parsed = JSON.parse(rawBody.toString('utf8'));
25
+ }
26
+ catch (error) {
27
+ const message = error instanceof Error ? error.message : String(error);
28
+ console.error(`Failed to read manifest at ${options.path}: ${message}`);
29
+ return 1;
30
+ }
31
+ const validation = (0, model_schema_1.validateManifest)(parsed);
32
+ if (!validation.ok) {
33
+ (0, print_errors_js_1.printValidationErrors)(validation.errors);
34
+ return 1;
35
+ }
36
+ const signatureHeader = (0, sign_body_js_1.signManifestBody)(rawBody, secret);
37
+ try {
38
+ const response = await (0, post_manifest_js_1.postManifest)(url, rawBody, signatureHeader);
39
+ if (response.status < 200 || response.status >= 300) {
40
+ console.error(`Sync failed (${response.status}): ${response.body}`);
41
+ return 1;
42
+ }
43
+ console.log(`Sync succeeded (${response.status})`);
44
+ return 0;
45
+ }
46
+ catch (error) {
47
+ const message = error instanceof Error ? error.message : String(error);
48
+ console.error(`Sync request failed: ${message}`);
49
+ return 1;
50
+ }
51
+ }
@@ -0,0 +1,5 @@
1
+ export interface ValidateOptions {
2
+ path: string;
3
+ cwd?: string;
4
+ }
5
+ export declare function runValidate(options: ValidateOptions): Promise<number>;
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runValidate = runValidate;
4
+ const node_fs_1 = require("node:fs");
5
+ const node_path_1 = require("node:path");
6
+ const model_schema_1 = require("@true-sight/model-schema");
7
+ const print_errors_js_1 = require("../util/print-errors.js");
8
+ async function runValidate(options) {
9
+ const cwd = options.cwd ?? process.cwd();
10
+ const absolutePath = (0, node_path_1.resolve)(cwd, options.path);
11
+ let parsed;
12
+ try {
13
+ parsed = JSON.parse((0, node_fs_1.readFileSync)(absolutePath, 'utf8'));
14
+ }
15
+ catch (error) {
16
+ const message = error instanceof Error ? error.message : String(error);
17
+ console.error(`Failed to read manifest at ${options.path}: ${message}`);
18
+ return 1;
19
+ }
20
+ const validation = (0, model_schema_1.validateManifest)(parsed);
21
+ if (!validation.ok) {
22
+ (0, print_errors_js_1.printValidationErrors)(validation.errors);
23
+ return 1;
24
+ }
25
+ return 0;
26
+ }
@@ -0,0 +1,5 @@
1
+ export interface PostManifestResult {
2
+ status: number;
3
+ body: string;
4
+ }
5
+ export declare function postManifest(url: string, rawBody: Buffer, signatureHeader: string): Promise<PostManifestResult>;
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.postManifest = postManifest;
4
+ async function postManifest(url, rawBody, signatureHeader) {
5
+ const response = await fetch(url, {
6
+ method: 'POST',
7
+ headers: {
8
+ 'Content-Type': 'application/json',
9
+ 'X-True-Sight-Signature-256': signatureHeader,
10
+ },
11
+ body: new Uint8Array(rawBody),
12
+ });
13
+ const body = await response.text();
14
+ return { status: response.status, body };
15
+ }
@@ -0,0 +1 @@
1
+ export declare function signManifestBody(rawBody: Buffer, secret: string): string;
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.signManifestBody = signManifestBody;
4
+ const node_crypto_1 = require("node:crypto");
5
+ function signManifestBody(rawBody, secret) {
6
+ const digest = (0, node_crypto_1.createHmac)('sha256', secret).update(rawBody).digest('hex');
7
+ return `sha256=${digest}`;
8
+ }
@@ -0,0 +1,2 @@
1
+ import type { ManifestValidationError } from '@true-sight/model-schema';
2
+ export declare function printValidationErrors(errors: ManifestValidationError[]): void;
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.printValidationErrors = printValidationErrors;
4
+ function printValidationErrors(errors) {
5
+ for (const error of errors) {
6
+ const prefix = error.path ? `${error.path}: ` : '';
7
+ console.error(`${prefix}${error.message}`);
8
+ }
9
+ }
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@true-sight/model-cli",
3
+ "version": "1.0.0",
4
+ "bin": {
5
+ "truesight-model": "./dist/cli.js"
6
+ },
7
+ "main": "./dist/cli.js",
8
+ "types": "./dist/cli.d.ts",
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "publishConfig": {
13
+ "access": "public"
14
+ },
15
+ "scripts": {
16
+ "build": "tsc",
17
+ "pretest": "npm run build -w @true-sight/model-schema && npm run build",
18
+ "test": "jest"
19
+ },
20
+ "dependencies": {
21
+ "@true-sight/model-schema": "^1.0.0",
22
+ "commander": "^13.1.0",
23
+ "dotenv": "^16.6.1"
24
+ },
25
+ "devDependencies": {
26
+ "@types/jest": "^30.0.0",
27
+ "jest": "^30.0.0",
28
+ "ts-jest": "^29.2.5",
29
+ "typescript": "^5.7.3"
30
+ },
31
+ "jest": {
32
+ "moduleFileExtensions": [
33
+ "js",
34
+ "json",
35
+ "ts"
36
+ ],
37
+ "rootDir": ".",
38
+ "testMatch": [
39
+ "**/test/**/*.spec.ts"
40
+ ],
41
+ "transform": {
42
+ "^.+\\.(t|j)s$": "ts-jest"
43
+ },
44
+ "moduleNameMapper": {
45
+ "^(\\.{1,2}/.*)\\.js$": "$1"
46
+ },
47
+ "testEnvironment": "node"
48
+ }
49
+ }