alepha 0.13.5 → 0.13.7

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.
Files changed (136) hide show
  1. package/dist/api-audits/index.browser.js +116 -0
  2. package/dist/api-audits/index.browser.js.map +1 -0
  3. package/dist/api-audits/index.d.ts +1194 -0
  4. package/dist/api-audits/index.js +674 -0
  5. package/dist/api-audits/index.js.map +1 -0
  6. package/dist/api-notifications/index.d.ts +147 -147
  7. package/dist/api-parameters/index.browser.js +36 -5
  8. package/dist/api-parameters/index.browser.js.map +1 -1
  9. package/dist/api-parameters/index.d.ts +711 -33
  10. package/dist/api-parameters/index.js +831 -17
  11. package/dist/api-parameters/index.js.map +1 -1
  12. package/dist/api-users/index.d.ts +16 -3
  13. package/dist/api-users/index.js +699 -19
  14. package/dist/api-users/index.js.map +1 -1
  15. package/dist/api-verifications/index.js +2 -1
  16. package/dist/api-verifications/index.js.map +1 -1
  17. package/dist/bin/index.js +1 -0
  18. package/dist/bin/index.js.map +1 -1
  19. package/dist/cli/index.d.ts +85 -31
  20. package/dist/cli/index.js +205 -33
  21. package/dist/cli/index.js.map +1 -1
  22. package/dist/command/index.d.ts +67 -6
  23. package/dist/command/index.js +30 -3
  24. package/dist/command/index.js.map +1 -1
  25. package/dist/core/index.browser.js +241 -61
  26. package/dist/core/index.browser.js.map +1 -1
  27. package/dist/core/index.d.ts +170 -90
  28. package/dist/core/index.js +264 -67
  29. package/dist/core/index.js.map +1 -1
  30. package/dist/core/index.native.js +248 -65
  31. package/dist/core/index.native.js.map +1 -1
  32. package/dist/email/index.js +15 -10554
  33. package/dist/email/index.js.map +1 -1
  34. package/dist/logger/index.d.ts +4 -4
  35. package/dist/logger/index.js +77 -72
  36. package/dist/logger/index.js.map +1 -1
  37. package/dist/orm/index.d.ts +5 -1
  38. package/dist/orm/index.js +24 -7
  39. package/dist/orm/index.js.map +1 -1
  40. package/dist/queue/index.d.ts +4 -4
  41. package/dist/redis/index.d.ts +10 -10
  42. package/dist/security/index.d.ts +28 -28
  43. package/dist/server/index.d.ts +10 -1
  44. package/dist/server/index.js +20 -6
  45. package/dist/server/index.js.map +1 -1
  46. package/dist/server-auth/index.d.ts +163 -152
  47. package/dist/server-auth/index.js +40 -10
  48. package/dist/server-auth/index.js.map +1 -1
  49. package/dist/server-cookies/index.js +5 -1
  50. package/dist/server-cookies/index.js.map +1 -1
  51. package/dist/server-links/index.d.ts +33 -33
  52. package/dist/server-security/index.d.ts +9 -9
  53. package/dist/thread/index.js +2 -2
  54. package/dist/thread/index.js.map +1 -1
  55. package/dist/vite/index.d.ts +2 -2
  56. package/dist/vite/index.js +102 -45
  57. package/dist/vite/index.js.map +1 -1
  58. package/dist/websocket/index.browser.js +3 -3
  59. package/dist/websocket/index.browser.js.map +1 -1
  60. package/dist/websocket/index.d.ts +7 -7
  61. package/dist/websocket/index.js +4 -4
  62. package/dist/websocket/index.js.map +1 -1
  63. package/package.json +14 -9
  64. package/src/api-audits/controllers/AuditController.ts +186 -0
  65. package/src/api-audits/entities/audits.ts +132 -0
  66. package/src/api-audits/index.browser.ts +18 -0
  67. package/src/api-audits/index.ts +58 -0
  68. package/src/api-audits/primitives/$audit.ts +159 -0
  69. package/src/api-audits/schemas/auditQuerySchema.ts +23 -0
  70. package/src/api-audits/schemas/auditResourceSchema.ts +9 -0
  71. package/src/api-audits/schemas/createAuditSchema.ts +27 -0
  72. package/src/api-audits/services/AuditService.ts +412 -0
  73. package/src/api-parameters/controllers/ConfigController.ts +324 -0
  74. package/src/api-parameters/entities/parameters.ts +93 -10
  75. package/src/api-parameters/index.ts +43 -4
  76. package/src/api-parameters/primitives/$config.ts +291 -19
  77. package/src/api-parameters/schedulers/ConfigActivationScheduler.ts +30 -0
  78. package/src/api-parameters/services/ConfigStore.ts +491 -0
  79. package/src/api-users/atoms/realmAuthSettingsAtom.ts +19 -0
  80. package/src/api-users/controllers/UserRealmController.ts +0 -2
  81. package/src/api-users/index.ts +2 -0
  82. package/src/api-users/primitives/$userRealm.ts +18 -3
  83. package/src/api-users/providers/UserRealmProvider.ts +6 -3
  84. package/src/api-users/services/RegistrationService.ts +2 -1
  85. package/src/api-users/services/SessionService.ts +4 -0
  86. package/src/api-users/services/UserService.ts +3 -0
  87. package/src/api-verifications/index.ts +7 -1
  88. package/src/bin/index.ts +1 -0
  89. package/src/cli/assets/biomeJson.ts +1 -1
  90. package/src/cli/assets/dummySpecTs.ts +7 -0
  91. package/src/cli/assets/editorconfig.ts +13 -0
  92. package/src/cli/assets/mainTs.ts +14 -0
  93. package/src/cli/commands/BiomeCommands.ts +2 -0
  94. package/src/cli/commands/CoreCommands.ts +28 -9
  95. package/src/cli/commands/VerifyCommands.ts +2 -1
  96. package/src/cli/commands/ViteCommands.ts +8 -9
  97. package/src/cli/services/AlephaCliUtils.ts +214 -23
  98. package/src/command/helpers/Asker.ts +0 -1
  99. package/src/command/primitives/$command.ts +67 -0
  100. package/src/command/providers/CliProvider.ts +39 -8
  101. package/src/core/Alepha.ts +40 -30
  102. package/src/core/helpers/jsonSchemaToTypeBox.ts +307 -0
  103. package/src/core/index.shared.ts +1 -0
  104. package/src/core/index.ts +30 -3
  105. package/src/core/providers/EventManager.ts +1 -1
  106. package/src/core/providers/StateManager.ts +23 -12
  107. package/src/core/providers/TypeProvider.ts +26 -34
  108. package/src/logger/index.ts +8 -6
  109. package/src/logger/primitives/$logger.ts +1 -1
  110. package/src/logger/providers/{SimpleFormatterProvider.ts → PrettyFormatterProvider.ts} +10 -1
  111. package/src/orm/index.ts +6 -0
  112. package/src/orm/services/PgRelationManager.ts +2 -2
  113. package/src/orm/services/PostgresModelBuilder.ts +11 -7
  114. package/src/orm/services/Repository.ts +16 -7
  115. package/src/orm/services/SqliteModelBuilder.ts +10 -0
  116. package/src/server/index.ts +6 -0
  117. package/src/server/primitives/$action.ts +10 -1
  118. package/src/server/providers/ServerBodyParserProvider.ts +11 -5
  119. package/src/server/providers/ServerRouterProvider.ts +13 -7
  120. package/src/server-auth/primitives/$auth.ts +7 -0
  121. package/src/server-auth/providers/ServerAuthProvider.ts +51 -8
  122. package/src/server-cookies/index.ts +2 -1
  123. package/src/thread/primitives/$thread.ts +2 -2
  124. package/src/vite/index.ts +0 -2
  125. package/src/vite/tasks/buildServer.ts +3 -4
  126. package/src/vite/tasks/generateCloudflare.ts +35 -19
  127. package/src/vite/tasks/generateDocker.ts +18 -4
  128. package/src/vite/tasks/generateSitemap.ts +5 -7
  129. package/src/vite/tasks/generateVercel.ts +76 -41
  130. package/src/vite/tasks/runAlepha.ts +16 -1
  131. package/src/websocket/providers/NodeWebSocketServerProvider.ts +3 -11
  132. package/src/websocket/services/WebSocketClient.ts +3 -3
  133. package/dist/cli/dist-BlfFtOk2.js +0 -2770
  134. package/dist/cli/dist-BlfFtOk2.js.map +0 -1
  135. package/src/api-parameters/controllers/ParameterController.ts +0 -45
  136. package/src/api-parameters/services/ParameterStore.ts +0 -23
