@nage-api/core 1.0.0-beta.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.
Files changed (81) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +141 -0
  3. package/dist/bootstrap/bootstrap.d.ts +48 -0
  4. package/dist/bootstrap/bootstrap.js +255 -0
  5. package/dist/bootstrap/drain.d.ts +48 -0
  6. package/dist/bootstrap/drain.js +113 -0
  7. package/dist/bootstrap/lifecycle.d.ts +30 -0
  8. package/dist/bootstrap/lifecycle.js +64 -0
  9. package/dist/bootstrap/process-guards.d.ts +42 -0
  10. package/dist/bootstrap/process-guards.js +103 -0
  11. package/dist/bootstrap/query-parser.d.ts +34 -0
  12. package/dist/bootstrap/query-parser.js +37 -0
  13. package/dist/bootstrap/shutdown.d.ts +55 -0
  14. package/dist/bootstrap/shutdown.js +182 -0
  15. package/dist/constants.d.ts +32 -0
  16. package/dist/constants.js +48 -0
  17. package/dist/context/active-context.d.ts +23 -0
  18. package/dist/context/active-context.js +34 -0
  19. package/dist/context/request-context.middleware.d.ts +31 -0
  20. package/dist/context/request-context.middleware.js +95 -0
  21. package/dist/context/request-context.service.d.ts +29 -0
  22. package/dist/context/request-context.service.js +67 -0
  23. package/dist/decorators/owner.decorator.d.ts +18 -0
  24. package/dist/decorators/owner.decorator.js +31 -0
  25. package/dist/decorators/public.decorator.d.ts +13 -0
  26. package/dist/decorators/public.decorator.js +23 -0
  27. package/dist/decorators/version.decorators.d.ts +34 -0
  28. package/dist/decorators/version.decorators.js +40 -0
  29. package/dist/errors/catalog.d.ts +149 -0
  30. package/dist/errors/catalog.js +289 -0
  31. package/dist/errors/index.d.ts +3 -0
  32. package/dist/errors/index.js +22 -0
  33. package/dist/errors/nage.error.d.ts +43 -0
  34. package/dist/errors/nage.error.js +45 -0
  35. package/dist/guards/api-version.guard.d.ts +20 -0
  36. package/dist/guards/api-version.guard.js +73 -0
  37. package/dist/http/all-exceptions.filter.d.ts +25 -0
  38. package/dist/http/all-exceptions.filter.js +256 -0
  39. package/dist/http/envelope.d.ts +25 -0
  40. package/dist/http/envelope.js +44 -0
  41. package/dist/http/no-envelope.decorator.d.ts +11 -0
  42. package/dist/http/no-envelope.decorator.js +16 -0
  43. package/dist/http/request-timeout.decorators.d.ts +23 -0
  44. package/dist/http/request-timeout.decorators.js +29 -0
  45. package/dist/http/request-timeout.interceptor.d.ts +28 -0
  46. package/dist/http/request-timeout.interceptor.js +75 -0
  47. package/dist/http/response.interceptor.d.ts +19 -0
  48. package/dist/http/response.interceptor.js +73 -0
  49. package/dist/index.d.ts +38 -0
  50. package/dist/index.js +135 -0
  51. package/dist/job/job.factory.d.ts +29 -0
  52. package/dist/job/job.factory.js +50 -0
  53. package/dist/logging/json.logger.d.ts +23 -0
  54. package/dist/logging/json.logger.js +136 -0
  55. package/dist/logging/nest-logger.adapter.d.ts +20 -0
  56. package/dist/logging/nest-logger.adapter.js +46 -0
  57. package/dist/module/core.module.d.ts +40 -0
  58. package/dist/module/core.module.js +112 -0
  59. package/dist/security/audit.d.ts +42 -0
  60. package/dist/security/audit.js +399 -0
  61. package/dist/security/index.d.ts +15 -0
  62. package/dist/security/index.js +50 -0
  63. package/dist/security/legacy-scan.d.ts +24 -0
  64. package/dist/security/legacy-scan.js +98 -0
  65. package/dist/security/random.d.ts +40 -0
  66. package/dist/security/random.js +87 -0
  67. package/dist/security/rate-limit.decorators.d.ts +24 -0
  68. package/dist/security/rate-limit.decorators.js +25 -0
  69. package/dist/security/rate-limit.guard.d.ts +44 -0
  70. package/dist/security/rate-limit.guard.js +130 -0
  71. package/dist/security/rate-limit.store.d.ts +30 -0
  72. package/dist/security/rate-limit.store.js +63 -0
  73. package/dist/security/redaction.d.ts +54 -0
  74. package/dist/security/redaction.js +146 -0
  75. package/dist/security/tls.d.ts +29 -0
  76. package/dist/security/tls.js +48 -0
  77. package/dist/tokens.d.ts +60 -0
  78. package/dist/tokens.js +89 -0
  79. package/dist/version.d.ts +5 -0
  80. package/dist/version.js +8 -0
  81. package/package.json +77 -0
