@cullet/erp-core 1.1.0 → 1.3.0
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/KIT_CONTEXT.md +1 -67
- package/dist/app-error.d.cts +108 -0
- package/dist/application/index.cjs +26 -0
- package/dist/application/index.d.cts +2 -0
- package/dist/application/index.d.ts +1 -1
- package/dist/application/index.js +2 -1
- package/dist/domain/index.cjs +16 -0
- package/dist/domain/index.d.cts +3 -0
- package/dist/domain/index.d.ts +3 -0
- package/dist/domain/index.js +3 -0
- package/dist/domain-event-contracts.cjs +33 -0
- package/dist/domain-event-contracts.cjs.map +1 -0
- package/dist/domain-event-contracts.d.cts +27 -0
- package/dist/domain-event-contracts.d.ts +27 -0
- package/dist/domain-event-contracts.js +22 -0
- package/dist/domain-event-contracts.js.map +1 -0
- package/dist/domain-exception.cjs +17 -0
- package/dist/domain-exception.cjs.map +1 -0
- package/dist/domain-exception.d.cts +7 -0
- package/dist/domain-exception.d.ts +7 -0
- package/dist/domain-exception.js +12 -0
- package/dist/domain-exception.js.map +1 -0
- package/dist/errors/index.cjs +41 -0
- package/dist/errors/index.d.cts +3 -0
- package/dist/errors/index.js +1 -1
- package/dist/exceptions/index.cjs +17 -0
- package/dist/exceptions/index.d.cts +5 -0
- package/dist/exceptions/index.d.ts +5 -0
- package/dist/exceptions/index.js +7 -0
- package/dist/exceptions/validation-field.cjs +3 -0
- package/dist/exceptions/validation-field.d.cts +2 -0
- package/dist/gate-engine-registry.cjs +308 -0
- package/dist/gate-engine-registry.cjs.map +1 -0
- package/dist/gate-engine-registry.d.cts +81 -0
- package/dist/gate-engine-registry.d.ts +2 -1
- package/dist/gate-engine-registry.js +2 -1
- package/dist/gate-engine-registry.js.map +1 -1
- package/dist/gate-types.d.cts +171 -0
- package/dist/gate-types.d.ts +3 -139
- package/dist/gate-v1-payload.schema.cjs +638 -0
- package/dist/gate-v1-payload.schema.cjs.map +1 -0
- package/dist/gate-v1-payload.schema.js +2 -116
- package/dist/gate-v1-payload.schema.js.map +1 -1
- package/dist/hashing.cjs +66 -0
- package/dist/hashing.cjs.map +1 -0
- package/dist/immutable.cjs +77 -0
- package/dist/immutable.cjs.map +1 -0
- package/dist/immutable.d.cts +6 -0
- package/dist/immutable.d.ts +6 -0
- package/dist/immutable.js +54 -0
- package/dist/immutable.js.map +1 -0
- package/dist/index.cjs +163 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +30 -0
- package/dist/index.d.ts +15 -160
- package/dist/index.js +19 -176
- package/dist/index.js.map +1 -1
- package/dist/invalid-state-transition-exception.cjs +47 -0
- package/dist/invalid-state-transition-exception.cjs.map +1 -0
- package/dist/invalid-state-transition-exception.js +30 -0
- package/dist/invalid-state-transition-exception.js.map +1 -0
- package/dist/invariant-violation-exception.cjs +16 -0
- package/dist/invariant-violation-exception.cjs.map +1 -0
- package/dist/invariant-violation-exception.js +11 -0
- package/dist/invariant-violation-exception.js.map +1 -0
- package/dist/not-found-error.cjs +65 -0
- package/dist/not-found-error.cjs.map +1 -0
- package/dist/not-found-error.js +1 -1
- package/dist/outcome.cjs +97 -0
- package/dist/outcome.cjs.map +1 -0
- package/dist/outcome.d.cts +140 -0
- package/dist/outcome.d.ts +140 -0
- package/dist/outcome.js +1 -1
- package/dist/parse-gate-payload.d.cts +62 -0
- package/dist/parse-gate-payload.d.ts +2 -1
- package/dist/path.d.cts +90 -0
- package/dist/path.d.ts +2 -1
- package/dist/plugin.cjs +79 -0
- package/dist/plugin.cjs.map +1 -0
- package/dist/plugin.d.cts +85 -0
- package/dist/plugin.d.ts +85 -0
- package/dist/plugin.js +74 -0
- package/dist/plugin.js.map +1 -0
- package/dist/plugins/index.cjs +3 -0
- package/dist/plugins/index.d.cts +2 -0
- package/dist/plugins/index.d.ts +2 -0
- package/dist/plugins/index.js +2 -0
- package/dist/policies/engines/index.cjs +10 -0
- package/dist/policies/engines/index.d.cts +4 -0
- package/dist/policies/engines/index.d.ts +1 -1
- package/dist/policies/engines/v1/gate/index.cjs +91 -0
- package/dist/policies/engines/v1/gate/index.cjs.map +1 -0
- package/dist/policies/engines/v1/gate/index.d.cts +121 -0
- package/dist/policies/engines/v1/gate/index.d.ts +2 -1
- package/dist/policies/engines/v1/gate/index.js +2 -1
- package/dist/policies/engines/v1/gate/index.js.map +1 -1
- package/dist/policies/index.cjs +41 -0
- package/dist/policies/index.d.cts +7 -0
- package/dist/policies/index.d.ts +2 -1
- package/dist/policies/index.js +2 -1
- package/dist/policy-service.cjs +1190 -0
- package/dist/policy-service.cjs.map +1 -0
- package/dist/policy-service.d.cts +559 -0
- package/dist/policy-service.d.ts +2 -1
- package/dist/policy-service.js +6 -3
- package/dist/policy-service.js.map +1 -1
- package/dist/result/index.cjs +7 -0
- package/dist/result/index.d.cts +2 -0
- package/dist/result/index.d.ts +2 -0
- package/dist/result/index.js +3 -0
- package/dist/result.cjs +135 -0
- package/dist/result.cjs.map +1 -0
- package/dist/result.js +118 -0
- package/dist/result.js.map +1 -0
- package/dist/ruleset-registry.cjs +47 -0
- package/dist/ruleset-registry.cjs.map +1 -0
- package/dist/ruleset-registry.js +42 -0
- package/dist/ruleset-registry.js.map +1 -0
- package/dist/rulesets/index.cjs +3 -0
- package/dist/rulesets/index.d.cts +2 -0
- package/dist/rulesets/index.d.ts +2 -0
- package/dist/rulesets/index.js +2 -0
- package/dist/temporal-guards.cjs +32 -0
- package/dist/temporal-guards.cjs.map +1 -0
- package/dist/temporal-guards.js +2 -17
- package/dist/temporal-guards.js.map +1 -1
- package/dist/temporal-use-case.cjs +191 -0
- package/dist/temporal-use-case.cjs.map +1 -0
- package/dist/temporal-use-case.d.cts +304 -0
- package/dist/temporal-use-case.d.ts +4 -9
- package/dist/temporal-use-case.js +6 -140
- package/dist/temporal-use-case.js.map +1 -1
- package/dist/unexpected-error.cjs +208 -0
- package/dist/unexpected-error.cjs.map +1 -0
- package/dist/unexpected-error.js +179 -0
- package/dist/unexpected-error.js.map +1 -0
- package/dist/use-case.cjs +96 -0
- package/dist/use-case.cjs.map +1 -0
- package/dist/use-case.js +91 -0
- package/dist/use-case.js.map +1 -0
- package/dist/uuid-identifier.cjs +65 -0
- package/dist/uuid-identifier.cjs.map +1 -0
- package/dist/uuid-identifier.d.cts +230 -0
- package/dist/uuid-identifier.d.ts +230 -0
- package/dist/uuid-identifier.js +60 -0
- package/dist/uuid-identifier.js.map +1 -0
- package/dist/validation-code.cjs +60 -0
- package/dist/validation-code.cjs.map +1 -0
- package/dist/validation-code.d.cts +23 -0
- package/dist/validation-code.d.ts +23 -0
- package/dist/validation-code.js +1 -177
- package/dist/validation-code.js.map +1 -1
- package/dist/validation-error.cjs +1020 -0
- package/dist/validation-error.cjs.map +1 -0
- package/dist/validation-error.d.cts +777 -0
- package/dist/validation-error.d.ts +1 -21
- package/dist/validation-error.js +1 -1
- package/dist/validation-exception.cjs +41 -0
- package/dist/validation-exception.cjs.map +1 -0
- package/dist/validation-exception.d.cts +50 -0
- package/dist/validation-exception.d.ts +50 -0
- package/dist/validation-exception.js +8 -2
- package/dist/validation-exception.js.map +1 -1
- package/dist/validation-field.cjs +28 -0
- package/dist/validation-field.cjs.map +1 -0
- package/dist/validation-field.d.cts +12 -0
- package/dist/value-object-ruleset.contracts.d.cts +36 -0
- package/dist/value-object-ruleset.contracts.d.ts +36 -0
- package/dist/value-object.cjs +208 -0
- package/dist/value-object.cjs.map +1 -0
- package/dist/value-object.js +191 -0
- package/dist/value-object.js.map +1 -0
- package/dist/version.d.cts +10 -0
- package/dist/version.d.ts +10 -0
- package/dist/versioning/index.cjs +7 -0
- package/dist/versioning/index.d.cts +3 -0
- package/dist/versioning/index.d.ts +3 -0
- package/dist/versioning/index.js +3 -0
- package/meta.json +18 -3
- package/package.json +165 -17
- package/src/core/domain/rulesets/index.ts +7 -0
- package/src/core/domain/uuid-identifier.ts +72 -0
- package/src/core/domain/value-object.ts +37 -5
- package/src/core/exceptions/index.ts +13 -0
- package/src/core/index.ts +14 -2
- package/src/core/plugins/index.ts +7 -0
- package/src/core/plugins/plugin.ts +99 -0
- package/src/core/plugins/types.ts +53 -0
- package/src/core/versioning/index.ts +14 -0
- package/src/domain/index.ts +7 -0
- package/src/exceptions/index.ts +1 -0
- package/src/plugins/index.ts +1 -0
- package/src/result/index.ts +2 -0
- package/src/rulesets/index.ts +1 -0
- package/src/version.ts +1 -1
- package/src/versioning/index.ts +1 -0
|
@@ -0,0 +1,1020 @@
|
|
|
1
|
+
const require_unexpected_error = require("./unexpected-error.cjs");
|
|
2
|
+
//#region src/core/errors/authentication-error.ts
|
|
3
|
+
var AuthenticationError = class AuthenticationError extends require_unexpected_error.AppError {
|
|
4
|
+
constructor(params) {
|
|
5
|
+
super(params.message, params.code, {
|
|
6
|
+
cause: params.cause,
|
|
7
|
+
metadata: {
|
|
8
|
+
reason: params.reason,
|
|
9
|
+
...params.metadata ?? {}
|
|
10
|
+
},
|
|
11
|
+
type: params.type,
|
|
12
|
+
severity: params.severity,
|
|
13
|
+
correlationId: params.correlationId,
|
|
14
|
+
requestId: params.requestId,
|
|
15
|
+
commandId: params.commandId,
|
|
16
|
+
createdAtIso: params.createdAtIso,
|
|
17
|
+
publicMessage: params.publicMessage
|
|
18
|
+
});
|
|
19
|
+
this.reason = params.reason;
|
|
20
|
+
Object.freeze(this);
|
|
21
|
+
}
|
|
22
|
+
static missingToken(options) {
|
|
23
|
+
return new AuthenticationError({
|
|
24
|
+
message: "Missing credentials",
|
|
25
|
+
code: require_unexpected_error.ErrorCodes.authentication.missingToken,
|
|
26
|
+
reason: "missing_token",
|
|
27
|
+
metadata: compactMetadata$1(options),
|
|
28
|
+
...extractAppErrorOptions$1(options)
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
static invalidToken(options) {
|
|
32
|
+
return new AuthenticationError({
|
|
33
|
+
message: "Invalid credentials",
|
|
34
|
+
code: require_unexpected_error.ErrorCodes.authentication.invalidToken,
|
|
35
|
+
reason: "invalid_token",
|
|
36
|
+
metadata: compactMetadata$1({
|
|
37
|
+
authScheme: options?.authScheme,
|
|
38
|
+
tokenLocation: options?.tokenLocation,
|
|
39
|
+
details: options?.details,
|
|
40
|
+
correlationId: options?.correlationId,
|
|
41
|
+
requestId: options?.requestId
|
|
42
|
+
}),
|
|
43
|
+
...extractAppErrorOptions$1(options)
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
static expiredToken(options) {
|
|
47
|
+
return new AuthenticationError({
|
|
48
|
+
message: "Expired credentials",
|
|
49
|
+
code: require_unexpected_error.ErrorCodes.authentication.expiredToken,
|
|
50
|
+
reason: "expired_token",
|
|
51
|
+
metadata: compactMetadata$1(options),
|
|
52
|
+
...extractAppErrorOptions$1(options)
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
static invalidCredentials(options) {
|
|
56
|
+
return new AuthenticationError({
|
|
57
|
+
message: "Invalid username or password",
|
|
58
|
+
code: require_unexpected_error.ErrorCodes.authentication.invalidCredentials,
|
|
59
|
+
reason: "invalid_credentials",
|
|
60
|
+
metadata: compactMetadata$1(options),
|
|
61
|
+
...extractAppErrorOptions$1(options)
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
function extractAppErrorOptions$1(options) {
|
|
66
|
+
return {
|
|
67
|
+
cause: options?.cause,
|
|
68
|
+
type: options?.type,
|
|
69
|
+
severity: options?.severity,
|
|
70
|
+
correlationId: options?.correlationId,
|
|
71
|
+
requestId: options?.requestId,
|
|
72
|
+
commandId: options?.commandId,
|
|
73
|
+
createdAtIso: options?.createdAtIso,
|
|
74
|
+
publicMessage: options?.publicMessage
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function compactMetadata$1(input) {
|
|
78
|
+
if (!input) return void 0;
|
|
79
|
+
return Object.fromEntries(Object.entries(input).filter(([, value]) => value !== void 0));
|
|
80
|
+
}
|
|
81
|
+
//#endregion
|
|
82
|
+
//#region src/core/errors/authorization-error.ts
|
|
83
|
+
/**
|
|
84
|
+
* Raised when an authenticated actor is not allowed to perform a business
|
|
85
|
+
* action — the "you may not" error, distinct from authentication ("who are
|
|
86
|
+
* you"). Maps to an HTTP 403 at the edge.
|
|
87
|
+
*
|
|
88
|
+
* The discriminating {@link reason} records *why* access was denied (a flat
|
|
89
|
+
* forbid, a missing role/capability, an out-of-scope target, or a policy
|
|
90
|
+
* decision) so the boundary can shape the response without re-deriving it. The
|
|
91
|
+
* metadata is deliberately built around a stable business `action` and a
|
|
92
|
+
* type/id resource reference rather than an HTTP route or full payload, keeping
|
|
93
|
+
* sensitive data out of logs. Instances are frozen. Construct through the
|
|
94
|
+
* static factories, never directly.
|
|
95
|
+
*/
|
|
96
|
+
var AuthorizationError = class AuthorizationError extends require_unexpected_error.AppError {
|
|
97
|
+
constructor(params) {
|
|
98
|
+
super(params.message, params.code, {
|
|
99
|
+
cause: params.cause,
|
|
100
|
+
metadata: params.metadata,
|
|
101
|
+
type: params.type,
|
|
102
|
+
severity: params.severity,
|
|
103
|
+
correlationId: params.correlationId,
|
|
104
|
+
requestId: params.requestId,
|
|
105
|
+
commandId: params.commandId,
|
|
106
|
+
createdAtIso: params.createdAtIso,
|
|
107
|
+
publicMessage: params.publicMessage
|
|
108
|
+
});
|
|
109
|
+
this.reason = params.reason;
|
|
110
|
+
Object.freeze(this);
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* A flat denial with no more specific reason — the actor simply may not
|
|
114
|
+
* perform this action. Reach for a more precise factory
|
|
115
|
+
* ({@link AuthorizationError.missingCapability}, {@link AuthorizationError.outOfScope},
|
|
116
|
+
* {@link AuthorizationError.policyDenied}) when the cause is known.
|
|
117
|
+
*/
|
|
118
|
+
static forbidden(input) {
|
|
119
|
+
return new AuthorizationError({
|
|
120
|
+
message: "Action not allowed",
|
|
121
|
+
code: require_unexpected_error.ErrorCodes.authorization.forbidden,
|
|
122
|
+
reason: "forbidden",
|
|
123
|
+
metadata: {
|
|
124
|
+
reason: "forbidden",
|
|
125
|
+
...extractMetadataOnly(input)
|
|
126
|
+
},
|
|
127
|
+
...extractAppErrorOptions(input)
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* The action was denied by an evaluated policy. Captures the deciding
|
|
132
|
+
* policy's id, version, and evaluation instant into the metadata (with
|
|
133
|
+
* `decision: "deny"`) so the denial is auditable back to the exact policy
|
|
134
|
+
* that produced it.
|
|
135
|
+
*
|
|
136
|
+
* @param input - Factory options plus the required `policyId`,
|
|
137
|
+
* `policyVersion`, and `evaluatedAtIso` of the deciding policy.
|
|
138
|
+
*/
|
|
139
|
+
static policyDenied(input) {
|
|
140
|
+
return new AuthorizationError({
|
|
141
|
+
message: "Action denied by policy",
|
|
142
|
+
code: require_unexpected_error.ErrorCodes.authorization.policyDenied,
|
|
143
|
+
reason: "policy_denied",
|
|
144
|
+
metadata: {
|
|
145
|
+
reason: "policy_denied",
|
|
146
|
+
...extractMetadataOnly(input),
|
|
147
|
+
decision: "deny"
|
|
148
|
+
},
|
|
149
|
+
...extractAppErrorOptions(input)
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* The actor lacks a required role or capability. The expected
|
|
154
|
+
* {@link AuthorizationRequirement} is recorded so the boundary can tell the
|
|
155
|
+
* caller precisely what grant is missing.
|
|
156
|
+
*
|
|
157
|
+
* @param input - Factory options plus the `required` role/capability/scope.
|
|
158
|
+
*/
|
|
159
|
+
static missingCapability(input) {
|
|
160
|
+
return new AuthorizationError({
|
|
161
|
+
message: "Insufficient capability to perform the action",
|
|
162
|
+
code: require_unexpected_error.ErrorCodes.authorization.missingCapability,
|
|
163
|
+
reason: "missing_capability",
|
|
164
|
+
metadata: {
|
|
165
|
+
reason: "missing_capability",
|
|
166
|
+
...extractMetadataOnly(input)
|
|
167
|
+
},
|
|
168
|
+
...extractAppErrorOptions(input)
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* The actor may perform the action in general, but not on *this* target —
|
|
173
|
+
* the resource falls outside the actor's permitted scope (e.g. a different
|
|
174
|
+
* school or tenant than the one they are bound to).
|
|
175
|
+
*
|
|
176
|
+
* @param input - Factory options plus the optional `required` scope that the
|
|
177
|
+
* target failed to satisfy.
|
|
178
|
+
*/
|
|
179
|
+
static outOfScope(input) {
|
|
180
|
+
return new AuthorizationError({
|
|
181
|
+
message: "Action outside the allowed scope",
|
|
182
|
+
code: require_unexpected_error.ErrorCodes.authorization.outOfScope,
|
|
183
|
+
reason: "out_of_scope",
|
|
184
|
+
metadata: {
|
|
185
|
+
reason: "out_of_scope",
|
|
186
|
+
...extractMetadataOnly(input)
|
|
187
|
+
},
|
|
188
|
+
...extractAppErrorOptions(input)
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
function extractAppErrorOptions(options) {
|
|
193
|
+
return {
|
|
194
|
+
cause: options?.cause,
|
|
195
|
+
type: options?.type,
|
|
196
|
+
severity: options?.severity,
|
|
197
|
+
correlationId: options?.correlationId,
|
|
198
|
+
requestId: options?.requestId,
|
|
199
|
+
commandId: options?.commandId,
|
|
200
|
+
createdAtIso: options?.createdAtIso,
|
|
201
|
+
publicMessage: options?.publicMessage
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
function extractMetadataOnly(input) {
|
|
205
|
+
const { cause, type, severity, correlationId, requestId, commandId, createdAtIso, publicMessage, ...metadata } = input;
|
|
206
|
+
return metadata;
|
|
207
|
+
}
|
|
208
|
+
//#endregion
|
|
209
|
+
//#region src/core/errors/conflict-error.ts
|
|
210
|
+
/**
|
|
211
|
+
* Base for errors that signal a state conflict — the request collides with data
|
|
212
|
+
* that already exists. Common at an HTTP 409 boundary.
|
|
213
|
+
*
|
|
214
|
+
* The discriminating {@link kind} lets a handler branch on *why* it conflicted
|
|
215
|
+
* (an already-existing record, a domain duplicate, or a raw storage uniqueness
|
|
216
|
+
* breach) without `instanceof` chains. Abstract: construct one of the concrete
|
|
217
|
+
* subclasses through its `detected(...)` factory, which fills in the right code,
|
|
218
|
+
* message, and metadata. Instances are frozen, so a conflict error can be shared
|
|
219
|
+
* and rethrown without risk of tampering.
|
|
220
|
+
*/
|
|
221
|
+
var ConflictError = class extends require_unexpected_error.AppError {
|
|
222
|
+
constructor(params) {
|
|
223
|
+
super(params.message, params.code, {
|
|
224
|
+
...params,
|
|
225
|
+
metadata: {
|
|
226
|
+
kind: params.kind,
|
|
227
|
+
...params.metadata
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
this.kind = params.kind;
|
|
231
|
+
Object.freeze(this);
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
/**
|
|
235
|
+
* The operation cannot proceed because a matching record already exists —
|
|
236
|
+
* e.g. creating something whose natural key is already taken. Construct via
|
|
237
|
+
* {@link AlreadyExistsError.detected}.
|
|
238
|
+
*/
|
|
239
|
+
var AlreadyExistsError = class AlreadyExistsError extends ConflictError {
|
|
240
|
+
constructor(input) {
|
|
241
|
+
super({
|
|
242
|
+
message: "A matching record already exists for the requested operation",
|
|
243
|
+
code: require_unexpected_error.ErrorCodes.conflict.alreadyExists,
|
|
244
|
+
kind: "already_exists",
|
|
245
|
+
...input
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Builds an {@link AlreadyExistsError} for a detected collision. `operation`
|
|
250
|
+
* defaults to `"create"` and a generic retry `hint` is supplied when none is
|
|
251
|
+
* given, so the error is actionable even from a minimal call site.
|
|
252
|
+
*
|
|
253
|
+
* @param input - The conflicting entity plus optional non-sensitive context
|
|
254
|
+
* (operation, field, existing id, value hash/preview, correlation ids).
|
|
255
|
+
*/
|
|
256
|
+
static detected(input) {
|
|
257
|
+
return new AlreadyExistsError({
|
|
258
|
+
metadata: {
|
|
259
|
+
entity: input.entity,
|
|
260
|
+
operation: input.operation ?? "create",
|
|
261
|
+
field: input.field,
|
|
262
|
+
existingId: input.existingId,
|
|
263
|
+
valueHash: input.valueHash,
|
|
264
|
+
valuePreview: input.valuePreview,
|
|
265
|
+
detectedAtIso: input.detectedAtIso,
|
|
266
|
+
correlationId: input.correlationId,
|
|
267
|
+
requestId: input.requestId,
|
|
268
|
+
commandId: input.commandId,
|
|
269
|
+
hint: input.hint ?? "Verify existing records before retrying."
|
|
270
|
+
},
|
|
271
|
+
cause: input.cause
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
/**
|
|
276
|
+
* A domain-level duplicate: the data being written would duplicate an existing
|
|
277
|
+
* record under the model's own uniqueness rules. Use this when the duplication
|
|
278
|
+
* is recognized in the domain, as opposed to a raw DB constraint
|
|
279
|
+
* ({@link UniqueConstraintViolationError}). Construct via
|
|
280
|
+
* {@link DuplicateError.detected}.
|
|
281
|
+
*/
|
|
282
|
+
var DuplicateError = class DuplicateError extends ConflictError {
|
|
283
|
+
constructor(input) {
|
|
284
|
+
super({
|
|
285
|
+
message: "Conflict: an existing record violates uniqueness",
|
|
286
|
+
code: require_unexpected_error.ErrorCodes.conflict.duplicate,
|
|
287
|
+
kind: "duplicate",
|
|
288
|
+
...input
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Builds a {@link DuplicateError} for a detected duplicate, defaulting to a
|
|
293
|
+
* "adjust the data so it does not duplicate" hint when none is provided.
|
|
294
|
+
*
|
|
295
|
+
* @param input - The conflicting entity plus optional non-sensitive context
|
|
296
|
+
* (field, constraint name, existing id, value hash/preview, correlation ids).
|
|
297
|
+
*/
|
|
298
|
+
static detected(input) {
|
|
299
|
+
return new DuplicateError({
|
|
300
|
+
metadata: {
|
|
301
|
+
entity: input.entity,
|
|
302
|
+
operation: input.operation,
|
|
303
|
+
field: input.field,
|
|
304
|
+
constraintName: input.constraintName,
|
|
305
|
+
existingId: input.existingId,
|
|
306
|
+
valueHash: input.valueHash,
|
|
307
|
+
valuePreview: input.valuePreview,
|
|
308
|
+
detectedAtIso: input.detectedAtIso,
|
|
309
|
+
correlationId: input.correlationId,
|
|
310
|
+
requestId: input.requestId,
|
|
311
|
+
commandId: input.commandId,
|
|
312
|
+
hint: input.hint ?? "Adjust the data so it does not duplicate existing records."
|
|
313
|
+
},
|
|
314
|
+
cause: input.cause
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
/**
|
|
319
|
+
* A uniqueness breach surfaced by the storage layer itself (a database unique
|
|
320
|
+
* index), carrying the raw {@link UniqueConstraintViolation} (constraint, table,
|
|
321
|
+
* columns). Keep this distinct from {@link DuplicateError}: this one is the
|
|
322
|
+
* infrastructure signal; translate it into a domain duplicate at the boundary
|
|
323
|
+
* with {@link translateUniqueViolationToDuplicate} when the model should own the
|
|
324
|
+
* message. Construct via {@link UniqueConstraintViolationError.detected}.
|
|
325
|
+
*/
|
|
326
|
+
var UniqueConstraintViolationError = class UniqueConstraintViolationError extends ConflictError {
|
|
327
|
+
constructor(input) {
|
|
328
|
+
super({
|
|
329
|
+
message: "Uniqueness violation in storage",
|
|
330
|
+
code: require_unexpected_error.ErrorCodes.conflict.uniqueViolation,
|
|
331
|
+
kind: "unique_violation",
|
|
332
|
+
...input
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Builds a {@link UniqueConstraintViolationError} from the constraint details
|
|
337
|
+
* a driver reports, packaging them into a nested `violation` payload.
|
|
338
|
+
*
|
|
339
|
+
* @param input - The entity plus the offending constraint name, table, and
|
|
340
|
+
* columns, and optional correlation ids.
|
|
341
|
+
*/
|
|
342
|
+
static detected(input) {
|
|
343
|
+
return new UniqueConstraintViolationError({
|
|
344
|
+
metadata: {
|
|
345
|
+
entity: input.entity,
|
|
346
|
+
operation: input.operation,
|
|
347
|
+
constraintName: input.constraintName,
|
|
348
|
+
detectedAtIso: input.detectedAtIso,
|
|
349
|
+
correlationId: input.correlationId,
|
|
350
|
+
requestId: input.requestId,
|
|
351
|
+
commandId: input.commandId,
|
|
352
|
+
violation: {
|
|
353
|
+
kind: "unique_violation",
|
|
354
|
+
constraintName: input.constraintName,
|
|
355
|
+
table: input.table,
|
|
356
|
+
columns: input.columns
|
|
357
|
+
},
|
|
358
|
+
hint: "Uniqueness conflict detected in the database."
|
|
359
|
+
},
|
|
360
|
+
cause: input.cause
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
/**
|
|
365
|
+
* Translates a raw storage {@link UniqueConstraintViolation} into a domain-level
|
|
366
|
+
* {@link DuplicateError}. This is the seam where an infrastructure concern (a DB
|
|
367
|
+
* unique index firing) is re-expressed in the language of the model, so callers
|
|
368
|
+
* upstream catch a `DuplicateError` and never have to know a database was
|
|
369
|
+
* involved.
|
|
370
|
+
*
|
|
371
|
+
* @param input - The entity, the driver-reported `violation`, and optional
|
|
372
|
+
* request context (`ctx`) carrying correlation ids and a clock instant.
|
|
373
|
+
* @returns A {@link DuplicateError} carrying the constraint name as its field hint.
|
|
374
|
+
*/
|
|
375
|
+
function translateUniqueViolationToDuplicate(input) {
|
|
376
|
+
return DuplicateError.detected({
|
|
377
|
+
entity: input.entity,
|
|
378
|
+
operation: input.operation,
|
|
379
|
+
field: input.field,
|
|
380
|
+
constraintName: input.violation.constraintName,
|
|
381
|
+
detectedAtIso: input.ctx?.nowIso,
|
|
382
|
+
correlationId: input.ctx?.correlationId,
|
|
383
|
+
requestId: input.ctx?.requestId,
|
|
384
|
+
commandId: input.ctx?.commandId,
|
|
385
|
+
cause: input.cause
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
//#endregion
|
|
389
|
+
//#region src/core/errors/idempotency-error.ts
|
|
390
|
+
var IdempotencyError = class extends require_unexpected_error.AppError {
|
|
391
|
+
constructor(input) {
|
|
392
|
+
super(input.message, input.code, {
|
|
393
|
+
cause: input.cause,
|
|
394
|
+
metadata: {
|
|
395
|
+
kind: input.kind,
|
|
396
|
+
...input.metadata
|
|
397
|
+
}
|
|
398
|
+
});
|
|
399
|
+
this.kind = input.kind;
|
|
400
|
+
Object.freeze(this);
|
|
401
|
+
}
|
|
402
|
+
static keyMissing(input) {
|
|
403
|
+
return IdempotencyKeyMissingError.detected(input);
|
|
404
|
+
}
|
|
405
|
+
static inProgress(input) {
|
|
406
|
+
return IdempotencyInProgressError.detected(input);
|
|
407
|
+
}
|
|
408
|
+
static payloadMismatch(input) {
|
|
409
|
+
return IdempotencyPayloadMismatchError.detected(input);
|
|
410
|
+
}
|
|
411
|
+
static replayNotSupported(input) {
|
|
412
|
+
return IdempotencyReplayNotSupportedError.detected(input);
|
|
413
|
+
}
|
|
414
|
+
};
|
|
415
|
+
var IdempotencyKeyMissingError = class IdempotencyKeyMissingError extends IdempotencyError {
|
|
416
|
+
constructor(input) {
|
|
417
|
+
super({
|
|
418
|
+
message: "Idempotency key/commandId missing for the requested operation",
|
|
419
|
+
code: require_unexpected_error.ErrorCodes.idempotency.keyMissing,
|
|
420
|
+
kind: "key_missing",
|
|
421
|
+
metadata: input.metadata,
|
|
422
|
+
cause: input.cause
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
static detected(input) {
|
|
426
|
+
return new IdempotencyKeyMissingError({
|
|
427
|
+
cause: input.cause,
|
|
428
|
+
metadata: {
|
|
429
|
+
operation: input.operation,
|
|
430
|
+
scope: input.scope,
|
|
431
|
+
entity: input.entity,
|
|
432
|
+
correlationId: input.correlationId,
|
|
433
|
+
requestId: input.requestId,
|
|
434
|
+
commandId: input.commandId,
|
|
435
|
+
detectedAtIso: input.detectedAtIso,
|
|
436
|
+
hint: input.hint ?? "Send a commandId/idempotency key to prevent duplicate processing.",
|
|
437
|
+
details: input.details
|
|
438
|
+
}
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
};
|
|
442
|
+
var IdempotencyInProgressError = class IdempotencyInProgressError extends IdempotencyError {
|
|
443
|
+
constructor(input) {
|
|
444
|
+
super({
|
|
445
|
+
message: "The same business intent is already being processed",
|
|
446
|
+
code: require_unexpected_error.ErrorCodes.idempotency.inProgress,
|
|
447
|
+
kind: "in_progress",
|
|
448
|
+
metadata: input.metadata,
|
|
449
|
+
cause: input.cause
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
static detected(input) {
|
|
453
|
+
const resolved = resolveKeyIdentity(input.key, input.keyHash, input.keyPreview);
|
|
454
|
+
return new IdempotencyInProgressError({
|
|
455
|
+
cause: input.cause,
|
|
456
|
+
metadata: {
|
|
457
|
+
operation: input.operation,
|
|
458
|
+
scope: input.scope,
|
|
459
|
+
entity: input.entity,
|
|
460
|
+
keyHash: resolved.keyHash,
|
|
461
|
+
keyPreview: resolved.keyPreview,
|
|
462
|
+
idempotencyRecordId: input.idempotencyRecordId,
|
|
463
|
+
correlationId: input.correlationId,
|
|
464
|
+
requestId: input.requestId,
|
|
465
|
+
commandId: input.commandId,
|
|
466
|
+
detectedAtIso: input.detectedAtIso,
|
|
467
|
+
hint: input.hint ?? "Wait for completion and retry using the same key.",
|
|
468
|
+
details: input.details
|
|
469
|
+
}
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
};
|
|
473
|
+
var IdempotencyPayloadMismatchError = class IdempotencyPayloadMismatchError extends IdempotencyError {
|
|
474
|
+
constructor(input) {
|
|
475
|
+
super({
|
|
476
|
+
message: "The idempotency key was reused with a different payload",
|
|
477
|
+
code: require_unexpected_error.ErrorCodes.idempotency.payloadMismatch,
|
|
478
|
+
kind: "payload_mismatch",
|
|
479
|
+
metadata: input.metadata,
|
|
480
|
+
cause: input.cause
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
static detected(input) {
|
|
484
|
+
const resolved = resolveKeyIdentity(input.key, input.keyHash, input.keyPreview);
|
|
485
|
+
return new IdempotencyPayloadMismatchError({
|
|
486
|
+
cause: input.cause,
|
|
487
|
+
metadata: {
|
|
488
|
+
operation: input.operation,
|
|
489
|
+
scope: input.scope,
|
|
490
|
+
entity: input.entity,
|
|
491
|
+
keyHash: resolved.keyHash,
|
|
492
|
+
keyPreview: resolved.keyPreview,
|
|
493
|
+
incomingPayloadHash: input.incomingPayloadHash,
|
|
494
|
+
existingPayloadHash: input.existingPayloadHash,
|
|
495
|
+
idempotencyRecordId: input.idempotencyRecordId,
|
|
496
|
+
correlationId: input.correlationId,
|
|
497
|
+
requestId: input.requestId,
|
|
498
|
+
commandId: input.commandId,
|
|
499
|
+
detectedAtIso: input.detectedAtIso,
|
|
500
|
+
hint: input.hint ?? "Use a new idempotency key for a new intent. The same key should only be reused for retries of the same payload.",
|
|
501
|
+
details: input.details
|
|
502
|
+
}
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
};
|
|
506
|
+
var IdempotencyReplayNotSupportedError = class IdempotencyReplayNotSupportedError extends IdempotencyError {
|
|
507
|
+
constructor(input) {
|
|
508
|
+
super({
|
|
509
|
+
message: "Re-execution detected, but replay of the previous result is not implemented",
|
|
510
|
+
code: require_unexpected_error.ErrorCodes.idempotency.replayNotSupported,
|
|
511
|
+
kind: "replay_not_supported",
|
|
512
|
+
metadata: input.metadata,
|
|
513
|
+
cause: input.cause
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
static detected(input) {
|
|
517
|
+
const resolved = resolveKeyIdentity(input.key, input.keyHash, input.keyPreview);
|
|
518
|
+
return new IdempotencyReplayNotSupportedError({
|
|
519
|
+
cause: input.cause,
|
|
520
|
+
metadata: {
|
|
521
|
+
operation: input.operation,
|
|
522
|
+
scope: input.scope,
|
|
523
|
+
entity: input.entity,
|
|
524
|
+
keyHash: resolved.keyHash,
|
|
525
|
+
keyPreview: resolved.keyPreview,
|
|
526
|
+
idempotencyRecordId: input.idempotencyRecordId,
|
|
527
|
+
correlationId: input.correlationId,
|
|
528
|
+
requestId: input.requestId,
|
|
529
|
+
commandId: input.commandId,
|
|
530
|
+
detectedAtIso: input.detectedAtIso,
|
|
531
|
+
hint: input.hint ?? "Try again later, or enable result replay for full idempotency.",
|
|
532
|
+
details: input.details
|
|
533
|
+
}
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
};
|
|
537
|
+
function resolveKeyIdentity(key, keyHash, keyPreview) {
|
|
538
|
+
return {
|
|
539
|
+
keyHash,
|
|
540
|
+
keyPreview: keyPreview ?? (key ? key.length <= 8 ? key : key.slice(0, 8) : void 0)
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
//#endregion
|
|
544
|
+
//#region src/core/errors/integration-error.ts
|
|
545
|
+
/**
|
|
546
|
+
* Raised when a call to an external provider fails — a timeout, an unreachable
|
|
547
|
+
* endpoint, or a malformed response. This is the boundary error that separates
|
|
548
|
+
* "our code is fine, the outside world misbehaved" from internal faults, which
|
|
549
|
+
* matters for retry and alerting decisions.
|
|
550
|
+
*
|
|
551
|
+
* Every instance records the `provider`, the `operation`, and timing
|
|
552
|
+
* (`startedAtIso` / `durationMs`) so failures are correlatable across services
|
|
553
|
+
* and a slow dependency is visible in the metadata. The discriminating
|
|
554
|
+
* {@link reason} lets callers decide whether a failure is worth retrying.
|
|
555
|
+
* Instances are frozen; construct through the static factories.
|
|
556
|
+
*/
|
|
557
|
+
var IntegrationError = class IntegrationError extends require_unexpected_error.AppError {
|
|
558
|
+
constructor(params) {
|
|
559
|
+
const { message, code, metadata, ...rest } = params;
|
|
560
|
+
super(message, code, {
|
|
561
|
+
...rest,
|
|
562
|
+
metadata
|
|
563
|
+
});
|
|
564
|
+
this.reason = params.reason;
|
|
565
|
+
Object.freeze(this);
|
|
566
|
+
}
|
|
567
|
+
/**
|
|
568
|
+
* The provider did not respond within the allotted time. Usually retryable,
|
|
569
|
+
* since a timeout leaves the outcome unknown rather than known-failed.
|
|
570
|
+
*/
|
|
571
|
+
static timeout(options) {
|
|
572
|
+
return new IntegrationError({
|
|
573
|
+
message: "Timeout while integrating with external provider",
|
|
574
|
+
code: require_unexpected_error.ErrorCodes.integration.timeout,
|
|
575
|
+
reason: "timeout",
|
|
576
|
+
metadata: buildMetadata({
|
|
577
|
+
reason: "timeout",
|
|
578
|
+
...options
|
|
579
|
+
}),
|
|
580
|
+
...pickAppErrorFields(options)
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
/**
|
|
584
|
+
* The provider could not be reached at all (connection refused, DNS
|
|
585
|
+
* failure, network partition) — the request never landed.
|
|
586
|
+
*/
|
|
587
|
+
static unreachable(options) {
|
|
588
|
+
return new IntegrationError({
|
|
589
|
+
message: "Provider unavailable",
|
|
590
|
+
code: require_unexpected_error.ErrorCodes.integration.unreachable,
|
|
591
|
+
reason: "unreachable",
|
|
592
|
+
metadata: buildMetadata({
|
|
593
|
+
reason: "unreachable",
|
|
594
|
+
...options
|
|
595
|
+
}),
|
|
596
|
+
...pickAppErrorFields(options)
|
|
597
|
+
});
|
|
598
|
+
}
|
|
599
|
+
/**
|
|
600
|
+
* The provider answered, but with something the system cannot use — an
|
|
601
|
+
* unexpected status, an unparsable body, or a contract mismatch. Unlike a
|
|
602
|
+
* timeout this is a definite failure, so blind retries rarely help.
|
|
603
|
+
*/
|
|
604
|
+
static badResponse(options) {
|
|
605
|
+
return new IntegrationError({
|
|
606
|
+
message: "Unexpected response from provider",
|
|
607
|
+
code: require_unexpected_error.ErrorCodes.integration.badResponse,
|
|
608
|
+
reason: "bad_response",
|
|
609
|
+
metadata: buildMetadata({
|
|
610
|
+
reason: "bad_response",
|
|
611
|
+
...options
|
|
612
|
+
}),
|
|
613
|
+
...pickAppErrorFields(options)
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
};
|
|
617
|
+
function buildMetadata(input) {
|
|
618
|
+
return compactMetadata({
|
|
619
|
+
reason: input.reason,
|
|
620
|
+
provider: input.provider,
|
|
621
|
+
operation: input.operation,
|
|
622
|
+
startedAtIso: input.startedAtIso,
|
|
623
|
+
durationMs: input.durationMs,
|
|
624
|
+
detectedAtIso: input.detectedAtIso,
|
|
625
|
+
correlationId: input.correlationId,
|
|
626
|
+
requestId: input.requestId,
|
|
627
|
+
commandId: input.commandId,
|
|
628
|
+
details: input.details,
|
|
629
|
+
statusCode: input.statusCode,
|
|
630
|
+
responseCode: input.responseCode
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
function pickAppErrorFields(options) {
|
|
634
|
+
return {
|
|
635
|
+
cause: options.cause,
|
|
636
|
+
type: options.type,
|
|
637
|
+
severity: options.severity,
|
|
638
|
+
correlationId: options.correlationId,
|
|
639
|
+
requestId: options.requestId,
|
|
640
|
+
commandId: options.commandId,
|
|
641
|
+
createdAtIso: options.createdAtIso,
|
|
642
|
+
publicMessage: options.publicMessage
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
function compactMetadata(input) {
|
|
646
|
+
return Object.fromEntries(Object.entries(input).filter(([, value]) => value !== void 0));
|
|
647
|
+
}
|
|
648
|
+
//#endregion
|
|
649
|
+
//#region src/core/errors/legacy-incompatible-error.ts
|
|
650
|
+
var LegacyIncompatibleError = class extends require_unexpected_error.AppError {
|
|
651
|
+
constructor(message, context, options) {
|
|
652
|
+
const mergedMetadata = options?.metadata ? {
|
|
653
|
+
...context ?? {},
|
|
654
|
+
...options.metadata
|
|
655
|
+
} : context;
|
|
656
|
+
super(message, require_unexpected_error.ErrorCodes.legacyIncompatible, {
|
|
657
|
+
...options,
|
|
658
|
+
metadata: mergedMetadata
|
|
659
|
+
});
|
|
660
|
+
}
|
|
661
|
+
};
|
|
662
|
+
//#endregion
|
|
663
|
+
//#region src/core/errors/serialization-error.ts
|
|
664
|
+
var SerializationError = class extends require_unexpected_error.AppError {
|
|
665
|
+
constructor(input) {
|
|
666
|
+
super(input.message ?? "Serialization failure", input.code, {
|
|
667
|
+
cause: input.cause,
|
|
668
|
+
metadata: {
|
|
669
|
+
direction: input.direction,
|
|
670
|
+
category: input.category,
|
|
671
|
+
...input.metadata
|
|
672
|
+
},
|
|
673
|
+
type: input.type,
|
|
674
|
+
severity: input.severity,
|
|
675
|
+
correlationId: input.correlationId,
|
|
676
|
+
requestId: input.requestId,
|
|
677
|
+
commandId: input.commandId,
|
|
678
|
+
createdAtIso: input.createdAtIso,
|
|
679
|
+
publicMessage: input.publicMessage
|
|
680
|
+
});
|
|
681
|
+
this.direction = input.direction;
|
|
682
|
+
this.category = input.category;
|
|
683
|
+
this.boundary = input.metadata.boundary;
|
|
684
|
+
this.contract = input.metadata.contract;
|
|
685
|
+
Object.freeze(this);
|
|
686
|
+
}
|
|
687
|
+
};
|
|
688
|
+
var SerializationInError = class SerializationInError extends SerializationError {
|
|
689
|
+
constructor(input) {
|
|
690
|
+
super(input);
|
|
691
|
+
}
|
|
692
|
+
static failure(category, metadata) {
|
|
693
|
+
return new SerializationInError({
|
|
694
|
+
message: SerializationMessages.message("deserialize", category),
|
|
695
|
+
code: SerializationCodes.code("deserialize", category),
|
|
696
|
+
category,
|
|
697
|
+
direction: "deserialize",
|
|
698
|
+
metadata,
|
|
699
|
+
cause: metadata.cause
|
|
700
|
+
});
|
|
701
|
+
}
|
|
702
|
+
};
|
|
703
|
+
var SerializationOutError = class SerializationOutError extends SerializationError {
|
|
704
|
+
constructor(input) {
|
|
705
|
+
super(input);
|
|
706
|
+
}
|
|
707
|
+
static failure(category, metadata) {
|
|
708
|
+
return new SerializationOutError({
|
|
709
|
+
message: SerializationMessages.message("serialize", category),
|
|
710
|
+
code: SerializationCodes.code("serialize", category),
|
|
711
|
+
category,
|
|
712
|
+
direction: "serialize",
|
|
713
|
+
metadata,
|
|
714
|
+
cause: metadata.cause
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
};
|
|
718
|
+
var SerializationCodes = class {
|
|
719
|
+
static code(direction, category) {
|
|
720
|
+
return require_unexpected_error.serializationErrorCode(direction, category);
|
|
721
|
+
}
|
|
722
|
+
};
|
|
723
|
+
var SerializationMessages = class {
|
|
724
|
+
static message(direction, category) {
|
|
725
|
+
if (direction === "deserialize") {
|
|
726
|
+
if (category === "invalid_json") return "Invalid payload: malformed JSON";
|
|
727
|
+
if (category === "schema_version_missing") return "Invalid payload: schemaVersion is missing";
|
|
728
|
+
if (category === "schema_version_unsupported") return "Invalid payload: schemaVersion is not supported";
|
|
729
|
+
if (category === "invalid_shape") return "Invalid payload: incompatible shape";
|
|
730
|
+
if (category === "mapping_not_implemented") return "Unsupported payload: mapping not implemented";
|
|
731
|
+
if (category === "contract_mismatch") return "Payload does not match the expected contract";
|
|
732
|
+
return "Failed to parse incoming payload";
|
|
733
|
+
}
|
|
734
|
+
if (category === "stringify_failed") return "Failed to serialize the response";
|
|
735
|
+
if (category === "contract_mismatch") return "Response does not match the expected contract";
|
|
736
|
+
return "Failed to build the response";
|
|
737
|
+
}
|
|
738
|
+
};
|
|
739
|
+
function safePreview(value, limit = 240) {
|
|
740
|
+
try {
|
|
741
|
+
const serialized = typeof value === "string" ? value : JSON.stringify(value);
|
|
742
|
+
return serialized.length > limit ? `${serialized.slice(0, limit)}…` : serialized;
|
|
743
|
+
} catch {
|
|
744
|
+
return;
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
//#endregion
|
|
748
|
+
//#region src/core/errors/temporal-error.ts
|
|
749
|
+
var TemporalError = class extends require_unexpected_error.AppError {
|
|
750
|
+
constructor(input) {
|
|
751
|
+
super(input.message, input.code, {
|
|
752
|
+
...input,
|
|
753
|
+
metadata: {
|
|
754
|
+
kind: input.kind,
|
|
755
|
+
...input.metadata
|
|
756
|
+
}
|
|
757
|
+
});
|
|
758
|
+
this.kind = input.kind;
|
|
759
|
+
this.resourceType = input.metadata.resourceType;
|
|
760
|
+
this.evaluatedAtIso = input.metadata.evaluatedAtIso;
|
|
761
|
+
Object.freeze(this);
|
|
762
|
+
}
|
|
763
|
+
};
|
|
764
|
+
var ExpiredError = class ExpiredError extends TemporalError {
|
|
765
|
+
constructor(input) {
|
|
766
|
+
super(input);
|
|
767
|
+
}
|
|
768
|
+
static detected(input) {
|
|
769
|
+
return new ExpiredError({
|
|
770
|
+
message: "The validity of the resource/action has expired",
|
|
771
|
+
code: require_unexpected_error.ErrorCodes.temporal.expired,
|
|
772
|
+
kind: "expired",
|
|
773
|
+
metadata: {
|
|
774
|
+
...input,
|
|
775
|
+
hint: input.hint ?? "The window has expired. Request a new link or try again within the valid period."
|
|
776
|
+
},
|
|
777
|
+
cause: input.cause,
|
|
778
|
+
type: input.type,
|
|
779
|
+
severity: input.severity,
|
|
780
|
+
correlationId: input.correlationId,
|
|
781
|
+
requestId: input.requestId,
|
|
782
|
+
commandId: input.commandId,
|
|
783
|
+
createdAtIso: input.createdAtIso,
|
|
784
|
+
publicMessage: input.publicMessage
|
|
785
|
+
});
|
|
786
|
+
}
|
|
787
|
+
};
|
|
788
|
+
var NotYetValidError = class NotYetValidError extends TemporalError {
|
|
789
|
+
constructor(input) {
|
|
790
|
+
super(input);
|
|
791
|
+
}
|
|
792
|
+
static detected(input) {
|
|
793
|
+
return new NotYetValidError({
|
|
794
|
+
message: "The resource/action is not yet valid",
|
|
795
|
+
code: require_unexpected_error.ErrorCodes.temporal.notYetValid,
|
|
796
|
+
kind: "not_yet_valid",
|
|
797
|
+
metadata: {
|
|
798
|
+
...input,
|
|
799
|
+
hint: input.hint ?? "Not yet within the valid period. Try again later."
|
|
800
|
+
},
|
|
801
|
+
cause: input.cause,
|
|
802
|
+
type: input.type,
|
|
803
|
+
severity: input.severity,
|
|
804
|
+
correlationId: input.correlationId,
|
|
805
|
+
requestId: input.requestId,
|
|
806
|
+
commandId: input.commandId,
|
|
807
|
+
createdAtIso: input.createdAtIso,
|
|
808
|
+
publicMessage: input.publicMessage
|
|
809
|
+
});
|
|
810
|
+
}
|
|
811
|
+
};
|
|
812
|
+
function evaluateTemporalWindow(input) {
|
|
813
|
+
const evaluatedMs = Date.parse(input.evaluatedAtIso);
|
|
814
|
+
if (Number.isNaN(evaluatedMs)) return null;
|
|
815
|
+
let notYetValid = false;
|
|
816
|
+
if (input.validFromIso) {
|
|
817
|
+
const fromMs = Date.parse(input.validFromIso);
|
|
818
|
+
if (!Number.isNaN(fromMs) && evaluatedMs < fromMs) notYetValid = true;
|
|
819
|
+
}
|
|
820
|
+
if (input.validUntilIso) {
|
|
821
|
+
const untilMs = Date.parse(input.validUntilIso);
|
|
822
|
+
if (!Number.isNaN(untilMs) && evaluatedMs > untilMs) return {
|
|
823
|
+
expired: true,
|
|
824
|
+
notYetValid: false
|
|
825
|
+
};
|
|
826
|
+
}
|
|
827
|
+
return {
|
|
828
|
+
expired: false,
|
|
829
|
+
notYetValid
|
|
830
|
+
};
|
|
831
|
+
}
|
|
832
|
+
//#endregion
|
|
833
|
+
//#region src/core/errors/validation-error.ts
|
|
834
|
+
/**
|
|
835
|
+
* Raised when a single input fails a validation rule — the canonical
|
|
836
|
+
* "this field is wrong, and here is why" error.
|
|
837
|
+
*
|
|
838
|
+
* It pins the offending `field` and a machine-readable `validationCode` into
|
|
839
|
+
* the metadata (merged ahead of any caller-supplied metadata) so an API layer
|
|
840
|
+
* can map the failure straight onto a form field without parsing the message.
|
|
841
|
+
* The fixed {@link ErrorCodes.validation} code lets callers catch all
|
|
842
|
+
* validation failures uniformly while still distinguishing the specific rule
|
|
843
|
+
* through `validationCode`.
|
|
844
|
+
*/
|
|
845
|
+
var ValidationError = class extends require_unexpected_error.AppError {
|
|
846
|
+
/**
|
|
847
|
+
* @param field - The input that failed validation; surfaced as `metadata.field`.
|
|
848
|
+
* @param code - The specific rule that was violated; surfaced as `metadata.validationCode`.
|
|
849
|
+
* @param message - The developer-facing description of the failure.
|
|
850
|
+
* @param options - Optional cause, correlation ids, and extra metadata
|
|
851
|
+
* (merged over the field/code metadata, never overwriting it by accident).
|
|
852
|
+
*/
|
|
853
|
+
constructor(field, code, message, options) {
|
|
854
|
+
const baseMetadata = {
|
|
855
|
+
field: field.value,
|
|
856
|
+
validationCode: code.value
|
|
857
|
+
};
|
|
858
|
+
const mergedMetadata = options?.metadata ? {
|
|
859
|
+
...baseMetadata,
|
|
860
|
+
...options.metadata
|
|
861
|
+
} : baseMetadata;
|
|
862
|
+
super(message, require_unexpected_error.ErrorCodes.validation, {
|
|
863
|
+
...options,
|
|
864
|
+
metadata: mergedMetadata
|
|
865
|
+
});
|
|
866
|
+
}
|
|
867
|
+
};
|
|
868
|
+
//#endregion
|
|
869
|
+
Object.defineProperty(exports, "AlreadyExistsError", {
|
|
870
|
+
enumerable: true,
|
|
871
|
+
get: function() {
|
|
872
|
+
return AlreadyExistsError;
|
|
873
|
+
}
|
|
874
|
+
});
|
|
875
|
+
Object.defineProperty(exports, "AuthenticationError", {
|
|
876
|
+
enumerable: true,
|
|
877
|
+
get: function() {
|
|
878
|
+
return AuthenticationError;
|
|
879
|
+
}
|
|
880
|
+
});
|
|
881
|
+
Object.defineProperty(exports, "AuthorizationError", {
|
|
882
|
+
enumerable: true,
|
|
883
|
+
get: function() {
|
|
884
|
+
return AuthorizationError;
|
|
885
|
+
}
|
|
886
|
+
});
|
|
887
|
+
Object.defineProperty(exports, "ConflictError", {
|
|
888
|
+
enumerable: true,
|
|
889
|
+
get: function() {
|
|
890
|
+
return ConflictError;
|
|
891
|
+
}
|
|
892
|
+
});
|
|
893
|
+
Object.defineProperty(exports, "DuplicateError", {
|
|
894
|
+
enumerable: true,
|
|
895
|
+
get: function() {
|
|
896
|
+
return DuplicateError;
|
|
897
|
+
}
|
|
898
|
+
});
|
|
899
|
+
Object.defineProperty(exports, "ExpiredError", {
|
|
900
|
+
enumerable: true,
|
|
901
|
+
get: function() {
|
|
902
|
+
return ExpiredError;
|
|
903
|
+
}
|
|
904
|
+
});
|
|
905
|
+
Object.defineProperty(exports, "IdempotencyError", {
|
|
906
|
+
enumerable: true,
|
|
907
|
+
get: function() {
|
|
908
|
+
return IdempotencyError;
|
|
909
|
+
}
|
|
910
|
+
});
|
|
911
|
+
Object.defineProperty(exports, "IdempotencyInProgressError", {
|
|
912
|
+
enumerable: true,
|
|
913
|
+
get: function() {
|
|
914
|
+
return IdempotencyInProgressError;
|
|
915
|
+
}
|
|
916
|
+
});
|
|
917
|
+
Object.defineProperty(exports, "IdempotencyKeyMissingError", {
|
|
918
|
+
enumerable: true,
|
|
919
|
+
get: function() {
|
|
920
|
+
return IdempotencyKeyMissingError;
|
|
921
|
+
}
|
|
922
|
+
});
|
|
923
|
+
Object.defineProperty(exports, "IdempotencyPayloadMismatchError", {
|
|
924
|
+
enumerable: true,
|
|
925
|
+
get: function() {
|
|
926
|
+
return IdempotencyPayloadMismatchError;
|
|
927
|
+
}
|
|
928
|
+
});
|
|
929
|
+
Object.defineProperty(exports, "IdempotencyReplayNotSupportedError", {
|
|
930
|
+
enumerable: true,
|
|
931
|
+
get: function() {
|
|
932
|
+
return IdempotencyReplayNotSupportedError;
|
|
933
|
+
}
|
|
934
|
+
});
|
|
935
|
+
Object.defineProperty(exports, "IntegrationError", {
|
|
936
|
+
enumerable: true,
|
|
937
|
+
get: function() {
|
|
938
|
+
return IntegrationError;
|
|
939
|
+
}
|
|
940
|
+
});
|
|
941
|
+
Object.defineProperty(exports, "LegacyIncompatibleError", {
|
|
942
|
+
enumerable: true,
|
|
943
|
+
get: function() {
|
|
944
|
+
return LegacyIncompatibleError;
|
|
945
|
+
}
|
|
946
|
+
});
|
|
947
|
+
Object.defineProperty(exports, "NotYetValidError", {
|
|
948
|
+
enumerable: true,
|
|
949
|
+
get: function() {
|
|
950
|
+
return NotYetValidError;
|
|
951
|
+
}
|
|
952
|
+
});
|
|
953
|
+
Object.defineProperty(exports, "SerializationCodes", {
|
|
954
|
+
enumerable: true,
|
|
955
|
+
get: function() {
|
|
956
|
+
return SerializationCodes;
|
|
957
|
+
}
|
|
958
|
+
});
|
|
959
|
+
Object.defineProperty(exports, "SerializationError", {
|
|
960
|
+
enumerable: true,
|
|
961
|
+
get: function() {
|
|
962
|
+
return SerializationError;
|
|
963
|
+
}
|
|
964
|
+
});
|
|
965
|
+
Object.defineProperty(exports, "SerializationInError", {
|
|
966
|
+
enumerable: true,
|
|
967
|
+
get: function() {
|
|
968
|
+
return SerializationInError;
|
|
969
|
+
}
|
|
970
|
+
});
|
|
971
|
+
Object.defineProperty(exports, "SerializationMessages", {
|
|
972
|
+
enumerable: true,
|
|
973
|
+
get: function() {
|
|
974
|
+
return SerializationMessages;
|
|
975
|
+
}
|
|
976
|
+
});
|
|
977
|
+
Object.defineProperty(exports, "SerializationOutError", {
|
|
978
|
+
enumerable: true,
|
|
979
|
+
get: function() {
|
|
980
|
+
return SerializationOutError;
|
|
981
|
+
}
|
|
982
|
+
});
|
|
983
|
+
Object.defineProperty(exports, "TemporalError", {
|
|
984
|
+
enumerable: true,
|
|
985
|
+
get: function() {
|
|
986
|
+
return TemporalError;
|
|
987
|
+
}
|
|
988
|
+
});
|
|
989
|
+
Object.defineProperty(exports, "UniqueConstraintViolationError", {
|
|
990
|
+
enumerable: true,
|
|
991
|
+
get: function() {
|
|
992
|
+
return UniqueConstraintViolationError;
|
|
993
|
+
}
|
|
994
|
+
});
|
|
995
|
+
Object.defineProperty(exports, "ValidationError", {
|
|
996
|
+
enumerable: true,
|
|
997
|
+
get: function() {
|
|
998
|
+
return ValidationError;
|
|
999
|
+
}
|
|
1000
|
+
});
|
|
1001
|
+
Object.defineProperty(exports, "evaluateTemporalWindow", {
|
|
1002
|
+
enumerable: true,
|
|
1003
|
+
get: function() {
|
|
1004
|
+
return evaluateTemporalWindow;
|
|
1005
|
+
}
|
|
1006
|
+
});
|
|
1007
|
+
Object.defineProperty(exports, "safePreview", {
|
|
1008
|
+
enumerable: true,
|
|
1009
|
+
get: function() {
|
|
1010
|
+
return safePreview;
|
|
1011
|
+
}
|
|
1012
|
+
});
|
|
1013
|
+
Object.defineProperty(exports, "translateUniqueViolationToDuplicate", {
|
|
1014
|
+
enumerable: true,
|
|
1015
|
+
get: function() {
|
|
1016
|
+
return translateUniqueViolationToDuplicate;
|
|
1017
|
+
}
|
|
1018
|
+
});
|
|
1019
|
+
|
|
1020
|
+
//# sourceMappingURL=validation-error.cjs.map
|