@@ -393,7 +393,8 @@ const AlephaApiVerification = $module({
393
393
  services: [
394
394
  VerificationController,
395
395
  VerificationJobs,
396
- VerificationService
396
+ VerificationService,
397
+ VerificationParameters
397
398
  ]
398
399
  });
399
400
 
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/api-verifications/schemas/requestVerificationCodeResponseSchema.ts","../../src/api-verifications/schemas/validateVerificationCodeResponseSchema.ts","../../src/api-verifications/schemas/verificationTypeEnumSchema.ts","../../src/api-verifications/entities/verifications.ts","../../src/api-verifications/schemas/verificationSettingsSchema.ts","../../src/api-verifications/parameters/VerificationParameters.ts","../../src/api-verifications/services/VerificationService.ts","../../src/api-verifications/controllers/VerificationController.ts","../../src/api-verifications/jobs/VerificationJobs.ts","../../src/api-verifications/index.ts"],"sourcesContent":["import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\nexport const requestVerificationCodeResponseSchema = t.object({\n token: t.string({\n description:\n \"The verification token (6-digit code for phone, UUID for email). The caller should send this to the user via their preferred notification method.\",\n }),\n codeExpiration: t.integer({\n description: \"Time in seconds before your verification token expires.\",\n }),\n verificationCooldown: t.integer({\n description:\n \"Cooldown period in seconds before you can request another verification.\",\n }),\n maxVerificationAttempts: t.integer({\n description:\n \"Maximum number of verification attempts allowed before the token is locked.\",\n }),\n});\n\nexport type RequestVerificationResponse = Static<\n typeof requestVerificationCodeResponseSchema\n>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\nexport const validateVerificationCodeResponseSchema = t.object({\n ok: t.boolean({\n description: \"Indicates whether the verification was successful.\",\n }),\n alreadyVerified: t.optional(\n t.boolean({\n description: \"Indicates whether the target was already verified.\",\n }),\n ),\n});\n\nexport type ValidateVerificationCodeResponse = Static<\n typeof validateVerificationCodeResponseSchema\n>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\nexport const verificationTypeEnumSchema = t.enum([\"code\", \"link\"]);\nexport type VerificationTypeEnum = Static<typeof verificationTypeEnumSchema>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\nimport { $entity, pg } from \"alepha/orm\";\nimport { verificationTypeEnumSchema } from \"../schemas/verificationTypeEnumSchema.ts\";\n\nexport const verifications = $entity({\n name: \"verification\",\n schema: t.object({\n id: pg.primaryKey(t.bigint()),\n\n createdAt: pg.createdAt(),\n\n updatedAt: pg.updatedAt(),\n\n version: pg.version(),\n\n type: verificationTypeEnumSchema,\n\n target: t.text({\n description: \"Can be a phone (E.164 format) or email address\",\n }),\n\n code: t.text({\n description: \"Hashed verification token (n-digit code or UUID)\",\n }),\n\n verifiedAt: t.optional(\n t.datetime({\n description: \"When it was successfully verified\",\n }),\n ),\n\n attempts: pg.default(\n t.integer({\n description: \"Number of failed attempts (to prevent brute-force)\",\n }),\n 0,\n ),\n }),\n indexes: [\n \"createdAt\",\n {\n columns: [\"target\", \"code\"],\n },\n ],\n});\n\nexport const verificationEntitySchema = verifications.schema;\nexport const verificationEntityInsertSchema = verifications.insertSchema;\nexport type VerificationEntity = Static<typeof verifications.schema>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\nexport const verificationSettingsSchema = t.object({\n code: t.object(\n {\n maxAttempts: t.integer({\n description:\n \"Maximum number of attempts before locking the verification.\",\n minimum: 1,\n maximum: 10,\n }),\n codeLength: t.integer({\n description: \"Length of the verification code.\",\n minimum: 4,\n maximum: 12,\n }),\n codeExpiration: t.integer({\n description: \"Time in seconds before the verification code expires.\",\n minimum: 60, // 1 minute\n maximum: 3600, // 1 hour\n }),\n verificationCooldown: t.integer({\n description: \"Cooldown period in seconds after a request verification.\",\n minimum: 0,\n maximum: 3600, // 1 hour\n }),\n limitPerDay: t.integer({\n description:\n \"Maximum number of verification requests per day for one entry.\",\n minimum: 1,\n maximum: 100,\n }),\n },\n {\n description: \"Settings specific to code verifications.\",\n },\n ),\n link: t.object(\n {\n maxAttempts: t.integer({\n description:\n \"Maximum number of attempts before locking the verification.\",\n minimum: 1,\n maximum: 10,\n }),\n codeExpiration: t.integer({\n description: \"Time in seconds before the verification token expires.\",\n minimum: 60, // 1 minute\n maximum: 7200, // 2 hours\n }),\n verificationCooldown: t.integer({\n description: \"Cooldown period in seconds after a request verification.\",\n minimum: 0,\n maximum: 3600, // 1 hour\n }),\n limitPerDay: t.integer({\n description:\n \"Maximum number of verification requests per day for one entry.\",\n minimum: 1,\n maximum: 100,\n }),\n },\n {\n description: \"Settings specific to link verifications.\",\n },\n ),\n purgeDays: t.integer({\n description:\n \"Number of days after which expired verifications are automatically deleted. Set to 0 to disable auto-deletion.\",\n minimum: 0,\n maximum: 365,\n }),\n});\n\nexport type VerificationSettings = Static<typeof verificationSettingsSchema>;\n","import { $atom, $use, type Static } from \"alepha\";\nimport {\n type VerificationSettings,\n verificationSettingsSchema,\n} from \"../schemas/verificationSettingsSchema.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Verification settings configuration atom\n */\nexport const verificationOptions = $atom({\n name: \"alepha.api.verifications.options\",\n schema: verificationSettingsSchema,\n default: {\n code: {\n maxAttempts: 5,\n codeLength: 6,\n codeExpiration: 300, // 5 minutes\n verificationCooldown: 90,\n limitPerDay: 10,\n },\n link: {\n maxAttempts: 3, // Lower since UUIDs are harder to guess\n codeExpiration: 1800, // 30 minutes\n verificationCooldown: 90,\n limitPerDay: 10,\n },\n purgeDays: 1,\n },\n});\n\nexport type VerificationOptions = Static<typeof verificationOptions.schema>;\n\ndeclare module \"alepha\" {\n interface State {\n [verificationOptions.key]: VerificationOptions;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class VerificationParameters {\n protected readonly options = $use(verificationOptions);\n\n public get<K extends keyof VerificationSettings>(\n key: K,\n ): VerificationSettings[K] {\n return this.options[key];\n }\n}\n","import { createHash, randomInt, randomUUID } from \"node:crypto\";\nimport { $inject } from \"alepha\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { $repository } from \"alepha/orm\";\nimport { BadRequestError, NotFoundError } from \"alepha/server\";\nimport {\n type VerificationEntity,\n verifications,\n} from \"../entities/verifications.ts\";\nimport { VerificationParameters } from \"../parameters/VerificationParameters.ts\";\nimport type { RequestVerificationResponse } from \"../schemas/requestVerificationCodeResponseSchema.ts\";\nimport type { ValidateVerificationCodeResponse } from \"../schemas/validateVerificationCodeResponseSchema.ts\";\nimport type { VerificationTypeEnum } from \"../schemas/verificationTypeEnumSchema.ts\";\n\nexport class VerificationService {\n protected readonly log = $logger();\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly verificationParameters = $inject(VerificationParameters);\n protected readonly verificationRepository = $repository(verifications);\n\n public async findByEntry(\n entry: VerificationEntry,\n ): Promise<VerificationEntity> {\n this.log.trace(\"Finding verification by entry\", {\n type: entry.type,\n target: entry.target,\n });\n\n const results = await this.verificationRepository.findMany({\n limit: 1, // only need the most recent entry\n orderBy: {\n column: \"createdAt\",\n direction: \"desc\",\n },\n where: {\n type: { eq: entry.type },\n target: { eq: entry.target },\n },\n });\n\n if (results.length === 0) {\n this.log.debug(\"Verification entry not found\", {\n type: entry.type,\n target: entry.target,\n });\n throw new NotFoundError(\"Verification entry not found\");\n }\n\n this.log.debug(\"Verification entry found\", {\n id: results[0].id,\n type: entry.type,\n target: entry.target,\n });\n\n return results[0];\n }\n\n public findRecentsByEntry(entry: VerificationEntry) {\n this.log.trace(\"Finding recent verifications by entry\", {\n type: entry.type,\n target: entry.target,\n });\n\n return this.verificationRepository.findMany({\n orderBy: {\n column: \"createdAt\",\n direction: \"desc\",\n },\n where: {\n type: { eq: entry.type },\n target: { eq: entry.target },\n createdAt: {\n gte: this.dateTimeProvider.now().startOf(\"day\").toISOString(),\n },\n },\n });\n }\n\n /**\n * Creates a verification entry and returns the token.\n * The caller is responsible for sending notifications with the token.\n * This allows for context-specific notifications (e.g., password reset vs email verification).\n */\n public async createVerification(\n entry: VerificationEntry,\n ): Promise<RequestVerificationResponse> {\n this.log.trace(\"Creating verification\", {\n type: entry.type,\n target: entry.target,\n });\n\n const settings = this.verificationParameters.get(entry.type);\n\n const recents = await this.findRecentsByEntry(entry);\n if (recents.length >= settings.limitPerDay) {\n this.log.warn(\"Daily verification limit reached\", {\n type: entry.type,\n target: entry.target,\n limit: settings.limitPerDay,\n count: recents.length,\n });\n throw new BadRequestError(\n `Maximum number of verification requests per day reached (${settings.limitPerDay})`,\n );\n }\n\n const existingVerification = recents[0];\n if (existingVerification) {\n const nowSec = this.dateTimeProvider.now().unix();\n const createdAtSec = this.dateTimeProvider\n .of(existingVerification.createdAt)\n .unix();\n\n const diffSec = nowSec - createdAtSec;\n if (diffSec < settings.verificationCooldown) {\n const remainingCooldown = Math.floor(\n settings.verificationCooldown - diffSec,\n );\n this.log.debug(\"Verification on cooldown\", {\n type: entry.type,\n target: entry.target,\n remainingSeconds: remainingCooldown,\n });\n throw new BadRequestError(\n `Verification is on cooldown for ${remainingCooldown} seconds`,\n );\n }\n }\n\n const token = this.generateToken(entry.type);\n\n const verification = await this.verificationRepository.create({\n type: entry.type,\n target: entry.target,\n code: this.hashCode(token),\n });\n\n this.log.info(\"Verification created\", {\n id: verification.id,\n type: entry.type,\n target: entry.target,\n expiresInSeconds: settings.codeExpiration,\n });\n\n return {\n token,\n codeExpiration: settings.codeExpiration,\n verificationCooldown: settings.verificationCooldown,\n maxVerificationAttempts: settings.maxAttempts,\n };\n }\n\n public async verifyCode(\n entry: VerificationEntry,\n code: string,\n ): Promise<ValidateVerificationCodeResponse> {\n this.log.trace(\"Verifying code\", {\n type: entry.type,\n target: entry.target,\n });\n\n const settings = this.verificationParameters.get(entry.type);\n\n const verification = await this.findByEntry(entry);\n if (verification.verifiedAt) {\n this.log.debug(\"Verification already verified\", {\n id: verification.id,\n type: entry.type,\n target: entry.target,\n verifiedAt: verification.verifiedAt,\n });\n return { ok: true, alreadyVerified: true };\n }\n\n // DO NOT DELETE THE VERIFICATION WHEN IT IS REJECTED,\n // or we won't be able to cooldown the verification\n\n const now = this.dateTimeProvider.now();\n const expirationDate = this.dateTimeProvider\n .of(verification.createdAt)\n .add(settings.codeExpiration, \"seconds\");\n\n if (now > expirationDate) {\n this.log.warn(\"Verification code expired\", {\n id: verification.id,\n type: entry.type,\n target: entry.target,\n createdAt: verification.createdAt,\n expiredAt: expirationDate.toISOString(),\n });\n throw new BadRequestError(\"Verification code has expired\");\n }\n\n if (verification.attempts >= settings.maxAttempts) {\n this.log.warn(\"Verification locked due to max attempts\", {\n id: verification.id,\n type: entry.type,\n target: entry.target,\n attempts: verification.attempts,\n maxAttempts: settings.maxAttempts,\n });\n throw new BadRequestError(\n \"Maximum number of attempts reached - verification is locked\",\n );\n }\n\n if (verification.code !== this.hashCode(code)) {\n const newAttempts = verification.attempts + 1;\n this.log.warn(\"Invalid verification code\", {\n id: verification.id,\n type: entry.type,\n target: entry.target,\n attempts: newAttempts,\n maxAttempts: settings.maxAttempts,\n });\n await this.verificationRepository.updateById(verification.id, {\n attempts: newAttempts,\n });\n throw new BadRequestError(\"Invalid verification code\");\n }\n\n await this.verificationRepository.updateById(verification.id, {\n verifiedAt: this.dateTimeProvider.nowISOString(),\n });\n\n this.log.info(\"Verification code verified\", {\n id: verification.id,\n type: entry.type,\n target: entry.target,\n });\n\n return { ok: true };\n }\n\n public hashCode(code: string): string {\n return createHash(\"sha256\").update(code).digest(\"hex\");\n }\n\n public generateToken(type: VerificationTypeEnum): string {\n if (type === \"code\") {\n const settings = this.verificationParameters.get(\"code\");\n return randomInt(0, 1_000_000)\n .toString()\n .padStart(settings.codeLength, \"0\");\n } else if (type === \"link\") {\n return randomUUID();\n }\n\n throw new BadRequestError(`Invalid verification type: ${type}`);\n }\n}\n\nexport interface VerificationEntry {\n type: VerificationTypeEnum;\n target: string;\n}\n","import { $inject, t } from \"alepha\";\nimport { $action } from \"alepha/server\";\nimport { requestVerificationCodeResponseSchema } from \"../schemas/requestVerificationCodeResponseSchema.ts\";\nimport { validateVerificationCodeResponseSchema } from \"../schemas/validateVerificationCodeResponseSchema.ts\";\nimport { verificationTypeEnumSchema } from \"../schemas/verificationTypeEnumSchema.ts\";\nimport { VerificationService } from \"../services/VerificationService.ts\";\n\nexport class VerificationController {\n protected readonly verificationService = $inject(VerificationService);\n\n public readonly url = \"/verifications\";\n public readonly group = \"verifications\";\n\n public readonly requestVerificationCode = $action({\n path: `${this.url}/:type`,\n group: this.group,\n method: \"POST\",\n schema: {\n params: t.object({\n type: verificationTypeEnumSchema,\n }),\n body: t.object({\n target: t.text(),\n }),\n response: requestVerificationCodeResponseSchema,\n },\n handler: async ({ body, params }) => {\n return await this.verificationService.createVerification({\n type: params.type,\n target: body.target,\n });\n },\n });\n\n public readonly validateVerificationCode = $action({\n path: `${this.url}/:type/validate`,\n group: this.group,\n method: \"POST\",\n schema: {\n params: t.object({\n type: verificationTypeEnumSchema,\n }),\n body: t.object({\n target: t.text(),\n token: t.text({\n description:\n \"The verification token (6-digit code for phone, UUID for email).\",\n }),\n }),\n response: validateVerificationCodeResponseSchema,\n },\n handler: async ({ body, params }) => {\n return this.verificationService.verifyCode(\n {\n type: params.type,\n target: body.target,\n },\n body.token,\n );\n },\n });\n}\n","import { $inject } from \"alepha\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { $repository } from \"alepha/orm\";\nimport { $scheduler } from \"alepha/scheduler\";\nimport { verifications } from \"../entities/verifications.ts\";\nimport { VerificationParameters } from \"../parameters/VerificationParameters.ts\";\n\nexport class VerificationJobs {\n protected readonly verificationRepository = $repository(verifications);\n protected readonly verificationParameters = $inject(VerificationParameters);\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n\n public readonly cleanExpired = $scheduler({\n cron: \"0 0 * * *\", // Every day at midnight\n description: \"Clean expired verifications\",\n handler: async () => {\n const purgeDays = this.verificationParameters.get(\"purgeDays\");\n if (purgeDays <= 0) {\n return; // Auto deletion is disabled\n }\n\n const dayMs = 24 * 60 * 60 * 1000;\n const purgeThreshold = Date.now() - purgeDays * dayMs;\n\n await this.verificationRepository.deleteMany({\n createdAt: {\n lt: this.dateTimeProvider.of(purgeThreshold).toISOString(),\n },\n });\n },\n });\n}\n","import { $module } from \"alepha\";\nimport { VerificationController } from \"./controllers/VerificationController.ts\";\nimport { VerificationJobs } from \"./jobs/VerificationJobs.ts\";\nimport { VerificationService } from \"./services/VerificationService.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./controllers/VerificationController.ts\";\nexport * from \"./entities/verifications.ts\";\nexport * from \"./schemas/requestVerificationCodeResponseSchema.ts\";\nexport * from \"./schemas/validateVerificationCodeResponseSchema.ts\";\nexport * from \"./schemas/verificationTypeEnumSchema.ts\";\nexport * from \"./services/VerificationService.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Provides email/phone verification management API endpoints for Alepha applications.\n *\n * This module includes verification code generation, validation,\n * and related functionalities. Notifications are handled by the consuming module\n * (e.g., api-users) for context-specific messaging.\n *\n * @module alepha.api.verifications\n */\nexport const AlephaApiVerification = $module({\n name: \"alepha.api.verifications\",\n services: [VerificationController, VerificationJobs, VerificationService],\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/api-verifications/schemas/requestVerificationCodeResponseSchema.ts","../../src/api-verifications/schemas/validateVerificationCodeResponseSchema.ts","../../src/api-verifications/schemas/verificationTypeEnumSchema.ts","../../src/api-verifications/entities/verifications.ts","../../src/api-verifications/schemas/verificationSettingsSchema.ts","../../src/api-verifications/parameters/VerificationParameters.ts","../../src/api-verifications/services/VerificationService.ts","../../src/api-verifications/controllers/VerificationController.ts","../../src/api-verifications/jobs/VerificationJobs.ts","../../src/api-verifications/index.ts"],"sourcesContent":["import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\nexport const requestVerificationCodeResponseSchema = t.object({\n token: t.string({\n description:\n \"The verification token (6-digit code for phone, UUID for email). The caller should send this to the user via their preferred notification method.\",\n }),\n codeExpiration: t.integer({\n description: \"Time in seconds before your verification token expires.\",\n }),\n verificationCooldown: t.integer({\n description:\n \"Cooldown period in seconds before you can request another verification.\",\n }),\n maxVerificationAttempts: t.integer({\n description:\n \"Maximum number of verification attempts allowed before the token is locked.\",\n }),\n});\n\nexport type RequestVerificationResponse = Static<\n typeof requestVerificationCodeResponseSchema\n>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\nexport const validateVerificationCodeResponseSchema = t.object({\n ok: t.boolean({\n description: \"Indicates whether the verification was successful.\",\n }),\n alreadyVerified: t.optional(\n t.boolean({\n description: \"Indicates whether the target was already verified.\",\n }),\n ),\n});\n\nexport type ValidateVerificationCodeResponse = Static<\n typeof validateVerificationCodeResponseSchema\n>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\nexport const verificationTypeEnumSchema = t.enum([\"code\", \"link\"]);\nexport type VerificationTypeEnum = Static<typeof verificationTypeEnumSchema>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\nimport { $entity, pg } from \"alepha/orm\";\nimport { verificationTypeEnumSchema } from \"../schemas/verificationTypeEnumSchema.ts\";\n\nexport const verifications = $entity({\n name: \"verification\",\n schema: t.object({\n id: pg.primaryKey(t.bigint()),\n\n createdAt: pg.createdAt(),\n\n updatedAt: pg.updatedAt(),\n\n version: pg.version(),\n\n type: verificationTypeEnumSchema,\n\n target: t.text({\n description: \"Can be a phone (E.164 format) or email address\",\n }),\n\n code: t.text({\n description: \"Hashed verification token (n-digit code or UUID)\",\n }),\n\n verifiedAt: t.optional(\n t.datetime({\n description: \"When it was successfully verified\",\n }),\n ),\n\n attempts: pg.default(\n t.integer({\n description: \"Number of failed attempts (to prevent brute-force)\",\n }),\n 0,\n ),\n }),\n indexes: [\n \"createdAt\",\n {\n columns: [\"target\", \"code\"],\n },\n ],\n});\n\nexport const verificationEntitySchema = verifications.schema;\nexport const verificationEntityInsertSchema = verifications.insertSchema;\nexport type VerificationEntity = Static<typeof verifications.schema>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\n\nexport const verificationSettingsSchema = t.object({\n code: t.object(\n {\n maxAttempts: t.integer({\n description:\n \"Maximum number of attempts before locking the verification.\",\n minimum: 1,\n maximum: 10,\n }),\n codeLength: t.integer({\n description: \"Length of the verification code.\",\n minimum: 4,\n maximum: 12,\n }),\n codeExpiration: t.integer({\n description: \"Time in seconds before the verification code expires.\",\n minimum: 60, // 1 minute\n maximum: 3600, // 1 hour\n }),\n verificationCooldown: t.integer({\n description: \"Cooldown period in seconds after a request verification.\",\n minimum: 0,\n maximum: 3600, // 1 hour\n }),\n limitPerDay: t.integer({\n description:\n \"Maximum number of verification requests per day for one entry.\",\n minimum: 1,\n maximum: 100,\n }),\n },\n {\n description: \"Settings specific to code verifications.\",\n },\n ),\n link: t.object(\n {\n maxAttempts: t.integer({\n description:\n \"Maximum number of attempts before locking the verification.\",\n minimum: 1,\n maximum: 10,\n }),\n codeExpiration: t.integer({\n description: \"Time in seconds before the verification token expires.\",\n minimum: 60, // 1 minute\n maximum: 7200, // 2 hours\n }),\n verificationCooldown: t.integer({\n description: \"Cooldown period in seconds after a request verification.\",\n minimum: 0,\n maximum: 3600, // 1 hour\n }),\n limitPerDay: t.integer({\n description:\n \"Maximum number of verification requests per day for one entry.\",\n minimum: 1,\n maximum: 100,\n }),\n },\n {\n description: \"Settings specific to link verifications.\",\n },\n ),\n purgeDays: t.integer({\n description:\n \"Number of days after which expired verifications are automatically deleted. Set to 0 to disable auto-deletion.\",\n minimum: 0,\n maximum: 365,\n }),\n});\n\nexport type VerificationSettings = Static<typeof verificationSettingsSchema>;\n","import { $atom, $use, type Static } from \"alepha\";\nimport {\n type VerificationSettings,\n verificationSettingsSchema,\n} from \"../schemas/verificationSettingsSchema.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Verification settings configuration atom\n */\nexport const verificationOptions = $atom({\n name: \"alepha.api.verifications.options\",\n schema: verificationSettingsSchema,\n default: {\n code: {\n maxAttempts: 5,\n codeLength: 6,\n codeExpiration: 300, // 5 minutes\n verificationCooldown: 90,\n limitPerDay: 10,\n },\n link: {\n maxAttempts: 3, // Lower since UUIDs are harder to guess\n codeExpiration: 1800, // 30 minutes\n verificationCooldown: 90,\n limitPerDay: 10,\n },\n purgeDays: 1,\n },\n});\n\nexport type VerificationOptions = Static<typeof verificationOptions.schema>;\n\ndeclare module \"alepha\" {\n interface State {\n [verificationOptions.key]: VerificationOptions;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class VerificationParameters {\n protected readonly options = $use(verificationOptions);\n\n public get<K extends keyof VerificationSettings>(\n key: K,\n ): VerificationSettings[K] {\n return this.options[key];\n }\n}\n","import { createHash, randomInt, randomUUID } from \"node:crypto\";\nimport { $inject } from \"alepha\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { $repository } from \"alepha/orm\";\nimport { BadRequestError, NotFoundError } from \"alepha/server\";\nimport {\n type VerificationEntity,\n verifications,\n} from \"../entities/verifications.ts\";\nimport { VerificationParameters } from \"../parameters/VerificationParameters.ts\";\nimport type { RequestVerificationResponse } from \"../schemas/requestVerificationCodeResponseSchema.ts\";\nimport type { ValidateVerificationCodeResponse } from \"../schemas/validateVerificationCodeResponseSchema.ts\";\nimport type { VerificationTypeEnum } from \"../schemas/verificationTypeEnumSchema.ts\";\n\nexport class VerificationService {\n protected readonly log = $logger();\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly verificationParameters = $inject(VerificationParameters);\n protected readonly verificationRepository = $repository(verifications);\n\n public async findByEntry(\n entry: VerificationEntry,\n ): Promise<VerificationEntity> {\n this.log.trace(\"Finding verification by entry\", {\n type: entry.type,\n target: entry.target,\n });\n\n const results = await this.verificationRepository.findMany({\n limit: 1, // only need the most recent entry\n orderBy: {\n column: \"createdAt\",\n direction: \"desc\",\n },\n where: {\n type: { eq: entry.type },\n target: { eq: entry.target },\n },\n });\n\n if (results.length === 0) {\n this.log.debug(\"Verification entry not found\", {\n type: entry.type,\n target: entry.target,\n });\n throw new NotFoundError(\"Verification entry not found\");\n }\n\n this.log.debug(\"Verification entry found\", {\n id: results[0].id,\n type: entry.type,\n target: entry.target,\n });\n\n return results[0];\n }\n\n public findRecentsByEntry(entry: VerificationEntry) {\n this.log.trace(\"Finding recent verifications by entry\", {\n type: entry.type,\n target: entry.target,\n });\n\n return this.verificationRepository.findMany({\n orderBy: {\n column: \"createdAt\",\n direction: \"desc\",\n },\n where: {\n type: { eq: entry.type },\n target: { eq: entry.target },\n createdAt: {\n gte: this.dateTimeProvider.now().startOf(\"day\").toISOString(),\n },\n },\n });\n }\n\n /**\n * Creates a verification entry and returns the token.\n * The caller is responsible for sending notifications with the token.\n * This allows for context-specific notifications (e.g., password reset vs email verification).\n */\n public async createVerification(\n entry: VerificationEntry,\n ): Promise<RequestVerificationResponse> {\n this.log.trace(\"Creating verification\", {\n type: entry.type,\n target: entry.target,\n });\n\n const settings = this.verificationParameters.get(entry.type);\n\n const recents = await this.findRecentsByEntry(entry);\n if (recents.length >= settings.limitPerDay) {\n this.log.warn(\"Daily verification limit reached\", {\n type: entry.type,\n target: entry.target,\n limit: settings.limitPerDay,\n count: recents.length,\n });\n throw new BadRequestError(\n `Maximum number of verification requests per day reached (${settings.limitPerDay})`,\n );\n }\n\n const existingVerification = recents[0];\n if (existingVerification) {\n const nowSec = this.dateTimeProvider.now().unix();\n const createdAtSec = this.dateTimeProvider\n .of(existingVerification.createdAt)\n .unix();\n\n const diffSec = nowSec - createdAtSec;\n if (diffSec < settings.verificationCooldown) {\n const remainingCooldown = Math.floor(\n settings.verificationCooldown - diffSec,\n );\n this.log.debug(\"Verification on cooldown\", {\n type: entry.type,\n target: entry.target,\n remainingSeconds: remainingCooldown,\n });\n throw new BadRequestError(\n `Verification is on cooldown for ${remainingCooldown} seconds`,\n );\n }\n }\n\n const token = this.generateToken(entry.type);\n\n const verification = await this.verificationRepository.create({\n type: entry.type,\n target: entry.target,\n code: this.hashCode(token),\n });\n\n this.log.info(\"Verification created\", {\n id: verification.id,\n type: entry.type,\n target: entry.target,\n expiresInSeconds: settings.codeExpiration,\n });\n\n return {\n token,\n codeExpiration: settings.codeExpiration,\n verificationCooldown: settings.verificationCooldown,\n maxVerificationAttempts: settings.maxAttempts,\n };\n }\n\n public async verifyCode(\n entry: VerificationEntry,\n code: string,\n ): Promise<ValidateVerificationCodeResponse> {\n this.log.trace(\"Verifying code\", {\n type: entry.type,\n target: entry.target,\n });\n\n const settings = this.verificationParameters.get(entry.type);\n\n const verification = await this.findByEntry(entry);\n if (verification.verifiedAt) {\n this.log.debug(\"Verification already verified\", {\n id: verification.id,\n type: entry.type,\n target: entry.target,\n verifiedAt: verification.verifiedAt,\n });\n return { ok: true, alreadyVerified: true };\n }\n\n // DO NOT DELETE THE VERIFICATION WHEN IT IS REJECTED,\n // or we won't be able to cooldown the verification\n\n const now = this.dateTimeProvider.now();\n const expirationDate = this.dateTimeProvider\n .of(verification.createdAt)\n .add(settings.codeExpiration, \"seconds\");\n\n if (now > expirationDate) {\n this.log.warn(\"Verification code expired\", {\n id: verification.id,\n type: entry.type,\n target: entry.target,\n createdAt: verification.createdAt,\n expiredAt: expirationDate.toISOString(),\n });\n throw new BadRequestError(\"Verification code has expired\");\n }\n\n if (verification.attempts >= settings.maxAttempts) {\n this.log.warn(\"Verification locked due to max attempts\", {\n id: verification.id,\n type: entry.type,\n target: entry.target,\n attempts: verification.attempts,\n maxAttempts: settings.maxAttempts,\n });\n throw new BadRequestError(\n \"Maximum number of attempts reached - verification is locked\",\n );\n }\n\n if (verification.code !== this.hashCode(code)) {\n const newAttempts = verification.attempts + 1;\n this.log.warn(\"Invalid verification code\", {\n id: verification.id,\n type: entry.type,\n target: entry.target,\n attempts: newAttempts,\n maxAttempts: settings.maxAttempts,\n });\n await this.verificationRepository.updateById(verification.id, {\n attempts: newAttempts,\n });\n throw new BadRequestError(\"Invalid verification code\");\n }\n\n await this.verificationRepository.updateById(verification.id, {\n verifiedAt: this.dateTimeProvider.nowISOString(),\n });\n\n this.log.info(\"Verification code verified\", {\n id: verification.id,\n type: entry.type,\n target: entry.target,\n });\n\n return { ok: true };\n }\n\n public hashCode(code: string): string {\n return createHash(\"sha256\").update(code).digest(\"hex\");\n }\n\n public generateToken(type: VerificationTypeEnum): string {\n if (type === \"code\") {\n const settings = this.verificationParameters.get(\"code\");\n return randomInt(0, 1_000_000)\n .toString()\n .padStart(settings.codeLength, \"0\");\n } else if (type === \"link\") {\n return randomUUID();\n }\n\n throw new BadRequestError(`Invalid verification type: ${type}`);\n }\n}\n\nexport interface VerificationEntry {\n type: VerificationTypeEnum;\n target: string;\n}\n","import { $inject, t } from \"alepha\";\nimport { $action } from \"alepha/server\";\nimport { requestVerificationCodeResponseSchema } from \"../schemas/requestVerificationCodeResponseSchema.ts\";\nimport { validateVerificationCodeResponseSchema } from \"../schemas/validateVerificationCodeResponseSchema.ts\";\nimport { verificationTypeEnumSchema } from \"../schemas/verificationTypeEnumSchema.ts\";\nimport { VerificationService } from \"../services/VerificationService.ts\";\n\nexport class VerificationController {\n protected readonly verificationService = $inject(VerificationService);\n\n public readonly url = \"/verifications\";\n public readonly group = \"verifications\";\n\n public readonly requestVerificationCode = $action({\n path: `${this.url}/:type`,\n group: this.group,\n method: \"POST\",\n schema: {\n params: t.object({\n type: verificationTypeEnumSchema,\n }),\n body: t.object({\n target: t.text(),\n }),\n response: requestVerificationCodeResponseSchema,\n },\n handler: async ({ body, params }) => {\n return await this.verificationService.createVerification({\n type: params.type,\n target: body.target,\n });\n },\n });\n\n public readonly validateVerificationCode = $action({\n path: `${this.url}/:type/validate`,\n group: this.group,\n method: \"POST\",\n schema: {\n params: t.object({\n type: verificationTypeEnumSchema,\n }),\n body: t.object({\n target: t.text(),\n token: t.text({\n description:\n \"The verification token (6-digit code for phone, UUID for email).\",\n }),\n }),\n response: validateVerificationCodeResponseSchema,\n },\n handler: async ({ body, params }) => {\n return this.verificationService.verifyCode(\n {\n type: params.type,\n target: body.target,\n },\n body.token,\n );\n },\n });\n}\n","import { $inject } from \"alepha\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { $repository } from \"alepha/orm\";\nimport { $scheduler } from \"alepha/scheduler\";\nimport { verifications } from \"../entities/verifications.ts\";\nimport { VerificationParameters } from \"../parameters/VerificationParameters.ts\";\n\nexport class VerificationJobs {\n protected readonly verificationRepository = $repository(verifications);\n protected readonly verificationParameters = $inject(VerificationParameters);\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n\n public readonly cleanExpired = $scheduler({\n cron: \"0 0 * * *\", // Every day at midnight\n description: \"Clean expired verifications\",\n handler: async () => {\n const purgeDays = this.verificationParameters.get(\"purgeDays\");\n if (purgeDays <= 0) {\n return; // Auto deletion is disabled\n }\n\n const dayMs = 24 * 60 * 60 * 1000;\n const purgeThreshold = Date.now() - purgeDays * dayMs;\n\n await this.verificationRepository.deleteMany({\n createdAt: {\n lt: this.dateTimeProvider.of(purgeThreshold).toISOString(),\n },\n });\n },\n });\n}\n","import { $module } from \"alepha\";\nimport { VerificationController } from \"./controllers/VerificationController.ts\";\nimport { VerificationJobs } from \"./jobs/VerificationJobs.ts\";\nimport { VerificationParameters } from \"./parameters/VerificationParameters.ts\";\nimport { VerificationService } from \"./services/VerificationService.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./controllers/VerificationController.ts\";\nexport * from \"./entities/verifications.ts\";\nexport * from \"./schemas/requestVerificationCodeResponseSchema.ts\";\nexport * from \"./schemas/validateVerificationCodeResponseSchema.ts\";\nexport * from \"./schemas/verificationTypeEnumSchema.ts\";\nexport * from \"./services/VerificationService.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Provides email/phone verification management API endpoints for Alepha applications.\n *\n * This module includes verification code generation, validation,\n * and related functionalities. Notifications are handled by the consuming module\n * (e.g., api-users) for context-specific messaging.\n *\n * @module alepha.api.verifications\n */\nexport const AlephaApiVerification = $module({\n name: \"alepha.api.verifications\",\n services: [\n VerificationController,\n VerificationJobs,\n VerificationService,\n VerificationParameters,\n ],\n});\n"],"mappings":"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"}
package/dist/bin/index.js CHANGED
@@ -5,6 +5,7 @@ import { AlephaCli, version } from "alepha/cli";
5
5
 
6
6
  //#region ../../src/bin/index.ts
7
7
  const alepha = Alepha.create({ env: {
8
+ APP_NAME: "CLI",
8
9
  LOG_LEVEL: "alepha.core:warn,info",
9
10
  LOG_FORMAT: "raw",
10
11
  CLI_NAME: "alepha",
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/bin/index.ts"],"sourcesContent":["#!/usr/bin/env node\nimport \"tsx\";\nimport { Alepha, run } from \"alepha\";\nimport { AlephaCli, version } from \"alepha/cli\";\n\nconst alepha = Alepha.create({\n env: {\n LOG_LEVEL: \"alepha.core:warn,info\",\n LOG_FORMAT: \"raw\",\n CLI_NAME: \"alepha\",\n CLI_DESCRIPTION: `Alepha CLI v${version} - Create and manage Alepha projects.`,\n },\n});\n\nalepha.with(AlephaCli);\n\nrun(alepha);\n"],"mappings":";;;;;;AAKA,MAAM,SAAS,OAAO,OAAO,EAC3B,KAAK;CACH,WAAW;CACX,YAAY;CACZ,UAAU;CACV,iBAAiB,eAAe,QAAQ;CACzC,EACF,CAAC;AAEF,OAAO,KAAK,UAAU;AAEtB,IAAI,OAAO"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/bin/index.ts"],"sourcesContent":["#!/usr/bin/env node\nimport \"tsx\";\nimport { Alepha, run } from \"alepha\";\nimport { AlephaCli, version } from \"alepha/cli\";\n\nconst alepha = Alepha.create({\n env: {\n APP_NAME: \"CLI\",\n LOG_LEVEL: \"alepha.core:warn,info\",\n LOG_FORMAT: \"raw\",\n CLI_NAME: \"alepha\",\n CLI_DESCRIPTION: `Alepha CLI v${version} - Create and manage Alepha projects.`,\n },\n});\n\nalepha.with(AlephaCli);\n\nrun(alepha);\n"],"mappings":";;;;;;AAKA,MAAM,SAAS,OAAO,OAAO,EAC3B,KAAK;CACH,UAAU;CACV,WAAW;CACX,YAAY;CACZ,UAAU;CACV,iBAAiB,eAAe,QAAQ;CACzC,EACF,CAAC;AAEF,OAAO,KAAK,UAAU;AAEtB,IAAI,OAAO"}
@@ -1,9 +1,9 @@
1
1
  import * as alepha0 from "alepha";
2
2
  import { Alepha } from "alepha";
3
3
  import { FileSystemProvider } from "alepha/file";
4
- import * as alepha_command0 from "alepha/command";
5
- import { CliProvider } from "alepha/command";
6
- import * as alepha_logger3 from "alepha/logger";
4
+ import * as alepha_command1 from "alepha/command";
5
+ import { CliProvider, RunnerMethod } from "alepha/command";
6
+ import * as alepha_logger2 from "alepha/logger";
7
7
 
8
8
  //#region ../../src/cli/apps/AlephaCli.d.ts
9
9
  declare const AlephaCli: alepha0.Service<alepha0.Module>;
@@ -20,7 +20,7 @@ declare class AlephaPackageBuilderCli {
20
20
  src: string;
21
21
  dist: string;
22
22
  fs: FileSystemProvider;
23
- make: alepha_command0.CommandPrimitive<alepha0.TObject<alepha0.TProperties>, alepha0.TSchema>;
23
+ make: alepha_command1.CommandPrimitive<alepha0.TObject<alepha0.TProperties>, alepha0.TSchema>;
24
24
  }
25
25
  declare function analyzeModules(srcDir: string, packageName: string): Promise<Module[]>;
26
26
  //#endregion
@@ -36,22 +36,21 @@ declare function analyzeModules(srcDir: string, packageName: string): Promise<Mo
36
36
  * - Alepha instance loading
37
37
  */
38
38
  declare class AlephaCliUtils {
39
- protected readonly log: alepha_logger3.Logger;
39
+ protected readonly log: alepha_logger2.Logger;
40
40
  protected readonly fs: FileSystemProvider;
41
41
  /**
42
42
  * Execute a command using npx with inherited stdio.
43
43
  *
44
- * @param command - The command to execute (will be passed to npx)
45
- * @param env - Optional environment variables to set for the command
46
- * @returns Promise that resolves when the process exits
47
- *
48
44
  * @example
49
45
  * ```ts
50
46
  * const runner = alepha.inject(ProcessRunner);
51
47
  * await runner.exec("tsx watch src/index.ts");
52
48
  * ```
53
49
  */
54
- exec(command: string, env?: Record<string, string>): Promise<void>;
50
+ exec(command: string, options?: {
51
+ env?: Record<string, string>;
52
+ global?: boolean;
53
+ }): Promise<void>;
55
54
  /**
56
55
  * Write a configuration file to node_modules/.alepha directory.
57
56
  *
@@ -77,6 +76,8 @@ declare class AlephaCliUtils {
77
76
  * @param root - The root directory of the project
78
77
  */
79
78
  ensureYarn(root: string): Promise<void>;
79
+ ensurePnpm(root: string): Promise<void>;
80
+ ensureNpm(root: string): Promise<void>;
80
81
  /**
81
82
  * Generate package.json content with Alepha dependencies.
82
83
  *
@@ -107,6 +108,7 @@ declare class AlephaCliUtils {
107
108
  viteConfigTs?: boolean;
108
109
  indexHtml?: boolean;
109
110
  biomeJson?: boolean;
111
+ editorconfig?: boolean;
110
112
  }): Promise<void>;
111
113
  /**
112
114
  * Ensure tsconfig.json exists in the project.
@@ -130,6 +132,14 @@ declare class AlephaCliUtils {
130
132
  * Looks for an existing biome.json in the project root, or creates one if it doesn't exist.
131
133
  */
132
134
  ensureBiomeConfig(root: string): Promise<void>;
135
+ /**
136
+ * Ensure .editorconfig exists in the project.
137
+ *
138
+ * Creates a standard .editorconfig if none exists.
139
+ *
140
+ * @param root - The root directory of the project
141
+ */
142
+ ensureEditorConfig(root: string): Promise<void>;
133
143
  /**
134
144
  * Load Alepha instance from a server entry file.
135
145
  *
@@ -192,10 +202,51 @@ declare class AlephaCliUtils {
192
202
  provider?: string;
193
203
  logMessage: (providerName: string, dialect: string) => string;
194
204
  }): Promise<void>;
195
- getPackageManager(root: string): Promise<"yarn" | "pnpm" | "npm">;
205
+ getPackageManager(root: string, flags?: {
206
+ yarn?: boolean;
207
+ pnpm?: boolean;
208
+ npm?: boolean;
209
+ bun?: boolean;
210
+ }): Promise<"yarn" | "pnpm" | "npm" | "bun">;
196
211
  ensureIndexHtml(root: string): Promise<void>;
197
212
  exists(root: string, dirName: string): Promise<boolean>;
213
+ /**
214
+ * Ensure src/main.ts exists with a minimal Alepha bootstrap.
215
+ *
216
+ * Creates the src directory and main.ts file if the src directory
217
+ * doesn't exist or is empty.
218
+ *
219
+ * @param root - The root directory of the project
220
+ */
221
+ ensureSrcMain(root: string): Promise<void>;
222
+ /**
223
+ * Ensure test directory exists with a dummy test file.
224
+ *
225
+ * Creates the test directory and a dummy.spec.ts file if the test directory
226
+ * doesn't exist or is empty.
227
+ *
228
+ * @param root - The root directory of the project
229
+ */
230
+ ensureTestDir(root: string): Promise<void>;
198
231
  readPackageJson(root: string): Promise<Record<string, any>>;
232
+ /**
233
+ * Check if a dependency is installed in the project.
234
+ *
235
+ * @param root - The root directory of the project
236
+ * @param packageName - The name of the package to check
237
+ * @returns True if the package is in dependencies or devDependencies
238
+ */
239
+ hasDependency(root: string, packageName: string): Promise<boolean>;
240
+ /**
241
+ * Install a dependency if it's missing from the project.
242
+ *
243
+ * Automatically detects the package manager (yarn, pnpm, npm) and installs
244
+ * the package as a dev dependency if not already present.
245
+ */
246
+ ensureDependency(root: string, packageName: string, options?: {
247
+ dev?: boolean;
248
+ run?: RunnerMethod;
249
+ }): Promise<void>;
199
250
  }
200
251
  interface DependencyModes {
201
252
  react?: boolean;
@@ -204,47 +255,50 @@ interface DependencyModes {
204
255
  //#endregion
205
256
  //#region ../../src/cli/commands/BiomeCommands.d.ts
206
257
  declare class BiomeCommands {
207
- protected readonly log: alepha_logger3.Logger;
258
+ protected readonly log: alepha_logger2.Logger;
208
259
  protected readonly utils: AlephaCliUtils;
209
- readonly format: alepha_command0.CommandPrimitive<alepha0.TObject<alepha0.TProperties>, alepha0.TSchema>;
210
- readonly lint: alepha_command0.CommandPrimitive<alepha0.TObject<alepha0.TProperties>, alepha0.TSchema>;
260
+ readonly format: alepha_command1.CommandPrimitive<alepha0.TObject<alepha0.TProperties>, alepha0.TSchema>;
261
+ readonly lint: alepha_command1.CommandPrimitive<alepha0.TObject<alepha0.TProperties>, alepha0.TSchema>;
211
262
  }
212
263
  //#endregion
213
264
  //#region ../../src/cli/commands/CoreCommands.d.ts
214
265
  declare class CoreCommands {
215
- protected readonly log: alepha_logger3.Logger;
266
+ protected readonly log: alepha_logger2.Logger;
216
267
  protected readonly cli: CliProvider;
217
268
  protected readonly utils: AlephaCliUtils;
218
269
  /**
219
270
  * Called when no command is provided
220
271
  */
221
- readonly root: alepha_command0.CommandPrimitive<alepha0.TObject<{
272
+ readonly root: alepha_command1.CommandPrimitive<alepha0.TObject<{
222
273
  version: alepha0.TOptional<alepha0.TBoolean>;
223
274
  }>, alepha0.TSchema>;
224
275
  /**
225
276
  * Clean the project, removing the "dist" directory
226
277
  */
227
- readonly clean: alepha_command0.CommandPrimitive<alepha0.TObject<alepha0.TProperties>, alepha0.TSchema>;
278
+ readonly clean: alepha_command1.CommandPrimitive<alepha0.TObject<alepha0.TProperties>, alepha0.TSchema>;
228
279
  /**
229
280
  * Ensure the project has the necessary Alepha configuration files.
230
281
  * Add the correct dependencies to package.json and install them.
231
282
  */
232
- readonly init: alepha_command0.CommandPrimitive<alepha0.TObject<{
283
+ readonly init: alepha_command1.CommandPrimitive<alepha0.TObject<{
233
284
  yarn: alepha0.TOptional<alepha0.TBoolean>;
234
285
  pnpm: alepha0.TOptional<alepha0.TBoolean>;
286
+ npm: alepha0.TOptional<alepha0.TBoolean>;
287
+ bun: alepha0.TOptional<alepha0.TBoolean>;
235
288
  react: alepha0.TOptional<alepha0.TBoolean>;
236
289
  ui: alepha0.TOptional<alepha0.TBoolean>;
290
+ test: alepha0.TOptional<alepha0.TBoolean>;
237
291
  }>, alepha0.TSchema>;
238
292
  }
239
293
  //#endregion
240
294
  //#region ../../src/cli/commands/DrizzleCommands.d.ts
241
295
  declare class DrizzleCommands {
242
- log: alepha_logger3.Logger;
296
+ log: alepha_logger2.Logger;
243
297
  utils: AlephaCliUtils;
244
298
  /**
245
299
  * Check if database migrations are up to date.
246
300
  */
247
- check: alepha_command0.CommandPrimitive<alepha0.TObject<{
301
+ check: alepha_command1.CommandPrimitive<alepha0.TObject<{
248
302
  provider: alepha0.TOptional<alepha0.TString>;
249
303
  }>, alepha0.TOptional<alepha0.TString>>;
250
304
  /**
@@ -256,7 +310,7 @@ declare class DrizzleCommands {
256
310
  * - Writes these definitions to a temporary schema file. (node_modules/.db/entities.ts)
257
311
  * - Invokes Drizzle Kit's CLI to generate migration files based on the current schema.
258
312
  */
259
- generate: alepha_command0.CommandPrimitive<alepha0.TObject<{
313
+ generate: alepha_command1.CommandPrimitive<alepha0.TObject<{
260
314
  provider: alepha0.TOptional<alepha0.TString>;
261
315
  custom: alepha0.TOptional<alepha0.TString>;
262
316
  }>, alepha0.TOptional<alepha0.TString>>;
@@ -268,7 +322,7 @@ declare class DrizzleCommands {
268
322
  * - Creates temporary entity definitions and Drizzle config.
269
323
  * - Invokes Drizzle Kit's push command to apply schema changes directly.
270
324
  */
271
- push: alepha_command0.CommandPrimitive<alepha0.TObject<{
325
+ push: alepha_command1.CommandPrimitive<alepha0.TObject<{
272
326
  provider: alepha0.TOptional<alepha0.TString>;
273
327
  }>, alepha0.TOptional<alepha0.TString>>;
274
328
  /**
@@ -279,7 +333,7 @@ declare class DrizzleCommands {
279
333
  * - Creates temporary entity definitions and Drizzle config.
280
334
  * - Invokes Drizzle Kit's migrate command to apply pending migrations.
281
335
  */
282
- migrate: alepha_command0.CommandPrimitive<alepha0.TObject<{
336
+ migrate: alepha_command1.CommandPrimitive<alepha0.TObject<{
283
337
  provider: alepha0.TOptional<alepha0.TString>;
284
338
  }>, alepha0.TOptional<alepha0.TString>>;
285
339
  /**
@@ -290,7 +344,7 @@ declare class DrizzleCommands {
290
344
  * - Creates temporary entity definitions and Drizzle config.
291
345
  * - Invokes Drizzle Kit's studio command to launch the web-based database browser.
292
346
  */
293
- studio: alepha_command0.CommandPrimitive<alepha0.TObject<{
347
+ studio: alepha_command1.CommandPrimitive<alepha0.TObject<{
294
348
  provider: alepha0.TOptional<alepha0.TString>;
295
349
  }>, alepha0.TOptional<alepha0.TString>>;
296
350
  }
@@ -311,21 +365,21 @@ declare class VerifyCommands {
311
365
  * - Build the project
312
366
  * - Clean the project again
313
367
  */
314
- readonly verify: alepha_command0.CommandPrimitive<alepha0.TObject<alepha0.TProperties>, alepha0.TSchema>;
368
+ readonly verify: alepha_command1.CommandPrimitive<alepha0.TObject<alepha0.TProperties>, alepha0.TSchema>;
315
369
  /**
316
370
  * Run TypeScript type checking across the codebase with no emit.
317
371
  */
318
- readonly typecheck: alepha_command0.CommandPrimitive<alepha0.TObject<alepha0.TProperties>, alepha0.TSchema>;
372
+ readonly typecheck: alepha_command1.CommandPrimitive<alepha0.TObject<alepha0.TProperties>, alepha0.TSchema>;
319
373
  }
320
374
  //#endregion
321
375
  //#region ../../src/cli/commands/ViteCommands.d.ts
322
376
  declare class ViteCommands {
323
- protected readonly log: alepha_logger3.Logger;
377
+ protected readonly log: alepha_logger2.Logger;
324
378
  protected readonly utils: AlephaCliUtils;
325
379
  protected readonly env: {
326
380
  VITEST_ARGS: string;
327
381
  };
328
- readonly run: alepha_command0.CommandPrimitive<alepha0.TObject<{
382
+ readonly run: alepha_command1.CommandPrimitive<alepha0.TObject<{
329
383
  watch: alepha0.TOptional<alepha0.TBoolean>;
330
384
  }>, alepha0.TString>;
331
385
  /**
@@ -334,8 +388,8 @@ declare class ViteCommands {
334
388
  * - If an index.html file is found in the project root, it will run Vite in dev mode.
335
389
  * - Otherwise, it will look for a server entry file and run it with tsx in watch mode.
336
390
  */
337
- readonly dev: alepha_command0.CommandPrimitive<alepha0.TObject<alepha0.TProperties>, alepha0.TOptional<alepha0.TString>>;
338
- readonly build: alepha_command0.CommandPrimitive<alepha0.TObject<{
391
+ readonly dev: alepha_command1.CommandPrimitive<alepha0.TObject<alepha0.TProperties>, alepha0.TOptional<alepha0.TString>>;
392
+ readonly build: alepha_command1.CommandPrimitive<alepha0.TObject<{
339
393
  stats: alepha0.TOptional<alepha0.TBoolean>;
340
394
  vercel: alepha0.TOptional<alepha0.TBoolean>;
341
395
  cloudflare: alepha0.TOptional<alepha0.TBoolean>;
@@ -343,7 +397,7 @@ declare class ViteCommands {
343
397
  sitemap: alepha0.TOptional<alepha0.TString>;
344
398
  prerender: alepha0.TOptional<alepha0.TBoolean>;
345
399
  }>, alepha0.TOptional<alepha0.TString>>;
346
- readonly test: alepha_command0.CommandPrimitive<alepha0.TObject<alepha0.TProperties>, alepha0.TSchema>;
400
+ readonly test: alepha_command1.CommandPrimitive<alepha0.TObject<alepha0.TProperties>, alepha0.TSchema>;
347
401
  }
348
402
  //#endregion
349
403
  //#region ../../src/cli/version.d.ts