@vulkano/core 1.27.0 → 1.30.1

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/AGENTS.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Overview
4
4
 
5
- `@vulkano/core` (v1.27.0) is the engine of the Vulkano MVC framework. It bootstraps the environment, connects to the database, and auto-loads all models, controllers, services, and responses before starting the Express server. The user app only calls `require('@vulkano/core')`.
5
+ `@vulkano/core` (v1.30.1) is the engine of the Vulkano MVC framework. It bootstraps the environment, connects to the database, and auto-loads all models, controllers, services, and responses before starting the Express server. The user app only calls `require('@vulkano/core')`.
6
6
 
7
7
  ```
8
8
  /**
@@ -39,7 +39,7 @@ core/
39
39
  │ ├── models.js ← Loads user models, merges lifecycle callbacks and scaffold methods
40
40
  │ └── scaffold.js ← Base CRUD methods: getAll, getByField, create, update, delete, subdocs
41
41
  ├── libs/
42
- │ ├── ApiClient.js ← Axios wrapper for outbound HTTP requests
42
+ │ ├── ApiClient.js ← fetch/undici wrapper for outbound HTTP requests (SSL-verified by default)
43
43
  │ ├── Crontab.js ← node-cron wrapper for scheduled tasks
44
44
  │ ├── Download.js ← File download helper
45
45
  │ ├── Encrypter.js ← AES-256-CBC encrypt/decrypt
@@ -291,7 +291,7 @@ These are set automatically — never import them manually:
291
291
  | `Jwt` | `services.js` | JWT encode/decode library |
292
292
  | `Encrypter` | `services.js` | AES-256-CBC encrypt/decrypt |
293
293
  | `ApiClient` | `services.js` | Outbound HTTP client |
294
- | `Crontab` | `services.js` | Cron job scheduler |
294
+ | `Crontab` | `services.js` | Cron job scheduler (`timeZone` defaults to `UTC`) |
295
295
  | `i18n` | `services.js` | i18next instance |
296
296
  | `View` | `services.js` | `View.render(view, data)` → Promise<html>, renders outside the request/response cycle |
297
297
  | `_` | `app.js` | Underscore.js |
@@ -589,7 +589,7 @@ All sources are deep-merged with `deepmerge`. Final result is in `app.config`.
589
589
 
590
590
  **Key config files:**
591
591
  - `settings.js` — port, database connection, paths
592
- - `express/cors.js`, `express/jwt.js`, `express/csp.js`, `express/cookies.js`
592
+ - `express/cors.js`, `express/jwt.js`, `express/csp.js`, `express/cookies.js`, `express/multer.js` (default `limits.fileSize` 25MB)
593
593
  - `routes.js` — explicit route mappings
594
594
  - `bootstrap.js` — startup hook (**required**)
595
595
  - `sockets/` — Socket.io config and adapters
@@ -616,11 +616,7 @@ Available globally as `io` and `app.socket`.
616
616
 
617
617
  ## Known issues / tech debt
618
618
 
619
- - **`services.js`** — All libs/services are injected into `global`. Makes unit testing hard without mocking globals.
620
- - **`bluebird`** — Still imported in a few places. Not needed in Node 18+ where `Promise` is native.
621
- - **`ApiClient`** — `rejectUnauthorized: false` disables SSL verification by default for all outbound requests.
622
- - **`Crontab`** — Default timezone is `America/New_York` instead of UTC.
623
- - **`path` and `fs` npm packages** — These are Node.js built-ins and should not be in `package.json` dependencies.
619
+ - **`services.js`** — All libs/services are injected into `global`. Makes unit testing hard without mocking globals; core libs also depend on each other via the global instead of `require()`-ing one another directly (e.g. `ApiClient` assumes `global.VSError` exists rather than requiring `./VSError`).
624
620
 
625
621
  ---
626
622
 
@@ -648,3 +644,22 @@ Requires `core/.env.test` with `TEST_DB_URI`, `TEST_PORT`, and `JWT_SECRET_KEY`
648
644
  - Starts a full Vulkano fixture server as a child process
649
645
  - Drops and rebuilds the test database on every run
650
646
  - Covers: VSR response format, routing (params + query strings), scaffold CRUD, pagination, model validation, ReDoS protection, file uploads, sockets (handshake auth + event routing)
647
+
648
+ ### Unit tests (`test/unit/`) — testing a core lib in isolation
649
+
650
+ Core libs (`core/libs/*.js`) read globals (`app`, `VSError`, `CORE_PATH`, `APP_PATH`, …) that are
651
+ normally set by `bootstrap/services.js` at framework boot — a unit test skips that boot to test one
652
+ lib alone, so those globals don't exist yet. Use the shared helper instead of hand-rolling a fake
653
+ per file:
654
+
655
+ ```js
656
+ const { setupGlobals, setupEncrypter, setupFilter } = require('../helpers/globals');
657
+
658
+ setupGlobals(); // app, VSError (the real one), CORE_PATH, APP_PATH
659
+ setupGlobals({ app: { config: { jwt: {...} } } }); // override app (shallow) for lib-specific config
660
+ setupEncrypter(); // only if the lib under test needs global.Encrypter
661
+ setupFilter(); // only if the lib under test needs global.Filter
662
+ ```
663
+
664
+ `setupGlobals()` always installs the real `libs/VSError.js`, not a per-file stand-in — keeps unit
665
+ tests honest about its actual behavior instead of drifting from a hand-copied fake.
package/CHANGELOG.md ADDED
@@ -0,0 +1,105 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@vulkano/core` are documented here.
4
+
5
+ ## [1.30.1]
6
+
7
+ ### Fixed
8
+ - `database/mongodb.js` — registered `error`/`disconnected` listeners on `mongoose.connection`
9
+ before connecting. Node throws an uncaught exception on an EventEmitter `'error'` event with
10
+ no listener — a MongoDB connection drop after the initial connect (network blip, DB restart)
11
+ was crashing the entire process instead of just failing the queries in flight. Guarded against
12
+ duplicate registration if the loader is invoked more than once in the same process.
13
+
14
+ ### Tests
15
+ - `test/unit/database/mongodb.test.js` — listeners registered before connect, no duplicate
16
+ registration on a second call, and emitting `'error'` on the connection no longer throws.
17
+
18
+ ## [1.30.0]
19
+
20
+ ### Added
21
+ - `bootstrap/express.js` — default multer `limits.fileSize` of **25MB**. Previously unbounded:
22
+ an upload could buffer to disk in full before `Upload.js`'s own `maxSize` check ever ran,
23
+ a real DoS surface (disk/memory/bandwidth) for any controller receiving uploads without an
24
+ explicit limit. Override via `app/config/express/multer.js` (`limits.fileSize`) — deep-merges,
25
+ doesn't drop the existing `fieldNestingDepth: 5` default.
26
+
27
+ ### Tests
28
+ - `test/unit/bootstrap/express.test.js` — default `fileSize`, override, and that
29
+ `fieldNestingDepth` survives a partial `limits` override (deep merge).
30
+
31
+ ### Docs
32
+ - `README.md` / `AGENTS.md`: documented the default and the override path.
33
+
34
+ ## [1.29.0]
35
+
36
+ ### Changed — BREAKING (default behavior)
37
+ - `Crontab.schedule()` default `timeZone` is now `UTC`, was `America/New_York`. Any job that
38
+ relied on the implicit New York default without passing `timeZone` explicitly now runs on a
39
+ different schedule relative to wall-clock time in that zone — pass `timeZone: 'America/New_York'`
40
+ explicitly to keep the old behavior. A job with an explicit `timeZone` is unaffected.
41
+
42
+ ### Tests
43
+ - `test/unit/libs/Crontab.test.js` — default/override timezone, `start: true/false`, manual tick.
44
+
45
+ ### Docs
46
+ - `AGENTS.md` / `README.md`: documented the `UTC` default; removed the now-resolved
47
+ `Crontab` timezone known-issue entry.
48
+
49
+ ## [1.28.1]
50
+
51
+ ### Changed (internal, no runtime/public API impact)
52
+ - `test/unit/` — added `test/unit/helpers/globals.js`, a shared `setupGlobals()` /
53
+ `setupEncrypter()` / `setupFilter()` bootstrap for unit-testing a core lib in isolation.
54
+ Replaces 4 duplicated, hand-rolled `VSError` stand-in classes across
55
+ `ApiClient.test.js` / `Encrypter.test.js` / `Jwt.test.js` with the real `libs/VSError.js`,
56
+ and a fragile `delete global.app` pattern in `Encrypter.test.js` with restoring the
57
+ baseline via `setupGlobals()`.
58
+ - Removed two stale `Known issues / tech debt` entries from `AGENTS.md` (`bluebird` and
59
+ `path`/`fs` as dependencies) — neither is present in the codebase or `package.json` anymore.
60
+
61
+ ## [1.28.0]
62
+
63
+ ### Changed
64
+ - `ApiClient` — SSL verification stays enabled (secure) by default, but the default itself can
65
+ now be flipped app-wide via `.env` with `API_CLIENT_REJECT_UNAUTHORIZED=false` (e.g. same-server
66
+ calls to other internal services on self-signed certs). A per-call `rejectUnauthorized` still
67
+ always wins over the env default, in either direction.
68
+
69
+ ### Tests
70
+ - Env-driven default, per-call override in both directions, and non-`"false"` values keeping
71
+ verification on.
72
+
73
+ ### Docs
74
+ - `AGENTS.md` / `README.md`: documented `API_CLIENT_REJECT_UNAUTHORIZED`; removed the stale
75
+ "SSL disabled by default" known-issue entry and the outdated "Axios wrapper" description.
76
+
77
+ ## [1.27.0]
78
+
79
+ ### Added
80
+ - Global `View` lib — `View.render(view, data)` returns a Promise<html>, rendering a view
81
+ outside the normal request/response cycle (email bodies, PDF generation, etc.), without
82
+ the `req.app.render` boilerplate.
83
+
84
+ ### Tests
85
+ - Nunjucks and Handlebars coverage for `View.render()`, verifying markup matches `res.render()`
86
+ on the same template.
87
+
88
+ ### Docs
89
+ - `AGENTS.md` / `README.md`: documented the `View` global.
90
+
91
+ ## [1.26.0]
92
+
93
+ ### Added
94
+ - Controllers support **arbitrary nesting depth** under `app/controllers/` (e.g.
95
+ `api/config/VatTypesController.js` → `/api/config/vat-types/...`), not just one subfolder
96
+ level — ideal for grouping controllers by module/domain. The route loader
97
+ (`controllers/controllers.js`) now walks the folder tree recursively instead of handling
98
+ a single hardcoded nesting level.
99
+
100
+ ### Tests
101
+ - `test/integration/nested-modules.test.js` — same controller name (`VatTypesController`) at
102
+ root, one, and two levels of nesting, all 5 CRUD methods, verifying full route isolation.
103
+
104
+ ### Docs
105
+ - `AGENTS.md` / `README.md`: documented multi-level controller nesting.
package/README.md CHANGED
@@ -354,6 +354,18 @@ All files in `app/services/` are auto-loaded as globals. The framework also expo
354
354
  | `Upload` | Validate, save and return the local path of an uploaded file |
355
355
  | `i18n` | Internationalization via i18next |
356
356
  | `mongoose` | Mongoose instance |
357
+ | `View` | `View.render(view, data)` → Promise<html>, renders outside the request/response cycle |
358
+
359
+ `ApiClient` verifies SSL by default. A single call can opt out with `rejectUnauthorized: false`
360
+ (and force it back on with `rejectUnauthorized: true`), and the default itself can be flipped for
361
+ the whole app via `.env`:
362
+
363
+ ```
364
+ # .env — disables SSL verification by default for every ApiClient call
365
+ # (e.g. calling other services on the same server over self-signed certs).
366
+ # A per-call rejectUnauthorized always overrides this, in either direction.
367
+ API_CLIENT_REJECT_UNAUTHORIZED=false
368
+ ```
357
369
 
358
370
  ---
359
371
 
@@ -361,6 +373,17 @@ All files in `app/services/` are auto-loaded as globals. The framework also expo
361
373
 
362
374
  Vulkano uses [Multer](https://github.com/expressjs/multer) v2. Files are available on `req.files` after a `multipart/form-data` POST — Multer writes them straight into `PUBLIC_PATH/files` under a temporary name.
363
375
 
376
+ Multer rejects any single file over **25MB** by default (`limits.fileSize`) — before it's fully
377
+ buffered to disk, unlike `Upload.file()`'s own `maxSize` check which only runs after. Override it
378
+ in `app/config/express/multer.js`:
379
+
380
+ ```js
381
+ // app/config/express/multer.js
382
+ module.exports = {
383
+ limits: { fileSize: 100 * 1024 * 1024 } // 100MB
384
+ };
385
+ ```
386
+
364
387
  ### The `Upload` lib
365
388
 
366
389
  `Upload.file(files, opts)` validates a single uploaded file (mimetype, extension, size, write
@@ -512,6 +535,9 @@ const { _id } = Jwt.decode(Jwt.getToken(req)) || {};
512
535
 
513
536
  When Vulkano starts, you can configure your own tasks to run at a given time.
514
537
 
538
+ `timeZone` defaults to `UTC` when omitted — pass it explicitly (e.g. `'America/New_York'`) for a
539
+ task that should run relative to a specific local time instead.
540
+
515
541
  ```js
516
542
  // app/config/bootstrap.js
517
543
  module.exports = (start) => {
@@ -55,7 +55,13 @@ module.exports = function getExpressConfiguration() {
55
55
  limits: {
56
56
  // Prevents DoS via deeply nested field names (e.g. a[b][c][d]...),
57
57
  // which multer forwards to append-field's unbounded recursive parser.
58
- fieldNestingDepth: 5
58
+ fieldNestingDepth: 5,
59
+ // Rejects an oversized upload mid-stream instead of buffering the
60
+ // whole file to disk first — Upload.js's own maxSize check only runs
61
+ // after multer has already written the file. Override via
62
+ // app/config/express/multer.js (limits.fileSize) if a project needs
63
+ // a different ceiling.
64
+ fileSize: 25 * 1024 * 1024
59
65
  }
60
66
  },
61
67
  morgan: {
@@ -57,6 +57,23 @@ module.exports = async function loadDatabaseApplication() {
57
57
  });
58
58
  }
59
59
 
60
+ // Node throws an uncaught exception on an EventEmitter's 'error' event when
61
+ // nothing is listening for it — without this, a connection drop after the
62
+ // initial connect (network blip, MongoDB restart) crashes the whole
63
+ // process instead of just failing the queries in flight. Attached before
64
+ // connect() so it also catches errors emitted during the initial attempt.
65
+ if (!mongoose.connection.listenerCount('error')) {
66
+ mongoose.connection.on('error', (err) => {
67
+ console.log(` \x1b[41mERROR\x1b[0m: MongoDB connection error: ${err.message}`);
68
+ });
69
+ }
70
+
71
+ if (!mongoose.connection.listenerCount('disconnected')) {
72
+ mongoose.connection.on('disconnected', () => {
73
+ console.log(' \x1b[33mWARNING\x1b[0m: MongoDB disconnected.');
74
+ });
75
+ }
76
+
60
77
  if (!mongoose.connection.readyState) {
61
78
  await mongoose.connect(toConnect, connectionProps);
62
79
  }
package/libs/ApiClient.js CHANGED
@@ -67,8 +67,12 @@ module.exports = {
67
67
  ? { url: props, method: 'GET' }
68
68
  : (props || {});
69
69
 
70
- // SSL verification is enabled by default; pass rejectUnauthorized: false to disable
71
- const sslVerify = rejectUnauthorized !== false;
70
+ // SSL verification is enabled by default (secure). It can be turned off globally via
71
+ // API_CLIENT_REJECT_UNAUTHORIZED=false in .env (e.g. internal calls to other services on
72
+ // the same server using self-signed certs) — a per-call `rejectUnauthorized` always wins
73
+ // over the env default, in either direction.
74
+ const envDefault = process.env.API_CLIENT_REJECT_UNAUTHORIZED !== 'false';
75
+ const sslVerify = rejectUnauthorized !== undefined ? rejectUnauthorized !== false : envDefault;
72
76
 
73
77
  const optHeaders = {
74
78
  'Content-Type': 'application/json',
package/libs/Crontab.js CHANGED
@@ -16,7 +16,7 @@ module.exports = {
16
16
  cronTime: time,
17
17
  onTick: task || ( () => {} ),
18
18
  onComplete: onComplete || ( () => {} ),
19
- timeZone: timeZone || 'America/New_York',
19
+ timeZone: timeZone || 'UTC',
20
20
  start: typeof start !== 'undefined' ? start : true
21
21
  };
22
22
 
package/libs/Upload.js CHANGED
@@ -70,6 +70,10 @@ const MIME_EXTENSION_MAP = {
70
70
  'video/x-mpg': 'mpg'
71
71
  };
72
72
 
73
+ // This only rejects a file after multer has already buffered it to disk —
74
+ // it never runs on an upload multer already rejected via its own
75
+ // limits.fileSize (bootstrap/express.js, default 25MB). Set maxSize below
76
+ // that ceiling for it to ever actually apply.
73
77
  const DEFAULT_MAX_SIZE = 10 * 1024 * 1024;
74
78
 
75
79
  module.exports = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vulkano/core",
3
- "version": "1.27.0",
3
+ "version": "1.30.1",
4
4
  "description": "A MVC framework using Express 4",
5
5
  "license": "MIT",
6
6
  "author": "Vulkano Team",
@@ -35,7 +35,7 @@
35
35
  "url": "https://github.com/vulkanojs/vulkano-core/issues"
36
36
  },
37
37
  "devDependencies": {
38
- "jest": "^30.4.2",
38
+ "jest": "^30.5.1",
39
39
  "nodemon": "^3.1.14",
40
40
  "socket.io-client": "^4.8.3",
41
41
  "vite-plus": "catalog:"
@@ -54,18 +54,18 @@
54
54
  "express-handlebars": "^9.0.1",
55
55
  "express-jwt": "^8.5.1",
56
56
  "express-session": "^1.19.0",
57
- "express-useragent": "^2.2.1",
57
+ "express-useragent": "^2.2.3",
58
58
  "frameguard": "^4.0.0",
59
59
  "helmet": "^8.3.0",
60
- "i18next": "^26.3.6",
60
+ "i18next": "^26.4.1",
61
61
  "include-all": "^4.0.3",
62
62
  "jwt-simple": "^0.5.6",
63
63
  "mongoose": "^8.12.1",
64
64
  "mongoose-paginate-v2": "^1.9.5",
65
- "morgan": "^1.11.0",
66
- "multer": "^2.2.0",
65
+ "morgan": "^1.12.0",
66
+ "multer": "^2.3.0",
67
67
  "nunjucks": "^3.2.4",
68
- "redis": "^6.1.0",
68
+ "redis": "^6.2.1",
69
69
  "socket.io": "^4.8.3",
70
70
  "socket.io-adapter": "^2.5.8",
71
71
  "undici": "^7.28.0"
@@ -1,10 +1,11 @@
1
1
  allowBuilds:
2
+ '@parcel/watcher': true
2
3
  fsevents: set this to true or false
3
- unrs-resolver: set this to true or false
4
+ unrs-resolver: true
4
5
  catalog:
5
6
  vite: npm:@voidzero-dev/vite-plus-core@latest
6
7
  vitest: npm:@voidzero-dev/vite-plus-test@latest
7
- vite-plus: ^0.2.4
8
+ vite-plus: ^0.3.0
8
9
  overrides:
9
10
  vite: "catalog:"
10
11
  vitest: "catalog:"