@zintrust/core 0.1.19 → 0.1.21
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/README.md +10 -10
- package/bin/zintrust-main.d.ts.map +1 -1
- package/bin/zintrust-main.js +9 -0
- package/package.json +3 -2
- package/public/error-pages/404.html +145 -0
- package/public/error-pages/500.html +266 -0
- package/public/error-pages/error.css +628 -0
- package/public/error-pages/error.js +428 -0
- package/public/zintrust.svg +30 -0
- package/routes/api.d.ts.map +1 -1
- package/routes/api.js +41 -17
- package/routes/metrics.d.ts +9 -0
- package/routes/metrics.d.ts.map +1 -0
- package/routes/metrics.js +20 -0
- package/routes/openapi.d.ts +9 -0
- package/routes/openapi.d.ts.map +1 -0
- package/routes/openapi.js +76 -0
- package/src/boot/Application.d.ts +2 -2
- package/src/boot/Application.d.ts.map +1 -1
- package/src/boot/Application.js +66 -13
- package/src/boot/Server.d.ts +3 -2
- package/src/boot/Server.d.ts.map +1 -1
- package/src/boot/Server.js +39 -165
- package/src/boot/bootstrap.js +2 -0
- package/src/cache/Cache.d.ts +1 -1
- package/src/cache/Cache.d.ts.map +1 -1
- package/src/cache/CacheDriver.d.ts +4 -0
- package/src/cache/CacheDriver.d.ts.map +1 -1
- package/src/cache/drivers/KVDriver.d.ts +1 -1
- package/src/cache/drivers/KVDriver.d.ts.map +1 -1
- package/src/cache/drivers/MemoryDriver.d.ts +1 -1
- package/src/cache/drivers/MemoryDriver.d.ts.map +1 -1
- package/src/cache/drivers/MemoryDriver.js +16 -0
- package/src/cache/drivers/MongoDriver.d.ts +1 -1
- package/src/cache/drivers/MongoDriver.d.ts.map +1 -1
- package/src/cache/drivers/RedisDriver.d.ts +1 -1
- package/src/cache/drivers/RedisDriver.d.ts.map +1 -1
- package/src/cli/CLI.d.ts.map +1 -1
- package/src/cli/CLI.js +10 -4
- package/src/cli/commands/AddCommand.d.ts +2 -2
- package/src/cli/commands/AddCommand.d.ts.map +1 -1
- package/src/cli/commands/AddCommand.js +135 -58
- package/src/cli/commands/ConfigCommand.d.ts +1 -1
- package/src/cli/commands/ConfigCommand.d.ts.map +1 -1
- package/src/cli/commands/CreateCommand.d.ts +15 -0
- package/src/cli/commands/CreateCommand.d.ts.map +1 -0
- package/src/cli/commands/CreateCommand.js +143 -0
- package/src/cli/commands/D1MigrateCommand.d.ts +1 -1
- package/src/cli/commands/D1MigrateCommand.d.ts.map +1 -1
- package/src/cli/commands/D1MigrateCommand.js +16 -20
- package/src/cli/commands/DbSeedCommand.d.ts +9 -0
- package/src/cli/commands/DbSeedCommand.d.ts.map +1 -0
- package/src/cli/commands/DbSeedCommand.js +171 -0
- package/src/cli/commands/DebugCommand.d.ts +1 -1
- package/src/cli/commands/DebugCommand.d.ts.map +1 -1
- package/src/cli/commands/FixCommand.d.ts +1 -1
- package/src/cli/commands/FixCommand.d.ts.map +1 -1
- package/src/cli/commands/JwtDevCommand.d.ts +8 -0
- package/src/cli/commands/JwtDevCommand.d.ts.map +1 -0
- package/src/cli/commands/JwtDevCommand.js +114 -0
- package/src/cli/commands/KeyGenerateCommand.d.ts +1 -1
- package/src/cli/commands/KeyGenerateCommand.d.ts.map +1 -1
- package/src/cli/commands/LogsCommand.d.ts +2 -2
- package/src/cli/commands/LogsCommand.d.ts.map +1 -1
- package/src/cli/commands/LogsCommand.js +36 -2
- package/src/cli/commands/MakeMailTemplateCommand.d.ts +1 -1
- package/src/cli/commands/MakeMailTemplateCommand.d.ts.map +1 -1
- package/src/cli/commands/MakeNotificationTemplateCommand.d.ts +1 -1
- package/src/cli/commands/MakeNotificationTemplateCommand.d.ts.map +1 -1
- package/src/cli/commands/MigrateCommand.d.ts +1 -1
- package/src/cli/commands/MigrateCommand.d.ts.map +1 -1
- package/src/cli/commands/MigrateCommand.js +324 -35
- package/src/cli/commands/NewCommand.d.ts +1 -1
- package/src/cli/commands/NewCommand.d.ts.map +1 -1
- package/src/cli/commands/NewCommand.js +12 -4
- package/src/cli/commands/PluginCommand.d.ts +1 -1
- package/src/cli/commands/PluginCommand.d.ts.map +1 -1
- package/src/cli/commands/PrepareCommand.d.ts +1 -1
- package/src/cli/commands/PrepareCommand.d.ts.map +1 -1
- package/src/cli/commands/QACommand.d.ts +2 -2
- package/src/cli/commands/QACommand.d.ts.map +1 -1
- package/src/cli/commands/RoutesCommand.d.ts +10 -0
- package/src/cli/commands/RoutesCommand.d.ts.map +1 -0
- package/src/cli/commands/RoutesCommand.js +242 -0
- package/src/cli/commands/SimulateCommand.d.ts +1 -1
- package/src/cli/commands/SimulateCommand.d.ts.map +1 -1
- package/src/cli/commands/index.d.ts +3 -0
- package/src/cli/commands/index.d.ts.map +1 -1
- package/src/cli/commands/index.js +3 -0
- package/src/cli/config/ConfigManager.d.ts +1 -1
- package/src/cli/config/ConfigManager.d.ts.map +1 -1
- package/src/cli/config/ConfigValidator.d.ts +1 -1
- package/src/cli/config/ConfigValidator.d.ts.map +1 -1
- package/src/cli/config/ConfigValidator.js +1 -1
- package/src/cli/d1/D1SqlMigrations.d.ts +20 -0
- package/src/cli/d1/D1SqlMigrations.d.ts.map +1 -0
- package/src/cli/d1/D1SqlMigrations.js +229 -0
- package/src/cli/d1/WranglerConfig.d.ts +4 -0
- package/src/cli/d1/WranglerConfig.d.ts.map +1 -0
- package/src/cli/d1/WranglerConfig.js +122 -0
- package/src/cli/d1/WranglerD1.d.ts +11 -0
- package/src/cli/d1/WranglerD1.d.ts.map +1 -0
- package/src/cli/d1/WranglerD1.js +16 -0
- package/src/cli/scaffolding/ControllerGenerator.d.ts.map +1 -1
- package/src/cli/scaffolding/ControllerGenerator.js +76 -26
- package/src/cli/scaffolding/FactoryGenerator.d.ts.map +1 -1
- package/src/cli/scaffolding/FactoryGenerator.js +3 -1
- package/src/cli/scaffolding/GovernanceScaffolder.d.ts +23 -0
- package/src/cli/scaffolding/GovernanceScaffolder.d.ts.map +1 -0
- package/src/cli/scaffolding/GovernanceScaffolder.js +327 -0
- package/src/cli/scaffolding/MigrationGenerator.d.ts +10 -0
- package/src/cli/scaffolding/MigrationGenerator.d.ts.map +1 -1
- package/src/cli/scaffolding/MigrationGenerator.js +137 -51
- package/src/cli/scaffolding/ModelGenerator.js +1 -1
- package/src/cli/scaffolding/ProjectScaffolder.d.ts.map +1 -1
- package/src/cli/scaffolding/ProjectScaffolder.js +36 -4
- package/src/cli/scaffolding/RouteGenerator.d.ts.map +1 -1
- package/src/cli/scaffolding/RouteGenerator.js +79 -43
- package/src/cli/scaffolding/SeederGenerator.d.ts +5 -0
- package/src/cli/scaffolding/SeederGenerator.d.ts.map +1 -1
- package/src/cli/scaffolding/SeederGenerator.js +63 -15
- package/src/cli/scaffolding/ServiceScaffolder.d.ts.map +1 -1
- package/src/cli/scaffolding/ServiceScaffolder.js +28 -7
- package/src/cli/scaffolding/index.d.ts +2 -0
- package/src/cli/scaffolding/index.d.ts.map +1 -1
- package/src/cli/scaffolding/index.js +1 -0
- package/src/common/index.d.ts +8 -0
- package/src/common/index.d.ts.map +1 -1
- package/src/common/index.js +28 -0
- package/src/common/utility.d.ts +38 -0
- package/src/common/utility.d.ts.map +1 -0
- package/src/common/utility.js +101 -0
- package/src/config/FileLogWriter.d.ts +2 -1
- package/src/config/FileLogWriter.d.ts.map +1 -1
- package/src/config/FileLogWriter.js +83 -2
- package/src/config/app.d.ts.map +1 -1
- package/src/config/app.js +3 -1
- package/src/config/broadcast.d.ts +14 -28
- package/src/config/broadcast.d.ts.map +1 -1
- package/src/config/broadcast.js +69 -35
- package/src/config/cache.d.ts +13 -45
- package/src/config/cache.d.ts.map +1 -1
- package/src/config/cache.js +69 -25
- package/src/config/cloudflare.d.ts +1 -1
- package/src/config/cloudflare.d.ts.map +1 -1
- package/src/config/database.d.ts +22 -64
- package/src/config/database.d.ts.map +1 -1
- package/src/config/database.js +191 -37
- package/src/config/env.d.ts +12 -0
- package/src/config/env.d.ts.map +1 -1
- package/src/config/env.js +14 -0
- package/src/config/index.d.ts +33 -137
- package/src/config/index.d.ts.map +1 -1
- package/src/config/logging/KvLogger.js +1 -1
- package/src/config/logging/SlackLogger.js +2 -2
- package/src/config/mail.d.ts +19 -55
- package/src/config/mail.d.ts.map +1 -1
- package/src/config/mail.js +63 -21
- package/src/config/middleware.d.ts +44 -1
- package/src/config/middleware.d.ts.map +1 -1
- package/src/config/middleware.js +157 -5
- package/src/config/notification.d.ts +14 -27
- package/src/config/notification.d.ts.map +1 -1
- package/src/config/notification.js +82 -36
- package/src/config/queue.d.ts +21 -51
- package/src/config/queue.d.ts.map +1 -1
- package/src/config/queue.js +72 -27
- package/src/config/security.d.ts +1 -1
- package/src/config/security.js +1 -1
- package/src/config/storage.d.ts +27 -34
- package/src/config/storage.d.ts.map +1 -1
- package/src/config/storage.js +97 -56
- package/src/config/type.d.ts +13 -2
- package/src/config/type.d.ts.map +1 -1
- package/src/events/EventDispatcher.d.ts.map +1 -1
- package/src/events/EventDispatcher.js +6 -4
- package/src/exceptions/ZintrustError.d.ts +7 -0
- package/src/exceptions/ZintrustError.d.ts.map +1 -1
- package/src/exceptions/ZintrustError.js +56 -0
- package/src/features/Auth.d.ts +1 -1
- package/src/features/Auth.d.ts.map +1 -1
- package/src/features/Auth.js +3 -3
- package/src/features/Queue.js +1 -1
- package/src/functions/cloudflare.d.ts.map +1 -1
- package/src/functions/cloudflare.js +3 -14
- package/src/functions/deno.d.ts.map +1 -1
- package/src/functions/deno.js +3 -14
- package/src/functions/lambda.d.ts.map +1 -1
- package/src/functions/lambda.js +3 -14
- package/src/health/StartupHealthChecks.js +1 -1
- package/src/http/Controller.d.ts +2 -2
- package/src/http/Controller.d.ts.map +1 -1
- package/src/http/FileUpload.d.ts +68 -0
- package/src/http/FileUpload.d.ts.map +1 -0
- package/src/http/FileUpload.js +120 -0
- package/src/http/Kernel.d.ts +5 -5
- package/src/http/Kernel.d.ts.map +1 -1
- package/src/http/Kernel.js +139 -23
- package/src/http/Request.d.ts +20 -1
- package/src/http/Request.d.ts.map +1 -1
- package/src/http/Request.js +23 -0
- package/src/http/RequestContext.d.ts +6 -0
- package/src/http/RequestContext.d.ts.map +1 -1
- package/src/http/RequestContext.js +77 -1
- package/src/http/Response.d.ts +1 -1
- package/src/http/Response.d.ts.map +1 -1
- package/src/http/ValidationHelper.d.ts +78 -0
- package/src/http/ValidationHelper.d.ts.map +1 -0
- package/src/http/ValidationHelper.js +121 -0
- package/src/http/error-pages/ErrorPageRenderer.d.ts +17 -0
- package/src/http/error-pages/ErrorPageRenderer.d.ts.map +1 -0
- package/src/http/error-pages/ErrorPageRenderer.js +88 -0
- package/src/http/middleware/BodyParsingMiddleware.d.ts +12 -0
- package/src/http/middleware/BodyParsingMiddleware.d.ts.map +1 -0
- package/src/http/middleware/BodyParsingMiddleware.js +251 -0
- package/src/http/middleware/FileUploadMiddleware.d.ts +12 -0
- package/src/http/middleware/FileUploadMiddleware.d.ts.map +1 -0
- package/src/http/middleware/FileUploadMiddleware.js +74 -0
- package/src/http/parsers/BodyParsers.d.ts +32 -0
- package/src/http/parsers/BodyParsers.d.ts.map +1 -0
- package/src/http/parsers/BodyParsers.js +159 -0
- package/src/http/parsers/MultipartParser.d.ts +33 -0
- package/src/http/parsers/MultipartParser.d.ts.map +1 -0
- package/src/http/parsers/MultipartParser.js +156 -0
- package/src/http/parsers/MultipartParserRegistry.d.ts +34 -0
- package/src/http/parsers/MultipartParserRegistry.d.ts.map +1 -0
- package/src/http/parsers/MultipartParserRegistry.js +20 -0
- package/src/http/validated.d.ts +12 -0
- package/src/http/validated.d.ts.map +1 -0
- package/src/http/validated.js +41 -0
- package/src/index.d.ts +73 -12
- package/src/index.d.ts.map +1 -1
- package/src/index.js +60 -5
- package/src/microservices/PostgresAdapter.d.ts.map +1 -1
- package/src/microservices/PostgresAdapter.js +0 -1
- package/src/microservices/RequestTracingMiddleware.d.ts +2 -2
- package/src/microservices/RequestTracingMiddleware.d.ts.map +1 -1
- package/src/microservices/RequestTracingMiddleware.js +3 -0
- package/src/microservices/ServiceAuthMiddleware.d.ts +2 -2
- package/src/microservices/ServiceAuthMiddleware.d.ts.map +1 -1
- package/src/middleware/AuthMiddleware.d.ts +10 -0
- package/src/middleware/AuthMiddleware.d.ts.map +1 -0
- package/src/middleware/AuthMiddleware.js +16 -0
- package/src/middleware/CsrfMiddleware.d.ts +11 -1
- package/src/middleware/CsrfMiddleware.d.ts.map +1 -1
- package/src/middleware/CsrfMiddleware.js +33 -0
- package/src/middleware/JwtAuthMiddleware.d.ts +11 -0
- package/src/middleware/JwtAuthMiddleware.d.ts.map +1 -0
- package/src/middleware/JwtAuthMiddleware.js +73 -0
- package/src/middleware/LoggingMiddleware.d.ts.map +1 -1
- package/src/middleware/LoggingMiddleware.js +8 -3
- package/src/middleware/MiddlewareStack.d.ts +2 -2
- package/src/middleware/MiddlewareStack.d.ts.map +1 -1
- package/src/middleware/RateLimiter.d.ts +2 -2
- package/src/middleware/RateLimiter.d.ts.map +1 -1
- package/src/middleware/SanitizeBodyMiddleware.d.ts +12 -0
- package/src/middleware/SanitizeBodyMiddleware.d.ts.map +1 -0
- package/src/middleware/SanitizeBodyMiddleware.js +31 -0
- package/src/middleware/SecurityMiddleware.d.ts +1 -1
- package/src/middleware/SecurityMiddleware.d.ts.map +1 -1
- package/src/middleware/SessionMiddleware.d.ts +1 -1
- package/src/middleware/SessionMiddleware.d.ts.map +1 -1
- package/src/middleware/ValidationMiddleware.d.ts +25 -0
- package/src/middleware/ValidationMiddleware.d.ts.map +1 -0
- package/src/middleware/ValidationMiddleware.js +251 -0
- package/src/migrations/MigrationDiscovery.d.ts +5 -0
- package/src/migrations/MigrationDiscovery.d.ts.map +1 -0
- package/src/migrations/MigrationDiscovery.js +16 -0
- package/src/migrations/MigrationLoader.d.ts +5 -0
- package/src/migrations/MigrationLoader.d.ts.map +1 -0
- package/src/migrations/MigrationLoader.js +43 -0
- package/src/migrations/MigrationLock.d.ts +4 -0
- package/src/migrations/MigrationLock.d.ts.map +1 -0
- package/src/migrations/MigrationLock.js +33 -0
- package/src/migrations/Migrator.d.ts +23 -0
- package/src/migrations/Migrator.d.ts.map +1 -0
- package/src/migrations/Migrator.js +4 -0
- package/src/migrations/MigratorFactory.d.ts +25 -0
- package/src/migrations/MigratorFactory.d.ts.map +1 -0
- package/src/migrations/MigratorFactory.js +339 -0
- package/src/migrations/schema/Blueprint.d.ts +5 -0
- package/src/migrations/schema/Blueprint.d.ts.map +1 -0
- package/src/migrations/schema/Blueprint.js +189 -0
- package/src/migrations/schema/Schema.d.ts +8 -0
- package/src/migrations/schema/Schema.d.ts.map +1 -0
- package/src/migrations/schema/Schema.js +141 -0
- package/src/migrations/schema/SchemaCompiler.d.ts +20 -0
- package/src/migrations/schema/SchemaCompiler.d.ts.map +1 -0
- package/src/migrations/schema/SchemaCompiler.js +262 -0
- package/src/migrations/schema/index.d.ts +5 -0
- package/src/migrations/schema/index.d.ts.map +1 -0
- package/src/migrations/schema/index.js +3 -0
- package/src/migrations/schema/types.d.ts +86 -0
- package/src/migrations/schema/types.d.ts.map +1 -0
- package/src/migrations/schema/types.js +1 -0
- package/src/migrations/types.d.ts +45 -0
- package/src/migrations/types.d.ts.map +1 -0
- package/src/migrations/types.js +1 -0
- package/src/node-singletons/crypto.d.ts +1 -1
- package/src/node-singletons/crypto.d.ts.map +1 -1
- package/src/node-singletons/crypto.js +1 -1
- package/src/node-singletons/fs.d.ts +2 -2
- package/src/node-singletons/fs.d.ts.map +1 -1
- package/src/node-singletons/fs.js +1 -1
- package/src/node-singletons/util.d.ts +6 -0
- package/src/node-singletons/util.d.ts.map +1 -0
- package/src/node-singletons/util.js +5 -0
- package/src/node.d.ts +3 -1
- package/src/node.d.ts.map +1 -1
- package/src/node.js +6 -2
- package/src/observability/OpenTelemetry.d.ts +62 -0
- package/src/observability/OpenTelemetry.d.ts.map +1 -0
- package/src/observability/OpenTelemetry.js +167 -0
- package/src/observability/PrometheusMetrics.d.ts +25 -0
- package/src/observability/PrometheusMetrics.d.ts.map +1 -0
- package/src/observability/PrometheusMetrics.js +114 -0
- package/src/openapi/OpenApiGenerator.d.ts +68 -0
- package/src/openapi/OpenApiGenerator.d.ts.map +1 -0
- package/src/openapi/OpenApiGenerator.js +287 -0
- package/src/orm/Database.d.ts +5 -2
- package/src/orm/Database.d.ts.map +1 -1
- package/src/orm/Database.js +219 -63
- package/src/orm/DatabaseAdapter.d.ts +14 -0
- package/src/orm/DatabaseAdapter.d.ts.map +1 -1
- package/src/orm/DatabaseAdapterRegistry.d.ts.map +1 -1
- package/src/orm/DatabaseAdapterRegistry.js +3 -1
- package/src/orm/DatabaseRuntimeRegistration.d.ts.map +1 -1
- package/src/orm/DatabaseRuntimeRegistration.js +12 -0
- package/src/orm/Model.d.ts +30 -2
- package/src/orm/Model.d.ts.map +1 -1
- package/src/orm/Model.js +255 -62
- package/src/orm/QueryBuilder.d.ts +22 -1
- package/src/orm/QueryBuilder.d.ts.map +1 -1
- package/src/orm/QueryBuilder.js +406 -99
- package/src/orm/Relationships.d.ts +7 -1
- package/src/orm/Relationships.d.ts.map +1 -1
- package/src/orm/Relationships.js +18 -0
- package/src/orm/SchemaCompiler.d.ts +9 -0
- package/src/orm/SchemaCompiler.d.ts.map +1 -0
- package/src/orm/SchemaCompiler.js +145 -0
- package/src/orm/adapters/D1Adapter.d.ts +1 -1
- package/src/orm/adapters/D1Adapter.d.ts.map +1 -1
- package/src/orm/adapters/MySQLAdapter.d.ts +1 -1
- package/src/orm/adapters/MySQLAdapter.d.ts.map +1 -1
- package/src/orm/adapters/MySQLAdapter.js +88 -69
- package/src/orm/adapters/PostgreSQLAdapter.d.ts +1 -1
- package/src/orm/adapters/PostgreSQLAdapter.d.ts.map +1 -1
- package/src/orm/adapters/PostgreSQLAdapter.js +88 -69
- package/src/orm/adapters/SQLServerAdapter.d.ts +1 -1
- package/src/orm/adapters/SQLServerAdapter.d.ts.map +1 -1
- package/src/orm/adapters/SQLiteAdapter.d.ts +1 -1
- package/src/orm/adapters/SQLiteAdapter.d.ts.map +1 -1
- package/src/orm/adapters/SQLiteAdapter.js +59 -3
- package/src/orm/maintenance/SqliteMaintenance.d.ts +5 -0
- package/src/orm/maintenance/SqliteMaintenance.d.ts.map +1 -0
- package/src/orm/maintenance/SqliteMaintenance.js +14 -0
- package/src/orm/migrations/MigrationStore.d.ts +38 -0
- package/src/orm/migrations/MigrationStore.d.ts.map +1 -0
- package/src/orm/migrations/MigrationStore.js +157 -0
- package/src/performance/CodeGenerationBenchmark.d.ts.map +1 -1
- package/src/performance/Optimizer.d.ts +7 -6
- package/src/performance/Optimizer.d.ts.map +1 -1
- package/src/performance/Optimizer.js +170 -55
- package/src/profiling/MemoryProfiler.d.ts +1 -1
- package/src/profiling/MemoryProfiler.d.ts.map +1 -1
- package/src/profiling/N1Detector.d.ts +1 -1
- package/src/profiling/N1Detector.d.ts.map +1 -1
- package/src/profiling/QueryLogger.d.ts +1 -1
- package/src/profiling/QueryLogger.d.ts.map +1 -1
- package/src/profiling/RequestProfiler.d.ts +3 -3
- package/src/profiling/RequestProfiler.d.ts.map +1 -1
- package/src/routes/metrics.d.ts +2 -0
- package/src/routes/metrics.d.ts.map +1 -0
- package/src/routes/metrics.js +1 -0
- package/src/routing/CoreRoutes.d.ts +12 -0
- package/src/routing/CoreRoutes.d.ts.map +1 -0
- package/src/routing/CoreRoutes.js +151 -0
- package/src/routing/RouteRegistry.d.ts +39 -0
- package/src/routing/RouteRegistry.d.ts.map +1 -0
- package/src/routing/RouteRegistry.js +44 -0
- package/src/routing/Router.d.ts +26 -9
- package/src/routing/Router.d.ts.map +1 -1
- package/src/routing/Router.js +79 -35
- package/src/routing/common.d.ts +15 -0
- package/src/routing/common.d.ts.map +1 -0
- package/src/routing/common.js +47 -0
- package/src/routing/doc.d.ts +27 -0
- package/src/routing/doc.d.ts.map +1 -0
- package/src/routing/doc.js +110 -0
- package/src/routing/error.d.ts +21 -0
- package/src/routing/error.d.ts.map +1 -0
- package/src/routing/error.js +126 -0
- package/src/routing/errorPages.d.ts +14 -0
- package/src/routing/errorPages.d.ts.map +1 -0
- package/src/routing/errorPages.js +103 -0
- package/src/routing/publicRoot.d.ts +27 -0
- package/src/routing/publicRoot.d.ts.map +1 -0
- package/src/routing/publicRoot.js +110 -0
- package/src/runtime/PluginAutoImports.d.ts +21 -0
- package/src/runtime/PluginAutoImports.d.ts.map +1 -0
- package/src/runtime/PluginAutoImports.js +59 -0
- package/src/runtime/PluginManager.d.ts +1 -5
- package/src/runtime/PluginManager.d.ts.map +1 -1
- package/src/runtime/PluginManager.js +25 -18
- package/src/runtime/RuntimeDetector.d.ts +1 -1
- package/src/runtime/RuntimeDetector.d.ts.map +1 -1
- package/src/runtime/StartupConfigFileRegistry.d.ts +20 -0
- package/src/runtime/StartupConfigFileRegistry.d.ts.map +1 -0
- package/src/runtime/StartupConfigFileRegistry.js +44 -0
- package/src/runtime/adapters/CloudflareAdapter.d.ts +1 -1
- package/src/runtime/adapters/CloudflareAdapter.d.ts.map +1 -1
- package/src/runtime/adapters/CloudflareAdapter.js +1 -1
- package/src/runtime/adapters/DenoAdapter.d.ts +1 -1
- package/src/runtime/adapters/DenoAdapter.d.ts.map +1 -1
- package/src/runtime/adapters/DenoAdapter.js +1 -1
- package/src/runtime/adapters/LambdaAdapter.d.ts +1 -1
- package/src/runtime/adapters/LambdaAdapter.d.ts.map +1 -1
- package/src/runtime/adapters/LambdaAdapter.js +1 -1
- package/src/runtime/adapters/NodeServerAdapter.d.ts +1 -1
- package/src/runtime/adapters/NodeServerAdapter.d.ts.map +1 -1
- package/src/runtime/getKernel.d.ts +9 -0
- package/src/runtime/getKernel.d.ts.map +1 -0
- package/src/runtime/getKernel.js +27 -0
- package/src/runtime/useFileLoader.d.ts +26 -0
- package/src/runtime/useFileLoader.d.ts.map +1 -0
- package/src/runtime/useFileLoader.js +188 -0
- package/src/scripts/TemplateImportsCheck.js +40 -0
- package/src/scripts/TemplateSync.js +90 -24
- package/src/security/Encryptor.d.ts.map +1 -1
- package/src/security/Encryptor.js +64 -7
- package/src/security/JwtManager.d.ts +1 -0
- package/src/security/JwtManager.d.ts.map +1 -1
- package/src/security/JwtManager.js +33 -0
- package/src/security/Sanitizer.d.ts +76 -0
- package/src/security/Sanitizer.d.ts.map +1 -0
- package/src/security/Sanitizer.js +412 -0
- package/src/security/TokenRevocation.d.ts +7 -0
- package/src/security/TokenRevocation.d.ts.map +1 -0
- package/src/security/TokenRevocation.js +57 -0
- package/src/security/XssProtection.d.ts.map +1 -1
- package/src/security/XssProtection.js +62 -14
- package/src/seeders/SeederDiscovery.d.ts +5 -0
- package/src/seeders/SeederDiscovery.d.ts.map +1 -0
- package/src/seeders/SeederDiscovery.js +21 -0
- package/src/seeders/SeederLoader.d.ts +5 -0
- package/src/seeders/SeederLoader.d.ts.map +1 -0
- package/src/seeders/SeederLoader.js +60 -0
- package/src/seeders/types.d.ts +18 -0
- package/src/seeders/types.d.ts.map +1 -0
- package/src/seeders/types.js +1 -0
- package/src/session/SessionManager.js +1 -1
- package/src/templates/adapters/MySQLAdapter.ts.tpl +109 -85
- package/src/templates/adapters/PostgreSQLAdapter.ts.tpl +129 -88
- package/src/templates/adapters/SQLServerAdapter.ts.tpl +5 -9
- package/src/templates/adapters/SQLiteAdapter.ts.tpl +78 -11
- package/src/templates/features/Queue.ts.tpl +3 -2
- package/src/templates/project/basic/app/Controllers/AuthController.ts.tpl +217 -0
- package/src/templates/project/basic/app/Controllers/UserController.ts.tpl +1 -12
- package/src/templates/project/basic/app/Types/controller.ts.tpl +46 -0
- package/src/templates/project/basic/config/FileLogWriter.ts.tpl +5 -236
- package/src/templates/project/basic/config/SecretsManager.ts.tpl +10 -447
- package/src/templates/project/basic/config/StartupConfigValidator.ts.tpl +9 -268
- package/src/templates/project/basic/config/app.ts.tpl +13 -153
- package/src/templates/project/basic/config/broadcast.ts.tpl +29 -126
- package/src/templates/project/basic/config/cache.ts.tpl +12 -70
- package/src/templates/project/basic/config/cloudflare.ts.tpl +4 -39
- package/src/templates/project/basic/config/constants.ts.tpl +9 -65
- package/src/templates/project/basic/config/database.ts.tpl +66 -123
- package/src/templates/project/basic/config/env.ts.tpl +5 -169
- package/src/templates/project/basic/config/features.ts.tpl +6 -54
- package/src/templates/project/basic/config/index.ts.tpl +8 -23
- package/src/templates/project/basic/config/logging/HttpLogger.ts.tpl +7 -114
- package/src/templates/project/basic/config/mail.ts.tpl +9 -62
- package/src/templates/project/basic/config/microservices.ts.tpl +11 -97
- package/src/templates/project/basic/config/middleware.ts.tpl +25 -43
- package/src/templates/project/basic/config/notification.ts.tpl +13 -114
- package/src/templates/project/basic/config/queue.ts.tpl +9 -40
- package/src/templates/project/basic/config/security.ts.tpl +11 -163
- package/src/templates/project/basic/config/startup.ts.tpl +10 -21
- package/src/templates/project/basic/config/storage.ts.tpl +57 -137
- package/src/templates/project/basic/config/type.ts.tpl +32 -451
- package/src/templates/project/basic/database/factories/UserFactory.ts.tpl +80 -0
- package/src/templates/project/basic/database/migrations/create_tasks_table.ts.tpl +28 -0
- package/src/templates/project/basic/database/migrations/create_users_table.ts.tpl +29 -0
- package/src/templates/project/basic/database/seeders/DatabaseSeeder.ts.tpl +19 -0
- package/src/templates/project/basic/database/seeders/UserSeeder.ts.tpl +18 -0
- package/src/templates/project/basic/database/seeders/index.ts.tpl +2 -0
- package/src/templates/project/basic/routes/api.ts.tpl +71 -33
- package/src/templates/project/basic/routes/metrics.ts.tpl +22 -0
- package/src/templates/project/basic/src/index.ts.tpl +3 -0
- package/src/templates/project/basic/tsconfig.json.tpl +12 -11
- package/src/testing/TestEnvironment.d.ts +40 -0
- package/src/testing/TestEnvironment.d.ts.map +1 -0
- package/src/testing/TestEnvironment.js +141 -0
- package/src/testing/TestHttp.d.ts +29 -0
- package/src/testing/TestHttp.d.ts.map +1 -0
- package/src/testing/TestHttp.js +96 -0
- package/src/testing/index.d.ts +5 -0
- package/src/testing/index.d.ts.map +1 -0
- package/src/testing/index.js +2 -0
- package/src/time/DateTime.d.ts +181 -0
- package/src/time/DateTime.d.ts.map +1 -0
- package/src/time/DateTime.js +300 -0
- package/src/time/index.d.ts +7 -0
- package/src/time/index.d.ts.map +1 -0
- package/src/time/index.js +5 -0
- package/src/tools/http/Http.d.ts.map +1 -1
- package/src/tools/http/Http.js +4 -0
- package/src/tools/mail/drivers/Smtp.js +1 -1
- package/src/tools/queue/drivers/InMemory.d.ts +1 -1
- package/src/tools/queue/drivers/InMemory.d.ts.map +1 -1
- package/src/tools/queue/drivers/InMemory.js +1 -1
- package/src/tools/queue/drivers/Redis.d.ts +1 -1
- package/src/tools/queue/drivers/Redis.d.ts.map +1 -1
- package/src/tools/queue/drivers/Redis.js +1 -1
- package/src/validation/ValidationError.d.ts.map +1 -1
- package/src/validation/ValidationError.js +4 -2
- package/src/validation/Validator.d.ts +49 -16
- package/src/validation/Validator.d.ts.map +1 -1
- package/src/validation/Validator.js +307 -5
- package/src/common/uuid.d.ts +0 -3
- package/src/common/uuid.d.ts.map +0 -1
- package/src/common/uuid.js +0 -30
- package/src/templates/project/basic/.env.example.tpl +0 -74
- package/src/templates/project/basic/.env.tpl +0 -166
- package/src/templates/project/basic/config/logging/KvLogger.ts.tpl +0 -181
- package/src/templates/project/basic/config/logging/SlackLogger.ts.tpl +0 -156
- package/src/templates/project/basic/database/migrations/index.ts.tpl +0 -2
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GovernanceScaffolder
|
|
3
|
+
*
|
|
4
|
+
* Adds optional governance tooling to a generated app:
|
|
5
|
+
* - ESLint config + scripts
|
|
6
|
+
* - Architecture tests (Vitest)
|
|
7
|
+
*/
|
|
8
|
+
import { FileGenerator } from '../scaffolding/FileGenerator.js';
|
|
9
|
+
import { SpawnUtil } from '../utils/spawn.js';
|
|
10
|
+
import { resolvePackageManager } from '../../common/index.js';
|
|
11
|
+
import { Logger } from '../../config/logger.js';
|
|
12
|
+
import { ErrorFactory } from '../../exceptions/ZintrustError.js';
|
|
13
|
+
import * as path from '../../node-singletons/path.js';
|
|
14
|
+
const getStringRecord = (value) => {
|
|
15
|
+
if (typeof value !== 'object' || value === null)
|
|
16
|
+
return undefined;
|
|
17
|
+
const obj = value;
|
|
18
|
+
const out = {};
|
|
19
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
20
|
+
if (typeof v === 'string')
|
|
21
|
+
out[k] = v;
|
|
22
|
+
}
|
|
23
|
+
return out;
|
|
24
|
+
};
|
|
25
|
+
const readPackageJsonOrThrow = (projectRoot) => {
|
|
26
|
+
const pkgPath = path.join(projectRoot, 'package.json');
|
|
27
|
+
if (!FileGenerator.fileExists(pkgPath)) {
|
|
28
|
+
throw ErrorFactory.createValidationError(`package.json not found at ${pkgPath}`);
|
|
29
|
+
}
|
|
30
|
+
const raw = FileGenerator.readFile(pkgPath);
|
|
31
|
+
const parsed = JSON.parse(raw);
|
|
32
|
+
if (typeof parsed !== 'object' || parsed === null) {
|
|
33
|
+
throw ErrorFactory.createValidationError('package.json is not a JSON object');
|
|
34
|
+
}
|
|
35
|
+
return parsed;
|
|
36
|
+
};
|
|
37
|
+
const writePackageJson = (projectRoot, pkg) => {
|
|
38
|
+
const pkgPath = path.join(projectRoot, 'package.json');
|
|
39
|
+
const content = `${JSON.stringify(pkg, null, 2)}\n`;
|
|
40
|
+
FileGenerator.writeFile(pkgPath, content, { overwrite: true });
|
|
41
|
+
};
|
|
42
|
+
const upsertScript = (scripts, name, value) => {
|
|
43
|
+
if (typeof scripts[name] !== 'string' || scripts[name] === '') {
|
|
44
|
+
scripts[name] = value;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
const ensureDevDependency = (devDependencies, name, version) => {
|
|
48
|
+
if (typeof devDependencies[name] !== 'string' || devDependencies[name] === '') {
|
|
49
|
+
devDependencies[name] = version;
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
const inferGovernanceVersion = (pkg) => {
|
|
53
|
+
const deps = getStringRecord(pkg.dependencies);
|
|
54
|
+
const core = deps?.['@zintrust/core'];
|
|
55
|
+
if (typeof core === 'string' && core.trim() !== '')
|
|
56
|
+
return core;
|
|
57
|
+
return '^0.1.0';
|
|
58
|
+
};
|
|
59
|
+
const writeEslintConfig = (projectRoot) => {
|
|
60
|
+
const eslintConfigPath = path.join(projectRoot, 'eslint.config.mjs');
|
|
61
|
+
const content = `import { zintrustAppEslintConfig } from '@zintrust/governance';
|
|
62
|
+
|
|
63
|
+
export default zintrustAppEslintConfig({
|
|
64
|
+
tsconfigRootDir: import.meta.dirname,
|
|
65
|
+
});
|
|
66
|
+
`;
|
|
67
|
+
const wrote = FileGenerator.writeFile(eslintConfigPath, content, { overwrite: false });
|
|
68
|
+
return wrote ? [eslintConfigPath] : [];
|
|
69
|
+
};
|
|
70
|
+
const IMPORT_BOUNDARIES_ARCH_TEST_CONTENT = `import * as fs from 'node:fs/promises';
|
|
71
|
+
import * as path from 'node:path';
|
|
72
|
+
import * as ts from 'typescript';
|
|
73
|
+
import { describe, expect, it } from 'vitest';
|
|
74
|
+
|
|
75
|
+
type ImportHit = {
|
|
76
|
+
specifier: string;
|
|
77
|
+
line: string;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
type Violation = {
|
|
81
|
+
file: string;
|
|
82
|
+
reason: string;
|
|
83
|
+
hit: ImportHit;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const repoRoot = process.cwd();
|
|
87
|
+
|
|
88
|
+
const isTsFile = (filePath: string): boolean => {
|
|
89
|
+
if (!filePath.endsWith('.ts')) return false;
|
|
90
|
+
if (filePath.endsWith('.d.ts')) return false;
|
|
91
|
+
return true;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const walkTsFiles = async (dir: string): Promise<string[]> => {
|
|
95
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
96
|
+
|
|
97
|
+
const files = entries
|
|
98
|
+
.filter((ent) => ent.isFile())
|
|
99
|
+
.map((ent) => path.join(dir, ent.name))
|
|
100
|
+
.filter(isTsFile);
|
|
101
|
+
|
|
102
|
+
const subDirs = entries.filter((ent) => ent.isDirectory()).map((ent) => path.join(dir, ent.name));
|
|
103
|
+
const nested = await Promise.all(subDirs.map((subDir) => walkTsFiles(subDir)));
|
|
104
|
+
|
|
105
|
+
return [...files, ...nested.flat()];
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const extractImportSpecifiers = (source: string, filePath: string): ImportHit[] => {
|
|
109
|
+
const sourceFile = ts.createSourceFile(
|
|
110
|
+
filePath,
|
|
111
|
+
source,
|
|
112
|
+
ts.ScriptTarget.ESNext,
|
|
113
|
+
true,
|
|
114
|
+
ts.ScriptKind.TS
|
|
115
|
+
);
|
|
116
|
+
const lines = source.split(/\r?\n/);
|
|
117
|
+
const hits: ImportHit[] = [];
|
|
118
|
+
|
|
119
|
+
const addHit = (specifier: string, pos: number) => {
|
|
120
|
+
const { line } = sourceFile.getLineAndCharacterOfPosition(pos);
|
|
121
|
+
hits.push({
|
|
122
|
+
specifier,
|
|
123
|
+
line: (lines[line] ?? '').trim(),
|
|
124
|
+
});
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const visit = (node: ts.Node) => {
|
|
128
|
+
if (ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) {
|
|
129
|
+
const moduleSpecifier = node.moduleSpecifier;
|
|
130
|
+
if (moduleSpecifier && ts.isStringLiteral(moduleSpecifier)) {
|
|
131
|
+
addHit(moduleSpecifier.text, moduleSpecifier.getStart(sourceFile));
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (ts.isCallExpression(node) && node.expression.kind === ts.SyntaxKind.ImportKeyword) {
|
|
136
|
+
const arg = node.arguments.at(0);
|
|
137
|
+
if (arg && ts.isStringLiteral(arg)) {
|
|
138
|
+
addHit(arg.text, arg.getStart(sourceFile));
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
ts.forEachChild(node, visit);
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
visit(sourceFile);
|
|
146
|
+
return hits.filter((h) => h.specifier.trim() !== '');
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
const relFromRoot = (absPath: string): string => path.relative(repoRoot, absPath).replaceAll('\\\\', '/');
|
|
150
|
+
|
|
151
|
+
const startsWithAny = (value: string, prefixes: readonly string[]): boolean => {
|
|
152
|
+
for (const p of prefixes) {
|
|
153
|
+
if (value.startsWith(p)) return true;
|
|
154
|
+
}
|
|
155
|
+
return false;
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const findViolations = async (
|
|
159
|
+
files: string[],
|
|
160
|
+
disallowedPrefixes: readonly string[],
|
|
161
|
+
reasonPrefix: string
|
|
162
|
+
): Promise<Violation[]> => {
|
|
163
|
+
const perFile = await Promise.all(
|
|
164
|
+
files.map(async (file): Promise<Violation[]> => {
|
|
165
|
+
const contents = await fs.readFile(file, 'utf-8');
|
|
166
|
+
const imports = extractImportSpecifiers(contents, file);
|
|
167
|
+
|
|
168
|
+
return imports
|
|
169
|
+
.filter((hit) => startsWithAny(hit.specifier, disallowedPrefixes))
|
|
170
|
+
.map((hit) => ({
|
|
171
|
+
file: relFromRoot(file),
|
|
172
|
+
reason: reasonPrefix + ' ' + hit.specifier,
|
|
173
|
+
hit,
|
|
174
|
+
}));
|
|
175
|
+
})
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
return perFile.flat();
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
describe('Architecture: import boundaries', () => {
|
|
182
|
+
it('prevents src/ from importing app/routes', async () => {
|
|
183
|
+
const srcDir = path.join(repoRoot, 'src');
|
|
184
|
+
const files = await walkTsFiles(srcDir);
|
|
185
|
+
|
|
186
|
+
const violations = await findViolations(files, ['@app/', '@routes/'], 'src/ must not import');
|
|
187
|
+
|
|
188
|
+
expect(violations).toEqual([]);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it('prevents app/ from importing routes', async () => {
|
|
192
|
+
const appDir = path.join(repoRoot, 'app');
|
|
193
|
+
const files = await walkTsFiles(appDir);
|
|
194
|
+
|
|
195
|
+
const violations = await findViolations(files, ['@routes/'], 'app/ must not import');
|
|
196
|
+
|
|
197
|
+
expect(violations).toEqual([]);
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
`;
|
|
201
|
+
const ROUTE_MIDDLEWARE_REGISTRY_ARCH_TEST_CONTENT = `import { middlewareConfigObj } from '../../config/middleware';
|
|
202
|
+
import { registerRoutes } from '../../../routes/api';
|
|
203
|
+
import { RouteRegistry, Router } from '../../index.js';
|
|
204
|
+
import { beforeEach, describe, expect, it } from 'vitest';
|
|
205
|
+
|
|
206
|
+
describe('Architecture: route middleware registry', () => {
|
|
207
|
+
beforeEach(() => {
|
|
208
|
+
RouteRegistry.clear();
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it('ensures all route middleware names exist in middlewareConfigObj', () => {
|
|
212
|
+
const router = Router.createRouter();
|
|
213
|
+
registerRoutes(router);
|
|
214
|
+
|
|
215
|
+
const allowed = new Set(Object.keys(middlewareConfigObj));
|
|
216
|
+
const unknown: Array<{ method: string; path: string; middleware: string }> = [];
|
|
217
|
+
|
|
218
|
+
for (const route of RouteRegistry.list()) {
|
|
219
|
+
for (const name of route.middleware ?? []) {
|
|
220
|
+
if (!allowed.has(name)) {
|
|
221
|
+
unknown.push({ method: route.method, path: route.path, middleware: name });
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
expect(unknown).toEqual([]);
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
`;
|
|
230
|
+
const writeArchTests = (projectRoot) => {
|
|
231
|
+
const files = [
|
|
232
|
+
{
|
|
233
|
+
rel: 'tests/unit/architecture/ImportBoundaries.arch.test.ts',
|
|
234
|
+
content: IMPORT_BOUNDARIES_ARCH_TEST_CONTENT,
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
rel: 'tests/unit/architecture/RouteMiddlewareRegistry.arch.test.ts',
|
|
238
|
+
content: ROUTE_MIDDLEWARE_REGISTRY_ARCH_TEST_CONTENT,
|
|
239
|
+
},
|
|
240
|
+
];
|
|
241
|
+
const created = [];
|
|
242
|
+
for (const file of files) {
|
|
243
|
+
const abs = path.join(projectRoot, file.rel);
|
|
244
|
+
const wrote = FileGenerator.writeFile(abs, file.content, {
|
|
245
|
+
overwrite: false,
|
|
246
|
+
createDirs: true,
|
|
247
|
+
});
|
|
248
|
+
if (wrote)
|
|
249
|
+
created.push(abs);
|
|
250
|
+
}
|
|
251
|
+
return created;
|
|
252
|
+
};
|
|
253
|
+
const installGovernanceDeps = async (projectRoot, pm, packages) => {
|
|
254
|
+
if (packages.length === 0)
|
|
255
|
+
return;
|
|
256
|
+
let command = pm;
|
|
257
|
+
let args;
|
|
258
|
+
switch (pm) {
|
|
259
|
+
case 'pnpm':
|
|
260
|
+
args = ['add', '-D', ...packages];
|
|
261
|
+
break;
|
|
262
|
+
case 'yarn':
|
|
263
|
+
args = ['add', '--dev', ...packages];
|
|
264
|
+
break;
|
|
265
|
+
case 'npm':
|
|
266
|
+
default:
|
|
267
|
+
command = 'npm';
|
|
268
|
+
args = ['install', '--save-dev', ...packages];
|
|
269
|
+
break;
|
|
270
|
+
}
|
|
271
|
+
const exit = await SpawnUtil.spawnAndWait({ command, args, cwd: projectRoot });
|
|
272
|
+
if (exit !== 0) {
|
|
273
|
+
throw ErrorFactory.createCliError(`Failed to install governance dependencies (exit ${exit})`);
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
export const GovernanceScaffolder = Object.freeze({
|
|
277
|
+
async scaffold(projectRoot, options = {}) {
|
|
278
|
+
try {
|
|
279
|
+
const pkg = readPackageJsonOrThrow(projectRoot);
|
|
280
|
+
const scripts = typeof pkg.scripts === 'object' && pkg.scripts !== null ? pkg.scripts : {};
|
|
281
|
+
pkg.scripts = scripts;
|
|
282
|
+
upsertScript(scripts, 'lint', 'eslint .');
|
|
283
|
+
upsertScript(scripts, 'test:coverage', 'vitest run --coverage');
|
|
284
|
+
upsertScript(scripts, 'sonarqube', 'node -e "console.log(\'SonarQube not configured for this project; skipping.\');"');
|
|
285
|
+
const devDeps = typeof pkg.devDependencies === 'object' && pkg.devDependencies !== null
|
|
286
|
+
? pkg.devDependencies
|
|
287
|
+
: {};
|
|
288
|
+
pkg.devDependencies = devDeps;
|
|
289
|
+
const govVersion = inferGovernanceVersion(pkg);
|
|
290
|
+
ensureDevDependency(devDeps, 'eslint', '^9.0.0');
|
|
291
|
+
ensureDevDependency(devDeps, '@zintrust/governance', govVersion);
|
|
292
|
+
writePackageJson(projectRoot, pkg);
|
|
293
|
+
const filesCreated = [];
|
|
294
|
+
if (options.writeEslintConfig !== false) {
|
|
295
|
+
filesCreated.push(...writeEslintConfig(projectRoot));
|
|
296
|
+
}
|
|
297
|
+
if (options.writeArchTests !== false) {
|
|
298
|
+
filesCreated.push(...writeArchTests(projectRoot));
|
|
299
|
+
}
|
|
300
|
+
if (options.install === true) {
|
|
301
|
+
const pm = typeof options.packageManager === 'string' && options.packageManager.trim() !== ''
|
|
302
|
+
? options.packageManager.trim()
|
|
303
|
+
: resolvePackageManager();
|
|
304
|
+
Logger.info(`Installing governance devDependencies using ${pm}...`);
|
|
305
|
+
await installGovernanceDeps(projectRoot, pm, [
|
|
306
|
+
'eslint',
|
|
307
|
+
`@zintrust/governance@${govVersion}`,
|
|
308
|
+
]);
|
|
309
|
+
}
|
|
310
|
+
return {
|
|
311
|
+
success: true,
|
|
312
|
+
filesCreated,
|
|
313
|
+
message: 'Governance installed successfully',
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
catch (error) {
|
|
317
|
+
Logger.error('Governance scaffolding failed', error);
|
|
318
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
319
|
+
return {
|
|
320
|
+
success: false,
|
|
321
|
+
filesCreated: [],
|
|
322
|
+
message: msg,
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
},
|
|
326
|
+
});
|
|
327
|
+
export default GovernanceScaffolder;
|
|
@@ -7,6 +7,16 @@ export interface MigrationOptions {
|
|
|
7
7
|
name: string;
|
|
8
8
|
migrationsPath: string;
|
|
9
9
|
type?: MigrationType;
|
|
10
|
+
/**
|
|
11
|
+
* Optional explicit table name to use in generated content.
|
|
12
|
+
* When provided, the generator will use it instead of inferring from the migration name.
|
|
13
|
+
*/
|
|
14
|
+
table?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Optional column name hint for alter migrations.
|
|
17
|
+
* When provided, the generator will use it in the example placeholder.
|
|
18
|
+
*/
|
|
19
|
+
column?: string;
|
|
10
20
|
}
|
|
11
21
|
export interface MigrationScaffoldResult {
|
|
12
22
|
success: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MigrationGenerator.d.ts","sourceRoot":"","sources":["../../../../src/cli/scaffolding/MigrationGenerator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,CAAC;AAExD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,CAAC,EAAE,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"MigrationGenerator.d.ts","sourceRoot":"","sources":["../../../../src/cli/scaffolding/MigrationGenerator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,CAAC;AAExD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAmB/F;AAED;;GAEG;AAEH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAwE7F;AAoRD;;GAEG;AACH,eAAO,MAAM,kBAAkB;;;EAG7B,CAAC"}
|
|
@@ -41,6 +41,15 @@ export function generateMigration(options) {
|
|
|
41
41
|
message: `Validation failed: ${validation.errors.join(', ')}`,
|
|
42
42
|
});
|
|
43
43
|
}
|
|
44
|
+
const existing = findExistingMigrationFile(options.migrationsPath, options.name);
|
|
45
|
+
if (existing !== undefined) {
|
|
46
|
+
return Promise.resolve({
|
|
47
|
+
success: false,
|
|
48
|
+
migrationName: options.name,
|
|
49
|
+
filePath: existing,
|
|
50
|
+
message: `Migration '${options.name}' already exists: ${path.basename(existing)}`,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
44
53
|
// Generate migration filename with timestamp
|
|
45
54
|
const timestamp = new Date()
|
|
46
55
|
.toISOString()
|
|
@@ -48,7 +57,7 @@ export function generateMigration(options) {
|
|
|
48
57
|
.slice(0, 14);
|
|
49
58
|
const filename = `${timestamp}_${options.name}.ts`;
|
|
50
59
|
const filePath = path.join(options.migrationsPath, filename);
|
|
51
|
-
//
|
|
60
|
+
// Defensive: exact timestamped file already exists.
|
|
52
61
|
if (FileGenerator.fileExists(filePath)) {
|
|
53
62
|
return Promise.resolve({
|
|
54
63
|
success: false,
|
|
@@ -59,7 +68,13 @@ export function generateMigration(options) {
|
|
|
59
68
|
}
|
|
60
69
|
// Generate migration content based on type
|
|
61
70
|
const type = options.type ?? detectType(options.name);
|
|
62
|
-
const content = generateMigrationContent(
|
|
71
|
+
const content = generateMigrationContent({
|
|
72
|
+
name: options.name,
|
|
73
|
+
type,
|
|
74
|
+
migrationsPath: options.migrationsPath,
|
|
75
|
+
table: options.table,
|
|
76
|
+
column: options.column,
|
|
77
|
+
});
|
|
63
78
|
// Write migration file
|
|
64
79
|
FileGenerator.writeFile(filePath, content);
|
|
65
80
|
Logger.info(`✅ Created migration: ${filename}`);
|
|
@@ -81,6 +96,16 @@ export function generateMigration(options) {
|
|
|
81
96
|
});
|
|
82
97
|
}
|
|
83
98
|
}
|
|
99
|
+
function findExistingMigrationFile(migrationsPath, name) {
|
|
100
|
+
const suffix = `_${name}.ts`;
|
|
101
|
+
const files = FileGenerator.listFiles(migrationsPath, false);
|
|
102
|
+
for (const file of files) {
|
|
103
|
+
const base = path.basename(file);
|
|
104
|
+
if (base.endsWith(suffix))
|
|
105
|
+
return file;
|
|
106
|
+
}
|
|
107
|
+
return undefined;
|
|
108
|
+
}
|
|
84
109
|
/**
|
|
85
110
|
* Detect migration type from name
|
|
86
111
|
*/
|
|
@@ -94,53 +119,85 @@ function detectType(name) {
|
|
|
94
119
|
/**
|
|
95
120
|
* Generate migration file content
|
|
96
121
|
*/
|
|
97
|
-
function generateMigrationContent(
|
|
98
|
-
const className = CommonUtils.toPascalCase(name);
|
|
99
|
-
|
|
100
|
-
|
|
122
|
+
function generateMigrationContent(options) {
|
|
123
|
+
const className = CommonUtils.toPascalCase(options.name);
|
|
124
|
+
const importBlock = resolveMigrationImportBlock(options.migrationsPath);
|
|
125
|
+
if (options.type === 'create') {
|
|
126
|
+
return generateCreateMigration({
|
|
127
|
+
className,
|
|
128
|
+
importBlock,
|
|
129
|
+
tableName: options.table ?? getTableNameFromMigrationName(options.name),
|
|
130
|
+
});
|
|
101
131
|
}
|
|
102
|
-
else if (type === 'drop') {
|
|
103
|
-
return generateDropMigration(
|
|
132
|
+
else if (options.type === 'drop') {
|
|
133
|
+
return generateDropMigration({
|
|
134
|
+
className,
|
|
135
|
+
importBlock,
|
|
136
|
+
tableName: options.table ?? getTableNameFromMigrationName(options.name),
|
|
137
|
+
});
|
|
104
138
|
}
|
|
105
139
|
else {
|
|
106
|
-
return generateAlterMigration(
|
|
140
|
+
return generateAlterMigration({
|
|
141
|
+
className,
|
|
142
|
+
importBlock,
|
|
143
|
+
tableName: options.table ?? getTableNameFromMigrationName(options.name),
|
|
144
|
+
exampleColumn: options.column,
|
|
145
|
+
});
|
|
107
146
|
}
|
|
108
147
|
}
|
|
148
|
+
function resolveMigrationImportBlock(migrationsPath) {
|
|
149
|
+
const projectRoot = path.resolve(migrationsPath, '..', '..');
|
|
150
|
+
const packageJsonPath = path.join(projectRoot, 'package.json');
|
|
151
|
+
if (FileGenerator.fileExists(packageJsonPath)) {
|
|
152
|
+
try {
|
|
153
|
+
const pkgRaw = FileGenerator.readFile(packageJsonPath);
|
|
154
|
+
const pkg = JSON.parse(pkgRaw);
|
|
155
|
+
if (pkg.name === '@zintrust/core') {
|
|
156
|
+
return `import type { IDatabase } from '../../orm/Database';\nimport { Schema as MigrationSchema, type Blueprint } from '../../migrations/schema';`;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
// fall through
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return `import { MigrationSchema, type Blueprint, type IDatabase } from '../../index.js';`;
|
|
164
|
+
}
|
|
109
165
|
/**
|
|
110
166
|
* Generate CREATE migration
|
|
111
167
|
*/
|
|
112
|
-
function generateCreateMigration(
|
|
113
|
-
const tableName =
|
|
168
|
+
function generateCreateMigration(options) {
|
|
169
|
+
const { className, importBlock, tableName } = options;
|
|
114
170
|
return `/**
|
|
115
171
|
* Migration: ${className}
|
|
116
172
|
* Creates ${tableName} table
|
|
117
173
|
*/
|
|
118
174
|
|
|
175
|
+
${importBlock}
|
|
176
|
+
|
|
119
177
|
export interface Migration {
|
|
120
|
-
up(): Promise<void>;
|
|
121
|
-
down(): Promise<void>;
|
|
178
|
+
up(db: IDatabase): Promise<void>;
|
|
179
|
+
down(db: IDatabase): Promise<void>;
|
|
122
180
|
}
|
|
123
181
|
|
|
124
182
|
export const migration: Migration = {
|
|
125
183
|
/**
|
|
126
184
|
* Run migration
|
|
127
185
|
*/
|
|
128
|
-
async up(): Promise<void> {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
// });
|
|
186
|
+
async up(db: IDatabase): Promise<void> {
|
|
187
|
+
const schema = MigrationSchema.create(db);
|
|
188
|
+
|
|
189
|
+
await schema.create('${tableName}', (table: Blueprint) => {
|
|
190
|
+
table.id();
|
|
191
|
+
table.timestamps();
|
|
192
|
+
});
|
|
136
193
|
},
|
|
137
194
|
|
|
138
195
|
/**
|
|
139
196
|
* Rollback migration
|
|
140
197
|
*/
|
|
141
|
-
async down(): Promise<void> {
|
|
142
|
-
|
|
143
|
-
|
|
198
|
+
async down(db: IDatabase): Promise<void> {
|
|
199
|
+
const schema = MigrationSchema.create(db);
|
|
200
|
+
await schema.dropIfExists('${tableName}');
|
|
144
201
|
},
|
|
145
202
|
};
|
|
146
203
|
`;
|
|
@@ -148,37 +205,48 @@ export const migration: Migration = {
|
|
|
148
205
|
/**
|
|
149
206
|
* Generate ALTER migration
|
|
150
207
|
*/
|
|
151
|
-
function generateAlterMigration(
|
|
152
|
-
const tableName =
|
|
208
|
+
function generateAlterMigration(options) {
|
|
209
|
+
const { className, importBlock, tableName, exampleColumn } = options;
|
|
210
|
+
const hasConcreteColumn = typeof exampleColumn === 'string' && exampleColumn.trim().length > 0;
|
|
211
|
+
const example = hasConcreteColumn ? exampleColumn : 'new_column';
|
|
212
|
+
const tableBody = hasConcreteColumn
|
|
213
|
+
? ` table.string('${example}');
|
|
214
|
+
// Example:
|
|
215
|
+
// table.dropColumn('old_column');
|
|
216
|
+
// table.index('new_column');`
|
|
217
|
+
: ` // Example:
|
|
218
|
+
// table.string('${example}');
|
|
219
|
+
// table.dropColumn('old_column');
|
|
220
|
+
// table.index('new_column');`;
|
|
153
221
|
return `/**
|
|
154
222
|
* Migration: ${className}
|
|
155
223
|
* Modifies ${tableName} table
|
|
156
224
|
*/
|
|
157
225
|
|
|
226
|
+
${importBlock}
|
|
227
|
+
|
|
158
228
|
export interface Migration {
|
|
159
|
-
up(): Promise<void>;
|
|
160
|
-
down(): Promise<void>;
|
|
229
|
+
up(db: IDatabase): Promise<void>;
|
|
230
|
+
down(db: IDatabase): Promise<void>;
|
|
161
231
|
}
|
|
162
232
|
|
|
163
233
|
export const migration: Migration = {
|
|
164
234
|
/**
|
|
165
235
|
* Run migration
|
|
166
236
|
*/
|
|
167
|
-
async up(): Promise<void> {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
237
|
+
async up(db: IDatabase): Promise<void> {
|
|
238
|
+
const schema = MigrationSchema.create(db);
|
|
239
|
+
|
|
240
|
+
await schema.table('${tableName}', (table: Blueprint) => {
|
|
241
|
+
${tableBody}
|
|
242
|
+
});
|
|
172
243
|
},
|
|
173
244
|
|
|
174
245
|
/**
|
|
175
246
|
* Rollback migration
|
|
176
247
|
*/
|
|
177
|
-
async down(): Promise<void> {
|
|
178
|
-
//
|
|
179
|
-
// await db.schema.alterTable('${tableName}', (table) => {
|
|
180
|
-
// table.dropColumn('new_column');
|
|
181
|
-
// });
|
|
248
|
+
async down(_db: IDatabase): Promise<void> {
|
|
249
|
+
// Note: dropping columns/FKs varies by driver; SQLite/D1 requires a table rebuild.
|
|
182
250
|
},
|
|
183
251
|
};
|
|
184
252
|
`;
|
|
@@ -186,36 +254,34 @@ export const migration: Migration = {
|
|
|
186
254
|
/**
|
|
187
255
|
* Generate DROP migration
|
|
188
256
|
*/
|
|
189
|
-
function generateDropMigration(
|
|
190
|
-
const tableName =
|
|
257
|
+
function generateDropMigration(options) {
|
|
258
|
+
const { className, importBlock, tableName } = options;
|
|
191
259
|
return `/**
|
|
192
260
|
* Migration: ${className}
|
|
193
261
|
* Drops ${tableName} table
|
|
194
262
|
*/
|
|
195
263
|
|
|
264
|
+
${importBlock}
|
|
265
|
+
|
|
196
266
|
export interface Migration {
|
|
197
|
-
up(): Promise<void>;
|
|
198
|
-
down(): Promise<void>;
|
|
267
|
+
up(db: IDatabase): Promise<void>;
|
|
268
|
+
down(db: IDatabase): Promise<void>;
|
|
199
269
|
}
|
|
200
270
|
|
|
201
271
|
export const migration: Migration = {
|
|
202
272
|
/**
|
|
203
273
|
* Run migration
|
|
204
274
|
*/
|
|
205
|
-
async up(): Promise<void> {
|
|
206
|
-
|
|
207
|
-
|
|
275
|
+
async up(db: IDatabase): Promise<void> {
|
|
276
|
+
const schema = MigrationSchema.create(db);
|
|
277
|
+
await schema.dropIfExists('${tableName}');
|
|
208
278
|
},
|
|
209
279
|
|
|
210
280
|
/**
|
|
211
281
|
* Rollback migration
|
|
212
282
|
*/
|
|
213
|
-
async down(): Promise<void> {
|
|
214
|
-
// Recreate table
|
|
215
|
-
// await db.schema.createTable('${tableName}', (table) => {
|
|
216
|
-
// table.increments('id').primary();
|
|
217
|
-
// table.timestamps();
|
|
218
|
-
// });
|
|
283
|
+
async down(_db: IDatabase): Promise<void> {
|
|
284
|
+
// Recreate table (DB-specific). Consider adding back columns explicitly.
|
|
219
285
|
},
|
|
220
286
|
};
|
|
221
287
|
`;
|
|
@@ -249,6 +315,26 @@ function getTableNameFromClass(className) {
|
|
|
249
315
|
}
|
|
250
316
|
return tableName;
|
|
251
317
|
}
|
|
318
|
+
function getTableNameFromMigrationName(name) {
|
|
319
|
+
if (name.endsWith('_table')) {
|
|
320
|
+
const ensurePlural = (t) => (t.endsWith('s') ? t : `${t}s`);
|
|
321
|
+
const withoutSuffix = name.slice(0, -'_table'.length);
|
|
322
|
+
if (withoutSuffix.startsWith('create_'))
|
|
323
|
+
return ensurePlural(withoutSuffix.slice('create_'.length));
|
|
324
|
+
if (withoutSuffix.startsWith('drop_'))
|
|
325
|
+
return ensurePlural(withoutSuffix.slice('drop_'.length));
|
|
326
|
+
if (withoutSuffix.includes('_to_'))
|
|
327
|
+
return ensurePlural(withoutSuffix.split('_to_').at(-1) ?? withoutSuffix);
|
|
328
|
+
if (withoutSuffix.startsWith('add_')) {
|
|
329
|
+
// Convention: add_<column>_<table>_table (table is the last segment)
|
|
330
|
+
const parts = withoutSuffix.split('_');
|
|
331
|
+
const last = parts.at(-1);
|
|
332
|
+
if (typeof last === 'string' && last.length > 0)
|
|
333
|
+
return ensurePlural(last);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
return getTableNameFromClass(CommonUtils.toPascalCase(name));
|
|
337
|
+
}
|
|
252
338
|
/**
|
|
253
339
|
* MigrationGenerator creates database migration files
|
|
254
340
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProjectScaffolder.d.ts","sourceRoot":"","sources":["../../../../src/cli/scaffolding/ProjectScaffolder.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
1
|
+
{"version":3,"file":"ProjectScaffolder.d.ts","sourceRoot":"","sources":["../../../../src/cli/scaffolding/ProjectScaffolder.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAUH,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,cAAc,GAAG,sBAAsB,CAAC;AAEpD,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED,MAAM,WAAW,kBAAkB;IACjC,cAAc,CAAC,OAAO,EAAE,sBAAsB,GAAG,IAAI,CAAC;IACtD,YAAY,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACxC,eAAe,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAAC;IACpE,cAAc,IAAI,MAAM,CAAC;IACzB,sBAAsB,IAAI,OAAO,CAAC;IAClC,iBAAiB,IAAI,MAAM,CAAC;IAC5B,WAAW,CAAC,OAAO,CAAC,EAAE,sBAAsB,GAAG,MAAM,CAAC;IACtD,gBAAgB,IAAI,OAAO,CAAC;IAC5B,aAAa,IAAI,OAAO,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAAC;CAC3E;AA6eD,wBAAgB,qBAAqB,IAAI,MAAM,EAAE,CAEhD;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAsBrE;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG;IAChE,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,CAsBA;AA4ID;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,WAAW,GAAE,MAAsB,GAAG,kBAAkB,CAsB/F;AAED,wBAAsB,eAAe,CACnC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,qBAAqB,CAAC,CAEhC;AAED;;GAEG;AACH,eAAO,MAAM,iBAAiB;;;;;;EAM5B,CAAC"}
|