@fade_networker/vono 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +47 -0
- package/dist/client/composables/navigateTo.d.ts +37 -0
- package/dist/client/composables/navigateTo.d.ts.map +1 -0
- package/dist/client/composables/navigateTo.js +56 -0
- package/dist/client/composables/navigateTo.js.map +1 -0
- package/dist/client/composables/useAsyncData.d.ts +35 -0
- package/dist/client/composables/useAsyncData.d.ts.map +1 -0
- package/dist/client/composables/useAsyncData.js +55 -0
- package/dist/client/composables/useAsyncData.js.map +1 -0
- package/dist/client/composables/useCookie.d.ts +33 -0
- package/dist/client/composables/useCookie.d.ts.map +1 -0
- package/dist/client/composables/useCookie.js +61 -0
- package/dist/client/composables/useCookie.js.map +1 -0
- package/dist/client/composables/useFormErrors.d.ts +34 -0
- package/dist/client/composables/useFormErrors.d.ts.map +1 -0
- package/dist/client/composables/useFormErrors.js +59 -0
- package/dist/client/composables/useFormErrors.js.map +1 -0
- package/dist/client/composables/useRouteRules.d.ts +26 -0
- package/dist/client/composables/useRouteRules.d.ts.map +1 -0
- package/dist/client/composables/useRouteRules.js +44 -0
- package/dist/client/composables/useRouteRules.js.map +1 -0
- package/dist/client/composables/useSeo.d.ts +35 -0
- package/dist/client/composables/useSeo.d.ts.map +1 -0
- package/dist/client/composables/useSeo.js +62 -0
- package/dist/client/composables/useSeo.js.map +1 -0
- package/dist/client/composables/useState.d.ts +35 -0
- package/dist/client/composables/useState.d.ts.map +1 -0
- package/dist/client/composables/useState.js +73 -0
- package/dist/client/composables/useState.js.map +1 -0
- package/dist/client/composables/useVonoFetch.d.ts +24 -0
- package/dist/client/composables/useVonoFetch.d.ts.map +1 -0
- package/dist/client/composables/useVonoFetch.js +49 -0
- package/dist/client/composables/useVonoFetch.js.map +1 -0
- package/dist/client/index.d.ts +19 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +20 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/layouts/index.d.ts +12 -0
- package/dist/client/layouts/index.d.ts.map +1 -0
- package/dist/client/layouts/index.js +11 -0
- package/dist/client/layouts/index.js.map +1 -0
- package/dist/client/layouts/resolve-layout.d.ts +23 -0
- package/dist/client/layouts/resolve-layout.d.ts.map +1 -0
- package/dist/client/layouts/resolve-layout.js +31 -0
- package/dist/client/layouts/resolve-layout.js.map +1 -0
- package/dist/client/nuxt-ui/index.d.ts +11 -0
- package/dist/client/nuxt-ui/index.d.ts.map +1 -0
- package/dist/client/nuxt-ui/index.js +11 -0
- package/dist/client/nuxt-ui/index.js.map +1 -0
- package/dist/client/nuxt-ui/setup.d.ts +27 -0
- package/dist/client/nuxt-ui/setup.d.ts.map +1 -0
- package/dist/client/nuxt-ui/setup.js +40 -0
- package/dist/client/nuxt-ui/setup.js.map +1 -0
- package/dist/client/pinia-hydration.d.ts +35 -0
- package/dist/client/pinia-hydration.d.ts.map +1 -0
- package/dist/client/pinia-hydration.js +48 -0
- package/dist/client/pinia-hydration.js.map +1 -0
- package/dist/config/define-config.d.ts +25 -0
- package/dist/config/define-config.d.ts.map +1 -0
- package/dist/config/define-config.js +63 -0
- package/dist/config/define-config.js.map +1 -0
- package/dist/config/env-helpers.d.ts +30 -0
- package/dist/config/env-helpers.d.ts.map +1 -0
- package/dist/config/env-helpers.js +50 -0
- package/dist/config/env-helpers.js.map +1 -0
- package/dist/config/use-config.d.ts +31 -0
- package/dist/config/use-config.d.ts.map +1 -0
- package/dist/config/use-config.js +49 -0
- package/dist/config/use-config.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/module-system/define-module.d.ts +82 -0
- package/dist/module-system/define-module.d.ts.map +1 -0
- package/dist/module-system/define-module.js +37 -0
- package/dist/module-system/define-module.js.map +1 -0
- package/dist/module-system/index.d.ts +14 -0
- package/dist/module-system/index.d.ts.map +1 -0
- package/dist/module-system/index.js +13 -0
- package/dist/module-system/index.js.map +1 -0
- package/dist/module-system/module-registry.d.ts +37 -0
- package/dist/module-system/module-registry.d.ts.map +1 -0
- package/dist/module-system/module-registry.js +68 -0
- package/dist/module-system/module-registry.js.map +1 -0
- package/dist/module-system/vite-integration.d.ts +23 -0
- package/dist/module-system/vite-integration.d.ts.map +1 -0
- package/dist/module-system/vite-integration.js +67 -0
- package/dist/module-system/vite-integration.js.map +1 -0
- package/dist/server/app-template.d.ts +47 -0
- package/dist/server/app-template.d.ts.map +1 -0
- package/dist/server/app-template.js +224 -0
- package/dist/server/app-template.js.map +1 -0
- package/dist/server/auth/gates.d.ts +79 -0
- package/dist/server/auth/gates.d.ts.map +1 -0
- package/dist/server/auth/gates.js +125 -0
- package/dist/server/auth/gates.js.map +1 -0
- package/dist/server/auth/index.d.ts +12 -0
- package/dist/server/auth/index.d.ts.map +1 -0
- package/dist/server/auth/index.js +12 -0
- package/dist/server/auth/index.js.map +1 -0
- package/dist/server/dto/query.dto.d.ts +44 -0
- package/dist/server/dto/query.dto.d.ts.map +1 -0
- package/dist/server/dto/query.dto.js +86 -0
- package/dist/server/dto/query.dto.js.map +1 -0
- package/dist/server/email/define-email.d.ts +41 -0
- package/dist/server/email/define-email.d.ts.map +1 -0
- package/dist/server/email/define-email.js +50 -0
- package/dist/server/email/define-email.js.map +1 -0
- package/dist/server/email/drivers/console.d.ts +16 -0
- package/dist/server/email/drivers/console.d.ts.map +1 -0
- package/dist/server/email/drivers/console.js +22 -0
- package/dist/server/email/drivers/console.js.map +1 -0
- package/dist/server/email/drivers/postmark.d.ts +19 -0
- package/dist/server/email/drivers/postmark.d.ts.map +1 -0
- package/dist/server/email/drivers/postmark.js +39 -0
- package/dist/server/email/drivers/postmark.js.map +1 -0
- package/dist/server/email/drivers/resend.d.ts +19 -0
- package/dist/server/email/drivers/resend.d.ts.map +1 -0
- package/dist/server/email/drivers/resend.js +37 -0
- package/dist/server/email/drivers/resend.js.map +1 -0
- package/dist/server/email/drivers/smtp.d.ts +26 -0
- package/dist/server/email/drivers/smtp.d.ts.map +1 -0
- package/dist/server/email/drivers/smtp.js +37 -0
- package/dist/server/email/drivers/smtp.js.map +1 -0
- package/dist/server/email/send-email.d.ts +42 -0
- package/dist/server/email/send-email.d.ts.map +1 -0
- package/dist/server/email/send-email.js +69 -0
- package/dist/server/email/send-email.js.map +1 -0
- package/dist/server/env-validation/define-env.d.ts +45 -0
- package/dist/server/env-validation/define-env.d.ts.map +1 -0
- package/dist/server/env-validation/define-env.js +41 -0
- package/dist/server/env-validation/define-env.js.map +1 -0
- package/dist/server/env-validation/index.d.ts +13 -0
- package/dist/server/env-validation/index.d.ts.map +1 -0
- package/dist/server/env-validation/index.js +12 -0
- package/dist/server/env-validation/index.js.map +1 -0
- package/dist/server/env-validation/validate-at-startup.d.ts +29 -0
- package/dist/server/env-validation/validate-at-startup.d.ts.map +1 -0
- package/dist/server/env-validation/validate-at-startup.js +61 -0
- package/dist/server/env-validation/validate-at-startup.js.map +1 -0
- package/dist/server/i18n/composables/useI18n.d.ts +39 -0
- package/dist/server/i18n/composables/useI18n.d.ts.map +1 -0
- package/dist/server/i18n/composables/useI18n.js +93 -0
- package/dist/server/i18n/composables/useI18n.js.map +1 -0
- package/dist/server/i18n/detect-locale.d.ts +22 -0
- package/dist/server/i18n/detect-locale.d.ts.map +1 -0
- package/dist/server/i18n/detect-locale.js +42 -0
- package/dist/server/i18n/detect-locale.js.map +1 -0
- package/dist/server/i18n/index.d.ts +14 -0
- package/dist/server/i18n/index.d.ts.map +1 -0
- package/dist/server/i18n/index.js +13 -0
- package/dist/server/i18n/index.js.map +1 -0
- package/dist/server/i18n/translate.d.ts +38 -0
- package/dist/server/i18n/translate.d.ts.map +1 -0
- package/dist/server/i18n/translate.js +83 -0
- package/dist/server/i18n/translate.js.map +1 -0
- package/dist/server/index.d.ts +40 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +36 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/jobs/define-job.d.ts +50 -0
- package/dist/server/jobs/define-job.d.ts.map +1 -0
- package/dist/server/jobs/define-job.js +32 -0
- package/dist/server/jobs/define-job.js.map +1 -0
- package/dist/server/jobs/index.d.ts +13 -0
- package/dist/server/jobs/index.d.ts.map +1 -0
- package/dist/server/jobs/index.js +12 -0
- package/dist/server/jobs/index.js.map +1 -0
- package/dist/server/jobs/runner.d.ts +37 -0
- package/dist/server/jobs/runner.d.ts.map +1 -0
- package/dist/server/jobs/runner.js +75 -0
- package/dist/server/jobs/runner.js.map +1 -0
- package/dist/server/middleware/configProvider.d.ts +26 -0
- package/dist/server/middleware/configProvider.d.ts.map +1 -0
- package/dist/server/middleware/configProvider.js +78 -0
- package/dist/server/middleware/configProvider.js.map +1 -0
- package/dist/server/middleware/dbProvider.d.ts +27 -0
- package/dist/server/middleware/dbProvider.d.ts.map +1 -0
- package/dist/server/middleware/dbProvider.js +66 -0
- package/dist/server/middleware/dbProvider.js.map +1 -0
- package/dist/server/middleware/rateLimiter.d.ts +74 -0
- package/dist/server/middleware/rateLimiter.d.ts.map +1 -0
- package/dist/server/middleware/rateLimiter.js +164 -0
- package/dist/server/middleware/rateLimiter.js.map +1 -0
- package/dist/server/middleware/validator.d.ts +57 -0
- package/dist/server/middleware/validator.d.ts.map +1 -0
- package/dist/server/middleware/validator.js +128 -0
- package/dist/server/middleware/validator.js.map +1 -0
- package/dist/server/openapi/append-tag.d.ts +21 -0
- package/dist/server/openapi/append-tag.d.ts.map +1 -0
- package/dist/server/openapi/append-tag.js +54 -0
- package/dist/server/openapi/append-tag.js.map +1 -0
- package/dist/server/openapi/index.d.ts +13 -0
- package/dist/server/openapi/index.d.ts.map +1 -0
- package/dist/server/openapi/index.js +12 -0
- package/dist/server/openapi/index.js.map +1 -0
- package/dist/server/openapi/spec-template.d.ts +45 -0
- package/dist/server/openapi/spec-template.d.ts.map +1 -0
- package/dist/server/openapi/spec-template.js +132 -0
- package/dist/server/openapi/spec-template.js.map +1 -0
- package/dist/server/renderer.d.ts +62 -0
- package/dist/server/renderer.d.ts.map +1 -0
- package/dist/server/renderer.js +115 -0
- package/dist/server/renderer.js.map +1 -0
- package/dist/server/resolvers/cache-driver.d.ts +22 -0
- package/dist/server/resolvers/cache-driver.d.ts.map +1 -0
- package/dist/server/resolvers/cache-driver.js +29 -0
- package/dist/server/resolvers/cache-driver.js.map +1 -0
- package/dist/server/resolvers/index.d.ts +17 -0
- package/dist/server/resolvers/index.d.ts.map +1 -0
- package/dist/server/resolvers/index.js +14 -0
- package/dist/server/resolvers/index.js.map +1 -0
- package/dist/server/resolvers/queue-driver.d.ts +25 -0
- package/dist/server/resolvers/queue-driver.d.ts.map +1 -0
- package/dist/server/resolvers/queue-driver.js +44 -0
- package/dist/server/resolvers/queue-driver.js.map +1 -0
- package/dist/server/resolvers/storage-driver.d.ts +24 -0
- package/dist/server/resolvers/storage-driver.d.ts.map +1 -0
- package/dist/server/resolvers/storage-driver.js +38 -0
- package/dist/server/resolvers/storage-driver.js.map +1 -0
- package/dist/server/resolvers/validate-compatibility.d.ts +22 -0
- package/dist/server/resolvers/validate-compatibility.d.ts.map +1 -0
- package/dist/server/resolvers/validate-compatibility.js +83 -0
- package/dist/server/resolvers/validate-compatibility.js.map +1 -0
- package/dist/server/resources/base-resource.d.ts +57 -0
- package/dist/server/resources/base-resource.d.ts.map +1 -0
- package/dist/server/resources/base-resource.js +74 -0
- package/dist/server/resources/base-resource.js.map +1 -0
- package/dist/server/route-rules.d.ts +45 -0
- package/dist/server/route-rules.d.ts.map +1 -0
- package/dist/server/route-rules.js +66 -0
- package/dist/server/route-rules.js.map +1 -0
- package/dist/server/seo.d.ts +58 -0
- package/dist/server/seo.d.ts.map +1 -0
- package/dist/server/seo.js +85 -0
- package/dist/server/seo.js.map +1 -0
- package/dist/server/storage/drivers/bunny.d.ts +33 -0
- package/dist/server/storage/drivers/bunny.d.ts.map +1 -0
- package/dist/server/storage/drivers/bunny.js +70 -0
- package/dist/server/storage/drivers/bunny.js.map +1 -0
- package/dist/server/storage/drivers/cloudinary.d.ts +30 -0
- package/dist/server/storage/drivers/cloudinary.d.ts.map +1 -0
- package/dist/server/storage/drivers/cloudinary.js +86 -0
- package/dist/server/storage/drivers/cloudinary.js.map +1 -0
- package/dist/server/storage/drivers/local.d.ts +28 -0
- package/dist/server/storage/drivers/local.d.ts.map +1 -0
- package/dist/server/storage/drivers/local.js +57 -0
- package/dist/server/storage/drivers/local.js.map +1 -0
- package/dist/server/storage/drivers/r2.d.ts +32 -0
- package/dist/server/storage/drivers/r2.d.ts.map +1 -0
- package/dist/server/storage/drivers/r2.js +70 -0
- package/dist/server/storage/drivers/r2.js.map +1 -0
- package/dist/server/storage/drivers/s3.d.ts +29 -0
- package/dist/server/storage/drivers/s3.d.ts.map +1 -0
- package/dist/server/storage/drivers/s3.js +68 -0
- package/dist/server/storage/drivers/s3.js.map +1 -0
- package/dist/server/storage/index.d.ts +17 -0
- package/dist/server/storage/index.d.ts.map +1 -0
- package/dist/server/storage/index.js +16 -0
- package/dist/server/storage/index.js.map +1 -0
- package/dist/server/storage/types.d.ts +70 -0
- package/dist/server/storage/types.d.ts.map +1 -0
- package/dist/server/storage/types.js +11 -0
- package/dist/server/storage/types.js.map +1 -0
- package/dist/server/storage/use-storage.d.ts +54 -0
- package/dist/server/storage/use-storage.d.ts.map +1 -0
- package/dist/server/storage/use-storage.js +150 -0
- package/dist/server/storage/use-storage.js.map +1 -0
- package/dist/server/stream-renderer.d.ts +49 -0
- package/dist/server/stream-renderer.d.ts.map +1 -0
- package/dist/server/stream-renderer.js +89 -0
- package/dist/server/stream-renderer.js.map +1 -0
- package/dist/server/templates/health.routes.d.ts +22 -0
- package/dist/server/templates/health.routes.d.ts.map +1 -0
- package/dist/server/templates/health.routes.js +28 -0
- package/dist/server/templates/health.routes.js.map +1 -0
- package/dist/server/utils/storage-url.d.ts +36 -0
- package/dist/server/utils/storage-url.d.ts.map +1 -0
- package/dist/server/utils/storage-url.js +53 -0
- package/dist/server/utils/storage-url.js.map +1 -0
- package/dist/shared/utils/autoRoutes.d.ts +22 -0
- package/dist/shared/utils/autoRoutes.d.ts.map +1 -0
- package/dist/shared/utils/autoRoutes.js +29 -0
- package/dist/shared/utils/autoRoutes.js.map +1 -0
- package/dist/shared/utils/id.d.ts +25 -0
- package/dist/shared/utils/id.d.ts.map +1 -0
- package/dist/shared/utils/id.js +29 -0
- package/dist/shared/utils/id.js.map +1 -0
- package/dist/shared/utils/logger.d.ts +33 -0
- package/dist/shared/utils/logger.d.ts.map +1 -0
- package/dist/shared/utils/logger.js +65 -0
- package/dist/shared/utils/logger.js.map +1 -0
- package/dist/shared/utils/mappers.d.ts +23 -0
- package/dist/shared/utils/mappers.d.ts.map +1 -0
- package/dist/shared/utils/mappers.js +37 -0
- package/dist/shared/utils/mappers.js.map +1 -0
- package/dist/shared/utils/pagination.d.ts +26 -0
- package/dist/shared/utils/pagination.d.ts.map +1 -0
- package/dist/shared/utils/pagination.js +28 -0
- package/dist/shared/utils/pagination.js.map +1 -0
- package/dist/shared/utils/response.d.ts +38 -0
- package/dist/shared/utils/response.d.ts.map +1 -0
- package/dist/shared/utils/response.js +46 -0
- package/dist/shared/utils/response.js.map +1 -0
- package/dist/shared/utils/softDeletes.d.ts +90 -0
- package/dist/shared/utils/softDeletes.d.ts.map +1 -0
- package/dist/shared/utils/softDeletes.js +72 -0
- package/dist/shared/utils/softDeletes.js.map +1 -0
- package/dist/types/index.d.ts +191 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +12 -0
- package/dist/types/index.js.map +1 -0
- package/dist/vite/index.d.ts +30 -0
- package/dist/vite/index.d.ts.map +1 -0
- package/dist/vite/index.js +218 -0
- package/dist/vite/index.js.map +1 -0
- package/package.json +77 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rateLimiter.d.ts","sourceRoot":"","sources":["../../../src/server/middleware/rateLimiter.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAE/C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AAmBzD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,aAAa,EACnB,KAAK,CAAC,EAAE,CAAC,CAAC,EAAE,UAAU,CAAC,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,MAAM,EACzE,OAAO,SAA+C,qDA0FvD;AAyBD;;;;;;;;;GASG;AACH,eAAO,MAAM,eAAe,mDAI3B,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,cAAc,mDAI1B,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,cAAc,mDAI1B,CAAA;AAID;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,4BAA4B,CAAC,eAAe,CAAC,EAAE;IAC7D,IAAI,CAAC,EAAE,aAAa,CAAA;IACpB,GAAG,CAAC,EAAE,aAAa,CAAA;IACnB,GAAG,CAAC,EAAE,aAAa,CAAA;CACpB;;;;EAkBA"}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ──────────────────────────────────────────────────────────────────
|
|
3
|
+
* 🏢 Company Name: Bonifade Technologies
|
|
4
|
+
* 👨💻 Developer: Bowofade Oyerinde
|
|
5
|
+
* 🐙 GitHub: oyenet1
|
|
6
|
+
* 📅 Created Date: 2026-04-05
|
|
7
|
+
* 🔄 Updated Date: 2026-04-05
|
|
8
|
+
* ──────────────────────────────────────────────────────────────────
|
|
9
|
+
*/
|
|
10
|
+
import { createMiddleware } from 'hono/factory';
|
|
11
|
+
import { Logger } from '../../shared/utils/logger.js';
|
|
12
|
+
// ─── Core factory ───────────────────────────────────────────────────
|
|
13
|
+
/**
|
|
14
|
+
* createRateLimiter — build a Hono middleware that enforces a sliding
|
|
15
|
+
* window rate limit using an in-memory store.
|
|
16
|
+
*
|
|
17
|
+
* Lazy-init pattern: the cleanup setInterval is deferred to the FIRST
|
|
18
|
+
* request so it is never called at module load time. This is required
|
|
19
|
+
* for Cloudflare Workers compatibility — CF Workers prohibit top-level
|
|
20
|
+
* I/O and timers outside of request handlers.
|
|
21
|
+
*
|
|
22
|
+
* @param tier — { windowMs, limit } from VonoConfig.rateLimit
|
|
23
|
+
* @param keyFn — function to derive the rate-limit key from the request
|
|
24
|
+
* (defaults to client IP)
|
|
25
|
+
* @param message — custom 429 message
|
|
26
|
+
*/
|
|
27
|
+
export function createRateLimiter(tier, keyFn, message = 'Too many attempts. Please try again later.') {
|
|
28
|
+
const state = {
|
|
29
|
+
store: new Map(),
|
|
30
|
+
initialized: false,
|
|
31
|
+
intervalHandle: null,
|
|
32
|
+
};
|
|
33
|
+
return createMiddleware(async (c, next) => {
|
|
34
|
+
// ── Lazy init: register cleanup interval on first request ────────
|
|
35
|
+
if (!state.initialized) {
|
|
36
|
+
state.initialized = true;
|
|
37
|
+
// Purge expired entries every windowMs to prevent memory leaks.
|
|
38
|
+
// Deferred here (not at module load) for CF Workers compatibility.
|
|
39
|
+
state.intervalHandle = setInterval(() => {
|
|
40
|
+
const now = Date.now();
|
|
41
|
+
for (const [key, entry] of state.store.entries()) {
|
|
42
|
+
if (entry.resetAt <= now) {
|
|
43
|
+
state.store.delete(key);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}, tier.windowMs);
|
|
47
|
+
// Allow the interval to be garbage-collected in serverless envs
|
|
48
|
+
if (state.intervalHandle?.unref) {
|
|
49
|
+
state.intervalHandle.unref();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// ── Derive rate-limit key ────────────────────────────────────────
|
|
53
|
+
// Security: sanitize the IP to prevent header injection attacks.
|
|
54
|
+
// Only take the first IP from x-forwarded-for (leftmost = client IP).
|
|
55
|
+
// Reject values that don't look like valid IPs to prevent spoofing.
|
|
56
|
+
const rawIp = keyFn
|
|
57
|
+
? keyFn(c)
|
|
58
|
+
: (c.req.header('cf-connecting-ip') ??
|
|
59
|
+
c.req.header('x-forwarded-for')?.split(',')[0]?.trim() ??
|
|
60
|
+
c.req.header('x-real-ip') ??
|
|
61
|
+
'unknown');
|
|
62
|
+
// Sanitize: strip anything that isn't a valid IP character
|
|
63
|
+
const key = rawIp.replace(/[^0-9a-fA-F.:]/g, '').slice(0, 45) || 'unknown';
|
|
64
|
+
const now = Date.now();
|
|
65
|
+
// ── Check / update store ─────────────────────────────────────────
|
|
66
|
+
const existing = state.store.get(key);
|
|
67
|
+
if (!existing || existing.resetAt <= now) {
|
|
68
|
+
// First request in this window — or window has expired
|
|
69
|
+
state.store.set(key, { count: 1, resetAt: now + tier.windowMs });
|
|
70
|
+
await next();
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
existing.count += 1;
|
|
74
|
+
if (existing.count > tier.limit) {
|
|
75
|
+
const retryAfterSec = Math.ceil((existing.resetAt - now) / 1000);
|
|
76
|
+
Logger.warn('[vono] Rate limit exceeded', {
|
|
77
|
+
key,
|
|
78
|
+
count: existing.count,
|
|
79
|
+
limit: tier.limit,
|
|
80
|
+
retryAfterSec,
|
|
81
|
+
});
|
|
82
|
+
return c.json({
|
|
83
|
+
success: false,
|
|
84
|
+
statusCode: 429,
|
|
85
|
+
message,
|
|
86
|
+
}, 429, {
|
|
87
|
+
'Retry-After': String(retryAfterSec),
|
|
88
|
+
'X-RateLimit-Limit': String(tier.limit),
|
|
89
|
+
'X-RateLimit-Remaining': '0',
|
|
90
|
+
'X-RateLimit-Reset': String(Math.ceil(existing.resetAt / 1000)),
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
// Under the limit — set informational headers and continue
|
|
94
|
+
c.header('X-RateLimit-Limit', String(tier.limit));
|
|
95
|
+
c.header('X-RateLimit-Remaining', String(Math.max(0, tier.limit - existing.count)));
|
|
96
|
+
c.header('X-RateLimit-Reset', String(Math.ceil(existing.resetAt / 1000)));
|
|
97
|
+
await next();
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
// ─── Default tiers ──────────────────────────────────────────────────
|
|
101
|
+
/**
|
|
102
|
+
* Default rate limit tiers — used when vono.config.ts does not specify
|
|
103
|
+
* custom values. Projects should override these in their config.
|
|
104
|
+
*/
|
|
105
|
+
const DEFAULT_AUTH_TIER = {
|
|
106
|
+
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
107
|
+
limit: 10, // 10 login attempts per window
|
|
108
|
+
};
|
|
109
|
+
const DEFAULT_OTP_TIER = {
|
|
110
|
+
windowMs: 10 * 60 * 1000, // 10 minutes
|
|
111
|
+
limit: 5, // 5 OTP attempts per window
|
|
112
|
+
};
|
|
113
|
+
const DEFAULT_API_TIER = {
|
|
114
|
+
windowMs: 60 * 1000, // 1 minute
|
|
115
|
+
limit: 100, // 100 requests per minute
|
|
116
|
+
};
|
|
117
|
+
// ─── Pre-built limiters ──────────────────────────────────────────────
|
|
118
|
+
/**
|
|
119
|
+
* authRateLimiter — strict limiter for login / register endpoints.
|
|
120
|
+
*
|
|
121
|
+
* Uses DEFAULT_AUTH_TIER unless overridden via config.
|
|
122
|
+
* Override in your src/index.ts:
|
|
123
|
+
* ```ts
|
|
124
|
+
* import { createRateLimiter } from 'vono/server'
|
|
125
|
+
* const authRateLimiter = createRateLimiter(config.rateLimit?.auth ?? DEFAULT_AUTH_TIER)
|
|
126
|
+
* ```
|
|
127
|
+
*/
|
|
128
|
+
export const authRateLimiter = createRateLimiter(DEFAULT_AUTH_TIER, undefined, 'Too many login attempts. Please wait 15 minutes before trying again.');
|
|
129
|
+
/**
|
|
130
|
+
* otpRateLimiter — strict limiter for OTP / password-reset endpoints.
|
|
131
|
+
*/
|
|
132
|
+
export const otpRateLimiter = createRateLimiter(DEFAULT_OTP_TIER, undefined, 'Too many OTP attempts. Please wait 10 minutes before trying again.');
|
|
133
|
+
/**
|
|
134
|
+
* apiRateLimiter — general-purpose limiter for public API endpoints.
|
|
135
|
+
*/
|
|
136
|
+
export const apiRateLimiter = createRateLimiter(DEFAULT_API_TIER, undefined, 'Too many requests. Please slow down.');
|
|
137
|
+
// ─── Config-aware factory ────────────────────────────────────────────
|
|
138
|
+
/**
|
|
139
|
+
* createConfiguredRateLimiters — build rate limiters from VonoConfig.
|
|
140
|
+
*
|
|
141
|
+
* Call this in your src/index.ts after loading vono.config.ts to get
|
|
142
|
+
* limiters that respect your project's custom tier settings.
|
|
143
|
+
*
|
|
144
|
+
* Usage:
|
|
145
|
+
* ```ts
|
|
146
|
+
* import { createConfiguredRateLimiters } from 'vono/server'
|
|
147
|
+
* import config from '../vono.config.js'
|
|
148
|
+
*
|
|
149
|
+
* const { authRateLimiter, otpRateLimiter, apiRateLimiter } =
|
|
150
|
+
* createConfiguredRateLimiters(config.rateLimit)
|
|
151
|
+
*
|
|
152
|
+
* authRoutes.use('/login', authRateLimiter)
|
|
153
|
+
* authRoutes.use('/otp/verify', otpRateLimiter)
|
|
154
|
+
* api.use('*', apiRateLimiter)
|
|
155
|
+
* ```
|
|
156
|
+
*/
|
|
157
|
+
export function createConfiguredRateLimiters(rateLimitConfig) {
|
|
158
|
+
return {
|
|
159
|
+
authRateLimiter: createRateLimiter(rateLimitConfig?.auth ?? DEFAULT_AUTH_TIER, undefined, 'Too many login attempts. Please wait before trying again.'),
|
|
160
|
+
otpRateLimiter: createRateLimiter(rateLimitConfig?.otp ?? DEFAULT_OTP_TIER, undefined, 'Too many OTP attempts. Please wait before trying again.'),
|
|
161
|
+
apiRateLimiter: createRateLimiter(rateLimitConfig?.api ?? DEFAULT_API_TIER, undefined, 'Too many requests. Please slow down.'),
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
//# sourceMappingURL=rateLimiter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rateLimiter.js","sourceRoot":"","sources":["../../../src/server/middleware/rateLimiter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ──────────────────────────────────────────────────────────────────
|
|
3
|
+
* 🏢 Company Name: Bonifade Technologies
|
|
4
|
+
* 👨💻 Developer: Bowofade Oyerinde
|
|
5
|
+
* 🐙 GitHub: oyenet1
|
|
6
|
+
* 📅 Created Date: 2026-04-05
|
|
7
|
+
* 🔄 Updated Date: 2026-04-05
|
|
8
|
+
* ──────────────────────────────────────────────────────────────────
|
|
9
|
+
*/
|
|
10
|
+
import type { ZodSchema } from 'zod';
|
|
11
|
+
/**
|
|
12
|
+
* The part of the request to validate.
|
|
13
|
+
* - 'json' — c.req.json()
|
|
14
|
+
* - 'query' — c.req.query() (all query params as an object)
|
|
15
|
+
* - 'param' — c.req.param() (URL path params)
|
|
16
|
+
* - 'header' — c.req.header() (request headers)
|
|
17
|
+
* - 'form' — c.req.formData() (multipart/form-data or urlencoded)
|
|
18
|
+
*/
|
|
19
|
+
export type ValidatorTarget = 'json' | 'query' | 'param' | 'header' | 'form';
|
|
20
|
+
export interface ValidationErrorBody {
|
|
21
|
+
success: false;
|
|
22
|
+
statusCode: 422;
|
|
23
|
+
message: string;
|
|
24
|
+
errors: Record<string, string>;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* zodValidator — Hono middleware factory for request validation.
|
|
28
|
+
*
|
|
29
|
+
* Validates the specified part of the request against a Zod schema.
|
|
30
|
+
* On failure, returns HTTP 422 with a structured error body.
|
|
31
|
+
* On success, sets the parsed data on `c.var` under the target key
|
|
32
|
+
* so downstream handlers can access it type-safely.
|
|
33
|
+
*
|
|
34
|
+
* Usage:
|
|
35
|
+
* ```ts
|
|
36
|
+
* import { zodValidator } from 'vono/server'
|
|
37
|
+
* import { z } from 'zod'
|
|
38
|
+
*
|
|
39
|
+
* const CreateUserSchema = z.object({
|
|
40
|
+
* email: z.string().email(),
|
|
41
|
+
* password: z.string().min(8),
|
|
42
|
+
* })
|
|
43
|
+
*
|
|
44
|
+
* router.post('/users',
|
|
45
|
+
* zodValidator('json', CreateUserSchema),
|
|
46
|
+
* async (c) => {
|
|
47
|
+
* const body = c.var.json as z.infer<typeof CreateUserSchema>
|
|
48
|
+
* // body is fully typed and validated
|
|
49
|
+
* }
|
|
50
|
+
* )
|
|
51
|
+
* ```
|
|
52
|
+
*
|
|
53
|
+
* @param target — which part of the request to validate
|
|
54
|
+
* @param schema — Zod schema to validate against
|
|
55
|
+
*/
|
|
56
|
+
export declare function zodValidator<T>(target: ValidatorTarget, schema: ZodSchema<T>): import("hono").MiddlewareHandler<any, string, {}>;
|
|
57
|
+
//# sourceMappingURL=validator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../../src/server/middleware/validator.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAY,MAAM,KAAK,CAAA;AAI9C;;;;;;;GAOG;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAA;AAE5E,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,KAAK,CAAA;IACd,UAAU,EAAE,GAAG,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC/B;AAID;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,qDAyE5E"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ──────────────────────────────────────────────────────────────────
|
|
3
|
+
* 🏢 Company Name: Bonifade Technologies
|
|
4
|
+
* 👨💻 Developer: Bowofade Oyerinde
|
|
5
|
+
* 🐙 GitHub: oyenet1
|
|
6
|
+
* 📅 Created Date: 2026-04-05
|
|
7
|
+
* 🔄 Updated Date: 2026-04-05
|
|
8
|
+
* ──────────────────────────────────────────────────────────────────
|
|
9
|
+
*/
|
|
10
|
+
import { createMiddleware } from 'hono/factory';
|
|
11
|
+
// ─── Middleware factory ──────────────────────────────────────────────
|
|
12
|
+
/**
|
|
13
|
+
* zodValidator — Hono middleware factory for request validation.
|
|
14
|
+
*
|
|
15
|
+
* Validates the specified part of the request against a Zod schema.
|
|
16
|
+
* On failure, returns HTTP 422 with a structured error body.
|
|
17
|
+
* On success, sets the parsed data on `c.var` under the target key
|
|
18
|
+
* so downstream handlers can access it type-safely.
|
|
19
|
+
*
|
|
20
|
+
* Usage:
|
|
21
|
+
* ```ts
|
|
22
|
+
* import { zodValidator } from 'vono/server'
|
|
23
|
+
* import { z } from 'zod'
|
|
24
|
+
*
|
|
25
|
+
* const CreateUserSchema = z.object({
|
|
26
|
+
* email: z.string().email(),
|
|
27
|
+
* password: z.string().min(8),
|
|
28
|
+
* })
|
|
29
|
+
*
|
|
30
|
+
* router.post('/users',
|
|
31
|
+
* zodValidator('json', CreateUserSchema),
|
|
32
|
+
* async (c) => {
|
|
33
|
+
* const body = c.var.json as z.infer<typeof CreateUserSchema>
|
|
34
|
+
* // body is fully typed and validated
|
|
35
|
+
* }
|
|
36
|
+
* )
|
|
37
|
+
* ```
|
|
38
|
+
*
|
|
39
|
+
* @param target — which part of the request to validate
|
|
40
|
+
* @param schema — Zod schema to validate against
|
|
41
|
+
*/
|
|
42
|
+
export function zodValidator(target, schema) {
|
|
43
|
+
return createMiddleware(async (c, next) => {
|
|
44
|
+
let rawData;
|
|
45
|
+
// Extract raw data from the appropriate request part
|
|
46
|
+
switch (target) {
|
|
47
|
+
case 'json': {
|
|
48
|
+
try {
|
|
49
|
+
rawData = await c.req.json();
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return c.json(buildErrorBody('Invalid JSON body'), 422);
|
|
53
|
+
}
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
case 'query': {
|
|
57
|
+
rawData = c.req.query();
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
case 'param': {
|
|
61
|
+
rawData = c.req.param();
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
case 'header': {
|
|
65
|
+
// Convert Headers to a plain object
|
|
66
|
+
const headers = {};
|
|
67
|
+
c.req.raw.headers.forEach((value, key) => {
|
|
68
|
+
headers[key] = value;
|
|
69
|
+
});
|
|
70
|
+
rawData = headers;
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
case 'form': {
|
|
74
|
+
try {
|
|
75
|
+
const formData = await c.req.formData();
|
|
76
|
+
const obj = {};
|
|
77
|
+
formData.forEach((value, key) => {
|
|
78
|
+
obj[key] = value;
|
|
79
|
+
});
|
|
80
|
+
rawData = obj;
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return c.json(buildErrorBody('Invalid form data'), 422);
|
|
84
|
+
}
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// Run Zod validation
|
|
89
|
+
const result = schema.safeParse(rawData);
|
|
90
|
+
if (!result.success) {
|
|
91
|
+
const errors = flattenZodErrors(result.error);
|
|
92
|
+
return c.json({
|
|
93
|
+
success: false,
|
|
94
|
+
statusCode: 422,
|
|
95
|
+
message: 'Validation failed',
|
|
96
|
+
errors,
|
|
97
|
+
}, 422);
|
|
98
|
+
}
|
|
99
|
+
// Store parsed data on c.var so handlers can access it
|
|
100
|
+
c.set(target, result.data);
|
|
101
|
+
await next();
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
// ─── Helpers ────────────────────────────────────────────────────────
|
|
105
|
+
/**
|
|
106
|
+
* Flatten Zod errors into a { field: message } map.
|
|
107
|
+
* For nested fields, the key is dot-separated (e.g. "address.city").
|
|
108
|
+
*/
|
|
109
|
+
function flattenZodErrors(error) {
|
|
110
|
+
const errors = {};
|
|
111
|
+
for (const issue of error.issues) {
|
|
112
|
+
const field = issue.path.join('.') || '_root';
|
|
113
|
+
// Keep the first error per field
|
|
114
|
+
if (!errors[field]) {
|
|
115
|
+
errors[field] = issue.message;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return errors;
|
|
119
|
+
}
|
|
120
|
+
function buildErrorBody(message) {
|
|
121
|
+
return {
|
|
122
|
+
success: false,
|
|
123
|
+
statusCode: 422,
|
|
124
|
+
message,
|
|
125
|
+
errors: {},
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=validator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validator.js","sourceRoot":"","sources":["../../../src/server/middleware/validator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ──────────────────────────────────────────────────────────────────
|
|
3
|
+
* 🏢 Company Name: Bonifade Technologies
|
|
4
|
+
* 👨💻 Developer: Bowofade Oyerinde
|
|
5
|
+
* 🐙 GitHub: oyenet1
|
|
6
|
+
* 📅 Created Date: 2026-04-05
|
|
7
|
+
* 🔄 Updated Date: 2026-04-05
|
|
8
|
+
* ──────────────────────────────────────────────────────────────────
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* appendModuleTag — reads src/openapi.ts, appends a new tag entry
|
|
12
|
+
* for the given module, and writes the file back.
|
|
13
|
+
*
|
|
14
|
+
* Idempotent: skips if the tag already exists.
|
|
15
|
+
*
|
|
16
|
+
* @param specPath — path to src/openapi.ts (relative to cwd)
|
|
17
|
+
* @param moduleName — module name, e.g. 'products'
|
|
18
|
+
* @param description — optional tag description
|
|
19
|
+
*/
|
|
20
|
+
export declare function appendModuleTag(specPath: string, moduleName: string, description?: string): void;
|
|
21
|
+
//# sourceMappingURL=append-tag.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"append-tag.d.ts","sourceRoot":"","sources":["../../../src/server/openapi/append-tag.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,WAAW,CAAC,EAAE,MAAM,GACnB,IAAI,CA0CN"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ──────────────────────────────────────────────────────────────────
|
|
3
|
+
* 🏢 Company Name: Bonifade Technologies
|
|
4
|
+
* 👨💻 Developer: Bowofade Oyerinde
|
|
5
|
+
* 🐙 GitHub: oyenet1
|
|
6
|
+
* 📅 Created Date: 2026-04-05
|
|
7
|
+
* 🔄 Updated Date: 2026-04-05
|
|
8
|
+
* ──────────────────────────────────────────────────────────────────
|
|
9
|
+
*/
|
|
10
|
+
import { readFileSync, writeFileSync, existsSync } from 'node:fs';
|
|
11
|
+
import { Logger } from '../../shared/utils/logger.js';
|
|
12
|
+
/**
|
|
13
|
+
* appendModuleTag — reads src/openapi.ts, appends a new tag entry
|
|
14
|
+
* for the given module, and writes the file back.
|
|
15
|
+
*
|
|
16
|
+
* Idempotent: skips if the tag already exists.
|
|
17
|
+
*
|
|
18
|
+
* @param specPath — path to src/openapi.ts (relative to cwd)
|
|
19
|
+
* @param moduleName — module name, e.g. 'products'
|
|
20
|
+
* @param description — optional tag description
|
|
21
|
+
*/
|
|
22
|
+
export function appendModuleTag(specPath, moduleName, description) {
|
|
23
|
+
if (!existsSync(specPath)) {
|
|
24
|
+
Logger.warn(`[openapi] spec file not found: ${specPath}`);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const content = readFileSync(specPath, 'utf8');
|
|
28
|
+
// Derive tag name: 'products' → 'Products'
|
|
29
|
+
const tagName = moduleName.charAt(0).toUpperCase() + moduleName.slice(1);
|
|
30
|
+
const tagDescription = description ?? `${tagName} module endpoints`;
|
|
31
|
+
// Idempotency check
|
|
32
|
+
if (content.includes(`name: '${tagName}'`) || content.includes(`name: "${tagName}"`)) {
|
|
33
|
+
Logger.info(`[openapi] tag "${tagName}" already exists in ${specPath} — skipping`);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
// Find the tags array and append the new entry before the closing bracket
|
|
37
|
+
// Pattern: looks for the last entry in the tags array
|
|
38
|
+
const tagEntry = ` { name: '${tagName}', description: '${tagDescription}' },`;
|
|
39
|
+
// Strategy: insert before the closing `]` of the tags array
|
|
40
|
+
// We look for the pattern `tags: [` and find its closing `]`
|
|
41
|
+
const tagsMatch = content.match(/tags:\s*\[([^\]]*)\]/s);
|
|
42
|
+
if (!tagsMatch) {
|
|
43
|
+
Logger.warn(`[openapi] could not locate tags array in ${specPath}`);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const updated = content.replace(/tags:\s*\[([^\]]*)\]/s, (match, inner) => {
|
|
47
|
+
const trimmed = inner.trimEnd();
|
|
48
|
+
const separator = trimmed.endsWith(',') ? '\n' : ',\n';
|
|
49
|
+
return `tags: [${trimmed}${separator}${tagEntry}\n ]`;
|
|
50
|
+
});
|
|
51
|
+
writeFileSync(specPath, updated, 'utf8');
|
|
52
|
+
Logger.info(`[openapi] appended tag "${tagName}" to ${specPath}`);
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=append-tag.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"append-tag.js","sourceRoot":"","sources":["../../../src/server/openapi/append-tag.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACjE,OAAO,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAA;AAErD;;;;;;;;;GASG;AACH,MAAM,UAAU,eAAe,CAC7B,QAAgB,EAChB,UAAkB,EAClB,WAAoB;IAEpB,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC1B,MAAM,CAAC,IAAI,CAAC,kCAAkC,QAAQ,EAAE,CAAC,CAAA;QACzD,OAAM;IACR,CAAC;IAED,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;IAE9C,2CAA2C;IAC3C,MAAM,OAAO,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;IACxE,MAAM,cAAc,GAAG,WAAW,IAAI,GAAG,OAAO,mBAAmB,CAAA;IAEnE,oBAAoB;IACpB,IAAI,OAAO,CAAC,QAAQ,CAAC,UAAU,OAAO,GAAG,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,UAAU,OAAO,GAAG,CAAC,EAAE,CAAC;QACrF,MAAM,CAAC,IAAI,CAAC,kBAAkB,OAAO,uBAAuB,QAAQ,aAAa,CAAC,CAAA;QAClF,OAAM;IACR,CAAC;IAED,0EAA0E;IAC1E,sDAAsD;IACtD,MAAM,QAAQ,GAAG,kBAAkB,OAAO,oBAAoB,cAAc,MAAM,CAAA;IAElF,4DAA4D;IAC5D,6DAA6D;IAC7D,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAA;IAExD,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,CAAC,IAAI,CAAC,4CAA4C,QAAQ,EAAE,CAAC,CAAA;QACnE,OAAM;IACR,CAAC;IAED,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAC7B,uBAAuB,EACvB,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;QACf,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,EAAE,CAAA;QAC/B,MAAM,SAAS,GAAG,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAA;QACtD,OAAO,UAAU,OAAO,GAAG,SAAS,GAAG,QAAQ,SAAS,CAAA;IAC1D,CAAC,CACF,CAAA;IAED,aAAa,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,CAAA;IACxC,MAAM,CAAC,IAAI,CAAC,2BAA2B,OAAO,QAAQ,QAAQ,EAAE,CAAC,CAAA;AACnE,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ──────────────────────────────────────────────────────────────────
|
|
3
|
+
* 🏢 Company Name: Bonifade Technologies
|
|
4
|
+
* 👨💻 Developer: Bowofade Oyerinde
|
|
5
|
+
* 🐙 GitHub: oyenet1
|
|
6
|
+
* 📅 Created Date: 2026-04-05
|
|
7
|
+
* 🔄 Updated Date: 2026-04-05
|
|
8
|
+
* ──────────────────────────────────────────────────────────────────
|
|
9
|
+
*/
|
|
10
|
+
export { generateOpenApiSpec } from './spec-template.js';
|
|
11
|
+
export { appendModuleTag } from './append-tag.js';
|
|
12
|
+
export type { OpenApiSpec } from './spec-template.js';
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/server/openapi/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAA;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AACjD,YAAY,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ──────────────────────────────────────────────────────────────────
|
|
3
|
+
* 🏢 Company Name: Bonifade Technologies
|
|
4
|
+
* 👨💻 Developer: Bowofade Oyerinde
|
|
5
|
+
* 🐙 GitHub: oyenet1
|
|
6
|
+
* 📅 Created Date: 2026-04-05
|
|
7
|
+
* 🔄 Updated Date: 2026-04-05
|
|
8
|
+
* ──────────────────────────────────────────────────────────────────
|
|
9
|
+
*/
|
|
10
|
+
export { generateOpenApiSpec } from './spec-template.js';
|
|
11
|
+
export { appendModuleTag } from './append-tag.js';
|
|
12
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/server/openapi/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAA;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ──────────────────────────────────────────────────────────────────
|
|
3
|
+
* 🏢 Company Name: Bonifade Technologies
|
|
4
|
+
* 👨💻 Developer: Bowofade Oyerinde
|
|
5
|
+
* 🐙 GitHub: oyenet1
|
|
6
|
+
* 📅 Created Date: 2026-04-05
|
|
7
|
+
* 🔄 Updated Date: 2026-04-05
|
|
8
|
+
* ──────────────────────────────────────────────────────────────────
|
|
9
|
+
*/
|
|
10
|
+
export interface OpenApiSpec {
|
|
11
|
+
openapi: string;
|
|
12
|
+
info: {
|
|
13
|
+
title: string;
|
|
14
|
+
version: string;
|
|
15
|
+
description?: string;
|
|
16
|
+
};
|
|
17
|
+
servers: Array<{
|
|
18
|
+
url: string;
|
|
19
|
+
description?: string;
|
|
20
|
+
}>;
|
|
21
|
+
tags: Array<{
|
|
22
|
+
name: string;
|
|
23
|
+
description?: string;
|
|
24
|
+
}>;
|
|
25
|
+
components: {
|
|
26
|
+
securitySchemes: Record<string, unknown>;
|
|
27
|
+
schemas: Record<string, unknown>;
|
|
28
|
+
};
|
|
29
|
+
paths: Record<string, unknown>;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* generateOpenApiSpec — returns a complete OpenAPI 3.1 spec object.
|
|
33
|
+
*
|
|
34
|
+
* Includes:
|
|
35
|
+
* - info, servers
|
|
36
|
+
* - tags: Health, Auth
|
|
37
|
+
* - components.securitySchemes: bearerAuth
|
|
38
|
+
* - components.schemas: SuccessResponse, ErrorResponse, PaginationMeta
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* // src/openapi.ts
|
|
42
|
+
* export default generateOpenApiSpec('MyApp', '1.0.0', 'https://api.example.com')
|
|
43
|
+
*/
|
|
44
|
+
export declare function generateOpenApiSpec(appName: string, appVersion: string, serverUrl: string): OpenApiSpec;
|
|
45
|
+
//# sourceMappingURL=spec-template.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spec-template.d.ts","sourceRoot":"","sources":["../../../src/server/openapi/spec-template.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE;QACJ,KAAK,EAAE,MAAM,CAAA;QACb,OAAO,EAAE,MAAM,CAAA;QACf,WAAW,CAAC,EAAE,MAAM,CAAA;KACrB,CAAA;IACD,OAAO,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACrD,IAAI,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACnD,UAAU,EAAE;QACV,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;QACxC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KACjC,CAAA;IACD,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC/B;AAID;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,GAChB,WAAW,CAmHb"}
|