@expressots/core 4.0.0-preview.1 → 4.0.0-preview.3
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.md +21 -21
- package/README.md +66 -66
- package/lib/CHANGELOG.md +774 -774
- package/lib/README.md +66 -66
- package/lib/cjs/application/application-factory.js +6 -0
- package/lib/cjs/application/bootstrap.js +117 -213
- package/lib/cjs/config/define-config.js +1 -1
- package/lib/cjs/config/env-field-builders.js +47 -0
- package/lib/cjs/config/index.js +7 -1
- package/lib/cjs/framework-version.js +10 -0
- package/lib/cjs/lazy-loading/index.js +5 -1
- package/lib/cjs/lazy-loading/lazy-module-helpers.js +49 -0
- package/lib/cjs/middleware/index.js +8 -9
- package/lib/cjs/middleware/middleware-service.js +68 -12
- package/lib/cjs/middleware/presets-standalone.js +93 -0
- package/lib/cjs/provider/db-in-memory/adapter/in-memory.adapter.js +23 -0
- package/lib/cjs/provider/db-in-memory/index.js +11 -1
- package/lib/cjs/provider/db-in-memory/query/query-engine.js +28 -0
- package/lib/cjs/provider/db-in-memory/schema/decorators.js +18 -0
- package/lib/cjs/provider/db-in-memory/storage/index.js +3 -1
- package/lib/cjs/provider/db-in-memory/storage/memory-store.js +72 -1
- package/lib/cjs/provider/logger/logger.banner.js +40 -31
- package/lib/cjs/provider/logger/logger.config.js +11 -1
- package/lib/cjs/provider/logger/logger.formatter.js +22 -1
- package/lib/cjs/provider/logger/logger.provider.js +59 -9
- package/lib/cjs/provider/logger/transports/console.transport.js +69 -6
- package/lib/cjs/provider/logger/transports/file.transport.js +27 -18
- package/lib/cjs/provider/logger/utils/log-levels.js +6 -5
- package/lib/cjs/provider/validation/adapters/index.js +12 -5
- package/lib/cjs/provider/validation/adapters/yup.adapter.js +118 -0
- package/lib/cjs/provider/validation/adapters/zod.adapter.js +137 -0
- package/lib/cjs/provider/validation/index.js +5 -1
- package/lib/cjs/render/adapters/react-adapter.js +14 -14
- package/lib/cjs/render/features/type-generator.js +30 -30
- package/lib/cjs/render/features/view-debugger.js +75 -55
- package/lib/cjs/testing/fluent-request.js +7 -0
- package/lib/cjs/testing/snapshot-request.js +2 -0
- package/lib/cjs/types/application/application-factory.d.ts +6 -0
- package/lib/cjs/types/application/bootstrap.d.ts +196 -24
- package/lib/cjs/types/config/config.interfaces.d.ts +7 -1
- package/lib/cjs/types/config/env-field-builders.d.ts +39 -0
- package/lib/cjs/types/config/index.d.ts +1 -1
- package/lib/cjs/types/framework-version.d.ts +7 -0
- package/lib/cjs/types/lazy-loading/index.d.ts +1 -0
- package/lib/cjs/types/lazy-loading/lazy-module-helpers.d.ts +42 -0
- package/lib/cjs/types/middleware/index.d.ts +1 -1
- package/lib/cjs/types/middleware/middleware-service.d.ts +21 -0
- package/lib/cjs/types/middleware/presets-standalone.d.ts +75 -0
- package/lib/cjs/types/provider/db-in-memory/adapter/in-memory.adapter.d.ts +2 -0
- package/lib/cjs/types/provider/db-in-memory/index.d.ts +9 -1
- package/lib/cjs/types/provider/db-in-memory/query/query-engine.d.ts +14 -1
- package/lib/cjs/types/provider/db-in-memory/schema/decorators.d.ts +14 -0
- package/lib/cjs/types/provider/db-in-memory/storage/index.d.ts +1 -1
- package/lib/cjs/types/provider/db-in-memory/storage/memory-store.d.ts +34 -0
- package/lib/cjs/types/provider/logger/logger.banner.d.ts +1 -1
- package/lib/cjs/types/provider/logger/logger.config.d.ts +7 -0
- package/lib/cjs/types/provider/logger/logger.formatter.d.ts +10 -0
- package/lib/cjs/types/provider/logger/logger.provider.d.ts +32 -1
- package/lib/cjs/types/provider/logger/transports/console.transport.d.ts +7 -0
- package/lib/cjs/types/provider/logger/utils/log-levels.d.ts +3 -3
- package/lib/cjs/types/provider/validation/adapters/index.d.ts +7 -4
- package/lib/cjs/types/provider/validation/adapters/yup.adapter.d.ts +65 -0
- package/lib/cjs/types/provider/validation/adapters/zod.adapter.d.ts +84 -0
- package/lib/cjs/types/provider/validation/index.d.ts +1 -1
- package/lib/cjs/types/render/features/view-debugger.d.ts +10 -0
- package/lib/cjs/types/testing/testing.interfaces.d.ts +31 -6
- package/lib/esm/application/application-factory.js +6 -0
- package/lib/esm/application/bootstrap.js +117 -213
- package/lib/esm/config/define-config.js +1 -1
- package/lib/esm/config/env-field-builders.js +48 -0
- package/lib/esm/config/index.js +6 -1
- package/lib/esm/framework-version.js +7 -0
- package/lib/esm/lazy-loading/index.js +2 -0
- package/lib/esm/lazy-loading/lazy-module-helpers.js +45 -0
- package/lib/esm/middleware/index.js +3 -2
- package/lib/esm/middleware/middleware-service.js +68 -12
- package/lib/esm/middleware/presets-standalone.js +87 -0
- package/lib/esm/provider/db-in-memory/adapter/in-memory.adapter.js +23 -0
- package/lib/esm/provider/db-in-memory/index.js +9 -1
- package/lib/esm/provider/db-in-memory/query/query-engine.js +28 -0
- package/lib/esm/provider/db-in-memory/schema/decorators.js +18 -0
- package/lib/esm/provider/db-in-memory/storage/index.js +1 -1
- package/lib/esm/provider/db-in-memory/storage/memory-store.js +75 -0
- package/lib/esm/provider/logger/logger.banner.js +40 -31
- package/lib/esm/provider/logger/logger.config.js +12 -2
- package/lib/esm/provider/logger/logger.formatter.js +22 -1
- package/lib/esm/provider/logger/logger.provider.js +61 -10
- package/lib/esm/provider/logger/transports/console.transport.js +69 -6
- package/lib/esm/provider/logger/transports/file.transport.js +27 -18
- package/lib/esm/provider/logger/utils/log-levels.js +6 -5
- package/lib/esm/provider/validation/adapters/index.js +7 -4
- package/lib/esm/provider/validation/adapters/yup.adapter.js +111 -0
- package/lib/esm/provider/validation/adapters/zod.adapter.js +130 -0
- package/lib/esm/provider/validation/index.js +1 -1
- package/lib/esm/render/adapters/react-adapter.js +14 -14
- package/lib/esm/render/features/type-generator.js +30 -30
- package/lib/esm/render/features/view-debugger.js +75 -55
- package/lib/esm/testing/fluent-request.js +7 -0
- package/lib/esm/testing/snapshot-request.js +2 -0
- package/lib/esm/types/application/application-factory.d.ts +6 -0
- package/lib/esm/types/application/bootstrap.d.ts +196 -24
- package/lib/esm/types/config/config.interfaces.d.ts +7 -1
- package/lib/esm/types/config/env-field-builders.d.ts +39 -0
- package/lib/esm/types/config/index.d.ts +1 -1
- package/lib/esm/types/framework-version.d.ts +7 -0
- package/lib/esm/types/lazy-loading/index.d.ts +1 -0
- package/lib/esm/types/lazy-loading/lazy-module-helpers.d.ts +42 -0
- package/lib/esm/types/middleware/index.d.ts +1 -1
- package/lib/esm/types/middleware/middleware-service.d.ts +21 -0
- package/lib/esm/types/middleware/presets-standalone.d.ts +75 -0
- package/lib/esm/types/provider/db-in-memory/adapter/in-memory.adapter.d.ts +2 -0
- package/lib/esm/types/provider/db-in-memory/index.d.ts +9 -1
- package/lib/esm/types/provider/db-in-memory/query/query-engine.d.ts +14 -1
- package/lib/esm/types/provider/db-in-memory/schema/decorators.d.ts +14 -0
- package/lib/esm/types/provider/db-in-memory/storage/index.d.ts +1 -1
- package/lib/esm/types/provider/db-in-memory/storage/memory-store.d.ts +34 -0
- package/lib/esm/types/provider/logger/logger.banner.d.ts +1 -1
- package/lib/esm/types/provider/logger/logger.config.d.ts +7 -0
- package/lib/esm/types/provider/logger/logger.formatter.d.ts +10 -0
- package/lib/esm/types/provider/logger/logger.provider.d.ts +32 -1
- package/lib/esm/types/provider/logger/transports/console.transport.d.ts +7 -0
- package/lib/esm/types/provider/logger/utils/log-levels.d.ts +3 -3
- package/lib/esm/types/provider/validation/adapters/index.d.ts +7 -4
- package/lib/esm/types/provider/validation/adapters/yup.adapter.d.ts +65 -0
- package/lib/esm/types/provider/validation/adapters/zod.adapter.d.ts +84 -0
- package/lib/esm/types/provider/validation/index.d.ts +1 -1
- package/lib/esm/types/render/features/view-debugger.d.ts +10 -0
- package/lib/esm/types/testing/testing.interfaces.d.ts +31 -6
- package/lib/package.json +23 -8
- package/package.json +23 -8
- package/lib/cjs/middleware/middleware-presets.js +0 -294
- package/lib/cjs/types/middleware/middleware-presets.d.ts +0 -90
- package/lib/esm/middleware/middleware-presets.js +0 -286
- package/lib/esm/types/middleware/middleware-presets.d.ts +0 -90
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Yup Validation Adapter
|
|
4
|
+
* @module @expressots/core/validation
|
|
5
|
+
*
|
|
6
|
+
* Built-in adapter for the [yup](https://github.com/jquense/yup) validation
|
|
7
|
+
* library. `yup` is declared as an *optional* peer dependency: install it
|
|
8
|
+
* explicitly (`npm i yup`) to enable this adapter.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* import * as yup from "yup";
|
|
13
|
+
* import { validationRegistry, createYupValidator } from "@expressots/core";
|
|
14
|
+
*
|
|
15
|
+
* validationRegistry.register(createYupValidator());
|
|
16
|
+
*
|
|
17
|
+
* const CreateUserSchema = yup.object({
|
|
18
|
+
* email: yup.string().email().required(),
|
|
19
|
+
* age: yup.number().integer().min(18).required(),
|
|
20
|
+
* });
|
|
21
|
+
*
|
|
22
|
+
* @controller("/users")
|
|
23
|
+
* class UsersController {
|
|
24
|
+
* @Post("/")
|
|
25
|
+
* create(@body(CreateUserSchema) input: yup.InferType<typeof CreateUserSchema>) {
|
|
26
|
+
* // input is fully typed and validated
|
|
27
|
+
* }
|
|
28
|
+
* }
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
32
|
+
exports.YupValidatorAdapter = void 0;
|
|
33
|
+
exports.createYupValidator = createYupValidator;
|
|
34
|
+
/**
|
|
35
|
+
* Adapter for [yup](https://github.com/jquense/yup). Detects yup schemas at
|
|
36
|
+
* runtime by checking for the `__isYupSchema__` brand or the presence of a
|
|
37
|
+
* `validate` + `cast` method pair.
|
|
38
|
+
*
|
|
39
|
+
* @public API
|
|
40
|
+
*/
|
|
41
|
+
class YupValidatorAdapter {
|
|
42
|
+
constructor() {
|
|
43
|
+
this.name = "yup";
|
|
44
|
+
this.priority = 80;
|
|
45
|
+
}
|
|
46
|
+
canHandle(schema) {
|
|
47
|
+
if (schema === null || typeof schema !== "object")
|
|
48
|
+
return false;
|
|
49
|
+
const candidate = schema;
|
|
50
|
+
if (candidate.__isYupSchema__ === true)
|
|
51
|
+
return true;
|
|
52
|
+
return (typeof candidate.validate === "function" &&
|
|
53
|
+
typeof candidate.cast === "function");
|
|
54
|
+
}
|
|
55
|
+
async validate(data, schema, options) {
|
|
56
|
+
if (typeof schema.validate !== "function") {
|
|
57
|
+
return {
|
|
58
|
+
success: false,
|
|
59
|
+
errors: [
|
|
60
|
+
{
|
|
61
|
+
path: "",
|
|
62
|
+
message: "YupValidatorAdapter received a schema with no validate() method.",
|
|
63
|
+
code: "invalid_schema",
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
const yupOptions = {
|
|
70
|
+
abortEarly: options?.abortEarly ?? false,
|
|
71
|
+
stripUnknown: options?.stripUnknown ?? false,
|
|
72
|
+
};
|
|
73
|
+
const validated = await schema.validate(data, yupOptions);
|
|
74
|
+
return { success: true, data: validated };
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
const yupError = error;
|
|
78
|
+
// yup batches per-field issues into `inner` when abortEarly is false,
|
|
79
|
+
// and emits a single message otherwise.
|
|
80
|
+
const issues = yupError?.inner && yupError.inner.length > 0
|
|
81
|
+
? yupError.inner
|
|
82
|
+
: [yupError];
|
|
83
|
+
return {
|
|
84
|
+
success: false,
|
|
85
|
+
errors: this.mapIssues(issues),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
async transform(data, schema) {
|
|
90
|
+
if (typeof schema.cast === "function") {
|
|
91
|
+
try {
|
|
92
|
+
return schema.cast(data);
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return data;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return data;
|
|
99
|
+
}
|
|
100
|
+
mapIssues(issues) {
|
|
101
|
+
return issues.map((issue) => ({
|
|
102
|
+
path: issue.path ?? "",
|
|
103
|
+
message: issue.message ?? "Validation failed",
|
|
104
|
+
code: issue.type ?? "validation_error",
|
|
105
|
+
received: issue.value,
|
|
106
|
+
}));
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
exports.YupValidatorAdapter = YupValidatorAdapter;
|
|
110
|
+
/**
|
|
111
|
+
* Convenience factory matching the `createXxxValidator` naming used by the
|
|
112
|
+
* other adapters. Equivalent to `new YupValidatorAdapter()`.
|
|
113
|
+
*
|
|
114
|
+
* @public API
|
|
115
|
+
*/
|
|
116
|
+
function createYupValidator() {
|
|
117
|
+
return new YupValidatorAdapter();
|
|
118
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Zod Validation Adapter
|
|
4
|
+
* @module @expressots/core/validation
|
|
5
|
+
*
|
|
6
|
+
* Built-in adapter for the [zod](https://zod.dev) validation library.
|
|
7
|
+
* `zod` is declared as an *optional* peer dependency: install it explicitly
|
|
8
|
+
* (`npm i zod`) to enable this adapter; otherwise validation falls back to
|
|
9
|
+
* the next registered adapter.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* import { z } from "zod";
|
|
14
|
+
* import { validationRegistry, ZodValidatorAdapter, createZodValidator } from "@expressots/core";
|
|
15
|
+
*
|
|
16
|
+
* validationRegistry.register(createZodValidator());
|
|
17
|
+
*
|
|
18
|
+
* const CreateUserSchema = z.object({
|
|
19
|
+
* email: z.string().email(),
|
|
20
|
+
* age: z.number().int().min(18),
|
|
21
|
+
* });
|
|
22
|
+
*
|
|
23
|
+
* @controller("/users")
|
|
24
|
+
* class UsersController {
|
|
25
|
+
* @Post("/")
|
|
26
|
+
* create(@body(CreateUserSchema) input: z.infer<typeof CreateUserSchema>) {
|
|
27
|
+
* // input is fully typed and validated
|
|
28
|
+
* }
|
|
29
|
+
* }
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
33
|
+
exports.ZodValidatorAdapter = void 0;
|
|
34
|
+
exports.createZodValidator = createZodValidator;
|
|
35
|
+
/**
|
|
36
|
+
* Adapter for [zod](https://zod.dev). Picks up any zod schema (`z.object`,
|
|
37
|
+
* `z.string`, ...) at runtime by structural detection — no compile-time
|
|
38
|
+
* dependency on `zod` is required.
|
|
39
|
+
*
|
|
40
|
+
* @public API
|
|
41
|
+
*/
|
|
42
|
+
class ZodValidatorAdapter {
|
|
43
|
+
constructor() {
|
|
44
|
+
this.name = "zod";
|
|
45
|
+
this.priority = 90;
|
|
46
|
+
}
|
|
47
|
+
canHandle(schema) {
|
|
48
|
+
if (schema === null || typeof schema !== "object")
|
|
49
|
+
return false;
|
|
50
|
+
const candidate = schema;
|
|
51
|
+
return (typeof candidate.safeParseAsync === "function" ||
|
|
52
|
+
typeof candidate.safeParse === "function" ||
|
|
53
|
+
typeof candidate.parseAsync === "function" ||
|
|
54
|
+
typeof candidate.parse === "function");
|
|
55
|
+
}
|
|
56
|
+
async validate(data, schema,
|
|
57
|
+
// ValidationOptions accepted for interface compliance; Zod surfaces its own
|
|
58
|
+
// configuration through the schema itself, so options are not consumed here.
|
|
59
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
60
|
+
_options) {
|
|
61
|
+
try {
|
|
62
|
+
// Prefer the safe variants because they encode the failure mode in the
|
|
63
|
+
// result rather than throwing.
|
|
64
|
+
if (typeof schema.safeParseAsync === "function") {
|
|
65
|
+
const r = await schema.safeParseAsync(data);
|
|
66
|
+
return r.success
|
|
67
|
+
? { success: true, data: r.data }
|
|
68
|
+
: { success: false, errors: this.mapIssues(r.error?.issues ?? []) };
|
|
69
|
+
}
|
|
70
|
+
if (typeof schema.safeParse === "function") {
|
|
71
|
+
const r = schema.safeParse(data);
|
|
72
|
+
return r.success
|
|
73
|
+
? { success: true, data: r.data }
|
|
74
|
+
: { success: false, errors: this.mapIssues(r.error?.issues ?? []) };
|
|
75
|
+
}
|
|
76
|
+
if (typeof schema.parseAsync === "function") {
|
|
77
|
+
const parsed = await schema.parseAsync(data);
|
|
78
|
+
return { success: true, data: parsed };
|
|
79
|
+
}
|
|
80
|
+
if (typeof schema.parse === "function") {
|
|
81
|
+
const parsed = schema.parse(data);
|
|
82
|
+
return { success: true, data: parsed };
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
success: false,
|
|
86
|
+
errors: [
|
|
87
|
+
{
|
|
88
|
+
path: "",
|
|
89
|
+
message: "ZodValidatorAdapter received a schema with no parse/safeParse method.",
|
|
90
|
+
code: "invalid_schema",
|
|
91
|
+
},
|
|
92
|
+
],
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
// zod throws ZodError for `parse`/`parseAsync`. Pull issues if present.
|
|
97
|
+
const maybeIssues = error?.issues;
|
|
98
|
+
if (Array.isArray(maybeIssues)) {
|
|
99
|
+
return { success: false, errors: this.mapIssues(maybeIssues) };
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
success: false,
|
|
103
|
+
errors: [
|
|
104
|
+
{
|
|
105
|
+
path: "",
|
|
106
|
+
message: error instanceof Error
|
|
107
|
+
? error.message
|
|
108
|
+
: "Validation failed unexpectedly",
|
|
109
|
+
code: "validation_error",
|
|
110
|
+
},
|
|
111
|
+
],
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async transform(data, schema) {
|
|
116
|
+
const result = await this.validate(data, schema);
|
|
117
|
+
return result.success ? result.data : data;
|
|
118
|
+
}
|
|
119
|
+
mapIssues(issues) {
|
|
120
|
+
return issues.map((issue) => ({
|
|
121
|
+
path: issue.path.join("."),
|
|
122
|
+
message: issue.message,
|
|
123
|
+
code: issue.code ?? "validation_error",
|
|
124
|
+
received: issue.received,
|
|
125
|
+
}));
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
exports.ZodValidatorAdapter = ZodValidatorAdapter;
|
|
129
|
+
/**
|
|
130
|
+
* Convenience factory matching the `createXxxValidator` naming used by the
|
|
131
|
+
* other adapters. Equivalent to `new ZodValidatorAdapter()`.
|
|
132
|
+
*
|
|
133
|
+
* @public API
|
|
134
|
+
*/
|
|
135
|
+
function createZodValidator() {
|
|
136
|
+
return new ZodValidatorAdapter();
|
|
137
|
+
}
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* - Universal parameter validation (@body, @query, @param, @headers)
|
|
14
14
|
*/
|
|
15
15
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
-
exports.ClassValidatorAdapter = exports.detectSchemaType = exports.isClassConstructor = exports.isZodSchema = exports.hasClassValidatorDecorators = exports.getClassProperties = exports.getAllParameterTypes = exports.getParameterType = exports.HelpfulErrorFormatter = exports.SmartFieldDetector = exports.ValidationRegistry = void 0;
|
|
16
|
+
exports.createYupValidator = exports.YupValidatorAdapter = exports.createZodValidator = exports.ZodValidatorAdapter = exports.ClassValidatorAdapter = exports.detectSchemaType = exports.isClassConstructor = exports.isZodSchema = exports.hasClassValidatorDecorators = exports.getClassProperties = exports.getAllParameterTypes = exports.getParameterType = exports.HelpfulErrorFormatter = exports.SmartFieldDetector = exports.ValidationRegistry = void 0;
|
|
17
17
|
// Core components
|
|
18
18
|
var validation_registry_js_1 = require("./validation-registry.js");
|
|
19
19
|
Object.defineProperty(exports, "ValidationRegistry", { enumerable: true, get: function () { return validation_registry_js_1.ValidationRegistry; } });
|
|
@@ -33,3 +33,7 @@ Object.defineProperty(exports, "detectSchemaType", { enumerable: true, get: func
|
|
|
33
33
|
// Built-in adapters
|
|
34
34
|
var index_js_1 = require("./adapters/index.js");
|
|
35
35
|
Object.defineProperty(exports, "ClassValidatorAdapter", { enumerable: true, get: function () { return index_js_1.ClassValidatorAdapter; } });
|
|
36
|
+
Object.defineProperty(exports, "ZodValidatorAdapter", { enumerable: true, get: function () { return index_js_1.ZodValidatorAdapter; } });
|
|
37
|
+
Object.defineProperty(exports, "createZodValidator", { enumerable: true, get: function () { return index_js_1.createZodValidator; } });
|
|
38
|
+
Object.defineProperty(exports, "YupValidatorAdapter", { enumerable: true, get: function () { return index_js_1.YupValidatorAdapter; } });
|
|
39
|
+
Object.defineProperty(exports, "createYupValidator", { enumerable: true, get: function () { return index_js_1.createYupValidator; } });
|
|
@@ -300,20 +300,20 @@ class ReactAdapter extends base_adapter_js_1.BaseEngineAdapter {
|
|
|
300
300
|
.replace(/</g, "\\u003c")
|
|
301
301
|
.replace(/>/g, "\\u003e")
|
|
302
302
|
.replace(/&/g, "\\u0026");
|
|
303
|
-
return `<!DOCTYPE html>
|
|
304
|
-
<html lang="en">
|
|
305
|
-
<head>
|
|
306
|
-
<meta charset="UTF-8">
|
|
307
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
308
|
-
<script>
|
|
309
|
-
window.__INITIAL_DATA__ = ${safeData};
|
|
310
|
-
window.__COMPONENT__ = "${view}";
|
|
311
|
-
</script>
|
|
312
|
-
</head>
|
|
313
|
-
<body>
|
|
314
|
-
<div id="root">${html}</div>
|
|
315
|
-
<script src="/assets/client.js" defer></script>
|
|
316
|
-
</body>
|
|
303
|
+
return `<!DOCTYPE html>
|
|
304
|
+
<html lang="en">
|
|
305
|
+
<head>
|
|
306
|
+
<meta charset="UTF-8">
|
|
307
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
308
|
+
<script>
|
|
309
|
+
window.__INITIAL_DATA__ = ${safeData};
|
|
310
|
+
window.__COMPONENT__ = "${view}";
|
|
311
|
+
</script>
|
|
312
|
+
</head>
|
|
313
|
+
<body>
|
|
314
|
+
<div id="root">${html}</div>
|
|
315
|
+
<script src="/assets/client.js" defer></script>
|
|
316
|
+
</body>
|
|
317
317
|
</html>`;
|
|
318
318
|
}
|
|
319
319
|
/**
|
|
@@ -114,36 +114,36 @@ class TypeGenerator {
|
|
|
114
114
|
const propsType = v.propsType || "Record<string, unknown>";
|
|
115
115
|
return ` "${v.name}": ${propsType};`;
|
|
116
116
|
});
|
|
117
|
-
return `/**
|
|
118
|
-
* Auto-generated view types
|
|
119
|
-
* Generated by ExpressoTS Render Engine
|
|
120
|
-
* Do not edit this file manually
|
|
121
|
-
*/
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* View name to props type mapping
|
|
125
|
-
*/
|
|
126
|
-
export interface Views {
|
|
127
|
-
${viewTypes.join("\n")}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* All view names
|
|
132
|
-
*/
|
|
133
|
-
export type ViewName = keyof Views;
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Get props type for a specific view
|
|
137
|
-
*/
|
|
138
|
-
export type ViewProps<T extends ViewName> = Views[T];
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Type-safe render function signature
|
|
142
|
-
*/
|
|
143
|
-
export type RenderFunction = <T extends ViewName>(
|
|
144
|
-
view: T,
|
|
145
|
-
data: Views[T],
|
|
146
|
-
) => Promise<string>;
|
|
117
|
+
return `/**
|
|
118
|
+
* Auto-generated view types
|
|
119
|
+
* Generated by ExpressoTS Render Engine
|
|
120
|
+
* Do not edit this file manually
|
|
121
|
+
*/
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* View name to props type mapping
|
|
125
|
+
*/
|
|
126
|
+
export interface Views {
|
|
127
|
+
${viewTypes.join("\n")}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* All view names
|
|
132
|
+
*/
|
|
133
|
+
export type ViewName = keyof Views;
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Get props type for a specific view
|
|
137
|
+
*/
|
|
138
|
+
export type ViewProps<T extends ViewName> = Views[T];
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Type-safe render function signature
|
|
142
|
+
*/
|
|
143
|
+
export type RenderFunction = <T extends ViewName>(
|
|
144
|
+
view: T,
|
|
145
|
+
data: Views[T],
|
|
146
|
+
) => Promise<string>;
|
|
147
147
|
`;
|
|
148
148
|
}
|
|
149
149
|
/**
|
|
@@ -29,12 +29,18 @@ class ViewDebugger {
|
|
|
29
29
|
app.get("/__views", (req, res) => {
|
|
30
30
|
this.handleViewsEndpoint(req, res);
|
|
31
31
|
});
|
|
32
|
-
// Preview a view
|
|
33
|
-
|
|
32
|
+
// Preview a view. The view name can contain slashes (e.g.
|
|
33
|
+
// `users/profile`), so we use a named splat (`*view`) — the
|
|
34
|
+
// path-to-regexp v6 form `:view(*)` shipped by Express 4 is not
|
|
35
|
+
// valid in path-to-regexp v8 (Express 5) and now throws
|
|
36
|
+
// `Missing parameter name`. Path-to-regexp v8 captures the splat
|
|
37
|
+
// segments as `req.params.view: string[]`; the helpers below
|
|
38
|
+
// normalise that to a slash-joined string for backward compat.
|
|
39
|
+
app.get("/__views/preview/*view", (req, res) => {
|
|
34
40
|
this.handlePreviewEndpoint(req, res);
|
|
35
41
|
});
|
|
36
|
-
// Get view info
|
|
37
|
-
app.get("/__views/info
|
|
42
|
+
// Get view info — same shape as preview.
|
|
43
|
+
app.get("/__views/info/*view", (req, res) => {
|
|
38
44
|
this.handleInfoEndpoint(req, res);
|
|
39
45
|
});
|
|
40
46
|
// Get metrics
|
|
@@ -73,7 +79,7 @@ class ViewDebugger {
|
|
|
73
79
|
*/
|
|
74
80
|
async handlePreviewEndpoint(req, res) {
|
|
75
81
|
try {
|
|
76
|
-
const view = req.params.view;
|
|
82
|
+
const view = this.resolveViewParam(req.params.view);
|
|
77
83
|
const data = req.query.data ? JSON.parse(req.query.data) : {};
|
|
78
84
|
const html = await this.renderService.render(view, data);
|
|
79
85
|
res.setHeader("Content-Type", "text/html");
|
|
@@ -82,16 +88,30 @@ class ViewDebugger {
|
|
|
82
88
|
catch (error) {
|
|
83
89
|
res.status(500).json({
|
|
84
90
|
error: error.message,
|
|
85
|
-
view: req.params.view,
|
|
91
|
+
view: this.resolveViewParam(req.params.view),
|
|
86
92
|
});
|
|
87
93
|
}
|
|
88
94
|
}
|
|
95
|
+
/**
|
|
96
|
+
* Normalise the `view` route param across Express 4 and 5.
|
|
97
|
+
*
|
|
98
|
+
* Express 4 / path-to-regexp v6 captured `/__views/preview/:view(*)`
|
|
99
|
+
* as a single string. Express 5 / path-to-regexp v8 changed the
|
|
100
|
+
* named-splat capture to `string[]` (one entry per slash-separated
|
|
101
|
+
* segment). The view engine needs the slash-joined form, so we always
|
|
102
|
+
* collapse the array shape back to that.
|
|
103
|
+
*/
|
|
104
|
+
resolveViewParam(value) {
|
|
105
|
+
if (Array.isArray(value))
|
|
106
|
+
return value.join("/");
|
|
107
|
+
return typeof value === "string" ? value : "";
|
|
108
|
+
}
|
|
89
109
|
/**
|
|
90
110
|
* Handle the view info endpoint.
|
|
91
111
|
*/
|
|
92
112
|
handleInfoEndpoint(req, res) {
|
|
93
113
|
try {
|
|
94
|
-
const view = req.params.view;
|
|
114
|
+
const view = this.resolveViewParam(req.params.view);
|
|
95
115
|
const viewFiles = this.renderService.getViewFiles();
|
|
96
116
|
// Find matching view
|
|
97
117
|
const matchingFiles = viewFiles.filter((file) => file.includes(view));
|
|
@@ -120,54 +140,54 @@ class ViewDebugger {
|
|
|
120
140
|
* Wrap preview HTML with debug info.
|
|
121
141
|
*/
|
|
122
142
|
wrapPreview(view, html) {
|
|
123
|
-
return `<!DOCTYPE html>
|
|
124
|
-
<html>
|
|
125
|
-
<head>
|
|
126
|
-
<title>View Preview: ${view}</title>
|
|
127
|
-
<style>
|
|
128
|
-
.debug-bar {
|
|
129
|
-
position: fixed;
|
|
130
|
-
top: 0;
|
|
131
|
-
left: 0;
|
|
132
|
-
right: 0;
|
|
133
|
-
background: #1a1a2e;
|
|
134
|
-
color: #eee;
|
|
135
|
-
padding: 8px 16px;
|
|
136
|
-
font-family: system-ui, -apple-system, sans-serif;
|
|
137
|
-
font-size: 13px;
|
|
138
|
-
z-index: 99999;
|
|
139
|
-
display: flex;
|
|
140
|
-
align-items: center;
|
|
141
|
-
gap: 16px;
|
|
142
|
-
box-shadow: 0 2px 8px rgba(0,0,0,0.2);
|
|
143
|
-
}
|
|
144
|
-
.debug-bar strong {
|
|
145
|
-
color: #00d9ff;
|
|
146
|
-
}
|
|
147
|
-
.debug-bar a {
|
|
148
|
-
color: #ff6b6b;
|
|
149
|
-
text-decoration: none;
|
|
150
|
-
}
|
|
151
|
-
.debug-bar a:hover {
|
|
152
|
-
text-decoration: underline;
|
|
153
|
-
}
|
|
154
|
-
.content-wrapper {
|
|
155
|
-
padding-top: 50px;
|
|
156
|
-
}
|
|
157
|
-
</style>
|
|
158
|
-
</head>
|
|
159
|
-
<body>
|
|
160
|
-
<div class="debug-bar">
|
|
161
|
-
<span>📁 <strong>${view}</strong></span>
|
|
162
|
-
<span>|</span>
|
|
163
|
-
<span>Engine: <strong>${this.renderService.getActiveEngine().name}</strong></span>
|
|
164
|
-
<span>|</span>
|
|
165
|
-
<a href="/__views">← Back to Views</a>
|
|
166
|
-
</div>
|
|
167
|
-
<div class="content-wrapper">
|
|
168
|
-
${html}
|
|
169
|
-
</div>
|
|
170
|
-
</body>
|
|
143
|
+
return `<!DOCTYPE html>
|
|
144
|
+
<html>
|
|
145
|
+
<head>
|
|
146
|
+
<title>View Preview: ${view}</title>
|
|
147
|
+
<style>
|
|
148
|
+
.debug-bar {
|
|
149
|
+
position: fixed;
|
|
150
|
+
top: 0;
|
|
151
|
+
left: 0;
|
|
152
|
+
right: 0;
|
|
153
|
+
background: #1a1a2e;
|
|
154
|
+
color: #eee;
|
|
155
|
+
padding: 8px 16px;
|
|
156
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
157
|
+
font-size: 13px;
|
|
158
|
+
z-index: 99999;
|
|
159
|
+
display: flex;
|
|
160
|
+
align-items: center;
|
|
161
|
+
gap: 16px;
|
|
162
|
+
box-shadow: 0 2px 8px rgba(0,0,0,0.2);
|
|
163
|
+
}
|
|
164
|
+
.debug-bar strong {
|
|
165
|
+
color: #00d9ff;
|
|
166
|
+
}
|
|
167
|
+
.debug-bar a {
|
|
168
|
+
color: #ff6b6b;
|
|
169
|
+
text-decoration: none;
|
|
170
|
+
}
|
|
171
|
+
.debug-bar a:hover {
|
|
172
|
+
text-decoration: underline;
|
|
173
|
+
}
|
|
174
|
+
.content-wrapper {
|
|
175
|
+
padding-top: 50px;
|
|
176
|
+
}
|
|
177
|
+
</style>
|
|
178
|
+
</head>
|
|
179
|
+
<body>
|
|
180
|
+
<div class="debug-bar">
|
|
181
|
+
<span>📁 <strong>${view}</strong></span>
|
|
182
|
+
<span>|</span>
|
|
183
|
+
<span>Engine: <strong>${this.renderService.getActiveEngine().name}</strong></span>
|
|
184
|
+
<span>|</span>
|
|
185
|
+
<a href="/__views">← Back to Views</a>
|
|
186
|
+
</div>
|
|
187
|
+
<div class="content-wrapper">
|
|
188
|
+
${html}
|
|
189
|
+
</div>
|
|
190
|
+
</body>
|
|
171
191
|
</html>`;
|
|
172
192
|
}
|
|
173
193
|
}
|
|
@@ -233,7 +233,13 @@ class FluentRequestImpl {
|
|
|
233
233
|
}
|
|
234
234
|
/**
|
|
235
235
|
* Execute the request and return the response.
|
|
236
|
+
*
|
|
237
|
+
* The generic defaults to `any` (see {@link FluentResponse} for the
|
|
238
|
+
* rationale) so test code can read `response.body.foo` directly. Pass
|
|
239
|
+
* an explicit type — `.execute<UserDto>()` — to opt into strict
|
|
240
|
+
* typing.
|
|
236
241
|
*/
|
|
242
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
237
243
|
async execute() {
|
|
238
244
|
const startTime = Date.now();
|
|
239
245
|
// Build URL with query parameters
|
|
@@ -317,6 +323,7 @@ class FluentRequestImpl {
|
|
|
317
323
|
/**
|
|
318
324
|
* Alias for execute().
|
|
319
325
|
*/
|
|
326
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
320
327
|
end() {
|
|
321
328
|
return this.execute();
|
|
322
329
|
}
|
|
@@ -120,9 +120,11 @@ class SnapshotRequestImpl {
|
|
|
120
120
|
this.fluentRequest.expect(assertion);
|
|
121
121
|
return this;
|
|
122
122
|
}
|
|
123
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
123
124
|
execute() {
|
|
124
125
|
return this.fluentRequest.execute();
|
|
125
126
|
}
|
|
127
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
126
128
|
end() {
|
|
127
129
|
return this.fluentRequest.end();
|
|
128
130
|
}
|
|
@@ -112,6 +112,12 @@ export declare class AppFactory {
|
|
|
112
112
|
*
|
|
113
113
|
* @throws {Error} If webServerType is not a valid constructor
|
|
114
114
|
*
|
|
115
|
+
* @deprecated Use `bootstrap()` from `@expressots/core` instead. `bootstrap()`
|
|
116
|
+
* additionally handles environment file loading, port configuration,
|
|
117
|
+
* graceful shutdown, the startup banner, and configuration validation.
|
|
118
|
+
* `AppFactory.create()` will keep working for advanced use cases but is
|
|
119
|
+
* scheduled for removal in a future major release.
|
|
120
|
+
*
|
|
115
121
|
* @public API
|
|
116
122
|
*/
|
|
117
123
|
static create<T extends IWebServer>(webServerType: IWebServerConstructor<T>): Promise<IWebServerBuilder>;
|