@avelonjs/core 0.1.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/LICENSE +21 -0
- package/README.md +566 -0
- package/package.json +46 -0
- package/src/adapter.ts +204 -0
- package/src/drivers/ai.ts +99 -0
- package/src/drivers/cache.ts +48 -0
- package/src/drivers/common.ts +29 -0
- package/src/drivers/database.ts +160 -0
- package/src/drivers/flags.ts +50 -0
- package/src/drivers/identity.ts +133 -0
- package/src/drivers/logs.ts +61 -0
- package/src/drivers/mail.ts +73 -0
- package/src/drivers/notifications.ts +64 -0
- package/src/drivers/payments.ts +107 -0
- package/src/drivers/queue.ts +87 -0
- package/src/drivers/ratelimit.ts +66 -0
- package/src/drivers/realtime.ts +63 -0
- package/src/drivers/search.ts +92 -0
- package/src/drivers/social.ts +63 -0
- package/src/drivers/storage.ts +78 -0
- package/src/drivers/tokens.ts +87 -0
- package/src/errors.ts +166 -0
- package/src/events.ts +185 -0
- package/src/index.ts +22 -0
- package/src/query.ts +86 -0
- package/src/runtime/config.ts +231 -0
- package/src/runtime/dispatcher.ts +335 -0
- package/src/runtime/errands.ts +148 -0
- package/src/runtime/gate.ts +96 -0
- package/src/runtime/index.ts +81 -0
- package/src/runtime/jobs.ts +126 -0
- package/src/runtime/kernel.ts +202 -0
- package/src/runtime/requests.ts +77 -0
- package/src/runtime/transaction.ts +45 -0
- package/src/runtime/wards.ts +362 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { DriverContract } from './common'
|
|
2
|
+
|
|
3
|
+
/** Literal search features used to constrain query options. */
|
|
4
|
+
export interface SearchCapabilities {
|
|
5
|
+
/** Whether queries may request facet counts. */
|
|
6
|
+
readonly facets: boolean
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/** Search document with a stable identifier and indexable fields. */
|
|
10
|
+
export interface SearchDocument {
|
|
11
|
+
/** Stable document identifier. */
|
|
12
|
+
id: string
|
|
13
|
+
/** Indexable document fields. */
|
|
14
|
+
fields: Readonly<Record<string, unknown>>
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Portable search query options. */
|
|
18
|
+
export interface SearchOptions {
|
|
19
|
+
/** Maximum hits to return. */
|
|
20
|
+
limit?: number
|
|
21
|
+
/** Number of hits to skip. */
|
|
22
|
+
offset?: number
|
|
23
|
+
/** Exact filters applied by the driver. */
|
|
24
|
+
filters?: Readonly<Record<string, unknown>>
|
|
25
|
+
/** Fields for which facet counts should be returned. */
|
|
26
|
+
facets?: readonly string[]
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** One search hit and its normalized relevance score. */
|
|
30
|
+
export interface SearchHit<TDocument extends SearchDocument = SearchDocument> {
|
|
31
|
+
/** Matching document. */
|
|
32
|
+
document: TDocument
|
|
33
|
+
/** Driver-normalized relevance score. */
|
|
34
|
+
score: number
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Portable search response. */
|
|
38
|
+
export interface SearchResult<TDocument extends SearchDocument = SearchDocument> {
|
|
39
|
+
/** Ordered matching documents. */
|
|
40
|
+
hits: readonly SearchHit<TDocument>[]
|
|
41
|
+
/** Total matches before pagination. */
|
|
42
|
+
total: number
|
|
43
|
+
/** Requested facet counts when supported. */
|
|
44
|
+
facets?: Readonly<Record<string, Readonly<Record<string, number>>>>
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Search indexing and query contract. */
|
|
48
|
+
export interface SearchDriver<
|
|
49
|
+
TCapabilities extends SearchCapabilities = SearchCapabilities,
|
|
50
|
+
TRaw = unknown,
|
|
51
|
+
> extends DriverContract<TCapabilities, TRaw> {
|
|
52
|
+
/** Adds or replaces documents in a named index. */
|
|
53
|
+
index(name: string, documents: readonly SearchDocument[]): Promise<void>
|
|
54
|
+
/** Removes documents from a named index. */
|
|
55
|
+
remove(name: string, ids: readonly string[]): Promise<void>
|
|
56
|
+
/** Queries a named index. */
|
|
57
|
+
query<TDocument extends SearchDocument = SearchDocument>(
|
|
58
|
+
name: string,
|
|
59
|
+
query: string,
|
|
60
|
+
options?: SearchOptions,
|
|
61
|
+
): Promise<SearchResult<TDocument>>
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
type SearchOptionsFor<TCapabilities extends SearchCapabilities> =
|
|
65
|
+
TCapabilities['facets'] extends true
|
|
66
|
+
? SearchOptions
|
|
67
|
+
: Omit<SearchOptions, 'facets'> & { facets?: never }
|
|
68
|
+
|
|
69
|
+
/** Search facade narrowed to its driver's facet capability. */
|
|
70
|
+
export interface SearchSurface<TDriver extends SearchDriver> {
|
|
71
|
+
/** Driver implementation name. */
|
|
72
|
+
readonly name: TDriver['name']
|
|
73
|
+
/** Configured search index connection name. */
|
|
74
|
+
readonly instance: TDriver['instance']
|
|
75
|
+
/** Exact search capability declaration. */
|
|
76
|
+
readonly capabilities: TDriver['capabilities']
|
|
77
|
+
/** Adds or replaces documents in an index. */
|
|
78
|
+
index(name: string, documents: readonly SearchDocument[]): Promise<void>
|
|
79
|
+
/** Removes documents from an index. */
|
|
80
|
+
remove(name: string, ids: readonly string[]): Promise<void>
|
|
81
|
+
/** Queries an index using only supported options. */
|
|
82
|
+
query<TDocument extends SearchDocument = SearchDocument>(
|
|
83
|
+
name: string,
|
|
84
|
+
query: string,
|
|
85
|
+
options?: SearchOptionsFor<TDriver['capabilities']>,
|
|
86
|
+
): Promise<SearchResult<TDocument>>
|
|
87
|
+
/** Returns the driver's typed underlying client. */
|
|
88
|
+
raw(): ReturnType<TDriver['raw']>
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Search facade narrowed to its driver's exact capabilities. */
|
|
92
|
+
export type Search<TDriver extends SearchDriver> = SearchSurface<TDriver>
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { CapabilityMembers, DriverContract } from './common'
|
|
2
|
+
|
|
3
|
+
/** Literal social authentication providers accepted by a driver instance. */
|
|
4
|
+
export interface SocialCapabilities<TProvider extends string = string> {
|
|
5
|
+
/** Provider identifiers retained as a literal readonly list. */
|
|
6
|
+
readonly providers: readonly TProvider[]
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/** Identity data returned after a social authorization callback. */
|
|
10
|
+
export interface SocialIdentity<TProfile = unknown> {
|
|
11
|
+
/** Driver-independent provider identifier. */
|
|
12
|
+
provider: string
|
|
13
|
+
/** Stable provider-side subject identifier. */
|
|
14
|
+
subject: string
|
|
15
|
+
/** Provider profile normalized by the driver. */
|
|
16
|
+
profile: TProfile
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** OAuth redirect and callback contract. */
|
|
20
|
+
export interface SocialDriver<
|
|
21
|
+
TCapabilities extends SocialCapabilities = SocialCapabilities,
|
|
22
|
+
TRaw = unknown,
|
|
23
|
+
TProfile = unknown,
|
|
24
|
+
> extends DriverContract<TCapabilities, TRaw> {
|
|
25
|
+
/** Builds the authorization URL for a configured provider. */
|
|
26
|
+
redirect(provider: string, callbackUrl: string, state?: string): Promise<string>
|
|
27
|
+
/** Verifies `state` and exchanges the callback `code` for normalized social identity data. */
|
|
28
|
+
callback(
|
|
29
|
+
provider: string,
|
|
30
|
+
params: Readonly<Record<string, string>>,
|
|
31
|
+
callbackUrl: string,
|
|
32
|
+
): Promise<SocialIdentity<TProfile>>
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
type SocialProfileOf<TDriver extends SocialDriver> =
|
|
36
|
+
TDriver extends SocialDriver<SocialCapabilities, unknown, infer TProfile> ? TProfile : never
|
|
37
|
+
|
|
38
|
+
/** Social facade restricted to the configured provider literals. */
|
|
39
|
+
export interface SocialSurface<TDriver extends SocialDriver> {
|
|
40
|
+
/** Driver implementation name. */
|
|
41
|
+
readonly name: TDriver['name']
|
|
42
|
+
/** Configured social connection name. */
|
|
43
|
+
readonly instance: TDriver['instance']
|
|
44
|
+
/** Exact provider capability list. */
|
|
45
|
+
readonly capabilities: TDriver['capabilities']
|
|
46
|
+
/** Builds an authorization URL for a configured provider. */
|
|
47
|
+
redirect(
|
|
48
|
+
provider: CapabilityMembers<TDriver['capabilities']['providers']>,
|
|
49
|
+
callbackUrl: string,
|
|
50
|
+
state?: string,
|
|
51
|
+
): Promise<string>
|
|
52
|
+
/** Verifies `state` and exchanges the configured provider's callback `code`. */
|
|
53
|
+
callback(
|
|
54
|
+
provider: CapabilityMembers<TDriver['capabilities']['providers']>,
|
|
55
|
+
params: Readonly<Record<string, string>>,
|
|
56
|
+
callbackUrl: string,
|
|
57
|
+
): Promise<SocialIdentity<SocialProfileOf<TDriver>>>
|
|
58
|
+
/** Returns the driver's typed underlying client. */
|
|
59
|
+
raw(): ReturnType<TDriver['raw']>
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Social authentication facade narrowed to configured provider literals. */
|
|
63
|
+
export type Social<TDriver extends SocialDriver> = SocialSurface<TDriver>
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { CapabilityMembers, CapabilitySurface, DriverContract } from './common'
|
|
2
|
+
|
|
3
|
+
/** Portable transform operations a storage driver may advertise. */
|
|
4
|
+
export type StorageTransform = 'resize' | 'crop' | 'format'
|
|
5
|
+
|
|
6
|
+
/** Literal storage features used to narrow file operations. */
|
|
7
|
+
export interface StorageCapabilities<TTransform extends StorageTransform = StorageTransform> {
|
|
8
|
+
/** Whether temporary signed URLs are available. */
|
|
9
|
+
readonly signedUrls: boolean
|
|
10
|
+
/** Transform operations retained as a literal readonly list. */
|
|
11
|
+
readonly transforms: readonly TTransform[]
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Metadata returned for a stored object. */
|
|
15
|
+
export interface StorageObject {
|
|
16
|
+
/** Object path within the configured disk. */
|
|
17
|
+
path: string
|
|
18
|
+
/** Object size in bytes. */
|
|
19
|
+
size: number
|
|
20
|
+
/** Media type when known. */
|
|
21
|
+
contentType?: string
|
|
22
|
+
/** Driver-independent entity tag when available. */
|
|
23
|
+
etag?: string
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Portable image or document transform request. */
|
|
27
|
+
export interface StorageTransformOptions {
|
|
28
|
+
/** Requested output width in pixels. */
|
|
29
|
+
width?: number
|
|
30
|
+
/** Requested output height in pixels. */
|
|
31
|
+
height?: number
|
|
32
|
+
/** Requested output media subtype. */
|
|
33
|
+
format?: string
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Base file storage contract. */
|
|
37
|
+
export interface StorageDriver<
|
|
38
|
+
TCapabilities extends StorageCapabilities = StorageCapabilities,
|
|
39
|
+
TRaw = unknown,
|
|
40
|
+
> extends DriverContract<TCapabilities, TRaw> {
|
|
41
|
+
/** Stores bytes at a path and returns normalized metadata. */
|
|
42
|
+
put(
|
|
43
|
+
path: string,
|
|
44
|
+
contents: Uint8Array | AsyncIterable<Uint8Array>,
|
|
45
|
+
options?: { readonly contentType?: string },
|
|
46
|
+
): Promise<StorageObject>
|
|
47
|
+
/** Reads an object's bytes. */
|
|
48
|
+
get(path: string): Promise<Uint8Array>
|
|
49
|
+
/** Deletes an object if it exists. */
|
|
50
|
+
delete(path: string): Promise<void>
|
|
51
|
+
/** Reports whether an object exists. */
|
|
52
|
+
exists(path: string): Promise<boolean>
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Signed URL methods exposed only by drivers that support them. */
|
|
56
|
+
export interface SignedUrlStorageSurface {
|
|
57
|
+
/** Creates a temporary URL for reading a stored object. */
|
|
58
|
+
signedUrl(path: string, expiresInSeconds: number): Promise<string>
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Transform methods restricted to a driver's literal operation list. */
|
|
62
|
+
export interface TransformStorageSurface<TTransform extends StorageTransform> {
|
|
63
|
+
/** Creates a URL or derivative for one supported transform operation. */
|
|
64
|
+
transform(path: string, operation: TTransform, options: StorageTransformOptions): Promise<string>
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
type StorageTransformFor<TCapabilities extends StorageCapabilities> =
|
|
68
|
+
TCapabilities['transforms'] extends readonly []
|
|
69
|
+
? object
|
|
70
|
+
: TransformStorageSurface<CapabilityMembers<TCapabilities['transforms']>>
|
|
71
|
+
|
|
72
|
+
/** Storage facade narrowed to signed URL and literal transform capabilities. */
|
|
73
|
+
export type Storage<TDriver extends StorageDriver> = Pick<
|
|
74
|
+
TDriver,
|
|
75
|
+
'name' | 'instance' | 'capabilities' | 'put' | 'get' | 'delete' | 'exists' | 'raw'
|
|
76
|
+
> &
|
|
77
|
+
CapabilitySurface<TDriver['capabilities']['signedUrls'], SignedUrlStorageSurface> &
|
|
78
|
+
StorageTransformFor<TDriver['capabilities']>
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { DriverContract } from './common'
|
|
2
|
+
|
|
3
|
+
/** Literal API-token features used to constrain token issuance. */
|
|
4
|
+
export interface TokenCapabilities {
|
|
5
|
+
/** Whether issued tokens may carry ability restrictions. */
|
|
6
|
+
readonly abilities: boolean
|
|
7
|
+
/** Whether issued tokens may expire automatically. */
|
|
8
|
+
readonly expiration: boolean
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** Options accepted when issuing an API token. */
|
|
12
|
+
export interface TokenIssueOptions {
|
|
13
|
+
/** Abilities granted to the token. */
|
|
14
|
+
abilities?: readonly string[]
|
|
15
|
+
/** Time after which the token is rejected. */
|
|
16
|
+
expiresAt?: Date
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** API-token metadata safe to persist or display. */
|
|
20
|
+
export interface TokenRecord {
|
|
21
|
+
/** Stable token identifier. */
|
|
22
|
+
id: string
|
|
23
|
+
/** Stable application subject authenticated by the token. */
|
|
24
|
+
subject: string
|
|
25
|
+
/** User-facing token name. */
|
|
26
|
+
name: string
|
|
27
|
+
/** Granted ability names. */
|
|
28
|
+
abilities: readonly string[]
|
|
29
|
+
/** Issuance time. */
|
|
30
|
+
createdAt: Date
|
|
31
|
+
/** Expiration time, or null for a non-expiring token. */
|
|
32
|
+
expiresAt: Date | null
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** One-time plaintext token returned only at issuance. */
|
|
36
|
+
export interface IssuedToken extends TokenRecord {
|
|
37
|
+
/** Secret token value that must not be returned by later list operations. */
|
|
38
|
+
plainText: string
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** API token authentication, issuance, listing, and revocation contract. */
|
|
42
|
+
export interface TokenDriver<
|
|
43
|
+
TCapabilities extends TokenCapabilities = TokenCapabilities,
|
|
44
|
+
TRaw = unknown,
|
|
45
|
+
> extends DriverContract<TCapabilities, TRaw> {
|
|
46
|
+
/**
|
|
47
|
+
* Authenticates a plaintext token, throwing `Unauthenticated` when it is missing, revoked, or
|
|
48
|
+
* expired.
|
|
49
|
+
*/
|
|
50
|
+
verify(plainText: string): Promise<TokenRecord>
|
|
51
|
+
/** Issues a named API token. */
|
|
52
|
+
issue(name: string, options?: TokenIssueOptions): Promise<IssuedToken>
|
|
53
|
+
/** Lists metadata for the current actor's tokens. */
|
|
54
|
+
list(): Promise<readonly TokenRecord[]>
|
|
55
|
+
/** Revokes one token by its stable identifier. */
|
|
56
|
+
revoke(id: string): Promise<void>
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
type TokenOptionsFor<TCapabilities extends TokenCapabilities> =
|
|
60
|
+
(TCapabilities['abilities'] extends true ? object : { abilities?: never }) &
|
|
61
|
+
(TCapabilities['expiration'] extends true ? object : { expiresAt?: never })
|
|
62
|
+
|
|
63
|
+
/** Token facade narrowed to the configured issuance features. */
|
|
64
|
+
export interface TokenSurface<TDriver extends TokenDriver> {
|
|
65
|
+
/** Driver implementation name. */
|
|
66
|
+
readonly name: TDriver['name']
|
|
67
|
+
/** Configured token issuer name. */
|
|
68
|
+
readonly instance: TDriver['instance']
|
|
69
|
+
/** Exact token capability declaration. */
|
|
70
|
+
readonly capabilities: TDriver['capabilities']
|
|
71
|
+
/**
|
|
72
|
+
* Authenticates a plaintext token, throwing `Unauthenticated` when it is missing, revoked, or
|
|
73
|
+
* expired.
|
|
74
|
+
*/
|
|
75
|
+
verify(plainText: string): Promise<TokenRecord>
|
|
76
|
+
/** Issues a token using only options supported by the driver. */
|
|
77
|
+
issue(name: string, options?: TokenOptionsFor<TDriver['capabilities']>): Promise<IssuedToken>
|
|
78
|
+
/** Lists token metadata without plaintext secrets. */
|
|
79
|
+
list(): Promise<readonly TokenRecord[]>
|
|
80
|
+
/** Revokes one token. */
|
|
81
|
+
revoke(id: string): Promise<void>
|
|
82
|
+
/** Returns the driver's typed underlying client. */
|
|
83
|
+
raw(): ReturnType<TDriver['raw']>
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** API-token facade narrowed to its driver's exact capabilities. */
|
|
87
|
+
export type Tokens<TDriver extends TokenDriver> = TokenSurface<TDriver>
|
package/src/errors.ts
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/** Stable machine-readable codes for framework and normalized driver failures. */
|
|
2
|
+
export type ErrorCode =
|
|
3
|
+
| 'NOT_FOUND'
|
|
4
|
+
| 'CONFLICT'
|
|
5
|
+
| 'UNAUTHENTICATED'
|
|
6
|
+
| 'FORBIDDEN'
|
|
7
|
+
| 'RATE_LIMITED'
|
|
8
|
+
| 'INVALID'
|
|
9
|
+
| 'UNAVAILABLE'
|
|
10
|
+
| 'DRIVER_FAULT'
|
|
11
|
+
|
|
12
|
+
/** Constructor options shared by every framework error. */
|
|
13
|
+
export interface AvelonErrorOptions<TMetadata extends object> {
|
|
14
|
+
/** Stable framework metadata without vendor response shapes. */
|
|
15
|
+
metadata: TMetadata
|
|
16
|
+
/** Original failure retained through standard error chaining. */
|
|
17
|
+
cause?: unknown
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Base error carrying a stable code, typed metadata, and a standard cause. */
|
|
21
|
+
export class AvelonError<TMetadata extends object> extends Error {
|
|
22
|
+
/** Stable machine-readable error code. */
|
|
23
|
+
readonly code: ErrorCode
|
|
24
|
+
|
|
25
|
+
/** Framework-owned metadata safe to inspect across driver changes. */
|
|
26
|
+
readonly metadata: Readonly<TMetadata>
|
|
27
|
+
|
|
28
|
+
/** Creates an error with a fixed code and typed metadata. */
|
|
29
|
+
constructor(
|
|
30
|
+
name: string,
|
|
31
|
+
code: ErrorCode,
|
|
32
|
+
message: string,
|
|
33
|
+
options: AvelonErrorOptions<TMetadata>,
|
|
34
|
+
) {
|
|
35
|
+
super(message, { cause: options.cause })
|
|
36
|
+
this.name = name
|
|
37
|
+
this.code = code
|
|
38
|
+
this.metadata = options.metadata
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Metadata identifying a resource lookup that failed. */
|
|
43
|
+
export interface NotFoundMetadata {
|
|
44
|
+
/** Resource type that could not be found. */
|
|
45
|
+
resource: string
|
|
46
|
+
/** Requested identifier when one is safe to expose. */
|
|
47
|
+
identifier?: string | number
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Failure raised when a requested resource does not exist. */
|
|
51
|
+
export class NotFound extends AvelonError<NotFoundMetadata> {
|
|
52
|
+
/** Creates a not-found failure. */
|
|
53
|
+
constructor(message: string, options: AvelonErrorOptions<NotFoundMetadata>) {
|
|
54
|
+
super('NotFound', 'NOT_FOUND', message, options)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Metadata describing a framework-level state conflict. */
|
|
59
|
+
export interface ConflictMetadata {
|
|
60
|
+
/** Resource involved in the conflict. */
|
|
61
|
+
resource?: string
|
|
62
|
+
/** Framework-owned conflict key, never a vendor constraint shape. */
|
|
63
|
+
key?: string
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Failure raised when an operation conflicts with existing state. */
|
|
67
|
+
export class Conflict extends AvelonError<ConflictMetadata> {
|
|
68
|
+
/** Creates a conflict failure. */
|
|
69
|
+
constructor(message: string, options: AvelonErrorOptions<ConflictMetadata>) {
|
|
70
|
+
super('Conflict', 'CONFLICT', message, options)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Metadata describing the authentication boundary that rejected a request. */
|
|
75
|
+
export interface UnauthenticatedMetadata {
|
|
76
|
+
/** Authentication guard or context used by the application. */
|
|
77
|
+
guard?: string
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Failure raised when an operation requires an authenticated actor. */
|
|
81
|
+
export class Unauthenticated extends AvelonError<UnauthenticatedMetadata> {
|
|
82
|
+
/** Creates an unauthenticated failure. */
|
|
83
|
+
constructor(message: string, options: AvelonErrorOptions<UnauthenticatedMetadata>) {
|
|
84
|
+
super('Unauthenticated', 'UNAUTHENTICATED', message, options)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Metadata describing an authorization denial. */
|
|
89
|
+
export interface ForbiddenMetadata {
|
|
90
|
+
/** Ability or action that was denied. */
|
|
91
|
+
ability?: string
|
|
92
|
+
/** Resource type involved in the authorization check. */
|
|
93
|
+
resource?: string
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Failure raised when an authenticated actor lacks permission. */
|
|
97
|
+
export class Forbidden extends AvelonError<ForbiddenMetadata> {
|
|
98
|
+
/** Creates a forbidden failure. */
|
|
99
|
+
constructor(message: string, options: AvelonErrorOptions<ForbiddenMetadata>) {
|
|
100
|
+
super('Forbidden', 'FORBIDDEN', message, options)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** Metadata describing a rate-limit decision. */
|
|
105
|
+
export interface RateLimitedMetadata {
|
|
106
|
+
/** Stable application key that was limited. */
|
|
107
|
+
key?: string
|
|
108
|
+
/** Milliseconds until another attempt may succeed. */
|
|
109
|
+
retryAfterMs?: number
|
|
110
|
+
/** Limit applied during the current window. */
|
|
111
|
+
limit?: number
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** Failure raised when an operation exceeds its allowed rate. */
|
|
115
|
+
export class RateLimited extends AvelonError<RateLimitedMetadata> {
|
|
116
|
+
/** Creates a rate-limited failure. */
|
|
117
|
+
constructor(message: string, options: AvelonErrorOptions<RateLimitedMetadata>) {
|
|
118
|
+
super('RateLimited', 'RATE_LIMITED', message, options)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** Metadata describing invalid input in framework-owned terms. */
|
|
123
|
+
export interface InvalidMetadata {
|
|
124
|
+
/** Field-keyed validation messages. */
|
|
125
|
+
fields?: Readonly<Record<string, readonly string[]>>
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** Failure raised when supplied input is invalid. */
|
|
129
|
+
export class Invalid extends AvelonError<InvalidMetadata> {
|
|
130
|
+
/** Creates an invalid-input failure. */
|
|
131
|
+
constructor(message: string, options: AvelonErrorOptions<InvalidMetadata>) {
|
|
132
|
+
super('Invalid', 'INVALID', message, options)
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** Metadata describing a temporarily unavailable framework service. */
|
|
137
|
+
export interface UnavailableMetadata {
|
|
138
|
+
/** Framework service or capability that is unavailable. */
|
|
139
|
+
service?: string
|
|
140
|
+
/** Milliseconds after which retrying may succeed. */
|
|
141
|
+
retryAfterMs?: number
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** Failure raised when a required service is temporarily unavailable. */
|
|
145
|
+
export class Unavailable extends AvelonError<UnavailableMetadata> {
|
|
146
|
+
/** Creates an unavailable failure. */
|
|
147
|
+
constructor(message: string, options: AvelonErrorOptions<UnavailableMetadata>) {
|
|
148
|
+
super('Unavailable', 'UNAVAILABLE', message, options)
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** Metadata identifying a normalized driver failure. */
|
|
153
|
+
export interface DriverFaultMetadata {
|
|
154
|
+
/** Driver contract that failed, such as `database` or `mail`. */
|
|
155
|
+
driver: string
|
|
156
|
+
/** Framework operation being attempted. */
|
|
157
|
+
operation: string
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** Failure raised when a driver returns an otherwise unmapped failure. */
|
|
161
|
+
export class DriverFault extends AvelonError<DriverFaultMetadata> {
|
|
162
|
+
/** Creates a normalized driver fault. */
|
|
163
|
+
constructor(message: string, options: AvelonErrorOptions<DriverFaultMetadata>) {
|
|
164
|
+
super('DriverFault', 'DRIVER_FAULT', message, options)
|
|
165
|
+
}
|
|
166
|
+
}
|
package/src/events.ts
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/** Listener delivery strategies supported by the event dispatcher. */
|
|
2
|
+
export type DeliveryMode = 'sync' | 'after' | 'queued'
|
|
3
|
+
|
|
4
|
+
/** Base event with explicit, dispatcher-readable propagation state. */
|
|
5
|
+
export class Event {
|
|
6
|
+
#propagationStopped = false
|
|
7
|
+
|
|
8
|
+
/** Stops remaining listeners, including queued listeners not yet scheduled. */
|
|
9
|
+
stopPropagation(): void {
|
|
10
|
+
this.#propagationStopped = true
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** Reports whether a listener has stopped the current dispatch chain. */
|
|
14
|
+
isPropagationStopped(): boolean {
|
|
15
|
+
return this.#propagationStopped
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Constructable event class used when registering listeners. */
|
|
20
|
+
export interface EventConstructor<TEvent extends Event = Event> {
|
|
21
|
+
/** Creates an event instance from its declared constructor arguments. */
|
|
22
|
+
new (...args: never[]): TEvent
|
|
23
|
+
/** Runtime class name used for event discovery. */
|
|
24
|
+
readonly name: string
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Options controlling one event dispatch. */
|
|
28
|
+
export interface EventDispatchOptions {
|
|
29
|
+
/** Holds dispatch until the enclosing transaction commits and drops it on rollback. */
|
|
30
|
+
afterCommit?: boolean
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Stable reference extracted while serializing a queued event. */
|
|
34
|
+
export interface SerializedReference<TId extends string | number = string | number> {
|
|
35
|
+
/** Framework registry type used to rehydrate the reference. */
|
|
36
|
+
type: string
|
|
37
|
+
/** Identifier resolved when the queued listener runs. */
|
|
38
|
+
id: TId
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Serialized event payload crossing the queue boundary. */
|
|
42
|
+
export interface SerializedEvent {
|
|
43
|
+
/** Registered event name. */
|
|
44
|
+
name: string
|
|
45
|
+
/** Serializable event data with references replaced by tokens. */
|
|
46
|
+
payload: unknown
|
|
47
|
+
/** References rehydrated against current state in the worker. */
|
|
48
|
+
references: readonly SerializedReference[]
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Serializer boundary supplied by the model/event integration layer. */
|
|
52
|
+
export interface EventSerializer<TEvent extends Event = Event> {
|
|
53
|
+
/** Converts an event into a queue-safe payload and explicit references. */
|
|
54
|
+
serialize(event: TEvent): Promise<SerializedEvent>
|
|
55
|
+
/** Rehydrates an event and its references from a queued payload. */
|
|
56
|
+
deserialize(serialized: SerializedEvent): Promise<TEvent>
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Context passed to an inline listener. */
|
|
60
|
+
export interface SyncListenerContext {
|
|
61
|
+
/** Actual delivery mode. */
|
|
62
|
+
delivery: 'sync'
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Context passed to a best-effort post-response listener. */
|
|
66
|
+
export interface AfterListenerContext {
|
|
67
|
+
/** Actual delivery mode. */
|
|
68
|
+
delivery: 'after'
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Worker context for a durable listener; actors must be carried explicitly on the event. */
|
|
72
|
+
export interface QueuedListenerContext {
|
|
73
|
+
/** Actual delivery mode. */
|
|
74
|
+
delivery: 'queued'
|
|
75
|
+
/** Named queue processing the listener. */
|
|
76
|
+
queue: string
|
|
77
|
+
/** One-based execution attempt. */
|
|
78
|
+
attempt: number
|
|
79
|
+
/** Maximum attempts before the payload moves to failed storage. */
|
|
80
|
+
tries: number
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Delivery-specific listener context with no implicit request or actor. */
|
|
84
|
+
export type ListenerContext<TDelivery extends DeliveryMode = DeliveryMode> =
|
|
85
|
+
TDelivery extends 'sync'
|
|
86
|
+
? SyncListenerContext
|
|
87
|
+
: TDelivery extends 'after'
|
|
88
|
+
? AfterListenerContext
|
|
89
|
+
: QueuedListenerContext
|
|
90
|
+
|
|
91
|
+
/** Result accepted from a listener; false stops propagation. */
|
|
92
|
+
export type ListenerResult<TResult = unknown> = TResult | false | null | void
|
|
93
|
+
|
|
94
|
+
type QueueOptions<TDelivery extends DeliveryMode> = TDelivery extends 'queued'
|
|
95
|
+
? {
|
|
96
|
+
queue: string
|
|
97
|
+
tries: number
|
|
98
|
+
backoff: number | readonly number[]
|
|
99
|
+
}
|
|
100
|
+
: {
|
|
101
|
+
queue?: never
|
|
102
|
+
tries?: never
|
|
103
|
+
backoff?: never
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
type DeliveryOption<TDelivery extends DeliveryMode> = TDelivery extends 'sync'
|
|
107
|
+
? { delivery?: 'sync' }
|
|
108
|
+
: { delivery: TDelivery }
|
|
109
|
+
|
|
110
|
+
/** Declarative listener contract with delivery-specific retry and queue settings. */
|
|
111
|
+
export type ListenerDefinition<
|
|
112
|
+
TEvent extends Event,
|
|
113
|
+
TResult = unknown,
|
|
114
|
+
TDelivery extends DeliveryMode = DeliveryMode,
|
|
115
|
+
> = {
|
|
116
|
+
/** Ascending execution priority. Lower values run first. */
|
|
117
|
+
priority?: number
|
|
118
|
+
/** Handles the event in a context determined by its actual delivery mode. */
|
|
119
|
+
handle(
|
|
120
|
+
event: TEvent,
|
|
121
|
+
context: ListenerContext<TDelivery>,
|
|
122
|
+
): ListenerResult<TResult> | Promise<ListenerResult<TResult>>
|
|
123
|
+
} & DeliveryOption<TDelivery> &
|
|
124
|
+
QueueOptions<TDelivery>
|
|
125
|
+
|
|
126
|
+
/** Creates a listener definition while preserving literal delivery and retry information. */
|
|
127
|
+
export function defineListener<
|
|
128
|
+
TEvent extends Event,
|
|
129
|
+
TResult = unknown,
|
|
130
|
+
const TDelivery extends DeliveryMode = 'sync',
|
|
131
|
+
>(
|
|
132
|
+
definition: ListenerDefinition<TEvent, TResult, TDelivery>,
|
|
133
|
+
): ListenerDefinition<TEvent, TResult, TDelivery> {
|
|
134
|
+
return definition
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** Registration joining an event class to one listener definition. */
|
|
138
|
+
export interface ListenerRegistration<TEvent extends Event = Event> {
|
|
139
|
+
/** Event class this registration handles. */
|
|
140
|
+
event: EventConstructor<TEvent>
|
|
141
|
+
/** Listener invoked for that event. */
|
|
142
|
+
listener: ListenerDefinition<TEvent>
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** Multi-event subscriber contract registered through a provider. */
|
|
146
|
+
export interface EventSubscriber {
|
|
147
|
+
/** Returns every event/listener pair owned by the subscriber. */
|
|
148
|
+
listeners(): readonly ListenerRegistration[]
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** Durable queue envelope with explicit execution context and no implicit actor. */
|
|
152
|
+
export interface QueuedEventEnvelope {
|
|
153
|
+
/** Serialized event data. */
|
|
154
|
+
event: SerializedEvent
|
|
155
|
+
/** Stable listener registration name. */
|
|
156
|
+
listener: string
|
|
157
|
+
/** Named queue receiving the event. */
|
|
158
|
+
queue: string
|
|
159
|
+
/** One-based attempt number. */
|
|
160
|
+
attempt: number
|
|
161
|
+
/** Earliest epoch time in milliseconds at which the listener may run. */
|
|
162
|
+
availableAt: number
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/** Event bus contract supporting ordered, post-response, queued, and result-seeking dispatch. */
|
|
166
|
+
export interface EventDispatcher {
|
|
167
|
+
/** Registers one or more listeners for a typed event class. */
|
|
168
|
+
listen<TEvent extends Event>(
|
|
169
|
+
event: EventConstructor<TEvent>,
|
|
170
|
+
listeners: ListenerDefinition<TEvent> | readonly ListenerDefinition<TEvent>[],
|
|
171
|
+
): void
|
|
172
|
+
/** Registers one listener for an event-name pattern such as `post.*`. */
|
|
173
|
+
listen(pattern: string, listener: ListenerDefinition<Event>): void
|
|
174
|
+
/** Registers every listener exposed by a multi-event subscriber. */
|
|
175
|
+
subscribe(subscriber: EventSubscriber): void
|
|
176
|
+
/** Runs synchronous listeners in priority order before scheduling later delivery modes. */
|
|
177
|
+
dispatch<TEvent extends Event>(event: TEvent, options?: EventDispatchOptions): Promise<void>
|
|
178
|
+
/** Runs every listener after the response regardless of its declared mode. */
|
|
179
|
+
dispatchAfterResponse<TEvent extends Event>(event: TEvent): Promise<void>
|
|
180
|
+
/** Returns the first non-null listener result and stops dispatching. */
|
|
181
|
+
until<TResult, TEvent extends Event>(
|
|
182
|
+
event: TEvent,
|
|
183
|
+
options?: EventDispatchOptions,
|
|
184
|
+
): Promise<TResult | null>
|
|
185
|
+
}
|