Skip to content

Feature/v3#37

Merged
ivanhuay merged 28 commits into
masterfrom
feature/v3
May 2, 2026
Merged

Feature/v3#37
ivanhuay merged 28 commits into
masterfrom
feature/v3

Conversation

@ivanhuay
Copy link
Copy Markdown
Owner

@ivanhuay ivanhuay commented May 2, 2026

feat: Hiroki v3 — adapter system, monorepo, full ecosystem

Summary

Complete rewrite of hiroki from a Mongoose-coupled wrapper into a
database-agnostic CRUD engine with a pluggable adapter system,
published as an npm monorepo.

Phases completed

Phase 1–3 — Core modernization

  • Full TypeScript migration, no any in public surface
  • HirokiAdapter interface — decouples routing from data layer
  • MongooseAdapter extracted from core
  • HirokiQuery AST — database-agnostic query representation
  • parseHirokiQuery — parses where, sort, select, limit,
    offset from URL query params
  • HirokiFilter operators: eq/ne/gt/gte/lt/lte/in/nin/regex

Phase 4 — Hooks & middleware

  • Lifecycle hooks: beforeCreate, afterCreate, beforeUpdate,
    afterUpdate, beforeDelete, afterDelete
  • Per-resource middleware chain — auth, auditing, transformations
    without touching core

Phase 5 — New adapters

  • MemoryAdapter — zero deps, full operator support, clear() for
    test resets
  • adapter option on importModel — inject any adapter directly

Phase 6 — Debt cleanup

  • Simplified ResolvedControllerConfig type
  • Extracted public types to src/types.ts
  • Removed FilterQuery<any> dependency on mongoose in validator

Phase 7 — Documentation

  • Full VitePress docs site (13 pages)
  • Getting started, why hiroki, configuration, hooks, security,
    adapters, API reference, changelog

Phase 8 — CI/CD

  • Replaced CircleCI with GitHub Actions
  • Node 20/22 matrix, triggers on push/PR to master and feature/**
  • Removed MongoDB service container (tests use MemoryAdapter)

Phase 9 — Monorepo

  • Migrated to npm workspaces under packages/
  • hiroki — core package (git mv, no history loss)
  • hiroki-drizzle — DrizzleAdapter skeleton (beta, peer: drizzle-orm)
  • hiroki-sequelize — SequelizeAdapter skeleton (beta, peer: sequelize)
  • hiroki-pino — PinoLogger implements HirokiLogger
  • hiroki-winston — WinstonLogger implements HirokiLogger
  • 316 tests total (270 core + 46 new across ecosystem packages)
  • Per-package READMEs, docs updated with all new packages

Security

  • Field whitelisting (allowedFields)
  • Query sanitization — blocks __proto__, constructor, prototype
  • Query depth limits — maxFilters, maxInValues, maxRegexLength

Breaking changes

  • mongoose-connector.ts removed — use MongooseAdapter directly
  • model.ts no longer exports a class — types only
  • Express removed as a dependency — framework-agnostic

Test plan

  • npm test — 316 tests, all green
  • npm run build — all 5 packages build clean (ESM + CJS + DTS)
  • npm run docs:build — VitePress builds without errors
  • Smoke test against a real Express + MongoDB app — verified locally

ivanhuay added 28 commits April 13, 2026 22:04
  - Make Model<T> generic so _parseConditions returns FilterQuery<T>
    without casts at each call site in MongooseConnector
  - _parsePopulate converts plain strings to { path } objects to
    satisfy Mongoose's populate() overload signatures cleanly
  - Remove _methods dynamic dispatch; call countDocuments /
    estimatedDocumentCount directly
  - UpdateSet / UpdateOperators use unknown instead of any
  - Controller and Hiroki body params typed as Record<string, unknown>
  - Hiroki.process returns ProcessResponse; dead models field removed
  - ErrorFactory facade and tests deleted
  Covers the three Fase 1 test tasks from the roadmap.
  Also adds Hiroki importModels/setConfig coverage.
  Statement coverage 92.4% → 95.5%, branch 65.7% → 72.1%.
  - Add HttpErrorResponse interface; toJSON() now returns it explicitly
  - validateModel and validateDocumentExist<T> converted to assertion
    functions (asserts) so TypeScript narrows types at call sites
  - validatePutParams, validateIdRequired, validateConditionsString,
    validateBody return void — return value was never used
  - _parseConditions uses validateConditions return value, removing
    duplicate JSON.parse logic
  - updateByConditions parses conditions once upfront instead of twice
  - delete() drops redundant validateDocumentExist (findById asserts it)
  - Fallback error in hiroki.process uses
  Static-only class is a namespace antipattern in TS — named exports
  are treeshakeable, easier to import selectively, and remove the
  indirection of  call sites.

  - Remove dead  and  (unused
    in production, latter also had a typo in the name)
  - Update all callers to named imports
  - Update tests to match
  - Add HirokiAdapter interface with canHandle, CRUD, and query methods
  - Add AdapterRegistry for pluggable adapter resolution
  - Extract MongooseAdapter from MongooseConnector (absorbs Model class)
  - Simplify model.ts to pure types — no class, no mongoose deps
  - Controller now depends on HirokiAdapter, defaults to MongooseAdapter
  - Remove mongoose-connector.ts

  Enables future adapters (SQL, Prisma, etc.) without touching core.
  - tsup replaces tsc for build; mongoose and pluralize marked external
  - exports field routes require → .cjs, import → .js
  - CJS footer makes require('hiroki') return the singleton directly
  - duck-type isMongooseModel to survive npm link / multiple mongoose copies
  - InvalidModelError now shows modelName, not full function source
  - smoke-test verifies both ESM and CJS paths
  - New HirokiQuery AST: where/limit/offset/sort/select
  - parseHirokiQuery maps URL params to typed filters —
    where[field][$op]=val, sort=-field,other, select=a,b
  - MongooseAdapter maps HirokiFilter[] to FilterQuery via
    _mapQuery/_mapFilter/_mapSort; drops raw QueryParams
  - HirokiAdapter interface updated to use HirokiQuery
  - Legacy conditions= and conditions[field]= preserved as
    escape hatch for raw DB filters

  BREAKING CHANGE: limit/skip now parsed as numbers (not
  strings); skip maps to offset; sort/select are arrays.
  Consumers reading ExtendedQueryParams directly must update.
  - Six hooks on ControllerConfig: beforeCreate/afterCreate,
    beforeUpdate/afterUpdate, beforeDelete/afterDelete
  - before-hooks receive body and can return a mutated copy;
    after-hooks observe the persisted doc
  - HirokiMiddleware[] chains via reduceRight (onion model);
    throw to deny, return without next() to short-circuit
  - HookContext carries modelName for all hook callbacks
  - All hook/middleware types exported from package
  - MemoryAdapter: zero-dep in-memory store implementing HirokiAdapter;
    supports all HirokiQuery ops (where/sort/limit/offset/select),
    legacy conditions, and clear() for test isolation
  - ControllerConfig.adapter injects any HirokiAdapter directly,
    bypassing MongooseAdapter and Mongoose model validation
  - Controller resolution order: config.adapter →
    adapterRegistry.resolve → MongooseAdapter fallback
  - Enables Hiroki usage without Mongoose for testing/prototyping
…apters

  - Controller logs operation type, id/keys at debug; create/update/delete
    at info — gives visibility into request flow without sensitive data
  - MongooseAdapter and MemoryAdapter log filter, options, and result
    count at debug level on every query
  - HirokiAdapter gains optional setLogger() so injected adapters
    inherit the controller's logger automatically
  - Adapters accept logger in constructor and via setLogger
  allowedFields in ControllerConfig filters create/update body
  before hooks run — prevents mass-assignment attacks.

  parseHirokiQuery drops __proto__, constructor, prototype from
  where[] and conditions[] params — prevents prototype pollution.

  - fix: merge split afterAll hooks in setup.ts (topology error)
  - fix: silence dangling find() promise in controller isolation test
  - test: 40-case query parser suite, 6-case security suite
  Covers importModel/importModels/process/setConfig, every
  ControllerConfig field, HirokiAdapter contract, parseHirokiQuery
  param reference, QueryLimits, and MemoryAdapter.

  Also adds query limit tests (8 cases) and exports QueryLimits type.
  Completed sections collapsed (Fases 1–5, Observabilidad, Seguridad,
  Testing, Build). Scattered [ ] items consolidated into:

  - Fase 6: internal debt (types, naming, dedup)
  - Fase 7: docs (getting started, examples, auth/rate-limit patterns)
  - Fase 8: monorepo + sub-packages (hiroki-drizzle, hiroki-sequelize,
    external loggers)
  - Extract HttpMethod/ProcessParams/Request*/ParsedQuery to src/types.ts
  - Simplify ResolvedControllerConfig: replace Required<Omit<>>& Pick<>
    with explicit type literal
  - Drop underscore prefix from private/protected members
    (_disabledMethods, _filterBody, _getQueryParams)
  - Remove FilterQuery<any> from validator.ts — ValidConditions now uses
    Record<string,unknown>, eliminating mongoose dep from validator
