@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 @@
|
|
|
1
|
+
{"version":3,"file":"validation-error.js","names":["compactMetadata","extractAppErrorOptions"],"sources":["../src/core/errors/authentication-error.ts","../src/core/errors/authorization-error.ts","../src/core/errors/business-rule-violation-error.ts","../src/core/errors/conflict-error.ts","../src/core/errors/idempotency-error.ts","../src/core/errors/integration-error.ts","../src/core/errors/legacy-incompatible-error.ts","../src/core/errors/not-found-error.ts","../src/core/errors/serialization-error.ts","../src/core/errors/temporal-error.ts","../src/core/errors/validation-error.ts"],"sourcesContent":["import { AppError } from \"./app-error\";\nimport { ErrorCodes } from \"./error-codes\";\nimport type { AppErrorOptions, ErrorSeverity } from \"./types\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────────────────────\n\ntype AuthenticationErrorReason =\n | \"missing_token\"\n | \"malformed_token\"\n | \"invalid_token\"\n | \"expired_token\"\n | \"revoked_token\"\n | \"invalid_credentials\"\n | \"session_not_found\"\n | \"session_expired\"\n | \"unknown\";\n\ntype AuthenticationErrorMetadata = {\n reason: AuthenticationErrorReason;\n authScheme?: \"bearer\" | \"cookie\" | \"basic\" | \"unknown\";\n tokenLocation?: \"header\" | \"cookie\" | \"query\" | \"unknown\";\n expiresAtIso?: string;\n correlationId?: string;\n requestId?: string;\n details?: string;\n};\n\ntype AuthenticationErrorFactoryOptions = Omit<\n AuthenticationErrorMetadata,\n \"reason\"\n> &\n Omit<AppErrorOptions, \"metadata\">;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// AuthenticationError\n// ─────────────────────────────────────────────────────────────────────────────\n\nclass AuthenticationError extends AppError {\n public readonly reason: AuthenticationErrorReason;\n\n private constructor(params: {\n message: string;\n code: string;\n reason: AuthenticationErrorReason;\n metadata?: Omit<AuthenticationErrorMetadata, \"reason\">;\n cause?: unknown;\n type?: string;\n severity?: ErrorSeverity;\n correlationId?: string;\n requestId?: string;\n commandId?: string;\n createdAtIso?: string;\n publicMessage?: string;\n }) {\n super(params.message, params.code, {\n cause: params.cause,\n metadata: {\n reason: params.reason,\n ...(params.metadata ?? {}),\n },\n type: params.type,\n severity: params.severity,\n correlationId: params.correlationId,\n requestId: params.requestId,\n commandId: params.commandId,\n createdAtIso: params.createdAtIso,\n publicMessage: params.publicMessage,\n });\n\n this.reason = params.reason;\n Object.freeze(this);\n }\n\n // ─────────────────────────────────────────────────────────────────────────\n // Factory methods\n // ─────────────────────────────────────────────────────────────────────────\n\n static missingToken(\n options?: AuthenticationErrorFactoryOptions,\n ): AuthenticationError {\n return new AuthenticationError({\n message: \"Missing credentials\",\n code: ErrorCodes.authentication.missingToken,\n reason: \"missing_token\",\n metadata: compactMetadata(options),\n ...extractAppErrorOptions(options),\n });\n }\n\n static invalidToken(\n options?: AuthenticationErrorFactoryOptions & { cause?: unknown },\n ): AuthenticationError {\n return new AuthenticationError({\n message: \"Invalid credentials\",\n code: ErrorCodes.authentication.invalidToken,\n reason: \"invalid_token\",\n metadata: compactMetadata({\n authScheme: options?.authScheme,\n tokenLocation: options?.tokenLocation,\n details: options?.details,\n correlationId: options?.correlationId,\n requestId: options?.requestId,\n }),\n ...extractAppErrorOptions(options),\n });\n }\n\n static expiredToken(\n options?: AuthenticationErrorFactoryOptions & { sessionId?: string },\n ): AuthenticationError {\n return new AuthenticationError({\n message: \"Expired credentials\",\n code: ErrorCodes.authentication.expiredToken,\n reason: \"expired_token\",\n metadata: compactMetadata(options),\n ...extractAppErrorOptions(options),\n });\n }\n\n static invalidCredentials(\n options?: Pick<\n AuthenticationErrorFactoryOptions,\n | \"correlationId\"\n | \"requestId\"\n | \"type\"\n | \"severity\"\n | \"createdAtIso\"\n | \"publicMessage\"\n >,\n ): AuthenticationError {\n return new AuthenticationError({\n message: \"Invalid username or password\",\n code: ErrorCodes.authentication.invalidCredentials,\n reason: \"invalid_credentials\",\n metadata: compactMetadata(options),\n ...extractAppErrorOptions(options),\n });\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\nfunction extractAppErrorOptions(options?: Partial<AppErrorOptions>) {\n return {\n cause: options?.cause,\n type: options?.type,\n severity: options?.severity,\n correlationId: options?.correlationId,\n requestId: options?.requestId,\n commandId: options?.commandId,\n createdAtIso: options?.createdAtIso,\n publicMessage: options?.publicMessage,\n };\n}\n\nfunction compactMetadata<T extends Record<string, unknown>>(\n input?: T,\n): T | undefined {\n if (!input) return undefined;\n\n return Object.fromEntries(\n Object.entries(input).filter(([, value]) => value !== undefined),\n ) as T;\n}\n\nexport { AuthenticationError };\nexport type { AuthenticationErrorMetadata, AuthenticationErrorReason };\n","import { AppError } from \"./app-error\";\nimport { ErrorCodes } from \"./error-codes\";\nimport type { AppErrorOptions, ErrorSeverity } from \"./types\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────────────────────\n\ntype AuthorizationErrorReason =\n | \"forbidden\"\n | \"missing_role\"\n | \"missing_capability\"\n | \"out_of_scope\"\n | \"outside_time_window\"\n | \"policy_denied\"\n | \"unknown\";\n\ntype AuthorizationRequirement = {\n role?: string;\n capability?: string;\n scope?: string; // e.g.: \"school:{id}\" or \"class:{id}\"\n};\n\ntype AuthorizationErrorMetadata = {\n reason: AuthorizationErrorReason;\n\n /** Stable action you evaluated (not an HTTP route — a \"business action\") */\n action: string;\n\n /** Resource identification (without leaking the full payload) */\n resource?: { type: string; id?: string };\n\n /** Optional: actor (do not include sensitive data) */\n actor?: { userId: string; role?: string };\n\n /** Optional: expected requirement */\n required?: AuthorizationRequirement;\n\n /** Optional: policy (when you already have PolicyEvaluation or similar) */\n policyId?: string;\n policyVersion?: number;\n evaluatedAtIso?: string;\n decision?: \"allow\" | \"deny\";\n reasonCode?: string;\n\n /** Optional: additional non-sensitive info */\n details?: string;\n};\n\n/**\n * Options for AuthorizationError factory methods.\n * Combines metadata fields (except 'reason') with common AppError options.\n */\ntype AuthorizationErrorFactoryOptions = Omit<\n AuthorizationErrorMetadata,\n \"reason\"\n> &\n Omit<AppErrorOptions, \"metadata\">;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// AuthorizationError\n// ─────────────────────────────────────────────────────────────────────────────\n\nclass AuthorizationError extends AppError {\n public readonly reason: AuthorizationErrorReason;\n\n private constructor(params: {\n message: string;\n code: string;\n reason: AuthorizationErrorReason;\n metadata: AuthorizationErrorMetadata;\n cause?: unknown;\n type?: string;\n severity?: ErrorSeverity;\n correlationId?: string;\n requestId?: string;\n commandId?: string;\n createdAtIso?: string;\n publicMessage?: string;\n }) {\n super(params.message, params.code, {\n cause: params.cause,\n metadata: params.metadata,\n type: params.type,\n severity: params.severity,\n correlationId: params.correlationId,\n requestId: params.requestId,\n commandId: params.commandId,\n createdAtIso: params.createdAtIso,\n publicMessage: params.publicMessage,\n });\n\n this.reason = params.reason;\n Object.freeze(this);\n }\n\n // ─────────────────────────────────────────────────────────────────────────\n // Factories\n // ─────────────────────────────────────────────────────────────────────────\n\n static forbidden(\n input: AuthorizationErrorFactoryOptions,\n ): AuthorizationError {\n return new AuthorizationError({\n message: \"Action not allowed\",\n code: ErrorCodes.authorization.forbidden,\n reason: \"forbidden\",\n metadata: { reason: \"forbidden\", ...extractMetadataOnly(input) },\n ...extractAppErrorOptions(input),\n });\n }\n\n static policyDenied(\n input: AuthorizationErrorFactoryOptions & {\n policyId: string;\n policyVersion: number;\n evaluatedAtIso: string;\n },\n ): AuthorizationError {\n return new AuthorizationError({\n message: \"Action denied by policy\",\n code: ErrorCodes.authorization.policyDenied,\n reason: \"policy_denied\",\n metadata: {\n reason: \"policy_denied\",\n ...extractMetadataOnly(input),\n decision: \"deny\",\n },\n ...extractAppErrorOptions(input),\n });\n }\n\n static missingCapability(\n input: AuthorizationErrorFactoryOptions & {\n required: AuthorizationRequirement;\n },\n ): AuthorizationError {\n return new AuthorizationError({\n message: \"Insufficient capability to perform the action\",\n code: ErrorCodes.authorization.missingCapability,\n reason: \"missing_capability\",\n metadata: {\n reason: \"missing_capability\",\n ...extractMetadataOnly(input),\n },\n ...extractAppErrorOptions(input),\n });\n }\n\n static outOfScope(\n input: AuthorizationErrorFactoryOptions & {\n required?: AuthorizationRequirement;\n },\n ): AuthorizationError {\n return new AuthorizationError({\n message: \"Action outside the allowed scope\",\n code: ErrorCodes.authorization.outOfScope,\n reason: \"out_of_scope\",\n metadata: { reason: \"out_of_scope\", ...extractMetadataOnly(input) },\n ...extractAppErrorOptions(input),\n });\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\nfunction extractAppErrorOptions(options?: Partial<AppErrorOptions>) {\n return {\n cause: options?.cause,\n type: options?.type,\n severity: options?.severity,\n correlationId: options?.correlationId,\n requestId: options?.requestId,\n commandId: options?.commandId,\n createdAtIso: options?.createdAtIso,\n publicMessage: options?.publicMessage,\n };\n}\n\nfunction extractMetadataOnly(\n input: AuthorizationErrorFactoryOptions,\n): Omit<AuthorizationErrorMetadata, \"reason\"> {\n const {\n cause,\n type,\n severity,\n correlationId,\n requestId,\n commandId,\n createdAtIso,\n publicMessage,\n ...metadata\n } = input;\n return metadata;\n}\n\nexport { AuthorizationError };\nexport type {\n AuthorizationErrorMetadata,\n AuthorizationErrorReason,\n AuthorizationRequirement,\n};\n","import { AppError } from \"./app-error\";\nimport { ErrorCodes } from \"./error-codes\";\nimport type { AppErrorOptions } from \"./types\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// BusinessRuleViolationError\n// ─────────────────────────────────────────────────────────────────────────────\n\nclass BusinessRuleViolationError extends AppError {\n constructor(\n rule: string,\n message: string,\n detail?: Record<string, unknown>,\n options?: AppErrorOptions,\n ) {\n const baseMetadata = detail ? { rule, detail } : { rule };\n const mergedMetadata = options?.metadata\n ? { ...baseMetadata, ...options.metadata }\n : baseMetadata;\n\n super(message, ErrorCodes.businessRuleViolation, {\n ...options,\n metadata: mergedMetadata,\n });\n }\n}\n\nexport { BusinessRuleViolationError };\n","import { AppError } from \"./app-error\";\nimport { ErrorCodes } from \"./error-codes\";\nimport type { AppErrorOptions } from \"./types\";\n\n/**\n * Conflict kinds we want to distinguish semantically.\n */\ntype ConflictKind = \"already_exists\" | \"duplicate\" | \"unique_violation\";\n\n/**\n * Metadata attached to conflicts without exposing sensitive values.\n */\ntype ConflictErrorMetadata = {\n kind: ConflictKind;\n entity: string;\n operation?: string;\n field?: string;\n constraintName?: string;\n existingId?: string;\n valueHash?: string;\n valuePreview?: string;\n detectedAtIso?: string;\n correlationId?: string;\n requestId?: string;\n commandId?: string;\n hint?: string;\n};\n\ntype UniqueConstraintViolation = {\n kind: \"unique_violation\";\n constraintName?: string;\n table?: string;\n columns?: string[];\n};\n\nabstract class ConflictError extends AppError {\n public readonly kind: ConflictKind;\n\n protected constructor(\n params: {\n message: string;\n code: string;\n kind: ConflictKind;\n metadata: Omit<ConflictErrorMetadata, \"kind\">;\n cause?: unknown;\n } & AppErrorOptions,\n ) {\n super(params.message, params.code, {\n ...params,\n metadata: {\n kind: params.kind,\n ...params.metadata,\n },\n });\n\n this.kind = params.kind;\n Object.freeze(this);\n }\n}\n\nclass AlreadyExistsError extends ConflictError {\n private constructor(\n input: {\n metadata: Omit<ConflictErrorMetadata, \"kind\">;\n cause?: unknown;\n } & AppErrorOptions,\n ) {\n super({\n message: \"A matching record already exists for the requested operation\",\n code: ErrorCodes.conflict.alreadyExists,\n kind: \"already_exists\",\n ...input,\n });\n }\n\n static detected(input: {\n entity: string;\n operation?: string;\n field?: string;\n existingId?: string;\n valueHash?: string;\n valuePreview?: string;\n detectedAtIso?: string;\n correlationId?: string;\n requestId?: string;\n commandId?: string;\n hint?: string;\n cause?: unknown;\n }): AlreadyExistsError {\n return new AlreadyExistsError({\n metadata: {\n entity: input.entity,\n operation: input.operation ?? \"create\",\n field: input.field,\n existingId: input.existingId,\n valueHash: input.valueHash,\n valuePreview: input.valuePreview,\n detectedAtIso: input.detectedAtIso,\n correlationId: input.correlationId,\n requestId: input.requestId,\n commandId: input.commandId,\n hint: input.hint ?? \"Verify existing records before retrying.\",\n },\n cause: input.cause,\n });\n }\n}\n\nclass DuplicateError extends ConflictError {\n private constructor(\n input: {\n metadata: Omit<ConflictErrorMetadata, \"kind\">;\n cause?: unknown;\n } & AppErrorOptions,\n ) {\n super({\n message: \"Conflict: an existing record violates uniqueness\",\n code: ErrorCodes.conflict.duplicate,\n kind: \"duplicate\",\n ...input,\n });\n }\n\n static detected(input: {\n entity: string;\n operation?: string;\n field?: string;\n constraintName?: string;\n existingId?: string;\n valueHash?: string;\n valuePreview?: string;\n detectedAtIso?: string;\n correlationId?: string;\n requestId?: string;\n commandId?: string;\n hint?: string;\n cause?: unknown;\n }): DuplicateError {\n return new DuplicateError({\n metadata: {\n entity: input.entity,\n operation: input.operation,\n field: input.field,\n constraintName: input.constraintName,\n existingId: input.existingId,\n valueHash: input.valueHash,\n valuePreview: input.valuePreview,\n detectedAtIso: input.detectedAtIso,\n correlationId: input.correlationId,\n requestId: input.requestId,\n commandId: input.commandId,\n hint:\n input.hint ??\n \"Adjust the data so it does not duplicate existing records.\",\n },\n cause: input.cause,\n });\n }\n}\n\nclass UniqueConstraintViolationError extends ConflictError {\n private constructor(\n input: {\n metadata: Omit<ConflictErrorMetadata, \"kind\"> & {\n violation: UniqueConstraintViolation;\n };\n cause?: unknown;\n } & AppErrorOptions,\n ) {\n super({\n message: \"Uniqueness violation in storage\",\n code: ErrorCodes.conflict.uniqueViolation,\n kind: \"unique_violation\",\n ...input,\n });\n }\n\n static detected(input: {\n entity: string;\n operation?: string;\n constraintName?: string;\n table?: string;\n columns?: string[];\n detectedAtIso?: string;\n correlationId?: string;\n requestId?: string;\n commandId?: string;\n cause?: unknown;\n }): UniqueConstraintViolationError {\n return new UniqueConstraintViolationError({\n metadata: {\n entity: input.entity,\n operation: input.operation,\n constraintName: input.constraintName,\n detectedAtIso: input.detectedAtIso,\n correlationId: input.correlationId,\n requestId: input.requestId,\n commandId: input.commandId,\n violation: {\n kind: \"unique_violation\",\n constraintName: input.constraintName,\n table: input.table,\n columns: input.columns,\n },\n hint: \"Uniqueness conflict detected in the database.\",\n },\n cause: input.cause,\n });\n }\n}\n\nfunction translateUniqueViolationToDuplicate(input: {\n entity: string;\n operation?: string;\n violation: UniqueConstraintViolation;\n field?: string;\n ctx?: {\n correlationId?: string;\n requestId?: string;\n commandId?: string;\n nowIso?: string;\n };\n cause?: unknown;\n}): DuplicateError {\n return DuplicateError.detected({\n entity: input.entity,\n operation: input.operation,\n field: input.field,\n constraintName: input.violation.constraintName,\n detectedAtIso: input.ctx?.nowIso,\n correlationId: input.ctx?.correlationId,\n requestId: input.ctx?.requestId,\n commandId: input.ctx?.commandId,\n cause: input.cause,\n });\n}\n\nexport {\n AlreadyExistsError,\n ConflictError,\n DuplicateError,\n translateUniqueViolationToDuplicate,\n UniqueConstraintViolationError,\n};\n\nexport type { ConflictErrorMetadata, ConflictKind, UniqueConstraintViolation };\n","import { payloadHash, sha256Hex, stableStringify } from \"../shared/hashing\";\n\nimport { AppError } from \"./app-error\";\nimport { ErrorCodes } from \"./error-codes\";\n\nexport type IdempotencyFailureKind =\n | \"key_missing\"\n | \"in_progress\"\n | \"payload_mismatch\"\n | \"replay_not_supported\"\n | \"unknown\";\n\nexport type IdempotencyErrorMetadata = {\n kind: IdempotencyFailureKind;\n operation: string;\n scope?: string;\n entity?: string;\n keyHash?: string;\n keyPreview?: string;\n incomingPayloadHash?: string;\n existingPayloadHash?: string;\n idempotencyRecordId?: string;\n correlationId?: string;\n requestId?: string;\n commandId?: string;\n detectedAtIso?: string;\n hint?: string;\n details?: string;\n};\n\ntype IdempotencyErrorConstructorInput = {\n message: string;\n code: string;\n kind: IdempotencyFailureKind;\n metadata: Omit<IdempotencyErrorMetadata, \"kind\">;\n cause?: unknown;\n};\n\nabstract class IdempotencyError extends AppError {\n public readonly kind: IdempotencyFailureKind;\n\n protected constructor(input: IdempotencyErrorConstructorInput) {\n super(input.message, input.code, {\n cause: input.cause,\n metadata: { kind: input.kind, ...input.metadata },\n });\n\n this.kind = input.kind;\n Object.freeze(this);\n }\n\n static keyMissing(input: {\n operation: string;\n scope?: string;\n entity?: string;\n correlationId?: string;\n requestId?: string;\n commandId?: string;\n detectedAtIso?: string;\n hint?: string;\n details?: string;\n cause?: unknown;\n }): IdempotencyKeyMissingError {\n return IdempotencyKeyMissingError.detected(input);\n }\n\n static inProgress(input: {\n operation: string;\n scope?: string;\n entity?: string;\n key?: string;\n keyHash?: string;\n keyPreview?: string;\n idempotencyRecordId?: string;\n correlationId?: string;\n requestId?: string;\n commandId?: string;\n detectedAtIso?: string;\n hint?: string;\n details?: string;\n cause?: unknown;\n }): IdempotencyInProgressError {\n return IdempotencyInProgressError.detected(input);\n }\n\n static payloadMismatch(input: {\n operation: string;\n scope?: string;\n entity?: string;\n key?: string;\n keyHash?: string;\n keyPreview?: string;\n incomingPayloadHash?: string;\n existingPayloadHash?: string;\n idempotencyRecordId?: string;\n correlationId?: string;\n requestId?: string;\n commandId?: string;\n detectedAtIso?: string;\n hint?: string;\n details?: string;\n cause?: unknown;\n }): IdempotencyPayloadMismatchError {\n return IdempotencyPayloadMismatchError.detected(input);\n }\n\n static replayNotSupported(input: {\n operation: string;\n scope?: string;\n entity?: string;\n key?: string;\n keyHash?: string;\n keyPreview?: string;\n idempotencyRecordId?: string;\n correlationId?: string;\n requestId?: string;\n commandId?: string;\n detectedAtIso?: string;\n hint?: string;\n details?: string;\n cause?: unknown;\n }): IdempotencyReplayNotSupportedError {\n return IdempotencyReplayNotSupportedError.detected(input);\n }\n}\n\nclass IdempotencyKeyMissingError extends IdempotencyError {\n private constructor(input: {\n metadata: Omit<IdempotencyErrorMetadata, \"kind\">;\n cause?: unknown;\n }) {\n super({\n message: \"Idempotency key/commandId missing for the requested operation\",\n code: ErrorCodes.idempotency.keyMissing,\n kind: \"key_missing\",\n metadata: input.metadata,\n cause: input.cause,\n });\n }\n\n static detected(input: {\n operation: string;\n scope?: string;\n entity?: string;\n correlationId?: string;\n requestId?: string;\n commandId?: string;\n detectedAtIso?: string;\n hint?: string;\n details?: string;\n cause?: unknown;\n }): IdempotencyKeyMissingError {\n return new IdempotencyKeyMissingError({\n cause: input.cause,\n metadata: {\n operation: input.operation,\n scope: input.scope,\n entity: input.entity,\n correlationId: input.correlationId,\n requestId: input.requestId,\n commandId: input.commandId,\n detectedAtIso: input.detectedAtIso,\n hint:\n input.hint ??\n \"Send a commandId/idempotency key to prevent duplicate processing.\",\n details: input.details,\n },\n });\n }\n}\n\nclass IdempotencyInProgressError extends IdempotencyError {\n private constructor(input: {\n metadata: Omit<IdempotencyErrorMetadata, \"kind\">;\n cause?: unknown;\n }) {\n super({\n message: \"The same business intent is already being processed\",\n code: ErrorCodes.idempotency.inProgress,\n kind: \"in_progress\",\n metadata: input.metadata,\n cause: input.cause,\n });\n }\n\n static detected(input: {\n operation: string;\n scope?: string;\n entity?: string;\n key?: string;\n keyHash?: string;\n keyPreview?: string;\n idempotencyRecordId?: string;\n correlationId?: string;\n requestId?: string;\n commandId?: string;\n detectedAtIso?: string;\n hint?: string;\n details?: string;\n cause?: unknown;\n }): IdempotencyInProgressError {\n const resolved = resolveKeyIdentity(\n input.key,\n input.keyHash,\n input.keyPreview,\n );\n\n return new IdempotencyInProgressError({\n cause: input.cause,\n metadata: {\n operation: input.operation,\n scope: input.scope,\n entity: input.entity,\n keyHash: resolved.keyHash,\n keyPreview: resolved.keyPreview,\n idempotencyRecordId: input.idempotencyRecordId,\n correlationId: input.correlationId,\n requestId: input.requestId,\n commandId: input.commandId,\n detectedAtIso: input.detectedAtIso,\n hint: input.hint ?? \"Wait for completion and retry using the same key.\",\n details: input.details,\n },\n });\n }\n}\n\nclass IdempotencyPayloadMismatchError extends IdempotencyError {\n private constructor(input: {\n metadata: Omit<IdempotencyErrorMetadata, \"kind\">;\n cause?: unknown;\n }) {\n super({\n message: \"The idempotency key was reused with a different payload\",\n code: ErrorCodes.idempotency.payloadMismatch,\n kind: \"payload_mismatch\",\n metadata: input.metadata,\n cause: input.cause,\n });\n }\n\n static detected(input: {\n operation: string;\n scope?: string;\n entity?: string;\n key?: string;\n keyHash?: string;\n keyPreview?: string;\n incomingPayloadHash?: string;\n existingPayloadHash?: string;\n idempotencyRecordId?: string;\n correlationId?: string;\n requestId?: string;\n commandId?: string;\n detectedAtIso?: string;\n hint?: string;\n details?: string;\n cause?: unknown;\n }): IdempotencyPayloadMismatchError {\n const resolved = resolveKeyIdentity(\n input.key,\n input.keyHash,\n input.keyPreview,\n );\n\n return new IdempotencyPayloadMismatchError({\n cause: input.cause,\n metadata: {\n operation: input.operation,\n scope: input.scope,\n entity: input.entity,\n keyHash: resolved.keyHash,\n keyPreview: resolved.keyPreview,\n incomingPayloadHash: input.incomingPayloadHash,\n existingPayloadHash: input.existingPayloadHash,\n idempotencyRecordId: input.idempotencyRecordId,\n correlationId: input.correlationId,\n requestId: input.requestId,\n commandId: input.commandId,\n detectedAtIso: input.detectedAtIso,\n hint:\n input.hint ??\n \"Use a new idempotency key for a new intent. The same key should only be reused for retries of the same payload.\",\n details: input.details,\n },\n });\n }\n}\n\nclass IdempotencyReplayNotSupportedError extends IdempotencyError {\n private constructor(input: {\n metadata: Omit<IdempotencyErrorMetadata, \"kind\">;\n cause?: unknown;\n }) {\n super({\n message:\n \"Re-execution detected, but replay of the previous result is not implemented\",\n code: ErrorCodes.idempotency.replayNotSupported,\n kind: \"replay_not_supported\",\n metadata: input.metadata,\n cause: input.cause,\n });\n }\n\n static detected(input: {\n operation: string;\n scope?: string;\n entity?: string;\n key?: string;\n keyHash?: string;\n keyPreview?: string;\n idempotencyRecordId?: string;\n correlationId?: string;\n requestId?: string;\n commandId?: string;\n detectedAtIso?: string;\n hint?: string;\n details?: string;\n cause?: unknown;\n }): IdempotencyReplayNotSupportedError {\n const resolved = resolveKeyIdentity(\n input.key,\n input.keyHash,\n input.keyPreview,\n );\n\n return new IdempotencyReplayNotSupportedError({\n cause: input.cause,\n metadata: {\n operation: input.operation,\n scope: input.scope,\n entity: input.entity,\n keyHash: resolved.keyHash,\n keyPreview: resolved.keyPreview,\n idempotencyRecordId: input.idempotencyRecordId,\n correlationId: input.correlationId,\n requestId: input.requestId,\n commandId: input.commandId,\n detectedAtIso: input.detectedAtIso,\n hint:\n input.hint ??\n \"Try again later, or enable result replay for full idempotency.\",\n details: input.details,\n },\n });\n }\n}\n\nfunction resolveKeyIdentity(\n key?: string,\n keyHash?: string,\n keyPreview?: string,\n): { keyHash?: string; keyPreview?: string } {\n const preview =\n keyPreview ?? (key ? (key.length <= 8 ? key : key.slice(0, 8)) : undefined);\n\n return {\n keyHash: keyHash,\n keyPreview: preview,\n };\n}\n\nexport {\n IdempotencyError,\n IdempotencyKeyMissingError,\n IdempotencyInProgressError,\n IdempotencyPayloadMismatchError,\n IdempotencyReplayNotSupportedError,\n sha256Hex,\n stableStringify,\n payloadHash,\n};\n","import type { AppErrorOptions, ErrorSeverity } from \"./types\";\nimport { AppError } from \"./app-error\";\nimport { ErrorCodes } from \"./error-codes\";\n\ntype IntegrationErrorReason =\n | \"timeout\"\n | \"unreachable\"\n | \"bad_response\"\n | \"unknown\";\n\ntype IntegrationErrorMetadata = {\n reason: IntegrationErrorReason;\n provider: string;\n operation: string;\n startedAtIso: string;\n durationMs: number;\n detectedAtIso?: string;\n correlationId?: string;\n requestId?: string;\n commandId?: string;\n details?: string;\n statusCode?: number;\n responseCode?: string;\n};\n\ntype IntegrationErrorAppOptions = Pick<\n AppErrorOptions,\n | \"cause\"\n | \"type\"\n | \"severity\"\n | \"correlationId\"\n | \"requestId\"\n | \"commandId\"\n | \"createdAtIso\"\n | \"publicMessage\"\n>;\n\ntype IntegrationErrorConstructorParams = {\n message: string;\n code: string;\n reason: IntegrationErrorReason;\n metadata: IntegrationErrorMetadata;\n} & IntegrationErrorAppOptions;\n\nclass IntegrationError extends AppError {\n public readonly reason: IntegrationErrorReason;\n\n private constructor(params: IntegrationErrorConstructorParams) {\n const { message, code, metadata, ...rest } = params;\n super(message, code, {\n ...rest,\n metadata,\n });\n\n this.reason = params.reason;\n Object.freeze(this);\n }\n\n static timeout(options: IntegrationErrorTimeoutOptions): IntegrationError {\n return new IntegrationError({\n message: \"Timeout while integrating with external provider\",\n code: ErrorCodes.integration.timeout,\n reason: \"timeout\",\n metadata: buildMetadata({ reason: \"timeout\", ...options }),\n ...pickAppErrorFields(options),\n });\n }\n\n static unreachable(\n options: IntegrationErrorEndpointOptions,\n ): IntegrationError {\n return new IntegrationError({\n message: \"Provider unavailable\",\n code: ErrorCodes.integration.unreachable,\n reason: \"unreachable\",\n metadata: buildMetadata({ reason: \"unreachable\", ...options }),\n ...pickAppErrorFields(options),\n });\n }\n\n static badResponse(\n options: IntegrationErrorEndpointOptions,\n ): IntegrationError {\n return new IntegrationError({\n message: \"Unexpected response from provider\",\n code: ErrorCodes.integration.badResponse,\n reason: \"bad_response\",\n metadata: buildMetadata({ reason: \"bad_response\", ...options }),\n ...pickAppErrorFields(options),\n });\n }\n}\n\ntype IntegrationErrorBaseOptions = {\n provider: string;\n operation: string;\n startedAtIso: string;\n durationMs: number;\n detectedAtIso?: string;\n correlationId?: string;\n requestId?: string;\n commandId?: string;\n details?: string;\n statusCode?: number;\n responseCode?: string;\n cause?: unknown;\n type?: string;\n severity?: ErrorSeverity;\n createdAtIso?: string;\n publicMessage?: string;\n};\n\ntype IntegrationErrorTimeoutOptions = IntegrationErrorBaseOptions;\ntype IntegrationErrorEndpointOptions = IntegrationErrorBaseOptions;\n\nfunction buildMetadata(\n input: IntegrationErrorBaseOptions & { reason: IntegrationErrorReason },\n): IntegrationErrorMetadata {\n return compactMetadata({\n reason: input.reason,\n provider: input.provider,\n operation: input.operation,\n startedAtIso: input.startedAtIso,\n durationMs: input.durationMs,\n detectedAtIso: input.detectedAtIso,\n correlationId: input.correlationId,\n requestId: input.requestId,\n commandId: input.commandId,\n details: input.details,\n statusCode: input.statusCode,\n responseCode: input.responseCode,\n }) as IntegrationErrorMetadata;\n}\n\nfunction pickAppErrorFields(\n options: IntegrationErrorBaseOptions,\n): IntegrationErrorAppOptions {\n return {\n cause: options.cause,\n type: options.type,\n severity: options.severity,\n correlationId: options.correlationId,\n requestId: options.requestId,\n commandId: options.commandId,\n createdAtIso: options.createdAtIso,\n publicMessage: options.publicMessage,\n };\n}\n\nfunction compactMetadata<T extends Record<string, unknown>>(input: T): T {\n return Object.fromEntries(\n Object.entries(input).filter(([, value]) => value !== undefined),\n ) as T;\n}\n\nexport { IntegrationError };\nexport type { IntegrationErrorReason, IntegrationErrorMetadata };\n","import { AppError } from \"./app-error\";\nimport { ErrorCodes } from \"./error-codes\";\nimport type { AppErrorOptions } from \"./types\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// LegacyIncompatibleError\n// ─────────────────────────────────────────────────────────────────────────────\n\nclass LegacyIncompatibleError extends AppError {\n constructor(\n message: string,\n context?: Record<string, unknown>,\n options?: AppErrorOptions,\n ) {\n const mergedMetadata = options?.metadata\n ? { ...(context ?? {}), ...options.metadata }\n : context;\n\n super(message, ErrorCodes.legacyIncompatible, {\n ...options,\n metadata: mergedMetadata,\n });\n }\n}\n\nexport { LegacyIncompatibleError };\n","import { AppError } from \"./app-error\";\nimport { ErrorCodes } from \"./error-codes\";\nimport type { AppErrorOptions } from \"./types\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// NotFoundError\n// ─────────────────────────────────────────────────────────────────────────────\n\nclass NotFoundError extends AppError {\n constructor(\n resource: string,\n criteria?: Record<string, unknown>,\n options?: AppErrorOptions,\n ) {\n const baseMetadata = criteria ? { resource, criteria } : { resource };\n const mergedMetadata = options?.metadata\n ? { ...baseMetadata, ...options.metadata }\n : baseMetadata;\n\n super(`${resource} not found`, ErrorCodes.notFound, {\n ...options,\n metadata: mergedMetadata,\n });\n }\n}\n\nexport { NotFoundError };\n","import { AppError } from \"./app-error\";\nimport { serializationErrorCode } from \"./error-codes\";\nimport type { AppErrorOptions } from \"./types\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────────────────────\n\ntype SerializationDirection = \"serialize\" | \"deserialize\";\n\ntype SerializationFailureCategory =\n | \"invalid_json\"\n | \"invalid_shape\"\n | \"missing_field\"\n | \"unexpected_field\"\n | \"invalid_type\"\n | \"schema_version_missing\"\n | \"schema_version_unsupported\"\n | \"mapping_not_implemented\"\n | \"contract_mismatch\"\n | \"parse_failed\"\n | \"stringify_failed\"\n | \"unknown\";\n\ntype SerializationBoundary =\n | \"http_in\"\n | \"http_out\"\n | \"graphql_in\"\n | \"graphql_out\"\n | \"dto_to_domain\"\n | \"domain_to_dto\"\n | \"persistence_to_domain\"\n | \"domain_to_persistence\"\n | \"event_in\"\n | \"event_out\"\n | \"projection\"\n | \"unknown\";\n\n/**\n * Metadata shared by serialization/deserialization failures. Payload previews must be sanitized.\n */\ntype SerializationErrorMetadata = {\n direction: SerializationDirection;\n category: SerializationFailureCategory;\n boundary: SerializationBoundary;\n contract: string;\n schemaVersion?: number | string;\n path?: string;\n payloadPreview?: string;\n correlationId?: string;\n requestId?: string;\n commandId?: string;\n hint?: string;\n details?: string;\n};\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Base error\n// ─────────────────────────────────────────────────────────────────────────────\n\ntype SerializationErrorOptions = Omit<AppErrorOptions, \"metadata\"> & {\n message: string;\n code: string;\n metadata: Omit<SerializationErrorMetadata, \"direction\" | \"category\">;\n direction: SerializationDirection;\n category: SerializationFailureCategory;\n};\n\nabstract class SerializationError extends AppError {\n public readonly direction: SerializationDirection;\n public readonly category: SerializationFailureCategory;\n public readonly boundary: SerializationBoundary;\n public readonly contract: string;\n\n protected constructor(input: SerializationErrorOptions) {\n super(input.message ?? \"Serialization failure\", input.code, {\n cause: input.cause,\n metadata: {\n direction: input.direction,\n category: input.category,\n ...input.metadata,\n },\n type: input.type,\n severity: input.severity,\n correlationId: input.correlationId,\n requestId: input.requestId,\n commandId: input.commandId,\n createdAtIso: input.createdAtIso,\n publicMessage: input.publicMessage,\n });\n\n this.direction = input.direction;\n this.category = input.category;\n this.boundary = input.metadata.boundary;\n this.contract = input.metadata.contract;\n\n Object.freeze(this);\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Serialization In\n// ─────────────────────────────────────────────────────────────────────────────\n\nclass SerializationInError extends SerializationError {\n private constructor(input: SerializationErrorOptions) {\n super(input);\n }\n\n static failure(\n category: SerializationFailureCategory,\n metadata: Omit<SerializationErrorMetadata, \"direction\" | \"category\"> & {\n cause?: unknown;\n },\n ): SerializationInError {\n return new SerializationInError({\n message: SerializationMessages.message(\"deserialize\", category),\n code: SerializationCodes.code(\"deserialize\", category),\n category,\n direction: \"deserialize\",\n metadata,\n cause: metadata.cause,\n });\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Serialization\n// ─────────────────────────────────────────────────────────────────────────────\n\nclass SerializationOutError extends SerializationError {\n private constructor(input: SerializationErrorOptions) {\n super(input);\n }\n\n static failure(\n category: SerializationFailureCategory,\n metadata: Omit<SerializationErrorMetadata, \"direction\" | \"category\"> & {\n cause?: unknown;\n },\n ): SerializationOutError {\n return new SerializationOutError({\n message: SerializationMessages.message(\"serialize\", category),\n code: SerializationCodes.code(\"serialize\", category),\n category,\n direction: \"serialize\",\n metadata,\n cause: metadata.cause,\n });\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Codes + Messages helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\nclass SerializationCodes {\n static code(\n direction: SerializationDirection,\n category: SerializationFailureCategory,\n ): string {\n return serializationErrorCode(direction, category);\n }\n}\n\nclass SerializationMessages {\n static message(\n direction: SerializationDirection,\n category: SerializationFailureCategory,\n ): string {\n if (direction === \"deserialize\") {\n if (category === \"invalid_json\") {\n return \"Invalid payload: malformed JSON\";\n }\n if (category === \"schema_version_missing\") {\n return \"Invalid payload: schemaVersion is missing\";\n }\n if (category === \"schema_version_unsupported\") {\n return \"Invalid payload: schemaVersion is not supported\";\n }\n if (category === \"invalid_shape\") {\n return \"Invalid payload: incompatible shape\";\n }\n if (category === \"mapping_not_implemented\") {\n return \"Unsupported payload: mapping not implemented\";\n }\n if (category === \"contract_mismatch\") {\n return \"Payload does not match the expected contract\";\n }\n return \"Failed to parse incoming payload\";\n }\n\n if (category === \"stringify_failed\") {\n return \"Failed to serialize the response\";\n }\n if (category === \"contract_mismatch\") {\n return \"Response does not match the expected contract\";\n }\n return \"Failed to build the response\";\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Utilities\n// ─────────────────────────────────────────────────────────────────────────────\n\nfunction safePreview(value: unknown, limit = 240): string | undefined {\n try {\n const serialized =\n typeof value === \"string\" ? value : JSON.stringify(value);\n return serialized.length > limit\n ? `${serialized.slice(0, limit)}…`\n : serialized;\n } catch {\n return undefined;\n }\n}\n\nexport {\n safePreview,\n SerializationCodes,\n SerializationError,\n SerializationInError as DeserializationError,\n SerializationInError,\n SerializationMessages,\n SerializationOutError,\n};\nexport type {\n SerializationBoundary,\n SerializationDirection,\n SerializationErrorMetadata,\n SerializationFailureCategory,\n};\n","import { AppError } from \"./app-error\";\nimport { ErrorCodes } from \"./error-codes\";\nimport type { AppErrorOptions } from \"./types\";\n\ntype TemporalKind = \"expired\" | \"not_yet_valid\";\n\ntype TemporalPrecision = \"EXACT\" | \"ESTIMATED\" | \"UNKNOWN\" | \"APPROXIMATE\";\n\ntype TemporalErrorMetadata = {\n resourceType: string;\n resourceId?: string;\n operation?: string;\n validFromIso?: string | null;\n validUntilIso?: string | null;\n evaluatedAtIso: string;\n policyId?: string;\n precision?: TemporalPrecision;\n hint?: string;\n details?: string;\n correlationId?: string;\n requestId?: string;\n commandId?: string;\n};\n\ntype TemporalErrorOptions = {\n message: string;\n code: string;\n kind: TemporalKind;\n metadata: TemporalErrorMetadata;\n} & AppErrorOptions;\n\nabstract class TemporalError extends AppError {\n public readonly kind: TemporalKind;\n public readonly resourceType: string;\n public readonly evaluatedAtIso: string;\n\n protected constructor(input: TemporalErrorOptions) {\n super(input.message, input.code, {\n ...input,\n metadata: {\n kind: input.kind,\n ...input.metadata,\n },\n });\n\n this.kind = input.kind;\n this.resourceType = input.metadata.resourceType;\n this.evaluatedAtIso = input.metadata.evaluatedAtIso;\n Object.freeze(this);\n }\n}\n\ntype TemporalCreationInput = TemporalErrorMetadata &\n Partial<Omit<AppErrorOptions, \"metadata\">> & {\n cause?: unknown;\n };\n\nclass ExpiredError extends TemporalError {\n private constructor(input: TemporalErrorOptions) {\n super(input);\n }\n\n static detected(input: TemporalCreationInput): ExpiredError {\n return new ExpiredError({\n message: \"The validity of the resource/action has expired\",\n code: ErrorCodes.temporal.expired,\n kind: \"expired\",\n metadata: {\n ...input,\n hint:\n input.hint ??\n \"The window has expired. Request a new link or try again within the valid period.\",\n },\n cause: input.cause,\n type: input.type,\n severity: input.severity,\n correlationId: input.correlationId,\n requestId: input.requestId,\n commandId: input.commandId,\n createdAtIso: input.createdAtIso,\n publicMessage: input.publicMessage,\n });\n }\n}\n\nclass NotYetValidError extends TemporalError {\n private constructor(input: TemporalErrorOptions) {\n super(input);\n }\n\n static detected(input: TemporalCreationInput): NotYetValidError {\n return new NotYetValidError({\n message: \"The resource/action is not yet valid\",\n code: ErrorCodes.temporal.notYetValid,\n kind: \"not_yet_valid\",\n metadata: {\n ...input,\n hint: input.hint ?? \"Not yet within the valid period. Try again later.\",\n },\n cause: input.cause,\n type: input.type,\n severity: input.severity,\n correlationId: input.correlationId,\n requestId: input.requestId,\n commandId: input.commandId,\n createdAtIso: input.createdAtIso,\n publicMessage: input.publicMessage,\n });\n }\n}\n\nfunction evaluateTemporalWindow(input: {\n validFromIso?: string | null;\n validUntilIso?: string | null;\n evaluatedAtIso: string;\n}): { expired: boolean; notYetValid: boolean } | null {\n const evaluatedMs = Date.parse(input.evaluatedAtIso);\n if (Number.isNaN(evaluatedMs)) return null;\n\n let notYetValid = false;\n\n if (input.validFromIso) {\n const fromMs = Date.parse(input.validFromIso);\n if (!Number.isNaN(fromMs) && evaluatedMs < fromMs) {\n notYetValid = true;\n }\n }\n\n if (input.validUntilIso) {\n const untilMs = Date.parse(input.validUntilIso);\n if (!Number.isNaN(untilMs) && evaluatedMs > untilMs) {\n return { expired: true, notYetValid: false };\n }\n }\n\n return { expired: false, notYetValid };\n}\n\nexport {\n evaluateTemporalWindow,\n ExpiredError,\n NotYetValidError,\n TemporalError,\n};\nexport type { TemporalErrorMetadata, TemporalKind, TemporalPrecision };\n","import { ValidationCode } from \"../exceptions/validation-code\";\nimport { ValidationField } from \"../exceptions/validation-field\";\n\nimport { AppError } from \"./app-error\";\nimport { ErrorCodes } from \"./error-codes\";\nimport type { AppErrorOptions } from \"./types\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// ValidationError\n// ─────────────────────────────────────────────────────────────────────────────\n\nclass ValidationError extends AppError {\n constructor(\n field: ValidationField,\n code: ValidationCode,\n message: string,\n options?: AppErrorOptions,\n ) {\n const baseMetadata = {\n field: field.value,\n validationCode: code.value,\n };\n const mergedMetadata = options?.metadata\n ? { ...baseMetadata, ...options.metadata }\n : baseMetadata;\n\n super(message, ErrorCodes.validation, {\n ...options,\n metadata: mergedMetadata,\n });\n }\n}\n\nexport { ValidationError };\n"],"mappings":";;AAuCA,IAAM,sBAAN,MAAM,4BAA4B,SAAS;CAGzC,YAAoB,QAajB;EACD,MAAM,OAAO,SAAS,OAAO,MAAM;GACjC,OAAO,OAAO;GACd,UAAU;IACR,QAAQ,OAAO;IACf,GAAI,OAAO,YAAY,CAAC;GAC1B;GACA,MAAM,OAAO;GACb,UAAU,OAAO;GACjB,eAAe,OAAO;GACtB,WAAW,OAAO;GAClB,WAAW,OAAO;GAClB,cAAc,OAAO;GACrB,eAAe,OAAO;EACxB,CAAC;EAED,KAAK,SAAS,OAAO;EACrB,OAAO,OAAO,IAAI;CACpB;CAMA,OAAO,aACL,SACqB;EACrB,OAAO,IAAI,oBAAoB;GAC7B,SAAS;GACT,MAAM,WAAW,eAAe;GAChC,QAAQ;GACR,UAAUA,kBAAgB,OAAO;GACjC,GAAGC,yBAAuB,OAAO;EACnC,CAAC;CACH;CAEA,OAAO,aACL,SACqB;EACrB,OAAO,IAAI,oBAAoB;GAC7B,SAAS;GACT,MAAM,WAAW,eAAe;GAChC,QAAQ;GACR,UAAUD,kBAAgB;IACxB,YAAY,SAAS;IACrB,eAAe,SAAS;IACxB,SAAS,SAAS;IAClB,eAAe,SAAS;IACxB,WAAW,SAAS;GACtB,CAAC;GACD,GAAGC,yBAAuB,OAAO;EACnC,CAAC;CACH;CAEA,OAAO,aACL,SACqB;EACrB,OAAO,IAAI,oBAAoB;GAC7B,SAAS;GACT,MAAM,WAAW,eAAe;GAChC,QAAQ;GACR,UAAUD,kBAAgB,OAAO;GACjC,GAAGC,yBAAuB,OAAO;EACnC,CAAC;CACH;CAEA,OAAO,mBACL,SASqB;EACrB,OAAO,IAAI,oBAAoB;GAC7B,SAAS;GACT,MAAM,WAAW,eAAe;GAChC,QAAQ;GACR,UAAUD,kBAAgB,OAAO;GACjC,GAAGC,yBAAuB,OAAO;EACnC,CAAC;CACH;AACF;AAMA,SAASA,yBAAuB,SAAoC;CAClE,OAAO;EACL,OAAO,SAAS;EAChB,MAAM,SAAS;EACf,UAAU,SAAS;EACnB,eAAe,SAAS;EACxB,WAAW,SAAS;EACpB,WAAW,SAAS;EACpB,cAAc,SAAS;EACvB,eAAe,SAAS;CAC1B;AACF;AAEA,SAASD,kBACP,OACe;CACf,IAAI,CAAC,OAAO,OAAO,KAAA;CAEnB,OAAO,OAAO,YACZ,OAAO,QAAQ,KAAK,EAAE,QAAQ,GAAG,WAAW,UAAU,KAAA,CAAS,CACjE;AACF;;;ACxGA,IAAM,qBAAN,MAAM,2BAA2B,SAAS;CAGxC,YAAoB,QAajB;EACD,MAAM,OAAO,SAAS,OAAO,MAAM;GACjC,OAAO,OAAO;GACd,UAAU,OAAO;GACjB,MAAM,OAAO;GACb,UAAU,OAAO;GACjB,eAAe,OAAO;GACtB,WAAW,OAAO;GAClB,WAAW,OAAO;GAClB,cAAc,OAAO;GACrB,eAAe,OAAO;EACxB,CAAC;EAED,KAAK,SAAS,OAAO;EACrB,OAAO,OAAO,IAAI;CACpB;CAMA,OAAO,UACL,OACoB;EACpB,OAAO,IAAI,mBAAmB;GAC5B,SAAS;GACT,MAAM,WAAW,cAAc;GAC/B,QAAQ;GACR,UAAU;IAAE,QAAQ;IAAa,GAAG,oBAAoB,KAAK;GAAE;GAC/D,GAAG,uBAAuB,KAAK;EACjC,CAAC;CACH;CAEA,OAAO,aACL,OAKoB;EACpB,OAAO,IAAI,mBAAmB;GAC5B,SAAS;GACT,MAAM,WAAW,cAAc;GAC/B,QAAQ;GACR,UAAU;IACR,QAAQ;IACR,GAAG,oBAAoB,KAAK;IAC5B,UAAU;GACZ;GACA,GAAG,uBAAuB,KAAK;EACjC,CAAC;CACH;CAEA,OAAO,kBACL,OAGoB;EACpB,OAAO,IAAI,mBAAmB;GAC5B,SAAS;GACT,MAAM,WAAW,cAAc;GAC/B,QAAQ;GACR,UAAU;IACR,QAAQ;IACR,GAAG,oBAAoB,KAAK;GAC9B;GACA,GAAG,uBAAuB,KAAK;EACjC,CAAC;CACH;CAEA,OAAO,WACL,OAGoB;EACpB,OAAO,IAAI,mBAAmB;GAC5B,SAAS;GACT,MAAM,WAAW,cAAc;GAC/B,QAAQ;GACR,UAAU;IAAE,QAAQ;IAAgB,GAAG,oBAAoB,KAAK;GAAE;GAClE,GAAG,uBAAuB,KAAK;EACjC,CAAC;CACH;AACF;AAMA,SAAS,uBAAuB,SAAoC;CAClE,OAAO;EACL,OAAO,SAAS;EAChB,MAAM,SAAS;EACf,UAAU,SAAS;EACnB,eAAe,SAAS;EACxB,WAAW,SAAS;EACpB,WAAW,SAAS;EACpB,cAAc,SAAS;EACvB,eAAe,SAAS;CAC1B;AACF;AAEA,SAAS,oBACP,OAC4C;CAC5C,MAAM,EACJ,OACA,MACA,UACA,eACA,WACA,WACA,cACA,eACA,GAAG,aACD;CACJ,OAAO;AACT;;;AC5LA,IAAM,6BAAN,cAAyC,SAAS;CAChD,YACE,MACA,SACA,QACA,SACA;EACA,MAAM,eAAe,SAAS;GAAE;GAAM;EAAO,IAAI,EAAE,KAAK;EACxD,MAAM,iBAAiB,SAAS,WAC5B;GAAE,GAAG;GAAc,GAAG,QAAQ;EAAS,IACvC;EAEJ,MAAM,SAAS,WAAW,uBAAuB;GAC/C,GAAG;GACH,UAAU;EACZ,CAAC;CACH;AACF;;;ACUA,IAAe,gBAAf,cAAqC,SAAS;CAG5C,YACE,QAOA;EACA,MAAM,OAAO,SAAS,OAAO,MAAM;GACjC,GAAG;GACH,UAAU;IACR,MAAM,OAAO;IACb,GAAG,OAAO;GACZ;EACF,CAAC;EAED,KAAK,OAAO,OAAO;EACnB,OAAO,OAAO,IAAI;CACpB;AACF;AAEA,IAAM,qBAAN,MAAM,2BAA2B,cAAc;CAC7C,YACE,OAIA;EACA,MAAM;GACJ,SAAS;GACT,MAAM,WAAW,SAAS;GAC1B,MAAM;GACN,GAAG;EACL,CAAC;CACH;CAEA,OAAO,SAAS,OAaO;EACrB,OAAO,IAAI,mBAAmB;GAC5B,UAAU;IACR,QAAQ,MAAM;IACd,WAAW,MAAM,aAAa;IAC9B,OAAO,MAAM;IACb,YAAY,MAAM;IAClB,WAAW,MAAM;IACjB,cAAc,MAAM;IACpB,eAAe,MAAM;IACrB,eAAe,MAAM;IACrB,WAAW,MAAM;IACjB,WAAW,MAAM;IACjB,MAAM,MAAM,QAAQ;GACtB;GACA,OAAO,MAAM;EACf,CAAC;CACH;AACF;AAEA,IAAM,iBAAN,MAAM,uBAAuB,cAAc;CACzC,YACE,OAIA;EACA,MAAM;GACJ,SAAS;GACT,MAAM,WAAW,SAAS;GAC1B,MAAM;GACN,GAAG;EACL,CAAC;CACH;CAEA,OAAO,SAAS,OAcG;EACjB,OAAO,IAAI,eAAe;GACxB,UAAU;IACR,QAAQ,MAAM;IACd,WAAW,MAAM;IACjB,OAAO,MAAM;IACb,gBAAgB,MAAM;IACtB,YAAY,MAAM;IAClB,WAAW,MAAM;IACjB,cAAc,MAAM;IACpB,eAAe,MAAM;IACrB,eAAe,MAAM;IACrB,WAAW,MAAM;IACjB,WAAW,MAAM;IACjB,MACE,MAAM,QACN;GACJ;GACA,OAAO,MAAM;EACf,CAAC;CACH;AACF;AAEA,IAAM,iCAAN,MAAM,uCAAuC,cAAc;CACzD,YACE,OAMA;EACA,MAAM;GACJ,SAAS;GACT,MAAM,WAAW,SAAS;GAC1B,MAAM;GACN,GAAG;EACL,CAAC;CACH;CAEA,OAAO,SAAS,OAWmB;EACjC,OAAO,IAAI,+BAA+B;GACxC,UAAU;IACR,QAAQ,MAAM;IACd,WAAW,MAAM;IACjB,gBAAgB,MAAM;IACtB,eAAe,MAAM;IACrB,eAAe,MAAM;IACrB,WAAW,MAAM;IACjB,WAAW,MAAM;IACjB,WAAW;KACT,MAAM;KACN,gBAAgB,MAAM;KACtB,OAAO,MAAM;KACb,SAAS,MAAM;IACjB;IACA,MAAM;GACR;GACA,OAAO,MAAM;EACf,CAAC;CACH;AACF;AAEA,SAAS,oCAAoC,OAY1B;CACjB,OAAO,eAAe,SAAS;EAC7B,QAAQ,MAAM;EACd,WAAW,MAAM;EACjB,OAAO,MAAM;EACb,gBAAgB,MAAM,UAAU;EAChC,eAAe,MAAM,KAAK;EAC1B,eAAe,MAAM,KAAK;EAC1B,WAAW,MAAM,KAAK;EACtB,WAAW,MAAM,KAAK;EACtB,OAAO,MAAM;CACf,CAAC;AACH;;;ACrMA,IAAe,mBAAf,cAAwC,SAAS;CAG/C,YAAsB,OAAyC;EAC7D,MAAM,MAAM,SAAS,MAAM,MAAM;GAC/B,OAAO,MAAM;GACb,UAAU;IAAE,MAAM,MAAM;IAAM,GAAG,MAAM;GAAS;EAClD,CAAC;EAED,KAAK,OAAO,MAAM;EAClB,OAAO,OAAO,IAAI;CACpB;CAEA,OAAO,WAAW,OAWa;EAC7B,OAAO,2BAA2B,SAAS,KAAK;CAClD;CAEA,OAAO,WAAW,OAea;EAC7B,OAAO,2BAA2B,SAAS,KAAK;CAClD;CAEA,OAAO,gBAAgB,OAiBa;EAClC,OAAO,gCAAgC,SAAS,KAAK;CACvD;CAEA,OAAO,mBAAmB,OAea;EACrC,OAAO,mCAAmC,SAAS,KAAK;CAC1D;AACF;AAEA,IAAM,6BAAN,MAAM,mCAAmC,iBAAiB;CACxD,YAAoB,OAGjB;EACD,MAAM;GACJ,SAAS;GACT,MAAM,WAAW,YAAY;GAC7B,MAAM;GACN,UAAU,MAAM;GAChB,OAAO,MAAM;EACf,CAAC;CACH;CAEA,OAAO,SAAS,OAWe;EAC7B,OAAO,IAAI,2BAA2B;GACpC,OAAO,MAAM;GACb,UAAU;IACR,WAAW,MAAM;IACjB,OAAO,MAAM;IACb,QAAQ,MAAM;IACd,eAAe,MAAM;IACrB,WAAW,MAAM;IACjB,WAAW,MAAM;IACjB,eAAe,MAAM;IACrB,MACE,MAAM,QACN;IACF,SAAS,MAAM;GACjB;EACF,CAAC;CACH;AACF;AAEA,IAAM,6BAAN,MAAM,mCAAmC,iBAAiB;CACxD,YAAoB,OAGjB;EACD,MAAM;GACJ,SAAS;GACT,MAAM,WAAW,YAAY;GAC7B,MAAM;GACN,UAAU,MAAM;GAChB,OAAO,MAAM;EACf,CAAC;CACH;CAEA,OAAO,SAAS,OAee;EAC7B,MAAM,WAAW,mBACf,MAAM,KACN,MAAM,SACN,MAAM,UACR;EAEA,OAAO,IAAI,2BAA2B;GACpC,OAAO,MAAM;GACb,UAAU;IACR,WAAW,MAAM;IACjB,OAAO,MAAM;IACb,QAAQ,MAAM;IACd,SAAS,SAAS;IAClB,YAAY,SAAS;IACrB,qBAAqB,MAAM;IAC3B,eAAe,MAAM;IACrB,WAAW,MAAM;IACjB,WAAW,MAAM;IACjB,eAAe,MAAM;IACrB,MAAM,MAAM,QAAQ;IACpB,SAAS,MAAM;GACjB;EACF,CAAC;CACH;AACF;AAEA,IAAM,kCAAN,MAAM,wCAAwC,iBAAiB;CAC7D,YAAoB,OAGjB;EACD,MAAM;GACJ,SAAS;GACT,MAAM,WAAW,YAAY;GAC7B,MAAM;GACN,UAAU,MAAM;GAChB,OAAO,MAAM;EACf,CAAC;CACH;CAEA,OAAO,SAAS,OAiBoB;EAClC,MAAM,WAAW,mBACf,MAAM,KACN,MAAM,SACN,MAAM,UACR;EAEA,OAAO,IAAI,gCAAgC;GACzC,OAAO,MAAM;GACb,UAAU;IACR,WAAW,MAAM;IACjB,OAAO,MAAM;IACb,QAAQ,MAAM;IACd,SAAS,SAAS;IAClB,YAAY,SAAS;IACrB,qBAAqB,MAAM;IAC3B,qBAAqB,MAAM;IAC3B,qBAAqB,MAAM;IAC3B,eAAe,MAAM;IACrB,WAAW,MAAM;IACjB,WAAW,MAAM;IACjB,eAAe,MAAM;IACrB,MACE,MAAM,QACN;IACF,SAAS,MAAM;GACjB;EACF,CAAC;CACH;AACF;AAEA,IAAM,qCAAN,MAAM,2CAA2C,iBAAiB;CAChE,YAAoB,OAGjB;EACD,MAAM;GACJ,SACE;GACF,MAAM,WAAW,YAAY;GAC7B,MAAM;GACN,UAAU,MAAM;GAChB,OAAO,MAAM;EACf,CAAC;CACH;CAEA,OAAO,SAAS,OAeuB;EACrC,MAAM,WAAW,mBACf,MAAM,KACN,MAAM,SACN,MAAM,UACR;EAEA,OAAO,IAAI,mCAAmC;GAC5C,OAAO,MAAM;GACb,UAAU;IACR,WAAW,MAAM;IACjB,OAAO,MAAM;IACb,QAAQ,MAAM;IACd,SAAS,SAAS;IAClB,YAAY,SAAS;IACrB,qBAAqB,MAAM;IAC3B,eAAe,MAAM;IACrB,WAAW,MAAM;IACjB,WAAW,MAAM;IACjB,eAAe,MAAM;IACrB,MACE,MAAM,QACN;IACF,SAAS,MAAM;GACjB;EACF,CAAC;CACH;AACF;AAEA,SAAS,mBACP,KACA,SACA,YAC2C;CAI3C,OAAO;EACI;EACT,YAJA,eAAe,MAAO,IAAI,UAAU,IAAI,MAAM,IAAI,MAAM,GAAG,CAAC,IAAK,KAAA;CAKnE;AACF;;;AC5TA,IAAM,mBAAN,MAAM,yBAAyB,SAAS;CAGtC,YAAoB,QAA2C;EAC7D,MAAM,EAAE,SAAS,MAAM,UAAU,GAAG,SAAS;EAC7C,MAAM,SAAS,MAAM;GACnB,GAAG;GACH;EACF,CAAC;EAED,KAAK,SAAS,OAAO;EACrB,OAAO,OAAO,IAAI;CACpB;CAEA,OAAO,QAAQ,SAA2D;EACxE,OAAO,IAAI,iBAAiB;GAC1B,SAAS;GACT,MAAM,WAAW,YAAY;GAC7B,QAAQ;GACR,UAAU,cAAc;IAAE,QAAQ;IAAW,GAAG;GAAQ,CAAC;GACzD,GAAG,mBAAmB,OAAO;EAC/B,CAAC;CACH;CAEA,OAAO,YACL,SACkB;EAClB,OAAO,IAAI,iBAAiB;GAC1B,SAAS;GACT,MAAM,WAAW,YAAY;GAC7B,QAAQ;GACR,UAAU,cAAc;IAAE,QAAQ;IAAe,GAAG;GAAQ,CAAC;GAC7D,GAAG,mBAAmB,OAAO;EAC/B,CAAC;CACH;CAEA,OAAO,YACL,SACkB;EAClB,OAAO,IAAI,iBAAiB;GAC1B,SAAS;GACT,MAAM,WAAW,YAAY;GAC7B,QAAQ;GACR,UAAU,cAAc;IAAE,QAAQ;IAAgB,GAAG;GAAQ,CAAC;GAC9D,GAAG,mBAAmB,OAAO;EAC/B,CAAC;CACH;AACF;AAwBA,SAAS,cACP,OAC0B;CAC1B,OAAO,gBAAgB;EACrB,QAAQ,MAAM;EACd,UAAU,MAAM;EAChB,WAAW,MAAM;EACjB,cAAc,MAAM;EACpB,YAAY,MAAM;EAClB,eAAe,MAAM;EACrB,eAAe,MAAM;EACrB,WAAW,MAAM;EACjB,WAAW,MAAM;EACjB,SAAS,MAAM;EACf,YAAY,MAAM;EAClB,cAAc,MAAM;CACtB,CAAC;AACH;AAEA,SAAS,mBACP,SAC4B;CAC5B,OAAO;EACL,OAAO,QAAQ;EACf,MAAM,QAAQ;EACd,UAAU,QAAQ;EAClB,eAAe,QAAQ;EACvB,WAAW,QAAQ;EACnB,WAAW,QAAQ;EACnB,cAAc,QAAQ;EACtB,eAAe,QAAQ;CACzB;AACF;AAEA,SAAS,gBAAmD,OAAa;CACvE,OAAO,OAAO,YACZ,OAAO,QAAQ,KAAK,EAAE,QAAQ,GAAG,WAAW,UAAU,KAAA,CAAS,CACjE;AACF;;;ACjJA,IAAM,0BAAN,cAAsC,SAAS;CAC7C,YACE,SACA,SACA,SACA;EACA,MAAM,iBAAiB,SAAS,WAC5B;GAAE,GAAI,WAAW,CAAC;GAAI,GAAG,QAAQ;EAAS,IAC1C;EAEJ,MAAM,SAAS,WAAW,oBAAoB;GAC5C,GAAG;GACH,UAAU;EACZ,CAAC;CACH;AACF;;;ACfA,IAAM,gBAAN,cAA4B,SAAS;CACnC,YACE,UACA,UACA,SACA;EACA,MAAM,eAAe,WAAW;GAAE;GAAU;EAAS,IAAI,EAAE,SAAS;EACpE,MAAM,iBAAiB,SAAS,WAC5B;GAAE,GAAG;GAAc,GAAG,QAAQ;EAAS,IACvC;EAEJ,MAAM,GAAG,SAAS,aAAa,WAAW,UAAU;GAClD,GAAG;GACH,UAAU;EACZ,CAAC;CACH;AACF;;;AC4CA,IAAe,qBAAf,cAA0C,SAAS;CAMjD,YAAsB,OAAkC;EACtD,MAAM,MAAM,WAAW,yBAAyB,MAAM,MAAM;GAC1D,OAAO,MAAM;GACb,UAAU;IACR,WAAW,MAAM;IACjB,UAAU,MAAM;IAChB,GAAG,MAAM;GACX;GACA,MAAM,MAAM;GACZ,UAAU,MAAM;GAChB,eAAe,MAAM;GACrB,WAAW,MAAM;GACjB,WAAW,MAAM;GACjB,cAAc,MAAM;GACpB,eAAe,MAAM;EACvB,CAAC;EAED,KAAK,YAAY,MAAM;EACvB,KAAK,WAAW,MAAM;EACtB,KAAK,WAAW,MAAM,SAAS;EAC/B,KAAK,WAAW,MAAM,SAAS;EAE/B,OAAO,OAAO,IAAI;CACpB;AACF;AAMA,IAAM,uBAAN,MAAM,6BAA6B,mBAAmB;CACpD,YAAoB,OAAkC;EACpD,MAAM,KAAK;CACb;CAEA,OAAO,QACL,UACA,UAGsB;EACtB,OAAO,IAAI,qBAAqB;GAC9B,SAAS,sBAAsB,QAAQ,eAAe,QAAQ;GAC9D,MAAM,mBAAmB,KAAK,eAAe,QAAQ;GACrD;GACA,WAAW;GACX;GACA,OAAO,SAAS;EAClB,CAAC;CACH;AACF;AAMA,IAAM,wBAAN,MAAM,8BAA8B,mBAAmB;CACrD,YAAoB,OAAkC;EACpD,MAAM,KAAK;CACb;CAEA,OAAO,QACL,UACA,UAGuB;EACvB,OAAO,IAAI,sBAAsB;GAC/B,SAAS,sBAAsB,QAAQ,aAAa,QAAQ;GAC5D,MAAM,mBAAmB,KAAK,aAAa,QAAQ;GACnD;GACA,WAAW;GACX;GACA,OAAO,SAAS;EAClB,CAAC;CACH;AACF;AAMA,IAAM,qBAAN,MAAyB;CACvB,OAAO,KACL,WACA,UACQ;EACR,OAAO,uBAAuB,WAAW,QAAQ;CACnD;AACF;AAEA,IAAM,wBAAN,MAA4B;CAC1B,OAAO,QACL,WACA,UACQ;EACR,IAAI,cAAc,eAAe;GAC/B,IAAI,aAAa,gBACf,OAAO;GAET,IAAI,aAAa,0BACf,OAAO;GAET,IAAI,aAAa,8BACf,OAAO;GAET,IAAI,aAAa,iBACf,OAAO;GAET,IAAI,aAAa,2BACf,OAAO;GAET,IAAI,aAAa,qBACf,OAAO;GAET,OAAO;EACT;EAEA,IAAI,aAAa,oBACf,OAAO;EAET,IAAI,aAAa,qBACf,OAAO;EAET,OAAO;CACT;AACF;AAMA,SAAS,YAAY,OAAgB,QAAQ,KAAyB;CACpE,IAAI;EACF,MAAM,aACJ,OAAO,UAAU,WAAW,QAAQ,KAAK,UAAU,KAAK;EAC1D,OAAO,WAAW,SAAS,QACvB,GAAG,WAAW,MAAM,GAAG,KAAK,EAAE,KAC9B;CACN,QAAQ;EACN;CACF;AACF;;;ACzLA,IAAe,gBAAf,cAAqC,SAAS;CAK5C,YAAsB,OAA6B;EACjD,MAAM,MAAM,SAAS,MAAM,MAAM;GAC/B,GAAG;GACH,UAAU;IACR,MAAM,MAAM;IACZ,GAAG,MAAM;GACX;EACF,CAAC;EAED,KAAK,OAAO,MAAM;EAClB,KAAK,eAAe,MAAM,SAAS;EACnC,KAAK,iBAAiB,MAAM,SAAS;EACrC,OAAO,OAAO,IAAI;CACpB;AACF;AAOA,IAAM,eAAN,MAAM,qBAAqB,cAAc;CACvC,YAAoB,OAA6B;EAC/C,MAAM,KAAK;CACb;CAEA,OAAO,SAAS,OAA4C;EAC1D,OAAO,IAAI,aAAa;GACtB,SAAS;GACT,MAAM,WAAW,SAAS;GAC1B,MAAM;GACN,UAAU;IACR,GAAG;IACH,MACE,MAAM,QACN;GACJ;GACA,OAAO,MAAM;GACb,MAAM,MAAM;GACZ,UAAU,MAAM;GAChB,eAAe,MAAM;GACrB,WAAW,MAAM;GACjB,WAAW,MAAM;GACjB,cAAc,MAAM;GACpB,eAAe,MAAM;EACvB,CAAC;CACH;AACF;AAEA,IAAM,mBAAN,MAAM,yBAAyB,cAAc;CAC3C,YAAoB,OAA6B;EAC/C,MAAM,KAAK;CACb;CAEA,OAAO,SAAS,OAAgD;EAC9D,OAAO,IAAI,iBAAiB;GAC1B,SAAS;GACT,MAAM,WAAW,SAAS;GAC1B,MAAM;GACN,UAAU;IACR,GAAG;IACH,MAAM,MAAM,QAAQ;GACtB;GACA,OAAO,MAAM;GACb,MAAM,MAAM;GACZ,UAAU,MAAM;GAChB,eAAe,MAAM;GACrB,WAAW,MAAM;GACjB,WAAW,MAAM;GACjB,cAAc,MAAM;GACpB,eAAe,MAAM;EACvB,CAAC;CACH;AACF;AAEA,SAAS,uBAAuB,OAIsB;CACpD,MAAM,cAAc,KAAK,MAAM,MAAM,cAAc;CACnD,IAAI,OAAO,MAAM,WAAW,GAAG,OAAO;CAEtC,IAAI,cAAc;CAElB,IAAI,MAAM,cAAc;EACtB,MAAM,SAAS,KAAK,MAAM,MAAM,YAAY;EAC5C,IAAI,CAAC,OAAO,MAAM,MAAM,KAAK,cAAc,QACzC,cAAc;CAElB;CAEA,IAAI,MAAM,eAAe;EACvB,MAAM,UAAU,KAAK,MAAM,MAAM,aAAa;EAC9C,IAAI,CAAC,OAAO,MAAM,OAAO,KAAK,cAAc,SAC1C,OAAO;GAAE,SAAS;GAAM,aAAa;EAAM;CAE/C;CAEA,OAAO;EAAE,SAAS;EAAO;CAAY;AACvC;;;AC7HA,IAAM,kBAAN,cAA8B,SAAS;CACrC,YACE,OACA,MACA,SACA,SACA;EACA,MAAM,eAAe;GACnB,OAAO,MAAM;GACb,gBAAgB,KAAK;EACvB;EACA,MAAM,iBAAiB,SAAS,WAC5B;GAAE,GAAG;GAAc,GAAG,QAAQ;EAAS,IACvC;EAEJ,MAAM,SAAS,WAAW,YAAY;GACpC,GAAG;GACH,UAAU;EACZ,CAAC;CACH;AACF"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
//#region src/core/exceptions/validation-field.d.ts
|
|
2
|
+
declare class ValidationField {
|
|
3
|
+
readonly value: string;
|
|
4
|
+
private constructor();
|
|
5
|
+
static of(value: string): ValidationField;
|
|
6
|
+
nested(child: string | ValidationField): ValidationField;
|
|
7
|
+
toString(): string;
|
|
8
|
+
equals(other: ValidationField): boolean;
|
|
9
|
+
}
|
|
10
|
+
//#endregion
|
|
11
|
+
export { ValidationField as t };
|
|
12
|
+
//# sourceMappingURL=validation-field.d.ts.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
//#region src/core/exceptions/validation-field.ts
|
|
2
|
+
var ValidationField = class ValidationField {
|
|
3
|
+
constructor(value) {
|
|
4
|
+
this.value = value;
|
|
5
|
+
}
|
|
6
|
+
static of(value) {
|
|
7
|
+
return new ValidationField(value);
|
|
8
|
+
}
|
|
9
|
+
nested(child) {
|
|
10
|
+
const childValue = child instanceof ValidationField ? child.value : child;
|
|
11
|
+
return new ValidationField(`${this.value}.${childValue}`);
|
|
12
|
+
}
|
|
13
|
+
toString() {
|
|
14
|
+
return this.value;
|
|
15
|
+
}
|
|
16
|
+
equals(other) {
|
|
17
|
+
return this.value === other.value;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
//#endregion
|
|
21
|
+
export { ValidationField as t };
|
|
22
|
+
|
|
23
|
+
//# sourceMappingURL=validation-field.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation-field.js","names":[],"sources":["../src/core/exceptions/validation-field.ts"],"sourcesContent":["// Object-oriented representation of validation fields.\n// Indicates WHERE a validation error occurred.\n// This is a minimal base class — field catalogs are defined per domain/module.\n\nclass ValidationField {\n public readonly value: string;\n\n private constructor(value: string) {\n this.value = value;\n }\n\n // Factory for custom or domain-specific fields\n public static of(value: string): ValidationField {\n return new ValidationField(value);\n }\n\n // Creates a nested field path: parent.nested('child') -> 'parent.child'\n public nested(child: string | ValidationField): ValidationField {\n const childValue = child instanceof ValidationField ? child.value : child;\n return new ValidationField(`${this.value}.${childValue}`);\n }\n\n public toString(): string {\n return this.value;\n }\n\n public equals(other: ValidationField): boolean {\n return this.value === other.value;\n }\n}\n\nexport { ValidationField };\n"],"mappings":";AAIA,IAAM,kBAAN,MAAM,gBAAgB;CAGpB,YAAoB,OAAe;EACjC,KAAK,QAAQ;CACf;CAGA,OAAc,GAAG,OAAgC;EAC/C,OAAO,IAAI,gBAAgB,KAAK;CAClC;CAGA,OAAc,OAAkD;EAC9D,MAAM,aAAa,iBAAiB,kBAAkB,MAAM,QAAQ;EACpE,OAAO,IAAI,gBAAgB,GAAG,KAAK,MAAM,GAAG,YAAY;CAC1D;CAEA,WAA0B;EACxB,OAAO,KAAK;CACd;CAEA,OAAc,OAAiC;EAC7C,OAAO,KAAK,UAAU,MAAM;CAC9B;AACF"}
|
package/meta.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": "2",
|
|
3
|
+
"name": "erp-core",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"description": "Core ERP com clean architecture, policies, erros e services de aplicação.",
|
|
6
|
+
"compatibility": {
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=18",
|
|
9
|
+
"typescript": ">=5.0.0"
|
|
10
|
+
},
|
|
11
|
+
"directImport": {
|
|
12
|
+
"peerDependencies": [
|
|
13
|
+
{
|
|
14
|
+
"name": "zod",
|
|
15
|
+
"range": ">=3.22.0 <5"
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
},
|
|
19
|
+
"fullControl": {
|
|
20
|
+
"dependencies": [
|
|
21
|
+
{
|
|
22
|
+
"name": "zod",
|
|
23
|
+
"range": ">=3.22.0 <5"
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"entryPoint": "index.ts",
|
|
29
|
+
"philosophy": {
|
|
30
|
+
"errorModel": "mixed",
|
|
31
|
+
"observability": ["log-port", "metric-port", "trace-port"],
|
|
32
|
+
"testStyle": "vitest-no-mocks-in-core",
|
|
33
|
+
"externalDeps": ["zod"],
|
|
34
|
+
"testDeps": ["vitest"]
|
|
35
|
+
},
|
|
36
|
+
"docs": {
|
|
37
|
+
"context": "KIT_CONTEXT.md",
|
|
38
|
+
"readme": "README.md"
|
|
39
|
+
},
|
|
40
|
+
"lint": {
|
|
41
|
+
"nodenextImports": "off",
|
|
42
|
+
"noBareAny": "warn",
|
|
43
|
+
"noExternalImports": "error",
|
|
44
|
+
"noUpwardImports": "error"
|
|
45
|
+
},
|
|
46
|
+
"exports": [
|
|
47
|
+
"Entity",
|
|
48
|
+
"ValueObject",
|
|
49
|
+
"AppError",
|
|
50
|
+
"ValidationError",
|
|
51
|
+
"NotFoundError",
|
|
52
|
+
"ConflictError",
|
|
53
|
+
"AuthorizationError",
|
|
54
|
+
"IntegrationError",
|
|
55
|
+
"PolicyCatalog",
|
|
56
|
+
"PolicyDefinition",
|
|
57
|
+
"PolicyDecision",
|
|
58
|
+
"PolicyResolver",
|
|
59
|
+
"PolicyService",
|
|
60
|
+
"mapPolicyEvaluationError"
|
|
61
|
+
],
|
|
62
|
+
"changelog": [
|
|
63
|
+
"1.0.0 - Registro inicial do boilerplate com primitives tipadas para import direto, CLI e fluxo full-control."
|
|
64
|
+
],
|
|
65
|
+
"deprecated": false
|
|
66
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cullet/erp-core",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Core ERP with clean architecture, temporality, policies, and rule sets",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./errors": {
|
|
14
|
+
"types": "./dist/errors/index.d.ts",
|
|
15
|
+
"import": "./dist/errors/index.js"
|
|
16
|
+
},
|
|
17
|
+
"./policies": {
|
|
18
|
+
"types": "./dist/policies/index.d.ts",
|
|
19
|
+
"import": "./dist/policies/index.js"
|
|
20
|
+
},
|
|
21
|
+
"./policies/engines": {
|
|
22
|
+
"types": "./dist/policies/engines/index.d.ts",
|
|
23
|
+
"import": "./dist/policies/engines/index.js"
|
|
24
|
+
},
|
|
25
|
+
"./policies/engines/v1/gate": {
|
|
26
|
+
"types": "./dist/policies/engines/v1/gate/index.d.ts",
|
|
27
|
+
"import": "./dist/policies/engines/v1/gate/index.js"
|
|
28
|
+
},
|
|
29
|
+
"./exceptions/validation-field": {
|
|
30
|
+
"types": "./dist/exceptions/validation-field.d.ts",
|
|
31
|
+
"import": "./dist/exceptions/validation-field.js"
|
|
32
|
+
},
|
|
33
|
+
"./package.json": "./package.json"
|
|
34
|
+
},
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build": "tsdown --config tsdown.config.ts",
|
|
37
|
+
"dev": "tsdown --config tsdown.config.ts --watch"
|
|
38
|
+
},
|
|
39
|
+
"files": [
|
|
40
|
+
"dist",
|
|
41
|
+
"src",
|
|
42
|
+
"!src/**/*.spec.ts",
|
|
43
|
+
"!src/**/*.test.ts",
|
|
44
|
+
"meta.json",
|
|
45
|
+
"KIT_CONTEXT.md",
|
|
46
|
+
"README.md"
|
|
47
|
+
],
|
|
48
|
+
"sideEffects": false,
|
|
49
|
+
"engines": {
|
|
50
|
+
"node": ">=18.17"
|
|
51
|
+
},
|
|
52
|
+
"peerDependencies": {
|
|
53
|
+
"zod": ">=3.22.0 <5"
|
|
54
|
+
},
|
|
55
|
+
"publishConfig": {
|
|
56
|
+
"access": "public"
|
|
57
|
+
},
|
|
58
|
+
"license": "MIT"
|
|
59
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { UseCase } from "../use-case";
|
|
2
|
+
import { version } from "../../versioning/version";
|
|
3
|
+
import type { Result } from "../../result/result";
|
|
4
|
+
|
|
5
|
+
import { RequestedBy } from "./requested-by";
|
|
6
|
+
|
|
7
|
+
interface CommandInput {
|
|
8
|
+
readonly requestedBy: RequestedBy;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Base for use cases that **mutate state** (writes), following CQS.
|
|
13
|
+
*
|
|
14
|
+
* - `Input extends CommandInput` ensures every mutation records who triggered it.
|
|
15
|
+
* - `Output extends Result<unknown, unknown>` ensures business errors are
|
|
16
|
+
* explicit values, never thrown exceptions.
|
|
17
|
+
*
|
|
18
|
+
* The Command/Query distinction is semantic: the type declares the intent
|
|
19
|
+
* before any implementation exists, guiding code review and API contracts.
|
|
20
|
+
*/
|
|
21
|
+
@version("1.0")
|
|
22
|
+
abstract class Command<
|
|
23
|
+
Input extends CommandInput,
|
|
24
|
+
Output extends Result<unknown, unknown> = Result<void, never>,
|
|
25
|
+
> extends UseCase<Input, Output> {}
|
|
26
|
+
|
|
27
|
+
export type { CommandInput };
|
|
28
|
+
export { Command };
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { ValidationCode } from "../../exceptions/validation-code";
|
|
2
|
+
import { InvalidValueException } from "../../exceptions/validation-exception";
|
|
3
|
+
import { ValidationField } from "../../exceptions/validation-field";
|
|
4
|
+
|
|
5
|
+
// Identifies who triggered a Command.
|
|
6
|
+
// Two valid formats:
|
|
7
|
+
// - Human user: UUID v4 (e.g.: "550e8400-e29b-41d4-a716-446655440000")
|
|
8
|
+
// - System identity: "system:<job>" where <job> is [a-z][a-z0-9]*(-[a-z0-9]+)*
|
|
9
|
+
// (e.g.: "system:late-fee-job", "system:email-sender")
|
|
10
|
+
//
|
|
11
|
+
// The split between `fromUser` / `fromSystem` / `parse` exists to make intent
|
|
12
|
+
// explicit at the call-site: whoever builds the value knows where it came from.
|
|
13
|
+
|
|
14
|
+
type RequestedByKind = "user" | "system";
|
|
15
|
+
|
|
16
|
+
const REQUESTED_BY_FIELD = ValidationField.of("requestedBy");
|
|
17
|
+
|
|
18
|
+
const UUID_PATTERN =
|
|
19
|
+
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
20
|
+
|
|
21
|
+
// system:<job> where <job> starts with a lowercase letter and may have hyphens between segments
|
|
22
|
+
const SYSTEM_IDENTITY_PATTERN = /^system:[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
|
|
23
|
+
|
|
24
|
+
class RequestedBy {
|
|
25
|
+
readonly kind: RequestedByKind;
|
|
26
|
+
readonly raw: string;
|
|
27
|
+
|
|
28
|
+
private constructor(kind: RequestedByKind, raw: string) {
|
|
29
|
+
this.kind = kind;
|
|
30
|
+
this.raw = raw;
|
|
31
|
+
Object.freeze(this);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Builds from a human user ID (must be a UUID). */
|
|
35
|
+
static fromUser(userId: string): RequestedBy {
|
|
36
|
+
if (!UUID_PATTERN.test(userId)) {
|
|
37
|
+
throw new InvalidValueException(
|
|
38
|
+
REQUESTED_BY_FIELD,
|
|
39
|
+
ValidationCode.INVALID_FORMAT,
|
|
40
|
+
`requestedBy: user identity must be a UUID, got "${userId}"`,
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return new RequestedBy("user", userId);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Builds from a system identity.
|
|
49
|
+
* Only accepts the format "system:<job>", where <job> matches [a-z][a-z0-9]*(-[a-z0-9]+)*.
|
|
50
|
+
*/
|
|
51
|
+
static fromSystem(systemIdentity: string): RequestedBy {
|
|
52
|
+
if (!SYSTEM_IDENTITY_PATTERN.test(systemIdentity)) {
|
|
53
|
+
throw new InvalidValueException(
|
|
54
|
+
REQUESTED_BY_FIELD,
|
|
55
|
+
ValidationCode.INVALID_FORMAT,
|
|
56
|
+
`requestedBy: system identity must match "system:<job>" (e.g. "system:late-fee-job"), got "${systemIdentity}"`,
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return new RequestedBy("system", systemIdentity);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Infers the kind from the raw value.
|
|
65
|
+
* Use when the origin (user vs system) is not known at the call-site.
|
|
66
|
+
*/
|
|
67
|
+
static parse(raw: string): RequestedBy {
|
|
68
|
+
if (UUID_PATTERN.test(raw)) {
|
|
69
|
+
return new RequestedBy("user", raw);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (SYSTEM_IDENTITY_PATTERN.test(raw)) {
|
|
73
|
+
return new RequestedBy("system", raw);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
throw new InvalidValueException(
|
|
77
|
+
REQUESTED_BY_FIELD,
|
|
78
|
+
ValidationCode.INVALID_FORMAT,
|
|
79
|
+
`requestedBy must be a UUID (user) or "system:<job>" (system), got "${raw}"`,
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
get isUser(): boolean {
|
|
84
|
+
return this.kind === "user";
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
get isSystem(): boolean {
|
|
88
|
+
return this.kind === "system";
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
toString(): string {
|
|
92
|
+
return this.raw;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export type { RequestedByKind };
|
|
97
|
+
export { RequestedBy };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export type { CommandInput } from "./commands";
|
|
2
|
+
export { Command } from "./commands";
|
|
3
|
+
export type {
|
|
4
|
+
LogPayload,
|
|
5
|
+
LoggerPort,
|
|
6
|
+
MetricLabels,
|
|
7
|
+
MetricsPort,
|
|
8
|
+
PolicyEvaluationError,
|
|
9
|
+
PolicyEvaluationInput,
|
|
10
|
+
PolicyEvaluationOutput,
|
|
11
|
+
PolicyPort,
|
|
12
|
+
Repository,
|
|
13
|
+
TraceAttributeValue,
|
|
14
|
+
TraceSpan,
|
|
15
|
+
TracerPort,
|
|
16
|
+
TemporalHistory,
|
|
17
|
+
TemporalRepository,
|
|
18
|
+
} from "./ports";
|
|
19
|
+
export { mapPolicyEvaluationError } from "./policy-error-mapper";
|
|
20
|
+
export type { CacheStrategy, Page } from "./queries";
|
|
21
|
+
export { Query } from "./queries";
|
|
22
|
+
export {
|
|
23
|
+
assertTemporalContext,
|
|
24
|
+
createTemporalContext,
|
|
25
|
+
type CreateTemporalContextInput,
|
|
26
|
+
type TemporalContext,
|
|
27
|
+
type TemporalizedContextSeed,
|
|
28
|
+
TemporalUseCase,
|
|
29
|
+
type TemporalUseCaseInput,
|
|
30
|
+
} from "./temporal";
|
|
31
|
+
export { type MaybePromise, UseCase } from "./use-case";
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type AppError,
|
|
3
|
+
BusinessRuleViolationError,
|
|
4
|
+
NotFoundError,
|
|
5
|
+
UnexpectedError,
|
|
6
|
+
} from "../errors";
|
|
7
|
+
import type { PolicyEvaluationError } from "../policies";
|
|
8
|
+
|
|
9
|
+
export function mapPolicyEvaluationError(err: PolicyEvaluationError): AppError {
|
|
10
|
+
switch (err.kind) {
|
|
11
|
+
case "INVALID_CONTEXT":
|
|
12
|
+
return new BusinessRuleViolationError(
|
|
13
|
+
`policy.${err.stage.toLowerCase()}`,
|
|
14
|
+
err.message,
|
|
15
|
+
{ policyKey: err.policyKey, stage: err.stage },
|
|
16
|
+
{ cause: err.cause },
|
|
17
|
+
);
|
|
18
|
+
case "INVALID_POLICY_KEY":
|
|
19
|
+
return new BusinessRuleViolationError(
|
|
20
|
+
"policy.invalid_key",
|
|
21
|
+
err.message,
|
|
22
|
+
{ rawPolicyKey: err.rawPolicyKey },
|
|
23
|
+
{ cause: err.cause },
|
|
24
|
+
);
|
|
25
|
+
case "POLICY_NOT_FOUND":
|
|
26
|
+
return new NotFoundError(
|
|
27
|
+
"Policy",
|
|
28
|
+
{ policyKey: err.policyKey },
|
|
29
|
+
{ cause: err.cause },
|
|
30
|
+
);
|
|
31
|
+
case "POLICY_DEFINITION_NOT_FOUND":
|
|
32
|
+
return new NotFoundError(
|
|
33
|
+
"PolicyDefinition",
|
|
34
|
+
{
|
|
35
|
+
policyKey: err.policyKey,
|
|
36
|
+
contextVersion: err.contextVersion,
|
|
37
|
+
asOf: err.asOf.toISOString(),
|
|
38
|
+
},
|
|
39
|
+
{ cause: err.cause },
|
|
40
|
+
);
|
|
41
|
+
case "POLICY_VARIANT_NOT_FOUND":
|
|
42
|
+
return new NotFoundError(
|
|
43
|
+
"PolicyVariant",
|
|
44
|
+
{
|
|
45
|
+
policyKey: err.policyKey,
|
|
46
|
+
policyKind: err.policyKind,
|
|
47
|
+
payloadSchemaVersion: err.payloadSchemaVersion,
|
|
48
|
+
},
|
|
49
|
+
{ cause: err.cause },
|
|
50
|
+
);
|
|
51
|
+
case "ENGINE_FAILURE":
|
|
52
|
+
return new UnexpectedError(err.message, err.cause, {
|
|
53
|
+
metadata: {
|
|
54
|
+
policyKey: err.policyKey,
|
|
55
|
+
engine: err.engine,
|
|
56
|
+
engineVersion: err.engineVersion,
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type { LogPayload, LoggerPort } from "./logger.port";
|
|
2
|
+
export type { MetricLabels, MetricsPort } from "./metrics.port";
|
|
3
|
+
export type {
|
|
4
|
+
PolicyEvaluationError,
|
|
5
|
+
PolicyEvaluationInput,
|
|
6
|
+
PolicyEvaluationOutput,
|
|
7
|
+
PolicyPort,
|
|
8
|
+
} from "./policy-port";
|
|
9
|
+
export type { Repository } from "./repository.port";
|
|
10
|
+
export type {
|
|
11
|
+
TemporalHistory,
|
|
12
|
+
TemporalRepository,
|
|
13
|
+
} from "./temporal-repository.port";
|
|
14
|
+
export type { TraceAttributeValue, TraceSpan, TracerPort } from "./tracer.port";
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
type LogPayload = Record<string, unknown>;
|
|
2
|
+
|
|
3
|
+
interface LoggerPort {
|
|
4
|
+
debug(message: string, payload?: LogPayload): void;
|
|
5
|
+
info(message: string, payload?: LogPayload): void;
|
|
6
|
+
warn(message: string, payload?: LogPayload): void;
|
|
7
|
+
error(message: string, payload?: LogPayload): void;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export type { LogPayload, LoggerPort };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
type MetricLabels = Readonly<Record<string, string | number | boolean>>;
|
|
2
|
+
|
|
3
|
+
interface MetricsPort {
|
|
4
|
+
counter(name: string, value: number, labels?: MetricLabels): void;
|
|
5
|
+
gauge(name: string, value: number, labels?: MetricLabels): void;
|
|
6
|
+
histogram(name: string, value: number, labels?: MetricLabels): void;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export type { MetricLabels, MetricsPort };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { AppError } from "../../errors";
|
|
2
|
+
import type { EvaluateInput, PolicyEvaluationResult } from "../../policies";
|
|
3
|
+
import type { Result } from "../../result/result";
|
|
4
|
+
|
|
5
|
+
type PolicyEvaluationInput = EvaluateInput;
|
|
6
|
+
type PolicyEvaluationOutput = PolicyEvaluationResult;
|
|
7
|
+
type PolicyEvaluationError = AppError;
|
|
8
|
+
|
|
9
|
+
interface PolicyPort {
|
|
10
|
+
evaluate(
|
|
11
|
+
input: PolicyEvaluationInput,
|
|
12
|
+
): Promise<Result<PolicyEvaluationOutput, PolicyEvaluationError>>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type {
|
|
16
|
+
PolicyEvaluationError,
|
|
17
|
+
PolicyEvaluationInput,
|
|
18
|
+
PolicyEvaluationOutput,
|
|
19
|
+
PolicyPort,
|
|
20
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { TemporalSnapshot } from "../../domain/temporal";
|
|
2
|
+
|
|
3
|
+
import type { Repository } from "./repository.port";
|
|
4
|
+
|
|
5
|
+
type TemporalHistory<TEntity> = readonly TemporalSnapshot<TEntity>[];
|
|
6
|
+
|
|
7
|
+
interface TemporalRepository<TEntity, TId> extends Repository<TEntity, TId> {
|
|
8
|
+
findAsOf(id: TId, asOf: Date): Promise<TEntity | null>;
|
|
9
|
+
findAtTransaction(id: TId, txTime: Date): Promise<TEntity | null>;
|
|
10
|
+
findHistory(id: TId): Promise<TemporalHistory<TEntity>>;
|
|
11
|
+
save(entity: TEntity, validFrom?: Date): Promise<void>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type { TemporalHistory, TemporalRepository };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
type TraceAttributeValue = string | number | boolean;
|
|
2
|
+
|
|
3
|
+
interface TraceSpan {
|
|
4
|
+
setAttribute(key: string, value: TraceAttributeValue): void;
|
|
5
|
+
recordException(error: unknown): void;
|
|
6
|
+
end(): void;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface TracerPort {
|
|
10
|
+
startSpan(
|
|
11
|
+
name: string,
|
|
12
|
+
attributes?: Record<string, TraceAttributeValue>,
|
|
13
|
+
): TraceSpan;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export type { TraceAttributeValue, TraceSpan, TracerPort };
|