@beignet/core 0.0.1
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/CHANGELOG.md +5 -0
- package/README.md +288 -0
- package/dist/application/index.d.ts +260 -0
- package/dist/application/index.d.ts.map +1 -0
- package/dist/application/index.js +324 -0
- package/dist/application/index.js.map +1 -0
- package/dist/client/client.d.ts +241 -0
- package/dist/client/client.d.ts.map +1 -0
- package/dist/client/client.js +531 -0
- package/dist/client/client.js.map +1 -0
- package/dist/client/index.d.ts +10 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +8 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/types.d.ts +139 -0
- package/dist/client/types.d.ts.map +1 -0
- package/dist/client/types.js +2 -0
- package/dist/client/types.js.map +1 -0
- package/dist/config/index.d.ts +122 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +216 -0
- package/dist/config/index.js.map +1 -0
- package/dist/contracts/contract-builder.d.ts +121 -0
- package/dist/contracts/contract-builder.d.ts.map +1 -0
- package/dist/contracts/contract-builder.js +346 -0
- package/dist/contracts/contract-builder.js.map +1 -0
- package/dist/contracts/contract-group.d.ts +106 -0
- package/dist/contracts/contract-group.d.ts.map +1 -0
- package/dist/contracts/contract-group.js +240 -0
- package/dist/contracts/contract-group.js.map +1 -0
- package/dist/contracts/contract-like.d.ts +21 -0
- package/dist/contracts/contract-like.d.ts.map +1 -0
- package/dist/contracts/contract-like.js +9 -0
- package/dist/contracts/contract-like.js.map +1 -0
- package/dist/contracts/index.d.ts +15 -0
- package/dist/contracts/index.d.ts.map +1 -0
- package/dist/contracts/index.js +11 -0
- package/dist/contracts/index.js.map +1 -0
- package/dist/contracts/openapi-meta.d.ts +23 -0
- package/dist/contracts/openapi-meta.d.ts.map +1 -0
- package/dist/contracts/openapi-meta.js +2 -0
- package/dist/contracts/openapi-meta.js.map +1 -0
- package/dist/contracts/path-template.d.ts +17 -0
- package/dist/contracts/path-template.d.ts.map +1 -0
- package/dist/contracts/path-template.js +50 -0
- package/dist/contracts/path-template.js.map +1 -0
- package/dist/contracts/rate-limit.d.ts +50 -0
- package/dist/contracts/rate-limit.d.ts.map +1 -0
- package/dist/contracts/rate-limit.js +2 -0
- package/dist/contracts/rate-limit.js.map +1 -0
- package/dist/contracts/types.d.ts +97 -0
- package/dist/contracts/types.d.ts.map +1 -0
- package/dist/contracts/types.js +54 -0
- package/dist/contracts/types.js.map +1 -0
- package/dist/contracts/utils.d.ts +3 -0
- package/dist/contracts/utils.d.ts.map +1 -0
- package/dist/contracts/utils.js +44 -0
- package/dist/contracts/utils.js.map +1 -0
- package/dist/domain/entity.d.ts +87 -0
- package/dist/domain/entity.d.ts.map +1 -0
- package/dist/domain/entity.js +155 -0
- package/dist/domain/entity.js.map +1 -0
- package/dist/domain/events.d.ts +41 -0
- package/dist/domain/events.d.ts.map +1 -0
- package/dist/domain/events.js +21 -0
- package/dist/domain/events.js.map +1 -0
- package/dist/domain/index.d.ts +14 -0
- package/dist/domain/index.d.ts.map +1 -0
- package/dist/domain/index.js +14 -0
- package/dist/domain/index.js.map +1 -0
- package/dist/domain/value-object.d.ts +60 -0
- package/dist/domain/value-object.d.ts.map +1 -0
- package/dist/domain/value-object.js +87 -0
- package/dist/domain/value-object.js.map +1 -0
- package/dist/errors/catalog.d.ts +71 -0
- package/dist/errors/catalog.d.ts.map +1 -0
- package/dist/errors/catalog.js +71 -0
- package/dist/errors/catalog.js.map +1 -0
- package/dist/errors/http.d.ts +77 -0
- package/dist/errors/http.d.ts.map +1 -0
- package/dist/errors/http.js +74 -0
- package/dist/errors/http.js.map +1 -0
- package/dist/errors/index.d.ts +10 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +14 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/errors/response.d.ts +26 -0
- package/dist/errors/response.d.ts.map +1 -0
- package/dist/errors/response.js +34 -0
- package/dist/errors/response.js.map +1 -0
- package/dist/errors/validation.d.ts +18 -0
- package/dist/errors/validation.d.ts.map +1 -0
- package/dist/errors/validation.js +21 -0
- package/dist/errors/validation.js.map +1 -0
- package/dist/events/index.d.ts +58 -0
- package/dist/events/index.d.ts.map +1 -0
- package/dist/events/index.js +102 -0
- package/dist/events/index.js.map +1 -0
- package/dist/jobs/index.d.ts +56 -0
- package/dist/jobs/index.d.ts.map +1 -0
- package/dist/jobs/index.js +89 -0
- package/dist/jobs/index.js.map +1 -0
- package/dist/mail/index.d.ts +75 -0
- package/dist/mail/index.d.ts.map +1 -0
- package/dist/mail/index.js +84 -0
- package/dist/mail/index.js.map +1 -0
- package/dist/openapi/index.d.ts +207 -0
- package/dist/openapi/index.d.ts.map +1 -0
- package/dist/openapi/index.js +449 -0
- package/dist/openapi/index.js.map +1 -0
- package/dist/openapi/schema-introspector.d.ts +38 -0
- package/dist/openapi/schema-introspector.d.ts.map +1 -0
- package/dist/openapi/schema-introspector.js +67 -0
- package/dist/openapi/schema-introspector.js.map +1 -0
- package/dist/ports/audit.d.ts +58 -0
- package/dist/ports/audit.d.ts.map +1 -0
- package/dist/ports/audit.js +74 -0
- package/dist/ports/audit.js.map +1 -0
- package/dist/ports/auth.d.ts +23 -0
- package/dist/ports/auth.d.ts.map +1 -0
- package/dist/ports/auth.js +31 -0
- package/dist/ports/auth.js.map +1 -0
- package/dist/ports/builder.d.ts +61 -0
- package/dist/ports/builder.d.ts.map +1 -0
- package/dist/ports/builder.js +48 -0
- package/dist/ports/builder.js.map +1 -0
- package/dist/ports/cache.d.ts +15 -0
- package/dist/ports/cache.d.ts.map +1 -0
- package/dist/ports/cache.js +57 -0
- package/dist/ports/cache.js.map +1 -0
- package/dist/ports/clock.d.ts +10 -0
- package/dist/ports/clock.d.ts.map +1 -0
- package/dist/ports/clock.js +21 -0
- package/dist/ports/clock.js.map +1 -0
- package/dist/ports/events.d.ts +71 -0
- package/dist/ports/events.d.ts.map +1 -0
- package/dist/ports/events.js +2 -0
- package/dist/ports/events.js.map +1 -0
- package/dist/ports/id-generator.d.ts +12 -0
- package/dist/ports/id-generator.d.ts.map +1 -0
- package/dist/ports/id-generator.js +22 -0
- package/dist/ports/id-generator.js.map +1 -0
- package/dist/ports/index.d.ts +98 -0
- package/dist/ports/index.d.ts.map +1 -0
- package/dist/ports/index.js +67 -0
- package/dist/ports/index.js.map +1 -0
- package/dist/ports/logger.d.ts +22 -0
- package/dist/ports/logger.d.ts.map +1 -0
- package/dist/ports/logger.js +34 -0
- package/dist/ports/logger.js.map +1 -0
- package/dist/ports/mailer.d.ts +6 -0
- package/dist/ports/mailer.d.ts.map +1 -0
- package/dist/ports/mailer.js +2 -0
- package/dist/ports/mailer.js.map +1 -0
- package/dist/ports/policy.d.ts +53 -0
- package/dist/ports/policy.d.ts.map +1 -0
- package/dist/ports/policy.js +81 -0
- package/dist/ports/policy.js.map +1 -0
- package/dist/ports/rate-limit.d.ts +41 -0
- package/dist/ports/rate-limit.d.ts.map +1 -0
- package/dist/ports/rate-limit.js +37 -0
- package/dist/ports/rate-limit.js.map +1 -0
- package/dist/ports/redaction.d.ts +26 -0
- package/dist/ports/redaction.d.ts.map +1 -0
- package/dist/ports/redaction.js +126 -0
- package/dist/ports/redaction.js.map +1 -0
- package/dist/ports/schedules.d.ts +9 -0
- package/dist/ports/schedules.d.ts.map +1 -0
- package/dist/ports/schedules.js +2 -0
- package/dist/ports/schedules.js.map +1 -0
- package/dist/ports/storage.d.ts +47 -0
- package/dist/ports/storage.d.ts.map +1 -0
- package/dist/ports/storage.js +185 -0
- package/dist/ports/storage.js.map +1 -0
- package/dist/ports/testing.d.ts +73 -0
- package/dist/ports/testing.d.ts.map +1 -0
- package/dist/ports/testing.js +105 -0
- package/dist/ports/testing.js.map +1 -0
- package/dist/ports/unit-of-work.d.ts +56 -0
- package/dist/ports/unit-of-work.d.ts.map +1 -0
- package/dist/ports/unit-of-work.js +64 -0
- package/dist/ports/unit-of-work.js.map +1 -0
- package/dist/providers/index.d.ts +8 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +8 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/instrumentation.d.ts +91 -0
- package/dist/providers/instrumentation.d.ts.map +1 -0
- package/dist/providers/instrumentation.js +93 -0
- package/dist/providers/instrumentation.js.map +1 -0
- package/dist/providers/provider.d.ts +146 -0
- package/dist/providers/provider.d.ts.map +1 -0
- package/dist/providers/provider.js +31 -0
- package/dist/providers/provider.js.map +1 -0
- package/dist/schedules/index.d.ts +105 -0
- package/dist/schedules/index.d.ts.map +1 -0
- package/dist/schedules/index.js +178 -0
- package/dist/schedules/index.js.map +1 -0
- package/dist/server/contract-like.d.ts +5 -0
- package/dist/server/contract-like.d.ts.map +1 -0
- package/dist/server/contract-like.js +5 -0
- package/dist/server/contract-like.js.map +1 -0
- package/dist/server/health.d.ts +41 -0
- package/dist/server/health.d.ts.map +1 -0
- package/dist/server/health.js +46 -0
- package/dist/server/health.js.map +1 -0
- package/dist/server/hooks/auth.d.ts +42 -0
- package/dist/server/hooks/auth.d.ts.map +1 -0
- package/dist/server/hooks/auth.js +61 -0
- package/dist/server/hooks/auth.js.map +1 -0
- package/dist/server/hooks/cors.d.ts +13 -0
- package/dist/server/hooks/cors.d.ts.map +1 -0
- package/dist/server/hooks/cors.js +70 -0
- package/dist/server/hooks/cors.js.map +1 -0
- package/dist/server/hooks/errors.d.ts +66 -0
- package/dist/server/hooks/errors.d.ts.map +1 -0
- package/dist/server/hooks/errors.js +83 -0
- package/dist/server/hooks/errors.js.map +1 -0
- package/dist/server/hooks/index.d.ts +12 -0
- package/dist/server/hooks/index.d.ts.map +1 -0
- package/dist/server/hooks/index.js +12 -0
- package/dist/server/hooks/index.js.map +1 -0
- package/dist/server/hooks/logging.d.ts +33 -0
- package/dist/server/hooks/logging.d.ts.map +1 -0
- package/dist/server/hooks/logging.js +90 -0
- package/dist/server/hooks/logging.js.map +1 -0
- package/dist/server/hooks/rate-limit.d.ts +29 -0
- package/dist/server/hooks/rate-limit.d.ts.map +1 -0
- package/dist/server/hooks/rate-limit.js +93 -0
- package/dist/server/hooks/rate-limit.js.map +1 -0
- package/dist/server/hooks/utils.d.ts +9 -0
- package/dist/server/hooks/utils.d.ts.map +1 -0
- package/dist/server/hooks/utils.js +16 -0
- package/dist/server/hooks/utils.js.map +1 -0
- package/dist/server/hooks.d.ts +2 -0
- package/dist/server/hooks.d.ts.map +1 -0
- package/dist/server/hooks.js +2 -0
- package/dist/server/hooks.js.map +1 -0
- package/dist/server/http.d.ts +124 -0
- package/dist/server/http.d.ts.map +1 -0
- package/dist/server/http.js +2 -0
- package/dist/server/http.js.map +1 -0
- package/dist/server/index.d.ts +19 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +15 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/openapi.d.ts +32 -0
- package/dist/server/openapi.d.ts.map +1 -0
- package/dist/server/openapi.js +43 -0
- package/dist/server/openapi.js.map +1 -0
- package/dist/server/providers/index.d.ts +4 -0
- package/dist/server/providers/index.d.ts.map +1 -0
- package/dist/server/providers/index.js +4 -0
- package/dist/server/providers/index.js.map +1 -0
- package/dist/server/providers/loadProviderConfig.d.ts +7 -0
- package/dist/server/providers/loadProviderConfig.d.ts.map +1 -0
- package/dist/server/providers/loadProviderConfig.js +42 -0
- package/dist/server/providers/loadProviderConfig.js.map +1 -0
- package/dist/server/server.d.ts +86 -0
- package/dist/server/server.d.ts.map +1 -0
- package/dist/server/server.js +1031 -0
- package/dist/server/server.js.map +1 -0
- package/dist/server/types.d.ts +3 -0
- package/dist/server/types.d.ts.map +1 -0
- package/dist/server/types.js +3 -0
- package/dist/server/types.js.map +1 -0
- package/package.json +129 -0
- package/src/application/index.ts +747 -0
- package/src/client/client.ts +1105 -0
- package/src/client/index.ts +45 -0
- package/src/client/types.ts +305 -0
- package/src/config/index.ts +497 -0
- package/src/contracts/contract-builder.ts +583 -0
- package/src/contracts/contract-group.ts +502 -0
- package/src/contracts/contract-like.ts +29 -0
- package/src/contracts/index.ts +53 -0
- package/src/contracts/openapi-meta.ts +22 -0
- package/src/contracts/path-template.ts +91 -0
- package/src/contracts/rate-limit.ts +50 -0
- package/src/contracts/types.ts +207 -0
- package/src/contracts/utils.ts +56 -0
- package/src/domain/entity.ts +256 -0
- package/src/domain/events.ts +52 -0
- package/src/domain/index.ts +18 -0
- package/src/domain/value-object.ts +135 -0
- package/src/errors/catalog.ts +149 -0
- package/src/errors/http.ts +80 -0
- package/src/errors/index.ts +28 -0
- package/src/errors/response.ts +54 -0
- package/src/errors/validation.ts +35 -0
- package/src/events/index.ts +246 -0
- package/src/jobs/index.ts +211 -0
- package/src/mail/index.ts +177 -0
- package/src/openapi/index.ts +865 -0
- package/src/openapi/schema-introspector.ts +107 -0
- package/src/ports/audit.ts +176 -0
- package/src/ports/auth.ts +76 -0
- package/src/ports/builder.ts +97 -0
- package/src/ports/cache.ts +94 -0
- package/src/ports/clock.ts +34 -0
- package/src/ports/events.ts +100 -0
- package/src/ports/id-generator.ts +36 -0
- package/src/ports/index.ts +221 -0
- package/src/ports/logger.ts +67 -0
- package/src/ports/policy.ts +242 -0
- package/src/ports/rate-limit.ts +91 -0
- package/src/ports/redaction.ts +199 -0
- package/src/ports/storage.ts +282 -0
- package/src/ports/testing.ts +234 -0
- package/src/ports/unit-of-work.ts +134 -0
- package/src/providers/index.ts +40 -0
- package/src/providers/instrumentation.ts +248 -0
- package/src/providers/provider.ts +191 -0
- package/src/schedules/index.ts +442 -0
- package/src/server/contract-like.ts +8 -0
- package/src/server/health.ts +82 -0
- package/src/server/hooks/auth.ts +147 -0
- package/src/server/hooks/cors.ts +87 -0
- package/src/server/hooks/errors.ts +126 -0
- package/src/server/hooks/index.ts +43 -0
- package/src/server/hooks/logging.ts +121 -0
- package/src/server/hooks/rate-limit.ts +171 -0
- package/src/server/hooks/utils.ts +16 -0
- package/src/server/hooks.ts +1 -0
- package/src/server/http.ts +189 -0
- package/src/server/index.ts +35 -0
- package/src/server/openapi.ts +72 -0
- package/src/server/providers/index.ts +3 -0
- package/src/server/providers/loadProviderConfig.ts +72 -0
- package/src/server/server.ts +1521 -0
- package/src/server/types.ts +2 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import type { StandardSchemaV1 } from "@standard-schema/spec";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Any Standard Schema validator type
|
|
5
|
+
*/
|
|
6
|
+
export type StandardSchema = StandardSchemaV1<unknown, unknown>;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Infer the output type from a Standard Schema
|
|
10
|
+
*/
|
|
11
|
+
export type InferOutput<T extends StandardSchemaV1> =
|
|
12
|
+
StandardSchemaV1.InferOutput<T>;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Validate data using a Standard Schema validator
|
|
16
|
+
*/
|
|
17
|
+
async function validateSchema<T>(
|
|
18
|
+
schema: StandardSchemaV1<unknown, T>,
|
|
19
|
+
data: unknown,
|
|
20
|
+
): Promise<T> {
|
|
21
|
+
const result = await schema["~standard"].validate(data);
|
|
22
|
+
if (result.issues?.length) {
|
|
23
|
+
const message = result.issues
|
|
24
|
+
.map((issue) => {
|
|
25
|
+
const path =
|
|
26
|
+
issue.path !== undefined
|
|
27
|
+
? Array.isArray(issue.path)
|
|
28
|
+
? issue.path.join(".")
|
|
29
|
+
: String(issue.path)
|
|
30
|
+
: "";
|
|
31
|
+
return path ? `${path}: ${issue.message}` : issue.message;
|
|
32
|
+
})
|
|
33
|
+
.join("; ");
|
|
34
|
+
throw new Error(`Validation failed: ${message}`);
|
|
35
|
+
}
|
|
36
|
+
if ("value" in result) {
|
|
37
|
+
return result.value;
|
|
38
|
+
}
|
|
39
|
+
throw new Error("Invalid Standard Schema result: missing value");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Check if data is valid according to a Standard Schema
|
|
44
|
+
*/
|
|
45
|
+
async function isValid<T>(
|
|
46
|
+
schema: StandardSchemaV1<unknown, T>,
|
|
47
|
+
data: unknown,
|
|
48
|
+
): Promise<boolean> {
|
|
49
|
+
const result = await schema["~standard"].validate(data);
|
|
50
|
+
return !result.issues?.length && "value" in result;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Represents a defined Value Object with validation and type information.
|
|
55
|
+
*/
|
|
56
|
+
export interface ValueObjectDef<
|
|
57
|
+
Name extends string,
|
|
58
|
+
Schema extends StandardSchema,
|
|
59
|
+
> {
|
|
60
|
+
/** The name of this value object (for debugging/introspection) */
|
|
61
|
+
name: Name;
|
|
62
|
+
/** The schema used for validation (any Standard Schema compatible) */
|
|
63
|
+
schema: Schema;
|
|
64
|
+
/** Create a validated value object from unknown input (async due to Standard Schema) */
|
|
65
|
+
create(input: unknown): Promise<InferOutput<Schema>>;
|
|
66
|
+
/** Type guard to check if an input is valid for this value object (async due to Standard Schema) */
|
|
67
|
+
isValid(input: unknown): Promise<boolean>;
|
|
68
|
+
/** Type alias for the inferred TypeScript type */
|
|
69
|
+
Type: InferOutput<Schema>;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Builder class for creating Value Objects.
|
|
74
|
+
*/
|
|
75
|
+
class ValueObjectBuilder<Name extends string, Schema extends StandardSchema> {
|
|
76
|
+
constructor(
|
|
77
|
+
private readonly cfg: {
|
|
78
|
+
name: Name;
|
|
79
|
+
schema?: Schema;
|
|
80
|
+
},
|
|
81
|
+
) {}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Set the schema for this value object.
|
|
85
|
+
*/
|
|
86
|
+
schema<S extends StandardSchema>(s: S): ValueObjectBuilder<Name, S> {
|
|
87
|
+
return new ValueObjectBuilder<Name, S>({ ...this.cfg, schema: s });
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Finalize the value object definition.
|
|
92
|
+
*/
|
|
93
|
+
build(): ValueObjectDef<Name, Schema> {
|
|
94
|
+
if (!this.cfg.schema) {
|
|
95
|
+
throw new Error(`Value object "${this.cfg.name}" is missing a schema`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const schemaToUse = this.cfg.schema;
|
|
99
|
+
|
|
100
|
+
const def: ValueObjectDef<Name, Schema> = {
|
|
101
|
+
name: this.cfg.name,
|
|
102
|
+
schema: schemaToUse,
|
|
103
|
+
async create(input: unknown) {
|
|
104
|
+
return validateSchema(schemaToUse, input);
|
|
105
|
+
},
|
|
106
|
+
async isValid(input: unknown) {
|
|
107
|
+
return isValid(schemaToUse, input);
|
|
108
|
+
},
|
|
109
|
+
// Type is undefined at runtime - used only for TypeScript type inference via `typeof ValueObject.Type`
|
|
110
|
+
Type: undefined as unknown as InferOutput<Schema>,
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
return def;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Create a new Value Object builder.
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* ```ts
|
|
122
|
+
* const Email = defineValueObject("Email")
|
|
123
|
+
* .schema(z.string().email())
|
|
124
|
+
* .build();
|
|
125
|
+
*
|
|
126
|
+
* type Email = typeof Email.Type;
|
|
127
|
+
*
|
|
128
|
+
* const email = await Email.create("test@example.com"); // OK
|
|
129
|
+
* const isValid = await Email.isValid("test@example.com"); // true
|
|
130
|
+
* ```
|
|
131
|
+
*/
|
|
132
|
+
export function defineValueObject<Name extends string>(name: Name) {
|
|
133
|
+
// biome-ignore lint/suspicious/noExplicitAny: Initial schema type is never, will be set via .schema()
|
|
134
|
+
return new ValueObjectBuilder<Name, never>({ name } as any);
|
|
135
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error catalog and AppError definitions for Beignet
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { StandardSchemaV1 } from "@standard-schema/spec";
|
|
6
|
+
|
|
7
|
+
type ErrorDetailsSchema = StandardSchemaV1<unknown, unknown>;
|
|
8
|
+
const APP_ERROR_BRAND = Symbol.for("beignet.AppError");
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Definition of a single error in the catalog
|
|
12
|
+
*/
|
|
13
|
+
export interface ErrorDef<
|
|
14
|
+
TDetails extends ErrorDetailsSchema | undefined =
|
|
15
|
+
| ErrorDetailsSchema
|
|
16
|
+
| undefined,
|
|
17
|
+
> {
|
|
18
|
+
/** Unique, stable code used across layers (e.g. "TODO_NOT_FOUND") */
|
|
19
|
+
code: string;
|
|
20
|
+
/** HTTP status code (e.g. 404, 401, 409, 422, 500, etc.) */
|
|
21
|
+
status: number;
|
|
22
|
+
/** Default human-readable message */
|
|
23
|
+
message: string;
|
|
24
|
+
/** Optional schema for the structured details value */
|
|
25
|
+
details?: TDetails;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* A mapping of keys to error definitions.
|
|
30
|
+
* Keys can be friendly identifiers, e.g. "TodoNotFound".
|
|
31
|
+
*/
|
|
32
|
+
export type ErrorCatalog = Record<string, ErrorDef>;
|
|
33
|
+
|
|
34
|
+
export type InferErrorDetails<TDef extends ErrorDef> = TDef extends {
|
|
35
|
+
details: ErrorDetailsSchema;
|
|
36
|
+
}
|
|
37
|
+
? StandardSchemaV1.InferInput<TDef["details"]>
|
|
38
|
+
: unknown;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Type-preserving helper to define an error catalog.
|
|
42
|
+
*/
|
|
43
|
+
export function defineErrors<const T extends ErrorCatalog>(defs: T): T {
|
|
44
|
+
return defs;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Application error class that domain & application code can throw.
|
|
49
|
+
* Contains the error definition from the catalog and optional structured details.
|
|
50
|
+
*/
|
|
51
|
+
export class AppError<TDef extends ErrorDef = ErrorDef> extends Error {
|
|
52
|
+
readonly [APP_ERROR_BRAND] = true;
|
|
53
|
+
/** The error definition from the catalog */
|
|
54
|
+
readonly def: TDef;
|
|
55
|
+
/** Optional structured details (for debugging, UI, etc.) */
|
|
56
|
+
readonly details?: InferErrorDetails<TDef>;
|
|
57
|
+
|
|
58
|
+
constructor(
|
|
59
|
+
def: TDef,
|
|
60
|
+
details?: InferErrorDetails<TDef>,
|
|
61
|
+
overrideMessage?: string,
|
|
62
|
+
options?: { cause?: unknown },
|
|
63
|
+
) {
|
|
64
|
+
super(overrideMessage ?? def.message, { cause: options?.cause });
|
|
65
|
+
this.name = "AppError";
|
|
66
|
+
this.def = def;
|
|
67
|
+
this.details = details;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
get code(): string {
|
|
71
|
+
return this.def.code;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
get status(): number {
|
|
75
|
+
return this.def.status;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Callable helper for creating AppErrors from a catalog.
|
|
81
|
+
*/
|
|
82
|
+
export type AppErrorCreator<TCatalog extends ErrorCatalog> = {
|
|
83
|
+
catalog: TCatalog;
|
|
84
|
+
/**
|
|
85
|
+
* Create an AppError from a catalog key.
|
|
86
|
+
*/
|
|
87
|
+
<Key extends keyof TCatalog>(
|
|
88
|
+
key: Key,
|
|
89
|
+
options?: {
|
|
90
|
+
cause?: unknown;
|
|
91
|
+
details?: InferErrorDetails<TCatalog[Key]>;
|
|
92
|
+
message?: string;
|
|
93
|
+
},
|
|
94
|
+
): AppError<TCatalog[Key]>;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Create a callable AppError helper bound to a specific catalog.
|
|
99
|
+
*/
|
|
100
|
+
export function createAppError<const T extends ErrorCatalog>(
|
|
101
|
+
catalog: T,
|
|
102
|
+
): AppErrorCreator<T> {
|
|
103
|
+
const appError = (<Key extends keyof T>(
|
|
104
|
+
key: Key,
|
|
105
|
+
options?: {
|
|
106
|
+
cause?: unknown;
|
|
107
|
+
details?: InferErrorDetails<T[Key]>;
|
|
108
|
+
message?: string;
|
|
109
|
+
},
|
|
110
|
+
): AppError<T[Key]> => {
|
|
111
|
+
const def = catalog[key as keyof T] as T[typeof key] | undefined;
|
|
112
|
+
if (!def) {
|
|
113
|
+
throw new Error(`Unknown error catalog key: ${String(key)}`);
|
|
114
|
+
}
|
|
115
|
+
return new AppError<T[typeof key]>(
|
|
116
|
+
def,
|
|
117
|
+
options?.details,
|
|
118
|
+
options?.message,
|
|
119
|
+
{
|
|
120
|
+
cause: options?.cause,
|
|
121
|
+
},
|
|
122
|
+
);
|
|
123
|
+
}) as AppErrorCreator<T>;
|
|
124
|
+
|
|
125
|
+
appError.catalog = catalog;
|
|
126
|
+
return appError;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Type guard to check if an unknown error is an AppError
|
|
131
|
+
*/
|
|
132
|
+
export function isAppError(err: unknown): err is AppError<ErrorDef> {
|
|
133
|
+
if (err instanceof AppError) return true;
|
|
134
|
+
if (typeof err !== "object" || err === null) return false;
|
|
135
|
+
|
|
136
|
+
const value = err as Record<PropertyKey, unknown>;
|
|
137
|
+
if (value[APP_ERROR_BRAND] === true) return true;
|
|
138
|
+
if (value.name !== "AppError") return false;
|
|
139
|
+
|
|
140
|
+
const def = value.def;
|
|
141
|
+
if (typeof def !== "object" || def === null) return false;
|
|
142
|
+
|
|
143
|
+
const errorDef = def as Record<string, unknown>;
|
|
144
|
+
return (
|
|
145
|
+
typeof errorDef.code === "string" &&
|
|
146
|
+
typeof errorDef.status === "number" &&
|
|
147
|
+
typeof errorDef.message === "string"
|
|
148
|
+
);
|
|
149
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standard HTTP error catalog
|
|
3
|
+
*
|
|
4
|
+
* A base catalog of common HTTP-related errors that apps can reuse and extend.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { defineErrors } from "./catalog";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A base catalog of common HTTP-related errors.
|
|
11
|
+
*
|
|
12
|
+
* Apps can spread this into their own error catalog:
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* import { defineErrors } from "@beignet/core/errors";
|
|
17
|
+
* import { httpErrors } from "@beignet/core/errors/http";
|
|
18
|
+
*
|
|
19
|
+
* export const errors = defineErrors({
|
|
20
|
+
* ...httpErrors,
|
|
21
|
+
* TodoNotFound: {
|
|
22
|
+
* code: "TODO_NOT_FOUND",
|
|
23
|
+
* status: 404,
|
|
24
|
+
* message: "Todo not found",
|
|
25
|
+
* },
|
|
26
|
+
* });
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export const httpErrors = defineErrors({
|
|
30
|
+
BadRequest: {
|
|
31
|
+
code: "BAD_REQUEST",
|
|
32
|
+
status: 400,
|
|
33
|
+
message: "Bad request",
|
|
34
|
+
},
|
|
35
|
+
Unauthorized: {
|
|
36
|
+
code: "UNAUTHORIZED",
|
|
37
|
+
status: 401,
|
|
38
|
+
message: "Unauthorized",
|
|
39
|
+
},
|
|
40
|
+
Forbidden: {
|
|
41
|
+
code: "FORBIDDEN",
|
|
42
|
+
status: 403,
|
|
43
|
+
message: "Forbidden",
|
|
44
|
+
},
|
|
45
|
+
NotFound: {
|
|
46
|
+
code: "NOT_FOUND",
|
|
47
|
+
status: 404,
|
|
48
|
+
message: "Resource not found",
|
|
49
|
+
},
|
|
50
|
+
Conflict: {
|
|
51
|
+
code: "CONFLICT",
|
|
52
|
+
status: 409,
|
|
53
|
+
message: "Conflict",
|
|
54
|
+
},
|
|
55
|
+
TooManyRequests: {
|
|
56
|
+
code: "TOO_MANY_REQUESTS",
|
|
57
|
+
status: 429,
|
|
58
|
+
message: "Too many requests",
|
|
59
|
+
},
|
|
60
|
+
UnprocessableEntity: {
|
|
61
|
+
code: "UNPROCESSABLE_ENTITY",
|
|
62
|
+
status: 422,
|
|
63
|
+
message: "Unprocessable entity",
|
|
64
|
+
},
|
|
65
|
+
ValidationError: {
|
|
66
|
+
code: "VALIDATION_ERROR",
|
|
67
|
+
status: 422,
|
|
68
|
+
message: "Validation failed",
|
|
69
|
+
},
|
|
70
|
+
InternalServerError: {
|
|
71
|
+
code: "INTERNAL_SERVER_ERROR",
|
|
72
|
+
status: 500,
|
|
73
|
+
message: "Internal server error",
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Type of the default HTTP error catalog.
|
|
79
|
+
*/
|
|
80
|
+
export type HttpErrorCatalog = typeof httpErrors;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @beignet/core/errors
|
|
3
|
+
*
|
|
4
|
+
* Error catalog and error handling utilities for Beignet
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// Error catalog and AppError
|
|
8
|
+
export {
|
|
9
|
+
AppError,
|
|
10
|
+
type AppErrorCreator,
|
|
11
|
+
createAppError,
|
|
12
|
+
defineErrors,
|
|
13
|
+
type ErrorCatalog,
|
|
14
|
+
type ErrorDef,
|
|
15
|
+
type InferErrorDetails,
|
|
16
|
+
isAppError,
|
|
17
|
+
} from "./catalog";
|
|
18
|
+
// HTTP error helpers
|
|
19
|
+
export { type HttpErrorCatalog, httpErrors } from "./http";
|
|
20
|
+
// Error response utilities
|
|
21
|
+
export {
|
|
22
|
+
createErrorResponseBody,
|
|
23
|
+
type ErrorResponseBody,
|
|
24
|
+
isErrorResponseBody,
|
|
25
|
+
toErrorResponseBody,
|
|
26
|
+
} from "./response";
|
|
27
|
+
// Schema validation error (shared across client/core/server)
|
|
28
|
+
export { SchemaValidationError, type ValidationIssue } from "./validation";
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standard error response schema and utilities for Beignet
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { AppError, ErrorDef } from "./catalog";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Standard error response body structure
|
|
9
|
+
*/
|
|
10
|
+
export interface ErrorResponseBody {
|
|
11
|
+
code: string;
|
|
12
|
+
message: string;
|
|
13
|
+
details?: unknown;
|
|
14
|
+
requestId?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Create a standard error response body.
|
|
19
|
+
*/
|
|
20
|
+
export function createErrorResponseBody(
|
|
21
|
+
args: ErrorResponseBody,
|
|
22
|
+
): ErrorResponseBody {
|
|
23
|
+
return {
|
|
24
|
+
code: args.code,
|
|
25
|
+
message: args.message,
|
|
26
|
+
...(args.details !== undefined ? { details: args.details } : {}),
|
|
27
|
+
...(args.requestId !== undefined ? { requestId: args.requestId } : {}),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Check whether a value is a standard error response body.
|
|
33
|
+
*/
|
|
34
|
+
export function isErrorResponseBody(
|
|
35
|
+
value: unknown,
|
|
36
|
+
): value is ErrorResponseBody {
|
|
37
|
+
if (typeof value !== "object" || value === null) return false;
|
|
38
|
+
|
|
39
|
+
const body = value as Record<string, unknown>;
|
|
40
|
+
return typeof body.code === "string" && typeof body.message === "string";
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Convert an AppError to a standard error response body
|
|
45
|
+
*/
|
|
46
|
+
export function toErrorResponseBody(
|
|
47
|
+
err: AppError<ErrorDef>,
|
|
48
|
+
): ErrorResponseBody {
|
|
49
|
+
return createErrorResponseBody({
|
|
50
|
+
code: err.code,
|
|
51
|
+
message: err.message,
|
|
52
|
+
details: err.details,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { StandardSchemaV1 } from "@standard-schema/spec";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Normalized validation issue with simplified path
|
|
5
|
+
*/
|
|
6
|
+
export interface ValidationIssue {
|
|
7
|
+
path?: (string | number)[];
|
|
8
|
+
message: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Error thrown when Standard Schema validation fails.
|
|
13
|
+
* Shared across client and server packages so `instanceof` checks work
|
|
14
|
+
* regardless of which package threw the error.
|
|
15
|
+
*/
|
|
16
|
+
export class SchemaValidationError extends Error {
|
|
17
|
+
readonly issues: ReadonlyArray<ValidationIssue>;
|
|
18
|
+
|
|
19
|
+
constructor(rawIssues: ReadonlyArray<StandardSchemaV1.Issue>) {
|
|
20
|
+
const issues: ValidationIssue[] = rawIssues.map((i) => ({
|
|
21
|
+
path: i.path?.map((p) => (typeof p === "object" ? p.key : p)) as
|
|
22
|
+
| (string | number)[]
|
|
23
|
+
| undefined,
|
|
24
|
+
message: i.message,
|
|
25
|
+
}));
|
|
26
|
+
const message = issues
|
|
27
|
+
.map((i) =>
|
|
28
|
+
i.path?.length ? `${i.path.join(".")}: ${i.message}` : i.message,
|
|
29
|
+
)
|
|
30
|
+
.join("; ");
|
|
31
|
+
super(message);
|
|
32
|
+
this.name = "SchemaValidationError";
|
|
33
|
+
this.issues = issues;
|
|
34
|
+
}
|
|
35
|
+
}
|