eventsourcingdb 1.10.0 → 2.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/README.md +198 -24
- package/dist/Client.d.ts +2 -1
- package/dist/Client.js +79 -439
- package/dist/DbApiError.d.ts +17 -0
- package/dist/DbApiError.js +24 -0
- package/dist/ErrorCode.d.ts +68 -0
- package/dist/ErrorCode.js +70 -0
- package/dist/EventType.d.ts +2 -2
- package/dist/OutcomeUnknownError.d.ts +6 -0
- package/dist/OutcomeUnknownError.js +9 -0
- package/dist/ReadSubjectsOptions.d.ts +4 -0
- package/dist/Transport.d.ts +25 -0
- package/dist/detectApiVersion.d.ts +7 -0
- package/dist/detectApiVersion.js +47 -0
- package/dist/http/assertEventSourcingDb.d.ts +2 -0
- package/dist/http/assertEventSourcingDb.js +14 -0
- package/dist/http/createDbApiErrorFromResponse.d.ts +4 -0
- package/dist/http/createDbApiErrorFromResponse.js +20 -0
- package/dist/http/createStreamEndedError.d.ts +2 -0
- package/dist/http/createStreamEndedError.js +6 -0
- package/dist/http/isAbortError.d.ts +2 -0
- package/dist/http/isAbortError.js +2 -0
- package/dist/http/parseRetryAfter.d.ts +2 -0
- package/dist/http/parseRetryAfter.js +14 -0
- package/dist/http/runWrite.d.ts +6 -0
- package/dist/http/runWrite.js +25 -0
- package/dist/http/trackSending.d.ts +7 -0
- package/dist/http/trackSending.js +46 -0
- package/dist/http/waitUnlessAborted.d.ts +2 -0
- package/dist/http/waitUnlessAborted.js +21 -0
- package/dist/index.d.ts +7 -3
- package/dist/index.js +4 -2
- package/dist/stall/StallGuard.d.ts +0 -1
- package/dist/stall/StallGuard.js +3 -9
- package/dist/stall/streamWithStallGuard.d.ts +6 -0
- package/dist/stall/streamWithStallGuard.js +25 -0
- package/dist/v1/V1Transport.d.ts +23 -0
- package/dist/v1/V1Transport.js +346 -0
- package/dist/v1/getV1ErrorCode.d.ts +4 -0
- package/dist/v1/getV1ErrorCode.js +65 -0
- package/dist/v1/isV1Refusal.d.ts +3 -0
- package/dist/v1/isV1Refusal.js +8 -0
- package/dist/v1/normalizeSubject.d.ts +2 -0
- package/dist/v1/normalizeSubject.js +6 -0
- package/dist/v1/stream/StreamRow.js +1 -0
- package/dist/v1/stream/StreamSubject.js +1 -0
- package/dist/{stream → v1/stream}/isStreamCloudEvent.js +5 -5
- package/dist/{stream → v1/stream}/isStreamError.js +2 -2
- package/dist/{stream → v1/stream}/isStreamEventType.js +3 -3
- package/dist/{stream → v1/stream}/isStreamHeartbeat.js +1 -1
- package/dist/{stream → v1/stream}/isStreamRow.js +1 -1
- package/dist/{stream → v1/stream}/isStreamSubject.js +2 -2
- package/dist/v2/V2Event.d.ts +16 -0
- package/dist/v2/V2Event.js +1 -0
- package/dist/v2/V2Item.d.ts +6 -0
- package/dist/v2/V2Item.js +1 -0
- package/dist/v2/V2Transport.d.ts +23 -0
- package/dist/v2/V2Transport.js +302 -0
- package/dist/v2/convertV2Item.d.ts +6 -0
- package/dist/v2/convertV2Item.js +52 -0
- package/dist/v2/isV2Refusal.d.ts +3 -0
- package/dist/v2/isV2Refusal.js +12 -0
- package/dist/v2/parseV2Item.d.ts +3 -0
- package/dist/v2/parseV2Item.js +30 -0
- package/dist/v2/readV2Answer.d.ts +4 -0
- package/dist/v2/readV2Answer.js +14 -0
- package/dist/v2/readV2Stream.d.ts +8 -0
- package/dist/v2/readV2Stream.js +34 -0
- package/dist/v2/toV2RequestBody.d.ts +8 -0
- package/dist/v2/toV2RequestBody.js +46 -0
- package/package.json +1 -1
- package/dist/HeartbeatTimeoutError.d.ts +0 -4
- package/dist/HeartbeatTimeoutError.js +0 -9
- /package/dist/{stream/StreamCloudEvent.js → ReadSubjectsOptions.js} +0 -0
- /package/dist/{stream/StreamError.js → Transport.js} +0 -0
- /package/dist/{stream → v1/stream}/StreamCloudEvent.d.ts +0 -0
- /package/dist/{stream/StreamEventType.js → v1/stream/StreamCloudEvent.js} +0 -0
- /package/dist/{stream → v1/stream}/StreamError.d.ts +0 -0
- /package/dist/{stream/StreamHeartbeat.js → v1/stream/StreamError.js} +0 -0
- /package/dist/{stream → v1/stream}/StreamEventType.d.ts +0 -0
- /package/dist/{stream/StreamRow.js → v1/stream/StreamEventType.js} +0 -0
- /package/dist/{stream → v1/stream}/StreamHeartbeat.d.ts +0 -0
- /package/dist/{stream/StreamSubject.js → v1/stream/StreamHeartbeat.js} +0 -0
- /package/dist/{stream → v1/stream}/StreamRow.d.ts +0 -0
- /package/dist/{stream → v1/stream}/StreamSubject.d.ts +0 -0
- /package/dist/{stream → v1/stream}/isStreamCloudEvent.d.ts +0 -0
- /package/dist/{stream → v1/stream}/isStreamError.d.ts +0 -0
- /package/dist/{stream → v1/stream}/isStreamEventType.d.ts +0 -0
- /package/dist/{stream → v1/stream}/isStreamHeartbeat.d.ts +0 -0
- /package/dist/{stream → v1/stream}/isStreamRow.d.ts +0 -0
- /package/dist/{stream → v1/stream}/isStreamSubject.d.ts +0 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
declare const ErrorCode: {
|
|
2
|
+
readonly subjectMissing: 'https://docs.eventsourcingdb.io/errors/subject-missing';
|
|
3
|
+
readonly subjectMalformed: 'https://docs.eventsourcingdb.io/errors/subject-malformed';
|
|
4
|
+
readonly orderUnknown: 'https://docs.eventsourcingdb.io/errors/order-unknown';
|
|
5
|
+
readonly eventIdMissing: 'https://docs.eventsourcingdb.io/errors/event-id-missing';
|
|
6
|
+
readonly eventIdMalformed: 'https://docs.eventsourcingdb.io/errors/event-id-malformed';
|
|
7
|
+
readonly boundTypeMissing: 'https://docs.eventsourcingdb.io/errors/bound-type-missing';
|
|
8
|
+
readonly boundTypeUnknown: 'https://docs.eventsourcingdb.io/errors/bound-type-unknown';
|
|
9
|
+
readonly rangeUnsatisfiable: 'https://docs.eventsourcingdb.io/errors/range-unsatisfiable';
|
|
10
|
+
readonly fromLatestEventIncomplete: 'https://docs.eventsourcingdb.io/errors/from-latest-event-incomplete';
|
|
11
|
+
readonly ifEventIsMissingUnknown: 'https://docs.eventsourcingdb.io/errors/if-event-is-missing-unknown';
|
|
12
|
+
readonly selectionConflicting: 'https://docs.eventsourcingdb.io/errors/selection-conflicting';
|
|
13
|
+
readonly eventTypeMissing: 'https://docs.eventsourcingdb.io/errors/event-type-missing';
|
|
14
|
+
readonly eventTypeMalformed: 'https://docs.eventsourcingdb.io/errors/event-type-malformed';
|
|
15
|
+
readonly eventTypeUnknown: 'https://docs.eventsourcingdb.io/errors/event-type-unknown';
|
|
16
|
+
readonly sourceMissing: 'https://docs.eventsourcingdb.io/errors/source-missing';
|
|
17
|
+
readonly sourceMalformed: 'https://docs.eventsourcingdb.io/errors/source-malformed';
|
|
18
|
+
readonly dataMissing: 'https://docs.eventsourcingdb.io/errors/data-missing';
|
|
19
|
+
readonly traceparentMalformed: 'https://docs.eventsourcingdb.io/errors/traceparent-malformed';
|
|
20
|
+
readonly tracestateMalformed: 'https://docs.eventsourcingdb.io/errors/tracestate-malformed';
|
|
21
|
+
readonly tracestateWithoutTraceparent: 'https://docs.eventsourcingdb.io/errors/tracestate-without-traceparent';
|
|
22
|
+
readonly eventsMissing: 'https://docs.eventsourcingdb.io/errors/events-missing';
|
|
23
|
+
readonly preconditionTypeMissing: 'https://docs.eventsourcingdb.io/errors/precondition-type-missing';
|
|
24
|
+
readonly preconditionUnknown: 'https://docs.eventsourcingdb.io/errors/precondition-unknown';
|
|
25
|
+
readonly payloadMissing: 'https://docs.eventsourcingdb.io/errors/payload-missing';
|
|
26
|
+
readonly licenseInvalid: 'https://docs.eventsourcingdb.io/errors/license-invalid';
|
|
27
|
+
readonly capacityExceeded: 'https://docs.eventsourcingdb.io/errors/capacity-exceeded';
|
|
28
|
+
readonly eventSchemaViolated: 'https://docs.eventsourcingdb.io/errors/event-schema-violated';
|
|
29
|
+
readonly eventSchemaUnregistered: 'https://docs.eventsourcingdb.io/errors/event-schema-unregistered';
|
|
30
|
+
readonly eventSchemaMissing: 'https://docs.eventsourcingdb.io/errors/event-schema-missing';
|
|
31
|
+
readonly eventSchemaMalformed: 'https://docs.eventsourcingdb.io/errors/event-schema-malformed';
|
|
32
|
+
readonly eventSchemaNotObject: 'https://docs.eventsourcingdb.io/errors/event-schema-not-object';
|
|
33
|
+
readonly eventSchemaAlreadyRegistered: 'https://docs.eventsourcingdb.io/errors/event-schema-already-registered';
|
|
34
|
+
readonly lastAuditedEventMissing: 'https://docs.eventsourcingdb.io/errors/last-audited-event-missing';
|
|
35
|
+
readonly lastAuditedEventMismatched: 'https://docs.eventsourcingdb.io/errors/last-audited-event-mismatched';
|
|
36
|
+
readonly lastAuditedEventUnknown: 'https://docs.eventsourcingdb.io/errors/last-audited-event-unknown';
|
|
37
|
+
readonly eventHashMissing: 'https://docs.eventsourcingdb.io/errors/event-hash-missing';
|
|
38
|
+
readonly eventHashMalformed: 'https://docs.eventsourcingdb.io/errors/event-hash-malformed';
|
|
39
|
+
readonly eventChainBroken: 'https://docs.eventsourcingdb.io/errors/event-chain-broken';
|
|
40
|
+
readonly lastBackedUpEventMissing: 'https://docs.eventsourcingdb.io/errors/last-backed-up-event-missing';
|
|
41
|
+
readonly lastBackedUpEventMismatched: 'https://docs.eventsourcingdb.io/errors/last-backed-up-event-mismatched';
|
|
42
|
+
readonly lastBackedUpEventUnknown: 'https://docs.eventsourcingdb.io/errors/last-backed-up-event-unknown';
|
|
43
|
+
readonly storeNotEmpty: 'https://docs.eventsourcingdb.io/errors/store-not-empty';
|
|
44
|
+
readonly eventHashVersionUnknown: 'https://docs.eventsourcingdb.io/errors/event-hash-version-unknown';
|
|
45
|
+
readonly backupMalformed: 'https://docs.eventsourcingdb.io/errors/backup-malformed';
|
|
46
|
+
readonly backupIncomplete: 'https://docs.eventsourcingdb.io/errors/backup-incomplete';
|
|
47
|
+
readonly backupFormatUnknown: 'https://docs.eventsourcingdb.io/errors/backup-format-unknown';
|
|
48
|
+
readonly fullBackupMissing: 'https://docs.eventsourcingdb.io/errors/full-backup-missing';
|
|
49
|
+
readonly backupIncrementDisconnected: 'https://docs.eventsourcingdb.io/errors/backup-increment-disconnected';
|
|
50
|
+
readonly backupFailed: 'https://docs.eventsourcingdb.io/errors/backup-failed';
|
|
51
|
+
readonly backupUnreadable: 'https://docs.eventsourcingdb.io/errors/backup-unreadable';
|
|
52
|
+
readonly eventTooLarge: 'https://docs.eventsourcingdb.io/errors/event-too-large';
|
|
53
|
+
readonly dataMalformed: 'https://docs.eventsourcingdb.io/errors/data-malformed';
|
|
54
|
+
readonly queryMissing: 'https://docs.eventsourcingdb.io/errors/query-missing';
|
|
55
|
+
readonly queryMalformed: 'https://docs.eventsourcingdb.io/errors/query-malformed';
|
|
56
|
+
readonly queryUnevaluable: 'https://docs.eventsourcingdb.io/errors/query-unevaluable';
|
|
57
|
+
readonly queryNotBoolean: 'https://docs.eventsourcingdb.io/errors/query-not-boolean';
|
|
58
|
+
readonly preconditionViolated: 'https://docs.eventsourcingdb.io/errors/precondition-violated';
|
|
59
|
+
readonly storeReadOnly: 'https://docs.eventsourcingdb.io/errors/store-read-only';
|
|
60
|
+
readonly readOnlyModeAlreadyEnabled: 'https://docs.eventsourcingdb.io/errors/read-only-mode-already-enabled';
|
|
61
|
+
readonly diskFull: 'https://docs.eventsourcingdb.io/errors/disk-full';
|
|
62
|
+
readonly restoreInProgress: 'https://docs.eventsourcingdb.io/errors/restore-in-progress';
|
|
63
|
+
readonly storeFailed: 'https://docs.eventsourcingdb.io/errors/store-failed';
|
|
64
|
+
readonly indexesRebuilding: 'https://docs.eventsourcingdb.io/errors/indexes-rebuilding';
|
|
65
|
+
readonly serverShuttingDown: 'https://docs.eventsourcingdb.io/errors/server-shutting-down';
|
|
66
|
+
};
|
|
67
|
+
type ErrorCode = (typeof ErrorCode)[keyof typeof ErrorCode];
|
|
68
|
+
export { ErrorCode };
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// The codes of the errors that the server reports. A code is the URL of the
|
|
2
|
+
// page that documents the error, exactly as the server sends it, so a caller
|
|
3
|
+
// can tell errors apart by comparing the code of a DbApiError with these.
|
|
4
|
+
const ErrorCode = {
|
|
5
|
+
subjectMissing: 'https://docs.eventsourcingdb.io/errors/subject-missing',
|
|
6
|
+
subjectMalformed: 'https://docs.eventsourcingdb.io/errors/subject-malformed',
|
|
7
|
+
orderUnknown: 'https://docs.eventsourcingdb.io/errors/order-unknown',
|
|
8
|
+
eventIdMissing: 'https://docs.eventsourcingdb.io/errors/event-id-missing',
|
|
9
|
+
eventIdMalformed: 'https://docs.eventsourcingdb.io/errors/event-id-malformed',
|
|
10
|
+
boundTypeMissing: 'https://docs.eventsourcingdb.io/errors/bound-type-missing',
|
|
11
|
+
boundTypeUnknown: 'https://docs.eventsourcingdb.io/errors/bound-type-unknown',
|
|
12
|
+
rangeUnsatisfiable: 'https://docs.eventsourcingdb.io/errors/range-unsatisfiable',
|
|
13
|
+
fromLatestEventIncomplete: 'https://docs.eventsourcingdb.io/errors/from-latest-event-incomplete',
|
|
14
|
+
ifEventIsMissingUnknown: 'https://docs.eventsourcingdb.io/errors/if-event-is-missing-unknown',
|
|
15
|
+
selectionConflicting: 'https://docs.eventsourcingdb.io/errors/selection-conflicting',
|
|
16
|
+
eventTypeMissing: 'https://docs.eventsourcingdb.io/errors/event-type-missing',
|
|
17
|
+
eventTypeMalformed: 'https://docs.eventsourcingdb.io/errors/event-type-malformed',
|
|
18
|
+
eventTypeUnknown: 'https://docs.eventsourcingdb.io/errors/event-type-unknown',
|
|
19
|
+
sourceMissing: 'https://docs.eventsourcingdb.io/errors/source-missing',
|
|
20
|
+
sourceMalformed: 'https://docs.eventsourcingdb.io/errors/source-malformed',
|
|
21
|
+
dataMissing: 'https://docs.eventsourcingdb.io/errors/data-missing',
|
|
22
|
+
traceparentMalformed: 'https://docs.eventsourcingdb.io/errors/traceparent-malformed',
|
|
23
|
+
tracestateMalformed: 'https://docs.eventsourcingdb.io/errors/tracestate-malformed',
|
|
24
|
+
tracestateWithoutTraceparent: 'https://docs.eventsourcingdb.io/errors/tracestate-without-traceparent',
|
|
25
|
+
eventsMissing: 'https://docs.eventsourcingdb.io/errors/events-missing',
|
|
26
|
+
preconditionTypeMissing: 'https://docs.eventsourcingdb.io/errors/precondition-type-missing',
|
|
27
|
+
preconditionUnknown: 'https://docs.eventsourcingdb.io/errors/precondition-unknown',
|
|
28
|
+
payloadMissing: 'https://docs.eventsourcingdb.io/errors/payload-missing',
|
|
29
|
+
licenseInvalid: 'https://docs.eventsourcingdb.io/errors/license-invalid',
|
|
30
|
+
capacityExceeded: 'https://docs.eventsourcingdb.io/errors/capacity-exceeded',
|
|
31
|
+
eventSchemaViolated: 'https://docs.eventsourcingdb.io/errors/event-schema-violated',
|
|
32
|
+
eventSchemaUnregistered: 'https://docs.eventsourcingdb.io/errors/event-schema-unregistered',
|
|
33
|
+
eventSchemaMissing: 'https://docs.eventsourcingdb.io/errors/event-schema-missing',
|
|
34
|
+
eventSchemaMalformed: 'https://docs.eventsourcingdb.io/errors/event-schema-malformed',
|
|
35
|
+
eventSchemaNotObject: 'https://docs.eventsourcingdb.io/errors/event-schema-not-object',
|
|
36
|
+
eventSchemaAlreadyRegistered: 'https://docs.eventsourcingdb.io/errors/event-schema-already-registered',
|
|
37
|
+
lastAuditedEventMissing: 'https://docs.eventsourcingdb.io/errors/last-audited-event-missing',
|
|
38
|
+
lastAuditedEventMismatched: 'https://docs.eventsourcingdb.io/errors/last-audited-event-mismatched',
|
|
39
|
+
lastAuditedEventUnknown: 'https://docs.eventsourcingdb.io/errors/last-audited-event-unknown',
|
|
40
|
+
eventHashMissing: 'https://docs.eventsourcingdb.io/errors/event-hash-missing',
|
|
41
|
+
eventHashMalformed: 'https://docs.eventsourcingdb.io/errors/event-hash-malformed',
|
|
42
|
+
eventChainBroken: 'https://docs.eventsourcingdb.io/errors/event-chain-broken',
|
|
43
|
+
lastBackedUpEventMissing: 'https://docs.eventsourcingdb.io/errors/last-backed-up-event-missing',
|
|
44
|
+
lastBackedUpEventMismatched: 'https://docs.eventsourcingdb.io/errors/last-backed-up-event-mismatched',
|
|
45
|
+
lastBackedUpEventUnknown: 'https://docs.eventsourcingdb.io/errors/last-backed-up-event-unknown',
|
|
46
|
+
storeNotEmpty: 'https://docs.eventsourcingdb.io/errors/store-not-empty',
|
|
47
|
+
eventHashVersionUnknown: 'https://docs.eventsourcingdb.io/errors/event-hash-version-unknown',
|
|
48
|
+
backupMalformed: 'https://docs.eventsourcingdb.io/errors/backup-malformed',
|
|
49
|
+
backupIncomplete: 'https://docs.eventsourcingdb.io/errors/backup-incomplete',
|
|
50
|
+
backupFormatUnknown: 'https://docs.eventsourcingdb.io/errors/backup-format-unknown',
|
|
51
|
+
fullBackupMissing: 'https://docs.eventsourcingdb.io/errors/full-backup-missing',
|
|
52
|
+
backupIncrementDisconnected: 'https://docs.eventsourcingdb.io/errors/backup-increment-disconnected',
|
|
53
|
+
backupFailed: 'https://docs.eventsourcingdb.io/errors/backup-failed',
|
|
54
|
+
backupUnreadable: 'https://docs.eventsourcingdb.io/errors/backup-unreadable',
|
|
55
|
+
eventTooLarge: 'https://docs.eventsourcingdb.io/errors/event-too-large',
|
|
56
|
+
dataMalformed: 'https://docs.eventsourcingdb.io/errors/data-malformed',
|
|
57
|
+
queryMissing: 'https://docs.eventsourcingdb.io/errors/query-missing',
|
|
58
|
+
queryMalformed: 'https://docs.eventsourcingdb.io/errors/query-malformed',
|
|
59
|
+
queryUnevaluable: 'https://docs.eventsourcingdb.io/errors/query-unevaluable',
|
|
60
|
+
queryNotBoolean: 'https://docs.eventsourcingdb.io/errors/query-not-boolean',
|
|
61
|
+
preconditionViolated: 'https://docs.eventsourcingdb.io/errors/precondition-violated',
|
|
62
|
+
storeReadOnly: 'https://docs.eventsourcingdb.io/errors/store-read-only',
|
|
63
|
+
readOnlyModeAlreadyEnabled: 'https://docs.eventsourcingdb.io/errors/read-only-mode-already-enabled',
|
|
64
|
+
diskFull: 'https://docs.eventsourcingdb.io/errors/disk-full',
|
|
65
|
+
restoreInProgress: 'https://docs.eventsourcingdb.io/errors/restore-in-progress',
|
|
66
|
+
storeFailed: 'https://docs.eventsourcingdb.io/errors/store-failed',
|
|
67
|
+
indexesRebuilding: 'https://docs.eventsourcingdb.io/errors/indexes-rebuilding',
|
|
68
|
+
serverShuttingDown: 'https://docs.eventsourcingdb.io/errors/server-shutting-down',
|
|
69
|
+
};
|
|
70
|
+
export { ErrorCode };
|
package/dist/EventType.d.ts
CHANGED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// A write that failed once it may have reached the server, so it may have been
|
|
2
|
+
// stored nonetheless. Its cause tells what went wrong.
|
|
3
|
+
class OutcomeUnknownError extends Error {
|
|
4
|
+
constructor({ cause }) {
|
|
5
|
+
super('The write may or may not have been stored.', { cause });
|
|
6
|
+
this.name = 'OutcomeUnknownError';
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export { OutcomeUnknownError };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Event } from './Event.js';
|
|
2
|
+
import type { EventCandidate } from './EventCandidate.js';
|
|
3
|
+
import type { EventType } from './EventType.js';
|
|
4
|
+
import type { ObserveEventsOptions } from './ObserveEventsOptions.js';
|
|
5
|
+
import type { Precondition } from './Precondition.js';
|
|
6
|
+
import type { ReadEventsOptions } from './ReadEventsOptions.js';
|
|
7
|
+
import type { ReadSubjectsOptions } from './ReadSubjectsOptions.js';
|
|
8
|
+
interface Transport {
|
|
9
|
+
ping: (signal?: AbortSignal) => Promise<void>;
|
|
10
|
+
verifyApiToken: (signal?: AbortSignal) => Promise<void>;
|
|
11
|
+
writeEvents: (events: EventCandidate[], preconditions: Precondition[], signal?: AbortSignal) => Promise<Event[]>;
|
|
12
|
+
readEvents: (subject: string, options: ReadEventsOptions, signal?: AbortSignal) => AsyncGenerator<Event, void, void>;
|
|
13
|
+
runEventQlQuery: (query: string, signal?: AbortSignal) => AsyncGenerator<unknown, void, void>;
|
|
14
|
+
observeEvents: (subject: string, options: ObserveEventsOptions, signal?: AbortSignal) => AsyncGenerator<Event, void, void>;
|
|
15
|
+
registerEventSchema: (eventType: string, schema: Record<string, unknown>, signal?: AbortSignal) => Promise<void>;
|
|
16
|
+
readSubjects: (subject: string, options: ReadSubjectsOptions, signal?: AbortSignal) => AsyncGenerator<string, void, void>;
|
|
17
|
+
readEventType: (eventType: string, signal?: AbortSignal) => Promise<EventType>;
|
|
18
|
+
readEventTypes: (signal?: AbortSignal) => AsyncGenerator<EventType, void, void>;
|
|
19
|
+
}
|
|
20
|
+
interface TransportOptions {
|
|
21
|
+
url: URL;
|
|
22
|
+
apiToken: string;
|
|
23
|
+
stallTimeout: number;
|
|
24
|
+
}
|
|
25
|
+
export type { Transport, TransportOptions };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { assertEventSourcingDb } from './http/assertEventSourcingDb.js';
|
|
2
|
+
import { createDbApiErrorFromResponse } from './http/createDbApiErrorFromResponse.js';
|
|
3
|
+
import { readResponseText } from './stall/readResponseText.js';
|
|
4
|
+
import { runWithStallGuard } from './stall/runWithStallGuard.js';
|
|
5
|
+
import { hasShapeOf } from './types/hasShapeOf.js';
|
|
6
|
+
import { isString } from './types/isString.js';
|
|
7
|
+
// An answer that is not JSON is not a ping, so it parses as null.
|
|
8
|
+
const parseJson = (text) => {
|
|
9
|
+
try {
|
|
10
|
+
return JSON.parse(text);
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
const statusNotFound = 404;
|
|
17
|
+
const statusMethodNotAllowed = 405;
|
|
18
|
+
// Finds out whether the server offers the v2 API, by pinging it there. A
|
|
19
|
+
// server that offers only the v1 API, as EventSourcingDB 1.2.0 does, answers
|
|
20
|
+
// a path of the v2 API with the status 404 or 405. Every other answer than
|
|
21
|
+
// these and a ping of the v2 API fails, so that the next request asks again.
|
|
22
|
+
const detectApiVersion = async ({ url, stallTimeout, }) => await runWithStallGuard({ stallTimeout }, async (stallGuard) => {
|
|
23
|
+
const response = await stallGuard.send(new URL('/api/v2/system/ping', url).toString(), {
|
|
24
|
+
method: 'GET',
|
|
25
|
+
});
|
|
26
|
+
if (response.status === statusNotFound || response.status === statusMethodNotAllowed) {
|
|
27
|
+
await readResponseText(response, stallGuard);
|
|
28
|
+
return 'v1';
|
|
29
|
+
}
|
|
30
|
+
assertEventSourcingDb(response);
|
|
31
|
+
if (response.status !== 200) {
|
|
32
|
+
throw await createDbApiErrorFromResponse(response, stallGuard);
|
|
33
|
+
}
|
|
34
|
+
const responseBody = parseJson(await readResponseText(response, stallGuard));
|
|
35
|
+
if (!hasShapeOf(responseBody, {
|
|
36
|
+
type: isString,
|
|
37
|
+
data: {
|
|
38
|
+
type: isString,
|
|
39
|
+
},
|
|
40
|
+
}) ||
|
|
41
|
+
responseBody.type !== 'event' ||
|
|
42
|
+
responseBody.data.type !== 'io.eventsourcingdb.api.ping-received') {
|
|
43
|
+
throw new Error('Failed to find out which API the server offers.');
|
|
44
|
+
}
|
|
45
|
+
return 'v2';
|
|
46
|
+
});
|
|
47
|
+
export { detectApiVersion };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { isValidServerHeader } from '../isValidServerHeader.js';
|
|
2
|
+
// Fails unless the response comes from EventSourcingDB, which names itself in
|
|
3
|
+
// the Server header.
|
|
4
|
+
const assertEventSourcingDb = (response) => {
|
|
5
|
+
if (isValidServerHeader(response)) {
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
const serverHeader = response.headers.get('Server');
|
|
9
|
+
if (!serverHeader) {
|
|
10
|
+
throw new Error('Server must be EventSourcingDB, but Server header is missing.');
|
|
11
|
+
}
|
|
12
|
+
throw new Error(`Server must be EventSourcingDB, got Server header: '${serverHeader}'.`);
|
|
13
|
+
};
|
|
14
|
+
export { assertEventSourcingDb };
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { DbApiError } from '../DbApiError.js';
|
|
2
|
+
import type { StallGuard } from '../stall/StallGuard.js';
|
|
3
|
+
declare const createDbApiErrorFromResponse: (response: Response, stallGuard: StallGuard, getCode?: (status: number, description: string) => string | undefined) => Promise<DbApiError>;
|
|
4
|
+
export { createDbApiErrorFromResponse };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { DbApiError } from '../DbApiError.js';
|
|
2
|
+
import { readResponseText } from '../stall/readResponseText.js';
|
|
3
|
+
import { parseRetryAfter } from './parseRetryAfter.js';
|
|
4
|
+
const statusTooManyRequests = 429;
|
|
5
|
+
// Creates the error for a response with a status other than 200, whose body
|
|
6
|
+
// is a text that describes the error. The text ends with a line break, which
|
|
7
|
+
// is not part of the description.
|
|
8
|
+
const createDbApiErrorFromResponse = async (response, stallGuard, getCode) => {
|
|
9
|
+
const { status } = response;
|
|
10
|
+
const description = (await readResponseText(response, stallGuard)).trim();
|
|
11
|
+
return new DbApiError({
|
|
12
|
+
status,
|
|
13
|
+
description,
|
|
14
|
+
code: getCode?.(status, description),
|
|
15
|
+
retryAfter: status === statusTooManyRequests
|
|
16
|
+
? parseRetryAfter(response.headers.get('Retry-After'))
|
|
17
|
+
: undefined,
|
|
18
|
+
});
|
|
19
|
+
};
|
|
20
|
+
export { createDbApiErrorFromResponse };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// Creates the error for a stream that the server closed before it was done: a
|
|
2
|
+
// finite stream that ends before its end item, or a stream of observed events,
|
|
3
|
+
// which never ends on its own. Either broke off because of a failure that the
|
|
4
|
+
// server did not report.
|
|
5
|
+
const createStreamEndedError = (action) => new Error(`Failed to ${action}, since the stream ended unexpectedly.`);
|
|
6
|
+
export { createStreamEndedError };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const secondsRegex = /^\d+$/u;
|
|
2
|
+
// Returns the seconds that a Retry-After header names, or undefined if there
|
|
3
|
+
// is no such header or it does not name seconds.
|
|
4
|
+
const parseRetryAfter = (header) => {
|
|
5
|
+
if (header === null) {
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
const value = header.trim();
|
|
9
|
+
if (!secondsRegex.test(value)) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
return Number(value);
|
|
13
|
+
};
|
|
14
|
+
export { parseRetryAfter };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { DbApiError } from '../DbApiError.js';
|
|
2
|
+
import { OutcomeUnknownError } from '../OutcomeUnknownError.js';
|
|
3
|
+
import { trackSending } from './trackSending.js';
|
|
4
|
+
// Runs a write that has not been sent yet. A write certainly was not stored if
|
|
5
|
+
// it failed before its request went out, whether the caller aborted it, it
|
|
6
|
+
// stalled, or it could not establish a connection, or if the server refused
|
|
7
|
+
// it, which isRefusal tells for an error that the server reports. Every other
|
|
8
|
+
// failure may come after the server stored the write, so it fails with an
|
|
9
|
+
// OutcomeUnknownError that wraps it.
|
|
10
|
+
const runWrite = async ({ signal, isRefusal }, write) => {
|
|
11
|
+
signal?.throwIfAborted();
|
|
12
|
+
const outcome = await trackSending(write);
|
|
13
|
+
if ('result' in outcome) {
|
|
14
|
+
return outcome.result;
|
|
15
|
+
}
|
|
16
|
+
const { error, hasBeenSent } = outcome;
|
|
17
|
+
if (!hasBeenSent) {
|
|
18
|
+
throw error;
|
|
19
|
+
}
|
|
20
|
+
if (error instanceof DbApiError && isRefusal(error)) {
|
|
21
|
+
throw error;
|
|
22
|
+
}
|
|
23
|
+
throw new OutcomeUnknownError({ cause: error });
|
|
24
|
+
};
|
|
25
|
+
export { runWrite };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
|
+
import diagnosticsChannel from 'node:diagnostics_channel';
|
|
3
|
+
const currentSending = new AsyncLocalStorage();
|
|
4
|
+
const sendingByRequest = new WeakMap();
|
|
5
|
+
const getRequest = (message) => {
|
|
6
|
+
if (typeof message !== 'object' || message === null || !('request' in message)) {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
const { request } = message;
|
|
10
|
+
if (typeof request !== 'object' || request === null) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
return request;
|
|
14
|
+
};
|
|
15
|
+
// The fetch built into Node.js creates its request within the call of fetch,
|
|
16
|
+
// so the request belongs to the write that runs there.
|
|
17
|
+
diagnosticsChannel.subscribe('undici:request:create', (message) => {
|
|
18
|
+
const sending = currentSending.getStore();
|
|
19
|
+
const request = getRequest(message);
|
|
20
|
+
if (sending === undefined || request === undefined || sending.state !== 'unknown') {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
sending.state = 'created';
|
|
24
|
+
sendingByRequest.set(request, sending);
|
|
25
|
+
});
|
|
26
|
+
diagnosticsChannel.subscribe('undici:client:sendHeaders', (message) => {
|
|
27
|
+
const request = getRequest(message);
|
|
28
|
+
const sending = request === undefined ? undefined : sendingByRequest.get(request);
|
|
29
|
+
if (sending !== undefined) {
|
|
30
|
+
sending.state = 'sent';
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
// Runs a write and tells, if it fails, whether its request certainly did not
|
|
34
|
+
// reach the server, since it was created but its headers never went out. If
|
|
35
|
+
// the client cannot tell, for example since fetch is not the one built into
|
|
36
|
+
// Node.js, the request counts as sent.
|
|
37
|
+
const trackSending = async (write) => {
|
|
38
|
+
const sending = { state: 'unknown' };
|
|
39
|
+
try {
|
|
40
|
+
return { result: await currentSending.run(sending, write) };
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
return { error, hasBeenSent: sending.state !== 'created' };
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
export { trackSending };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Waits for a promise that other callers may share, unless the signal of this
|
|
2
|
+
// caller aborts first, which then rejects with its reason, while the promise
|
|
3
|
+
// goes on for the others.
|
|
4
|
+
const waitUnlessAborted = async (promise, signal) => {
|
|
5
|
+
if (signal === undefined) {
|
|
6
|
+
return await promise;
|
|
7
|
+
}
|
|
8
|
+
signal.throwIfAborted();
|
|
9
|
+
const aborted = Promise.withResolvers();
|
|
10
|
+
const onAbort = () => {
|
|
11
|
+
aborted.reject(signal.reason);
|
|
12
|
+
};
|
|
13
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
14
|
+
try {
|
|
15
|
+
return await Promise.race([promise, aborted.promise]);
|
|
16
|
+
}
|
|
17
|
+
finally {
|
|
18
|
+
signal.removeEventListener('abort', onAbort);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
export { waitUnlessAborted };
|
package/dist/index.d.ts
CHANGED
|
@@ -2,19 +2,23 @@ import { Client } from './Client.js';
|
|
|
2
2
|
import type { ClientOptions } from './ClientOptions.js';
|
|
3
3
|
import { ConnectionStalledError } from './ConnectionStalledError.js';
|
|
4
4
|
import { Container } from './Container.js';
|
|
5
|
+
import type { DbApiErrorOptions } from './DbApiError.js';
|
|
6
|
+
import { DbApiError } from './DbApiError.js';
|
|
7
|
+
import { ErrorCode } from './ErrorCode.js';
|
|
5
8
|
import type { Event } from './Event.js';
|
|
6
9
|
import type { EventCandidate } from './EventCandidate.js';
|
|
7
10
|
import type { EventType } from './EventType.js';
|
|
8
11
|
import { HashVerificationFailedError } from './HashVerificationFailedError.js';
|
|
9
|
-
import { HeartbeatTimeoutError } from './HeartbeatTimeoutError.js';
|
|
10
12
|
import { isEventQlQueryTrue } from './isEventQlQueryTrue.js';
|
|
11
13
|
import { isSubjectOnEventId } from './isSubjectOnEventId.js';
|
|
12
14
|
import { isSubjectPopulated } from './isSubjectPopulated.js';
|
|
13
15
|
import { isSubjectPristine } from './isSubjectPristine.js';
|
|
14
16
|
import type { ObserveEventsOptions } from './ObserveEventsOptions.js';
|
|
17
|
+
import { OutcomeUnknownError } from './OutcomeUnknownError.js';
|
|
15
18
|
import type { Precondition } from './Precondition.js';
|
|
16
19
|
import type { ReadEventsOptions } from './ReadEventsOptions.js';
|
|
20
|
+
import type { ReadSubjectsOptions } from './ReadSubjectsOptions.js';
|
|
17
21
|
import { SignatureVerificationFailedError } from './SignatureVerificationFailedError.js';
|
|
18
22
|
import { UnsupportedHashVersionError } from './UnsupportedHashVersionError.js';
|
|
19
|
-
export type { ClientOptions, Event, EventCandidate, EventType, ObserveEventsOptions, Precondition, ReadEventsOptions, };
|
|
20
|
-
export { Client, ConnectionStalledError, Container,
|
|
23
|
+
export type { ClientOptions, DbApiErrorOptions, Event, EventCandidate, EventType, ObserveEventsOptions, Precondition, ReadEventsOptions, ReadSubjectsOptions, };
|
|
24
|
+
export { Client, ConnectionStalledError, Container, DbApiError, ErrorCode, HashVerificationFailedError, isEventQlQueryTrue, isSubjectOnEventId, isSubjectPopulated, isSubjectPristine, OutcomeUnknownError, SignatureVerificationFailedError, UnsupportedHashVersionError, };
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { Client } from './Client.js';
|
|
2
2
|
import { ConnectionStalledError } from './ConnectionStalledError.js';
|
|
3
3
|
import { Container } from './Container.js';
|
|
4
|
+
import { DbApiError } from './DbApiError.js';
|
|
5
|
+
import { ErrorCode } from './ErrorCode.js';
|
|
4
6
|
import { HashVerificationFailedError } from './HashVerificationFailedError.js';
|
|
5
|
-
import { HeartbeatTimeoutError } from './HeartbeatTimeoutError.js';
|
|
6
7
|
import { isEventQlQueryTrue } from './isEventQlQueryTrue.js';
|
|
7
8
|
import { isSubjectOnEventId } from './isSubjectOnEventId.js';
|
|
8
9
|
import { isSubjectPopulated } from './isSubjectPopulated.js';
|
|
9
10
|
import { isSubjectPristine } from './isSubjectPristine.js';
|
|
11
|
+
import { OutcomeUnknownError } from './OutcomeUnknownError.js';
|
|
10
12
|
import { SignatureVerificationFailedError } from './SignatureVerificationFailedError.js';
|
|
11
13
|
import { UnsupportedHashVersionError } from './UnsupportedHashVersionError.js';
|
|
12
|
-
export { Client, ConnectionStalledError, Container,
|
|
14
|
+
export { Client, ConnectionStalledError, Container, DbApiError, ErrorCode, HashVerificationFailedError, isEventQlQueryTrue, isSubjectOnEventId, isSubjectPopulated, isSubjectPristine, OutcomeUnknownError, SignatureVerificationFailedError, UnsupportedHashVersionError, };
|
|
@@ -3,7 +3,6 @@ declare class StallGuard {
|
|
|
3
3
|
constructor(stallTimeout: number, callerSignal?: AbortSignal);
|
|
4
4
|
get signal(): AbortSignal;
|
|
5
5
|
send(url: string, init: RequestInit): Promise<Response>;
|
|
6
|
-
onStall(createStallError: () => Error): void;
|
|
7
6
|
restart(): void;
|
|
8
7
|
pause(): void;
|
|
9
8
|
throwIfStalled(): void;
|
package/dist/stall/StallGuard.js
CHANGED
|
@@ -9,7 +9,6 @@ class StallGuard {
|
|
|
9
9
|
#stallTimeout;
|
|
10
10
|
#controller = new AbortController();
|
|
11
11
|
#callerSignal;
|
|
12
|
-
#createStallError;
|
|
13
12
|
#phase = 'headers';
|
|
14
13
|
#stallError;
|
|
15
14
|
#timer;
|
|
@@ -19,9 +18,6 @@ class StallGuard {
|
|
|
19
18
|
constructor(stallTimeout, callerSignal) {
|
|
20
19
|
this.#stallTimeout = stallTimeout;
|
|
21
20
|
this.#callerSignal = callerSignal;
|
|
22
|
-
this.#createStallError = () => new ConnectionStalledError(stallTimeout, {
|
|
23
|
-
cause: createFetchTimeoutError(this.#phase),
|
|
24
|
-
});
|
|
25
21
|
if (callerSignal?.aborted) {
|
|
26
22
|
this.#onCallerAbort();
|
|
27
23
|
}
|
|
@@ -42,10 +38,6 @@ class StallGuard {
|
|
|
42
38
|
this.restart();
|
|
43
39
|
return response;
|
|
44
40
|
}
|
|
45
|
-
// From now on, a stall ends the request with the error that this creates.
|
|
46
|
-
onStall(createStallError) {
|
|
47
|
-
this.#createStallError = createStallError;
|
|
48
|
-
}
|
|
49
41
|
// Starts the clock, or starts it anew, since something arrived.
|
|
50
42
|
restart() {
|
|
51
43
|
if (this.#controller.signal.aborted) {
|
|
@@ -56,7 +48,9 @@ class StallGuard {
|
|
|
56
48
|
this.pause();
|
|
57
49
|
this.#timer = setTimeout(() => {
|
|
58
50
|
this.#timer = undefined;
|
|
59
|
-
this.#stallError = this.#
|
|
51
|
+
this.#stallError = new ConnectionStalledError(this.#stallTimeout, {
|
|
52
|
+
cause: createFetchTimeoutError(this.#phase),
|
|
53
|
+
});
|
|
60
54
|
this.#controller.abort(this.#stallError);
|
|
61
55
|
}, this.#stallTimeout);
|
|
62
56
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { StallGuard } from './StallGuard.js';
|
|
2
|
+
declare const streamWithStallGuard: <TItem>({ stallTimeout, signal }: {
|
|
3
|
+
stallTimeout: number;
|
|
4
|
+
signal?: AbortSignal;
|
|
5
|
+
}, stream: (stallGuard: StallGuard) => AsyncGenerator<TItem, void, void>) => AsyncGenerator<TItem, void, void>;
|
|
6
|
+
export { streamWithStallGuard };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { isAbortError } from '../http/isAbortError.js';
|
|
2
|
+
import { StallGuard } from './StallGuard.js';
|
|
3
|
+
// Runs a request that is a stream under a stall guard. If the request stalls,
|
|
4
|
+
// the stream fails with the error of the stall, whatever fetch throws then. If
|
|
5
|
+
// the caller aborts, the stream ends without an error. Ending the stream early
|
|
6
|
+
// closes the connection of the request.
|
|
7
|
+
const streamWithStallGuard = async function* ({ stallTimeout, signal }, stream) {
|
|
8
|
+
const stallGuard = new StallGuard(stallTimeout, signal);
|
|
9
|
+
try {
|
|
10
|
+
stallGuard.restart();
|
|
11
|
+
yield* stream(stallGuard);
|
|
12
|
+
stallGuard.throwIfStalled();
|
|
13
|
+
}
|
|
14
|
+
catch (error) {
|
|
15
|
+
stallGuard.throwIfStalled();
|
|
16
|
+
if (isAbortError(error)) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
throw error;
|
|
20
|
+
}
|
|
21
|
+
finally {
|
|
22
|
+
stallGuard.dispose({ abortRequest: true });
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
export { streamWithStallGuard };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Event } from '../Event.js';
|
|
2
|
+
import type { EventCandidate } from '../EventCandidate.js';
|
|
3
|
+
import type { EventType } from '../EventType.js';
|
|
4
|
+
import type { ObserveEventsOptions } from '../ObserveEventsOptions.js';
|
|
5
|
+
import type { Precondition } from '../Precondition.js';
|
|
6
|
+
import type { ReadEventsOptions } from '../ReadEventsOptions.js';
|
|
7
|
+
import type { ReadSubjectsOptions } from '../ReadSubjectsOptions.js';
|
|
8
|
+
import type { Transport, TransportOptions } from '../Transport.js';
|
|
9
|
+
declare class V1Transport implements Transport {
|
|
10
|
+
#private;
|
|
11
|
+
constructor({ url, apiToken, stallTimeout }: TransportOptions);
|
|
12
|
+
ping(signal?: AbortSignal): Promise<void>;
|
|
13
|
+
verifyApiToken(signal?: AbortSignal): Promise<void>;
|
|
14
|
+
writeEvents(events: EventCandidate[], preconditions: Precondition[], signal?: AbortSignal): Promise<Event[]>;
|
|
15
|
+
readEvents(subject: string, options: ReadEventsOptions, signal?: AbortSignal): AsyncGenerator<Event, void, void>;
|
|
16
|
+
runEventQlQuery(query: string, signal?: AbortSignal): AsyncGenerator<unknown, void, void>;
|
|
17
|
+
observeEvents(subject: string, options: ObserveEventsOptions, signal?: AbortSignal): AsyncGenerator<Event, void, void>;
|
|
18
|
+
registerEventSchema(eventType: string, schema: Record<string, unknown>, signal?: AbortSignal): Promise<void>;
|
|
19
|
+
readSubjects(subject: string, { recursive }: ReadSubjectsOptions, signal?: AbortSignal): AsyncGenerator<string, void, void>;
|
|
20
|
+
readEventType(eventType: string, signal?: AbortSignal): Promise<EventType>;
|
|
21
|
+
readEventTypes(signal?: AbortSignal): AsyncGenerator<EventType, void, void>;
|
|
22
|
+
}
|
|
23
|
+
export { V1Transport };
|