@elysiumjs/moirai 0.0.0-stage → 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/CHANGELOG.md +53 -0
- package/README.md +171 -2
- package/dist/action.d.ts +211 -0
- package/dist/config.d.ts +58 -0
- package/dist/errors.d.ts +209 -0
- package/dist/event.d.ts +97 -0
- package/dist/hash.d.ts +7 -0
- package/dist/heracles.d.ts +43 -0
- package/dist/heracles.js +1 -0
- package/dist/ids.d.ts +7 -0
- package/dist/index-2t45ppfs.js +2 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +1 -0
- package/dist/moirai.module.d.ts +55 -0
- package/dist/outbox.d.ts +100 -0
- package/dist/stores.d.ts +155 -0
- package/package.json +79 -4
package/dist/stores.d.ts
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What an idempotency store keeps of an action call: its response, replayed when the key comes
|
|
3
|
+
* back with the same request.
|
|
4
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
5
|
+
*/
|
|
6
|
+
export interface IdempotencyRecord {
|
|
7
|
+
key: string;
|
|
8
|
+
action: string;
|
|
9
|
+
/** The fingerprint of the input (see `requestHash`). */
|
|
10
|
+
requestHash: string;
|
|
11
|
+
/** The JSON-safe response of the action. */
|
|
12
|
+
response: unknown;
|
|
13
|
+
createdAt: Date;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Where idempotent actions keep their responses. `put` runs in the action's unit of work, and
|
|
17
|
+
* throws {@link IdempotencyConflictError} when another call stored the key first.
|
|
18
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
19
|
+
*/
|
|
20
|
+
export interface IdempotencyStore {
|
|
21
|
+
get(key: string): Promise<IdempotencyRecord | undefined>;
|
|
22
|
+
put(record: IdempotencyRecord): Promise<void>;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Serializes the actions that share a key (`serializeBy`): `lock` waits for the other holders,
|
|
26
|
+
* and the lock lasts until the unit of work ends, or until the returned release runs.
|
|
27
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
28
|
+
*/
|
|
29
|
+
export interface ActionLock {
|
|
30
|
+
lock(key: string): Promise<(() => void) | void>;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* A side effect an action asks for. It is written to the outbox in the action's unit of work,
|
|
34
|
+
* so it happens if and only if the action commits.
|
|
35
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
36
|
+
*/
|
|
37
|
+
export interface OutboxEffect {
|
|
38
|
+
/** The kind, which picks the `@Outbox.handle()` handler. */
|
|
39
|
+
kind: string;
|
|
40
|
+
/** The JSON-safe payload given to the handler. */
|
|
41
|
+
payload: unknown;
|
|
42
|
+
/** When the effect may run. Defaults to now. */
|
|
43
|
+
availableAt?: Date;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* A row of the outbox.
|
|
47
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
48
|
+
*/
|
|
49
|
+
export interface OutboxRow {
|
|
50
|
+
id: string;
|
|
51
|
+
kind: string;
|
|
52
|
+
payload: unknown;
|
|
53
|
+
createdAt: Date;
|
|
54
|
+
availableAt: Date;
|
|
55
|
+
attempts: number;
|
|
56
|
+
lastError: string | null;
|
|
57
|
+
dispatchedAt: Date | null;
|
|
58
|
+
deadAt: Date | null;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Narrows the rows a relay claims. Both fields must match when both are set; an empty list
|
|
62
|
+
* matches nothing.
|
|
63
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
64
|
+
*/
|
|
65
|
+
export interface OutboxClaimFilter {
|
|
66
|
+
kinds?: readonly string[];
|
|
67
|
+
ids?: readonly string[];
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Claims due outbox rows for one relay.
|
|
71
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
72
|
+
*/
|
|
73
|
+
export interface OutboxClaim extends OutboxClaimFilter {
|
|
74
|
+
now: Date;
|
|
75
|
+
/** The claimed rows become due again at this date, when the relay dies before marking them. */
|
|
76
|
+
leaseUntil: Date;
|
|
77
|
+
limit: number;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Where the outbox rows live. `claimDue` takes the rows that are neither dispatched nor dead and
|
|
81
|
+
* whose `availableAt` has passed, oldest first, and moves their `availableAt` to `leaseUntil` in
|
|
82
|
+
* the same statement, so that other relays leave them alone while this one handles them. Each
|
|
83
|
+
* row is then handled and marked in a transaction of its own.
|
|
84
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
85
|
+
*/
|
|
86
|
+
export interface OutboxStore {
|
|
87
|
+
insert(rows: readonly OutboxRow[]): Promise<void>;
|
|
88
|
+
claimDue(claim: OutboxClaim): Promise<readonly OutboxRow[]>;
|
|
89
|
+
markDispatched(id: string, dispatchedAt: Date): Promise<void>;
|
|
90
|
+
markFailed(id: string, update: {
|
|
91
|
+
attempts: number;
|
|
92
|
+
lastError: string;
|
|
93
|
+
availableAt: Date;
|
|
94
|
+
}): Promise<void>;
|
|
95
|
+
markDead(id: string, update: {
|
|
96
|
+
attempts: number;
|
|
97
|
+
lastError: string;
|
|
98
|
+
deadAt: Date;
|
|
99
|
+
}): Promise<void>;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Where consumers remember the messages they processed. `record` answers `true` when the pair of
|
|
103
|
+
* the message and the consumer is new.
|
|
104
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
105
|
+
*/
|
|
106
|
+
export interface InboxStore {
|
|
107
|
+
record(messageId: string, consumer: string): Promise<boolean>;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Keeps idempotency records in memory, for tests and single-process apps.
|
|
111
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
112
|
+
*/
|
|
113
|
+
export declare class MemoryIdempotencyStore implements IdempotencyStore {
|
|
114
|
+
#private;
|
|
115
|
+
get(key: string): Promise<IdempotencyRecord | undefined>;
|
|
116
|
+
put(record: IdempotencyRecord): Promise<void>;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Serializes actions in the process, one holder per key at a time.
|
|
120
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
121
|
+
*/
|
|
122
|
+
export declare class MemoryActionLock implements ActionLock {
|
|
123
|
+
#private;
|
|
124
|
+
lock(key: string): Promise<() => void>;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Keeps outbox rows in memory, for tests and single-process apps.
|
|
128
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
129
|
+
*/
|
|
130
|
+
export declare class MemoryOutboxStore implements OutboxStore {
|
|
131
|
+
#private;
|
|
132
|
+
/** Every row, for assertions. */
|
|
133
|
+
get rows(): OutboxRow[];
|
|
134
|
+
insert(rows: readonly OutboxRow[]): Promise<void>;
|
|
135
|
+
claimDue(claim: OutboxClaim): Promise<readonly OutboxRow[]>;
|
|
136
|
+
markDispatched(id: string, dispatchedAt: Date): Promise<void>;
|
|
137
|
+
markFailed(id: string, update: {
|
|
138
|
+
attempts: number;
|
|
139
|
+
lastError: string;
|
|
140
|
+
availableAt: Date;
|
|
141
|
+
}): Promise<void>;
|
|
142
|
+
markDead(id: string, update: {
|
|
143
|
+
attempts: number;
|
|
144
|
+
lastError: string;
|
|
145
|
+
deadAt: Date;
|
|
146
|
+
}): Promise<void>;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Keeps inbox records in memory, for tests and single-process apps.
|
|
150
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
151
|
+
*/
|
|
152
|
+
export declare class MemoryInboxStore implements InboxStore {
|
|
153
|
+
#private;
|
|
154
|
+
record(messageId: string, consumer: string): Promise<boolean>;
|
|
155
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,81 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elysiumjs/moirai",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"description": "
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"license": "Apache-2.0",
|
|
5
|
+
"description": "Actions, queries, idempotency, outbox, inbox and domain events for the Elysium.js framework.",
|
|
6
|
+
"private": false,
|
|
7
|
+
"author": {
|
|
8
|
+
"name": "Axel Nana",
|
|
9
|
+
"email": "axel.nana@workbud.com"
|
|
10
|
+
},
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "git+https://github.com/na2axl/elysiumjs.git",
|
|
14
|
+
"directory": "@elysiumjs/moirai"
|
|
15
|
+
},
|
|
16
|
+
"homepage": "https://github.com/na2axl/elysiumjs/tree/main/@elysiumjs/moirai#readme",
|
|
17
|
+
"bugs": {
|
|
18
|
+
"url": "https://github.com/na2axl/elysiumjs/issues"
|
|
19
|
+
},
|
|
20
|
+
"scripts": {
|
|
21
|
+
"format": "prettier --write \"./src/**/*.ts\"",
|
|
22
|
+
"clean": "rimraf dist",
|
|
23
|
+
"build:js": "bun build ./src/index.ts ./src/heracles.ts --outdir ./dist --target browser --packages external --splitting --root ./src --minify-syntax --minify-whitespace",
|
|
24
|
+
"build:ts": "tsc",
|
|
25
|
+
"build": "bun run clean && bun run build:js && bun run build:ts",
|
|
26
|
+
"typecheck": "tsc --noEmit",
|
|
27
|
+
"prepublishOnly": "bun run build",
|
|
28
|
+
"test": "bun test",
|
|
29
|
+
"test:coverage": "bun test --coverage"
|
|
30
|
+
},
|
|
31
|
+
"keywords": [
|
|
32
|
+
"elysium",
|
|
33
|
+
"framework",
|
|
34
|
+
"typescript",
|
|
35
|
+
"cqrs",
|
|
36
|
+
"commands",
|
|
37
|
+
"idempotency",
|
|
38
|
+
"outbox"
|
|
39
|
+
],
|
|
40
|
+
"type": "module",
|
|
41
|
+
"module": "dist/index.js",
|
|
42
|
+
"types": "dist/index.d.ts",
|
|
43
|
+
"exports": {
|
|
44
|
+
".": {
|
|
45
|
+
"types": "./dist/index.d.ts",
|
|
46
|
+
"import": "./dist/index.js"
|
|
47
|
+
},
|
|
48
|
+
"./heracles": {
|
|
49
|
+
"types": "./dist/heracles.d.ts",
|
|
50
|
+
"import": "./dist/heracles.js"
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
"files": [
|
|
54
|
+
"dist",
|
|
55
|
+
"CHANGELOG.md"
|
|
56
|
+
],
|
|
57
|
+
"sideEffects": [
|
|
58
|
+
"./dist/heracles.js",
|
|
59
|
+
"./src/heracles.ts"
|
|
60
|
+
],
|
|
61
|
+
"dependencies": {
|
|
62
|
+
"typebox": "^1.3.0"
|
|
63
|
+
},
|
|
64
|
+
"devDependencies": {
|
|
65
|
+
"@elysiumjs/core": "0.11.0",
|
|
66
|
+
"@elysiumjs/heracles": "0.8.0",
|
|
67
|
+
"@elysiumjs/mnemosyne": "0.4.0",
|
|
68
|
+
"@elysiumjs/redis": "0.1.0",
|
|
69
|
+
"elysia": "^2.0.0-rc.1"
|
|
70
|
+
},
|
|
71
|
+
"peerDependencies": {
|
|
72
|
+
"@elysiumjs/core": "^0.11.0",
|
|
73
|
+
"@elysiumjs/heracles": "^0.8.0",
|
|
74
|
+
"@elysiumjs/mnemosyne": "^0.4.0"
|
|
75
|
+
},
|
|
76
|
+
"peerDependenciesMeta": {
|
|
77
|
+
"@elysiumjs/heracles": {
|
|
78
|
+
"optional": true
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|