@@ -0,0 +1,34 @@
1
+ /**
2
+ * API-version decorators (PLAN.md §16.2, feature inventory row 21).
3
+ *
4
+ * Retained from the legacy framework, which selected handlers by an
5
+ * `x-application-version` header. Nest's built-in `@Version()` only matches
6
+ * exact versions, so ranges (`from` / `till` / `between`) are expressed as a
7
+ * rule read by `ApiVersionGuard`.
8
+ */
9
+ import { type CustomDecorator } from '@nestjs/common';
10
+ export type VersionRule = {
11
+ readonly kind: 'exact';
12
+ readonly versions: readonly number[];
13
+ } | {
14
+ readonly kind: 'from';
15
+ readonly from: number;
16
+ } | {
17
+ readonly kind: 'till';
18
+ readonly till: number;
19
+ } | {
20
+ readonly kind: 'between';
21
+ readonly from: number;
22
+ readonly till: number;
23
+ };
24
+ /** Serve this route only to the listed versions. */
25
+ export declare const ForVersion: (...versions: readonly number[]) => CustomDecorator;
26
+ /** Serve this route from `version` onwards (inclusive). */
27
+ export declare const FromVersion: (version: number) => CustomDecorator;
28
+ /** Serve this route up to `version` (inclusive) — how a route is retired. */
29
+ export declare const TillVersion: (version: number) => CustomDecorator;
30
+ /** Serve this route for `from`..`till` inclusive. */
31
+ export declare const BetweenVersions: (from: number, till: number) => CustomDecorator;
32
+ /** Does `version` satisfy `rule`? Exported for testing and for the CLI's docs. */
33
+ export declare function versionSatisfies(rule: VersionRule, version: number): boolean;
34
+ //# sourceMappingURL=version.decorators.d.ts.map
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ /**
3
+ * API-version decorators (PLAN.md §16.2, feature inventory row 21).
4
+ *
5
+ * Retained from the legacy framework, which selected handlers by an
6
+ * `x-application-version` header. Nest's built-in `@Version()` only matches
7
+ * exact versions, so ranges (`from` / `till` / `between`) are expressed as a
8
+ * rule read by `ApiVersionGuard`.
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.BetweenVersions = exports.TillVersion = exports.FromVersion = exports.ForVersion = void 0;
12
+ exports.versionSatisfies = versionSatisfies;
13
+ const common_1 = require("@nestjs/common");
14
+ const constants_js_1 = require("../constants.js");
15
+ /** Serve this route only to the listed versions. */
16
+ const ForVersion = (...versions) => (0, common_1.SetMetadata)(constants_js_1.METADATA_KEYS.versionRule, { kind: 'exact', versions });
17
+ exports.ForVersion = ForVersion;
18
+ /** Serve this route from `version` onwards (inclusive). */
19
+ const FromVersion = (version) => (0, common_1.SetMetadata)(constants_js_1.METADATA_KEYS.versionRule, { kind: 'from', from: version });
20
+ exports.FromVersion = FromVersion;
21
+ /** Serve this route up to `version` (inclusive) — how a route is retired. */
22
+ const TillVersion = (version) => (0, common_1.SetMetadata)(constants_js_1.METADATA_KEYS.versionRule, { kind: 'till', till: version });
23
+ exports.TillVersion = TillVersion;
24
+ /** Serve this route for `from`..`till` inclusive. */
25
+ const BetweenVersions = (from, till) => (0, common_1.SetMetadata)(constants_js_1.METADATA_KEYS.versionRule, { kind: 'between', from, till });
26
+ exports.BetweenVersions = BetweenVersions;
27
+ /** Does `version` satisfy `rule`? Exported for testing and for the CLI's docs. */
28
+ function versionSatisfies(rule, version) {
29
+ switch (rule.kind) {
30
+ case 'exact':
31
+ return rule.versions.includes(version);
32
+ case 'from':
33
+ return version >= rule.from;
34
+ case 'till':
35
+ return version <= rule.till;
36
+ case 'between':
37
+ return version >= rule.from && version <= rule.till;
38
+ }
39
+ }
40
+ //# sourceMappingURL=version.decorators.js.map
@@ -0,0 +1,149 @@
1
+ /**
2
+ * The concrete error catalog (PLAN.md §17.1).
3
+ *
4
+ * Each class fixes its stable `code` and its HTTP status, so mapping lives with
5
+ * the error rather than in a `switch` inside the filter, and clients can program
6
+ * against codes instead of messages.
7
+ */
8
+ import type { AuthenticationErrorCode, AuthorizationErrorCode, ErrorCode, ErrorDetail } from '@nage-api/contracts';
9
+ import { NageError, type NageErrorOptions } from './nage.error.js';
10
+ /**
11
+ * Status for every code in the catalog. Also consumed by the OpenAPI generator
12
+ * (Phase 6) so documented error schemas cannot drift from runtime behaviour.
13
+ */
14
+ export declare const ERROR_STATUS_BY_CODE: Readonly<Record<ErrorCode, number>>;
15
+ /**
16
+ * Client-safe default message per code. Deliberately generic: an authentication
17
+ * failure never reveals whether it was the address or the password that was
18
+ * wrong.
19
+ */
20
+ export declare const DEFAULT_SAFE_MESSAGE: Readonly<Record<ErrorCode, string>>;
21
+ /** Base for errors whose code is fixed by the class. */
22
+ declare abstract class FixedCodeError extends NageError {
23
+ protected constructor(code: ErrorCode, options?: NageErrorOptions);
24
+ }
25
+ /** 422 — the request body or params failed validation. */
26
+ export declare class ValidationError extends FixedCodeError {
27
+ readonly code: "VALIDATION_FAILED";
28
+ readonly httpStatus: number;
29
+ constructor(options?: NageErrorOptions);
30
+ /** Build from field-level feedback, the common case for a `ValidationPipe`. */
31
+ static fromDetails(details: readonly ErrorDetail[], message?: string): ValidationError;
32
+ }
33
+ /** 400 — the query DSL was malformed or referenced a disallowed field. */
34
+ export declare class InvalidQueryError extends FixedCodeError {
35
+ readonly code: "INVALID_QUERY";
36
+ readonly httpStatus: number;
37
+ constructor(options?: NageErrorOptions);
38
+ }
39
+ /** 400 — `limit` exceeded the model's `maxLimit` (no unbounded queries, §12). */
40
+ export declare class QueryLimitExceededError extends FixedCodeError {
41
+ readonly maxLimit: number;
42
+ readonly code: "QUERY_LIMIT_EXCEEDED";
43
+ readonly httpStatus: number;
44
+ constructor(maxLimit: number, options?: NageErrorOptions);
45
+ }
46
+ /** 401/423 — the caller could not be authenticated. */
47
+ export declare class AuthenticationError extends NageError {
48
+ readonly code: AuthenticationErrorCode;
49
+ readonly httpStatus: number;
50
+ constructor(code?: AuthenticationErrorCode, options?: NageErrorOptions);
51
+ }
52
+ /** 403 — the caller is known but not allowed. */
53
+ export declare class AuthorizationError extends NageError {
54
+ readonly code: AuthorizationErrorCode;
55
+ readonly httpStatus: number;
56
+ constructor(code?: AuthorizationErrorCode, options?: NageErrorOptions);
57
+ }
58
+ /** 404 — the resource does not exist, or the caller may not know that it does. */
59
+ export declare class NotFoundError extends FixedCodeError {
60
+ readonly code: "RESOURCE_NOT_FOUND";
61
+ readonly httpStatus: number;
62
+ constructor(options?: NageErrorOptions);
63
+ }
64
+ /** 409 — the write conflicts with the current state (e.g. a unique constraint). */
65
+ export declare class ConflictError extends FixedCodeError {
66
+ readonly code: "RESOURCE_CONFLICT";
67
+ readonly httpStatus: number;
68
+ constructor(options?: NageErrorOptions);
69
+ }
70
+ /** 409 — the record changed since it was read (optimistic locking, §14.2). */
71
+ export declare class OptimisticLockError extends FixedCodeError {
72
+ readonly code: "OPTIMISTIC_LOCK_CONFLICT";
73
+ readonly httpStatus: number;
74
+ constructor(options?: NageErrorOptions);
75
+ }
76
+ /** 429 — throttled. Carries the retry hint for the `Retry-After` header. */
77
+ export declare class RateLimitError extends FixedCodeError {
78
+ readonly retryAfterSeconds?: number | undefined;
79
+ readonly code: "RATE_LIMIT_EXCEEDED";
80
+ readonly httpStatus: number;
81
+ constructor(retryAfterSeconds?: number | undefined, options?: NageErrorOptions);
82
+ }
83
+ /**
84
+ * 504 — the handler exceeded the request timeout (§21).
85
+ *
86
+ * 504 rather than 503: the deployment is up and took the request, it was this
87
+ * request that ran out of time, and a client that retries a 503 immediately
88
+ * would make a slow dependency slower.
89
+ */
90
+ export declare class RequestTimeoutError extends FixedCodeError {
91
+ readonly timeoutMs: number;
92
+ readonly code: "REQUEST_TIMEOUT";
93
+ readonly httpStatus: number;
94
+ constructor(timeoutMs: number, options?: NageErrorOptions);
95
+ }
96
+ /** 502 — an upstream dependency failed. Its body is logged, never returned. */
97
+ export declare class ExternalServiceError extends FixedCodeError {
98
+ readonly service: string;
99
+ readonly code: "EXTERNAL_SERVICE_ERROR";
100
+ readonly httpStatus: number;
101
+ constructor(service: string, options?: NageErrorOptions);
102
+ }
103
+ /** 500 — the data layer failed. SQL and driver detail stay in the log. */
104
+ export declare class DatabaseError extends FixedCodeError {
105
+ readonly code: "DATABASE_ERROR";
106
+ readonly httpStatus: number;
107
+ constructor(options?: NageErrorOptions);
108
+ }
109
+ /** 500 — a `UnitOfWork` could not commit. */
110
+ export declare class TransactionFailedError extends FixedCodeError {
111
+ readonly code: "TRANSACTION_FAILED";
112
+ readonly httpStatus: number;
113
+ constructor(options?: NageErrorOptions);
114
+ }
115
+ /**
116
+ * 500 — invalid configuration. Thrown at boot so a misconfigured process fails
117
+ * fast rather than serving traffic insecurely (§21).
118
+ */
119
+ export declare class ConfigurationError extends FixedCodeError {
120
+ readonly code: "CONFIGURATION_INVALID";
121
+ readonly httpStatus: number;
122
+ constructor(options?: NageErrorOptions);
123
+ }
124
+ /** 501 — the selected driver does not implement this capability. */
125
+ export declare class UnsupportedOperationError extends FixedCodeError {
126
+ readonly code: "UNSUPPORTED_OPERATION";
127
+ readonly httpStatus: number;
128
+ constructor(options?: NageErrorOptions);
129
+ }
130
+ /** 500 — the catch-all. Anything unrecognised becomes this at the boundary. */
131
+ export declare class InternalError extends FixedCodeError {
132
+ readonly code: "INTERNAL_ERROR";
133
+ readonly httpStatus: number;
134
+ constructor(options?: NageErrorOptions);
135
+ }
136
+ /** 422 — a business rule rejected an otherwise well-formed request. */
137
+ export declare class DomainError extends FixedCodeError {
138
+ readonly code: "DOMAIN_RULE_VIOLATED";
139
+ readonly httpStatus: number;
140
+ constructor(options?: NageErrorOptions);
141
+ }
142
+ /** 412 — a stated precondition did not hold. */
143
+ export declare class PreconditionFailedError extends FixedCodeError {
144
+ readonly code: "PRECONDITION_FAILED";
145
+ readonly httpStatus: number;
146
+ constructor(options?: NageErrorOptions);
147
+ }
148
+ export {};
149
+ //# sourceMappingURL=catalog.d.ts.map
@@ -0,0 +1,289 @@
1
+ "use strict";
2
+ /**
3
+ * The concrete error catalog (PLAN.md §17.1).
4
+ *
5
+ * Each class fixes its stable `code` and its HTTP status, so mapping lives with
6
+ * the error rather than in a `switch` inside the filter, and clients can program
7
+ * against codes instead of messages.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.PreconditionFailedError = exports.DomainError = exports.InternalError = exports.UnsupportedOperationError = exports.ConfigurationError = exports.TransactionFailedError = exports.DatabaseError = exports.ExternalServiceError = exports.RequestTimeoutError = exports.RateLimitError = exports.OptimisticLockError = exports.ConflictError = exports.NotFoundError = exports.AuthorizationError = exports.AuthenticationError = exports.QueryLimitExceededError = exports.InvalidQueryError = exports.ValidationError = exports.DEFAULT_SAFE_MESSAGE = exports.ERROR_STATUS_BY_CODE = void 0;
11
+ const nage_error_js_1 = require("./nage.error.js");
12
+ /**
13
+ * Status for every code in the catalog. Also consumed by the OpenAPI generator
14
+ * (Phase 6) so documented error schemas cannot drift from runtime behaviour.
15
+ */
16
+ exports.ERROR_STATUS_BY_CODE = {
17
+ VALIDATION_FAILED: 422,
18
+ INVALID_QUERY: 400,
19
+ QUERY_LIMIT_EXCEEDED: 400,
20
+ PAYLOAD_TOO_LARGE: 413,
21
+ AUTH_INVALID_CREDENTIALS: 401,
22
+ AUTH_TOKEN_EXPIRED: 401,
23
+ AUTH_TOKEN_INVALID: 401,
24
+ AUTH_TOKEN_REUSED: 401,
25
+ AUTH_SESSION_REVOKED: 401,
26
+ AUTH_ACCOUNT_LOCKED: 423,
27
+ AUTH_OTP_INVALID: 401,
28
+ AUTH_OTP_EXPIRED: 401,
29
+ AUTH_REQUIRED: 401,
30
+ FORBIDDEN: 403,
31
+ INSUFFICIENT_ROLE: 403,
32
+ INSUFFICIENT_PERMISSION: 403,
33
+ POLICY_DENIED: 403,
34
+ RESOURCE_NOT_FOUND: 404,
35
+ RESOURCE_CONFLICT: 409,
36
+ RESOURCE_GONE: 410,
37
+ OPTIMISTIC_LOCK_CONFLICT: 409,
38
+ RATE_LIMIT_EXCEEDED: 429,
39
+ REQUEST_TIMEOUT: 504,
40
+ EXTERNAL_SERVICE_ERROR: 502,
41
+ DATABASE_ERROR: 500,
42
+ TRANSACTION_FAILED: 500,
43
+ CONFIGURATION_INVALID: 500,
44
+ UNSUPPORTED_OPERATION: 501,
45
+ INTERNAL_ERROR: 500,
46
+ DOMAIN_RULE_VIOLATED: 422,
47
+ PRECONDITION_FAILED: 412,
48
+ };
49
+ /**
50
+ * Client-safe default message per code. Deliberately generic: an authentication
51
+ * failure never reveals whether it was the address or the password that was
52
+ * wrong.
53
+ */
54
+ exports.DEFAULT_SAFE_MESSAGE = {
55
+ VALIDATION_FAILED: 'Validation failed',
56
+ INVALID_QUERY: 'The query is not valid',
57
+ QUERY_LIMIT_EXCEEDED: 'The requested page size is too large',
58
+ PAYLOAD_TOO_LARGE: 'The request body is too large',
59
+ AUTH_INVALID_CREDENTIALS: 'Invalid credentials',
60
+ AUTH_TOKEN_EXPIRED: 'The token has expired',
61
+ AUTH_TOKEN_INVALID: 'The token is not valid',
62
+ AUTH_TOKEN_REUSED: 'The session has been revoked',
63
+ AUTH_SESSION_REVOKED: 'The session has been revoked',
64
+ AUTH_ACCOUNT_LOCKED: 'The account is temporarily locked',
65
+ AUTH_OTP_INVALID: 'The code is not valid',
66
+ AUTH_OTP_EXPIRED: 'The code has expired',
67
+ AUTH_REQUIRED: 'Authentication is required',
68
+ FORBIDDEN: 'You are not allowed to perform this action',
69
+ INSUFFICIENT_ROLE: 'You are not allowed to perform this action',
70
+ INSUFFICIENT_PERMISSION: 'You are not allowed to perform this action',
71
+ POLICY_DENIED: 'You are not allowed to perform this action',
72
+ RESOURCE_NOT_FOUND: 'The requested resource was not found',
73
+ RESOURCE_CONFLICT: 'The resource conflicts with the current state',
74
+ RESOURCE_GONE: 'The resource is no longer available',
75
+ OPTIMISTIC_LOCK_CONFLICT: 'The resource was modified by someone else',
76
+ RATE_LIMIT_EXCEEDED: 'Too many requests',
77
+ REQUEST_TIMEOUT: 'The request took too long to process',
78
+ EXTERNAL_SERVICE_ERROR: 'An upstream service failed',
79
+ DATABASE_ERROR: 'Internal server error',
80
+ TRANSACTION_FAILED: 'Internal server error',
81
+ CONFIGURATION_INVALID: 'Internal server error',
82
+ UNSUPPORTED_OPERATION: 'This operation is not supported',
83
+ INTERNAL_ERROR: 'Internal server error',
84
+ DOMAIN_RULE_VIOLATED: 'The request violates a business rule',
85
+ PRECONDITION_FAILED: 'A precondition for this request was not met',
86
+ };
87
+ /** Base for errors whose code is fixed by the class. */
88
+ class FixedCodeError extends nage_error_js_1.NageError {
89
+ constructor(code, options = {}) {
90
+ super(exports.DEFAULT_SAFE_MESSAGE[code], options);
91
+ }
92
+ }
93
+ /** 422 — the request body or params failed validation. */
94
+ class ValidationError extends FixedCodeError {
95
+ code = 'VALIDATION_FAILED';
96
+ httpStatus = exports.ERROR_STATUS_BY_CODE.VALIDATION_FAILED;
97
+ constructor(options = {}) {
98
+ super('VALIDATION_FAILED', options);
99
+ }
100
+ /** Build from field-level feedback, the common case for a `ValidationPipe`. */
101
+ static fromDetails(details, message) {
102
+ return new ValidationError(message === undefined ? { details } : { details, message });
103
+ }
104
+ }
105
+ exports.ValidationError = ValidationError;
106
+ /** 400 — the query DSL was malformed or referenced a disallowed field. */
107
+ class InvalidQueryError extends FixedCodeError {
108
+ code = 'INVALID_QUERY';
109
+ httpStatus = exports.ERROR_STATUS_BY_CODE.INVALID_QUERY;
110
+ constructor(options = {}) {
111
+ super('INVALID_QUERY', options);
112
+ }
113
+ }
114
+ exports.InvalidQueryError = InvalidQueryError;
115
+ /** 400 — `limit` exceeded the model's `maxLimit` (no unbounded queries, §12). */
116
+ class QueryLimitExceededError extends FixedCodeError {
117
+ maxLimit;
118
+ code = 'QUERY_LIMIT_EXCEEDED';
119
+ httpStatus = exports.ERROR_STATUS_BY_CODE.QUERY_LIMIT_EXCEEDED;
120
+ constructor(maxLimit, options = {}) {
121
+ super('QUERY_LIMIT_EXCEEDED', {
122
+ ...options,
123
+ message: options.message ?? `The requested page size exceeds the maximum of ${maxLimit}`,
124
+ });
125
+ this.maxLimit = maxLimit;
126
+ }
127
+ }
128
+ exports.QueryLimitExceededError = QueryLimitExceededError;
129
+ /** 401/423 — the caller could not be authenticated. */
130
+ class AuthenticationError extends nage_error_js_1.NageError {
131
+ code;
132
+ httpStatus;
133
+ constructor(code = 'AUTH_REQUIRED', options = {}) {
134
+ super(exports.DEFAULT_SAFE_MESSAGE[code], options);
135
+ this.code = code;
136
+ this.httpStatus = exports.ERROR_STATUS_BY_CODE[code];
137
+ }
138
+ }
139
+ exports.AuthenticationError = AuthenticationError;
140
+ /** 403 — the caller is known but not allowed. */
141
+ class AuthorizationError extends nage_error_js_1.NageError {
142
+ code;
143
+ httpStatus;
144
+ constructor(code = 'FORBIDDEN', options = {}) {
145
+ super(exports.DEFAULT_SAFE_MESSAGE[code], options);
146
+ this.code = code;
147
+ this.httpStatus = exports.ERROR_STATUS_BY_CODE[code];
148
+ }
149
+ }
150
+ exports.AuthorizationError = AuthorizationError;
151
+ /** 404 — the resource does not exist, or the caller may not know that it does. */
152
+ class NotFoundError extends FixedCodeError {
153
+ code = 'RESOURCE_NOT_FOUND';
154
+ httpStatus = exports.ERROR_STATUS_BY_CODE.RESOURCE_NOT_FOUND;
155
+ constructor(options = {}) {
156
+ super('RESOURCE_NOT_FOUND', options);
157
+ }
158
+ }
159
+ exports.NotFoundError = NotFoundError;
160
+ /** 409 — the write conflicts with the current state (e.g. a unique constraint). */
161
+ class ConflictError extends FixedCodeError {
162
+ code = 'RESOURCE_CONFLICT';
163
+ httpStatus = exports.ERROR_STATUS_BY_CODE.RESOURCE_CONFLICT;
164
+ constructor(options = {}) {
165
+ super('RESOURCE_CONFLICT', options);
166
+ }
167
+ }
168
+ exports.ConflictError = ConflictError;
169
+ /** 409 — the record changed since it was read (optimistic locking, §14.2). */
170
+ class OptimisticLockError extends FixedCodeError {
171
+ code = 'OPTIMISTIC_LOCK_CONFLICT';
172
+ httpStatus = exports.ERROR_STATUS_BY_CODE.OPTIMISTIC_LOCK_CONFLICT;
173
+ constructor(options = {}) {
174
+ super('OPTIMISTIC_LOCK_CONFLICT', options);
175
+ }
176
+ }
177
+ exports.OptimisticLockError = OptimisticLockError;
178
+ /** 429 — throttled. Carries the retry hint for the `Retry-After` header. */
179
+ class RateLimitError extends FixedCodeError {
180
+ retryAfterSeconds;
181
+ code = 'RATE_LIMIT_EXCEEDED';
182
+ httpStatus = exports.ERROR_STATUS_BY_CODE.RATE_LIMIT_EXCEEDED;
183
+ constructor(retryAfterSeconds, options = {}) {
184
+ super('RATE_LIMIT_EXCEEDED', options);
185
+ this.retryAfterSeconds = retryAfterSeconds;
186
+ }
187
+ }
188
+ exports.RateLimitError = RateLimitError;
189
+ /**
190
+ * 504 — the handler exceeded the request timeout (§21).
191
+ *
192
+ * 504 rather than 503: the deployment is up and took the request, it was this
193
+ * request that ran out of time, and a client that retries a 503 immediately
194
+ * would make a slow dependency slower.
195
+ */
196
+ class RequestTimeoutError extends FixedCodeError {
197
+ timeoutMs;
198
+ code = 'REQUEST_TIMEOUT';
199
+ httpStatus = exports.ERROR_STATUS_BY_CODE.REQUEST_TIMEOUT;
200
+ constructor(timeoutMs, options = {}) {
201
+ super('REQUEST_TIMEOUT', {
202
+ ...options,
203
+ meta: { ...options.meta, timeoutMs },
204
+ });
205
+ this.timeoutMs = timeoutMs;
206
+ }
207
+ }
208
+ exports.RequestTimeoutError = RequestTimeoutError;
209
+ /** 502 — an upstream dependency failed. Its body is logged, never returned. */
210
+ class ExternalServiceError extends FixedCodeError {
211
+ service;
212
+ code = 'EXTERNAL_SERVICE_ERROR';
213
+ httpStatus = exports.ERROR_STATUS_BY_CODE.EXTERNAL_SERVICE_ERROR;
214
+ constructor(service, options = {}) {
215
+ super('EXTERNAL_SERVICE_ERROR', {
216
+ ...options,
217
+ meta: { ...options.meta, service },
218
+ });
219
+ this.service = service;
220
+ }
221
+ }
222
+ exports.ExternalServiceError = ExternalServiceError;
223
+ /** 500 — the data layer failed. SQL and driver detail stay in the log. */
224
+ class DatabaseError extends FixedCodeError {
225
+ code = 'DATABASE_ERROR';
226
+ httpStatus = exports.ERROR_STATUS_BY_CODE.DATABASE_ERROR;
227
+ constructor(options = {}) {
228
+ super('DATABASE_ERROR', options);
229
+ }
230
+ }
231
+ exports.DatabaseError = DatabaseError;
232
+ /** 500 — a `UnitOfWork` could not commit. */
233
+ class TransactionFailedError extends FixedCodeError {
234
+ code = 'TRANSACTION_FAILED';
235
+ httpStatus = exports.ERROR_STATUS_BY_CODE.TRANSACTION_FAILED;
236
+ constructor(options = {}) {
237
+ super('TRANSACTION_FAILED', options);
238
+ }
239
+ }
240
+ exports.TransactionFailedError = TransactionFailedError;
241
+ /**
242
+ * 500 — invalid configuration. Thrown at boot so a misconfigured process fails
243
+ * fast rather than serving traffic insecurely (§21).
244
+ */
245
+ class ConfigurationError extends FixedCodeError {
246
+ code = 'CONFIGURATION_INVALID';
247
+ httpStatus = exports.ERROR_STATUS_BY_CODE.CONFIGURATION_INVALID;
248
+ constructor(options = {}) {
249
+ super('CONFIGURATION_INVALID', options);
250
+ }
251
+ }
252
+ exports.ConfigurationError = ConfigurationError;
253
+ /** 501 — the selected driver does not implement this capability. */
254
+ class UnsupportedOperationError extends FixedCodeError {
255
+ code = 'UNSUPPORTED_OPERATION';
256
+ httpStatus = exports.ERROR_STATUS_BY_CODE.UNSUPPORTED_OPERATION;
257
+ constructor(options = {}) {
258
+ super('UNSUPPORTED_OPERATION', options);
259
+ }
260
+ }
261
+ exports.UnsupportedOperationError = UnsupportedOperationError;
262
+ /** 500 — the catch-all. Anything unrecognised becomes this at the boundary. */
263
+ class InternalError extends FixedCodeError {
264
+ code = 'INTERNAL_ERROR';
265
+ httpStatus = exports.ERROR_STATUS_BY_CODE.INTERNAL_ERROR;
266
+ constructor(options = {}) {
267
+ super('INTERNAL_ERROR', options);
268
+ }
269
+ }
270
+ exports.InternalError = InternalError;
271
+ /** 422 — a business rule rejected an otherwise well-formed request. */
272
+ class DomainError extends FixedCodeError {
273
+ code = 'DOMAIN_RULE_VIOLATED';
274
+ httpStatus = exports.ERROR_STATUS_BY_CODE.DOMAIN_RULE_VIOLATED;
275
+ constructor(options = {}) {
276
+ super('DOMAIN_RULE_VIOLATED', options);
277
+ }
278
+ }
279
+ exports.DomainError = DomainError;
280
+ /** 412 — a stated precondition did not hold. */
281
+ class PreconditionFailedError extends FixedCodeError {
282
+ code = 'PRECONDITION_FAILED';
283
+ httpStatus = exports.ERROR_STATUS_BY_CODE.PRECONDITION_FAILED;
284
+ constructor(options = {}) {
285
+ super('PRECONDITION_FAILED', options);
286
+ }
287
+ }
288
+ exports.PreconditionFailedError = PreconditionFailedError;
289
+ //# sourceMappingURL=catalog.js.map
@@ -0,0 +1,3 @@
1
+ export { NageError, isNageError, type NageErrorOptions } from './nage.error.js';
2
+ export * from './catalog.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.isNageError = exports.NageError = void 0;
18
+ var nage_error_js_1 = require("./nage.error.js");
19
+ Object.defineProperty(exports, "NageError", { enumerable: true, get: function () { return nage_error_js_1.NageError; } });
20
+ Object.defineProperty(exports, "isNageError", { enumerable: true, get: function () { return nage_error_js_1.isNageError; } });
21
+ __exportStar(require("./catalog.js"), exports);
22
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Base class of the typed error hierarchy (PLAN.md §17).
3
+ *
4
+ * Two audiences, deliberately separated:
5
+ * - the **client** gets `code`, `safeMessage` and (for validation) `details`
6
+ * - the **server log** gets everything else, including `meta` and the cause
7
+ *
8
+ * `meta` is never serialized into a response. That is the whole point: the
9
+ * legacy `ErrorResponse` echoed `error.message` on 500s, leaking SQL and
10
+ * upstream payloads to callers.
11
+ */
12
+ import type { ErrorCode, ErrorDetail, ErrorMetadata, ErrorPayload } from '@nage-api/contracts';
13
+ export interface NageErrorOptions {
14
+ /**
15
+ * Client-safe message, replacing the code's default. Only pass a string here
16
+ * that you would be content to see in a public API response.
17
+ */
18
+ readonly message?: string;
19
+ /**
20
+ * Operator-facing description: becomes `Error.message`, so it appears in
21
+ * stack traces and log lines, but never in a response. This is where the
22
+ * useful specifics go — which variable, which model, which secret.
23
+ */
24
+ readonly detail?: string;
25
+ /** Field-level feedback; the only detail ever sent to a client. */
26
+ readonly details?: readonly ErrorDetail[];
27
+ /** Server-only debugging context. Logged, never serialized. */
28
+ readonly meta?: ErrorMetadata;
29
+ readonly cause?: unknown;
30
+ }
31
+ export declare abstract class NageError extends Error {
32
+ abstract readonly code: ErrorCode;
33
+ abstract readonly httpStatus: number;
34
+ /** Message safe to return to the caller. */
35
+ readonly safeMessage: string;
36
+ readonly details?: readonly ErrorDetail[];
37
+ readonly meta?: ErrorMetadata;
38
+ protected constructor(safeMessage: string, options?: NageErrorOptions);
39
+ /** The client-facing projection. Note what it omits. */
40
+ toPayload(): ErrorPayload;
41
+ }
42
+ export declare function isNageError(value: unknown): value is NageError;
43
+ //# sourceMappingURL=nage.error.d.ts.map
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ /**
3
+ * Base class of the typed error hierarchy (PLAN.md §17).
4
+ *
5
+ * Two audiences, deliberately separated:
6
+ * - the **client** gets `code`, `safeMessage` and (for validation) `details`
7
+ * - the **server log** gets everything else, including `meta` and the cause
8
+ *
9
+ * `meta` is never serialized into a response. That is the whole point: the
10
+ * legacy `ErrorResponse` echoed `error.message` on 500s, leaking SQL and
11
+ * upstream payloads to callers.
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.NageError = void 0;
15
+ exports.isNageError = isNageError;
16
+ class NageError extends Error {
17
+ /** Message safe to return to the caller. */
18
+ safeMessage;
19
+ details;
20
+ meta;
21
+ constructor(safeMessage, options = {}) {
22
+ // Error.message is the operator's view; safeMessage is the caller's. Keeping
23
+ // them separate is what lets an error say "Required secret JWT_PRIVATE_KEY
24
+ // is not set" in the log and "Internal server error" on the wire.
25
+ super(options.detail ?? options.message ?? safeMessage, options.cause === undefined ? {} : { cause: options.cause });
26
+ this.name = new.target.name;
27
+ this.safeMessage = options.message ?? safeMessage;
28
+ if (options.details !== undefined)
29
+ this.details = options.details;
30
+ if (options.meta !== undefined)
31
+ this.meta = options.meta;
32
+ Error.captureStackTrace(this, new.target);
33
+ }
34
+ /** The client-facing projection. Note what it omits. */
35
+ toPayload() {
36
+ return this.details === undefined
37
+ ? { code: this.code, message: this.safeMessage }
38
+ : { code: this.code, message: this.safeMessage, details: this.details };
39
+ }
40
+ }
41
+ exports.NageError = NageError;
42
+ function isNageError(value) {
43
+ return value instanceof NageError;
44
+ }
45
+ //# sourceMappingURL=nage.error.js.map
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Enforces the version rules declared by `@ForVersion` and friends
3
+ * (PLAN.md §16.2).
4
+ *
5
+ * A route that does not apply to the requested version is reported as **not
6
+ * found**, not as a validation error: from the client's point of view that
7
+ * endpoint does not exist in the version it asked for.
8
+ */
9
+ import { type CanActivate, type ExecutionContext } from '@nestjs/common';
10
+ import { Reflector } from '@nestjs/core';
11
+ import type { NageCoreConfig } from '@nage-api/contracts';
12
+ import { RequestContextService } from '../context/request-context.service.js';
13
+ export declare class ApiVersionGuard implements CanActivate {
14
+ private readonly reflector;
15
+ private readonly context;
16
+ private readonly config;
17
+ constructor(reflector: Reflector, context: RequestContextService, config: NageCoreConfig);
18
+ canActivate(context: ExecutionContext): boolean;
19
+ }
20
+ //# sourceMappingURL=api-version.guard.d.ts.map