@cullet/erp-core 1.0.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 +39 -0
- package/README.md +110 -0
- package/dist/errors/index.d.ts +2 -0
- package/dist/errors/index.js +3 -0
- package/dist/exceptions/validation-field.d.ts +2 -0
- package/dist/exceptions/validation-field.js +2 -0
- package/dist/gate-engine-registry.js +260 -0
- package/dist/gate-engine-registry.js.map +1 -0
- package/dist/gate-types.d.ts +307 -0
- package/dist/gate-v1-payload.schema.js +677 -0
- package/dist/gate-v1-payload.schema.js.map +1 -0
- package/dist/index.d.ts +88 -0
- package/dist/index.js +153 -0
- package/dist/index.js.map +1 -0
- package/dist/outcome.js +92 -0
- package/dist/outcome.js.map +1 -0
- package/dist/parse-gate-payload.d.ts +136 -0
- package/dist/policies/engines/index.d.ts +3 -0
- package/dist/policies/engines/index.js +2 -0
- package/dist/policies/engines/v1/gate/index.d.ts +115 -0
- package/dist/policies/engines/v1/gate/index.js +84 -0
- package/dist/policies/engines/v1/gate/index.js.map +1 -0
- package/dist/policies/index.d.ts +4 -0
- package/dist/policies/index.js +5 -0
- package/dist/policy-service.d.ts +541 -0
- package/dist/policy-service.js +992 -0
- package/dist/policy-service.js.map +1 -0
- package/dist/validation-code.js +210 -0
- package/dist/validation-code.js.map +1 -0
- package/dist/validation-error.d.ts +686 -0
- package/dist/validation-error.js +757 -0
- package/dist/validation-error.js.map +1 -0
- package/dist/validation-field.d.ts +12 -0
- package/dist/validation-field.js +23 -0
- package/dist/validation-field.js.map +1 -0
- package/meta.json +66 -0
- package/package.json +59 -0
- package/src/core/application/commands/command.ts +28 -0
- package/src/core/application/commands/index.ts +2 -0
- package/src/core/application/commands/requested-by.ts +97 -0
- package/src/core/application/index.ts +31 -0
- package/src/core/application/policy-error-mapper.ts +60 -0
- package/src/core/application/ports/index.ts +14 -0
- package/src/core/application/ports/logger.port.ts +10 -0
- package/src/core/application/ports/metrics.port.ts +9 -0
- package/src/core/application/ports/policy-port.ts +20 -0
- package/src/core/application/ports/repository.port.ts +7 -0
- package/src/core/application/ports/temporal-repository.port.ts +14 -0
- package/src/core/application/ports/tracer.port.ts +16 -0
- package/src/core/application/queries/index.ts +2 -0
- package/src/core/application/queries/query.ts +53 -0
- package/src/core/application/temporal/index.ts +11 -0
- package/src/core/application/temporal/temporal-context.ts +41 -0
- package/src/core/application/temporal/temporal-use-case.ts +41 -0
- package/src/core/application/use-case.ts +24 -0
- package/src/core/config/core-config.instance.ts +14 -0
- package/src/core/config/core-config.ts +87 -0
- package/src/core/config/index.ts +12 -0
- package/src/core/config/policy-reporter.ts +60 -0
- package/src/core/config/silent-policy-reporter.ts +5 -0
- package/src/core/domain/entity.ts +69 -0
- package/src/core/domain/rulesets/entity-ruleset.contracts.ts +16 -0
- package/src/core/domain/rulesets/ruleset-registry.ts +71 -0
- package/src/core/domain/rulesets/ruleset.contracts.ts +15 -0
- package/src/core/domain/rulesets/value-object-ruleset.contracts.ts +13 -0
- package/src/core/domain/temporal/index.ts +18 -0
- package/src/core/domain/temporal/temporal-range.ts +53 -0
- package/src/core/domain/temporal/temporal-snapshot.ts +47 -0
- package/src/core/domain/temporal/transaction-time.ts +60 -0
- package/src/core/domain/temporal/valid-time.ts +57 -0
- package/src/core/domain/value-object.ts +30 -0
- package/src/core/errors/app-error.ts +85 -0
- package/src/core/errors/authentication-error.ts +171 -0
- package/src/core/errors/authorization-error.ts +204 -0
- package/src/core/errors/business-rule-violation-error.ts +28 -0
- package/src/core/errors/conflict-error.ts +246 -0
- package/src/core/errors/error-codes.ts +60 -0
- package/src/core/errors/idempotency-error.ts +372 -0
- package/src/core/errors/index.ts +99 -0
- package/src/core/errors/integration-error.ts +157 -0
- package/src/core/errors/legacy-incompatible-error.ts +26 -0
- package/src/core/errors/not-found-error.ts +27 -0
- package/src/core/errors/serialization-error.ts +233 -0
- package/src/core/errors/temporal-error.ts +145 -0
- package/src/core/errors/types.ts +74 -0
- package/src/core/errors/unexpected-error.ts +22 -0
- package/src/core/errors/utils/index.ts +4 -0
- package/src/core/errors/utils/json-safe.ts +93 -0
- package/src/core/errors/validation-error.ts +34 -0
- package/src/core/exceptions/business-rule-violation-exception.ts +12 -0
- package/src/core/exceptions/domain-exception.ts +9 -0
- package/src/core/exceptions/entity-not-found-exception.ts +12 -0
- package/src/core/exceptions/invalid-state-transition-exception.ts +15 -0
- package/src/core/exceptions/invariant-violation-exception.ts +9 -0
- package/src/core/exceptions/validation-code.ts +39 -0
- package/src/core/exceptions/validation-exception.ts +39 -0
- package/src/core/exceptions/validation-field.ts +32 -0
- package/src/core/index.ts +15 -0
- package/src/core/policies/asof/asof.ts +68 -0
- package/src/core/policies/asof/index.ts +2 -0
- package/src/core/policies/catalog/catalog-types.ts +12 -0
- package/src/core/policies/catalog/catalog.instance.ts +21 -0
- package/src/core/policies/catalog/index.ts +11 -0
- package/src/core/policies/catalog/policy-catalog-entry.ts +220 -0
- package/src/core/policies/catalog/policy-catalog.ts +124 -0
- package/src/core/policies/catalog/policy-key.ts +78 -0
- package/src/core/policies/context/context-builder.ts +391 -0
- package/src/core/policies/context/context-registry.instance.ts +26 -0
- package/src/core/policies/context/context-registry.ts +95 -0
- package/src/core/policies/context/context-resolver.ts +31 -0
- package/src/core/policies/context/context-seed.ts +32 -0
- package/src/core/policies/context/index.ts +21 -0
- package/src/core/policies/context/path.ts +152 -0
- package/src/core/policies/defs/in-memory-policy-definition-repo.ts +73 -0
- package/src/core/policies/defs/index.ts +20 -0
- package/src/core/policies/defs/policy-definition-repository.ts +8 -0
- package/src/core/policies/defs/policy-definition.ts +192 -0
- package/src/core/policies/defs/policy-payload.contracts.ts +13 -0
- package/src/core/policies/defs/policy-scope.ts +53 -0
- package/src/core/policies/defs/policy-semver.ts +83 -0
- package/src/core/policies/engines/compute-engine-registry.ts +57 -0
- package/src/core/policies/engines/compute-evaluator-registry.ts +28 -0
- package/src/core/policies/engines/compute-payload.ts +5 -0
- package/src/core/policies/engines/compute-registry.ts +67 -0
- package/src/core/policies/engines/compute-types.ts +51 -0
- package/src/core/policies/engines/condition-evaluator-reporter.ts +34 -0
- package/src/core/policies/engines/gate-engine-registry.ts +38 -0
- package/src/core/policies/engines/gate-payload.ts +5 -0
- package/src/core/policies/engines/gate-types.ts +63 -0
- package/src/core/policies/engines/index.ts +48 -0
- package/src/core/policies/engines/parse-compute-payload.ts +71 -0
- package/src/core/policies/engines/parse-gate-payload.ts +68 -0
- package/src/core/policies/engines/v1/compute/compute-engine-v1.ts +55 -0
- package/src/core/policies/engines/v1/compute/compute-payload.schema.ts +75 -0
- package/src/core/policies/engines/v1/compute/index.ts +12 -0
- package/src/core/policies/engines/v1/compute/resolve-decision-table-v1.ts +54 -0
- package/src/core/policies/engines/v1/condition-evaluator.ts +592 -0
- package/src/core/policies/engines/v1/condition-schema.ts +50 -0
- package/src/core/policies/engines/v1/condition-types.ts +41 -0
- package/src/core/policies/engines/v1/gate/gate-engine-v1.ts +157 -0
- package/src/core/policies/engines/v1/gate/gate-types-v1.ts +12 -0
- package/src/core/policies/engines/v1/gate/gate-v1-payload.schema.ts +46 -0
- package/src/core/policies/engines/v1/gate/index.ts +9 -0
- package/src/core/policies/engines/v1/index.ts +13 -0
- package/src/core/policies/index.ts +132 -0
- package/src/core/policies/package/policy-package.ts +17 -0
- package/src/core/policies/policy-ids.ts +60 -0
- package/src/core/policies/resolver/index.ts +1 -0
- package/src/core/policies/resolver/policy-resolver.ts +57 -0
- package/src/core/policies/service/index.ts +10 -0
- package/src/core/policies/service/policy-evaluation-error.ts +148 -0
- package/src/core/policies/service/policy-service.ts +493 -0
- package/src/core/policies/utils/date.ts +7 -0
- package/src/core/policies/utils/hash.ts +90 -0
- package/src/core/policies/utils/index.ts +3 -0
- package/src/core/policies/utils/result.ts +4 -0
- package/src/core/result/outcome.ts +158 -0
- package/src/core/result/result.ts +153 -0
- package/src/core/shared/aggregate-version.ts +11 -0
- package/src/core/shared/hashing.ts +38 -0
- package/src/core/shared/immutable.ts +46 -0
- package/src/core/shared/temporal-guards.ts +22 -0
- package/src/core/versioning/domain-event-contracts.ts +73 -0
- package/src/core/versioning/version.ts +34 -0
- package/src/errors/index.ts +1 -0
- package/src/examples/rulesets/entity/order-creation-rules-v1.ts +31 -0
- package/src/examples/rulesets/entity/order-invariants-v1.ts +48 -0
- package/src/examples/rulesets/entity/order-invariants-v2.ts +28 -0
- package/src/examples/rulesets/entity/order.ts +162 -0
- package/src/examples/rulesets/value-object/cpf-rules-v1.ts +52 -0
- package/src/examples/rulesets/value-object/cpf-rules-v2.ts +27 -0
- package/src/examples/rulesets/value-object/cpf.ts +37 -0
- package/src/examples/rulesets/value-object/customer.ts +67 -0
- package/src/examples/rulesets/value-object/person-name-rules-v1.ts +37 -0
- package/src/examples/rulesets/value-object/person-name-rules-v2.ts +27 -0
- package/src/examples/rulesets/value-object/person-name.ts +39 -0
- package/src/exceptions/validation-field.ts +1 -0
- package/src/index.ts +11 -0
- package/src/policies/engines/index.ts +1 -0
- package/src/policies/engines/v1/gate/index.ts +1 -0
- package/src/policies/index.ts +1 -0
- package/src/version.ts +7 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
2
|
+
// Types
|
|
3
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
4
|
+
|
|
5
|
+
export type {
|
|
6
|
+
AppErrorOptions,
|
|
7
|
+
ErrorSeverity,
|
|
8
|
+
JsonSafePrimitive,
|
|
9
|
+
JsonSafeRecord,
|
|
10
|
+
JsonSafeValue,
|
|
11
|
+
} from "./types";
|
|
12
|
+
|
|
13
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
14
|
+
// Utilities
|
|
15
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
16
|
+
|
|
17
|
+
export { ErrorCodes, serializationErrorCode } from "./error-codes";
|
|
18
|
+
export { assertJsonSafeMetadata } from "./utils";
|
|
19
|
+
|
|
20
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
21
|
+
// Error classes
|
|
22
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
23
|
+
|
|
24
|
+
export { AppError } from "./app-error";
|
|
25
|
+
export type {
|
|
26
|
+
AuthenticationErrorMetadata,
|
|
27
|
+
AuthenticationErrorReason,
|
|
28
|
+
} from "./authentication-error";
|
|
29
|
+
export { AuthenticationError } from "./authentication-error";
|
|
30
|
+
export type {
|
|
31
|
+
AuthorizationErrorMetadata,
|
|
32
|
+
AuthorizationErrorReason,
|
|
33
|
+
AuthorizationRequirement,
|
|
34
|
+
} from "./authorization-error";
|
|
35
|
+
export { AuthorizationError } from "./authorization-error";
|
|
36
|
+
export { BusinessRuleViolationError } from "./business-rule-violation-error";
|
|
37
|
+
export type {
|
|
38
|
+
ConflictErrorMetadata,
|
|
39
|
+
ConflictKind,
|
|
40
|
+
UniqueConstraintViolation,
|
|
41
|
+
} from "./conflict-error";
|
|
42
|
+
export {
|
|
43
|
+
AlreadyExistsError,
|
|
44
|
+
ConflictError,
|
|
45
|
+
DuplicateError,
|
|
46
|
+
translateUniqueViolationToDuplicate,
|
|
47
|
+
UniqueConstraintViolationError,
|
|
48
|
+
} from "./conflict-error";
|
|
49
|
+
export type {
|
|
50
|
+
IdempotencyErrorMetadata,
|
|
51
|
+
IdempotencyFailureKind,
|
|
52
|
+
} from "./idempotency-error";
|
|
53
|
+
export {
|
|
54
|
+
IdempotencyError,
|
|
55
|
+
IdempotencyInProgressError,
|
|
56
|
+
IdempotencyKeyMissingError,
|
|
57
|
+
IdempotencyPayloadMismatchError,
|
|
58
|
+
IdempotencyReplayNotSupportedError,
|
|
59
|
+
payloadHash,
|
|
60
|
+
sha256Hex,
|
|
61
|
+
stableStringify,
|
|
62
|
+
} from "./idempotency-error";
|
|
63
|
+
export type {
|
|
64
|
+
IntegrationErrorMetadata,
|
|
65
|
+
IntegrationErrorReason,
|
|
66
|
+
} from "./integration-error";
|
|
67
|
+
export { IntegrationError } from "./integration-error";
|
|
68
|
+
export { LegacyIncompatibleError } from "./legacy-incompatible-error";
|
|
69
|
+
export { NotFoundError } from "./not-found-error";
|
|
70
|
+
export type {
|
|
71
|
+
SerializationBoundary,
|
|
72
|
+
SerializationDirection,
|
|
73
|
+
SerializationErrorMetadata,
|
|
74
|
+
SerializationFailureCategory,
|
|
75
|
+
} from "./serialization-error";
|
|
76
|
+
export {
|
|
77
|
+
SerializationInError as DeserializationError,
|
|
78
|
+
safePreview,
|
|
79
|
+
SerializationCodes,
|
|
80
|
+
SerializationError,
|
|
81
|
+
SerializationInError,
|
|
82
|
+
SerializationMessages,
|
|
83
|
+
SerializationOutError,
|
|
84
|
+
} from "./serialization-error";
|
|
85
|
+
export type {
|
|
86
|
+
TemporalErrorMetadata as ExpiredErrorMetadata,
|
|
87
|
+
TemporalPrecision as ExpiredErrorPrecision,
|
|
88
|
+
TemporalErrorMetadata,
|
|
89
|
+
TemporalKind,
|
|
90
|
+
TemporalPrecision,
|
|
91
|
+
} from "./temporal-error";
|
|
92
|
+
export {
|
|
93
|
+
evaluateTemporalWindow,
|
|
94
|
+
ExpiredError,
|
|
95
|
+
NotYetValidError,
|
|
96
|
+
TemporalError,
|
|
97
|
+
} from "./temporal-error";
|
|
98
|
+
export { UnexpectedError } from "./unexpected-error";
|
|
99
|
+
export { ValidationError } from "./validation-error";
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import type { AppErrorOptions, ErrorSeverity } from "./types";
|
|
2
|
+
import { AppError } from "./app-error";
|
|
3
|
+
import { ErrorCodes } from "./error-codes";
|
|
4
|
+
|
|
5
|
+
type IntegrationErrorReason =
|
|
6
|
+
| "timeout"
|
|
7
|
+
| "unreachable"
|
|
8
|
+
| "bad_response"
|
|
9
|
+
| "unknown";
|
|
10
|
+
|
|
11
|
+
type IntegrationErrorMetadata = {
|
|
12
|
+
reason: IntegrationErrorReason;
|
|
13
|
+
provider: string;
|
|
14
|
+
operation: string;
|
|
15
|
+
startedAtIso: string;
|
|
16
|
+
durationMs: number;
|
|
17
|
+
detectedAtIso?: string;
|
|
18
|
+
correlationId?: string;
|
|
19
|
+
requestId?: string;
|
|
20
|
+
commandId?: string;
|
|
21
|
+
details?: string;
|
|
22
|
+
statusCode?: number;
|
|
23
|
+
responseCode?: string;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
type IntegrationErrorAppOptions = Pick<
|
|
27
|
+
AppErrorOptions,
|
|
28
|
+
| "cause"
|
|
29
|
+
| "type"
|
|
30
|
+
| "severity"
|
|
31
|
+
| "correlationId"
|
|
32
|
+
| "requestId"
|
|
33
|
+
| "commandId"
|
|
34
|
+
| "createdAtIso"
|
|
35
|
+
| "publicMessage"
|
|
36
|
+
>;
|
|
37
|
+
|
|
38
|
+
type IntegrationErrorConstructorParams = {
|
|
39
|
+
message: string;
|
|
40
|
+
code: string;
|
|
41
|
+
reason: IntegrationErrorReason;
|
|
42
|
+
metadata: IntegrationErrorMetadata;
|
|
43
|
+
} & IntegrationErrorAppOptions;
|
|
44
|
+
|
|
45
|
+
class IntegrationError extends AppError {
|
|
46
|
+
public readonly reason: IntegrationErrorReason;
|
|
47
|
+
|
|
48
|
+
private constructor(params: IntegrationErrorConstructorParams) {
|
|
49
|
+
const { message, code, metadata, ...rest } = params;
|
|
50
|
+
super(message, code, {
|
|
51
|
+
...rest,
|
|
52
|
+
metadata,
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
this.reason = params.reason;
|
|
56
|
+
Object.freeze(this);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
static timeout(options: IntegrationErrorTimeoutOptions): IntegrationError {
|
|
60
|
+
return new IntegrationError({
|
|
61
|
+
message: "Timeout while integrating with external provider",
|
|
62
|
+
code: ErrorCodes.integration.timeout,
|
|
63
|
+
reason: "timeout",
|
|
64
|
+
metadata: buildMetadata({ reason: "timeout", ...options }),
|
|
65
|
+
...pickAppErrorFields(options),
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
static unreachable(
|
|
70
|
+
options: IntegrationErrorEndpointOptions,
|
|
71
|
+
): IntegrationError {
|
|
72
|
+
return new IntegrationError({
|
|
73
|
+
message: "Provider unavailable",
|
|
74
|
+
code: ErrorCodes.integration.unreachable,
|
|
75
|
+
reason: "unreachable",
|
|
76
|
+
metadata: buildMetadata({ reason: "unreachable", ...options }),
|
|
77
|
+
...pickAppErrorFields(options),
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
static badResponse(
|
|
82
|
+
options: IntegrationErrorEndpointOptions,
|
|
83
|
+
): IntegrationError {
|
|
84
|
+
return new IntegrationError({
|
|
85
|
+
message: "Unexpected response from provider",
|
|
86
|
+
code: ErrorCodes.integration.badResponse,
|
|
87
|
+
reason: "bad_response",
|
|
88
|
+
metadata: buildMetadata({ reason: "bad_response", ...options }),
|
|
89
|
+
...pickAppErrorFields(options),
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
type IntegrationErrorBaseOptions = {
|
|
95
|
+
provider: string;
|
|
96
|
+
operation: string;
|
|
97
|
+
startedAtIso: string;
|
|
98
|
+
durationMs: number;
|
|
99
|
+
detectedAtIso?: string;
|
|
100
|
+
correlationId?: string;
|
|
101
|
+
requestId?: string;
|
|
102
|
+
commandId?: string;
|
|
103
|
+
details?: string;
|
|
104
|
+
statusCode?: number;
|
|
105
|
+
responseCode?: string;
|
|
106
|
+
cause?: unknown;
|
|
107
|
+
type?: string;
|
|
108
|
+
severity?: ErrorSeverity;
|
|
109
|
+
createdAtIso?: string;
|
|
110
|
+
publicMessage?: string;
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
type IntegrationErrorTimeoutOptions = IntegrationErrorBaseOptions;
|
|
114
|
+
type IntegrationErrorEndpointOptions = IntegrationErrorBaseOptions;
|
|
115
|
+
|
|
116
|
+
function buildMetadata(
|
|
117
|
+
input: IntegrationErrorBaseOptions & { reason: IntegrationErrorReason },
|
|
118
|
+
): IntegrationErrorMetadata {
|
|
119
|
+
return compactMetadata({
|
|
120
|
+
reason: input.reason,
|
|
121
|
+
provider: input.provider,
|
|
122
|
+
operation: input.operation,
|
|
123
|
+
startedAtIso: input.startedAtIso,
|
|
124
|
+
durationMs: input.durationMs,
|
|
125
|
+
detectedAtIso: input.detectedAtIso,
|
|
126
|
+
correlationId: input.correlationId,
|
|
127
|
+
requestId: input.requestId,
|
|
128
|
+
commandId: input.commandId,
|
|
129
|
+
details: input.details,
|
|
130
|
+
statusCode: input.statusCode,
|
|
131
|
+
responseCode: input.responseCode,
|
|
132
|
+
}) as IntegrationErrorMetadata;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function pickAppErrorFields(
|
|
136
|
+
options: IntegrationErrorBaseOptions,
|
|
137
|
+
): IntegrationErrorAppOptions {
|
|
138
|
+
return {
|
|
139
|
+
cause: options.cause,
|
|
140
|
+
type: options.type,
|
|
141
|
+
severity: options.severity,
|
|
142
|
+
correlationId: options.correlationId,
|
|
143
|
+
requestId: options.requestId,
|
|
144
|
+
commandId: options.commandId,
|
|
145
|
+
createdAtIso: options.createdAtIso,
|
|
146
|
+
publicMessage: options.publicMessage,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function compactMetadata<T extends Record<string, unknown>>(input: T): T {
|
|
151
|
+
return Object.fromEntries(
|
|
152
|
+
Object.entries(input).filter(([, value]) => value !== undefined),
|
|
153
|
+
) as T;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export { IntegrationError };
|
|
157
|
+
export type { IntegrationErrorReason, IntegrationErrorMetadata };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { AppError } from "./app-error";
|
|
2
|
+
import { ErrorCodes } from "./error-codes";
|
|
3
|
+
import type { AppErrorOptions } from "./types";
|
|
4
|
+
|
|
5
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
6
|
+
// LegacyIncompatibleError
|
|
7
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
8
|
+
|
|
9
|
+
class LegacyIncompatibleError extends AppError {
|
|
10
|
+
constructor(
|
|
11
|
+
message: string,
|
|
12
|
+
context?: Record<string, unknown>,
|
|
13
|
+
options?: AppErrorOptions,
|
|
14
|
+
) {
|
|
15
|
+
const mergedMetadata = options?.metadata
|
|
16
|
+
? { ...(context ?? {}), ...options.metadata }
|
|
17
|
+
: context;
|
|
18
|
+
|
|
19
|
+
super(message, ErrorCodes.legacyIncompatible, {
|
|
20
|
+
...options,
|
|
21
|
+
metadata: mergedMetadata,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export { LegacyIncompatibleError };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { AppError } from "./app-error";
|
|
2
|
+
import { ErrorCodes } from "./error-codes";
|
|
3
|
+
import type { AppErrorOptions } from "./types";
|
|
4
|
+
|
|
5
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
6
|
+
// NotFoundError
|
|
7
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
8
|
+
|
|
9
|
+
class NotFoundError extends AppError {
|
|
10
|
+
constructor(
|
|
11
|
+
resource: string,
|
|
12
|
+
criteria?: Record<string, unknown>,
|
|
13
|
+
options?: AppErrorOptions,
|
|
14
|
+
) {
|
|
15
|
+
const baseMetadata = criteria ? { resource, criteria } : { resource };
|
|
16
|
+
const mergedMetadata = options?.metadata
|
|
17
|
+
? { ...baseMetadata, ...options.metadata }
|
|
18
|
+
: baseMetadata;
|
|
19
|
+
|
|
20
|
+
super(`${resource} not found`, ErrorCodes.notFound, {
|
|
21
|
+
...options,
|
|
22
|
+
metadata: mergedMetadata,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export { NotFoundError };
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import { AppError } from "./app-error";
|
|
2
|
+
import { serializationErrorCode } from "./error-codes";
|
|
3
|
+
import type { AppErrorOptions } from "./types";
|
|
4
|
+
|
|
5
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
6
|
+
// Types
|
|
7
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
8
|
+
|
|
9
|
+
type SerializationDirection = "serialize" | "deserialize";
|
|
10
|
+
|
|
11
|
+
type SerializationFailureCategory =
|
|
12
|
+
| "invalid_json"
|
|
13
|
+
| "invalid_shape"
|
|
14
|
+
| "missing_field"
|
|
15
|
+
| "unexpected_field"
|
|
16
|
+
| "invalid_type"
|
|
17
|
+
| "schema_version_missing"
|
|
18
|
+
| "schema_version_unsupported"
|
|
19
|
+
| "mapping_not_implemented"
|
|
20
|
+
| "contract_mismatch"
|
|
21
|
+
| "parse_failed"
|
|
22
|
+
| "stringify_failed"
|
|
23
|
+
| "unknown";
|
|
24
|
+
|
|
25
|
+
type SerializationBoundary =
|
|
26
|
+
| "http_in"
|
|
27
|
+
| "http_out"
|
|
28
|
+
| "graphql_in"
|
|
29
|
+
| "graphql_out"
|
|
30
|
+
| "dto_to_domain"
|
|
31
|
+
| "domain_to_dto"
|
|
32
|
+
| "persistence_to_domain"
|
|
33
|
+
| "domain_to_persistence"
|
|
34
|
+
| "event_in"
|
|
35
|
+
| "event_out"
|
|
36
|
+
| "projection"
|
|
37
|
+
| "unknown";
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Metadata shared by serialization/deserialization failures. Payload previews must be sanitized.
|
|
41
|
+
*/
|
|
42
|
+
type SerializationErrorMetadata = {
|
|
43
|
+
direction: SerializationDirection;
|
|
44
|
+
category: SerializationFailureCategory;
|
|
45
|
+
boundary: SerializationBoundary;
|
|
46
|
+
contract: string;
|
|
47
|
+
schemaVersion?: number | string;
|
|
48
|
+
path?: string;
|
|
49
|
+
payloadPreview?: string;
|
|
50
|
+
correlationId?: string;
|
|
51
|
+
requestId?: string;
|
|
52
|
+
commandId?: string;
|
|
53
|
+
hint?: string;
|
|
54
|
+
details?: string;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
58
|
+
// Base error
|
|
59
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
60
|
+
|
|
61
|
+
type SerializationErrorOptions = Omit<AppErrorOptions, "metadata"> & {
|
|
62
|
+
message: string;
|
|
63
|
+
code: string;
|
|
64
|
+
metadata: Omit<SerializationErrorMetadata, "direction" | "category">;
|
|
65
|
+
direction: SerializationDirection;
|
|
66
|
+
category: SerializationFailureCategory;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
abstract class SerializationError extends AppError {
|
|
70
|
+
public readonly direction: SerializationDirection;
|
|
71
|
+
public readonly category: SerializationFailureCategory;
|
|
72
|
+
public readonly boundary: SerializationBoundary;
|
|
73
|
+
public readonly contract: string;
|
|
74
|
+
|
|
75
|
+
protected constructor(input: SerializationErrorOptions) {
|
|
76
|
+
super(input.message ?? "Serialization failure", input.code, {
|
|
77
|
+
cause: input.cause,
|
|
78
|
+
metadata: {
|
|
79
|
+
direction: input.direction,
|
|
80
|
+
category: input.category,
|
|
81
|
+
...input.metadata,
|
|
82
|
+
},
|
|
83
|
+
type: input.type,
|
|
84
|
+
severity: input.severity,
|
|
85
|
+
correlationId: input.correlationId,
|
|
86
|
+
requestId: input.requestId,
|
|
87
|
+
commandId: input.commandId,
|
|
88
|
+
createdAtIso: input.createdAtIso,
|
|
89
|
+
publicMessage: input.publicMessage,
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
this.direction = input.direction;
|
|
93
|
+
this.category = input.category;
|
|
94
|
+
this.boundary = input.metadata.boundary;
|
|
95
|
+
this.contract = input.metadata.contract;
|
|
96
|
+
|
|
97
|
+
Object.freeze(this);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
102
|
+
// Serialization In
|
|
103
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
104
|
+
|
|
105
|
+
class SerializationInError extends SerializationError {
|
|
106
|
+
private constructor(input: SerializationErrorOptions) {
|
|
107
|
+
super(input);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
static failure(
|
|
111
|
+
category: SerializationFailureCategory,
|
|
112
|
+
metadata: Omit<SerializationErrorMetadata, "direction" | "category"> & {
|
|
113
|
+
cause?: unknown;
|
|
114
|
+
},
|
|
115
|
+
): SerializationInError {
|
|
116
|
+
return new SerializationInError({
|
|
117
|
+
message: SerializationMessages.message("deserialize", category),
|
|
118
|
+
code: SerializationCodes.code("deserialize", category),
|
|
119
|
+
category,
|
|
120
|
+
direction: "deserialize",
|
|
121
|
+
metadata,
|
|
122
|
+
cause: metadata.cause,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
128
|
+
// Serialization
|
|
129
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
130
|
+
|
|
131
|
+
class SerializationOutError extends SerializationError {
|
|
132
|
+
private constructor(input: SerializationErrorOptions) {
|
|
133
|
+
super(input);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
static failure(
|
|
137
|
+
category: SerializationFailureCategory,
|
|
138
|
+
metadata: Omit<SerializationErrorMetadata, "direction" | "category"> & {
|
|
139
|
+
cause?: unknown;
|
|
140
|
+
},
|
|
141
|
+
): SerializationOutError {
|
|
142
|
+
return new SerializationOutError({
|
|
143
|
+
message: SerializationMessages.message("serialize", category),
|
|
144
|
+
code: SerializationCodes.code("serialize", category),
|
|
145
|
+
category,
|
|
146
|
+
direction: "serialize",
|
|
147
|
+
metadata,
|
|
148
|
+
cause: metadata.cause,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
154
|
+
// Codes + Messages helpers
|
|
155
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
156
|
+
|
|
157
|
+
class SerializationCodes {
|
|
158
|
+
static code(
|
|
159
|
+
direction: SerializationDirection,
|
|
160
|
+
category: SerializationFailureCategory,
|
|
161
|
+
): string {
|
|
162
|
+
return serializationErrorCode(direction, category);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
class SerializationMessages {
|
|
167
|
+
static message(
|
|
168
|
+
direction: SerializationDirection,
|
|
169
|
+
category: SerializationFailureCategory,
|
|
170
|
+
): string {
|
|
171
|
+
if (direction === "deserialize") {
|
|
172
|
+
if (category === "invalid_json") {
|
|
173
|
+
return "Invalid payload: malformed JSON";
|
|
174
|
+
}
|
|
175
|
+
if (category === "schema_version_missing") {
|
|
176
|
+
return "Invalid payload: schemaVersion is missing";
|
|
177
|
+
}
|
|
178
|
+
if (category === "schema_version_unsupported") {
|
|
179
|
+
return "Invalid payload: schemaVersion is not supported";
|
|
180
|
+
}
|
|
181
|
+
if (category === "invalid_shape") {
|
|
182
|
+
return "Invalid payload: incompatible shape";
|
|
183
|
+
}
|
|
184
|
+
if (category === "mapping_not_implemented") {
|
|
185
|
+
return "Unsupported payload: mapping not implemented";
|
|
186
|
+
}
|
|
187
|
+
if (category === "contract_mismatch") {
|
|
188
|
+
return "Payload does not match the expected contract";
|
|
189
|
+
}
|
|
190
|
+
return "Failed to parse incoming payload";
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (category === "stringify_failed") {
|
|
194
|
+
return "Failed to serialize the response";
|
|
195
|
+
}
|
|
196
|
+
if (category === "contract_mismatch") {
|
|
197
|
+
return "Response does not match the expected contract";
|
|
198
|
+
}
|
|
199
|
+
return "Failed to build the response";
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
204
|
+
// Utilities
|
|
205
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
206
|
+
|
|
207
|
+
function safePreview(value: unknown, limit = 240): string | undefined {
|
|
208
|
+
try {
|
|
209
|
+
const serialized =
|
|
210
|
+
typeof value === "string" ? value : JSON.stringify(value);
|
|
211
|
+
return serialized.length > limit
|
|
212
|
+
? `${serialized.slice(0, limit)}…`
|
|
213
|
+
: serialized;
|
|
214
|
+
} catch {
|
|
215
|
+
return undefined;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export {
|
|
220
|
+
safePreview,
|
|
221
|
+
SerializationCodes,
|
|
222
|
+
SerializationError,
|
|
223
|
+
SerializationInError as DeserializationError,
|
|
224
|
+
SerializationInError,
|
|
225
|
+
SerializationMessages,
|
|
226
|
+
SerializationOutError,
|
|
227
|
+
};
|
|
228
|
+
export type {
|
|
229
|
+
SerializationBoundary,
|
|
230
|
+
SerializationDirection,
|
|
231
|
+
SerializationErrorMetadata,
|
|
232
|
+
SerializationFailureCategory,
|
|
233
|
+
};
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { AppError } from "./app-error";
|
|
2
|
+
import { ErrorCodes } from "./error-codes";
|
|
3
|
+
import type { AppErrorOptions } from "./types";
|
|
4
|
+
|
|
5
|
+
type TemporalKind = "expired" | "not_yet_valid";
|
|
6
|
+
|
|
7
|
+
type TemporalPrecision = "EXACT" | "ESTIMATED" | "UNKNOWN" | "APPROXIMATE";
|
|
8
|
+
|
|
9
|
+
type TemporalErrorMetadata = {
|
|
10
|
+
resourceType: string;
|
|
11
|
+
resourceId?: string;
|
|
12
|
+
operation?: string;
|
|
13
|
+
validFromIso?: string | null;
|
|
14
|
+
validUntilIso?: string | null;
|
|
15
|
+
evaluatedAtIso: string;
|
|
16
|
+
policyId?: string;
|
|
17
|
+
precision?: TemporalPrecision;
|
|
18
|
+
hint?: string;
|
|
19
|
+
details?: string;
|
|
20
|
+
correlationId?: string;
|
|
21
|
+
requestId?: string;
|
|
22
|
+
commandId?: string;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
type TemporalErrorOptions = {
|
|
26
|
+
message: string;
|
|
27
|
+
code: string;
|
|
28
|
+
kind: TemporalKind;
|
|
29
|
+
metadata: TemporalErrorMetadata;
|
|
30
|
+
} & AppErrorOptions;
|
|
31
|
+
|
|
32
|
+
abstract class TemporalError extends AppError {
|
|
33
|
+
public readonly kind: TemporalKind;
|
|
34
|
+
public readonly resourceType: string;
|
|
35
|
+
public readonly evaluatedAtIso: string;
|
|
36
|
+
|
|
37
|
+
protected constructor(input: TemporalErrorOptions) {
|
|
38
|
+
super(input.message, input.code, {
|
|
39
|
+
...input,
|
|
40
|
+
metadata: {
|
|
41
|
+
kind: input.kind,
|
|
42
|
+
...input.metadata,
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
this.kind = input.kind;
|
|
47
|
+
this.resourceType = input.metadata.resourceType;
|
|
48
|
+
this.evaluatedAtIso = input.metadata.evaluatedAtIso;
|
|
49
|
+
Object.freeze(this);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
type TemporalCreationInput = TemporalErrorMetadata &
|
|
54
|
+
Partial<Omit<AppErrorOptions, "metadata">> & {
|
|
55
|
+
cause?: unknown;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
class ExpiredError extends TemporalError {
|
|
59
|
+
private constructor(input: TemporalErrorOptions) {
|
|
60
|
+
super(input);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
static detected(input: TemporalCreationInput): ExpiredError {
|
|
64
|
+
return new ExpiredError({
|
|
65
|
+
message: "The validity of the resource/action has expired",
|
|
66
|
+
code: ErrorCodes.temporal.expired,
|
|
67
|
+
kind: "expired",
|
|
68
|
+
metadata: {
|
|
69
|
+
...input,
|
|
70
|
+
hint:
|
|
71
|
+
input.hint ??
|
|
72
|
+
"The window has expired. Request a new link or try again within the valid period.",
|
|
73
|
+
},
|
|
74
|
+
cause: input.cause,
|
|
75
|
+
type: input.type,
|
|
76
|
+
severity: input.severity,
|
|
77
|
+
correlationId: input.correlationId,
|
|
78
|
+
requestId: input.requestId,
|
|
79
|
+
commandId: input.commandId,
|
|
80
|
+
createdAtIso: input.createdAtIso,
|
|
81
|
+
publicMessage: input.publicMessage,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
class NotYetValidError extends TemporalError {
|
|
87
|
+
private constructor(input: TemporalErrorOptions) {
|
|
88
|
+
super(input);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
static detected(input: TemporalCreationInput): NotYetValidError {
|
|
92
|
+
return new NotYetValidError({
|
|
93
|
+
message: "The resource/action is not yet valid",
|
|
94
|
+
code: ErrorCodes.temporal.notYetValid,
|
|
95
|
+
kind: "not_yet_valid",
|
|
96
|
+
metadata: {
|
|
97
|
+
...input,
|
|
98
|
+
hint: input.hint ?? "Not yet within the valid period. Try again later.",
|
|
99
|
+
},
|
|
100
|
+
cause: input.cause,
|
|
101
|
+
type: input.type,
|
|
102
|
+
severity: input.severity,
|
|
103
|
+
correlationId: input.correlationId,
|
|
104
|
+
requestId: input.requestId,
|
|
105
|
+
commandId: input.commandId,
|
|
106
|
+
createdAtIso: input.createdAtIso,
|
|
107
|
+
publicMessage: input.publicMessage,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function evaluateTemporalWindow(input: {
|
|
113
|
+
validFromIso?: string | null;
|
|
114
|
+
validUntilIso?: string | null;
|
|
115
|
+
evaluatedAtIso: string;
|
|
116
|
+
}): { expired: boolean; notYetValid: boolean } | null {
|
|
117
|
+
const evaluatedMs = Date.parse(input.evaluatedAtIso);
|
|
118
|
+
if (Number.isNaN(evaluatedMs)) return null;
|
|
119
|
+
|
|
120
|
+
let notYetValid = false;
|
|
121
|
+
|
|
122
|
+
if (input.validFromIso) {
|
|
123
|
+
const fromMs = Date.parse(input.validFromIso);
|
|
124
|
+
if (!Number.isNaN(fromMs) && evaluatedMs < fromMs) {
|
|
125
|
+
notYetValid = true;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (input.validUntilIso) {
|
|
130
|
+
const untilMs = Date.parse(input.validUntilIso);
|
|
131
|
+
if (!Number.isNaN(untilMs) && evaluatedMs > untilMs) {
|
|
132
|
+
return { expired: true, notYetValid: false };
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return { expired: false, notYetValid };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export {
|
|
140
|
+
evaluateTemporalWindow,
|
|
141
|
+
ExpiredError,
|
|
142
|
+
NotYetValidError,
|
|
143
|
+
TemporalError,
|
|
144
|
+
};
|
|
145
|
+
export type { TemporalErrorMetadata, TemporalKind, TemporalPrecision };
|