Replace MkDocs (Python) with VitePress (Node.js) to eliminate external
toolchain dependency. Docs source stays in mkdocs/, build outputs to
docs/ for GitHub Pages compatibility.

- mkdocs/.vitepress/config.ts: full nav/sidebar config, outDir ../docs
- 13 new pages: guide (5), API reference (4), adapters (4)
- package.json: add docs:dev / docs:build / docs:preview scripts
- .gitignore: add mkdocs/.vitepress/cache
- srcExclude legacy mkdocs/docs and mkdocs/v0.2.9 from VitePress build
Default was true (no pluralization) but the name implies disabled=false
should be the default. Routes now pluralize by default; set
disabledPluralize: true to opt out.

- Default changed from true → false
- Condition flipped: disabledPluralize ? baseName : pluralize(baseName)
- JSDoc, guide/configuration.md, api/controller-config.md updated
- Tests updated: two cases now cover default pluralize + explicit disable
  Node 20/22 matrix, no MongoDB service needed (tests use MemoryAdapter).
  Removes legacy mongo container dependency from CI config.
  Restructures the repo into a monorepo under packages/ to allow
  publishing adapter and logger integrations independently without
  bloating the core hiroki package.

  - packages/hiroki — core (all existing source, git mv)
  - packages/hiroki-drizzle — DrizzleAdapter skeleton, peer: drizzle-orm
  - packages/hiroki-sequelize — SequelizeAdapter skeleton, peer: sequelize
  - packages/hiroki-pino — PinoLogger implements HirokiLogger
  - packages/hiroki-winston — WinstonLogger implements HirokiLogger

  Exports HirokiLogger, ValidConditions, LogLevel, LoggerConfig from
  core index so ecosystem packages can import types from a single
  entry point.

  316 tests across all packages (270 hiroki + 46 new).
  Docs updated: drizzle, sequelize, loggers pages added to VitePress.
  drizzle/sequelize bumped to 0.1.0-beta.0 so npm install won't pull
  them by default — only via explicit @beta tag. Each package README
  covers install, usage, and links to full docs. Beta READMEs include
  implementation status checklist.
@ivanhuay ivanhuay merged commit 0d88bfd into master May 2, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant