@venizia/ignis-kernel 0.4.0 → 0.4.2
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/dist/cjs/base/applications/abstract.js +2 -2
- package/dist/cjs/base/applications/abstract.js.map +1 -1
- package/dist/cjs/base/applications/artifact-group.js +1 -1
- package/dist/cjs/base/applications/artifact-group.js.map +1 -1
- package/dist/cjs/base/applications/artifact-index.js +11 -11
- package/dist/cjs/base/applications/artifact-index.js.map +1 -1
- package/dist/cjs/base/applications/boot-sequence.js +1 -1
- package/dist/cjs/base/applications/boot-sequence.js.map +1 -1
- package/dist/cjs/base/applications/cors.d.ts +13 -0
- package/dist/cjs/base/applications/cors.d.ts.map +1 -0
- package/dist/cjs/base/applications/cors.js +43 -0
- package/dist/cjs/base/applications/cors.js.map +1 -0
- package/dist/cjs/base/applications/deprecations.d.ts +10 -0
- package/dist/cjs/base/applications/deprecations.d.ts.map +1 -0
- package/dist/cjs/base/applications/deprecations.js +23 -0
- package/dist/cjs/base/applications/deprecations.js.map +1 -0
- package/dist/cjs/base/applications/rest.d.ts.map +1 -1
- package/dist/cjs/base/applications/rest.js +11 -5
- package/dist/cjs/base/applications/rest.js.map +1 -1
- package/dist/cjs/base/auth/authenticate/common/codecs.js +2 -2
- package/dist/cjs/base/auth/authenticate/common/codecs.js.map +1 -1
- package/dist/cjs/base/auth/authenticate/providers/authentication.provider.js +1 -1
- package/dist/cjs/base/auth/authenticate/providers/authentication.provider.js.map +1 -1
- package/dist/cjs/base/auth/authenticate/strategies/strategy-registry.js +1 -1
- package/dist/cjs/base/auth/authenticate/strategies/strategy-registry.js.map +1 -1
- package/dist/cjs/base/auth/authorize/builders/grant.builder.js +12 -12
- package/dist/cjs/base/auth/authorize/builders/grant.builder.js.map +1 -1
- package/dist/cjs/base/auth/authorize/builders/permission.builder.js +1 -1
- package/dist/cjs/base/auth/authorize/builders/permission.builder.js.map +1 -1
- package/dist/cjs/base/auth/authorize/builders/policy.builder.js +1 -1
- package/dist/cjs/base/auth/authorize/builders/policy.builder.js.map +1 -1
- package/dist/cjs/base/auth/authorize/common/constants/authorization.d.ts.map +1 -1
- package/dist/cjs/base/auth/authorize/common/constants/authorization.js +2 -2
- package/dist/cjs/base/auth/authorize/common/constants/authorization.js.map +1 -1
- package/dist/cjs/base/auth/authorize/enforcers/enforcer-registry.js +1 -1
- package/dist/cjs/base/auth/authorize/enforcers/enforcer-registry.js.map +1 -1
- package/dist/cjs/base/auth/authorize/providers/authorization.provider.js +3 -3
- package/dist/cjs/base/auth/authorize/providers/authorization.provider.js.map +1 -1
- package/dist/cjs/base/controllers/factory/authorize-guard.d.ts +19 -0
- package/dist/cjs/base/controllers/factory/authorize-guard.d.ts.map +1 -0
- package/dist/cjs/base/controllers/factory/authorize-guard.js +60 -0
- package/dist/cjs/base/controllers/factory/authorize-guard.js.map +1 -0
- package/dist/cjs/base/controllers/factory/controller.d.ts +2 -2
- package/dist/cjs/base/controllers/factory/controller.d.ts.map +1 -1
- package/dist/cjs/base/controllers/factory/controller.js +9 -0
- package/dist/cjs/base/controllers/factory/controller.js.map +1 -1
- package/dist/cjs/base/controllers/factory/crud/abstract.d.ts +1 -1
- package/dist/cjs/base/controllers/factory/crud/abstract.d.ts.map +1 -1
- package/dist/cjs/base/controllers/factory/crud/abstract.js.map +1 -1
- package/dist/cjs/base/controllers/factory/definition.js +1 -1
- package/dist/cjs/base/controllers/factory/definition.js.map +1 -1
- package/dist/cjs/base/controllers/rest/extras.js +5 -5
- package/dist/cjs/base/controllers/rest/extras.js.map +1 -1
- package/dist/cjs/base/metadata/injectable.js +1 -1
- package/dist/cjs/base/metadata/injectable.js.map +1 -1
- package/dist/cjs/base/metadata/model-settings.d.ts +21 -0
- package/dist/cjs/base/metadata/model-settings.d.ts.map +1 -0
- package/dist/cjs/base/metadata/model-settings.js +77 -0
- package/dist/cjs/base/metadata/model-settings.js.map +1 -0
- package/dist/cjs/base/metadata/persistents.d.ts +1 -1
- package/dist/cjs/base/metadata/persistents.d.ts.map +1 -1
- package/dist/cjs/base/metadata/persistents.js +24 -12
- package/dist/cjs/base/metadata/persistents.js.map +1 -1
- package/dist/cjs/base/metadata/routes/controller.js +1 -1
- package/dist/cjs/base/metadata/routes/controller.js.map +1 -1
- package/dist/cjs/base/middlewares/app-error/app-error.middleware.js +3 -3
- package/dist/cjs/base/middlewares/app-error/app-error.middleware.js.map +1 -1
- package/dist/cjs/base/middlewares/app-error/zod.handler.js +2 -2
- package/dist/cjs/base/middlewares/app-error/zod.handler.js.map +1 -1
- package/dist/cjs/base/middlewares/form-body/form-body.middleware.js +2 -2
- package/dist/cjs/base/middlewares/form-body/form-body.middleware.js.map +1 -1
- package/dist/cjs/base/middlewares/not-found/not-found.middleware.js.map +1 -1
- package/dist/cjs/base/models/common/utilities.js +1 -1
- package/dist/cjs/base/models/common/utilities.js.map +1 -1
- package/dist/cjs/base/repositories/common/constants.d.ts +18 -1
- package/dist/cjs/base/repositories/common/constants.d.ts.map +1 -1
- package/dist/cjs/base/repositories/common/constants.js +22 -2
- package/dist/cjs/base/repositories/common/constants.js.map +1 -1
- package/dist/cjs/base/repositories/common/types/contracts.d.ts +17 -17
- package/dist/cjs/base/repositories/common/types/contracts.d.ts.map +1 -1
- package/dist/cjs/base/repositories/common/types/contracts.js.map +1 -1
- package/dist/cjs/base/repositories/core/abstract.d.ts +18 -18
- package/dist/cjs/base/repositories/core/abstract.d.ts.map +1 -1
- package/dist/cjs/base/repositories/core/abstract.js +8 -7
- package/dist/cjs/base/repositories/core/abstract.js.map +1 -1
- package/dist/cjs/helpers/inversion/common/types/model.d.ts +94 -22
- package/dist/cjs/helpers/inversion/common/types/model.d.ts.map +1 -1
- package/dist/cjs/helpers/inversion/common/types/model.js.map +1 -1
- package/dist/cjs/helpers/inversion/mixins/model.mixin.js +1 -1
- package/dist/cjs/helpers/inversion/mixins/model.mixin.js.map +1 -1
- package/dist/cjs/helpers/inversion/mixins/repository.mixin.js +1 -1
- package/dist/cjs/helpers/inversion/mixins/repository.mixin.js.map +1 -1
- package/dist/cjs/repository.d.ts +1 -1
- package/dist/cjs/repository.d.ts.map +1 -1
- package/dist/cjs/repository.js.map +1 -1
- package/dist/esm/base/applications/abstract.js +2 -2
- package/dist/esm/base/applications/abstract.js.map +1 -1
- package/dist/esm/base/applications/artifact-group.js +1 -1
- package/dist/esm/base/applications/artifact-group.js.map +1 -1
- package/dist/esm/base/applications/artifact-index.js +11 -11
- package/dist/esm/base/applications/artifact-index.js.map +1 -1
- package/dist/esm/base/applications/boot-sequence.js +1 -1
- package/dist/esm/base/applications/boot-sequence.js.map +1 -1
- package/dist/esm/base/applications/cors.d.ts +13 -0
- package/dist/esm/base/applications/cors.d.ts.map +1 -0
- package/dist/esm/base/applications/cors.js +39 -0
- package/dist/esm/base/applications/cors.js.map +1 -0
- package/dist/esm/base/applications/deprecations.d.ts +10 -0
- package/dist/esm/base/applications/deprecations.d.ts.map +1 -0
- package/dist/esm/base/applications/deprecations.js +19 -0
- package/dist/esm/base/applications/deprecations.js.map +1 -0
- package/dist/esm/base/applications/rest.d.ts.map +1 -1
- package/dist/esm/base/applications/rest.js +11 -5
- package/dist/esm/base/applications/rest.js.map +1 -1
- package/dist/esm/base/auth/authenticate/common/codecs.js +2 -2
- package/dist/esm/base/auth/authenticate/common/codecs.js.map +1 -1
- package/dist/esm/base/auth/authenticate/providers/authentication.provider.js +1 -1
- package/dist/esm/base/auth/authenticate/providers/authentication.provider.js.map +1 -1
- package/dist/esm/base/auth/authenticate/strategies/strategy-registry.js +1 -1
- package/dist/esm/base/auth/authenticate/strategies/strategy-registry.js.map +1 -1
- package/dist/esm/base/auth/authorize/builders/grant.builder.js +12 -12
- package/dist/esm/base/auth/authorize/builders/grant.builder.js.map +1 -1
- package/dist/esm/base/auth/authorize/builders/permission.builder.js +1 -1
- package/dist/esm/base/auth/authorize/builders/permission.builder.js.map +1 -1
- package/dist/esm/base/auth/authorize/builders/policy.builder.js +1 -1
- package/dist/esm/base/auth/authorize/builders/policy.builder.js.map +1 -1
- package/dist/esm/base/auth/authorize/common/constants/authorization.d.ts.map +1 -1
- package/dist/esm/base/auth/authorize/common/constants/authorization.js +2 -2
- package/dist/esm/base/auth/authorize/common/constants/authorization.js.map +1 -1
- package/dist/esm/base/auth/authorize/enforcers/enforcer-registry.js +1 -1
- package/dist/esm/base/auth/authorize/enforcers/enforcer-registry.js.map +1 -1
- package/dist/esm/base/auth/authorize/providers/authorization.provider.js +3 -3
- package/dist/esm/base/auth/authorize/providers/authorization.provider.js.map +1 -1
- package/dist/esm/base/controllers/factory/authorize-guard.d.ts +19 -0
- package/dist/esm/base/controllers/factory/authorize-guard.d.ts.map +1 -0
- package/dist/esm/base/controllers/factory/authorize-guard.js +56 -0
- package/dist/esm/base/controllers/factory/authorize-guard.js.map +1 -0
- package/dist/esm/base/controllers/factory/controller.d.ts +2 -2
- package/dist/esm/base/controllers/factory/controller.d.ts.map +1 -1
- package/dist/esm/base/controllers/factory/controller.js +9 -0
- package/dist/esm/base/controllers/factory/controller.js.map +1 -1
- package/dist/esm/base/controllers/factory/crud/abstract.d.ts +1 -1
- package/dist/esm/base/controllers/factory/crud/abstract.d.ts.map +1 -1
- package/dist/esm/base/controllers/factory/crud/abstract.js.map +1 -1
- package/dist/esm/base/controllers/factory/definition.js +1 -1
- package/dist/esm/base/controllers/factory/definition.js.map +1 -1
- package/dist/esm/base/controllers/rest/extras.js +5 -5
- package/dist/esm/base/controllers/rest/extras.js.map +1 -1
- package/dist/esm/base/metadata/injectable.js +1 -1
- package/dist/esm/base/metadata/injectable.js.map +1 -1
- package/dist/esm/base/metadata/model-settings.d.ts +21 -0
- package/dist/esm/base/metadata/model-settings.d.ts.map +1 -0
- package/dist/esm/base/metadata/model-settings.js +73 -0
- package/dist/esm/base/metadata/model-settings.js.map +1 -0
- package/dist/esm/base/metadata/persistents.d.ts +1 -1
- package/dist/esm/base/metadata/persistents.d.ts.map +1 -1
- package/dist/esm/base/metadata/persistents.js +24 -12
- package/dist/esm/base/metadata/persistents.js.map +1 -1
- package/dist/esm/base/metadata/routes/controller.js +1 -1
- package/dist/esm/base/metadata/routes/controller.js.map +1 -1
- package/dist/esm/base/middlewares/app-error/app-error.middleware.js +3 -3
- package/dist/esm/base/middlewares/app-error/app-error.middleware.js.map +1 -1
- package/dist/esm/base/middlewares/app-error/zod.handler.js +2 -2
- package/dist/esm/base/middlewares/app-error/zod.handler.js.map +1 -1
- package/dist/esm/base/middlewares/form-body/form-body.middleware.js +2 -2
- package/dist/esm/base/middlewares/form-body/form-body.middleware.js.map +1 -1
- package/dist/esm/base/middlewares/not-found/not-found.middleware.js.map +1 -1
- package/dist/esm/base/models/common/utilities.js +1 -1
- package/dist/esm/base/models/common/utilities.js.map +1 -1
- package/dist/esm/base/repositories/common/constants.d.ts +18 -1
- package/dist/esm/base/repositories/common/constants.d.ts.map +1 -1
- package/dist/esm/base/repositories/common/constants.js +20 -1
- package/dist/esm/base/repositories/common/constants.js.map +1 -1
- package/dist/esm/base/repositories/common/types/contracts.d.ts +17 -17
- package/dist/esm/base/repositories/common/types/contracts.d.ts.map +1 -1
- package/dist/esm/base/repositories/common/types/contracts.js.map +1 -1
- package/dist/esm/base/repositories/core/abstract.d.ts +18 -18
- package/dist/esm/base/repositories/core/abstract.d.ts.map +1 -1
- package/dist/esm/base/repositories/core/abstract.js +8 -7
- package/dist/esm/base/repositories/core/abstract.js.map +1 -1
- package/dist/esm/helpers/inversion/common/types/model.d.ts +94 -22
- package/dist/esm/helpers/inversion/common/types/model.d.ts.map +1 -1
- package/dist/esm/helpers/inversion/common/types/model.js.map +1 -1
- package/dist/esm/helpers/inversion/mixins/model.mixin.js +1 -1
- package/dist/esm/helpers/inversion/mixins/model.mixin.js.map +1 -1
- package/dist/esm/helpers/inversion/mixins/repository.mixin.js +1 -1
- package/dist/esm/helpers/inversion/mixins/repository.mixin.js.map +1 -1
- package/dist/esm/repository.d.ts +1 -1
- package/dist/esm/repository.d.ts.map +1 -1
- package/dist/esm/repository.js.map +1 -1
- package/package.json +6 -6
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { IDeprecatedModelSetting, IModelSettings } from '../../helpers/inversion/index.js';
|
|
2
|
+
/** The release that drops the deprecated keys. */
|
|
3
|
+
export declare const DEPRECATED_MODEL_SETTINGS_REMOVED_IN = "0.4.3";
|
|
4
|
+
/**
|
|
5
|
+
* The one place the old flat settings become the new grouped ones. Everything that reads model
|
|
6
|
+
* settings reads the result, so no reader knows two shapes.
|
|
7
|
+
*
|
|
8
|
+
* - `defaultFilter` -> `query.filter`; `defaultLimit` -> `query.limit.default`; `maxLimit` ->
|
|
9
|
+
* `query.limit.max`; `scopeFilter` -> `access` (`writeGuard` -> `access.write.mode`, `sharedRows`
|
|
10
|
+
* -> `access.write.sharedRows`).
|
|
11
|
+
* - An old key together with the key that replaces it is an error.
|
|
12
|
+
* - Returns the old keys in use so the application can warn once at boot.
|
|
13
|
+
*/
|
|
14
|
+
export declare const normalizeModelSettings: (opts: {
|
|
15
|
+
model: string;
|
|
16
|
+
settings: IModelSettings | undefined;
|
|
17
|
+
}) => {
|
|
18
|
+
settings: IModelSettings | undefined;
|
|
19
|
+
deprecated: IDeprecatedModelSetting[];
|
|
20
|
+
};
|
|
21
|
+
//# sourceMappingURL=model-settings.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-settings.d.ts","sourceRoot":"","sources":["../../../../src/base/metadata/model-settings.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,uBAAuB,EAEvB,cAAc,EACf,MAAM,qBAAqB,CAAC;AAE7B,kDAAkD;AAClD,eAAO,MAAM,oCAAoC,UAAU,CAAC;AAQ5D;;;;;;;;;GASG;AACH,eAAO,MAAM,sBAAsB,GAAI,MAAM;IAC3C,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,cAAc,GAAG,SAAS,CAAC;CACtC,KAAG;IAAE,QAAQ,EAAE,cAAc,GAAG,SAAS,CAAC;IAAC,UAAU,EAAE,uBAAuB,EAAE,CAAA;CAmEhF,CAAC"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { getError } from '@venizia/ignis-helpers/core';
|
|
2
|
+
/** The release that drops the deprecated keys. */
|
|
3
|
+
export const DEPRECATED_MODEL_SETTINGS_REMOVED_IN = '0.4.3';
|
|
4
|
+
const conflict = (opts) => {
|
|
5
|
+
return getError({
|
|
6
|
+
message: `[model][${opts.model}] settings.${opts.old} and settings.${opts.use} are both set | ${opts.old} is the deprecated name of ${opts.use} | keep ${opts.use} and remove ${opts.old}`,
|
|
7
|
+
});
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* The one place the old flat settings become the new grouped ones. Everything that reads model
|
|
11
|
+
* settings reads the result, so no reader knows two shapes.
|
|
12
|
+
*
|
|
13
|
+
* - `defaultFilter` -> `query.filter`; `defaultLimit` -> `query.limit.default`; `maxLimit` ->
|
|
14
|
+
* `query.limit.max`; `scopeFilter` -> `access` (`writeGuard` -> `access.write.mode`, `sharedRows`
|
|
15
|
+
* -> `access.write.sharedRows`).
|
|
16
|
+
* - An old key together with the key that replaces it is an error.
|
|
17
|
+
* - Returns the old keys in use so the application can warn once at boot.
|
|
18
|
+
*/
|
|
19
|
+
export const normalizeModelSettings = (opts) => {
|
|
20
|
+
const { model, settings } = opts;
|
|
21
|
+
if (!settings) {
|
|
22
|
+
return { settings, deprecated: [] };
|
|
23
|
+
}
|
|
24
|
+
const { defaultFilter, defaultLimit, maxLimit, scopeFilter, ...current } = settings;
|
|
25
|
+
const deprecated = [];
|
|
26
|
+
const normalized = { ...current };
|
|
27
|
+
if (defaultFilter !== undefined) {
|
|
28
|
+
if (current.query?.filter !== undefined) {
|
|
29
|
+
throw conflict({ model, old: 'defaultFilter', use: 'query.filter' });
|
|
30
|
+
}
|
|
31
|
+
normalized.query = { ...normalized.query, filter: defaultFilter };
|
|
32
|
+
deprecated.push({ key: 'defaultFilter', use: 'query.filter' });
|
|
33
|
+
}
|
|
34
|
+
if (defaultLimit !== undefined) {
|
|
35
|
+
if (current.query?.limit?.default !== undefined) {
|
|
36
|
+
throw conflict({ model, old: 'defaultLimit', use: 'query.limit.default' });
|
|
37
|
+
}
|
|
38
|
+
normalized.query = {
|
|
39
|
+
...normalized.query,
|
|
40
|
+
limit: { ...normalized.query?.limit, default: defaultLimit },
|
|
41
|
+
};
|
|
42
|
+
deprecated.push({ key: 'defaultLimit', use: 'query.limit.default' });
|
|
43
|
+
}
|
|
44
|
+
if (maxLimit !== undefined) {
|
|
45
|
+
if (current.query?.limit?.max !== undefined) {
|
|
46
|
+
throw conflict({ model, old: 'maxLimit', use: 'query.limit.max' });
|
|
47
|
+
}
|
|
48
|
+
normalized.query = {
|
|
49
|
+
...normalized.query,
|
|
50
|
+
limit: { ...normalized.query?.limit, max: maxLimit },
|
|
51
|
+
};
|
|
52
|
+
deprecated.push({ key: 'maxLimit', use: 'query.limit.max' });
|
|
53
|
+
}
|
|
54
|
+
if (scopeFilter !== undefined) {
|
|
55
|
+
if (current.access !== undefined) {
|
|
56
|
+
throw conflict({ model, old: 'scopeFilter', use: 'access' });
|
|
57
|
+
}
|
|
58
|
+
const { writeGuard, sharedRows, ...rest } = scopeFilter;
|
|
59
|
+
const access = { ...rest };
|
|
60
|
+
deprecated.push({ key: 'scopeFilter', use: 'access' });
|
|
61
|
+
if (writeGuard !== undefined) {
|
|
62
|
+
access.write = { ...access.write, mode: writeGuard };
|
|
63
|
+
deprecated.push({ key: 'scopeFilter.writeGuard', use: 'access.write.mode' });
|
|
64
|
+
}
|
|
65
|
+
if (sharedRows !== undefined) {
|
|
66
|
+
access.write = { ...access.write, sharedRows };
|
|
67
|
+
deprecated.push({ key: 'scopeFilter.sharedRows', use: 'access.write.sharedRows' });
|
|
68
|
+
}
|
|
69
|
+
normalized.access = access;
|
|
70
|
+
}
|
|
71
|
+
return { settings: normalized, deprecated };
|
|
72
|
+
};
|
|
73
|
+
//# sourceMappingURL=model-settings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-settings.js","sourceRoot":"","sources":["../../../../src/base/metadata/model-settings.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAOvD,kDAAkD;AAClD,MAAM,CAAC,MAAM,oCAAoC,GAAG,OAAO,CAAC;AAE5D,MAAM,QAAQ,GAAG,CAAC,IAAiD,EAAE,EAAE;IACrE,OAAO,QAAQ,CAAC;QACd,OAAO,EAAE,WAAW,IAAI,CAAC,KAAK,cAAc,IAAI,CAAC,GAAG,iBAAiB,IAAI,CAAC,GAAG,mBAAmB,IAAI,CAAC,GAAG,8BAA8B,IAAI,CAAC,GAAG,WAAW,IAAI,CAAC,GAAG,eAAe,IAAI,CAAC,GAAG,EAAE;KAC3L,CAAC,CAAC;AACL,CAAC,CAAC;AAEF;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,IAGtC,EAAmF,EAAE;IACpF,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC;IAEjC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC;IACtC,CAAC;IAED,MAAM,EAAE,aAAa,EAAE,YAAY,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,OAAO,EAAE,GAAG,QAAQ,CAAC;IACpF,MAAM,UAAU,GAA8B,EAAE,CAAC;IACjD,MAAM,UAAU,GAAmB,EAAE,GAAG,OAAO,EAAE,CAAC;IAElD,IAAI,aAAa,KAAK,SAAS,EAAE,CAAC;QAChC,IAAI,OAAO,CAAC,KAAK,EAAE,MAAM,KAAK,SAAS,EAAE,CAAC;YACxC,MAAM,QAAQ,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,cAAc,EAAE,CAAC,CAAC;QACvE,CAAC;QAED,UAAU,CAAC,KAAK,GAAG,EAAE,GAAG,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;QAClE,UAAU,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,cAAc,EAAE,CAAC,CAAC;IACjE,CAAC;IAED,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;QAC/B,IAAI,OAAO,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,KAAK,SAAS,EAAE,CAAC;YAChD,MAAM,QAAQ,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,qBAAqB,EAAE,CAAC,CAAC;QAC7E,CAAC;QAED,UAAU,CAAC,KAAK,GAAG;YACjB,GAAG,UAAU,CAAC,KAAK;YACnB,KAAK,EAAE,EAAE,GAAG,UAAU,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE;SAC7D,CAAC;QACF,UAAU,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,qBAAqB,EAAE,CAAC,CAAC;IACvE,CAAC;IAED,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,IAAI,OAAO,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,KAAK,SAAS,EAAE,CAAC;YAC5C,MAAM,QAAQ,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,iBAAiB,EAAE,CAAC,CAAC;QACrE,CAAC;QAED,UAAU,CAAC,KAAK,GAAG;YACjB,GAAG,UAAU,CAAC,KAAK;YACnB,KAAK,EAAE,EAAE,GAAG,UAAU,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE;SACrD,CAAC;QACF,UAAU,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,iBAAiB,EAAE,CAAC,CAAC;IAC/D,CAAC;IAED,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;QAC9B,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YACjC,MAAM,QAAQ,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,aAAa,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC/D,CAAC;QAED,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,GAAG,IAAI,EAAE,GAAG,WAAW,CAAC;QACxD,MAAM,MAAM,GAAyB,EAAE,GAAG,IAAI,EAAE,CAAC;QACjD,UAAU,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,aAAa,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,CAAC;QAEvD,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;YAC7B,MAAM,CAAC,KAAK,GAAG,EAAE,GAAG,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;YACrD,UAAU,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,wBAAwB,EAAE,GAAG,EAAE,mBAAmB,EAAE,CAAC,CAAC;QAC/E,CAAC;QAED,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;YAC7B,MAAM,CAAC,KAAK,GAAG,EAAE,GAAG,MAAM,CAAC,KAAK,EAAE,UAAU,EAAE,CAAC;YAC/C,UAAU,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,wBAAwB,EAAE,GAAG,EAAE,yBAAyB,EAAE,CAAC,CAAC;QACrF,CAAC;QAED,UAAU,CAAC,MAAM,GAAG,MAAM,CAAC;IAC7B,CAAC;IAED,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC;AAC9C,CAAC,CAAC","sourcesContent":["import { getError } from '@venizia/ignis-helpers/core';\nimport type {\n IDeprecatedModelSetting,\n IModelAccessSettings,\n IModelSettings,\n} from '@/helpers/inversion';\n\n/** The release that drops the deprecated keys. */\nexport const DEPRECATED_MODEL_SETTINGS_REMOVED_IN = '0.4.3';\n\nconst conflict = (opts: { model: string; old: string; use: string }) => {\n return getError({\n message: `[model][${opts.model}] settings.${opts.old} and settings.${opts.use} are both set | ${opts.old} is the deprecated name of ${opts.use} | keep ${opts.use} and remove ${opts.old}`,\n });\n};\n\n/**\n * The one place the old flat settings become the new grouped ones. Everything that reads model\n * settings reads the result, so no reader knows two shapes.\n *\n * - `defaultFilter` -> `query.filter`; `defaultLimit` -> `query.limit.default`; `maxLimit` ->\n * `query.limit.max`; `scopeFilter` -> `access` (`writeGuard` -> `access.write.mode`, `sharedRows`\n * -> `access.write.sharedRows`).\n * - An old key together with the key that replaces it is an error.\n * - Returns the old keys in use so the application can warn once at boot.\n */\nexport const normalizeModelSettings = (opts: {\n model: string;\n settings: IModelSettings | undefined;\n}): { settings: IModelSettings | undefined; deprecated: IDeprecatedModelSetting[] } => {\n const { model, settings } = opts;\n\n if (!settings) {\n return { settings, deprecated: [] };\n }\n\n const { defaultFilter, defaultLimit, maxLimit, scopeFilter, ...current } = settings;\n const deprecated: IDeprecatedModelSetting[] = [];\n const normalized: IModelSettings = { ...current };\n\n if (defaultFilter !== undefined) {\n if (current.query?.filter !== undefined) {\n throw conflict({ model, old: 'defaultFilter', use: 'query.filter' });\n }\n\n normalized.query = { ...normalized.query, filter: defaultFilter };\n deprecated.push({ key: 'defaultFilter', use: 'query.filter' });\n }\n\n if (defaultLimit !== undefined) {\n if (current.query?.limit?.default !== undefined) {\n throw conflict({ model, old: 'defaultLimit', use: 'query.limit.default' });\n }\n\n normalized.query = {\n ...normalized.query,\n limit: { ...normalized.query?.limit, default: defaultLimit },\n };\n deprecated.push({ key: 'defaultLimit', use: 'query.limit.default' });\n }\n\n if (maxLimit !== undefined) {\n if (current.query?.limit?.max !== undefined) {\n throw conflict({ model, old: 'maxLimit', use: 'query.limit.max' });\n }\n\n normalized.query = {\n ...normalized.query,\n limit: { ...normalized.query?.limit, max: maxLimit },\n };\n deprecated.push({ key: 'maxLimit', use: 'query.limit.max' });\n }\n\n if (scopeFilter !== undefined) {\n if (current.access !== undefined) {\n throw conflict({ model, old: 'scopeFilter', use: 'access' });\n }\n\n const { writeGuard, sharedRows, ...rest } = scopeFilter;\n const access: IModelAccessSettings = { ...rest };\n deprecated.push({ key: 'scopeFilter', use: 'access' });\n\n if (writeGuard !== undefined) {\n access.write = { ...access.write, mode: writeGuard };\n deprecated.push({ key: 'scopeFilter.writeGuard', use: 'access.write.mode' });\n }\n\n if (sharedRows !== undefined) {\n access.write = { ...access.write, sharedRows };\n deprecated.push({ key: 'scopeFilter.sharedRows', use: 'access.write.sharedRows' });\n }\n\n normalized.access = access;\n }\n\n return { settings: normalized, deprecated };\n};\n"]}
|
|
@@ -2,7 +2,7 @@ import type { IDataSourceMetadata, IModelMetadata, TRepositoryMetadata } from '.
|
|
|
2
2
|
import type { IDataSource } from '../datasources/index.js';
|
|
3
3
|
import type { AbstractEntity } from '../models/index.js';
|
|
4
4
|
/** Registers a model class with its static schema and relations. */
|
|
5
|
-
export declare const model: (
|
|
5
|
+
export declare const model: (input: IModelMetadata) => ClassDecorator;
|
|
6
6
|
/** Registers a datasource with driver and auto-discovery settings. */
|
|
7
7
|
export declare const datasource: (metadata?: IDataSourceMetadata) => ClassDecorator;
|
|
8
8
|
export declare const repository: <Model extends AbstractEntity = AbstractEntity, DataSource extends IDataSource = IDataSource>(metadata?: TRepositoryMetadata<Model, DataSource>) => ClassDecorator;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"persistents.d.ts","sourceRoot":"","sources":["../../../../src/base/metadata/persistents.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAEV,mBAAmB,EAEnB,cAAc,EAEd,mBAAmB,EACpB,MAAM,qBAAqB,CAAC;AAU7B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAElD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"persistents.d.ts","sourceRoot":"","sources":["../../../../src/base/metadata/persistents.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAEV,mBAAmB,EAEnB,cAAc,EAEd,mBAAmB,EACpB,MAAM,qBAAqB,CAAC;AAU7B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAElD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAKhD,oEAAoE;AACpE,eAAO,MAAM,KAAK,GAAI,OAAO,cAAc,KAAG,cA0C7C,CAAC;AAEF,sEAAsE;AACtE,eAAO,MAAM,UAAU,GAAI,WAAW,mBAAmB,KAAG,cAQ3D,CAAC;AA0PF,eAAO,MAAM,UAAU,GACrB,KAAK,SAAS,cAAc,GAAG,cAAc,EAC7C,UAAU,SAAS,WAAW,GAAG,WAAW,EAE5C,WAAW,mBAAmB,CAAC,KAAK,EAAE,UAAU,CAAC,KAChD,cAeF,CAAC"}
|
|
@@ -5,26 +5,38 @@ import { getError } from '@venizia/ignis-helpers/core';
|
|
|
5
5
|
import { isDataSourceClass } from '../datasources/index.js';
|
|
6
6
|
import { RepositoryTypes } from '../repositories/common/constants.js';
|
|
7
7
|
import { injectable, pickRegistrationOptions } from './injectable.js';
|
|
8
|
+
import { normalizeModelSettings } from './model-settings.js';
|
|
8
9
|
/** Registers a model class with its static schema and relations. */
|
|
9
|
-
export const model = (
|
|
10
|
-
return target => {
|
|
11
|
-
injectable({ type: ArtifactTypes.MODEL, ...pickRegistrationOptions({ metadata }) })(target);
|
|
12
|
-
|
|
10
|
+
export const model = (input) => {
|
|
11
|
+
return (target) => {
|
|
12
|
+
injectable({ type: ArtifactTypes.MODEL, ...pickRegistrationOptions({ metadata: input }) })(target);
|
|
13
|
+
// The one place the deprecated flat keys become the grouped ones; the registry, and so every
|
|
14
|
+
// reader, only ever sees the grouped shape.
|
|
15
|
+
const { settings, deprecated } = normalizeModelSettings({
|
|
16
|
+
model: target.name,
|
|
17
|
+
settings: input.settings,
|
|
18
|
+
});
|
|
19
|
+
const metadata = {
|
|
20
|
+
...input,
|
|
21
|
+
settings,
|
|
22
|
+
...(deprecated.length > 0 ? { deprecatedSettings: deprecated } : {}),
|
|
23
|
+
};
|
|
24
|
+
const defaultLimit = settings?.query?.limit?.default;
|
|
13
25
|
if (defaultLimit !== undefined && (!Number.isInteger(defaultLimit) || defaultLimit <= 0)) {
|
|
14
26
|
throw getError({
|
|
15
|
-
message: `[model][${target.name}] Invalid '
|
|
27
|
+
message: `[model][${target.name}] Invalid 'query.limit.default' | Expected a positive integer | Got: ${defaultLimit}`,
|
|
16
28
|
});
|
|
17
29
|
}
|
|
18
|
-
// Validated at DECORATION time, like
|
|
30
|
+
// Validated at DECORATION time, like the default limit: a bad ceiling is a wiring mistake, and
|
|
19
31
|
// catching it at boot beats catching it on the one request that happens to reach the limit.
|
|
20
|
-
const maxLimit =
|
|
32
|
+
const maxLimit = settings?.query?.limit?.max;
|
|
21
33
|
if (maxLimit !== undefined && (!Number.isInteger(maxLimit) || maxLimit <= 0)) {
|
|
22
34
|
throw getError({
|
|
23
|
-
message: `[model][${target.name}] Invalid '
|
|
35
|
+
message: `[model][${target.name}] Invalid 'query.limit.max' | Expected a positive integer | Got: ${maxLimit}`,
|
|
24
36
|
});
|
|
25
37
|
}
|
|
26
38
|
// Auto-populate AUTHORIZATION_SUBJECT from authorize.principal if not already set
|
|
27
|
-
const principal =
|
|
39
|
+
const principal = settings?.authorize?.principal;
|
|
28
40
|
if (principal && !Object.hasOwn(target, 'AUTHORIZATION_SUBJECT')) {
|
|
29
41
|
target.AUTHORIZATION_SUBJECT = principal;
|
|
30
42
|
}
|
|
@@ -33,7 +45,7 @@ export const model = (metadata) => {
|
|
|
33
45
|
};
|
|
34
46
|
/** Registers a datasource with driver and auto-discovery settings. */
|
|
35
47
|
export const datasource = (metadata) => {
|
|
36
|
-
return target => {
|
|
48
|
+
return (target) => {
|
|
37
49
|
injectable({
|
|
38
50
|
type: ArtifactTypes.DATASOURCE,
|
|
39
51
|
...pickRegistrationOptions({ metadata: metadata ?? {} }),
|
|
@@ -96,7 +108,7 @@ const registerDataSourceInjection = (opts) => {
|
|
|
96
108
|
const { target, registry, dataSourceRef, resolvedDataSource } = opts;
|
|
97
109
|
// Own metadata only: `getInjectMetadata` walks the prototype chain, so a repository extending another @repository class would see the BASE's injection at param[0] and silently resolve the base's dataSource.
|
|
98
110
|
const ownInjects = Reflect.getOwnMetadata(MetadataKeys.INJECT, target);
|
|
99
|
-
const injectAtIndex0 = ownInjects?.find(entry => entry?.index === 0);
|
|
111
|
+
const injectAtIndex0 = ownInjects?.find((entry) => entry?.index === 0);
|
|
100
112
|
if (injectAtIndex0) {
|
|
101
113
|
// `@inject({ target })` carries no key; read the one recorded on the class. The function form
|
|
102
114
|
// resolves through the same helper the container uses, so both paths read one class.
|
|
@@ -212,7 +224,7 @@ const applyRepositoryMetadata = (opts) => {
|
|
|
212
224
|
registry.setRepositoryMetadata({ target, metadata: { ...metadata, _resolved: resolved } });
|
|
213
225
|
};
|
|
214
226
|
export const repository = (metadata) => {
|
|
215
|
-
return target => {
|
|
227
|
+
return (target) => {
|
|
216
228
|
const registry = MetadataRegistry.getInstance();
|
|
217
229
|
if (!metadata) {
|
|
218
230
|
applyRepositoryMetadata({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"persistents.js","sourceRoot":"","sources":["../../../../src/base/metadata/persistents.ts"],"names":[],"mappings":"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","sourcesContent":["import { BindingNamespaces } from '@/common/bindings';\nimport type {\n IArtifactRegistrationOptions,\n IDataSourceMetadata,\n IInjectMetadata,\n IModelMetadata,\n IResolvedRepositoryMetadata,\n TRepositoryMetadata,\n} from '@/helpers/inversion';\nimport {\n ArtifactTypes,\n BindingKeys,\n MetadataKeys,\n MetadataRegistry,\n resolveInjectTarget,\n} from '@/helpers/inversion';\nimport { resolveClass, resolveValue } from '@venizia/ignis-helpers/common';\nimport { getError } from '@venizia/ignis-helpers/core';\nimport type { IDataSource } from '../datasources';\nimport { isDataSourceClass } from '../datasources';\nimport type { AbstractEntity } from '../models';\nimport { RepositoryTypes } from '../repositories/common/constants';\nimport { injectable, pickRegistrationOptions } from './injectable';\n\n/** Registers a model class with its static schema and relations. */\nexport const model = (metadata: IModelMetadata): ClassDecorator => {\n return target => {\n injectable({ type: ArtifactTypes.MODEL, ...pickRegistrationOptions({ metadata }) })(target);\n\n const defaultLimit = metadata.settings?.defaultLimit;\n if (defaultLimit !== undefined && (!Number.isInteger(defaultLimit) || defaultLimit <= 0)) {\n throw getError({\n message: `[model][${target.name}] Invalid 'defaultLimit' | Expected a positive integer | Got: ${defaultLimit}`,\n });\n }\n\n // Validated at DECORATION time, like defaultLimit: a bad ceiling is a wiring mistake, and\n // catching it at boot beats catching it on the one request that happens to reach the limit.\n const maxLimit = metadata.settings?.maxLimit;\n if (maxLimit !== undefined && (!Number.isInteger(maxLimit) || maxLimit <= 0)) {\n throw getError({\n message: `[model][${target.name}] Invalid 'maxLimit' | Expected a positive integer | Got: ${maxLimit}`,\n });\n }\n\n // Auto-populate AUTHORIZATION_SUBJECT from authorize.principal if not already set\n const principal = metadata.settings?.authorize?.principal;\n if (principal && !Object.hasOwn(target, 'AUTHORIZATION_SUBJECT')) {\n (target as Record<string, unknown>).AUTHORIZATION_SUBJECT = principal;\n }\n\n MetadataRegistry.getInstance().registerModel({ target, metadata });\n };\n};\n\n/** Registers a datasource with driver and auto-discovery settings. */\nexport const datasource = (metadata?: IDataSourceMetadata): ClassDecorator => {\n return target => {\n injectable({\n type: ArtifactTypes.DATASOURCE,\n ...pickRegistrationOptions({ metadata: metadata ?? {} }),\n })(target);\n MetadataRegistry.getInstance().setDataSourceMetadata({ target, metadata });\n };\n};\n\n/** Mirrors the `TRepositoryMetadata` union for JavaScript callers: a known type, a dataSource always, a model exactly when the type is RepositoryTypes.MODEL. */\nconst validateRepositoryMetadata = <\n Model extends AbstractEntity = AbstractEntity,\n DataSource extends IDataSource = IDataSource,\n>(opts: {\n metadata: TRepositoryMetadata<Model, DataSource>;\n target: Function;\n}): void => {\n const { metadata, target } = opts;\n const type = metadata.type ?? RepositoryTypes.MODEL;\n\n if (!RepositoryTypes.isValid(type)) {\n throw getError({\n message: `[validateRepositoryMetadata][@repository][${target.name}] Invalid metadata | Unknown 'type': ${type} | Expected one of: ${[...RepositoryTypes.SCHEME_SET].join(', ')}`,\n });\n }\n\n if (type === RepositoryTypes.MODEL && !metadata.model) {\n throw getError({\n message: `[validateRepositoryMetadata][@repository][${target.name}] Invalid metadata | Missing 'model'`,\n });\n }\n\n if (type === RepositoryTypes.REMOTE && metadata.model) {\n throw getError({\n message: `[validateRepositoryMetadata][@repository][${target.name}] Invalid metadata | A '${RepositoryTypes.REMOTE}' repository takes no 'model'`,\n });\n }\n\n if (!metadata.dataSource) {\n throw getError({\n message: `[validateRepositoryMetadata][@repository][${target.name}] Invalid metadata | Missing 'dataSource'`,\n });\n }\n};\n\n/** Asserts the constructor's first parameter is an AbstractDataSource compatible with what @repository declared. */\nconst validateFirstConstructorParameter = (opts: {\n target: Function;\n firstParamType: Function;\n resolvedDataSource: string | Function;\n}): void => {\n const { target, firstParamType, resolvedDataSource } = opts;\n\n // NOT `instanceof`: two copies of this package give two `AbstractDataSource` classes and the\n // check would be false across them, rejecting a perfectly valid repository at import time.\n const isDataSourceType = isDataSourceClass(firstParamType);\n\n if (!isDataSourceType) {\n throw getError({\n message: `[@repository][${target.name}] Invalid constructor | First parameter must extend AbstractDataSource | Received: '${firstParamType.name}'`,\n });\n }\n\n if (typeof resolvedDataSource !== 'function') {\n return;\n }\n\n const isCompatible =\n firstParamType === resolvedDataSource || resolvedDataSource.prototype instanceof firstParamType;\n\n if (!isCompatible) {\n throw getError({\n message: `[@repository][${target.name}] Invalid constructor | Type mismatch | Constructor expects '${firstParamType.name}' but @repository specifies '${resolvedDataSource.name}'`,\n });\n }\n};\n\n/**\n * Auto-injects dataSource at constructor param[0] unless explicit @inject exists. A class is injected\n * by the class itself, so the container reads the key its registration recorded: a pinned\n * `binding` and a minified class name both resolve. Only a string reference is a key.\n */\nconst registerDataSourceInjection = (opts: {\n target: Function;\n registry: MetadataRegistry;\n dataSourceRef: TRepositoryMetadata['dataSource'];\n resolvedDataSource: string | Function;\n}): void => {\n const { target, registry, dataSourceRef, resolvedDataSource } = opts;\n\n // Own metadata only: `getInjectMetadata` walks the prototype chain, so a repository extending another @repository class would see the BASE's injection at param[0] and silently resolve the base's dataSource.\n const ownInjects: IInjectMetadata[] | undefined = Reflect.getOwnMetadata(\n MetadataKeys.INJECT,\n target,\n );\n const injectAtIndex0 = ownInjects?.find(entry => entry?.index === 0);\n\n if (injectAtIndex0) {\n // `@inject({ target })` carries no key; read the one recorded on the class. The function form\n // resolves through the same helper the container uses, so both paths read one class.\n const injectedTarget = injectAtIndex0.target\n ? resolveInjectTarget({ target: injectAtIndex0.target })\n : undefined;\n const injectKey =\n injectAtIndex0.key ??\n (injectedTarget ? registry.getBindingKey({ target: injectedTarget }) : undefined);\n\n // A class is judged by its brand: its key may sit under any namespace a `binding` pinned, and\n // a provisional key may still change at registration. Only a bare key is judged by its prefix.\n const isDataSourceTarget = injectedTarget\n ? isDataSourceClass(injectedTarget)\n : typeof injectKey === 'string' && injectKey.startsWith(`${BindingNamespaces.DATASOURCE}.`);\n\n if (!isDataSourceTarget) {\n throw getError({\n message: `[@repository][${target.name}] Invalid constructor | First parameter must be a DataSource | Found @inject with key: '${String(injectKey)}' | Expected a key starting with '${BindingNamespaces.DATASOURCE}.' or a DataSource class`,\n });\n }\n\n return;\n }\n\n // Checked only when this decorator writes the injection: an explicit @inject decides for itself.\n // An interface emits `Object` and a type-only class import emits `Function` under tsc (`Object`\n // under bun); neither names the class, so neither is judged.\n const firstParamType = Reflect.getMetadata('design:paramtypes', target)?.[0];\n if (firstParamType && firstParamType !== Object && firstParamType !== Function) {\n validateFirstConstructorParameter({ target, firstParamType, resolvedDataSource });\n }\n\n // The inherited list is the base repository's own, and setInjectMetadata may write straight into it.\n if (!ownInjects) {\n Reflect.defineMetadata(\n MetadataKeys.INJECT,\n [...(registry.getInjectMetadata({ target }) ?? [])],\n target,\n );\n }\n\n const derivedKey = BindingKeys.build({\n namespace: BindingNamespaces.DATASOURCE,\n key: typeof resolvedDataSource === 'string' ? resolvedDataSource : resolvedDataSource.name,\n });\n\n // The class first, decided when the repository is resolved: by then any registration - decorated,\n // `bindingList()`, or `dataSource(X, { binding })` - has recorded its key on the class. The derived\n // key is the fallback for a class nothing recorded, bound by hand under exactly that key.\n registry.setInjectMetadata({\n target,\n index: 0,\n metadata:\n typeof dataSourceRef === 'string'\n ? { key: derivedKey, index: 0, isOptional: false }\n : { target: dataSourceRef, key: derivedKey, index: 0, isOptional: false },\n });\n};\n\n/** Resolves repository metadata and registers bindings for schema auto-discovery. */\nconst resolveRepositoryMetadata = <\n Model extends AbstractEntity = AbstractEntity,\n DataSource extends IDataSource = IDataSource,\n>(opts: {\n metadata: TRepositoryMetadata<Model, DataSource>;\n target: Function;\n registry: MetadataRegistry;\n}): IResolvedRepositoryMetadata<Model, DataSource> => {\n const { metadata, target, registry } = opts;\n\n validateRepositoryMetadata({ metadata, target });\n\n const resolvedDataSource = resolveClass({ ref: metadata.dataSource });\n if (!resolvedDataSource) {\n throw getError({\n message: `[@repository][${target.name}] The dataSource resolver returned nothing at decoration time | An import cycle leaves the datasource class undefined here; import it without the cycle, or name it as a string`,\n });\n }\n\n // No model, so no binding: the datasource owns no schema through this repository.\n if (metadata.type === RepositoryTypes.REMOTE) {\n registerDataSourceInjection({\n target,\n registry,\n dataSourceRef: metadata.dataSource,\n resolvedDataSource,\n });\n return {\n type: RepositoryTypes.REMOTE,\n dataSource: resolvedDataSource,\n operationScope: metadata.operationScope,\n };\n }\n\n const resolvedModel = resolveValue({ value: metadata.model });\n\n registry.registerRepositoryBinding({\n repository: target,\n model: resolvedModel,\n dataSource: resolvedDataSource,\n });\n\n registerDataSourceInjection({\n target,\n registry,\n dataSourceRef: metadata.dataSource,\n resolvedDataSource,\n });\n\n return {\n type: RepositoryTypes.MODEL,\n model: resolvedModel,\n dataSource: resolvedDataSource,\n operationScope: metadata.operationScope,\n };\n};\n\n/** A bare `@repository()` on a subclass reuses the type, model and datasource its nearest decorated parent declared; the registration options (`binding`, `when`, ...) are never inherited, so the subclass registers under its own name. */\nconst inheritRepositoryMetadata = (opts: {\n target: Function;\n registry: MetadataRegistry;\n}): TRepositoryMetadata => {\n // Own metadata does not exist yet at decoration time, so this read walks the prototype chain.\n const inherited = opts.registry.getRepositoryMetadata({ target: opts.target });\n if (!inherited) {\n throw getError({\n message: `[@repository][${opts.target.name}] No metadata given and no decorated repository above it in the prototype chain | pass { model, dataSource } or decorate the parent`,\n });\n }\n\n if (inherited.type === RepositoryTypes.REMOTE) {\n const { type, dataSource, operationScope } = inherited;\n return { type, dataSource, operationScope };\n }\n\n const { type, model: parentModel, dataSource, operationScope } = inherited;\n return { type, model: parentModel, dataSource, operationScope };\n};\n\nconst applyRepositoryMetadata = <\n Model extends AbstractEntity = AbstractEntity,\n DataSource extends IDataSource = IDataSource,\n>(opts: {\n metadata: TRepositoryMetadata<Model, DataSource>;\n registration: IArtifactRegistrationOptions;\n target: Function;\n registry: MetadataRegistry;\n}): void => {\n const { metadata, registration, target, registry } = opts;\n injectable({\n type: ArtifactTypes.REPOSITORY,\n ...pickRegistrationOptions({ metadata: registration }),\n })(target);\n\n const resolved = resolveRepositoryMetadata({ metadata, target, registry });\n\n registry.setRepositoryMetadata({ target, metadata: { ...metadata, _resolved: resolved } });\n};\n\nexport const repository = <\n Model extends AbstractEntity = AbstractEntity,\n DataSource extends IDataSource = IDataSource,\n>(\n metadata?: TRepositoryMetadata<Model, DataSource>,\n): ClassDecorator => {\n return target => {\n const registry = MetadataRegistry.getInstance();\n if (!metadata) {\n applyRepositoryMetadata({\n metadata: inheritRepositoryMetadata({ target, registry }),\n registration: {},\n target,\n registry,\n });\n return;\n }\n\n applyRepositoryMetadata({ metadata, registration: metadata, target, registry });\n };\n};\n"]}
|
|
1
|
+
{"version":3,"file":"persistents.js","sourceRoot":"","sources":["../../../../src/base/metadata/persistents.ts"],"names":[],"mappings":"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","sourcesContent":["import { BindingNamespaces } from '@/common/bindings';\nimport type {\n IArtifactRegistrationOptions,\n IDataSourceMetadata,\n IInjectMetadata,\n IModelMetadata,\n IResolvedRepositoryMetadata,\n TRepositoryMetadata,\n} from '@/helpers/inversion';\nimport {\n ArtifactTypes,\n BindingKeys,\n MetadataKeys,\n MetadataRegistry,\n resolveInjectTarget,\n} from '@/helpers/inversion';\nimport { resolveClass, resolveValue } from '@venizia/ignis-helpers/common';\nimport { getError } from '@venizia/ignis-helpers/core';\nimport type { IDataSource } from '../datasources';\nimport { isDataSourceClass } from '../datasources';\nimport type { AbstractEntity } from '../models';\nimport { RepositoryTypes } from '../repositories/common/constants';\nimport { injectable, pickRegistrationOptions } from './injectable';\nimport { normalizeModelSettings } from './model-settings';\n\n/** Registers a model class with its static schema and relations. */\nexport const model = (input: IModelMetadata): ClassDecorator => {\n return (target) => {\n injectable({ type: ArtifactTypes.MODEL, ...pickRegistrationOptions({ metadata: input }) })(\n target,\n );\n\n // The one place the deprecated flat keys become the grouped ones; the registry, and so every\n // reader, only ever sees the grouped shape.\n const { settings, deprecated } = normalizeModelSettings({\n model: target.name,\n settings: input.settings,\n });\n const metadata: IModelMetadata = {\n ...input,\n settings,\n ...(deprecated.length > 0 ? { deprecatedSettings: deprecated } : {}),\n };\n\n const defaultLimit = settings?.query?.limit?.default;\n if (defaultLimit !== undefined && (!Number.isInteger(defaultLimit) || defaultLimit <= 0)) {\n throw getError({\n message: `[model][${target.name}] Invalid 'query.limit.default' | Expected a positive integer | Got: ${defaultLimit}`,\n });\n }\n\n // Validated at DECORATION time, like the default limit: a bad ceiling is a wiring mistake, and\n // catching it at boot beats catching it on the one request that happens to reach the limit.\n const maxLimit = settings?.query?.limit?.max;\n if (maxLimit !== undefined && (!Number.isInteger(maxLimit) || maxLimit <= 0)) {\n throw getError({\n message: `[model][${target.name}] Invalid 'query.limit.max' | Expected a positive integer | Got: ${maxLimit}`,\n });\n }\n\n // Auto-populate AUTHORIZATION_SUBJECT from authorize.principal if not already set\n const principal = settings?.authorize?.principal;\n if (principal && !Object.hasOwn(target, 'AUTHORIZATION_SUBJECT')) {\n (target as Record<string, unknown>).AUTHORIZATION_SUBJECT = principal;\n }\n\n MetadataRegistry.getInstance().registerModel({ target, metadata });\n };\n};\n\n/** Registers a datasource with driver and auto-discovery settings. */\nexport const datasource = (metadata?: IDataSourceMetadata): ClassDecorator => {\n return (target) => {\n injectable({\n type: ArtifactTypes.DATASOURCE,\n ...pickRegistrationOptions({ metadata: metadata ?? {} }),\n })(target);\n MetadataRegistry.getInstance().setDataSourceMetadata({ target, metadata });\n };\n};\n\n/** Mirrors the `TRepositoryMetadata` union for JavaScript callers: a known type, a dataSource always, a model exactly when the type is RepositoryTypes.MODEL. */\nconst validateRepositoryMetadata = <\n Model extends AbstractEntity = AbstractEntity,\n DataSource extends IDataSource = IDataSource,\n>(opts: {\n metadata: TRepositoryMetadata<Model, DataSource>;\n target: Function;\n}): void => {\n const { metadata, target } = opts;\n const type = metadata.type ?? RepositoryTypes.MODEL;\n\n if (!RepositoryTypes.isValid(type)) {\n throw getError({\n message: `[validateRepositoryMetadata][@repository][${target.name}] Invalid metadata | Unknown 'type': ${type} | Expected one of: ${[...RepositoryTypes.SCHEME_SET].join(', ')}`,\n });\n }\n\n if (type === RepositoryTypes.MODEL && !metadata.model) {\n throw getError({\n message: `[validateRepositoryMetadata][@repository][${target.name}] Invalid metadata | Missing 'model'`,\n });\n }\n\n if (type === RepositoryTypes.REMOTE && metadata.model) {\n throw getError({\n message: `[validateRepositoryMetadata][@repository][${target.name}] Invalid metadata | A '${RepositoryTypes.REMOTE}' repository takes no 'model'`,\n });\n }\n\n if (!metadata.dataSource) {\n throw getError({\n message: `[validateRepositoryMetadata][@repository][${target.name}] Invalid metadata | Missing 'dataSource'`,\n });\n }\n};\n\n/** Asserts the constructor's first parameter is an AbstractDataSource compatible with what @repository declared. */\nconst validateFirstConstructorParameter = (opts: {\n target: Function;\n firstParamType: Function;\n resolvedDataSource: string | Function;\n}): void => {\n const { target, firstParamType, resolvedDataSource } = opts;\n\n // NOT `instanceof`: two copies of this package give two `AbstractDataSource` classes and the\n // check would be false across them, rejecting a perfectly valid repository at import time.\n const isDataSourceType = isDataSourceClass(firstParamType);\n\n if (!isDataSourceType) {\n throw getError({\n message: `[@repository][${target.name}] Invalid constructor | First parameter must extend AbstractDataSource | Received: '${firstParamType.name}'`,\n });\n }\n\n if (typeof resolvedDataSource !== 'function') {\n return;\n }\n\n const isCompatible =\n firstParamType === resolvedDataSource || resolvedDataSource.prototype instanceof firstParamType;\n\n if (!isCompatible) {\n throw getError({\n message: `[@repository][${target.name}] Invalid constructor | Type mismatch | Constructor expects '${firstParamType.name}' but @repository specifies '${resolvedDataSource.name}'`,\n });\n }\n};\n\n/**\n * Auto-injects dataSource at constructor param[0] unless explicit @inject exists. A class is injected\n * by the class itself, so the container reads the key its registration recorded: a pinned\n * `binding` and a minified class name both resolve. Only a string reference is a key.\n */\nconst registerDataSourceInjection = (opts: {\n target: Function;\n registry: MetadataRegistry;\n dataSourceRef: TRepositoryMetadata['dataSource'];\n resolvedDataSource: string | Function;\n}): void => {\n const { target, registry, dataSourceRef, resolvedDataSource } = opts;\n\n // Own metadata only: `getInjectMetadata` walks the prototype chain, so a repository extending another @repository class would see the BASE's injection at param[0] and silently resolve the base's dataSource.\n const ownInjects: IInjectMetadata[] | undefined = Reflect.getOwnMetadata(\n MetadataKeys.INJECT,\n target,\n );\n const injectAtIndex0 = ownInjects?.find((entry) => entry?.index === 0);\n\n if (injectAtIndex0) {\n // `@inject({ target })` carries no key; read the one recorded on the class. The function form\n // resolves through the same helper the container uses, so both paths read one class.\n const injectedTarget = injectAtIndex0.target\n ? resolveInjectTarget({ target: injectAtIndex0.target })\n : undefined;\n const injectKey =\n injectAtIndex0.key ??\n (injectedTarget ? registry.getBindingKey({ target: injectedTarget }) : undefined);\n\n // A class is judged by its brand: its key may sit under any namespace a `binding` pinned, and\n // a provisional key may still change at registration. Only a bare key is judged by its prefix.\n const isDataSourceTarget = injectedTarget\n ? isDataSourceClass(injectedTarget)\n : typeof injectKey === 'string' && injectKey.startsWith(`${BindingNamespaces.DATASOURCE}.`);\n\n if (!isDataSourceTarget) {\n throw getError({\n message: `[@repository][${target.name}] Invalid constructor | First parameter must be a DataSource | Found @inject with key: '${String(injectKey)}' | Expected a key starting with '${BindingNamespaces.DATASOURCE}.' or a DataSource class`,\n });\n }\n\n return;\n }\n\n // Checked only when this decorator writes the injection: an explicit @inject decides for itself.\n // An interface emits `Object` and a type-only class import emits `Function` under tsc (`Object`\n // under bun); neither names the class, so neither is judged.\n const firstParamType = Reflect.getMetadata('design:paramtypes', target)?.[0];\n if (firstParamType && firstParamType !== Object && firstParamType !== Function) {\n validateFirstConstructorParameter({ target, firstParamType, resolvedDataSource });\n }\n\n // The inherited list is the base repository's own, and setInjectMetadata may write straight into it.\n if (!ownInjects) {\n Reflect.defineMetadata(\n MetadataKeys.INJECT,\n [...(registry.getInjectMetadata({ target }) ?? [])],\n target,\n );\n }\n\n const derivedKey = BindingKeys.build({\n namespace: BindingNamespaces.DATASOURCE,\n key: typeof resolvedDataSource === 'string' ? resolvedDataSource : resolvedDataSource.name,\n });\n\n // The class first, decided when the repository is resolved: by then any registration - decorated,\n // `bindingList()`, or `dataSource(X, { binding })` - has recorded its key on the class. The derived\n // key is the fallback for a class nothing recorded, bound by hand under exactly that key.\n registry.setInjectMetadata({\n target,\n index: 0,\n metadata:\n typeof dataSourceRef === 'string'\n ? { key: derivedKey, index: 0, isOptional: false }\n : { target: dataSourceRef, key: derivedKey, index: 0, isOptional: false },\n });\n};\n\n/** Resolves repository metadata and registers bindings for schema auto-discovery. */\nconst resolveRepositoryMetadata = <\n Model extends AbstractEntity = AbstractEntity,\n DataSource extends IDataSource = IDataSource,\n>(opts: {\n metadata: TRepositoryMetadata<Model, DataSource>;\n target: Function;\n registry: MetadataRegistry;\n}): IResolvedRepositoryMetadata<Model, DataSource> => {\n const { metadata, target, registry } = opts;\n\n validateRepositoryMetadata({ metadata, target });\n\n const resolvedDataSource = resolveClass({ ref: metadata.dataSource });\n if (!resolvedDataSource) {\n throw getError({\n message: `[@repository][${target.name}] The dataSource resolver returned nothing at decoration time | An import cycle leaves the datasource class undefined here; import it without the cycle, or name it as a string`,\n });\n }\n\n // No model, so no binding: the datasource owns no schema through this repository.\n if (metadata.type === RepositoryTypes.REMOTE) {\n registerDataSourceInjection({\n target,\n registry,\n dataSourceRef: metadata.dataSource,\n resolvedDataSource,\n });\n return {\n type: RepositoryTypes.REMOTE,\n dataSource: resolvedDataSource,\n operationScope: metadata.operationScope,\n };\n }\n\n const resolvedModel = resolveValue({ value: metadata.model });\n\n registry.registerRepositoryBinding({\n repository: target,\n model: resolvedModel,\n dataSource: resolvedDataSource,\n });\n\n registerDataSourceInjection({\n target,\n registry,\n dataSourceRef: metadata.dataSource,\n resolvedDataSource,\n });\n\n return {\n type: RepositoryTypes.MODEL,\n model: resolvedModel,\n dataSource: resolvedDataSource,\n operationScope: metadata.operationScope,\n };\n};\n\n/** A bare `@repository()` on a subclass reuses the type, model and datasource its nearest decorated parent declared; the registration options (`binding`, `when`, ...) are never inherited, so the subclass registers under its own name. */\nconst inheritRepositoryMetadata = (opts: {\n target: Function;\n registry: MetadataRegistry;\n}): TRepositoryMetadata => {\n // Own metadata does not exist yet at decoration time, so this read walks the prototype chain.\n const inherited = opts.registry.getRepositoryMetadata({ target: opts.target });\n if (!inherited) {\n throw getError({\n message: `[@repository][${opts.target.name}] No metadata given and no decorated repository above it in the prototype chain | pass { model, dataSource } or decorate the parent`,\n });\n }\n\n if (inherited.type === RepositoryTypes.REMOTE) {\n const { type, dataSource, operationScope } = inherited;\n return { type, dataSource, operationScope };\n }\n\n const { type, model: parentModel, dataSource, operationScope } = inherited;\n return { type, model: parentModel, dataSource, operationScope };\n};\n\nconst applyRepositoryMetadata = <\n Model extends AbstractEntity = AbstractEntity,\n DataSource extends IDataSource = IDataSource,\n>(opts: {\n metadata: TRepositoryMetadata<Model, DataSource>;\n registration: IArtifactRegistrationOptions;\n target: Function;\n registry: MetadataRegistry;\n}): void => {\n const { metadata, registration, target, registry } = opts;\n injectable({\n type: ArtifactTypes.REPOSITORY,\n ...pickRegistrationOptions({ metadata: registration }),\n })(target);\n\n const resolved = resolveRepositoryMetadata({ metadata, target, registry });\n\n registry.setRepositoryMetadata({ target, metadata: { ...metadata, _resolved: resolved } });\n};\n\nexport const repository = <\n Model extends AbstractEntity = AbstractEntity,\n DataSource extends IDataSource = IDataSource,\n>(\n metadata?: TRepositoryMetadata<Model, DataSource>,\n): ClassDecorator => {\n return (target) => {\n const registry = MetadataRegistry.getInstance();\n if (!metadata) {\n applyRepositoryMetadata({\n metadata: inheritRepositoryMetadata({ target, registry }),\n registration: {},\n target,\n registry,\n });\n return;\n }\n\n applyRepositoryMetadata({ metadata, registration: metadata, target, registry });\n };\n};\n"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ArtifactTypes, MetadataRegistry } from '../../../helpers/inversion/index.js';
|
|
2
2
|
import { injectable, pickRegistrationOptions } from '../injectable.js';
|
|
3
3
|
export const controller = (metadata) => {
|
|
4
|
-
return target => {
|
|
4
|
+
return (target) => {
|
|
5
5
|
injectable({ type: ArtifactTypes.CONTROLLER, ...pickRegistrationOptions({ metadata }) })(target);
|
|
6
6
|
MetadataRegistry.getInstance().setControllerMetadata({ target, metadata });
|
|
7
7
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"controller.js","sourceRoot":"","sources":["../../../../../src/base/metadata/routes/controller.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACtE,OAAO,EAAE,UAAU,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAEpE,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,QAA6B,EAAkB,EAAE;IAC1E,OAAO,MAAM,
|
|
1
|
+
{"version":3,"file":"controller.js","sourceRoot":"","sources":["../../../../../src/base/metadata/routes/controller.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACtE,OAAO,EAAE,UAAU,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAEpE,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,QAA6B,EAAkB,EAAE;IAC1E,OAAO,CAAC,MAAM,EAAE,EAAE;QAChB,UAAU,CAAC,EAAE,IAAI,EAAE,aAAa,CAAC,UAAU,EAAE,GAAG,uBAAuB,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CACtF,MAAM,CACP,CAAC;QACF,gBAAgB,CAAC,WAAW,EAAE,CAAC,qBAAqB,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC7E,CAAC,CAAC;AACJ,CAAC,CAAC","sourcesContent":["import type { TControllerMetadata } from '@/helpers/inversion';\nimport { ArtifactTypes, MetadataRegistry } from '@/helpers/inversion';\nimport { injectable, pickRegistrationOptions } from '../injectable';\n\nexport const controller = (metadata: TControllerMetadata): ClassDecorator => {\n return (target) => {\n injectable({ type: ArtifactTypes.CONTROLLER, ...pickRegistrationOptions({ metadata }) })(\n target,\n );\n MetadataRegistry.getInstance().setControllerMetadata({ target, metadata });\n };\n};\n"]}
|
|
@@ -70,7 +70,7 @@ export class BaseAppErrorMiddleware extends BaseHelper {
|
|
|
70
70
|
}
|
|
71
71
|
/** Renders a thrown error for the log, through the `formatError` option when one was given. */
|
|
72
72
|
formatError(opts) {
|
|
73
|
-
return (this.formatErrorOverride ?? (o => this.renderErrorForLog(o)))(opts);
|
|
73
|
+
return (this.formatErrorOverride ?? ((o) => this.renderErrorForLog(o)))(opts);
|
|
74
74
|
}
|
|
75
75
|
/**
|
|
76
76
|
* The default log rendering: pure and deliberately small, so the whole middleware stays
|
|
@@ -94,13 +94,13 @@ export class BaseAppErrorMiddleware extends BaseHelper {
|
|
|
94
94
|
lines.push(`extra: ${renderValue({ value: extra })}`);
|
|
95
95
|
}
|
|
96
96
|
if (includeStack && typeof thrown?.stack === 'string') {
|
|
97
|
-
const frames = thrown.stack.split('\n').filter(line => line.trimStart().startsWith('at '));
|
|
97
|
+
const frames = thrown.stack.split('\n').filter((line) => line.trimStart().startsWith('at '));
|
|
98
98
|
const capped = maxStackFrames === undefined ? frames : frames.slice(0, maxStackFrames);
|
|
99
99
|
if (capped.length > 0) {
|
|
100
100
|
lines.push(`stack:\n${capped.join('\n')}`);
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
|
-
return lines.map(line => `- ${line}`).join('\n');
|
|
103
|
+
return lines.map((line) => `- ${line}`).join('\n');
|
|
104
104
|
}
|
|
105
105
|
withRootKey(payload) {
|
|
106
106
|
return this.rootKey ? { [this.rootKey]: payload } : payload;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-error.middleware.js","sourceRoot":"","sources":["../../../../../src/base/middlewares/app-error/app-error.middleware.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ILogger, TLogLevel } from '@venizia/ignis-helpers/core';\nimport type { TNullable } from '@venizia/ignis-helpers/common';\nimport {\n BaseHelper,\n EnvironmentNames,\n LogLevels,\n MessageCode,\n MessageTemplate,\n} from '@venizia/ignis-helpers/core';\nimport { HTTP, toJsonSafe } from '@venizia/ignis-helpers/common';\nimport type { IProvider } from '@venizia/ignis-inversion';\nimport type { Context } from 'hono';\nimport type { ErrorHandler, HTTPResponseError } from 'hono/types';\nimport { REQUEST_ID_KEY } from '../common';\nimport { isDatabaseClientError, isRetryableDatabaseError } from './database.handler';\nimport {\n ApplicationErrorTypes,\n DATABASE_RETRYABLE_ERROR_CODE,\n DATABASE_RETRYABLE_ERROR_MESSAGE,\n} from './definition';\nimport type { IResolvedApplicationError, TApplicationErrorType } from './common';\nimport { formatZodError } from './zod.handler';\n\nconst DEFAULT_INTERNAL_ERROR_MESSAGE = 'Internal Server Error';\n\n/** An intentional error's throw site is still worth naming; its caller chain is framework plumbing, so it gets a smaller budget than an unexpected one. */\nconst DEFAULT_INTENTIONAL_STACK_FRAMES = 5;\nconst DEFAULT_UNEXPECTED_STACK_FRAMES = 10;\n\n/** How a thrown error is rendered into the log line. Must never throw - a throw inside the error handler turns a handled failure into an unhandled one. */\nexport type TErrorLogFormatter = (opts: {\n error: unknown;\n messageCode?: string;\n extra?: Record<string, unknown>;\n includeStack?: boolean;\n maxStackFrames?: number;\n}) => string;\n\ntype TThrown = Error | HTTPResponseError;\ntype TDatabaseClientError = ReturnType<typeof isDatabaseClientError>;\n\n/**\n * Renders a value into one log-safe fragment.\n *\n * Redaction is not optional here. This runs on `extra`, which is whatever a throw site chose to\n * attach - `getError({ extra: { apiKey } })` is a shape applications write - and in a Worker this\n * fragment lands in the browser console. `toJsonSafe` masks secret-shaped keys; a hand-rolled\n * `JSON.stringify` does not, and the server path has always redacted.\n *\n * The BigInt replacer stays: `toJsonSafe` leaves BigInt intact and `JSON.stringify` refuses it, so\n * without this a single BigInt would collapse the whole fragment to the marker below. Anything else\n * it refuses yields that marker rather than a throw - this is the last-resort error handler, where a\n * throw turns a handled failure into an unhandled one.\n */\nconst renderValue = (opts: { value: unknown }): string => {\n const { value } = opts;\n\n if (typeof value === 'string') {\n return value;\n }\n\n const safe = toJsonSafe({ value });\n\n try {\n return JSON.stringify(safe) ?? String(value);\n } catch {\n // A replacer is 6x the cost of plain stringify and deopts every key, so it is installed only\n // once BigInt has actually thrown - the case it exists for, and the rare one.\n try {\n const rendered = JSON.stringify(safe, (_key, entry: unknown) =>\n typeof entry === 'bigint' ? entry.toString() : entry,\n );\n\n return rendered ?? String(value);\n } catch {\n // Not a swallowed failure: this marker IS the report, and it lands in the log line the caller\n // is building.\n return '[unrenderable]';\n }\n }\n};\n\n/**\n * Hono `onError`: ZodError -> 422, DB client error -> 400, retryable conflict -> 409, `getError` ->\n * its own status, else 500. Browser-pure: the two reads that need a host - the ambient environment\n * name and the error formatter - are constructor OPTIONS, not methods to subclass.\n *\n * They were hooks once, and three subclasses existed to answer them and nothing else. A class seam\n * is also unreachable from application code, so the browser example had to fake the environment with\n * a Hono middleware assigning `context.env`. `@venizia/ignis`'s `AppErrorMiddleware` now just\n * supplies `process.env.NODE_ENV` and `ErrorPrettier` as options.\n */\nexport class BaseAppErrorMiddleware extends BaseHelper implements IProvider<ErrorHandler> {\n private rootKey: TNullable<string>;\n private intentionalStackFrames: number;\n private unexpectedStackFrames: number;\n\n /**\n * How this host reads its ambient environment name, and the ONE option that says whether it has\n * one at all: absent means it does not (a browser), so an unresolved name is by design; present\n * but returning `undefined` means a host that should have one is misconfigured. Either way\n * {@link isProduction} fails closed.\n *\n * A function, not a string: `process.env.NODE_ENV` has to be read per request, since a test - and\n * a `--env-file` reload - can change it after the middleware is built.\n */\n private readonly environment?: () => string | undefined;\n\n /** Replaces {@link renderErrorForLog} - `@venizia/ignis` passes `ErrorPrettier`, which reaches `node:util` and so cannot live here. */\n private readonly formatErrorOverride?: TErrorLogFormatter;\n\n constructor(opts?: {\n logger?: ILogger;\n rootKey?: string;\n intentionalStackFrames?: number;\n unexpectedStackFrames?: number;\n environment?: () => string | undefined;\n formatError?: TErrorLogFormatter;\n }) {\n // `new.target`, not a literal: a subclass logs under its own name, so `@venizia/ignis`'s\n // `AppErrorMiddleware` keeps the scope it has always logged under.\n super({ scope: new.target.name });\n\n this.rootKey = opts?.rootKey;\n this.intentionalStackFrames = opts?.intentionalStackFrames ?? DEFAULT_INTENTIONAL_STACK_FRAMES;\n this.unexpectedStackFrames = opts?.unexpectedStackFrames ?? DEFAULT_UNEXPECTED_STACK_FRAMES;\n this.environment = opts?.environment;\n this.formatErrorOverride = opts?.formatError;\n\n if (opts?.logger) {\n this.logger = opts.logger;\n }\n }\n\n /** Renders a thrown error for the log, through the `formatError` option when one was given. */\n protected formatError(opts: {\n error: unknown;\n messageCode?: string;\n extra?: Record<string, unknown>;\n includeStack?: boolean;\n maxStackFrames?: number;\n }): string {\n return (this.formatErrorOverride ?? (o => this.renderErrorForLog(o)))(opts);\n }\n\n /**\n * The default log rendering: pure and deliberately small, so the whole middleware stays\n * browser-pure.\n *\n * Must never throw, cycles included: a throw inside the error handler turns a handled failure\n * into an unhandled one.\n */\n private renderErrorForLog(opts: {\n error: unknown;\n messageCode?: string;\n extra?: Record<string, unknown>;\n includeStack?: boolean;\n maxStackFrames?: number;\n }): string {\n const { error, messageCode, extra, includeStack = true, maxStackFrames } = opts;\n const thrown = error as Partial<Error> | null | undefined;\n const lines: Array<string> = [];\n\n if (typeof thrown?.name === 'string') {\n lines.push(`name: ${thrown.name}`);\n }\n\n lines.push(\n `message: ${typeof thrown?.message === 'string' ? thrown.message : renderValue({ value: error })}`,\n );\n\n if (messageCode) {\n lines.push(`code: ${messageCode}`);\n }\n\n if (extra && Object.keys(extra).length > 0) {\n lines.push(`extra: ${renderValue({ value: extra })}`);\n }\n\n if (includeStack && typeof thrown?.stack === 'string') {\n const frames = thrown.stack.split('\\n').filter(line => line.trimStart().startsWith('at '));\n const capped = maxStackFrames === undefined ? frames : frames.slice(0, maxStackFrames);\n\n if (capped.length > 0) {\n lines.push(`stack:\\n${capped.join('\\n')}`);\n }\n }\n\n return lines.map(line => `- ${line}`).join('\\n');\n }\n\n private withRootKey(payload: object) {\n return this.rootKey ? { [this.rootKey]: payload } : payload;\n }\n\n /** Fail-closed: an unset or unrecognized env is production. */\n private isProduction(opts: { context: Context; error: TThrown; requestId: string }): boolean {\n const { context, error, requestId } = opts;\n const env = [context.env?.NODE_ENV, this.environment?.()].find(Boolean);\n\n if (!env) {\n // Only a host that HAS an ambient environment can be misconfigured about it. In a browser the\n // absence is by design, and shouting it at error level on every request buries the error the\n // reader is actually looking at. Both paths still sanitize.\n if (this.environment) {\n this.logger.error(\n '[%s] INVALID ENV IDENTIFIER | env: %s | path: %s | method: %s | url: %s\\n%s',\n requestId,\n env,\n context.req.path,\n context.req.method,\n context.req.url,\n this.formatError({ error }),\n );\n } else {\n this.logger.debug(\n '[%s] No ambient environment - sanitizing as production | path: %s | set NODE_ENV on the Hono env binding to opt out',\n requestId,\n context.req.path,\n );\n }\n\n return true;\n }\n\n return !EnvironmentNames.DEVELOPMENT_ENVS.has(env.toLowerCase());\n }\n\n private classify(opts: { error: TThrown; dbError: TDatabaseClientError }): TApplicationErrorType {\n const { error, dbError } = opts;\n\n if (dbError.isClientError) {\n return ApplicationErrorTypes.DATABASE_CLIENT;\n }\n\n if (isRetryableDatabaseError({ error })) {\n return ApplicationErrorTypes.DATABASE_RETRYABLE;\n }\n\n if ('statusCode' in error || this.readClientErrorStatus({ error }) !== undefined) {\n return ApplicationErrorTypes.INTENTIONAL;\n }\n\n return ApplicationErrorTypes.UNEXPECTED;\n }\n\n /** Hono's `HTTPException` carries `status`, not `statusCode`. Only a 4xx is the caller's fault; a 5xx stays unexpected, so its message is sanitized. */\n private readClientErrorStatus(opts: { error: TThrown }): number | undefined {\n const { error } = opts;\n\n if (!('getResponse' in error) || !('status' in error) || typeof error.status !== 'number') {\n return undefined;\n }\n\n const isClientError =\n error.status >= HTTP.ResultCodes.RS_4.BadRequest &&\n error.status < HTTP.ResultCodes.RS_5.InternalServerError;\n\n return isClientError ? error.status : undefined;\n }\n\n /** `getError` sets `statusCode`, and it wins when a foreign error carries both. Neither usable is a 500. */\n private readIntentionalStatus(opts: { error: TThrown }): number {\n const { error } = opts;\n\n if ('statusCode' in error && typeof error.statusCode === 'number') {\n return error.statusCode;\n }\n\n return this.readClientErrorStatus({ error }) ?? HTTP.ResultCodes.RS_5.InternalServerError;\n }\n\n /** An ApplicationError's `normalized` is authoritative - a `transform` may have reworded `text`. */\n private build(opts: {\n error: TThrown;\n statusCode: number;\n message: string;\n type: TApplicationErrorType;\n messageCode?: string;\n }): IResolvedApplicationError {\n const { error, statusCode, message, type, messageCode } = opts;\n\n // Fallback for a FOREIGN error that sets its own code; an ApplicationError reports via `normalized`.\n const code =\n messageCode ??\n MessageCode.resolve('messageCode' in error ? (error.messageCode as string) : undefined);\n\n const normalized =\n 'normalized' in error\n ? (error.normalized as IResolvedApplicationError['normalized'])\n : { text: message, code, args: {} };\n\n return { statusCode, message, normalized, type };\n }\n\n private resolve(opts: { error: TThrown; isProduction: boolean }): IResolvedApplicationError {\n const { error, isProduction } = opts;\n const dbError = isDatabaseClientError({ error, isProduction });\n const type = this.classify({ error, dbError });\n\n switch (type) {\n case ApplicationErrorTypes.DATABASE_CLIENT: {\n return this.build({\n error,\n type,\n statusCode: HTTP.ResultCodes.RS_4.BadRequest,\n // The fallback is unreachable today; it keeps the driver's raw text from ever surfacing.\n message: dbError.message ?? DEFAULT_INTERNAL_ERROR_MESSAGE,\n });\n }\n\n case ApplicationErrorTypes.DATABASE_RETRYABLE: {\n return this.build({\n error,\n type,\n statusCode: HTTP.ResultCodes.RS_4.Conflict,\n message: DATABASE_RETRYABLE_ERROR_MESSAGE,\n messageCode: DATABASE_RETRYABLE_ERROR_CODE,\n });\n }\n\n case ApplicationErrorTypes.INTENTIONAL: {\n return this.build({\n error,\n type,\n statusCode: this.readIntentionalStatus({ error }),\n message: error.message,\n });\n }\n\n default: {\n return this.build({\n error,\n type: ApplicationErrorTypes.UNEXPECTED,\n statusCode: HTTP.ResultCodes.RS_5.InternalServerError,\n // Never leak a raw message - it may carry SQL, schema names or connection details.\n message: isProduction ? DEFAULT_INTERNAL_ERROR_MESSAGE : error.message,\n });\n }\n }\n }\n\n /** A throw site may set `logLevel` via `getError`; anything unset or malformed logs at `error`. */\n private resolveLogLevel(opts: { error: TThrown }): TLogLevel {\n const candidate = (opts.error as { logLevel?: unknown }).logLevel;\n\n if (typeof candidate === 'string' && LogLevels.isValid(candidate)) {\n return candidate as TLogLevel;\n }\n\n return LogLevels.ERROR;\n }\n\n /** The one place a thrown error reaches the log - called once the status is known, so it carries it. */\n private logError(opts: {\n error: TThrown;\n context: Context;\n requestId: string;\n statusCode: number;\n messageCode: string;\n type?: TApplicationErrorType;\n }) {\n const { error, context, requestId, statusCode, messageCode, type } = opts;\n\n this.logger.log(\n this.resolveLogLevel({ error }),\n '[%s] REQUEST ERROR | %s | %s %s\\n%s',\n requestId,\n statusCode,\n context.req.method,\n context.req.url,\n this.formatError({\n error,\n // The default code means \"no code\" - logging it on every error would be a noise line.\n messageCode: messageCode === MessageCode.DEFAULT ? undefined : messageCode,\n extra: 'extra' in error ? (error.extra as Record<string, unknown>) : undefined,\n includeStack: true,\n // An intentional error still needs its throw site; it just does not need the caller chain behind it.\n maxStackFrames:\n type === ApplicationErrorTypes.UNEXPECTED\n ? this.unexpectedStackFrames\n : this.intentionalStackFrames,\n }),\n );\n }\n\n /** Only an intentional error's message is a catalog template; every other branch writes its own text, so it passes through as is. */\n private renderMessage(opts: {\n resolved: IResolvedApplicationError;\n isProduction: boolean;\n requestId: string;\n }): string {\n const { resolved, isProduction, requestId } = opts;\n\n if (resolved.type !== ApplicationErrorTypes.INTENTIONAL) {\n return resolved.message;\n }\n\n const { text, missing } = MessageTemplate.render({\n text: resolved.message,\n args: resolved.normalized.args,\n });\n\n // Production stays silent and renders what it can; anywhere else a missing arg is a throw site to fix.\n if (missing.length > 0 && !isProduction) {\n this.logger.warn(\n '[%s] MESSAGE PLACEHOLDER WITHOUT ARG | code: %s | keys: %s',\n requestId,\n resolved.normalized.code,\n missing.join(', '),\n );\n }\n\n return text;\n }\n\n value(): ErrorHandler {\n return async (error, context) => {\n const requestId = context.get(REQUEST_ID_KEY);\n const isProduction = this.isProduction({ context, error, requestId });\n\n if (error.name === 'ZodError') {\n const rs = formatZodError({\n isProduction,\n requestId,\n url: context.req.url,\n path: context.req.path,\n error,\n });\n\n this.logError({\n error,\n context,\n requestId,\n statusCode: rs.statusCode,\n messageCode: rs.response.normalized.code,\n });\n\n return context.json(\n this.withRootKey(rs.response),\n rs.statusCode as Parameters<typeof context.json>[1],\n );\n }\n\n const resolved = this.resolve({ error, isProduction });\n const { statusCode, normalized, type } = resolved;\n\n this.logError({ error, context, requestId, statusCode, messageCode: normalized.code, type });\n\n const rs = {\n message: this.renderMessage({ resolved, isProduction, requestId }),\n statusCode,\n normalized,\n requestId,\n extra: 'extra' in error ? error?.extra : undefined,\n details: {\n url: context.req.url,\n path: context.req.path,\n stack: !isProduction ? error.stack : undefined,\n cause: !isProduction ? error.cause : undefined,\n },\n };\n\n return context.json(this.withRootKey(rs), statusCode as Parameters<typeof context.json>[1]);\n };\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"app-error.middleware.js","sourceRoot":"","sources":["../../../../../src/base/middlewares/app-error/app-error.middleware.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ILogger, TLogLevel } from '@venizia/ignis-helpers/core';\nimport type { TNullable } from '@venizia/ignis-helpers/common';\nimport {\n BaseHelper,\n EnvironmentNames,\n LogLevels,\n MessageCode,\n MessageTemplate,\n} from '@venizia/ignis-helpers/core';\nimport { HTTP, toJsonSafe } from '@venizia/ignis-helpers/common';\nimport type { IProvider } from '@venizia/ignis-inversion';\nimport type { Context } from 'hono';\nimport type { ErrorHandler, HTTPResponseError } from 'hono/types';\nimport { REQUEST_ID_KEY } from '../common';\nimport { isDatabaseClientError, isRetryableDatabaseError } from './database.handler';\nimport {\n ApplicationErrorTypes,\n DATABASE_RETRYABLE_ERROR_CODE,\n DATABASE_RETRYABLE_ERROR_MESSAGE,\n} from './definition';\nimport type { IResolvedApplicationError, TApplicationErrorType } from './common';\nimport { formatZodError } from './zod.handler';\n\nconst DEFAULT_INTERNAL_ERROR_MESSAGE = 'Internal Server Error';\n\n/** An intentional error's throw site is still worth naming; its caller chain is framework plumbing, so it gets a smaller budget than an unexpected one. */\nconst DEFAULT_INTENTIONAL_STACK_FRAMES = 5;\nconst DEFAULT_UNEXPECTED_STACK_FRAMES = 10;\n\n/** How a thrown error is rendered into the log line. Must never throw - a throw inside the error handler turns a handled failure into an unhandled one. */\nexport type TErrorLogFormatter = (opts: {\n error: unknown;\n messageCode?: string;\n extra?: Record<string, unknown>;\n includeStack?: boolean;\n maxStackFrames?: number;\n}) => string;\n\ntype TThrown = Error | HTTPResponseError;\ntype TDatabaseClientError = ReturnType<typeof isDatabaseClientError>;\n\n/**\n * Renders a value into one log-safe fragment.\n *\n * Redaction is not optional here. This runs on `extra`, which is whatever a throw site chose to\n * attach - `getError({ extra: { apiKey } })` is a shape applications write - and in a Worker this\n * fragment lands in the browser console. `toJsonSafe` masks secret-shaped keys; a hand-rolled\n * `JSON.stringify` does not, and the server path has always redacted.\n *\n * The BigInt replacer stays: `toJsonSafe` leaves BigInt intact and `JSON.stringify` refuses it, so\n * without this a single BigInt would collapse the whole fragment to the marker below. Anything else\n * it refuses yields that marker rather than a throw - this is the last-resort error handler, where a\n * throw turns a handled failure into an unhandled one.\n */\nconst renderValue = (opts: { value: unknown }): string => {\n const { value } = opts;\n\n if (typeof value === 'string') {\n return value;\n }\n\n const safe = toJsonSafe({ value });\n\n try {\n return JSON.stringify(safe) ?? String(value);\n } catch {\n // A replacer is 6x the cost of plain stringify and deopts every key, so it is installed only\n // once BigInt has actually thrown - the case it exists for, and the rare one.\n try {\n const rendered = JSON.stringify(safe, (_key, entry: unknown) =>\n typeof entry === 'bigint' ? entry.toString() : entry,\n );\n\n return rendered ?? String(value);\n } catch {\n // Not a swallowed failure: this marker IS the report, and it lands in the log line the caller\n // is building.\n return '[unrenderable]';\n }\n }\n};\n\n/**\n * Hono `onError`: ZodError -> 422, DB client error -> 400, retryable conflict -> 409, `getError` ->\n * its own status, else 500. Browser-pure: the two reads that need a host - the ambient environment\n * name and the error formatter - are constructor OPTIONS, not methods to subclass.\n *\n * They were hooks once, and three subclasses existed to answer them and nothing else. A class seam\n * is also unreachable from application code, so the browser example had to fake the environment with\n * a Hono middleware assigning `context.env`. `@venizia/ignis`'s `AppErrorMiddleware` now just\n * supplies `process.env.NODE_ENV` and `ErrorPrettier` as options.\n */\nexport class BaseAppErrorMiddleware extends BaseHelper implements IProvider<ErrorHandler> {\n private rootKey: TNullable<string>;\n private intentionalStackFrames: number;\n private unexpectedStackFrames: number;\n\n /**\n * How this host reads its ambient environment name, and the ONE option that says whether it has\n * one at all: absent means it does not (a browser), so an unresolved name is by design; present\n * but returning `undefined` means a host that should have one is misconfigured. Either way\n * {@link isProduction} fails closed.\n *\n * A function, not a string: `process.env.NODE_ENV` has to be read per request, since a test - and\n * a `--env-file` reload - can change it after the middleware is built.\n */\n private readonly environment?: () => string | undefined;\n\n /** Replaces {@link renderErrorForLog} - `@venizia/ignis` passes `ErrorPrettier`, which reaches `node:util` and so cannot live here. */\n private readonly formatErrorOverride?: TErrorLogFormatter;\n\n constructor(opts?: {\n logger?: ILogger;\n rootKey?: string;\n intentionalStackFrames?: number;\n unexpectedStackFrames?: number;\n environment?: () => string | undefined;\n formatError?: TErrorLogFormatter;\n }) {\n // `new.target`, not a literal: a subclass logs under its own name, so `@venizia/ignis`'s\n // `AppErrorMiddleware` keeps the scope it has always logged under.\n super({ scope: new.target.name });\n\n this.rootKey = opts?.rootKey;\n this.intentionalStackFrames = opts?.intentionalStackFrames ?? DEFAULT_INTENTIONAL_STACK_FRAMES;\n this.unexpectedStackFrames = opts?.unexpectedStackFrames ?? DEFAULT_UNEXPECTED_STACK_FRAMES;\n this.environment = opts?.environment;\n this.formatErrorOverride = opts?.formatError;\n\n if (opts?.logger) {\n this.logger = opts.logger;\n }\n }\n\n /** Renders a thrown error for the log, through the `formatError` option when one was given. */\n protected formatError(opts: {\n error: unknown;\n messageCode?: string;\n extra?: Record<string, unknown>;\n includeStack?: boolean;\n maxStackFrames?: number;\n }): string {\n return (this.formatErrorOverride ?? ((o) => this.renderErrorForLog(o)))(opts);\n }\n\n /**\n * The default log rendering: pure and deliberately small, so the whole middleware stays\n * browser-pure.\n *\n * Must never throw, cycles included: a throw inside the error handler turns a handled failure\n * into an unhandled one.\n */\n private renderErrorForLog(opts: {\n error: unknown;\n messageCode?: string;\n extra?: Record<string, unknown>;\n includeStack?: boolean;\n maxStackFrames?: number;\n }): string {\n const { error, messageCode, extra, includeStack = true, maxStackFrames } = opts;\n const thrown = error as Partial<Error> | null | undefined;\n const lines: Array<string> = [];\n\n if (typeof thrown?.name === 'string') {\n lines.push(`name: ${thrown.name}`);\n }\n\n lines.push(\n `message: ${typeof thrown?.message === 'string' ? thrown.message : renderValue({ value: error })}`,\n );\n\n if (messageCode) {\n lines.push(`code: ${messageCode}`);\n }\n\n if (extra && Object.keys(extra).length > 0) {\n lines.push(`extra: ${renderValue({ value: extra })}`);\n }\n\n if (includeStack && typeof thrown?.stack === 'string') {\n const frames = thrown.stack.split('\\n').filter((line) => line.trimStart().startsWith('at '));\n const capped = maxStackFrames === undefined ? frames : frames.slice(0, maxStackFrames);\n\n if (capped.length > 0) {\n lines.push(`stack:\\n${capped.join('\\n')}`);\n }\n }\n\n return lines.map((line) => `- ${line}`).join('\\n');\n }\n\n private withRootKey(payload: object) {\n return this.rootKey ? { [this.rootKey]: payload } : payload;\n }\n\n /** Fail-closed: an unset or unrecognized env is production. */\n private isProduction(opts: { context: Context; error: TThrown; requestId: string }): boolean {\n const { context, error, requestId } = opts;\n const env = [context.env?.NODE_ENV, this.environment?.()].find(Boolean);\n\n if (!env) {\n // Only a host that HAS an ambient environment can be misconfigured about it. In a browser the\n // absence is by design, and shouting it at error level on every request buries the error the\n // reader is actually looking at. Both paths still sanitize.\n if (this.environment) {\n this.logger.error(\n '[%s] INVALID ENV IDENTIFIER | env: %s | path: %s | method: %s | url: %s\\n%s',\n requestId,\n env,\n context.req.path,\n context.req.method,\n context.req.url,\n this.formatError({ error }),\n );\n } else {\n this.logger.debug(\n '[%s] No ambient environment - sanitizing as production | path: %s | set NODE_ENV on the Hono env binding to opt out',\n requestId,\n context.req.path,\n );\n }\n\n return true;\n }\n\n return !EnvironmentNames.DEVELOPMENT_ENVS.has(env.toLowerCase());\n }\n\n private classify(opts: { error: TThrown; dbError: TDatabaseClientError }): TApplicationErrorType {\n const { error, dbError } = opts;\n\n if (dbError.isClientError) {\n return ApplicationErrorTypes.DATABASE_CLIENT;\n }\n\n if (isRetryableDatabaseError({ error })) {\n return ApplicationErrorTypes.DATABASE_RETRYABLE;\n }\n\n if ('statusCode' in error || this.readClientErrorStatus({ error }) !== undefined) {\n return ApplicationErrorTypes.INTENTIONAL;\n }\n\n return ApplicationErrorTypes.UNEXPECTED;\n }\n\n /** Hono's `HTTPException` carries `status`, not `statusCode`. Only a 4xx is the caller's fault; a 5xx stays unexpected, so its message is sanitized. */\n private readClientErrorStatus(opts: { error: TThrown }): number | undefined {\n const { error } = opts;\n\n if (!('getResponse' in error) || !('status' in error) || typeof error.status !== 'number') {\n return undefined;\n }\n\n const isClientError =\n error.status >= HTTP.ResultCodes.RS_4.BadRequest &&\n error.status < HTTP.ResultCodes.RS_5.InternalServerError;\n\n return isClientError ? error.status : undefined;\n }\n\n /** `getError` sets `statusCode`, and it wins when a foreign error carries both. Neither usable is a 500. */\n private readIntentionalStatus(opts: { error: TThrown }): number {\n const { error } = opts;\n\n if ('statusCode' in error && typeof error.statusCode === 'number') {\n return error.statusCode;\n }\n\n return this.readClientErrorStatus({ error }) ?? HTTP.ResultCodes.RS_5.InternalServerError;\n }\n\n /** An ApplicationError's `normalized` is authoritative - a `transform` may have reworded `text`. */\n private build(opts: {\n error: TThrown;\n statusCode: number;\n message: string;\n type: TApplicationErrorType;\n messageCode?: string;\n }): IResolvedApplicationError {\n const { error, statusCode, message, type, messageCode } = opts;\n\n // Fallback for a FOREIGN error that sets its own code; an ApplicationError reports via `normalized`.\n const code =\n messageCode ??\n MessageCode.resolve('messageCode' in error ? (error.messageCode as string) : undefined);\n\n const normalized =\n 'normalized' in error\n ? (error.normalized as IResolvedApplicationError['normalized'])\n : { text: message, code, args: {} };\n\n return { statusCode, message, normalized, type };\n }\n\n private resolve(opts: { error: TThrown; isProduction: boolean }): IResolvedApplicationError {\n const { error, isProduction } = opts;\n const dbError = isDatabaseClientError({ error, isProduction });\n const type = this.classify({ error, dbError });\n\n switch (type) {\n case ApplicationErrorTypes.DATABASE_CLIENT: {\n return this.build({\n error,\n type,\n statusCode: HTTP.ResultCodes.RS_4.BadRequest,\n // The fallback is unreachable today; it keeps the driver's raw text from ever surfacing.\n message: dbError.message ?? DEFAULT_INTERNAL_ERROR_MESSAGE,\n });\n }\n\n case ApplicationErrorTypes.DATABASE_RETRYABLE: {\n return this.build({\n error,\n type,\n statusCode: HTTP.ResultCodes.RS_4.Conflict,\n message: DATABASE_RETRYABLE_ERROR_MESSAGE,\n messageCode: DATABASE_RETRYABLE_ERROR_CODE,\n });\n }\n\n case ApplicationErrorTypes.INTENTIONAL: {\n return this.build({\n error,\n type,\n statusCode: this.readIntentionalStatus({ error }),\n message: error.message,\n });\n }\n\n default: {\n return this.build({\n error,\n type: ApplicationErrorTypes.UNEXPECTED,\n statusCode: HTTP.ResultCodes.RS_5.InternalServerError,\n // Never leak a raw message - it may carry SQL, schema names or connection details.\n message: isProduction ? DEFAULT_INTERNAL_ERROR_MESSAGE : error.message,\n });\n }\n }\n }\n\n /** A throw site may set `logLevel` via `getError`; anything unset or malformed logs at `error`. */\n private resolveLogLevel(opts: { error: TThrown }): TLogLevel {\n const candidate = (opts.error as { logLevel?: unknown }).logLevel;\n\n if (typeof candidate === 'string' && LogLevels.isValid(candidate)) {\n return candidate as TLogLevel;\n }\n\n return LogLevels.ERROR;\n }\n\n /** The one place a thrown error reaches the log - called once the status is known, so it carries it. */\n private logError(opts: {\n error: TThrown;\n context: Context;\n requestId: string;\n statusCode: number;\n messageCode: string;\n type?: TApplicationErrorType;\n }) {\n const { error, context, requestId, statusCode, messageCode, type } = opts;\n\n this.logger.log(\n this.resolveLogLevel({ error }),\n '[%s] REQUEST ERROR | %s | %s %s\\n%s',\n requestId,\n statusCode,\n context.req.method,\n context.req.url,\n this.formatError({\n error,\n // The default code means \"no code\" - logging it on every error would be a noise line.\n messageCode: messageCode === MessageCode.DEFAULT ? undefined : messageCode,\n extra: 'extra' in error ? (error.extra as Record<string, unknown>) : undefined,\n includeStack: true,\n // An intentional error still needs its throw site; it just does not need the caller chain behind it.\n maxStackFrames:\n type === ApplicationErrorTypes.UNEXPECTED\n ? this.unexpectedStackFrames\n : this.intentionalStackFrames,\n }),\n );\n }\n\n /** Only an intentional error's message is a catalog template; every other branch writes its own text, so it passes through as is. */\n private renderMessage(opts: {\n resolved: IResolvedApplicationError;\n isProduction: boolean;\n requestId: string;\n }): string {\n const { resolved, isProduction, requestId } = opts;\n\n if (resolved.type !== ApplicationErrorTypes.INTENTIONAL) {\n return resolved.message;\n }\n\n const { text, missing } = MessageTemplate.render({\n text: resolved.message,\n args: resolved.normalized.args,\n });\n\n // Production stays silent and renders what it can; anywhere else a missing arg is a throw site to fix.\n if (missing.length > 0 && !isProduction) {\n this.logger.warn(\n '[%s] MESSAGE PLACEHOLDER WITHOUT ARG | code: %s | keys: %s',\n requestId,\n resolved.normalized.code,\n missing.join(', '),\n );\n }\n\n return text;\n }\n\n value(): ErrorHandler {\n return async (error, context) => {\n const requestId = context.get(REQUEST_ID_KEY);\n const isProduction = this.isProduction({ context, error, requestId });\n\n if (error.name === 'ZodError') {\n const rs = formatZodError({\n isProduction,\n requestId,\n url: context.req.url,\n path: context.req.path,\n error,\n });\n\n this.logError({\n error,\n context,\n requestId,\n statusCode: rs.statusCode,\n messageCode: rs.response.normalized.code,\n });\n\n return context.json(\n this.withRootKey(rs.response),\n rs.statusCode as Parameters<typeof context.json>[1],\n );\n }\n\n const resolved = this.resolve({ error, isProduction });\n const { statusCode, normalized, type } = resolved;\n\n this.logError({ error, context, requestId, statusCode, messageCode: normalized.code, type });\n\n const rs = {\n message: this.renderMessage({ resolved, isProduction, requestId }),\n statusCode,\n normalized,\n requestId,\n extra: 'extra' in error ? error?.extra : undefined,\n details: {\n url: context.req.url,\n path: context.req.path,\n stack: !isProduction ? error.stack : undefined,\n cause: !isProduction ? error.cause : undefined,\n },\n };\n\n return context.json(this.withRootKey(rs), statusCode as Parameters<typeof context.json>[1]);\n };\n }\n}\n"]}
|
|
@@ -25,7 +25,7 @@ export const formatZodError = (opts) => {
|
|
|
25
25
|
let messageCode;
|
|
26
26
|
let message = DEFAULT_VALIDATION_MESSAGE;
|
|
27
27
|
if (issues && issues.length > 0) {
|
|
28
|
-
const primaryIssue = issues.find(issue => extractIssueCode({ issue }) !== undefined) ?? issues[0];
|
|
28
|
+
const primaryIssue = issues.find((issue) => extractIssueCode({ issue }) !== undefined) ?? issues[0];
|
|
29
29
|
messageCode = extractIssueCode({ issue: primaryIssue }) ?? primaryIssue.code;
|
|
30
30
|
message = primaryIssue.message;
|
|
31
31
|
}
|
|
@@ -44,7 +44,7 @@ export const formatZodError = (opts) => {
|
|
|
44
44
|
path,
|
|
45
45
|
stack: !isProduction ? error.stack : undefined,
|
|
46
46
|
cause: issues
|
|
47
|
-
? issues.map(el => ({
|
|
47
|
+
? issues.map((el) => ({
|
|
48
48
|
path: el.path.join('.') || 'root',
|
|
49
49
|
message: el.message,
|
|
50
50
|
code: el.code,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"zod.handler.js","sourceRoot":"","sources":["../../../../../src/base/middlewares/app-error/zod.handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAC1D,OAAO,EAAE,IAAI,EAAE,MAAM,+BAA+B,CAAC;AAIrD,MAAM,0BAA0B,GAAG,iBAAiB,CAAC;AAErD,iGAAiG;AACjG,MAAM,gBAAgB,GAAG,CAAC,IAA8B,EAAE,EAAE;IAC1D,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC;IAE5C,IAAI,OAAO,UAAU,KAAK,QAAQ,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5D,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC,CAAC;AAEF,2DAA2D;AAC3D,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,IAM9B,EAAE,EAAE;IACH,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC;IAC3D,MAAM,UAAU,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,mBAAmB,CAAC;IAE7D,IAAI,gBAAyB,CAAC;IAC9B,IAAI,CAAC;QACH,gBAAgB,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC/C,CAAC;IAAC,MAAM,CAAC;QACP,gBAAgB,GAAG,KAAK,CAAC;IAC3B,CAAC;IAED,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAE,gBAAoC,CAAC,CAAC,CAAC,IAAI,CAAC;IAE9F,6EAA6E;IAC7E,IAAI,WAA+B,CAAC;IACpC,IAAI,OAAO,GAAG,0BAA0B,CAAC;IAEzC,IAAI,MAAM,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAChC,MAAM,YAAY,GAChB,MAAM,CAAC,IAAI,CAAC,KAAK,
|
|
1
|
+
{"version":3,"file":"zod.handler.js","sourceRoot":"","sources":["../../../../../src/base/middlewares/app-error/zod.handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAC1D,OAAO,EAAE,IAAI,EAAE,MAAM,+BAA+B,CAAC;AAIrD,MAAM,0BAA0B,GAAG,iBAAiB,CAAC;AAErD,iGAAiG;AACjG,MAAM,gBAAgB,GAAG,CAAC,IAA8B,EAAE,EAAE;IAC1D,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC;IAE5C,IAAI,OAAO,UAAU,KAAK,QAAQ,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5D,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC,CAAC;AAEF,2DAA2D;AAC3D,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,IAM9B,EAAE,EAAE;IACH,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC;IAC3D,MAAM,UAAU,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,mBAAmB,CAAC;IAE7D,IAAI,gBAAyB,CAAC;IAC9B,IAAI,CAAC;QACH,gBAAgB,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC/C,CAAC;IAAC,MAAM,CAAC;QACP,gBAAgB,GAAG,KAAK,CAAC;IAC3B,CAAC;IAED,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAE,gBAAoC,CAAC,CAAC,CAAC,IAAI,CAAC;IAE9F,6EAA6E;IAC7E,IAAI,WAA+B,CAAC;IACpC,IAAI,OAAO,GAAG,0BAA0B,CAAC;IAEzC,IAAI,MAAM,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAChC,MAAM,YAAY,GAChB,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,gBAAgB,CAAC,EAAE,KAAK,EAAE,CAAC,KAAK,SAAS,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC;QACjF,WAAW,GAAG,gBAAgB,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,IAAI,YAAY,CAAC,IAAI,CAAC;QAC7E,OAAO,GAAG,YAAY,CAAC,OAAO,CAAC;IACjC,CAAC;IAED,4KAA4K;IAC5K,MAAM,mBAAmB,GAAG,WAAW,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IAE7D,OAAO;QACL,UAAU;QACV,QAAQ,EAAE;YACR,OAAO;YACP,UAAU;YACV,yGAAyG;YACzG,UAAU,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,mBAAmB,EAAE,IAAI,EAAE,EAAE,EAAE;YAClE,SAAS;YACT,OAAO,EAAE;gBACP,GAAG;gBACH,IAAI;gBACJ,KAAK,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS;gBAC9C,KAAK,EAAE,MAAM;oBACX,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;wBAClB,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,MAAM;wBACjC,OAAO,EAAE,EAAE,CAAC,OAAO;wBACnB,IAAI,EAAE,EAAE,CAAC,IAAI;wBACb,QAAQ,EAAE,EAAE,CAAC,QAAQ;wBACrB,QAAQ,EAAE,EAAE,CAAC,QAAQ;qBACtB,CAAC,CAAC;oBACL,CAAC,CAAC,gBAAgB;aACrB;SACF;KACF,CAAC;AACJ,CAAC,CAAC","sourcesContent":["import { MessageCode } from '@venizia/ignis-helpers/core';\nimport { HTTP } from '@venizia/ignis-helpers/common';\nimport type { HTTPResponseError } from 'hono/types';\nimport type { IZodIssueLike } from './common';\n\nconst DEFAULT_VALIDATION_MESSAGE = 'ValidationError';\n\n/** Returns a schema-author-supplied code (`issue.params.code`) when it is a non-empty string. */\nconst extractIssueCode = (opts: { issue: IZodIssueLike }) => {\n const paramsCode = opts.issue?.params?.code;\n\n if (typeof paramsCode === 'string' && paramsCode.length > 0) {\n return paramsCode;\n }\n\n return undefined;\n};\n\n/** Formats a ZodError into the 422 validation response. */\nexport const formatZodError = (opts: {\n isProduction: boolean;\n requestId: string;\n url: string;\n path: string;\n error: Error | HTTPResponseError;\n}) => {\n const { isProduction, requestId, url, path, error } = opts;\n const statusCode = HTTP.ResultCodes.RS_4.UnprocessableEntity;\n\n let validationErrors: unknown;\n try {\n validationErrors = JSON.parse(error.message);\n } catch {\n validationErrors = error;\n }\n\n const issues = Array.isArray(validationErrors) ? (validationErrors as IZodIssueLike[]) : null;\n\n // Prefer the first issue with a custom `params.code`, else its raw Zod code.\n let messageCode: string | undefined;\n let message = DEFAULT_VALIDATION_MESSAGE;\n\n if (issues && issues.length > 0) {\n const primaryIssue =\n issues.find((issue) => extractIssueCode({ issue }) !== undefined) ?? issues[0];\n messageCode = extractIssueCode({ issue: primaryIssue }) ?? primaryIssue.code;\n message = primaryIssue.message;\n }\n\n // A ZodError we could not parse into issues yields no code of its own; the response still carries one, so no error response is ever missing the field a client branches on.\n const resolvedMessageCode = MessageCode.resolve(messageCode);\n\n return {\n statusCode,\n response: {\n message,\n statusCode,\n // `args` is empty: a Zod issue carries no interpolation values - per-field detail is in `details.cause`.\n normalized: { text: message, code: resolvedMessageCode, args: {} },\n requestId,\n details: {\n url,\n path,\n stack: !isProduction ? error.stack : undefined,\n cause: issues\n ? issues.map((el) => ({\n path: el.path.join('.') || 'root',\n message: el.message,\n code: el.code,\n expected: el.expected,\n received: el.received,\n }))\n : validationErrors,\n },\n },\n };\n};\n"]}
|
|
@@ -7,7 +7,7 @@ const FORM_CONTENT_TYPES = [
|
|
|
7
7
|
/** Whether a route declares a form body, so its validator will parse one. */
|
|
8
8
|
export const hasFormBody = (opts) => {
|
|
9
9
|
const { content } = opts;
|
|
10
|
-
return content !== undefined && FORM_CONTENT_TYPES.some(contentType => contentType in content);
|
|
10
|
+
return content !== undefined && FORM_CONTENT_TYPES.some((contentType) => contentType in content);
|
|
11
11
|
};
|
|
12
12
|
/**
|
|
13
13
|
* Parses a declared form body just ahead of the route's validator, which reads the cached result.
|
|
@@ -16,7 +16,7 @@ export const hasFormBody = (opts) => {
|
|
|
16
16
|
*/
|
|
17
17
|
export const formBodyReader = async (context, next) => {
|
|
18
18
|
const contentType = context.req.header(HTTP.Headers.CONTENT_TYPE) ?? '';
|
|
19
|
-
if (FORM_CONTENT_TYPES.some(formType => contentType.includes(formType))) {
|
|
19
|
+
if (FORM_CONTENT_TYPES.some((formType) => contentType.includes(formType))) {
|
|
20
20
|
await readFormBody({ req: context.req });
|
|
21
21
|
}
|
|
22
22
|
await next();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"form-body.middleware.js","sourceRoot":"","sources":["../../../../../src/base/middlewares/form-body/form-body.middleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,+BAA+B,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAG3D,MAAM,kBAAkB,GAAG;IACzB,IAAI,CAAC,YAAY,CAAC,mBAAmB;IACrC,IAAI,CAAC,YAAY,CAAC,2BAA2B;CAC9C,CAAC;AAEF,6EAA6E;AAC7E,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,IAA2C,EAAW,EAAE;IAClF,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IACzB,OAAO,OAAO,KAAK,SAAS,IAAI,kBAAkB,CAAC,IAAI,CAAC,WAAW,
|
|
1
|
+
{"version":3,"file":"form-body.middleware.js","sourceRoot":"","sources":["../../../../../src/base/middlewares/form-body/form-body.middleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,+BAA+B,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAG3D,MAAM,kBAAkB,GAAG;IACzB,IAAI,CAAC,YAAY,CAAC,mBAAmB;IACrC,IAAI,CAAC,YAAY,CAAC,2BAA2B;CAC9C,CAAC;AAEF,6EAA6E;AAC7E,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,IAA2C,EAAW,EAAE;IAClF,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IACzB,OAAO,OAAO,KAAK,SAAS,IAAI,kBAAkB,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,WAAW,IAAI,OAAO,CAAC,CAAC;AACnG,CAAC,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAsB,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;IACvE,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC;IAExE,IAAI,kBAAkB,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,WAAW,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC;QAC1E,MAAM,YAAY,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IAC3C,CAAC;IAED,MAAM,IAAI,EAAE,CAAC;AACf,CAAC,CAAC","sourcesContent":["import { HTTP } from '@venizia/ignis-helpers/common';\nimport { readFormBody } from '@venizia/ignis-helpers/core';\nimport type { MiddlewareHandler } from 'hono/types';\n\nconst FORM_CONTENT_TYPES = [\n HTTP.HeaderValues.MULTIPART_FORM_DATA,\n HTTP.HeaderValues.APPLICATION_FORM_URLENCODED,\n];\n\n/** Whether a route declares a form body, so its validator will parse one. */\nexport const hasFormBody = (opts: { content?: Record<string, unknown> }): boolean => {\n const { content } = opts;\n return content !== undefined && FORM_CONTENT_TYPES.some((contentType) => contentType in content);\n};\n\n/**\n * Parses a declared form body just ahead of the route's validator, which reads the cached result.\n * Left to the validator, a malformed body is a bare `HTTPException(400)` carrying the parser's own\n * text and no code; read here, it is `core.request.body_malformed`.\n */\nexport const formBodyReader: MiddlewareHandler = async (context, next) => {\n const contentType = context.req.header(HTTP.Headers.CONTENT_TYPE) ?? '';\n\n if (FORM_CONTENT_TYPES.some((formType) => contentType.includes(formType))) {\n await readFormBody({ req: context.req });\n }\n\n await next();\n};\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"not-found.middleware.js","sourceRoot":"","sources":["../../../../../src/base/middlewares/not-found/not-found.middleware.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,+BAA+B,CAAC;AAErD,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAE3C,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,IAA0B,EAAE,EAAE;IAC5D,MAAM,EAAE,MAAM,GAAG,OAAO,EAAE,GAAG,IAAI,CAAC;IAElC,MAAM,EAAE,GAAoB,KAAK,
|
|
1
|
+
{"version":3,"file":"not-found.middleware.js","sourceRoot":"","sources":["../../../../../src/base/middlewares/not-found/not-found.middleware.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,+BAA+B,CAAC;AAErD,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAE3C,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,IAA0B,EAAE,EAAE;IAC5D,MAAM,EAAE,MAAM,GAAG,OAAO,EAAE,GAAG,IAAI,CAAC;IAElC,MAAM,EAAE,GAAoB,KAAK,EAAE,OAAO,EAAE,EAAE;QAC5C,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;QAE9C,6FAA6F;QAC7F,+FAA+F;QAC/F,6FAA6F;QAC7F,MAAM,CAAC,IAAI,CACT,yCAAyC,EACzC,SAAS,EACT,OAAO,CAAC,GAAG,CAAC,IAAI,EAChB,OAAO,CAAC,GAAG,CAAC,GAAG,CAChB,CAAC;QAEF,OAAO,OAAO,CAAC,IAAI,CACjB;YACE,OAAO,EAAE,eAAe;YACxB,UAAU,EAAE,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ;YAC1C,SAAS;YACT,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,IAAI;YACtB,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,GAAG;SACrB,EACD,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAC/B,CAAC;IACJ,CAAC,CAAC;IAEF,OAAO,EAAE,CAAC;AACZ,CAAC,CAAC","sourcesContent":["import type { ILogger } from '@venizia/ignis-helpers/core';\nimport { HTTP } from '@venizia/ignis-helpers/common';\nimport type { NotFoundHandler } from 'hono/types';\nimport { REQUEST_ID_KEY } from '../common';\n\nexport const notFoundHandler = (opts: { logger?: ILogger }) => {\n const { logger = console } = opts;\n\n const mw: NotFoundHandler = async (context) => {\n const requestId = context.get(REQUEST_ID_KEY);\n\n // WARN, not ERROR: an unrouted path is the callers mistake, and this middleware is installed\n // unconditionally - so scanner traffic filled the dedicated error log and tripped error-volume\n // alerting. The same reasoning is already applied to an expired token elsewhere in the tree.\n logger.warn(\n '[%s] URL NOT FOUND | path: %s | url: %s',\n requestId,\n context.req.path,\n context.req.url,\n );\n\n return context.json(\n {\n message: 'URL NOT FOUND',\n statusCode: HTTP.ResultCodes.RS_4.NotFound,\n requestId,\n path: context.req.path,\n url: context.req.url,\n },\n HTTP.ResultCodes.RS_4.NotFound,\n );\n };\n\n return mw;\n};\n"]}
|