exisjs 0.7.12 → 0.7.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +195 -39
- package/dist/cli/commands/run.d.ts.map +1 -1
- package/dist/cli/commands/run.js +3 -1
- package/dist/cli/commands/run.js.map +1 -1
- package/dist/config/types.d.ts +2 -2
- package/dist/config/types.d.ts.map +1 -1
- package/dist/decorators/params.d.ts +21 -0
- package/dist/decorators/params.d.ts.map +1 -1
- package/dist/decorators/params.js +21 -1
- package/dist/decorators/params.js.map +1 -1
- package/dist/di/container.d.ts +15 -6
- package/dist/di/container.d.ts.map +1 -1
- package/dist/di/container.js +26 -3
- package/dist/di/container.js.map +1 -1
- package/dist/di/index.d.ts +1 -1
- package/dist/di/index.d.ts.map +1 -1
- package/dist/di/index.js.map +1 -1
- package/dist/di/inject.d.ts +2 -1
- package/dist/di/inject.d.ts.map +1 -1
- package/dist/di/inject.js +2 -1
- package/dist/di/inject.js.map +1 -1
- package/dist/docs/ARCHITECTURE.md +130 -78
- package/dist/docs/BENCHMARKS.md +4 -4
- package/dist/docs/content/README.md +43 -44
- package/dist/docs/content/boundaries.mdx +138 -61
- package/dist/docs/content/cli/commands.mdx +151 -128
- package/dist/docs/content/cli/overview.mdx +61 -47
- package/dist/docs/content/cli/usage.mdx +107 -30
- package/dist/docs/content/config.mdx +145 -158
- package/dist/docs/content/controllers.mdx +193 -464
- package/dist/docs/content/cron.mdx +183 -69
- package/dist/docs/content/decorators.mdx +449 -154
- package/dist/docs/content/errors.mdx +202 -305
- package/dist/docs/content/exisjs-mcp.mdx +59 -49
- package/dist/docs/content/installation.mdx +243 -157
- package/dist/docs/content/introduction.mdx +220 -48
- package/dist/docs/content/middleware.mdx +171 -497
- package/dist/docs/content/modules.mdx +141 -83
- package/dist/docs/content/providers.mdx +216 -180
- package/dist/docs/content/requests.mdx +168 -488
- package/dist/docs/content/responses.mdx +200 -533
- package/dist/docs/content/routing.mdx +235 -598
- package/dist/docs/content/security.mdx +233 -57
- package/dist/docs/content/structure.mdx +151 -240
- package/dist/docs/content/swagger.mdx +192 -91
- package/dist/docs/content/testing.mdx +205 -74
- package/dist/docs/content/validation.mdx +479 -341
- package/dist/docs/reference/README.md +34 -2
- package/dist/docs/reference/config.mdx +277 -75
- package/dist/docs/reference/context.mdx +197 -106
- package/dist/docs/reference/cron.mdx +266 -152
- package/dist/docs/reference/database.mdx +135 -53
- package/dist/docs/reference/decorators.mdx +234 -116
- package/dist/docs/reference/di.mdx +146 -112
- package/dist/docs/reference/error.mdx +155 -178
- package/dist/docs/reference/exis.mdx +141 -70
- package/dist/docs/reference/logger.mdx +110 -126
- package/dist/docs/reference/middleware.mdx +235 -158
- package/dist/docs/reference/plugin.mdx +136 -64
- package/dist/docs/reference/router.mdx +275 -293
- package/dist/docs/reference/sanitize.mdx +91 -87
- package/dist/docs/reference/swagger.mdx +280 -100
- package/dist/docs/reference/testing.mdx +223 -89
- package/dist/docs/reference/validator.mdx +194 -206
- package/dist/lib/start-server.js +22 -8
- package/dist/lib/start-server.js.map +1 -1
- package/dist/module/module.d.ts +6 -3
- package/dist/module/module.d.ts.map +1 -1
- package/dist/module/module.js +17 -3
- package/dist/module/module.js.map +1 -1
- package/dist/router/controller-registrar.d.ts.map +1 -1
- package/dist/router/controller-registrar.js +26 -4
- package/dist/router/controller-registrar.js.map +1 -1
- package/dist/router/scanner/fs-scanner.d.ts.map +1 -1
- package/dist/router/scanner/fs-scanner.js +22 -0
- package/dist/router/scanner/fs-scanner.js.map +1 -1
- package/dist/router/types.d.ts +6 -4
- package/dist/router/types.d.ts.map +1 -1
- package/dist/server/app.d.ts +3 -1
- package/dist/server/app.d.ts.map +1 -1
- package/dist/server/app.js +24 -8
- package/dist/server/app.js.map +1 -1
- package/dist/server/bootstrapper.d.ts.map +1 -1
- package/dist/server/bootstrapper.js +19 -0
- package/dist/server/bootstrapper.js.map +1 -1
- package/dist/server/request.d.ts +2 -0
- package/dist/server/request.d.ts.map +1 -1
- package/dist/server/request.js +11 -0
- package/dist/server/request.js.map +1 -1
- package/dist/server/response.d.ts +10 -5
- package/dist/server/response.d.ts.map +1 -1
- package/dist/server/response.js +65 -18
- package/dist/server/response.js.map +1 -1
- package/dist/swagger/generator.d.ts.map +1 -1
- package/dist/swagger/generator.js +130 -10
- package/dist/swagger/generator.js.map +1 -1
- package/dist/telemetry/index.d.ts +12 -0
- package/dist/telemetry/index.d.ts.map +1 -0
- package/dist/telemetry/index.js +121 -0
- package/dist/telemetry/index.js.map +1 -0
- package/dist/testing/client.d.ts +1 -0
- package/dist/testing/client.d.ts.map +1 -1
- package/dist/testing/client.js +4 -1
- package/dist/testing/client.js.map +1 -1
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +11 -4
- package/dist/testing/index.js.map +1 -1
- package/dist/validator/builder.d.ts +26 -1
- package/dist/validator/builder.d.ts.map +1 -1
- package/dist/validator/builder.js +31 -0
- package/dist/validator/builder.js.map +1 -1
- package/dist/validator/engine.d.ts +23 -0
- package/dist/validator/engine.d.ts.map +1 -1
- package/dist/validator/engine.js +228 -31
- package/dist/validator/engine.js.map +1 -1
- package/dist/validator/index.d.ts +8 -4
- package/dist/validator/index.d.ts.map +1 -1
- package/dist/validator/index.js +4 -1
- package/dist/validator/index.js.map +1 -1
- package/dist/validator/tex-types.d.ts +19 -4
- package/dist/validator/tex-types.d.ts.map +1 -1
- package/dist/validator/tex-types.js +102 -0
- package/dist/validator/tex-types.js.map +1 -1
- package/dist/validator/tex.d.ts +1 -1
- package/dist/validator/tex.d.ts.map +1 -1
- package/dist/validator/tex.js +3 -1
- package/dist/validator/tex.js.map +1 -1
- package/dist/validator/types.d.ts +1 -0
- package/dist/validator/types.d.ts.map +1 -1
- package/package.json +46 -14
- package/dist/docs/content/dataloader.mdx +0 -170
- package/dist/docs/reference/dataloader.mdx +0 -91
|
@@ -5,13 +5,36 @@ class TexType {
|
|
|
5
5
|
constructor(_raw, defaultValue) {
|
|
6
6
|
this._raw = _raw;
|
|
7
7
|
this.sanitizers = [];
|
|
8
|
+
this.transformations = [];
|
|
8
9
|
this.refinements = [];
|
|
9
10
|
this.defaultValue = defaultValue;
|
|
10
11
|
}
|
|
12
|
+
describe(description) {
|
|
13
|
+
this.description = description;
|
|
14
|
+
return this;
|
|
15
|
+
}
|
|
16
|
+
example(example) {
|
|
17
|
+
this.exampleValue = example;
|
|
18
|
+
return this;
|
|
19
|
+
}
|
|
20
|
+
setExample(example) {
|
|
21
|
+
this.exampleValue = example;
|
|
22
|
+
return this;
|
|
23
|
+
}
|
|
11
24
|
sanitize(...fns) {
|
|
12
25
|
this.sanitizers.push(...fns);
|
|
13
26
|
return this;
|
|
14
27
|
}
|
|
28
|
+
transform(fn) {
|
|
29
|
+
const clone = new TexType(this._raw, this.defaultValue);
|
|
30
|
+
clone.sanitizers = [...this.sanitizers];
|
|
31
|
+
clone.transformations = [...this.transformations, fn];
|
|
32
|
+
clone.refinements = [...this.refinements];
|
|
33
|
+
clone.description = this.description;
|
|
34
|
+
clone.exampleValue = this.exampleValue;
|
|
35
|
+
clone.__kind = 'TexTransformed';
|
|
36
|
+
return clone;
|
|
37
|
+
}
|
|
15
38
|
refine(fn, message) {
|
|
16
39
|
this.refinements.push({ async: false, fn, message });
|
|
17
40
|
return this;
|
|
@@ -32,6 +55,85 @@ class TexType {
|
|
|
32
55
|
this._raw += ' | nullish';
|
|
33
56
|
return this;
|
|
34
57
|
}
|
|
58
|
+
toOpenApi() {
|
|
59
|
+
const raw = this._raw.split('|')[0].trim();
|
|
60
|
+
const isNullable = this._raw.includes('nullable') || this._raw.includes('nullish');
|
|
61
|
+
const base = raw.replace('?', '');
|
|
62
|
+
let schema = { type: 'string' };
|
|
63
|
+
if (base.startsWith('number')) {
|
|
64
|
+
schema = { type: 'number' };
|
|
65
|
+
const minMatch = this._raw.match(/min:(-?\d+(\.\d+)?)/);
|
|
66
|
+
if (minMatch)
|
|
67
|
+
schema.minimum = parseFloat(minMatch[1]);
|
|
68
|
+
const maxMatch = this._raw.match(/max:(-?\d+(\.\d+)?)/);
|
|
69
|
+
if (maxMatch)
|
|
70
|
+
schema.maximum = parseFloat(maxMatch[1]);
|
|
71
|
+
}
|
|
72
|
+
else if (base.startsWith('boolean')) {
|
|
73
|
+
schema = { type: 'boolean' };
|
|
74
|
+
}
|
|
75
|
+
else if (base.startsWith('date')) {
|
|
76
|
+
schema = { type: 'string', format: 'date-time' };
|
|
77
|
+
}
|
|
78
|
+
else if (base.startsWith('email')) {
|
|
79
|
+
schema = { type: 'string', format: 'email' };
|
|
80
|
+
}
|
|
81
|
+
else if (base.startsWith('uuid')) {
|
|
82
|
+
schema = { type: 'string', format: 'uuid' };
|
|
83
|
+
}
|
|
84
|
+
else if (base.startsWith('cuid')) {
|
|
85
|
+
schema = { type: 'string' };
|
|
86
|
+
}
|
|
87
|
+
else if (base.startsWith('password')) {
|
|
88
|
+
schema = { type: 'string', format: 'password' };
|
|
89
|
+
}
|
|
90
|
+
else if (base.startsWith('literal:')) {
|
|
91
|
+
const val = base.replace('literal:', '');
|
|
92
|
+
if (val === 'true' || val === 'false') {
|
|
93
|
+
schema = { type: 'boolean', enum: [val === 'true'] };
|
|
94
|
+
}
|
|
95
|
+
else if (!isNaN(Number(val))) {
|
|
96
|
+
schema = { type: 'number', enum: [Number(val)] };
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
schema = { type: 'string', enum: [val] };
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
else if (base.startsWith('enum:')) {
|
|
103
|
+
const vals = base.replace('enum:', '').split(',');
|
|
104
|
+
schema = { type: 'string', enum: vals };
|
|
105
|
+
}
|
|
106
|
+
else if (base.startsWith('array<')) {
|
|
107
|
+
const inner = this._item;
|
|
108
|
+
const itemSchema = inner && typeof inner.toOpenApi === 'function'
|
|
109
|
+
? inner.toOpenApi()
|
|
110
|
+
: { type: 'string' };
|
|
111
|
+
schema = { type: 'array', items: itemSchema };
|
|
112
|
+
const minMatch = this._raw.match(/min:(\d+)/);
|
|
113
|
+
if (minMatch)
|
|
114
|
+
schema.minItems = parseInt(minMatch[1], 10);
|
|
115
|
+
const maxMatch = this._raw.match(/max:(\d+)/);
|
|
116
|
+
if (maxMatch)
|
|
117
|
+
schema.maxItems = parseInt(maxMatch[1], 10);
|
|
118
|
+
}
|
|
119
|
+
else if (base.startsWith('record<')) {
|
|
120
|
+
schema = { type: 'object', additionalProperties: true };
|
|
121
|
+
}
|
|
122
|
+
else if (base.startsWith('file')) {
|
|
123
|
+
schema = { type: 'string', format: 'binary' };
|
|
124
|
+
}
|
|
125
|
+
if (this.description)
|
|
126
|
+
schema.description = this.description;
|
|
127
|
+
if (this.exampleValue !== undefined)
|
|
128
|
+
schema.example = this.exampleValue;
|
|
129
|
+
if (this.defaultValue !== undefined &&
|
|
130
|
+
typeof this.defaultValue !== 'function') {
|
|
131
|
+
schema.default = this.defaultValue;
|
|
132
|
+
}
|
|
133
|
+
if (isNullable)
|
|
134
|
+
schema.nullable = true;
|
|
135
|
+
return schema;
|
|
136
|
+
}
|
|
35
137
|
}
|
|
36
138
|
exports.TexType = TexType;
|
|
37
139
|
//# sourceMappingURL=tex-types.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tex-types.js","sourceRoot":"","sources":["../../src/validator/tex-types.ts"],"names":[],"mappings":";;;AAAA,MAAa,OAAO;
|
|
1
|
+
{"version":3,"file":"tex-types.js","sourceRoot":"","sources":["../../src/validator/tex-types.ts"],"names":[],"mappings":"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"}
|
package/dist/validator/tex.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { TexBuilder } from './builder';
|
|
2
2
|
export { TexBuilder } from './builder';
|
|
3
3
|
export declare const tex: TexBuilder;
|
|
4
|
-
export { TexEngine } from './engine';
|
|
4
|
+
export { TexEngine, TexTransformedEngine, TexDiscriminatedUnionEngine, } from './engine';
|
|
5
5
|
export { ValidatorError, getNestedValue, formatReceivedValue } from './error';
|
|
6
6
|
export { paginate, getPaginationSkip } from './pagination';
|
|
7
7
|
export type { TexDefaultValue, TexBaseOptions, TexStringOptions, TexNumberOptions, TexBooleanOptions, TexArrayOptions, TexEnumOptions, TexObjectOptions, TexFileOptions, TexPasswordOptions, TexDateOptions, ValidationErrorDescriptor, PaginationMeta, PaginatedResult, } from './types';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tex.d.ts","sourceRoot":"","sources":["../../src/validator/tex.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAEtC,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AACtC,eAAO,MAAM,GAAG,YAAmB,CAAA;AACnC,OAAO,
|
|
1
|
+
{"version":3,"file":"tex.d.ts","sourceRoot":"","sources":["../../src/validator/tex.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAEtC,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AACtC,eAAO,MAAM,GAAG,YAAmB,CAAA;AACnC,OAAO,EACL,SAAS,EACT,oBAAoB,EACpB,2BAA2B,GAC5B,MAAM,UAAU,CAAA;AACjB,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAA;AAC7E,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAC1D,YAAY,EACV,eAAe,EACf,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,gBAAgB,EAChB,cAAc,EACd,kBAAkB,EAClB,cAAc,EACd,yBAAyB,EACzB,cAAc,EACd,eAAe,GAChB,MAAM,SAAS,CAAA"}
|
package/dist/validator/tex.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getPaginationSkip = exports.paginate = exports.formatReceivedValue = exports.getNestedValue = exports.ValidatorError = exports.TexEngine = exports.tex = exports.TexBuilder = void 0;
|
|
3
|
+
exports.getPaginationSkip = exports.paginate = exports.formatReceivedValue = exports.getNestedValue = exports.ValidatorError = exports.TexDiscriminatedUnionEngine = exports.TexTransformedEngine = exports.TexEngine = exports.tex = exports.TexBuilder = void 0;
|
|
4
4
|
const builder_1 = require("./builder");
|
|
5
5
|
var builder_2 = require("./builder");
|
|
6
6
|
Object.defineProperty(exports, "TexBuilder", { enumerable: true, get: function () { return builder_2.TexBuilder; } });
|
|
7
7
|
exports.tex = new builder_1.TexBuilder();
|
|
8
8
|
var engine_1 = require("./engine");
|
|
9
9
|
Object.defineProperty(exports, "TexEngine", { enumerable: true, get: function () { return engine_1.TexEngine; } });
|
|
10
|
+
Object.defineProperty(exports, "TexTransformedEngine", { enumerable: true, get: function () { return engine_1.TexTransformedEngine; } });
|
|
11
|
+
Object.defineProperty(exports, "TexDiscriminatedUnionEngine", { enumerable: true, get: function () { return engine_1.TexDiscriminatedUnionEngine; } });
|
|
10
12
|
var error_1 = require("./error");
|
|
11
13
|
Object.defineProperty(exports, "ValidatorError", { enumerable: true, get: function () { return error_1.ValidatorError; } });
|
|
12
14
|
Object.defineProperty(exports, "getNestedValue", { enumerable: true, get: function () { return error_1.getNestedValue; } });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tex.js","sourceRoot":"","sources":["../../src/validator/tex.ts"],"names":[],"mappings":";;;AAAA,uCAAsC;AAEtC,qCAAsC;AAA7B,qGAAA,UAAU,OAAA;AACN,QAAA,GAAG,GAAG,IAAI,oBAAU,EAAE,CAAA;AACnC,
|
|
1
|
+
{"version":3,"file":"tex.js","sourceRoot":"","sources":["../../src/validator/tex.ts"],"names":[],"mappings":";;;AAAA,uCAAsC;AAEtC,qCAAsC;AAA7B,qGAAA,UAAU,OAAA;AACN,QAAA,GAAG,GAAG,IAAI,oBAAU,EAAE,CAAA;AACnC,mCAIiB;AAHf,mGAAA,SAAS,OAAA;AACT,8GAAA,oBAAoB,OAAA;AACpB,qHAAA,2BAA2B,OAAA;AAE7B,iCAA6E;AAApE,uGAAA,cAAc,OAAA;AAAE,uGAAA,cAAc,OAAA;AAAE,4GAAA,mBAAmB,OAAA;AAC5D,2CAA0D;AAAjD,sGAAA,QAAQ,OAAA;AAAE,+GAAA,iBAAiB,OAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/validator/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAEjD,MAAM,MAAM,eAAe,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAA;AAE9C,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,OAAO,CAAC,EAAE,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/validator/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "exisjs",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.14",
|
|
4
4
|
"description": "efficient full-stack web framework for Node.js",
|
|
5
5
|
"author": "V25 Group <support@v25group.com>",
|
|
6
6
|
"keywords": [
|
|
@@ -88,6 +88,10 @@
|
|
|
88
88
|
"./swagger": {
|
|
89
89
|
"types": "./dist/swagger/index.d.ts",
|
|
90
90
|
"default": "./dist/swagger/index.js"
|
|
91
|
+
},
|
|
92
|
+
"./telemetry": {
|
|
93
|
+
"types": "./dist/telemetry/index.d.ts",
|
|
94
|
+
"default": "./dist/telemetry/index.js"
|
|
91
95
|
}
|
|
92
96
|
},
|
|
93
97
|
"files": [
|
|
@@ -97,7 +101,7 @@
|
|
|
97
101
|
"build": "tsc",
|
|
98
102
|
"postbuild": "node -e \"require('fs').cpSync('../../docs', './dist/docs', {recursive:true, force:true})\"",
|
|
99
103
|
"dev": "tsc --watch",
|
|
100
|
-
"test": "cross-env NODE_ENV=test tsx --test --test-reporter=./src/testing/reporter.mts tests/**/*.test.ts"
|
|
104
|
+
"test": "cross-env NODE_ENV=test tsx --tsconfig ../../tsconfig.base.json --test --test-reporter=./src/testing/reporter.mts tests/**/*.test.ts"
|
|
101
105
|
},
|
|
102
106
|
"bin": {
|
|
103
107
|
"exis": "bin/exis.js",
|
|
@@ -110,7 +114,6 @@
|
|
|
110
114
|
},
|
|
111
115
|
"dependencies": {
|
|
112
116
|
"@exisjs/rs": "*",
|
|
113
|
-
"@exisjs/telemetry": "^0.7.0",
|
|
114
117
|
"accepts": "^1.3.8",
|
|
115
118
|
"busboy": "^1.6.0",
|
|
116
119
|
"chokidar": "^5.0.0",
|
|
@@ -119,34 +122,63 @@
|
|
|
119
122
|
"dotenv": "^17.4.2",
|
|
120
123
|
"dotenv-expand": "^13.0.0",
|
|
121
124
|
"esbuild": "^0.28.2",
|
|
122
|
-
"execa": "^10.0.1",
|
|
123
|
-
"fast-json-stringify": "^7.0.1",
|
|
124
125
|
"fast-querystring": "^1.1.2",
|
|
125
126
|
"fresh": "^0.5.2",
|
|
126
|
-
"fs-extra": "^11.4.0",
|
|
127
127
|
"mime-types": "^3.0.2",
|
|
128
|
-
"ora": "^9.4.1",
|
|
129
128
|
"pino": "^10.3.1",
|
|
130
129
|
"pino-pretty": "^13.1.3",
|
|
131
|
-
"
|
|
132
|
-
"
|
|
133
|
-
|
|
130
|
+
"tsx": "^4.23.15",
|
|
131
|
+
"typescript": "^5.9.3"
|
|
132
|
+
},
|
|
133
|
+
"devDependencies": {
|
|
134
134
|
"@types/accepts": "^1.3.7",
|
|
135
135
|
"@types/busboy": "^1.5.4",
|
|
136
136
|
"@types/content-disposition": "^0.5.9",
|
|
137
137
|
"@types/fresh": "^0.5.3",
|
|
138
138
|
"@types/mime-types": "^3.0.1",
|
|
139
|
-
"@types/node": "^26.4.0"
|
|
140
|
-
},
|
|
141
|
-
"devDependencies": {
|
|
139
|
+
"@types/node": "^26.4.0",
|
|
142
140
|
"@types/autocannon": "^7.12.7",
|
|
143
141
|
"@types/better-sqlite3": "^9.6.0",
|
|
144
142
|
"@types/bun": "^1.4.2",
|
|
145
143
|
"@types/mocha": "^10.0.10",
|
|
144
|
+
"fast-json-stringify": "^7.0.1",
|
|
146
145
|
"find-my-way": "^9.9.0",
|
|
146
|
+
"fs-extra": "^11.4.1",
|
|
147
147
|
"mitata": "^1.0.34"
|
|
148
148
|
},
|
|
149
|
+
"peerDependencies": {
|
|
150
|
+
"@opentelemetry/api": "^1.9.0",
|
|
151
|
+
"@opentelemetry/sdk-node": "^0.222.0",
|
|
152
|
+
"@opentelemetry/auto-instrumentations-node": "^0.80.0",
|
|
153
|
+
"@opentelemetry/exporter-trace-otlp-http": "^0.222.0",
|
|
154
|
+
"@opentelemetry/sdk-trace-node": "^2.11.0",
|
|
155
|
+
"@opentelemetry/resources": "^2.11.0",
|
|
156
|
+
"@opentelemetry/semantic-conventions": "^1.43.0"
|
|
157
|
+
},
|
|
158
|
+
"peerDependenciesMeta": {
|
|
159
|
+
"@opentelemetry/api": {
|
|
160
|
+
"optional": true
|
|
161
|
+
},
|
|
162
|
+
"@opentelemetry/sdk-node": {
|
|
163
|
+
"optional": true
|
|
164
|
+
},
|
|
165
|
+
"@opentelemetry/auto-instrumentations-node": {
|
|
166
|
+
"optional": true
|
|
167
|
+
},
|
|
168
|
+
"@opentelemetry/exporter-trace-otlp-http": {
|
|
169
|
+
"optional": true
|
|
170
|
+
},
|
|
171
|
+
"@opentelemetry/sdk-trace-node": {
|
|
172
|
+
"optional": true
|
|
173
|
+
},
|
|
174
|
+
"@opentelemetry/resources": {
|
|
175
|
+
"optional": true
|
|
176
|
+
},
|
|
177
|
+
"@opentelemetry/semantic-conventions": {
|
|
178
|
+
"optional": true
|
|
179
|
+
}
|
|
180
|
+
},
|
|
149
181
|
"optionalDependencies": {
|
|
150
|
-
"uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.
|
|
182
|
+
"uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.71.0"
|
|
151
183
|
}
|
|
152
184
|
}
|
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
nextUrl: "/docs/security"
|
|
3
|
-
nextTitle: "Security & Auth"
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Dataloader (N+1 Queries)
|
|
7
|
-
|
|
8
|
-
The **N+1 problem** is a common performance issue in backend development. It occurs when your code executes one query to fetch a list of items (e.g. 100 Posts), and then executes $N$ additional queries to fetch related data (e.g. 100 separate queries for each Post's Author).
|
|
9
|
-
|
|
10
|
-
ExisJS ships with a native `Dataloader` implementation (inspired by GraphQL/Facebook's DataLoader) built directly into the core framework. It automatically **batches** and **caches** requests over a single tick of the Node.js event loop.
|
|
11
|
-
|
|
12
|
-
---
|
|
13
|
-
|
|
14
|
-
## 1. Defining Loaders
|
|
15
|
-
|
|
16
|
-
The best practice in ExisJS is to define a central registry of Dataloaders that are **securely scoped to the current HTTP Request** (to prevent data leaking between different users).
|
|
17
|
-
|
|
18
|
-
ExisJS provides `defineLoaders` to create this context-aware registry.
|
|
19
|
-
|
|
20
|
-
```typescript filename="src/loaders.ts"
|
|
21
|
-
import { Dataloader, defineLoaders } from 'exisjs/dataloader'
|
|
22
|
-
import { getRequest } from 'exisjs/router'
|
|
23
|
-
import { db } from './db'
|
|
24
|
-
|
|
25
|
-
export const { loaderMiddleware, getLoaders } = defineLoaders({
|
|
26
|
-
// 1. Author Loader
|
|
27
|
-
authorLoader: () => new Dataloader<number, any>(async (keys) => {
|
|
28
|
-
// You can access the current HTTP Request anywhere using getRequest()
|
|
29
|
-
const req = getRequest()
|
|
30
|
-
const authToken = req.headers.authorization
|
|
31
|
-
|
|
32
|
-
// This function will only be called ONCE per event-loop tick,
|
|
33
|
-
// receiving an array of all requested Author IDs: e.g. [1, 2, 3]
|
|
34
|
-
const authors = await db.authors.findMany({
|
|
35
|
-
where: { id: { in: keys } }
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
// You MUST return an array of results in the EXACT same order as the keys
|
|
39
|
-
return keys.map(key => authors.find(a => a.id === key) || new Error('Not found'))
|
|
40
|
-
}),
|
|
41
|
-
|
|
42
|
-
// 2. Comments Loader
|
|
43
|
-
commentsLoader: () => new Dataloader<number, any[]>(async (keys) => {
|
|
44
|
-
const comments = await db.comments.findMany({
|
|
45
|
-
where: { postId: { in: keys } }
|
|
46
|
-
})
|
|
47
|
-
|
|
48
|
-
// Group comments by postId
|
|
49
|
-
return keys.map(key => comments.filter(c => c.postId === key))
|
|
50
|
-
})
|
|
51
|
-
})
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
---
|
|
55
|
-
|
|
56
|
-
## 2. Registering the Middleware
|
|
57
|
-
|
|
58
|
-
`defineLoaders` returns a `loaderMiddleware`. You must register this middleware so that ExisJS can securely inject a fresh, isolated instance of your Dataloaders into every incoming request.
|
|
59
|
-
|
|
60
|
-
Because ExisJS uses lazy-initialization, there is **zero performance penalty** for routes that don't actually use the loaders.
|
|
61
|
-
|
|
62
|
-
#### Functional Paradigm
|
|
63
|
-
|
|
64
|
-
```typescript filename="src/http/server.ts"
|
|
65
|
-
import { exis } from 'exisjs'
|
|
66
|
-
import { loaderMiddleware } from '../loaders'
|
|
67
|
-
|
|
68
|
-
export default exis({
|
|
69
|
-
async onStart(app) {
|
|
70
|
-
// Register globally for all routes
|
|
71
|
-
app.use(loaderMiddleware)
|
|
72
|
-
}
|
|
73
|
-
})
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
#### Class-Based (OOP) Paradigm
|
|
77
|
-
|
|
78
|
-
```typescript filename="src/http/server.ts"
|
|
79
|
-
import { Server } from 'exisjs/decorators'
|
|
80
|
-
import { loaderMiddleware } from '../loaders'
|
|
81
|
-
import type { App } from 'exisjs'
|
|
82
|
-
|
|
83
|
-
@Server()
|
|
84
|
-
export default class RootServer {
|
|
85
|
-
async onStart(app: App) {
|
|
86
|
-
// Register globally for all routes
|
|
87
|
-
app.use(loaderMiddleware)
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
---
|
|
93
|
-
|
|
94
|
-
## 3. Using Dataloader in Routes
|
|
95
|
-
|
|
96
|
-
Anywhere in your application (Routes, Controllers, Services), you can call `getLoaders()` to retrieve the securely isolated loaders for the *current* request.
|
|
97
|
-
|
|
98
|
-
#### Functional Paradigm
|
|
99
|
-
|
|
100
|
-
```typescript filename="src/http/posts/route.ts"
|
|
101
|
-
import { controller, route } from 'exisjs/router'
|
|
102
|
-
import { getLoaders } from '../../loaders'
|
|
103
|
-
|
|
104
|
-
export default controller({
|
|
105
|
-
list: route.get('/', {
|
|
106
|
-
async handle() {
|
|
107
|
-
// 1. Fetch 100 posts (1 Query)
|
|
108
|
-
const posts = await db.posts.findMany()
|
|
109
|
-
|
|
110
|
-
const { authorLoader } = getLoaders()
|
|
111
|
-
|
|
112
|
-
// 2. Loop through 100 posts and call .load()
|
|
113
|
-
const postsWithAuthors = await Promise.all(
|
|
114
|
-
posts.map(async (post) => {
|
|
115
|
-
// Instead of firing 100 queries to the database,
|
|
116
|
-
// Dataloader queues them up and fires exactly ONE batched query.
|
|
117
|
-
const author = await authorLoader.load(post.authorId)
|
|
118
|
-
return { ...post, author }
|
|
119
|
-
})
|
|
120
|
-
)
|
|
121
|
-
|
|
122
|
-
return postsWithAuthors
|
|
123
|
-
// Total Queries: 2 (Not 101)
|
|
124
|
-
}
|
|
125
|
-
})
|
|
126
|
-
})
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
#### Class-Based (OOP) Paradigm
|
|
130
|
-
|
|
131
|
-
```typescript filename="src/http/posts/route.ts"
|
|
132
|
-
import { Controller, Get } from 'exisjs/decorators'
|
|
133
|
-
import { getLoaders } from '../../loaders'
|
|
134
|
-
|
|
135
|
-
@Controller()
|
|
136
|
-
export default class PostsController {
|
|
137
|
-
@Get('/')
|
|
138
|
-
async list() {
|
|
139
|
-
// 1. Fetch 100 posts (1 Query)
|
|
140
|
-
const posts = await db.posts.findMany()
|
|
141
|
-
|
|
142
|
-
const { authorLoader } = getLoaders()
|
|
143
|
-
|
|
144
|
-
// 2. Loop through 100 posts and call .load()
|
|
145
|
-
const postsWithAuthors = await Promise.all(
|
|
146
|
-
posts.map(async (post) => {
|
|
147
|
-
// Instead of firing 100 queries to the database,
|
|
148
|
-
// Dataloader queues them up and fires exactly ONE batched query.
|
|
149
|
-
const author = await authorLoader.load(post.authorId)
|
|
150
|
-
return { ...post, author }
|
|
151
|
-
})
|
|
152
|
-
)
|
|
153
|
-
|
|
154
|
-
return postsWithAuthors
|
|
155
|
-
// Total Queries: 2 (Not 101)
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
---
|
|
161
|
-
|
|
162
|
-
## API Reference (`exisjs/dataloader`)
|
|
163
|
-
|
|
164
|
-
The `Dataloader` class exposes the standard, battle-tested API:
|
|
165
|
-
|
|
166
|
-
- **`load(key: K): Promise<V>`**: Queues a key to be fetched and returns a Promise.
|
|
167
|
-
- **`loadMany(keys: K[]): Promise<V[]>`**: Equivalent to calling `Promise.all(keys.map(k => load(k)))`.
|
|
168
|
-
- **`clear(key: K): this`**: Clears the cached result for a specific key.
|
|
169
|
-
- **`clearAll(): this`**: Clears the entire cache for this dataloader instance.
|
|
170
|
-
- **`prime(key: K, value: V): this`**: Primes the cache with a predefined value, preventing the batch function from being called for that key.
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: "Data Loader"
|
|
3
|
-
description: "Strict API Reference for the N+1 batching utility."
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Data Loader (`exisjs/dataloader`)
|
|
7
|
-
|
|
8
|
-
This document provides the strict API signatures and TypeScript interfaces exported by the `exisjs/dataloader` module. For high-level guides on solving N+1 query problems, see the [Data Loader Guide](/docs/dataloader).
|
|
9
|
-
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
## `Dataloader<K, V, C>` Class
|
|
13
|
-
|
|
14
|
-
The `Dataloader` class batches and caches requests to a backend data source.
|
|
15
|
-
|
|
16
|
-
```typescript
|
|
17
|
-
import { Dataloader } from 'exisjs/dataloader'
|
|
18
|
-
|
|
19
|
-
class Dataloader<K, V, C = K> {
|
|
20
|
-
constructor(batchFn: BatchLoadFn<K, V>, options?: DataloaderOptions<K, C>)
|
|
21
|
-
|
|
22
|
-
// Enqueues a key to be batched, returning a Promise that resolves to the value
|
|
23
|
-
load(key: K): Promise<V>
|
|
24
|
-
|
|
25
|
-
// Loads multiple keys, returning an array of Promises
|
|
26
|
-
loadMany(keys: readonly K[]): Promise<V[]>
|
|
27
|
-
|
|
28
|
-
// Clears the value at `key` from the cache
|
|
29
|
-
clear(key: K): this
|
|
30
|
-
|
|
31
|
-
// Clears all values from the cache
|
|
32
|
-
clearAll(): this
|
|
33
|
-
|
|
34
|
-
// Primes the cache with a predefined value or Error for a specific key
|
|
35
|
-
prime(key: K, value: V | Error): this
|
|
36
|
-
}
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
### `BatchLoadFn<K, V>`
|
|
40
|
-
|
|
41
|
-
The function responsible for loading the batched keys. **It must return an array of values (or Errors) that are precisely the same length and in the exact same order as the provided keys array.**
|
|
42
|
-
|
|
43
|
-
```typescript
|
|
44
|
-
type BatchLoadFn<K, V> = (
|
|
45
|
-
keys: readonly K[]
|
|
46
|
-
) => Promise<readonly (V | Error)[]>
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
### `DataloaderOptions<K, C>`
|
|
50
|
-
|
|
51
|
-
```typescript
|
|
52
|
-
interface DataloaderOptions<K, C = K> {
|
|
53
|
-
// Whether to cache resolved keys within this Dataloader instance. Default: true
|
|
54
|
-
cache?: boolean
|
|
55
|
-
|
|
56
|
-
// The maximum number of keys to include in a single batch. Default: 1000
|
|
57
|
-
maxBatchSize?: number
|
|
58
|
-
|
|
59
|
-
// A function to generate a unique cache key from the provided load key
|
|
60
|
-
cacheKeyFn?: (key: K) => C
|
|
61
|
-
}
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
---
|
|
65
|
-
|
|
66
|
-
## Context Registry (`defineLoaders`)
|
|
67
|
-
|
|
68
|
-
Because `Dataloader` instances are inherently stateful (they cache data), they **must not** be shared across different HTTP requests, or else users might read each other's cached data.
|
|
69
|
-
|
|
70
|
-
The `defineLoaders` function securely isolates Dataloader instances per-request by binding them to the ExisJS asynchronous context.
|
|
71
|
-
|
|
72
|
-
```typescript
|
|
73
|
-
import { defineLoaders } from 'exisjs/dataloader'
|
|
74
|
-
|
|
75
|
-
function defineLoaders<
|
|
76
|
-
T extends Record<string, () => Dataloader<any, any>>
|
|
77
|
-
>(factories: T): {
|
|
78
|
-
// Middleware to register onto your router/app
|
|
79
|
-
loaderMiddleware: (req, res, next) => void;
|
|
80
|
-
|
|
81
|
-
// Hook to retrieve the isolated loaders anywhere in the request lifecycle
|
|
82
|
-
getLoaders: () => { [K in keyof T]: ReturnType<T[K]> }
|
|
83
|
-
}
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
### Usage Signature
|
|
87
|
-
```typescript
|
|
88
|
-
const { loaderMiddleware, getLoaders } = defineLoaders({
|
|
89
|
-
userLoader: () => new Dataloader(async (keys) => { /* ... */ })
|
|
90
|
-
})
|
|
91
|
-
```
|