@forklaunch/testing 0.0.2 → 0.0.3

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.
package/lib/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { StartedTestContainer } from 'testcontainers';
2
- import { MikroORM } from '@mikro-orm/core';
2
+ import { MikroORM, Options } from '@mikro-orm/core';
3
3
  import Redis from 'ioredis';
4
4
 
5
5
  type DatabaseType = 'postgres' | 'postgresql' | 'mysql' | 'mariadb' | 'mongodb' | 'mongo' | 'mssql' | 'libsql' | 'sqlite' | 'better-sqlite';
@@ -86,9 +86,9 @@ declare function setupTestEnvironment(config: TestEnvConfig): void;
86
86
 
87
87
  interface MikroOrmTestConfig {
88
88
  /**
89
- * Path to the MikroORM config file (e.g., '../mikro-orm.config')
89
+ * MikroORM config object (imported from mikro-orm.config)
90
90
  */
91
- mikroOrmConfigPath: string;
91
+ mikroOrmConfig: Options;
92
92
  /**
93
93
  * Database type (postgres, mysql, mongodb, etc.)
94
94
  */
@@ -119,9 +119,10 @@ declare function clearTestDatabase(orm: MikroORM, redis?: Redis): Promise<void>;
119
119
 
120
120
  interface BlueprintTestConfig {
121
121
  /**
122
- * Path to the MikroORM config file (e.g., '../mikro-orm.config')
122
+ * Function that imports and returns the MikroORM config
123
+ * This is called AFTER environment variables are set
123
124
  */
124
- mikroOrmConfigPath: string;
125
+ getConfig: () => Promise<Options>;
125
126
  /**
126
127
  * Database type (postgres, mysql, mongodb, etc.)
127
128
  * @default 'postgres'
package/lib/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { StartedTestContainer } from 'testcontainers';
2
- import { MikroORM } from '@mikro-orm/core';
2
+ import { MikroORM, Options } from '@mikro-orm/core';
3
3
  import Redis from 'ioredis';
4
4
 
5
5
  type DatabaseType = 'postgres' | 'postgresql' | 'mysql' | 'mariadb' | 'mongodb' | 'mongo' | 'mssql' | 'libsql' | 'sqlite' | 'better-sqlite';
@@ -86,9 +86,9 @@ declare function setupTestEnvironment(config: TestEnvConfig): void;
86
86
 
87
87
  interface MikroOrmTestConfig {
88
88
  /**
89
- * Path to the MikroORM config file (e.g., '../mikro-orm.config')
89
+ * MikroORM config object (imported from mikro-orm.config)
90
90
  */
91
- mikroOrmConfigPath: string;
91
+ mikroOrmConfig: Options;
92
92
  /**
93
93
  * Database type (postgres, mysql, mongodb, etc.)
94
94
  */
@@ -119,9 +119,10 @@ declare function clearTestDatabase(orm: MikroORM, redis?: Redis): Promise<void>;
119
119
 
120
120
  interface BlueprintTestConfig {
121
121
  /**
122
- * Path to the MikroORM config file (e.g., '../mikro-orm.config')
122
+ * Function that imports and returns the MikroORM config
123
+ * This is called AFTER environment variables are set
123
124
  */
124
- mikroOrmConfigPath: string;
125
+ getConfig: () => Promise<Options>;
125
126
  /**
126
127
  * Database type (postgres, mysql, mongodb, etc.)
127
128
  * @default 'postgres'
package/lib/index.js CHANGED
@@ -146,8 +146,11 @@ var TestContainerManager = class {
146
146
  */
147
147
  async setupMSSQLContainer(config = {}) {
148
148
  const {
149
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
149
150
  user = "SA",
151
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
150
152
  password = "Test_Password123!",
153
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
151
154
  database = "test_db",
152
155
  saPassword = "Test_Password123!"
153
156
  } = config;
@@ -271,14 +274,13 @@ function getDatabasePort2(type) {
271
274
  }
272
275
  async function setupTestORM(config) {
273
276
  const {
274
- mikroOrmConfigPath,
277
+ mikroOrmConfig,
275
278
  databaseType,
276
279
  useMigrations = false,
277
280
  container
278
281
  } = config;
279
- const { default: mikroOrmConfig } = await import(mikroOrmConfigPath);
280
282
  const dbPort = getDatabasePort2(databaseType);
281
- let ormConfig;
283
+ let ormConfig = {};
282
284
  if (databaseType === "sqlite" || databaseType === "better-sqlite" || databaseType === "libsql") {
283
285
  ormConfig = {
284
286
  ...mikroOrmConfig,
@@ -293,8 +295,7 @@ async function setupTestORM(config) {
293
295
  }
294
296
  } : {
295
297
  schemaGenerator: {
296
- createForeignKeyConstraints: false,
297
- wrap: false
298
+ createForeignKeyConstraints: false
298
299
  }
299
300
  }
300
301
  };
@@ -315,8 +316,7 @@ async function setupTestORM(config) {
315
316
  }
316
317
  } : {
317
318
  schemaGenerator: {
318
- createForeignKeyConstraints: false,
319
- wrap: false
319
+ createForeignKeyConstraints: false
320
320
  }
321
321
  }
322
322
  };
@@ -369,8 +369,9 @@ var BlueprintTestHarness = class {
369
369
  redis: redisContainer,
370
370
  customVars: this.config.customEnvVars
371
371
  });
372
+ const mikroOrmConfig = await this.config.getConfig();
372
373
  const orm = await setupTestORM({
373
- mikroOrmConfigPath: this.config.mikroOrmConfigPath,
374
+ mikroOrmConfig,
374
375
  databaseType,
375
376
  useMigrations: this.config.useMigrations,
376
377
  migrationsPath: this.config.migrationsPath,
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/containers.ts","../src/environment.ts","../src/database.ts","../src/harness.ts","../src/tokens.ts"],"sourcesContent":["/**\n * @forklaunch/testing\n *\n * Testing utilities for forklaunch-js blueprints\n *\n * @packageDocumentation\n */\n\n// Container management\nexport {\n DatabaseConfig,\n DatabaseType,\n MongoDBConfig,\n MSSQLConfig,\n MySQLConfig,\n PostgresConfig,\n RedisConfig,\n SQLiteConfig,\n TestContainerManager\n} from './containers';\n\nexport { setupTestEnvironment, TestEnvConfig } from './environment';\n\nexport {\n clearTestDatabase,\n MikroOrmTestConfig,\n setupTestORM\n} from './database';\n\nexport {\n BlueprintTestConfig,\n BlueprintTestHarness,\n TestSetupResult\n} from './harness';\n\nexport { TEST_TOKENS } from './tokens';\n","import { GenericContainer, StartedTestContainer } from 'testcontainers';\n\nexport type DatabaseType =\n | 'postgres'\n | 'postgresql'\n | 'mysql'\n | 'mariadb'\n | 'mongodb'\n | 'mongo'\n | 'mssql'\n | 'libsql'\n | 'sqlite'\n | 'better-sqlite';\n\nexport interface PostgresConfig {\n user?: string;\n password?: string;\n database?: string;\n command?: string[];\n}\n\nexport interface MySQLConfig {\n user?: string;\n password?: string;\n database?: string;\n rootPassword?: string;\n}\n\nexport interface MongoDBConfig {\n user?: string;\n password?: string;\n database?: string;\n}\n\nexport interface MSSQLConfig {\n user?: string;\n password?: string;\n database?: string;\n saPassword?: string;\n}\n\nexport interface SQLiteConfig {\n database?: string;\n}\n\nexport interface RedisConfig {\n command?: string[];\n}\n\nexport type DatabaseConfig =\n | PostgresConfig\n | MySQLConfig\n | MongoDBConfig\n | MSSQLConfig\n | SQLiteConfig;\n\n/**\n * Manages test containers (PostgreSQL, MySQL, MongoDB, Redis, etc.) for E2E testing\n */\nexport class TestContainerManager {\n private containers: StartedTestContainer[] = [];\n\n /**\n * Setup database container based on type\n */\n async setupDatabaseContainer(\n type: DatabaseType,\n config: DatabaseConfig = {}\n ): Promise<StartedTestContainer | null> {\n const normalizedType = this.normalizeDatabaseType(type);\n\n switch (normalizedType) {\n case 'postgres':\n return this.setupPostgresContainer(config as PostgresConfig);\n case 'mysql':\n return this.setupMySQLContainer(config as MySQLConfig);\n case 'mongodb':\n return this.setupMongoDBContainer(config as MongoDBConfig);\n case 'mssql':\n return this.setupMSSQLContainer(config as MSSQLConfig);\n case 'sqlite':\n // SQLite doesn't need a container (file-based)\n return null;\n default:\n throw new Error(`Unsupported database type: ${type}`);\n }\n }\n\n /**\n * Normalize database type aliases\n */\n private normalizeDatabaseType(\n type: DatabaseType\n ): 'postgres' | 'mysql' | 'mongodb' | 'mssql' | 'sqlite' {\n switch (type) {\n case 'postgres':\n case 'postgresql':\n return 'postgres';\n case 'mysql':\n case 'mariadb':\n return 'mysql';\n case 'mongodb':\n case 'mongo':\n return 'mongodb';\n case 'mssql':\n return 'mssql';\n case 'sqlite':\n case 'better-sqlite':\n case 'libsql':\n return 'sqlite';\n default:\n throw new Error(`Unknown database type: ${type}`);\n }\n }\n\n /**\n * Setup PostgreSQL test container\n */\n async setupPostgresContainer(\n config: PostgresConfig = {}\n ): Promise<StartedTestContainer> {\n const {\n user = 'test_user',\n password = 'test_password',\n database = 'test_db',\n command = ['postgres', '-c', 'log_statement=all']\n } = config;\n\n const container = await new GenericContainer('postgres:latest')\n .withExposedPorts(5432)\n .withEnvironment({\n POSTGRES_USER: user,\n POSTGRES_PASSWORD: password,\n POSTGRES_DB: database\n })\n .withCommand(command)\n .start();\n\n this.containers.push(container);\n return container;\n }\n\n /**\n * Setup MySQL test container\n */\n async setupMySQLContainer(\n config: MySQLConfig = {}\n ): Promise<StartedTestContainer> {\n const {\n user = 'test_user',\n password = 'test_password',\n database = 'test_db',\n rootPassword = 'root_password'\n } = config;\n\n const container = await new GenericContainer('mysql:8')\n .withExposedPorts(3306)\n .withEnvironment({\n MYSQL_ROOT_PASSWORD: rootPassword,\n MYSQL_DATABASE: database,\n MYSQL_USER: user,\n MYSQL_PASSWORD: password\n })\n .start();\n\n this.containers.push(container);\n return container;\n }\n\n /**\n * Setup MongoDB test container\n */\n async setupMongoDBContainer(\n config: MongoDBConfig = {}\n ): Promise<StartedTestContainer> {\n const {\n user = 'test_user',\n password = 'test_password',\n database = 'test_db'\n } = config;\n\n const container = await new GenericContainer('mongo:latest')\n .withExposedPorts(27017)\n .withEnvironment({\n MONGO_INITDB_ROOT_USERNAME: user,\n MONGO_INITDB_ROOT_PASSWORD: password,\n MONGO_INITDB_DATABASE: database\n })\n .start();\n\n this.containers.push(container);\n return container;\n }\n\n /**\n * Setup Microsoft SQL Server test container\n */\n async setupMSSQLContainer(\n config: MSSQLConfig = {}\n ): Promise<StartedTestContainer> {\n const {\n user = 'SA',\n password = 'Test_Password123!',\n database = 'test_db',\n saPassword = 'Test_Password123!'\n } = config;\n\n const container = await new GenericContainer(\n 'mcr.microsoft.com/mssql/server:2022-latest'\n )\n .withExposedPorts(1433)\n .withEnvironment({\n ACCEPT_EULA: 'Y',\n SA_PASSWORD: saPassword,\n MSSQL_PID: 'Developer'\n })\n .start();\n\n // Wait a bit for SQL Server to be ready\n await new Promise((resolve) => setTimeout(resolve, 3000));\n\n this.containers.push(container);\n return container;\n }\n\n /**\n * Setup Redis test container\n */\n async setupRedisContainer(\n config: RedisConfig = {}\n ): Promise<StartedTestContainer> {\n const { command = ['redis-server', '--appendonly', 'yes'] } = config;\n\n const container = await new GenericContainer('redis:latest')\n .withExposedPorts(6379)\n .withCommand(command)\n .start();\n\n this.containers.push(container);\n return container;\n }\n\n /**\n * Cleanup all containers\n */\n async cleanup(): Promise<void> {\n await Promise.all(\n this.containers.map((container) =>\n container.stop({ remove: true, removeVolumes: true }).catch(() => {\n // Ignore cleanup errors\n })\n )\n );\n this.containers = [];\n }\n}\n","import { StartedTestContainer } from 'testcontainers';\nimport { DatabaseType } from './containers';\n\nexport interface TestEnvConfig {\n database: StartedTestContainer | null;\n databaseType: DatabaseType;\n redis?: StartedTestContainer;\n hmacSecret?: string;\n customVars?: Record<string, string>;\n}\n\n/**\n * Get the default port for a database type\n */\nfunction getDatabasePort(type: DatabaseType): number {\n switch (type) {\n case 'postgres':\n case 'postgresql':\n return 5432;\n case 'mysql':\n case 'mariadb':\n return 3306;\n case 'mongodb':\n case 'mongo':\n return 27017;\n case 'mssql':\n return 1433;\n case 'sqlite':\n case 'better-sqlite':\n case 'libsql':\n return 0; // SQLite is file-based, no port\n default:\n return 5432;\n }\n}\n\n/**\n * Setup test environment variables for a blueprint test\n */\nexport function setupTestEnvironment(config: TestEnvConfig): void {\n const {\n database,\n databaseType,\n redis,\n hmacSecret = 'test-secret-key',\n customVars = {}\n } = config;\n\n const dbPort = getDatabasePort(databaseType);\n\n // Database environment variables\n process.env.DB_NAME = 'test_db';\n\n // SQLite databases are file-based, no container needed\n if (\n databaseType === 'sqlite' ||\n databaseType === 'better-sqlite' ||\n databaseType === 'libsql'\n ) {\n process.env.DB_PATH = ':memory:'; // In-memory SQLite for tests\n } else if (database) {\n process.env.DB_HOST = database.getHost();\n process.env.DB_USER = databaseType === 'mssql' ? 'SA' : 'test_user';\n process.env.DB_PASSWORD =\n databaseType === 'mssql' ? 'Test_Password123!' : 'test_password';\n process.env.DB_PORT = database.getMappedPort(dbPort).toString();\n }\n\n // Redis environment variables (if provided)\n if (redis) {\n process.env.REDIS_URL = `redis://${redis.getHost()}:${redis.getMappedPort(6379)}`;\n }\n\n // Standard test environment variables\n process.env.HMAC_SECRET_KEY = hmacSecret;\n process.env.JWKS_PUBLIC_KEY_URL =\n 'http://localhost:3000/.well-known/jwks.json';\n process.env.OTEL_SERVICE_NAME = 'test-service';\n process.env.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://localhost:4318';\n process.env.HOST = 'localhost';\n process.env.PORT = '3000';\n process.env.NODE_ENV = 'test';\n process.env.VERSION = 'v1';\n process.env.DOCS_PATH = '/docs';\n process.env.OTEL_LEVEL = 'info';\n process.env.DOTENV_FILE_PATH = '.env.test';\n\n // Custom environment variables\n Object.entries(customVars).forEach(([key, value]) => {\n process.env[key] = value;\n });\n}\n","import { MikroORM } from '@mikro-orm/core';\nimport Redis from 'ioredis';\nimport { StartedTestContainer } from 'testcontainers';\nimport { DatabaseType } from './containers';\n\nexport interface MikroOrmTestConfig {\n /**\n * Path to the MikroORM config file (e.g., '../mikro-orm.config')\n */\n mikroOrmConfigPath: string;\n\n /**\n * Database type (postgres, mysql, mongodb, etc.)\n */\n databaseType: DatabaseType;\n\n /**\n * Whether to use migrations (true) or schema generation (false)\n * - true: IAM blueprints (uses getMigrator().up())\n * - false: Billing blueprints (uses getSchemaGenerator().createSchema())\n */\n useMigrations?: boolean;\n\n /**\n * Path to migrations directory (required if useMigrations is true)\n */\n migrationsPath?: string;\n\n /**\n * Database container instance (null for file-based databases like SQLite)\n */\n container: StartedTestContainer | null;\n}\n\n/**\n * Get the default port for a database type\n */\nfunction getDatabasePort(type: DatabaseType): number {\n switch (type) {\n case 'postgres':\n case 'postgresql':\n return 5432;\n case 'mysql':\n case 'mariadb':\n return 3306;\n case 'mongodb':\n case 'mongo':\n return 27017;\n case 'mssql':\n return 1433;\n case 'sqlite':\n case 'better-sqlite':\n case 'libsql':\n return 0; // SQLite is file-based, no port\n default:\n return 5432;\n }\n}\n\n/**\n * Setup MikroORM for testing with proper schema/migrations\n */\nexport async function setupTestORM(\n config: MikroOrmTestConfig\n): Promise<MikroORM> {\n const {\n mikroOrmConfigPath,\n databaseType,\n useMigrations = false,\n container\n } = config;\n\n // Dynamically import the MikroORM config\n const { default: mikroOrmConfig } = await import(mikroOrmConfigPath);\n\n const dbPort = getDatabasePort(databaseType);\n\n // SQLite databases are file-based\n let ormConfig: any;\n if (\n databaseType === 'sqlite' ||\n databaseType === 'better-sqlite' ||\n databaseType === 'libsql'\n ) {\n ormConfig = {\n ...mikroOrmConfig,\n dbName: ':memory:', // In-memory SQLite for tests\n debug: false,\n ...(useMigrations\n ? {\n migrations: {\n path: config.migrationsPath,\n glob: '!(*.d).{js,ts}',\n dropTables: true\n }\n }\n : {\n schemaGenerator: {\n createForeignKeyConstraints: false,\n wrap: false\n }\n })\n };\n } else if (container) {\n // Container-based databases\n ormConfig = {\n ...mikroOrmConfig,\n dbName: 'test_db',\n host: container.getHost(),\n user: databaseType === 'mssql' ? 'SA' : 'test_user',\n password:\n databaseType === 'mssql' ? 'Test_Password123!' : 'test_password',\n port: container.getMappedPort(dbPort),\n debug: false,\n ...(useMigrations\n ? {\n migrations: {\n path: config.migrationsPath,\n glob: '!(*.d).{js,ts}',\n dropTables: true\n }\n }\n : {\n schemaGenerator: {\n createForeignKeyConstraints: false,\n wrap: false\n }\n })\n };\n }\n\n const orm = await MikroORM.init(ormConfig);\n\n // Initialize database schema\n if (useMigrations) {\n await orm.getMigrator().up();\n } else {\n await orm.getSchemaGenerator().createSchema();\n }\n\n return orm;\n}\n\n/**\n * Clear all data from the test database\n */\nexport async function clearTestDatabase(\n orm: MikroORM,\n redis?: Redis\n): Promise<void> {\n // Clear Redis if provided\n if (redis) {\n await redis.flushall();\n }\n\n // Clear all database entities\n const em = orm.em.fork();\n const entities = Object.values(orm.getMetadata().getAll());\n\n // Delete in reverse order to avoid foreign key constraints\n for (const entity of entities.reverse()) {\n try {\n await em.nativeDelete(entity.class, {});\n } catch (error) {\n // Ignore \"table does not exist\" errors\n if (!(error as Error).message?.includes('does not exist')) {\n throw error;\n }\n }\n }\n\n await em.flush();\n}\n","import { MikroORM } from '@mikro-orm/core';\nimport Redis from 'ioredis';\nimport { StartedTestContainer } from 'testcontainers';\nimport { DatabaseType, TestContainerManager } from './containers';\nimport { clearTestDatabase, setupTestORM } from './database';\nimport { setupTestEnvironment } from './environment';\n\nexport interface BlueprintTestConfig {\n /**\n * Path to the MikroORM config file (e.g., '../mikro-orm.config')\n */\n mikroOrmConfigPath: string;\n\n /**\n * Database type (postgres, mysql, mongodb, etc.)\n * @default 'postgres'\n */\n databaseType?: DatabaseType;\n\n /**\n * Whether to use migrations (true) or schema generation (false)\n */\n useMigrations?: boolean;\n\n /**\n * Path to migrations directory (required if useMigrations is true)\n */\n migrationsPath?: string;\n\n /**\n * Whether the blueprint needs Redis\n */\n needsRedis?: boolean;\n\n /**\n * Custom environment variables to set\n */\n customEnvVars?: Record<string, string>;\n\n /**\n * Custom setup hook called after containers and ORM are initialized\n */\n onSetup?: (setup: TestSetupResult) => Promise<void>;\n}\n\nexport interface TestSetupResult {\n container: StartedTestContainer | null;\n redisContainer?: StartedTestContainer;\n orm: MikroORM;\n redis?: Redis;\n}\n\n/**\n * Complete test harness for blueprint E2E testing\n *\n * Handles container setup, environment configuration, and database initialization\n *\n * @example\n * ```typescript\n * const harness = new BlueprintTestHarness({\n * mikroOrmConfigPath: '../mikro-orm.config',\n * useMigrations: false,\n * needsRedis: true,\n * customEnvVars: {\n * STRIPE_API_KEY: 'sk_test_...'\n * }\n * });\n *\n * const setup = await harness.setup();\n * // ... run tests\n * await harness.cleanup();\n * ```\n */\nexport class BlueprintTestHarness {\n private containers: TestContainerManager;\n private result?: TestSetupResult;\n\n constructor(private config: BlueprintTestConfig) {\n this.containers = new TestContainerManager();\n }\n\n /**\n * Setup all test infrastructure (containers, ORM, Redis)\n */\n async setup(): Promise<TestSetupResult> {\n const databaseType = this.config.databaseType || 'postgres';\n\n // Setup database container\n const container =\n await this.containers.setupDatabaseContainer(databaseType);\n\n // Setup Redis container if needed\n const redisContainer = this.config.needsRedis\n ? await this.containers.setupRedisContainer()\n : undefined;\n\n // Setup environment variables\n setupTestEnvironment({\n database: container,\n databaseType,\n redis: redisContainer,\n customVars: this.config.customEnvVars\n });\n\n // Setup ORM\n const orm = await setupTestORM({\n mikroOrmConfigPath: this.config.mikroOrmConfigPath,\n databaseType,\n useMigrations: this.config.useMigrations,\n migrationsPath: this.config.migrationsPath,\n container\n });\n\n // Setup Redis client if needed\n let redis: Redis | undefined;\n if (redisContainer) {\n redis = new Redis({\n host: redisContainer.getHost(),\n port: redisContainer.getMappedPort(6379),\n maxRetriesPerRequest: 3\n });\n await redis.ping();\n }\n\n this.result = { container, redisContainer, orm, redis };\n\n // Call custom setup hook\n if (this.config.onSetup) {\n await this.config.onSetup(this.result);\n }\n\n return this.result;\n }\n\n /**\n * Cleanup all test infrastructure\n */\n async cleanup(): Promise<void> {\n if (this.result?.redis) {\n await this.result.redis.quit();\n }\n if (this.result?.orm) {\n await this.result.orm.close();\n }\n await this.containers.cleanup();\n this.result = undefined;\n }\n\n /**\n * Clear all data from the database\n */\n async clearDatabase(): Promise<void> {\n if (this.result) {\n await clearTestDatabase(this.result.orm, this.result.redis);\n }\n }\n}\n","/**\n * Standard mock authentication tokens for testing\n */\nexport const TEST_TOKENS = {\n /**\n * Mock Bearer token for testing\n */\n AUTH: 'Bearer test-token',\n\n /**\n * Mock valid HMAC token for testing\n */\n HMAC: 'HMAC keyId=test-key ts=1234567890 nonce=test-nonce signature=test-signature',\n\n /**\n * Mock invalid HMAC token for testing authentication failures\n */\n HMAC_INVALID:\n 'HMAC keyId=invalid-key ts=1234567890 nonce=invalid-nonce signature=invalid-signature'\n} as const;\n\n"],"mappings":"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","names":["getDatabasePort","Redis"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/containers.ts","../src/environment.ts","../src/database.ts","../src/harness.ts","../src/tokens.ts"],"sourcesContent":["/**\n * @forklaunch/testing\n *\n * Testing utilities for forklaunch-js blueprints\n *\n * @packageDocumentation\n */\n\n// Container management\nexport {\n DatabaseConfig,\n DatabaseType,\n MongoDBConfig,\n MSSQLConfig,\n MySQLConfig,\n PostgresConfig,\n RedisConfig,\n SQLiteConfig,\n TestContainerManager\n} from './containers';\n\nexport { setupTestEnvironment, TestEnvConfig } from './environment';\n\nexport {\n clearTestDatabase,\n MikroOrmTestConfig,\n setupTestORM\n} from './database';\n\nexport {\n BlueprintTestConfig,\n BlueprintTestHarness,\n TestSetupResult\n} from './harness';\n\nexport { TEST_TOKENS } from './tokens';\n","import { GenericContainer, StartedTestContainer } from 'testcontainers';\n\nexport type DatabaseType =\n | 'postgres'\n | 'postgresql'\n | 'mysql'\n | 'mariadb'\n | 'mongodb'\n | 'mongo'\n | 'mssql'\n | 'libsql'\n | 'sqlite'\n | 'better-sqlite';\n\nexport interface PostgresConfig {\n user?: string;\n password?: string;\n database?: string;\n command?: string[];\n}\n\nexport interface MySQLConfig {\n user?: string;\n password?: string;\n database?: string;\n rootPassword?: string;\n}\n\nexport interface MongoDBConfig {\n user?: string;\n password?: string;\n database?: string;\n}\n\nexport interface MSSQLConfig {\n user?: string;\n password?: string;\n database?: string;\n saPassword?: string;\n}\n\nexport interface SQLiteConfig {\n database?: string;\n}\n\nexport interface RedisConfig {\n command?: string[];\n}\n\nexport type DatabaseConfig =\n | PostgresConfig\n | MySQLConfig\n | MongoDBConfig\n | MSSQLConfig\n | SQLiteConfig;\n\n/**\n * Manages test containers (PostgreSQL, MySQL, MongoDB, Redis, etc.) for E2E testing\n */\nexport class TestContainerManager {\n private containers: StartedTestContainer[] = [];\n\n /**\n * Setup database container based on type\n */\n async setupDatabaseContainer(\n type: DatabaseType,\n config: DatabaseConfig = {}\n ): Promise<StartedTestContainer | null> {\n const normalizedType = this.normalizeDatabaseType(type);\n\n switch (normalizedType) {\n case 'postgres':\n return this.setupPostgresContainer(config as PostgresConfig);\n case 'mysql':\n return this.setupMySQLContainer(config as MySQLConfig);\n case 'mongodb':\n return this.setupMongoDBContainer(config as MongoDBConfig);\n case 'mssql':\n return this.setupMSSQLContainer(config as MSSQLConfig);\n case 'sqlite':\n // SQLite doesn't need a container (file-based)\n return null;\n default:\n throw new Error(`Unsupported database type: ${type}`);\n }\n }\n\n /**\n * Normalize database type aliases\n */\n private normalizeDatabaseType(\n type: DatabaseType\n ): 'postgres' | 'mysql' | 'mongodb' | 'mssql' | 'sqlite' {\n switch (type) {\n case 'postgres':\n case 'postgresql':\n return 'postgres';\n case 'mysql':\n case 'mariadb':\n return 'mysql';\n case 'mongodb':\n case 'mongo':\n return 'mongodb';\n case 'mssql':\n return 'mssql';\n case 'sqlite':\n case 'better-sqlite':\n case 'libsql':\n return 'sqlite';\n default:\n throw new Error(`Unknown database type: ${type}`);\n }\n }\n\n /**\n * Setup PostgreSQL test container\n */\n async setupPostgresContainer(\n config: PostgresConfig = {}\n ): Promise<StartedTestContainer> {\n const {\n user = 'test_user',\n password = 'test_password',\n database = 'test_db',\n command = ['postgres', '-c', 'log_statement=all']\n } = config;\n\n const container = await new GenericContainer('postgres:latest')\n .withExposedPorts(5432)\n .withEnvironment({\n POSTGRES_USER: user,\n POSTGRES_PASSWORD: password,\n POSTGRES_DB: database\n })\n .withCommand(command)\n .start();\n\n this.containers.push(container);\n return container;\n }\n\n /**\n * Setup MySQL test container\n */\n async setupMySQLContainer(\n config: MySQLConfig = {}\n ): Promise<StartedTestContainer> {\n const {\n user = 'test_user',\n password = 'test_password',\n database = 'test_db',\n rootPassword = 'root_password'\n } = config;\n\n const container = await new GenericContainer('mysql:8')\n .withExposedPorts(3306)\n .withEnvironment({\n MYSQL_ROOT_PASSWORD: rootPassword,\n MYSQL_DATABASE: database,\n MYSQL_USER: user,\n MYSQL_PASSWORD: password\n })\n .start();\n\n this.containers.push(container);\n return container;\n }\n\n /**\n * Setup MongoDB test container\n */\n async setupMongoDBContainer(\n config: MongoDBConfig = {}\n ): Promise<StartedTestContainer> {\n const {\n user = 'test_user',\n password = 'test_password',\n database = 'test_db'\n } = config;\n\n const container = await new GenericContainer('mongo:latest')\n .withExposedPorts(27017)\n .withEnvironment({\n MONGO_INITDB_ROOT_USERNAME: user,\n MONGO_INITDB_ROOT_PASSWORD: password,\n MONGO_INITDB_DATABASE: database\n })\n .start();\n\n this.containers.push(container);\n return container;\n }\n\n /**\n * Setup Microsoft SQL Server test container\n */\n async setupMSSQLContainer(\n config: MSSQLConfig = {}\n ): Promise<StartedTestContainer> {\n const {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n user = 'SA',\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n password = 'Test_Password123!',\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n database = 'test_db',\n saPassword = 'Test_Password123!'\n } = config;\n\n const container = await new GenericContainer(\n 'mcr.microsoft.com/mssql/server:2022-latest'\n )\n .withExposedPorts(1433)\n .withEnvironment({\n ACCEPT_EULA: 'Y',\n SA_PASSWORD: saPassword,\n MSSQL_PID: 'Developer'\n })\n .start();\n\n // Wait a bit for SQL Server to be ready\n await new Promise((resolve) => setTimeout(resolve, 3000));\n\n this.containers.push(container);\n return container;\n }\n\n /**\n * Setup Redis test container\n */\n async setupRedisContainer(\n config: RedisConfig = {}\n ): Promise<StartedTestContainer> {\n const { command = ['redis-server', '--appendonly', 'yes'] } = config;\n\n const container = await new GenericContainer('redis:latest')\n .withExposedPorts(6379)\n .withCommand(command)\n .start();\n\n this.containers.push(container);\n return container;\n }\n\n /**\n * Cleanup all containers\n */\n async cleanup(): Promise<void> {\n await Promise.all(\n this.containers.map((container) =>\n container.stop({ remove: true, removeVolumes: true }).catch(() => {\n // Ignore cleanup errors\n })\n )\n );\n this.containers = [];\n }\n}\n","import { StartedTestContainer } from 'testcontainers';\nimport { DatabaseType } from './containers';\n\nexport interface TestEnvConfig {\n database: StartedTestContainer | null;\n databaseType: DatabaseType;\n redis?: StartedTestContainer;\n hmacSecret?: string;\n customVars?: Record<string, string>;\n}\n\n/**\n * Get the default port for a database type\n */\nfunction getDatabasePort(type: DatabaseType): number {\n switch (type) {\n case 'postgres':\n case 'postgresql':\n return 5432;\n case 'mysql':\n case 'mariadb':\n return 3306;\n case 'mongodb':\n case 'mongo':\n return 27017;\n case 'mssql':\n return 1433;\n case 'sqlite':\n case 'better-sqlite':\n case 'libsql':\n return 0; // SQLite is file-based, no port\n default:\n return 5432;\n }\n}\n\n/**\n * Setup test environment variables for a blueprint test\n */\nexport function setupTestEnvironment(config: TestEnvConfig): void {\n const {\n database,\n databaseType,\n redis,\n hmacSecret = 'test-secret-key',\n customVars = {}\n } = config;\n\n const dbPort = getDatabasePort(databaseType);\n\n // Database environment variables\n process.env.DB_NAME = 'test_db';\n\n // SQLite databases are file-based, no container needed\n if (\n databaseType === 'sqlite' ||\n databaseType === 'better-sqlite' ||\n databaseType === 'libsql'\n ) {\n process.env.DB_PATH = ':memory:'; // In-memory SQLite for tests\n } else if (database) {\n process.env.DB_HOST = database.getHost();\n process.env.DB_USER = databaseType === 'mssql' ? 'SA' : 'test_user';\n process.env.DB_PASSWORD =\n databaseType === 'mssql' ? 'Test_Password123!' : 'test_password';\n process.env.DB_PORT = database.getMappedPort(dbPort).toString();\n }\n\n // Redis environment variables (if provided)\n if (redis) {\n process.env.REDIS_URL = `redis://${redis.getHost()}:${redis.getMappedPort(6379)}`;\n }\n\n // Standard test environment variables\n process.env.HMAC_SECRET_KEY = hmacSecret;\n process.env.JWKS_PUBLIC_KEY_URL =\n 'http://localhost:3000/.well-known/jwks.json';\n process.env.OTEL_SERVICE_NAME = 'test-service';\n process.env.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://localhost:4318';\n process.env.HOST = 'localhost';\n process.env.PORT = '3000';\n process.env.NODE_ENV = 'test';\n process.env.VERSION = 'v1';\n process.env.DOCS_PATH = '/docs';\n process.env.OTEL_LEVEL = 'info';\n process.env.DOTENV_FILE_PATH = '.env.test';\n\n // Custom environment variables\n Object.entries(customVars).forEach(([key, value]) => {\n process.env[key] = value;\n });\n}\n","import { MikroORM, Options } from '@mikro-orm/core';\nimport Redis from 'ioredis';\nimport { StartedTestContainer } from 'testcontainers';\nimport { DatabaseType } from './containers';\n\nexport interface MikroOrmTestConfig {\n /**\n * MikroORM config object (imported from mikro-orm.config)\n */\n mikroOrmConfig: Options;\n\n /**\n * Database type (postgres, mysql, mongodb, etc.)\n */\n databaseType: DatabaseType;\n\n /**\n * Whether to use migrations (true) or schema generation (false)\n * - true: IAM blueprints (uses getMigrator().up())\n * - false: Billing blueprints (uses getSchemaGenerator().createSchema())\n */\n useMigrations?: boolean;\n\n /**\n * Path to migrations directory (required if useMigrations is true)\n */\n migrationsPath?: string;\n\n /**\n * Database container instance (null for file-based databases like SQLite)\n */\n container: StartedTestContainer | null;\n}\n\n/**\n * Get the default port for a database type\n */\nfunction getDatabasePort(type: DatabaseType): number {\n switch (type) {\n case 'postgres':\n case 'postgresql':\n return 5432;\n case 'mysql':\n case 'mariadb':\n return 3306;\n case 'mongodb':\n case 'mongo':\n return 27017;\n case 'mssql':\n return 1433;\n case 'sqlite':\n case 'better-sqlite':\n case 'libsql':\n return 0; // SQLite is file-based, no port\n default:\n return 5432;\n }\n}\n\n/**\n * Setup MikroORM for testing with proper schema/migrations\n */\nexport async function setupTestORM(\n config: MikroOrmTestConfig\n): Promise<MikroORM> {\n const {\n mikroOrmConfig,\n databaseType,\n useMigrations = false,\n container\n } = config;\n\n const dbPort = getDatabasePort(databaseType);\n\n // SQLite databases are file-based\n let ormConfig: Options = {};\n if (\n databaseType === 'sqlite' ||\n databaseType === 'better-sqlite' ||\n databaseType === 'libsql'\n ) {\n ormConfig = {\n ...mikroOrmConfig,\n dbName: ':memory:', // In-memory SQLite for tests\n debug: false,\n ...(useMigrations\n ? {\n migrations: {\n path: config.migrationsPath,\n glob: '!(*.d).{js,ts}',\n dropTables: true\n }\n }\n : {\n schemaGenerator: {\n createForeignKeyConstraints: false\n }\n })\n };\n } else if (container) {\n // Container-based databases\n ormConfig = {\n ...mikroOrmConfig,\n dbName: 'test_db',\n host: container.getHost(),\n user: databaseType === 'mssql' ? 'SA' : 'test_user',\n password:\n databaseType === 'mssql' ? 'Test_Password123!' : 'test_password',\n port: container.getMappedPort(dbPort),\n debug: false,\n ...(useMigrations\n ? {\n migrations: {\n path: config.migrationsPath,\n glob: '!(*.d).{js,ts}',\n dropTables: true\n }\n }\n : {\n schemaGenerator: {\n createForeignKeyConstraints: false\n }\n })\n };\n }\n\n const orm = await MikroORM.init(ormConfig);\n\n // Initialize database schema\n if (useMigrations) {\n await orm.getMigrator().up();\n } else {\n await orm.getSchemaGenerator().createSchema();\n }\n\n return orm;\n}\n\n/**\n * Clear all data from the test database\n */\nexport async function clearTestDatabase(\n orm: MikroORM,\n redis?: Redis\n): Promise<void> {\n // Clear Redis if provided\n if (redis) {\n await redis.flushall();\n }\n\n // Clear all database entities\n const em = orm.em.fork();\n const entities = Object.values(orm.getMetadata().getAll());\n\n // Delete in reverse order to avoid foreign key constraints\n for (const entity of entities.reverse()) {\n try {\n await em.nativeDelete(entity.class, {});\n } catch (error) {\n // Ignore \"table does not exist\" errors\n if (!(error as Error).message?.includes('does not exist')) {\n throw error;\n }\n }\n }\n\n await em.flush();\n}\n","import { MikroORM, Options } from '@mikro-orm/core';\nimport Redis from 'ioredis';\nimport { StartedTestContainer } from 'testcontainers';\nimport { DatabaseType, TestContainerManager } from './containers';\nimport { clearTestDatabase, setupTestORM } from './database';\nimport { setupTestEnvironment } from './environment';\n\nexport interface BlueprintTestConfig {\n /**\n * Function that imports and returns the MikroORM config\n * This is called AFTER environment variables are set\n */\n getConfig: () => Promise<Options>;\n\n /**\n * Database type (postgres, mysql, mongodb, etc.)\n * @default 'postgres'\n */\n databaseType?: DatabaseType;\n\n /**\n * Whether to use migrations (true) or schema generation (false)\n */\n useMigrations?: boolean;\n\n /**\n * Path to migrations directory (required if useMigrations is true)\n */\n migrationsPath?: string;\n\n /**\n * Whether the blueprint needs Redis\n */\n needsRedis?: boolean;\n\n /**\n * Custom environment variables to set\n */\n customEnvVars?: Record<string, string>;\n\n /**\n * Custom setup hook called after containers and ORM are initialized\n */\n onSetup?: (setup: TestSetupResult) => Promise<void>;\n}\n\nexport interface TestSetupResult {\n container: StartedTestContainer | null;\n redisContainer?: StartedTestContainer;\n orm: MikroORM;\n redis?: Redis;\n}\n\n/**\n * Complete test harness for blueprint E2E testing\n *\n * Handles container setup, environment configuration, and database initialization\n *\n * @example\n * ```typescript\n * const harness = new BlueprintTestHarness({\n * mikroOrmConfigPath: '../mikro-orm.config',\n * useMigrations: false,\n * needsRedis: true,\n * customEnvVars: {\n * STRIPE_API_KEY: 'sk_test_...'\n * }\n * });\n *\n * const setup = await harness.setup();\n * // ... run tests\n * await harness.cleanup();\n * ```\n */\nexport class BlueprintTestHarness {\n private containers: TestContainerManager;\n private result?: TestSetupResult;\n\n constructor(private config: BlueprintTestConfig) {\n this.containers = new TestContainerManager();\n }\n\n /**\n * Setup all test infrastructure (containers, ORM, Redis)\n */\n async setup(): Promise<TestSetupResult> {\n const databaseType = this.config.databaseType || 'postgres';\n\n // Setup database container\n const container =\n await this.containers.setupDatabaseContainer(databaseType);\n\n // Setup Redis container if needed\n const redisContainer = this.config.needsRedis\n ? await this.containers.setupRedisContainer()\n : undefined;\n\n // Setup environment variables\n setupTestEnvironment({\n database: container,\n databaseType,\n redis: redisContainer,\n customVars: this.config.customEnvVars\n });\n\n // Get the config AFTER environment is set\n const mikroOrmConfig = await this.config.getConfig();\n\n // Setup ORM\n const orm = await setupTestORM({\n mikroOrmConfig,\n databaseType,\n useMigrations: this.config.useMigrations,\n migrationsPath: this.config.migrationsPath,\n container\n });\n\n // Setup Redis client if needed\n let redis: Redis | undefined;\n if (redisContainer) {\n redis = new Redis({\n host: redisContainer.getHost(),\n port: redisContainer.getMappedPort(6379),\n maxRetriesPerRequest: 3\n });\n await redis.ping();\n }\n\n this.result = { container, redisContainer, orm, redis };\n\n // Call custom setup hook\n if (this.config.onSetup) {\n await this.config.onSetup(this.result);\n }\n\n return this.result;\n }\n\n /**\n * Cleanup all test infrastructure\n */\n async cleanup(): Promise<void> {\n if (this.result?.redis) {\n await this.result.redis.quit();\n }\n if (this.result?.orm) {\n await this.result.orm.close();\n }\n await this.containers.cleanup();\n this.result = undefined;\n }\n\n /**\n * Clear all data from the database\n */\n async clearDatabase(): Promise<void> {\n if (this.result) {\n await clearTestDatabase(this.result.orm, this.result.redis);\n }\n }\n}\n","/**\n * Standard mock authentication tokens for testing\n */\nexport const TEST_TOKENS = {\n /**\n * Mock Bearer token for testing\n */\n AUTH: 'Bearer test-token',\n\n /**\n * Mock valid HMAC token for testing\n */\n HMAC: 'HMAC keyId=test-key ts=1234567890 nonce=test-nonce signature=test-signature',\n\n /**\n * Mock invalid HMAC token for testing authentication failures\n */\n HMAC_INVALID:\n 'HMAC keyId=invalid-key ts=1234567890 nonce=invalid-nonce signature=invalid-signature'\n} as const;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,4BAAuD;AA2DhD,IAAM,uBAAN,MAA2B;AAAA,EACxB,aAAqC,CAAC;AAAA;AAAA;AAAA;AAAA,EAK9C,MAAM,uBACJ,MACA,SAAyB,CAAC,GACY;AACtC,UAAM,iBAAiB,KAAK,sBAAsB,IAAI;AAEtD,YAAQ,gBAAgB;AAAA,MACtB,KAAK;AACH,eAAO,KAAK,uBAAuB,MAAwB;AAAA,MAC7D,KAAK;AACH,eAAO,KAAK,oBAAoB,MAAqB;AAAA,MACvD,KAAK;AACH,eAAO,KAAK,sBAAsB,MAAuB;AAAA,MAC3D,KAAK;AACH,eAAO,KAAK,oBAAoB,MAAqB;AAAA,MACvD,KAAK;AAEH,eAAO;AAAA,MACT;AACE,cAAM,IAAI,MAAM,8BAA8B,IAAI,EAAE;AAAA,IACxD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,sBACN,MACuD;AACvD,YAAQ,MAAM;AAAA,MACZ,KAAK;AAAA,MACL,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AAAA,MACL,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AAAA,MACL,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AACH,eAAO;AAAA,MACT;AACE,cAAM,IAAI,MAAM,0BAA0B,IAAI,EAAE;AAAA,IACpD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,uBACJ,SAAyB,CAAC,GACK;AAC/B,UAAM;AAAA,MACJ,OAAO;AAAA,MACP,WAAW;AAAA,MACX,WAAW;AAAA,MACX,UAAU,CAAC,YAAY,MAAM,mBAAmB;AAAA,IAClD,IAAI;AAEJ,UAAM,YAAY,MAAM,IAAI,uCAAiB,iBAAiB,EAC3D,iBAAiB,IAAI,EACrB,gBAAgB;AAAA,MACf,eAAe;AAAA,MACf,mBAAmB;AAAA,MACnB,aAAa;AAAA,IACf,CAAC,EACA,YAAY,OAAO,EACnB,MAAM;AAET,SAAK,WAAW,KAAK,SAAS;AAC9B,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,oBACJ,SAAsB,CAAC,GACQ;AAC/B,UAAM;AAAA,MACJ,OAAO;AAAA,MACP,WAAW;AAAA,MACX,WAAW;AAAA,MACX,eAAe;AAAA,IACjB,IAAI;AAEJ,UAAM,YAAY,MAAM,IAAI,uCAAiB,SAAS,EACnD,iBAAiB,IAAI,EACrB,gBAAgB;AAAA,MACf,qBAAqB;AAAA,MACrB,gBAAgB;AAAA,MAChB,YAAY;AAAA,MACZ,gBAAgB;AAAA,IAClB,CAAC,EACA,MAAM;AAET,SAAK,WAAW,KAAK,SAAS;AAC9B,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,sBACJ,SAAwB,CAAC,GACM;AAC/B,UAAM;AAAA,MACJ,OAAO;AAAA,MACP,WAAW;AAAA,MACX,WAAW;AAAA,IACb,IAAI;AAEJ,UAAM,YAAY,MAAM,IAAI,uCAAiB,cAAc,EACxD,iBAAiB,KAAK,EACtB,gBAAgB;AAAA,MACf,4BAA4B;AAAA,MAC5B,4BAA4B;AAAA,MAC5B,uBAAuB;AAAA,IACzB,CAAC,EACA,MAAM;AAET,SAAK,WAAW,KAAK,SAAS;AAC9B,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,oBACJ,SAAsB,CAAC,GACQ;AAC/B,UAAM;AAAA;AAAA,MAEJ,OAAO;AAAA;AAAA,MAEP,WAAW;AAAA;AAAA,MAEX,WAAW;AAAA,MACX,aAAa;AAAA,IACf,IAAI;AAEJ,UAAM,YAAY,MAAM,IAAI;AAAA,MAC1B;AAAA,IACF,EACG,iBAAiB,IAAI,EACrB,gBAAgB;AAAA,MACf,aAAa;AAAA,MACb,aAAa;AAAA,MACb,WAAW;AAAA,IACb,CAAC,EACA,MAAM;AAGT,UAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,GAAI,CAAC;AAExD,SAAK,WAAW,KAAK,SAAS;AAC9B,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,oBACJ,SAAsB,CAAC,GACQ;AAC/B,UAAM,EAAE,UAAU,CAAC,gBAAgB,gBAAgB,KAAK,EAAE,IAAI;AAE9D,UAAM,YAAY,MAAM,IAAI,uCAAiB,cAAc,EACxD,iBAAiB,IAAI,EACrB,YAAY,OAAO,EACnB,MAAM;AAET,SAAK,WAAW,KAAK,SAAS;AAC9B,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,UAAyB;AAC7B,UAAM,QAAQ;AAAA,MACZ,KAAK,WAAW;AAAA,QAAI,CAAC,cACnB,UAAU,KAAK,EAAE,QAAQ,MAAM,eAAe,KAAK,CAAC,EAAE,MAAM,MAAM;AAAA,QAElE,CAAC;AAAA,MACH;AAAA,IACF;AACA,SAAK,aAAa,CAAC;AAAA,EACrB;AACF;;;ACpPA,SAAS,gBAAgB,MAA4B;AACnD,UAAQ,MAAM;AAAA,IACZ,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAKO,SAAS,qBAAqB,QAA6B;AAChE,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA,aAAa;AAAA,IACb,aAAa,CAAC;AAAA,EAChB,IAAI;AAEJ,QAAM,SAAS,gBAAgB,YAAY;AAG3C,UAAQ,IAAI,UAAU;AAGtB,MACE,iBAAiB,YACjB,iBAAiB,mBACjB,iBAAiB,UACjB;AACA,YAAQ,IAAI,UAAU;AAAA,EACxB,WAAW,UAAU;AACnB,YAAQ,IAAI,UAAU,SAAS,QAAQ;AACvC,YAAQ,IAAI,UAAU,iBAAiB,UAAU,OAAO;AACxD,YAAQ,IAAI,cACV,iBAAiB,UAAU,sBAAsB;AACnD,YAAQ,IAAI,UAAU,SAAS,cAAc,MAAM,EAAE,SAAS;AAAA,EAChE;AAGA,MAAI,OAAO;AACT,YAAQ,IAAI,YAAY,WAAW,MAAM,QAAQ,CAAC,IAAI,MAAM,cAAc,IAAI,CAAC;AAAA,EACjF;AAGA,UAAQ,IAAI,kBAAkB;AAC9B,UAAQ,IAAI,sBACV;AACF,UAAQ,IAAI,oBAAoB;AAChC,UAAQ,IAAI,8BAA8B;AAC1C,UAAQ,IAAI,OAAO;AACnB,UAAQ,IAAI,OAAO;AACnB,UAAQ,IAAI,WAAW;AACvB,UAAQ,IAAI,UAAU;AACtB,UAAQ,IAAI,YAAY;AACxB,UAAQ,IAAI,aAAa;AACzB,UAAQ,IAAI,mBAAmB;AAG/B,SAAO,QAAQ,UAAU,EAAE,QAAQ,CAAC,CAAC,KAAK,KAAK,MAAM;AACnD,YAAQ,IAAI,GAAG,IAAI;AAAA,EACrB,CAAC;AACH;;;AC3FA,kBAAkC;AAqClC,SAASA,iBAAgB,MAA4B;AACnD,UAAQ,MAAM;AAAA,IACZ,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAKA,eAAsB,aACpB,QACmB;AACnB,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA,gBAAgB;AAAA,IAChB;AAAA,EACF,IAAI;AAEJ,QAAM,SAASA,iBAAgB,YAAY;AAG3C,MAAI,YAAqB,CAAC;AAC1B,MACE,iBAAiB,YACjB,iBAAiB,mBACjB,iBAAiB,UACjB;AACA,gBAAY;AAAA,MACV,GAAG;AAAA,MACH,QAAQ;AAAA;AAAA,MACR,OAAO;AAAA,MACP,GAAI,gBACA;AAAA,QACE,YAAY;AAAA,UACV,MAAM,OAAO;AAAA,UACb,MAAM;AAAA,UACN,YAAY;AAAA,QACd;AAAA,MACF,IACA;AAAA,QACE,iBAAiB;AAAA,UACf,6BAA6B;AAAA,QAC/B;AAAA,MACF;AAAA,IACN;AAAA,EACF,WAAW,WAAW;AAEpB,gBAAY;AAAA,MACV,GAAG;AAAA,MACH,QAAQ;AAAA,MACR,MAAM,UAAU,QAAQ;AAAA,MACxB,MAAM,iBAAiB,UAAU,OAAO;AAAA,MACxC,UACE,iBAAiB,UAAU,sBAAsB;AAAA,MACnD,MAAM,UAAU,cAAc,MAAM;AAAA,MACpC,OAAO;AAAA,MACP,GAAI,gBACA;AAAA,QACE,YAAY;AAAA,UACV,MAAM,OAAO;AAAA,UACb,MAAM;AAAA,UACN,YAAY;AAAA,QACd;AAAA,MACF,IACA;AAAA,QACE,iBAAiB;AAAA,UACf,6BAA6B;AAAA,QAC/B;AAAA,MACF;AAAA,IACN;AAAA,EACF;AAEA,QAAM,MAAM,MAAM,qBAAS,KAAK,SAAS;AAGzC,MAAI,eAAe;AACjB,UAAM,IAAI,YAAY,EAAE,GAAG;AAAA,EAC7B,OAAO;AACL,UAAM,IAAI,mBAAmB,EAAE,aAAa;AAAA,EAC9C;AAEA,SAAO;AACT;AAKA,eAAsB,kBACpB,KACA,OACe;AAEf,MAAI,OAAO;AACT,UAAM,MAAM,SAAS;AAAA,EACvB;AAGA,QAAM,KAAK,IAAI,GAAG,KAAK;AACvB,QAAM,WAAW,OAAO,OAAO,IAAI,YAAY,EAAE,OAAO,CAAC;AAGzD,aAAW,UAAU,SAAS,QAAQ,GAAG;AACvC,QAAI;AACF,YAAM,GAAG,aAAa,OAAO,OAAO,CAAC,CAAC;AAAA,IACxC,SAAS,OAAO;AAEd,UAAI,CAAE,MAAgB,SAAS,SAAS,gBAAgB,GAAG;AACzD,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,QAAM,GAAG,MAAM;AACjB;;;ACtKA,qBAAkB;AAyEX,IAAM,uBAAN,MAA2B;AAAA,EAIhC,YAAoB,QAA6B;AAA7B;AAClB,SAAK,aAAa,IAAI,qBAAqB;AAAA,EAC7C;AAAA,EALQ;AAAA,EACA;AAAA;AAAA;AAAA;AAAA,EASR,MAAM,QAAkC;AACtC,UAAM,eAAe,KAAK,OAAO,gBAAgB;AAGjD,UAAM,YACJ,MAAM,KAAK,WAAW,uBAAuB,YAAY;AAG3D,UAAM,iBAAiB,KAAK,OAAO,aAC/B,MAAM,KAAK,WAAW,oBAAoB,IAC1C;AAGJ,yBAAqB;AAAA,MACnB,UAAU;AAAA,MACV;AAAA,MACA,OAAO;AAAA,MACP,YAAY,KAAK,OAAO;AAAA,IAC1B,CAAC;AAGD,UAAM,iBAAiB,MAAM,KAAK,OAAO,UAAU;AAGnD,UAAM,MAAM,MAAM,aAAa;AAAA,MAC7B;AAAA,MACA;AAAA,MACA,eAAe,KAAK,OAAO;AAAA,MAC3B,gBAAgB,KAAK,OAAO;AAAA,MAC5B;AAAA,IACF,CAAC;AAGD,QAAI;AACJ,QAAI,gBAAgB;AAClB,cAAQ,IAAI,eAAAC,QAAM;AAAA,QAChB,MAAM,eAAe,QAAQ;AAAA,QAC7B,MAAM,eAAe,cAAc,IAAI;AAAA,QACvC,sBAAsB;AAAA,MACxB,CAAC;AACD,YAAM,MAAM,KAAK;AAAA,IACnB;AAEA,SAAK,SAAS,EAAE,WAAW,gBAAgB,KAAK,MAAM;AAGtD,QAAI,KAAK,OAAO,SAAS;AACvB,YAAM,KAAK,OAAO,QAAQ,KAAK,MAAM;AAAA,IACvC;AAEA,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,UAAyB;AAC7B,QAAI,KAAK,QAAQ,OAAO;AACtB,YAAM,KAAK,OAAO,MAAM,KAAK;AAAA,IAC/B;AACA,QAAI,KAAK,QAAQ,KAAK;AACpB,YAAM,KAAK,OAAO,IAAI,MAAM;AAAA,IAC9B;AACA,UAAM,KAAK,WAAW,QAAQ;AAC9B,SAAK,SAAS;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,gBAA+B;AACnC,QAAI,KAAK,QAAQ;AACf,YAAM,kBAAkB,KAAK,OAAO,KAAK,KAAK,OAAO,KAAK;AAAA,IAC5D;AAAA,EACF;AACF;;;AC7JO,IAAM,cAAc;AAAA;AAAA;AAAA;AAAA,EAIzB,MAAM;AAAA;AAAA;AAAA;AAAA,EAKN,MAAM;AAAA;AAAA;AAAA;AAAA,EAKN,cACE;AACJ;","names":["getDatabasePort","Redis"]}
package/lib/index.mjs CHANGED
@@ -105,8 +105,11 @@ var TestContainerManager = class {
105
105
  */
106
106
  async setupMSSQLContainer(config = {}) {
107
107
  const {
108
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
108
109
  user = "SA",
110
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
109
111
  password = "Test_Password123!",
112
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
110
113
  database = "test_db",
111
114
  saPassword = "Test_Password123!"
112
115
  } = config;
@@ -230,14 +233,13 @@ function getDatabasePort2(type) {
230
233
  }
231
234
  async function setupTestORM(config) {
232
235
  const {
233
- mikroOrmConfigPath,
236
+ mikroOrmConfig,
234
237
  databaseType,
235
238
  useMigrations = false,
236
239
  container
237
240
  } = config;
238
- const { default: mikroOrmConfig } = await import(mikroOrmConfigPath);
239
241
  const dbPort = getDatabasePort2(databaseType);
240
- let ormConfig;
242
+ let ormConfig = {};
241
243
  if (databaseType === "sqlite" || databaseType === "better-sqlite" || databaseType === "libsql") {
242
244
  ormConfig = {
243
245
  ...mikroOrmConfig,
@@ -252,8 +254,7 @@ async function setupTestORM(config) {
252
254
  }
253
255
  } : {
254
256
  schemaGenerator: {
255
- createForeignKeyConstraints: false,
256
- wrap: false
257
+ createForeignKeyConstraints: false
257
258
  }
258
259
  }
259
260
  };
@@ -274,8 +275,7 @@ async function setupTestORM(config) {
274
275
  }
275
276
  } : {
276
277
  schemaGenerator: {
277
- createForeignKeyConstraints: false,
278
- wrap: false
278
+ createForeignKeyConstraints: false
279
279
  }
280
280
  }
281
281
  };
@@ -328,8 +328,9 @@ var BlueprintTestHarness = class {
328
328
  redis: redisContainer,
329
329
  customVars: this.config.customEnvVars
330
330
  });
331
+ const mikroOrmConfig = await this.config.getConfig();
331
332
  const orm = await setupTestORM({
332
- mikroOrmConfigPath: this.config.mikroOrmConfigPath,
333
+ mikroOrmConfig,
333
334
  databaseType,
334
335
  useMigrations: this.config.useMigrations,
335
336
  migrationsPath: this.config.migrationsPath,
package/lib/index.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/containers.ts","../src/environment.ts","../src/database.ts","../src/harness.ts","../src/tokens.ts"],"sourcesContent":["import { GenericContainer, StartedTestContainer } from 'testcontainers';\n\nexport type DatabaseType =\n | 'postgres'\n | 'postgresql'\n | 'mysql'\n | 'mariadb'\n | 'mongodb'\n | 'mongo'\n | 'mssql'\n | 'libsql'\n | 'sqlite'\n | 'better-sqlite';\n\nexport interface PostgresConfig {\n user?: string;\n password?: string;\n database?: string;\n command?: string[];\n}\n\nexport interface MySQLConfig {\n user?: string;\n password?: string;\n database?: string;\n rootPassword?: string;\n}\n\nexport interface MongoDBConfig {\n user?: string;\n password?: string;\n database?: string;\n}\n\nexport interface MSSQLConfig {\n user?: string;\n password?: string;\n database?: string;\n saPassword?: string;\n}\n\nexport interface SQLiteConfig {\n database?: string;\n}\n\nexport interface RedisConfig {\n command?: string[];\n}\n\nexport type DatabaseConfig =\n | PostgresConfig\n | MySQLConfig\n | MongoDBConfig\n | MSSQLConfig\n | SQLiteConfig;\n\n/**\n * Manages test containers (PostgreSQL, MySQL, MongoDB, Redis, etc.) for E2E testing\n */\nexport class TestContainerManager {\n private containers: StartedTestContainer[] = [];\n\n /**\n * Setup database container based on type\n */\n async setupDatabaseContainer(\n type: DatabaseType,\n config: DatabaseConfig = {}\n ): Promise<StartedTestContainer | null> {\n const normalizedType = this.normalizeDatabaseType(type);\n\n switch (normalizedType) {\n case 'postgres':\n return this.setupPostgresContainer(config as PostgresConfig);\n case 'mysql':\n return this.setupMySQLContainer(config as MySQLConfig);\n case 'mongodb':\n return this.setupMongoDBContainer(config as MongoDBConfig);\n case 'mssql':\n return this.setupMSSQLContainer(config as MSSQLConfig);\n case 'sqlite':\n // SQLite doesn't need a container (file-based)\n return null;\n default:\n throw new Error(`Unsupported database type: ${type}`);\n }\n }\n\n /**\n * Normalize database type aliases\n */\n private normalizeDatabaseType(\n type: DatabaseType\n ): 'postgres' | 'mysql' | 'mongodb' | 'mssql' | 'sqlite' {\n switch (type) {\n case 'postgres':\n case 'postgresql':\n return 'postgres';\n case 'mysql':\n case 'mariadb':\n return 'mysql';\n case 'mongodb':\n case 'mongo':\n return 'mongodb';\n case 'mssql':\n return 'mssql';\n case 'sqlite':\n case 'better-sqlite':\n case 'libsql':\n return 'sqlite';\n default:\n throw new Error(`Unknown database type: ${type}`);\n }\n }\n\n /**\n * Setup PostgreSQL test container\n */\n async setupPostgresContainer(\n config: PostgresConfig = {}\n ): Promise<StartedTestContainer> {\n const {\n user = 'test_user',\n password = 'test_password',\n database = 'test_db',\n command = ['postgres', '-c', 'log_statement=all']\n } = config;\n\n const container = await new GenericContainer('postgres:latest')\n .withExposedPorts(5432)\n .withEnvironment({\n POSTGRES_USER: user,\n POSTGRES_PASSWORD: password,\n POSTGRES_DB: database\n })\n .withCommand(command)\n .start();\n\n this.containers.push(container);\n return container;\n }\n\n /**\n * Setup MySQL test container\n */\n async setupMySQLContainer(\n config: MySQLConfig = {}\n ): Promise<StartedTestContainer> {\n const {\n user = 'test_user',\n password = 'test_password',\n database = 'test_db',\n rootPassword = 'root_password'\n } = config;\n\n const container = await new GenericContainer('mysql:8')\n .withExposedPorts(3306)\n .withEnvironment({\n MYSQL_ROOT_PASSWORD: rootPassword,\n MYSQL_DATABASE: database,\n MYSQL_USER: user,\n MYSQL_PASSWORD: password\n })\n .start();\n\n this.containers.push(container);\n return container;\n }\n\n /**\n * Setup MongoDB test container\n */\n async setupMongoDBContainer(\n config: MongoDBConfig = {}\n ): Promise<StartedTestContainer> {\n const {\n user = 'test_user',\n password = 'test_password',\n database = 'test_db'\n } = config;\n\n const container = await new GenericContainer('mongo:latest')\n .withExposedPorts(27017)\n .withEnvironment({\n MONGO_INITDB_ROOT_USERNAME: user,\n MONGO_INITDB_ROOT_PASSWORD: password,\n MONGO_INITDB_DATABASE: database\n })\n .start();\n\n this.containers.push(container);\n return container;\n }\n\n /**\n * Setup Microsoft SQL Server test container\n */\n async setupMSSQLContainer(\n config: MSSQLConfig = {}\n ): Promise<StartedTestContainer> {\n const {\n user = 'SA',\n password = 'Test_Password123!',\n database = 'test_db',\n saPassword = 'Test_Password123!'\n } = config;\n\n const container = await new GenericContainer(\n 'mcr.microsoft.com/mssql/server:2022-latest'\n )\n .withExposedPorts(1433)\n .withEnvironment({\n ACCEPT_EULA: 'Y',\n SA_PASSWORD: saPassword,\n MSSQL_PID: 'Developer'\n })\n .start();\n\n // Wait a bit for SQL Server to be ready\n await new Promise((resolve) => setTimeout(resolve, 3000));\n\n this.containers.push(container);\n return container;\n }\n\n /**\n * Setup Redis test container\n */\n async setupRedisContainer(\n config: RedisConfig = {}\n ): Promise<StartedTestContainer> {\n const { command = ['redis-server', '--appendonly', 'yes'] } = config;\n\n const container = await new GenericContainer('redis:latest')\n .withExposedPorts(6379)\n .withCommand(command)\n .start();\n\n this.containers.push(container);\n return container;\n }\n\n /**\n * Cleanup all containers\n */\n async cleanup(): Promise<void> {\n await Promise.all(\n this.containers.map((container) =>\n container.stop({ remove: true, removeVolumes: true }).catch(() => {\n // Ignore cleanup errors\n })\n )\n );\n this.containers = [];\n }\n}\n","import { StartedTestContainer } from 'testcontainers';\nimport { DatabaseType } from './containers';\n\nexport interface TestEnvConfig {\n database: StartedTestContainer | null;\n databaseType: DatabaseType;\n redis?: StartedTestContainer;\n hmacSecret?: string;\n customVars?: Record<string, string>;\n}\n\n/**\n * Get the default port for a database type\n */\nfunction getDatabasePort(type: DatabaseType): number {\n switch (type) {\n case 'postgres':\n case 'postgresql':\n return 5432;\n case 'mysql':\n case 'mariadb':\n return 3306;\n case 'mongodb':\n case 'mongo':\n return 27017;\n case 'mssql':\n return 1433;\n case 'sqlite':\n case 'better-sqlite':\n case 'libsql':\n return 0; // SQLite is file-based, no port\n default:\n return 5432;\n }\n}\n\n/**\n * Setup test environment variables for a blueprint test\n */\nexport function setupTestEnvironment(config: TestEnvConfig): void {\n const {\n database,\n databaseType,\n redis,\n hmacSecret = 'test-secret-key',\n customVars = {}\n } = config;\n\n const dbPort = getDatabasePort(databaseType);\n\n // Database environment variables\n process.env.DB_NAME = 'test_db';\n\n // SQLite databases are file-based, no container needed\n if (\n databaseType === 'sqlite' ||\n databaseType === 'better-sqlite' ||\n databaseType === 'libsql'\n ) {\n process.env.DB_PATH = ':memory:'; // In-memory SQLite for tests\n } else if (database) {\n process.env.DB_HOST = database.getHost();\n process.env.DB_USER = databaseType === 'mssql' ? 'SA' : 'test_user';\n process.env.DB_PASSWORD =\n databaseType === 'mssql' ? 'Test_Password123!' : 'test_password';\n process.env.DB_PORT = database.getMappedPort(dbPort).toString();\n }\n\n // Redis environment variables (if provided)\n if (redis) {\n process.env.REDIS_URL = `redis://${redis.getHost()}:${redis.getMappedPort(6379)}`;\n }\n\n // Standard test environment variables\n process.env.HMAC_SECRET_KEY = hmacSecret;\n process.env.JWKS_PUBLIC_KEY_URL =\n 'http://localhost:3000/.well-known/jwks.json';\n process.env.OTEL_SERVICE_NAME = 'test-service';\n process.env.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://localhost:4318';\n process.env.HOST = 'localhost';\n process.env.PORT = '3000';\n process.env.NODE_ENV = 'test';\n process.env.VERSION = 'v1';\n process.env.DOCS_PATH = '/docs';\n process.env.OTEL_LEVEL = 'info';\n process.env.DOTENV_FILE_PATH = '.env.test';\n\n // Custom environment variables\n Object.entries(customVars).forEach(([key, value]) => {\n process.env[key] = value;\n });\n}\n","import { MikroORM } from '@mikro-orm/core';\nimport Redis from 'ioredis';\nimport { StartedTestContainer } from 'testcontainers';\nimport { DatabaseType } from './containers';\n\nexport interface MikroOrmTestConfig {\n /**\n * Path to the MikroORM config file (e.g., '../mikro-orm.config')\n */\n mikroOrmConfigPath: string;\n\n /**\n * Database type (postgres, mysql, mongodb, etc.)\n */\n databaseType: DatabaseType;\n\n /**\n * Whether to use migrations (true) or schema generation (false)\n * - true: IAM blueprints (uses getMigrator().up())\n * - false: Billing blueprints (uses getSchemaGenerator().createSchema())\n */\n useMigrations?: boolean;\n\n /**\n * Path to migrations directory (required if useMigrations is true)\n */\n migrationsPath?: string;\n\n /**\n * Database container instance (null for file-based databases like SQLite)\n */\n container: StartedTestContainer | null;\n}\n\n/**\n * Get the default port for a database type\n */\nfunction getDatabasePort(type: DatabaseType): number {\n switch (type) {\n case 'postgres':\n case 'postgresql':\n return 5432;\n case 'mysql':\n case 'mariadb':\n return 3306;\n case 'mongodb':\n case 'mongo':\n return 27017;\n case 'mssql':\n return 1433;\n case 'sqlite':\n case 'better-sqlite':\n case 'libsql':\n return 0; // SQLite is file-based, no port\n default:\n return 5432;\n }\n}\n\n/**\n * Setup MikroORM for testing with proper schema/migrations\n */\nexport async function setupTestORM(\n config: MikroOrmTestConfig\n): Promise<MikroORM> {\n const {\n mikroOrmConfigPath,\n databaseType,\n useMigrations = false,\n container\n } = config;\n\n // Dynamically import the MikroORM config\n const { default: mikroOrmConfig } = await import(mikroOrmConfigPath);\n\n const dbPort = getDatabasePort(databaseType);\n\n // SQLite databases are file-based\n let ormConfig: any;\n if (\n databaseType === 'sqlite' ||\n databaseType === 'better-sqlite' ||\n databaseType === 'libsql'\n ) {\n ormConfig = {\n ...mikroOrmConfig,\n dbName: ':memory:', // In-memory SQLite for tests\n debug: false,\n ...(useMigrations\n ? {\n migrations: {\n path: config.migrationsPath,\n glob: '!(*.d).{js,ts}',\n dropTables: true\n }\n }\n : {\n schemaGenerator: {\n createForeignKeyConstraints: false,\n wrap: false\n }\n })\n };\n } else if (container) {\n // Container-based databases\n ormConfig = {\n ...mikroOrmConfig,\n dbName: 'test_db',\n host: container.getHost(),\n user: databaseType === 'mssql' ? 'SA' : 'test_user',\n password:\n databaseType === 'mssql' ? 'Test_Password123!' : 'test_password',\n port: container.getMappedPort(dbPort),\n debug: false,\n ...(useMigrations\n ? {\n migrations: {\n path: config.migrationsPath,\n glob: '!(*.d).{js,ts}',\n dropTables: true\n }\n }\n : {\n schemaGenerator: {\n createForeignKeyConstraints: false,\n wrap: false\n }\n })\n };\n }\n\n const orm = await MikroORM.init(ormConfig);\n\n // Initialize database schema\n if (useMigrations) {\n await orm.getMigrator().up();\n } else {\n await orm.getSchemaGenerator().createSchema();\n }\n\n return orm;\n}\n\n/**\n * Clear all data from the test database\n */\nexport async function clearTestDatabase(\n orm: MikroORM,\n redis?: Redis\n): Promise<void> {\n // Clear Redis if provided\n if (redis) {\n await redis.flushall();\n }\n\n // Clear all database entities\n const em = orm.em.fork();\n const entities = Object.values(orm.getMetadata().getAll());\n\n // Delete in reverse order to avoid foreign key constraints\n for (const entity of entities.reverse()) {\n try {\n await em.nativeDelete(entity.class, {});\n } catch (error) {\n // Ignore \"table does not exist\" errors\n if (!(error as Error).message?.includes('does not exist')) {\n throw error;\n }\n }\n }\n\n await em.flush();\n}\n","import { MikroORM } from '@mikro-orm/core';\nimport Redis from 'ioredis';\nimport { StartedTestContainer } from 'testcontainers';\nimport { DatabaseType, TestContainerManager } from './containers';\nimport { clearTestDatabase, setupTestORM } from './database';\nimport { setupTestEnvironment } from './environment';\n\nexport interface BlueprintTestConfig {\n /**\n * Path to the MikroORM config file (e.g., '../mikro-orm.config')\n */\n mikroOrmConfigPath: string;\n\n /**\n * Database type (postgres, mysql, mongodb, etc.)\n * @default 'postgres'\n */\n databaseType?: DatabaseType;\n\n /**\n * Whether to use migrations (true) or schema generation (false)\n */\n useMigrations?: boolean;\n\n /**\n * Path to migrations directory (required if useMigrations is true)\n */\n migrationsPath?: string;\n\n /**\n * Whether the blueprint needs Redis\n */\n needsRedis?: boolean;\n\n /**\n * Custom environment variables to set\n */\n customEnvVars?: Record<string, string>;\n\n /**\n * Custom setup hook called after containers and ORM are initialized\n */\n onSetup?: (setup: TestSetupResult) => Promise<void>;\n}\n\nexport interface TestSetupResult {\n container: StartedTestContainer | null;\n redisContainer?: StartedTestContainer;\n orm: MikroORM;\n redis?: Redis;\n}\n\n/**\n * Complete test harness for blueprint E2E testing\n *\n * Handles container setup, environment configuration, and database initialization\n *\n * @example\n * ```typescript\n * const harness = new BlueprintTestHarness({\n * mikroOrmConfigPath: '../mikro-orm.config',\n * useMigrations: false,\n * needsRedis: true,\n * customEnvVars: {\n * STRIPE_API_KEY: 'sk_test_...'\n * }\n * });\n *\n * const setup = await harness.setup();\n * // ... run tests\n * await harness.cleanup();\n * ```\n */\nexport class BlueprintTestHarness {\n private containers: TestContainerManager;\n private result?: TestSetupResult;\n\n constructor(private config: BlueprintTestConfig) {\n this.containers = new TestContainerManager();\n }\n\n /**\n * Setup all test infrastructure (containers, ORM, Redis)\n */\n async setup(): Promise<TestSetupResult> {\n const databaseType = this.config.databaseType || 'postgres';\n\n // Setup database container\n const container =\n await this.containers.setupDatabaseContainer(databaseType);\n\n // Setup Redis container if needed\n const redisContainer = this.config.needsRedis\n ? await this.containers.setupRedisContainer()\n : undefined;\n\n // Setup environment variables\n setupTestEnvironment({\n database: container,\n databaseType,\n redis: redisContainer,\n customVars: this.config.customEnvVars\n });\n\n // Setup ORM\n const orm = await setupTestORM({\n mikroOrmConfigPath: this.config.mikroOrmConfigPath,\n databaseType,\n useMigrations: this.config.useMigrations,\n migrationsPath: this.config.migrationsPath,\n container\n });\n\n // Setup Redis client if needed\n let redis: Redis | undefined;\n if (redisContainer) {\n redis = new Redis({\n host: redisContainer.getHost(),\n port: redisContainer.getMappedPort(6379),\n maxRetriesPerRequest: 3\n });\n await redis.ping();\n }\n\n this.result = { container, redisContainer, orm, redis };\n\n // Call custom setup hook\n if (this.config.onSetup) {\n await this.config.onSetup(this.result);\n }\n\n return this.result;\n }\n\n /**\n * Cleanup all test infrastructure\n */\n async cleanup(): Promise<void> {\n if (this.result?.redis) {\n await this.result.redis.quit();\n }\n if (this.result?.orm) {\n await this.result.orm.close();\n }\n await this.containers.cleanup();\n this.result = undefined;\n }\n\n /**\n * Clear all data from the database\n */\n async clearDatabase(): Promise<void> {\n if (this.result) {\n await clearTestDatabase(this.result.orm, this.result.redis);\n }\n }\n}\n","/**\n * Standard mock authentication tokens for testing\n */\nexport const TEST_TOKENS = {\n /**\n * Mock Bearer token for testing\n */\n AUTH: 'Bearer test-token',\n\n /**\n * Mock valid HMAC token for testing\n */\n HMAC: 'HMAC keyId=test-key ts=1234567890 nonce=test-nonce signature=test-signature',\n\n /**\n * Mock invalid HMAC token for testing authentication failures\n */\n HMAC_INVALID:\n 'HMAC keyId=invalid-key ts=1234567890 nonce=invalid-nonce signature=invalid-signature'\n} as const;\n\n"],"mappings":"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","names":["getDatabasePort"]}
1
+ {"version":3,"sources":["../src/containers.ts","../src/environment.ts","../src/database.ts","../src/harness.ts","../src/tokens.ts"],"sourcesContent":["import { GenericContainer, StartedTestContainer } from 'testcontainers';\n\nexport type DatabaseType =\n | 'postgres'\n | 'postgresql'\n | 'mysql'\n | 'mariadb'\n | 'mongodb'\n | 'mongo'\n | 'mssql'\n | 'libsql'\n | 'sqlite'\n | 'better-sqlite';\n\nexport interface PostgresConfig {\n user?: string;\n password?: string;\n database?: string;\n command?: string[];\n}\n\nexport interface MySQLConfig {\n user?: string;\n password?: string;\n database?: string;\n rootPassword?: string;\n}\n\nexport interface MongoDBConfig {\n user?: string;\n password?: string;\n database?: string;\n}\n\nexport interface MSSQLConfig {\n user?: string;\n password?: string;\n database?: string;\n saPassword?: string;\n}\n\nexport interface SQLiteConfig {\n database?: string;\n}\n\nexport interface RedisConfig {\n command?: string[];\n}\n\nexport type DatabaseConfig =\n | PostgresConfig\n | MySQLConfig\n | MongoDBConfig\n | MSSQLConfig\n | SQLiteConfig;\n\n/**\n * Manages test containers (PostgreSQL, MySQL, MongoDB, Redis, etc.) for E2E testing\n */\nexport class TestContainerManager {\n private containers: StartedTestContainer[] = [];\n\n /**\n * Setup database container based on type\n */\n async setupDatabaseContainer(\n type: DatabaseType,\n config: DatabaseConfig = {}\n ): Promise<StartedTestContainer | null> {\n const normalizedType = this.normalizeDatabaseType(type);\n\n switch (normalizedType) {\n case 'postgres':\n return this.setupPostgresContainer(config as PostgresConfig);\n case 'mysql':\n return this.setupMySQLContainer(config as MySQLConfig);\n case 'mongodb':\n return this.setupMongoDBContainer(config as MongoDBConfig);\n case 'mssql':\n return this.setupMSSQLContainer(config as MSSQLConfig);\n case 'sqlite':\n // SQLite doesn't need a container (file-based)\n return null;\n default:\n throw new Error(`Unsupported database type: ${type}`);\n }\n }\n\n /**\n * Normalize database type aliases\n */\n private normalizeDatabaseType(\n type: DatabaseType\n ): 'postgres' | 'mysql' | 'mongodb' | 'mssql' | 'sqlite' {\n switch (type) {\n case 'postgres':\n case 'postgresql':\n return 'postgres';\n case 'mysql':\n case 'mariadb':\n return 'mysql';\n case 'mongodb':\n case 'mongo':\n return 'mongodb';\n case 'mssql':\n return 'mssql';\n case 'sqlite':\n case 'better-sqlite':\n case 'libsql':\n return 'sqlite';\n default:\n throw new Error(`Unknown database type: ${type}`);\n }\n }\n\n /**\n * Setup PostgreSQL test container\n */\n async setupPostgresContainer(\n config: PostgresConfig = {}\n ): Promise<StartedTestContainer> {\n const {\n user = 'test_user',\n password = 'test_password',\n database = 'test_db',\n command = ['postgres', '-c', 'log_statement=all']\n } = config;\n\n const container = await new GenericContainer('postgres:latest')\n .withExposedPorts(5432)\n .withEnvironment({\n POSTGRES_USER: user,\n POSTGRES_PASSWORD: password,\n POSTGRES_DB: database\n })\n .withCommand(command)\n .start();\n\n this.containers.push(container);\n return container;\n }\n\n /**\n * Setup MySQL test container\n */\n async setupMySQLContainer(\n config: MySQLConfig = {}\n ): Promise<StartedTestContainer> {\n const {\n user = 'test_user',\n password = 'test_password',\n database = 'test_db',\n rootPassword = 'root_password'\n } = config;\n\n const container = await new GenericContainer('mysql:8')\n .withExposedPorts(3306)\n .withEnvironment({\n MYSQL_ROOT_PASSWORD: rootPassword,\n MYSQL_DATABASE: database,\n MYSQL_USER: user,\n MYSQL_PASSWORD: password\n })\n .start();\n\n this.containers.push(container);\n return container;\n }\n\n /**\n * Setup MongoDB test container\n */\n async setupMongoDBContainer(\n config: MongoDBConfig = {}\n ): Promise<StartedTestContainer> {\n const {\n user = 'test_user',\n password = 'test_password',\n database = 'test_db'\n } = config;\n\n const container = await new GenericContainer('mongo:latest')\n .withExposedPorts(27017)\n .withEnvironment({\n MONGO_INITDB_ROOT_USERNAME: user,\n MONGO_INITDB_ROOT_PASSWORD: password,\n MONGO_INITDB_DATABASE: database\n })\n .start();\n\n this.containers.push(container);\n return container;\n }\n\n /**\n * Setup Microsoft SQL Server test container\n */\n async setupMSSQLContainer(\n config: MSSQLConfig = {}\n ): Promise<StartedTestContainer> {\n const {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n user = 'SA',\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n password = 'Test_Password123!',\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n database = 'test_db',\n saPassword = 'Test_Password123!'\n } = config;\n\n const container = await new GenericContainer(\n 'mcr.microsoft.com/mssql/server:2022-latest'\n )\n .withExposedPorts(1433)\n .withEnvironment({\n ACCEPT_EULA: 'Y',\n SA_PASSWORD: saPassword,\n MSSQL_PID: 'Developer'\n })\n .start();\n\n // Wait a bit for SQL Server to be ready\n await new Promise((resolve) => setTimeout(resolve, 3000));\n\n this.containers.push(container);\n return container;\n }\n\n /**\n * Setup Redis test container\n */\n async setupRedisContainer(\n config: RedisConfig = {}\n ): Promise<StartedTestContainer> {\n const { command = ['redis-server', '--appendonly', 'yes'] } = config;\n\n const container = await new GenericContainer('redis:latest')\n .withExposedPorts(6379)\n .withCommand(command)\n .start();\n\n this.containers.push(container);\n return container;\n }\n\n /**\n * Cleanup all containers\n */\n async cleanup(): Promise<void> {\n await Promise.all(\n this.containers.map((container) =>\n container.stop({ remove: true, removeVolumes: true }).catch(() => {\n // Ignore cleanup errors\n })\n )\n );\n this.containers = [];\n }\n}\n","import { StartedTestContainer } from 'testcontainers';\nimport { DatabaseType } from './containers';\n\nexport interface TestEnvConfig {\n database: StartedTestContainer | null;\n databaseType: DatabaseType;\n redis?: StartedTestContainer;\n hmacSecret?: string;\n customVars?: Record<string, string>;\n}\n\n/**\n * Get the default port for a database type\n */\nfunction getDatabasePort(type: DatabaseType): number {\n switch (type) {\n case 'postgres':\n case 'postgresql':\n return 5432;\n case 'mysql':\n case 'mariadb':\n return 3306;\n case 'mongodb':\n case 'mongo':\n return 27017;\n case 'mssql':\n return 1433;\n case 'sqlite':\n case 'better-sqlite':\n case 'libsql':\n return 0; // SQLite is file-based, no port\n default:\n return 5432;\n }\n}\n\n/**\n * Setup test environment variables for a blueprint test\n */\nexport function setupTestEnvironment(config: TestEnvConfig): void {\n const {\n database,\n databaseType,\n redis,\n hmacSecret = 'test-secret-key',\n customVars = {}\n } = config;\n\n const dbPort = getDatabasePort(databaseType);\n\n // Database environment variables\n process.env.DB_NAME = 'test_db';\n\n // SQLite databases are file-based, no container needed\n if (\n databaseType === 'sqlite' ||\n databaseType === 'better-sqlite' ||\n databaseType === 'libsql'\n ) {\n process.env.DB_PATH = ':memory:'; // In-memory SQLite for tests\n } else if (database) {\n process.env.DB_HOST = database.getHost();\n process.env.DB_USER = databaseType === 'mssql' ? 'SA' : 'test_user';\n process.env.DB_PASSWORD =\n databaseType === 'mssql' ? 'Test_Password123!' : 'test_password';\n process.env.DB_PORT = database.getMappedPort(dbPort).toString();\n }\n\n // Redis environment variables (if provided)\n if (redis) {\n process.env.REDIS_URL = `redis://${redis.getHost()}:${redis.getMappedPort(6379)}`;\n }\n\n // Standard test environment variables\n process.env.HMAC_SECRET_KEY = hmacSecret;\n process.env.JWKS_PUBLIC_KEY_URL =\n 'http://localhost:3000/.well-known/jwks.json';\n process.env.OTEL_SERVICE_NAME = 'test-service';\n process.env.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://localhost:4318';\n process.env.HOST = 'localhost';\n process.env.PORT = '3000';\n process.env.NODE_ENV = 'test';\n process.env.VERSION = 'v1';\n process.env.DOCS_PATH = '/docs';\n process.env.OTEL_LEVEL = 'info';\n process.env.DOTENV_FILE_PATH = '.env.test';\n\n // Custom environment variables\n Object.entries(customVars).forEach(([key, value]) => {\n process.env[key] = value;\n });\n}\n","import { MikroORM, Options } from '@mikro-orm/core';\nimport Redis from 'ioredis';\nimport { StartedTestContainer } from 'testcontainers';\nimport { DatabaseType } from './containers';\n\nexport interface MikroOrmTestConfig {\n /**\n * MikroORM config object (imported from mikro-orm.config)\n */\n mikroOrmConfig: Options;\n\n /**\n * Database type (postgres, mysql, mongodb, etc.)\n */\n databaseType: DatabaseType;\n\n /**\n * Whether to use migrations (true) or schema generation (false)\n * - true: IAM blueprints (uses getMigrator().up())\n * - false: Billing blueprints (uses getSchemaGenerator().createSchema())\n */\n useMigrations?: boolean;\n\n /**\n * Path to migrations directory (required if useMigrations is true)\n */\n migrationsPath?: string;\n\n /**\n * Database container instance (null for file-based databases like SQLite)\n */\n container: StartedTestContainer | null;\n}\n\n/**\n * Get the default port for a database type\n */\nfunction getDatabasePort(type: DatabaseType): number {\n switch (type) {\n case 'postgres':\n case 'postgresql':\n return 5432;\n case 'mysql':\n case 'mariadb':\n return 3306;\n case 'mongodb':\n case 'mongo':\n return 27017;\n case 'mssql':\n return 1433;\n case 'sqlite':\n case 'better-sqlite':\n case 'libsql':\n return 0; // SQLite is file-based, no port\n default:\n return 5432;\n }\n}\n\n/**\n * Setup MikroORM for testing with proper schema/migrations\n */\nexport async function setupTestORM(\n config: MikroOrmTestConfig\n): Promise<MikroORM> {\n const {\n mikroOrmConfig,\n databaseType,\n useMigrations = false,\n container\n } = config;\n\n const dbPort = getDatabasePort(databaseType);\n\n // SQLite databases are file-based\n let ormConfig: Options = {};\n if (\n databaseType === 'sqlite' ||\n databaseType === 'better-sqlite' ||\n databaseType === 'libsql'\n ) {\n ormConfig = {\n ...mikroOrmConfig,\n dbName: ':memory:', // In-memory SQLite for tests\n debug: false,\n ...(useMigrations\n ? {\n migrations: {\n path: config.migrationsPath,\n glob: '!(*.d).{js,ts}',\n dropTables: true\n }\n }\n : {\n schemaGenerator: {\n createForeignKeyConstraints: false\n }\n })\n };\n } else if (container) {\n // Container-based databases\n ormConfig = {\n ...mikroOrmConfig,\n dbName: 'test_db',\n host: container.getHost(),\n user: databaseType === 'mssql' ? 'SA' : 'test_user',\n password:\n databaseType === 'mssql' ? 'Test_Password123!' : 'test_password',\n port: container.getMappedPort(dbPort),\n debug: false,\n ...(useMigrations\n ? {\n migrations: {\n path: config.migrationsPath,\n glob: '!(*.d).{js,ts}',\n dropTables: true\n }\n }\n : {\n schemaGenerator: {\n createForeignKeyConstraints: false\n }\n })\n };\n }\n\n const orm = await MikroORM.init(ormConfig);\n\n // Initialize database schema\n if (useMigrations) {\n await orm.getMigrator().up();\n } else {\n await orm.getSchemaGenerator().createSchema();\n }\n\n return orm;\n}\n\n/**\n * Clear all data from the test database\n */\nexport async function clearTestDatabase(\n orm: MikroORM,\n redis?: Redis\n): Promise<void> {\n // Clear Redis if provided\n if (redis) {\n await redis.flushall();\n }\n\n // Clear all database entities\n const em = orm.em.fork();\n const entities = Object.values(orm.getMetadata().getAll());\n\n // Delete in reverse order to avoid foreign key constraints\n for (const entity of entities.reverse()) {\n try {\n await em.nativeDelete(entity.class, {});\n } catch (error) {\n // Ignore \"table does not exist\" errors\n if (!(error as Error).message?.includes('does not exist')) {\n throw error;\n }\n }\n }\n\n await em.flush();\n}\n","import { MikroORM, Options } from '@mikro-orm/core';\nimport Redis from 'ioredis';\nimport { StartedTestContainer } from 'testcontainers';\nimport { DatabaseType, TestContainerManager } from './containers';\nimport { clearTestDatabase, setupTestORM } from './database';\nimport { setupTestEnvironment } from './environment';\n\nexport interface BlueprintTestConfig {\n /**\n * Function that imports and returns the MikroORM config\n * This is called AFTER environment variables are set\n */\n getConfig: () => Promise<Options>;\n\n /**\n * Database type (postgres, mysql, mongodb, etc.)\n * @default 'postgres'\n */\n databaseType?: DatabaseType;\n\n /**\n * Whether to use migrations (true) or schema generation (false)\n */\n useMigrations?: boolean;\n\n /**\n * Path to migrations directory (required if useMigrations is true)\n */\n migrationsPath?: string;\n\n /**\n * Whether the blueprint needs Redis\n */\n needsRedis?: boolean;\n\n /**\n * Custom environment variables to set\n */\n customEnvVars?: Record<string, string>;\n\n /**\n * Custom setup hook called after containers and ORM are initialized\n */\n onSetup?: (setup: TestSetupResult) => Promise<void>;\n}\n\nexport interface TestSetupResult {\n container: StartedTestContainer | null;\n redisContainer?: StartedTestContainer;\n orm: MikroORM;\n redis?: Redis;\n}\n\n/**\n * Complete test harness for blueprint E2E testing\n *\n * Handles container setup, environment configuration, and database initialization\n *\n * @example\n * ```typescript\n * const harness = new BlueprintTestHarness({\n * mikroOrmConfigPath: '../mikro-orm.config',\n * useMigrations: false,\n * needsRedis: true,\n * customEnvVars: {\n * STRIPE_API_KEY: 'sk_test_...'\n * }\n * });\n *\n * const setup = await harness.setup();\n * // ... run tests\n * await harness.cleanup();\n * ```\n */\nexport class BlueprintTestHarness {\n private containers: TestContainerManager;\n private result?: TestSetupResult;\n\n constructor(private config: BlueprintTestConfig) {\n this.containers = new TestContainerManager();\n }\n\n /**\n * Setup all test infrastructure (containers, ORM, Redis)\n */\n async setup(): Promise<TestSetupResult> {\n const databaseType = this.config.databaseType || 'postgres';\n\n // Setup database container\n const container =\n await this.containers.setupDatabaseContainer(databaseType);\n\n // Setup Redis container if needed\n const redisContainer = this.config.needsRedis\n ? await this.containers.setupRedisContainer()\n : undefined;\n\n // Setup environment variables\n setupTestEnvironment({\n database: container,\n databaseType,\n redis: redisContainer,\n customVars: this.config.customEnvVars\n });\n\n // Get the config AFTER environment is set\n const mikroOrmConfig = await this.config.getConfig();\n\n // Setup ORM\n const orm = await setupTestORM({\n mikroOrmConfig,\n databaseType,\n useMigrations: this.config.useMigrations,\n migrationsPath: this.config.migrationsPath,\n container\n });\n\n // Setup Redis client if needed\n let redis: Redis | undefined;\n if (redisContainer) {\n redis = new Redis({\n host: redisContainer.getHost(),\n port: redisContainer.getMappedPort(6379),\n maxRetriesPerRequest: 3\n });\n await redis.ping();\n }\n\n this.result = { container, redisContainer, orm, redis };\n\n // Call custom setup hook\n if (this.config.onSetup) {\n await this.config.onSetup(this.result);\n }\n\n return this.result;\n }\n\n /**\n * Cleanup all test infrastructure\n */\n async cleanup(): Promise<void> {\n if (this.result?.redis) {\n await this.result.redis.quit();\n }\n if (this.result?.orm) {\n await this.result.orm.close();\n }\n await this.containers.cleanup();\n this.result = undefined;\n }\n\n /**\n * Clear all data from the database\n */\n async clearDatabase(): Promise<void> {\n if (this.result) {\n await clearTestDatabase(this.result.orm, this.result.redis);\n }\n }\n}\n","/**\n * Standard mock authentication tokens for testing\n */\nexport const TEST_TOKENS = {\n /**\n * Mock Bearer token for testing\n */\n AUTH: 'Bearer test-token',\n\n /**\n * Mock valid HMAC token for testing\n */\n HMAC: 'HMAC keyId=test-key ts=1234567890 nonce=test-nonce signature=test-signature',\n\n /**\n * Mock invalid HMAC token for testing authentication failures\n */\n HMAC_INVALID:\n 'HMAC keyId=invalid-key ts=1234567890 nonce=invalid-nonce signature=invalid-signature'\n} as const;\n"],"mappings":"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","names":["getDatabasePort"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forklaunch/testing",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "description": "Testing utilities for forklaunch-js blueprints including testcontainer management",
5
5
  "homepage": "https://github.com/forklaunch/forklaunch-js#readme",
6
6
  "bugs": {
@@ -28,21 +28,21 @@
28
28
  "lib/**"
29
29
  ],
30
30
  "dependencies": {
31
- "@mikro-orm/core": "^6.5.6",
32
- "ioredis": "^5.5.1",
31
+ "@mikro-orm/core": "^6.5.7",
32
+ "ioredis": "^5.8.1",
33
33
  "testcontainers": "^11.7.1"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@eslint/js": "^9.37.0",
37
- "@types/node": "^24.6.2",
38
- "@typescript/native-preview": "7.0.0-dev.20251003.1",
37
+ "@types/node": "^24.7.0",
38
+ "@typescript/native-preview": "7.0.0-dev.20251007.1",
39
39
  "globals": "^16.4.0",
40
40
  "prettier": "^3.6.2",
41
41
  "ts-node": "^10.9.2",
42
42
  "tsup": "^8.5.0",
43
43
  "typescript": "^5.9.3",
44
- "typescript-eslint": "^8.45.0",
45
- "vitest": "^3.0.0"
44
+ "typescript-eslint": "^8.46.0",
45
+ "vitest": "^3.2.4"
46
46
  },
47
47
  "peerDependencies": {
48
48
  "vitest": "^3.0.0"