@dogmatiq/protobuf 0.4.1 → 0.5.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/README.md CHANGED
@@ -30,12 +30,12 @@ import "github.com/dogmatiq/protobuf/uuidpb/uuid.proto";
30
30
 
31
31
  ## Packages
32
32
 
33
- | Package | Protocol Buffers package | Contents |
34
- | --------------- | ------------------------ | ------------------------------------------ |
35
- | `uuidpb` | `dogma.protobuf.v1` | `UUID` |
36
- | `identitypb` | `dogma.protobuf.v1` | `Identity` |
37
- | `envelopepb` | `dogma.protobuf.v1` | `Envelope`, `MultiEnvelope` and extensions |
38
- | `messaginggrpc` | `dogma.messaging.v1` | The event stream consumer API |
33
+ | Package | Protocol Buffers package | Contents |
34
+ | --------------- | ------------------------ | ----------------------------- |
35
+ | `uuidpb` | `dogma.protobuf.v1` | `UUID` |
36
+ | `identitypb` | `dogma.protobuf.v1` | `Identity` |
37
+ | `envelopepb` | `dogma.protobuf.v1` | `Envelope` and extensions |
38
+ | `messaginggrpc` | `dogma.messaging.v1` | The event stream consumer API |
39
39
 
40
40
  ## Message data
41
41
 
@@ -1,28 +1,4 @@
1
- import { type Body, type Envelope, type Header, type Message, type MultiEnvelope } from "./envelope_pb.js";
2
- /**
3
- * Returns each message in a {@link MultiEnvelope} as a standalone
4
- * {@link Envelope}.
5
- *
6
- * The returned envelopes share the multi-envelope's header.
7
- */
8
- export declare function expandMultiEnvelope(x: MultiEnvelope): Envelope[];
9
- /**
10
- * Returns a {@link MultiEnvelope} that contains the same header and body as
11
- * env.
12
- *
13
- * The multi-envelope shares the envelope's header and body.
14
- *
15
- * @throws {Error} If env has no body.
16
- */
17
- export declare function asMultiEnvelope(env: Envelope): MultiEnvelope;
18
- /**
19
- * Appends the body of env to x if they have equivalent headers.
20
- *
21
- * It returns false, and does not modify x, if the headers differ.
22
- *
23
- * @throws {Error} If env has no body.
24
- */
25
- export declare function tryAppendEnvelope(x: MultiEnvelope, env: Envelope): boolean;
1
+ import type { Body, Envelope, Header, Message } from "./envelope_pb.js";
26
2
  /**
27
3
  * Throws an error if env is not well-formed.
28
4
  *
@@ -35,12 +11,6 @@ export declare function tryAppendEnvelope(x: MultiEnvelope, env: Envelope): bool
35
11
  * data can be decoded.
36
12
  */
37
13
  export declare function validateEnvelope(env: Envelope): void;
38
- /**
39
- * Throws an error if x is not well-formed.
40
- *
41
- * See {@link validateEnvelope} for what well-formedness means.
42
- */
43
- export declare function validateMultiEnvelope(x: MultiEnvelope): void;
44
14
  /**
45
15
  * Throws an error if x is not well-formed.
46
16
  *
@@ -1,55 +1,8 @@
1
- import { create, createRegistry, equals } from "@bufbuild/protobuf";
1
+ import { create } from "@bufbuild/protobuf";
2
2
  import { IdentitySchema } from "../identitypb/identity_pb.js";
3
3
  import { identityToString, validateIdentity } from "../identitypb/identity.js";
4
4
  import { UUIDSchema } from "../uuidpb/uuid_pb.js";
5
5
  import { uuidToString, validateUUID } from "../uuidpb/uuid.js";
6
- import { EnvelopeSchema, HeaderSchema, MultiEnvelopeSchema, } from "./envelope_pb.js";
7
- /**
8
- * Returns each message in a {@link MultiEnvelope} as a standalone
9
- * {@link Envelope}.
10
- *
11
- * The returned envelopes share the multi-envelope's header.
12
- */
13
- export function expandMultiEnvelope(x) {
14
- return x.bodies.map((body) => create(EnvelopeSchema, { header: x.header, body }));
15
- }
16
- /**
17
- * Returns a {@link MultiEnvelope} that contains the same header and body as
18
- * env.
19
- *
20
- * The multi-envelope shares the envelope's header and body.
21
- *
22
- * @throws {Error} If env has no body.
23
- */
24
- export function asMultiEnvelope(env) {
25
- if (!env.body) {
26
- throw new Error("envelope must have a body");
27
- }
28
- return create(MultiEnvelopeSchema, {
29
- header: env.header,
30
- bodies: [env.body],
31
- });
32
- }
33
- /**
34
- * Appends the body of env to x if they have equivalent headers.
35
- *
36
- * It returns false, and does not modify x, if the headers differ.
37
- *
38
- * @throws {Error} If env has no body.
39
- */
40
- export function tryAppendEnvelope(x, env) {
41
- if (!env.body) {
42
- throw new Error("envelope must have a body");
43
- }
44
- const a = x.header;
45
- const b = env.header;
46
- const same = a && b ? headersEqual(a, b) : a === b;
47
- if (!same) {
48
- return false;
49
- }
50
- x.bodies.push(env.body);
51
- return true;
52
- }
53
6
  /**
54
7
  * Throws an error if env is not well-formed.
55
8
  *
@@ -65,15 +18,6 @@ export function validateEnvelope(env) {
65
18
  check("invalid header", () => validateHeader(env.header));
66
19
  check("invalid body", () => validateBody(env.body, env.header));
67
20
  }
68
- /**
69
- * Throws an error if x is not well-formed.
70
- *
71
- * See {@link validateEnvelope} for what well-formedness means.
72
- */
73
- export function validateMultiEnvelope(x) {
74
- check("invalid header", () => validateHeader(x.header));
75
- x.bodies.forEach((body, i) => check(`invalid body at index ${i}`, () => validateBody(body, x.header)));
76
- }
77
21
  /**
78
22
  * Throws an error if x is not well-formed.
79
23
  *
@@ -196,20 +140,3 @@ function check(prefix, fn) {
196
140
  throw new Error(`${prefix}: ${cause.message}`, { cause });
197
141
  }
198
142
  }
199
- // headersEqual returns true if a and b are equivalent, including any unknown
200
- // fields, as Go's proto.Equal() does. The equals() function from protobuf-es
201
- // ignores unknown fields unless the "unknown" option is set.
202
- //
203
- // Unlike proto.Equal(), it compares unknown fields in the order they were
204
- // decoded, rather than grouped by field number, so headers whose unknown fields
205
- // arrived in a different order are not equal.
206
- //
207
- // The "unknown" option is marked as experimental by protobuf-es, so it may
208
- // change in a later release. A test covers it, so that such a change is
209
- // noticed.
210
- function headersEqual(a, b) {
211
- return equals(HeaderSchema, a, b, { registry: REGISTRY, unknown: true });
212
- }
213
- // REGISTRY is the registry passed to equals(). It's empty because Header has no
214
- // extensions, so every unknown field is genuinely unknown.
215
- const REGISTRY = createRegistry();
@@ -10,8 +10,6 @@ export declare const file_github_com_dogmatiq_protobuf_envelopepb_envelope: GenF
10
10
  /**
11
11
  * Envelope is a container for a Dogma message and its meta-data.
12
12
  *
13
- * It is wire-compatible with [MultiEnvelope].
14
- *
15
13
  * @generated from message dogma.protobuf.v1.Envelope
16
14
  */
17
15
  export type Envelope = Message$1<"dogma.protobuf.v1.Envelope"> & {
@@ -29,28 +27,6 @@ export type Envelope = Message$1<"dogma.protobuf.v1.Envelope"> & {
29
27
  * Use `create(EnvelopeSchema)` to create a new message.
30
28
  */
31
29
  export declare const EnvelopeSchema: GenMessage<Envelope>;
32
- /**
33
- * MultiEnvelope is an efficient encoding of an ordered sequence of [Envelope]
34
- * values that share common header context.
35
- *
36
- *
37
- * @generated from message dogma.protobuf.v1.MultiEnvelope
38
- */
39
- export type MultiEnvelope = Message$1<"dogma.protobuf.v1.MultiEnvelope"> & {
40
- /**
41
- * @generated from field: dogma.protobuf.v1.Header header = 1;
42
- */
43
- header?: Header | undefined;
44
- /**
45
- * @generated from field: repeated dogma.protobuf.v1.Body bodies = 2;
46
- */
47
- bodies: Body[];
48
- };
49
- /**
50
- * Describes the message dogma.protobuf.v1.MultiEnvelope.
51
- * Use `create(MultiEnvelopeSchema)` to create a new message.
52
- */
53
- export declare const MultiEnvelopeSchema: GenMessage<MultiEnvelope>;
54
30
  /**
55
31
  * Source identifies where a message originated.
56
32
  *
@@ -140,12 +116,18 @@ export type Message = Message$1<"dogma.protobuf.v1.Message"> & {
140
116
  */
141
117
  export declare const MessageSchema: GenMessage<Message>;
142
118
  /**
119
+ * Header contains origin and causal context that can be common to messages
120
+ * produced by the same cause.
121
+ *
122
+ * Extensions and baggage from the header and body are combined by message
123
+ * type. Body values take precedence over header values of the same type.
124
+ *
143
125
  * @generated from message dogma.protobuf.v1.Header
144
126
  */
145
127
  export type Header = Message$1<"dogma.protobuf.v1.Header"> & {
146
128
  /**
147
129
  * CausationId is the ID of the message that was the direct cause of the
148
- * messages.
130
+ * message.
149
131
  *
150
132
  * It is required. A message that was not caused by any other message is its
151
133
  * own cause, so its causation ID is its own message ID.
@@ -154,9 +136,9 @@ export type Header = Message$1<"dogma.protobuf.v1.Header"> & {
154
136
  */
155
137
  causationId?: UUID | undefined;
156
138
  /**
157
- * CorrelationId is the ID of the first ancestor of the messages that was
139
+ * CorrelationId is the ID of the first ancestor of the message that was
158
140
  * itself not caused by another message, that is, it's the root of the message
159
- * chain that ultimately resulted in these messages.
141
+ * chain that ultimately resulted in this message.
160
142
  *
161
143
  * It is required. A message that was not caused by any other message is the
162
144
  * root of its own chain, so its correlation ID is its own message ID.
@@ -165,14 +147,14 @@ export type Header = Message$1<"dogma.protobuf.v1.Header"> & {
165
147
  */
166
148
  correlationId?: UUID | undefined;
167
149
  /**
168
- * Source identifies the origin of the messages.
150
+ * Source identifies the origin of the message.
169
151
  *
170
152
  * @generated from field: dogma.protobuf.v1.Source source = 3;
171
153
  */
172
154
  source?: Source | undefined;
173
155
  /**
174
- * Extensions is a set of extension values associated with the messages,
175
- * their causal chain, or the envelope itself.
156
+ * Extensions is a set of extension values associated with the message,
157
+ * its causal chain, or the envelope itself.
176
158
  *
177
159
  * @generated from field: repeated google.protobuf.Any extensions = 4;
178
160
  */
@@ -192,7 +174,7 @@ export type Header = Message$1<"dogma.protobuf.v1.Header"> & {
192
174
  export declare const HeaderSchema: GenMessage<Header>;
193
175
  /**
194
176
  * Body contains the metadata and encoded form of a single message in an
195
- * [Envelope] or [MultiEnvelope].
177
+ * [Envelope].
196
178
  *
197
179
  * @generated from message dogma.protobuf.v1.Body
198
180
  */
@@ -8,34 +8,29 @@ import { file_github_com_dogmatiq_protobuf_uuidpb_uuid } from "../uuidpb/uuid_pb
8
8
  /**
9
9
  * Describes the file github.com/dogmatiq/protobuf/envelopepb/envelope.proto.
10
10
  */
11
- export const file_github_com_dogmatiq_protobuf_envelopepb_envelope = /*@__PURE__*/ fileDesc("CjZnaXRodWIuY29tL2RvZ21hdGlxL3Byb3RvYnVmL2VudmVsb3BlcGIvZW52ZWxvcGUucHJvdG8SEWRvZ21hLnByb3RvYnVmLnYxIlwKCEVudmVsb3BlEikKBmhlYWRlchgBIAEoCzIZLmRvZ21hLnByb3RvYnVmLnYxLkhlYWRlchIlCgRib2R5GAIgASgLMhcuZG9nbWEucHJvdG9idWYudjEuQm9keSJjCg1NdWx0aUVudmVsb3BlEikKBmhlYWRlchgBIAEoCzIZLmRvZ21hLnByb3RvYnVmLnYxLkhlYWRlchInCgZib2RpZXMYAiADKAsyFy5kb2dtYS5wcm90b2J1Zi52MS5Cb2R5Iq8BCgZTb3VyY2USKQoEc2l0ZRgBIAEoCzIbLmRvZ21hLnByb3RvYnVmLnYxLklkZW50aXR5EjAKC2FwcGxpY2F0aW9uGAIgASgLMhsuZG9nbWEucHJvdG9idWYudjEuSWRlbnRpdHkSLAoHaGFuZGxlchgDIAEoCzIbLmRvZ21hLnByb3RvYnVmLnYxLklkZW50aXR5EhoKC2luc3RhbmNlX2lkGAQgASgJQgWqAQIIAiJkCgdNZXNzYWdlEigKB3R5cGVfaWQYASABKAsyFy5kb2dtYS5wcm90b2J1Zi52MS5VVUlEEhoKC2Rlc2NyaXB0aW9uGAIgASgJQgWqAQIIAhITCgRkYXRhGAMgASgMQgWqAQIIAiLkAQoGSGVhZGVyEi0KDGNhdXNhdGlvbl9pZBgBIAEoCzIXLmRvZ21hLnByb3RvYnVmLnYxLlVVSUQSLwoOY29ycmVsYXRpb25faWQYAiABKAsyFy5kb2dtYS5wcm90b2J1Zi52MS5VVUlEEikKBnNvdXJjZRgDIAEoCzIZLmRvZ21hLnByb3RvYnVmLnYxLlNvdXJjZRIoCgpleHRlbnNpb25zGAQgAygLMhQuZ29vZ2xlLnByb3RvYnVmLkFueRIlCgdiYWdnYWdlGAUgAygLMhQuZ29vZ2xlLnByb3RvYnVmLkFueSK0AgoEQm9keRIrCgptZXNzYWdlX2lkGAEgASgLMhcuZG9nbWEucHJvdG9idWYudjEuVVVJRBIeCg9pZGVtcG90ZW5jeV9rZXkYAiABKAlCBaoBAggCEi4KCmNyZWF0ZWRfYXQYAyABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wEjEKDXNjaGVkdWxlZF9mb3IYBCABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wEisKB21lc3NhZ2UYBSABKAsyGi5kb2dtYS5wcm90b2J1Zi52MS5NZXNzYWdlEigKCmV4dGVuc2lvbnMYBiADKAsyFC5nb29nbGUucHJvdG9idWYuQW55EiUKB2JhZ2dhZ2UYByADKAsyFC5nb29nbGUucHJvdG9idWYuQW55QilaJ2dpdGh1Yi5jb20vZG9nbWF0aXEvcHJvdG9idWYvZW52ZWxvcGVwYmIIZWRpdGlvbnNw6Qc", [file_google_protobuf_timestamp, file_google_protobuf_any, file_github_com_dogmatiq_protobuf_identitypb_identity, file_github_com_dogmatiq_protobuf_uuidpb_uuid]);
11
+ export const file_github_com_dogmatiq_protobuf_envelopepb_envelope = /*@__PURE__*/ fileDesc("CjZnaXRodWIuY29tL2RvZ21hdGlxL3Byb3RvYnVmL2VudmVsb3BlcGIvZW52ZWxvcGUucHJvdG8SEWRvZ21hLnByb3RvYnVmLnYxIlwKCEVudmVsb3BlEikKBmhlYWRlchgBIAEoCzIZLmRvZ21hLnByb3RvYnVmLnYxLkhlYWRlchIlCgRib2R5GAIgASgLMhcuZG9nbWEucHJvdG9idWYudjEuQm9keSKvAQoGU291cmNlEikKBHNpdGUYASABKAsyGy5kb2dtYS5wcm90b2J1Zi52MS5JZGVudGl0eRIwCgthcHBsaWNhdGlvbhgCIAEoCzIbLmRvZ21hLnByb3RvYnVmLnYxLklkZW50aXR5EiwKB2hhbmRsZXIYAyABKAsyGy5kb2dtYS5wcm90b2J1Zi52MS5JZGVudGl0eRIaCgtpbnN0YW5jZV9pZBgEIAEoCUIFqgECCAIiZAoHTWVzc2FnZRIoCgd0eXBlX2lkGAEgASgLMhcuZG9nbWEucHJvdG9idWYudjEuVVVJRBIaCgtkZXNjcmlwdGlvbhgCIAEoCUIFqgECCAISEwoEZGF0YRgDIAEoDEIFqgECCAIi5AEKBkhlYWRlchItCgxjYXVzYXRpb25faWQYASABKAsyFy5kb2dtYS5wcm90b2J1Zi52MS5VVUlEEi8KDmNvcnJlbGF0aW9uX2lkGAIgASgLMhcuZG9nbWEucHJvdG9idWYudjEuVVVJRBIpCgZzb3VyY2UYAyABKAsyGS5kb2dtYS5wcm90b2J1Zi52MS5Tb3VyY2USKAoKZXh0ZW5zaW9ucxgEIAMoCzIULmdvb2dsZS5wcm90b2J1Zi5BbnkSJQoHYmFnZ2FnZRgFIAMoCzIULmdvb2dsZS5wcm90b2J1Zi5BbnkitAIKBEJvZHkSKwoKbWVzc2FnZV9pZBgBIAEoCzIXLmRvZ21hLnByb3RvYnVmLnYxLlVVSUQSHgoPaWRlbXBvdGVuY3lfa2V5GAIgASgJQgWqAQIIAhIuCgpjcmVhdGVkX2F0GAMgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcBIxCg1zY2hlZHVsZWRfZm9yGAQgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcBIrCgdtZXNzYWdlGAUgASgLMhouZG9nbWEucHJvdG9idWYudjEuTWVzc2FnZRIoCgpleHRlbnNpb25zGAYgAygLMhQuZ29vZ2xlLnByb3RvYnVmLkFueRIlCgdiYWdnYWdlGAcgAygLMhQuZ29vZ2xlLnByb3RvYnVmLkFueUIpWidnaXRodWIuY29tL2RvZ21hdGlxL3Byb3RvYnVmL2VudmVsb3BlcGJiCGVkaXRpb25zcOkH", [file_google_protobuf_timestamp, file_google_protobuf_any, file_github_com_dogmatiq_protobuf_identitypb_identity, file_github_com_dogmatiq_protobuf_uuidpb_uuid]);
12
12
  /**
13
13
  * Describes the message dogma.protobuf.v1.Envelope.
14
14
  * Use `create(EnvelopeSchema)` to create a new message.
15
15
  */
16
16
  export const EnvelopeSchema = /*@__PURE__*/ messageDesc(file_github_com_dogmatiq_protobuf_envelopepb_envelope, 0);
17
- /**
18
- * Describes the message dogma.protobuf.v1.MultiEnvelope.
19
- * Use `create(MultiEnvelopeSchema)` to create a new message.
20
- */
21
- export const MultiEnvelopeSchema = /*@__PURE__*/ messageDesc(file_github_com_dogmatiq_protobuf_envelopepb_envelope, 1);
22
17
  /**
23
18
  * Describes the message dogma.protobuf.v1.Source.
24
19
  * Use `create(SourceSchema)` to create a new message.
25
20
  */
26
- export const SourceSchema = /*@__PURE__*/ messageDesc(file_github_com_dogmatiq_protobuf_envelopepb_envelope, 2);
21
+ export const SourceSchema = /*@__PURE__*/ messageDesc(file_github_com_dogmatiq_protobuf_envelopepb_envelope, 1);
27
22
  /**
28
23
  * Describes the message dogma.protobuf.v1.Message.
29
24
  * Use `create(MessageSchema)` to create a new message.
30
25
  */
31
- export const MessageSchema = /*@__PURE__*/ messageDesc(file_github_com_dogmatiq_protobuf_envelopepb_envelope, 3);
26
+ export const MessageSchema = /*@__PURE__*/ messageDesc(file_github_com_dogmatiq_protobuf_envelopepb_envelope, 2);
32
27
  /**
33
28
  * Describes the message dogma.protobuf.v1.Header.
34
29
  * Use `create(HeaderSchema)` to create a new message.
35
30
  */
36
- export const HeaderSchema = /*@__PURE__*/ messageDesc(file_github_com_dogmatiq_protobuf_envelopepb_envelope, 4);
31
+ export const HeaderSchema = /*@__PURE__*/ messageDesc(file_github_com_dogmatiq_protobuf_envelopepb_envelope, 3);
37
32
  /**
38
33
  * Describes the message dogma.protobuf.v1.Body.
39
34
  * Use `create(BodySchema)` to create a new message.
40
35
  */
41
- export const BodySchema = /*@__PURE__*/ messageDesc(file_github_com_dogmatiq_protobuf_envelopepb_envelope, 5);
36
+ export const BodySchema = /*@__PURE__*/ messageDesc(file_github_com_dogmatiq_protobuf_envelopepb_envelope, 4);
@@ -16,6 +16,8 @@ export type Identity = Message<"dogma.protobuf.v1.Identity"> & {
16
16
  /**
17
17
  * Name is the entity's human-readable name.
18
18
  *
19
+ * The name MUST be between 1 and 255 bytes when encoded as UTF-8.
20
+ *
19
21
  * The name should be unique enough to allow a human to identify the entity
20
22
  * without ambiguity. There is no hard requirement for uniqueness.
21
23
  *
@@ -24,3 +24,27 @@ export type UnrecognizedEventStream = Message<"dogma.messaging.v1.UnrecognizedEv
24
24
  * Use `create(UnrecognizedEventStreamSchema)` to create a new message.
25
25
  */
26
26
  export declare const UnrecognizedEventStreamSchema: GenMessage<UnrecognizedEventStream>;
27
+ /**
28
+ * EventStreamConsumerTooSlow is an error-details value for RESOURCE_EXHAUSTED
29
+ * errors returned by WatchEvents when a client cannot keep up and the server
30
+ * can no longer deliver a stream contiguously.
31
+ *
32
+ * A client that is behind the tail does not cause this error while contiguous
33
+ * delivery remains possible.
34
+ *
35
+ * @generated from message dogma.messaging.v1.EventStreamConsumerTooSlow
36
+ */
37
+ export type EventStreamConsumerTooSlow = Message<"dogma.messaging.v1.EventStreamConsumerTooSlow"> & {
38
+ /**
39
+ * EventStreamId is the ID of the stream that could no longer be delivered
40
+ * contiguously.
41
+ *
42
+ * @generated from field: dogma.protobuf.v1.UUID event_stream_id = 1;
43
+ */
44
+ eventStreamId?: UUID | undefined;
45
+ };
46
+ /**
47
+ * Describes the message dogma.messaging.v1.EventStreamConsumerTooSlow.
48
+ * Use `create(EventStreamConsumerTooSlowSchema)` to create a new message.
49
+ */
50
+ export declare const EventStreamConsumerTooSlowSchema: GenMessage<EventStreamConsumerTooSlow>;
@@ -6,9 +6,14 @@ import { file_github_com_dogmatiq_protobuf_uuidpb_uuid } from "../uuidpb/uuid_pb
6
6
  /**
7
7
  * Describes the file github.com/dogmatiq/protobuf/messaginggrpc/errors.proto.
8
8
  */
9
- export const file_github_com_dogmatiq_protobuf_messaginggrpc_errors = /*@__PURE__*/ fileDesc("CjdnaXRodWIuY29tL2RvZ21hdGlxL3Byb3RvYnVmL21lc3NhZ2luZ2dycGMvZXJyb3JzLnByb3RvEhJkb2dtYS5tZXNzYWdpbmcudjEiSwoXVW5yZWNvZ25pemVkRXZlbnRTdHJlYW0SMAoPZXZlbnRfc3RyZWFtX2lkGAEgASgLMhcuZG9nbWEucHJvdG9idWYudjEuVVVJREIsWipnaXRodWIuY29tL2RvZ21hdGlxL3Byb3RvYnVmL21lc3NhZ2luZ2dycGNiCGVkaXRpb25zcOkH", [file_github_com_dogmatiq_protobuf_uuidpb_uuid]);
9
+ export const file_github_com_dogmatiq_protobuf_messaginggrpc_errors = /*@__PURE__*/ fileDesc("CjdnaXRodWIuY29tL2RvZ21hdGlxL3Byb3RvYnVmL21lc3NhZ2luZ2dycGMvZXJyb3JzLnByb3RvEhJkb2dtYS5tZXNzYWdpbmcudjEiSwoXVW5yZWNvZ25pemVkRXZlbnRTdHJlYW0SMAoPZXZlbnRfc3RyZWFtX2lkGAEgASgLMhcuZG9nbWEucHJvdG9idWYudjEuVVVJRCJOChpFdmVudFN0cmVhbUNvbnN1bWVyVG9vU2xvdxIwCg9ldmVudF9zdHJlYW1faWQYASABKAsyFy5kb2dtYS5wcm90b2J1Zi52MS5VVUlEQixaKmdpdGh1Yi5jb20vZG9nbWF0aXEvcHJvdG9idWYvbWVzc2FnaW5nZ3JwY2IIZWRpdGlvbnNw6Qc", [file_github_com_dogmatiq_protobuf_uuidpb_uuid]);
10
10
  /**
11
11
  * Describes the message dogma.messaging.v1.UnrecognizedEventStream.
12
12
  * Use `create(UnrecognizedEventStreamSchema)` to create a new message.
13
13
  */
14
14
  export const UnrecognizedEventStreamSchema = /*@__PURE__*/ messageDesc(file_github_com_dogmatiq_protobuf_messaginggrpc_errors, 0);
15
+ /**
16
+ * Describes the message dogma.messaging.v1.EventStreamConsumerTooSlow.
17
+ * Use `create(EventStreamConsumerTooSlowSchema)` to create a new message.
18
+ */
19
+ export const EventStreamConsumerTooSlowSchema = /*@__PURE__*/ messageDesc(file_github_com_dogmatiq_protobuf_messaginggrpc_errors, 1);
@@ -1,6 +1,7 @@
1
1
  import type { GenFile, GenMessage, GenService } from "@bufbuild/protobuf/codegenv2";
2
- import type { MultiEnvelope } from "../envelopepb/envelope_pb.js";
2
+ import type { Envelope } from "../envelopepb/envelope_pb.js";
3
3
  import type { UUID } from "../uuidpb/uuid_pb.js";
4
+ import type { MessageFilter } from "./filter_pb.js";
4
5
  import type { Message } from "@bufbuild/protobuf";
5
6
  /**
6
7
  * Describes the file github.com/dogmatiq/protobuf/messaginggrpc/eventstreamconsumer.proto.
@@ -14,12 +15,11 @@ export declare const file_github_com_dogmatiq_protobuf_messaginggrpc_eventstream
14
15
  */
15
16
  export type WatchEventsRequest = Message<"dogma.messaging.v1.WatchEventsRequest"> & {
16
17
  /**
17
- * MessageTypeIds is the set of message type IDs of the event types to
18
- * include in the segments. An empty set matches every event.
18
+ * Filter describes the events to deliver.
19
19
  *
20
- * @generated from field: repeated dogma.protobuf.v1.UUID message_type_ids = 1;
20
+ * @generated from field: dogma.messaging.v1.MessageFilter filter = 1;
21
21
  */
22
- messageTypeIds: UUID[];
22
+ filter?: MessageFilter | undefined;
23
23
  };
24
24
  /**
25
25
  * Describes the message dogma.messaging.v1.WatchEventsRequest.
@@ -48,12 +48,11 @@ export type ReadEventsRequest = Message<"dogma.messaging.v1.ReadEventsRequest">
48
48
  */
49
49
  beginOffset: bigint;
50
50
  /**
51
- * MessageTypeIds is the set of message type IDs of the event types to
52
- * include in the segments. An empty set matches every event.
51
+ * Filter describes the events to deliver.
53
52
  *
54
- * @generated from field: repeated dogma.protobuf.v1.UUID message_type_ids = 3;
53
+ * @generated from field: dogma.messaging.v1.MessageFilter filter = 3;
55
54
  */
56
- messageTypeIds: UUID[];
55
+ filter?: MessageFilter | undefined;
57
56
  };
58
57
  /**
59
58
  * Describes the message dogma.messaging.v1.ReadEventsRequest.
@@ -93,19 +92,18 @@ export type EventStreamSegment = Message<"dogma.messaging.v1.EventStreamSegment"
93
92
  */
94
93
  endOffset: bigint;
95
94
  /**
96
- * Envelopes is a (possibly empty) list of multi-envelopes containing the
95
+ * Envelopes is a (possibly empty) list of envelopes containing the
97
96
  * events in the range that match the requested message types.
98
97
  *
99
98
  * Each envelope includes the [envelopepb.EventStreamPosition] extension.
100
99
  *
101
- * The events appear in ascending offset order, both within each
102
- * multi-envelope and across the list. Their offsets may have gaps, because
103
- * events of other types are omitted. There is no additional relationship
104
- * implied between envelopes that appear in the same segment.
100
+ * The events appear in ascending offset order. Their offsets may have gaps,
101
+ * because events of other types are omitted. There is no additional
102
+ * relationship implied between envelopes that appear in the same segment.
105
103
  *
106
- * @generated from field: repeated dogma.protobuf.v1.MultiEnvelope envelopes = 4;
104
+ * @generated from field: repeated dogma.protobuf.v1.Envelope envelopes = 4;
107
105
  */
108
- envelopes: MultiEnvelope[];
106
+ envelopes: Envelope[];
109
107
  };
110
108
  /**
111
109
  * Describes the message dogma.messaging.v1.EventStreamSegment.
@@ -120,25 +118,22 @@ export declare const EventStreamSegmentSchema: GenMessage<EventStreamSegment>;
120
118
  */
121
119
  export declare const EventStreamConsumerAPI: GenService<{
122
120
  /**
123
- * WatchEvents delivers events of the requested message types as they are
124
- * appended to any event stream offered by the server, for as long as the call
125
- * is open.
126
- *
127
- * The feed starts at the tail of each stream. The first [EventStreamSegment]
128
- * the client receives for a stream tells it that the stream exists and where
129
- * in the stream the feed begins. After this first segment, each subsequent
130
- * segment for the same stream begins at the end offset of the one before it.
131
- *
132
- * The server MAY send a first segment for every stream it offers, or only for
133
- * those streams that contain events of a requested type. In the latter case
134
- * the server MUST decide from the events in the stream, not from the types
135
- * that the server is able to record.
136
- *
137
- * If the server can no longer deliver a stream contiguously, for example
138
- * because the client has fallen too far behind, it closes the call. A client
139
- * that reconnects MUST expect to have missed events. On the new call, the
140
- * first segment it receives for each stream again tells it where the feed
141
- * begins, which is not necessarily where the previous call left off.
121
+ * WatchEvents delivers events from all streams available from the server.
122
+ * Events are delivered as they are appended, provided they match the
123
+ * request's filter.
124
+ *
125
+ * The feed starts at the tail of each stream. Every available stream that
126
+ * already contains matching events is represented by at least one segment.
127
+ * The first [EventStreamSegment] received for a stream may be empty, but it
128
+ * tells the client that the stream exists and the offset at which delivery
129
+ * begins. Each subsequent segment for that stream begins at the end offset
130
+ * of the previous segment.
131
+ *
132
+ * If the client cannot keep up and the server can no longer deliver a stream
133
+ * contiguously, the call ends with RESOURCE_EXHAUSTED and an attached
134
+ * [EventStreamConsumerTooSlow] value identifying the affected stream.
135
+ *
136
+ * A missing or invalid message filter results in INVALID_ARGUMENT.
142
137
  *
143
138
  * @generated from rpc dogma.messaging.v1.EventStreamConsumerAPI.WatchEvents
144
139
  */
@@ -148,20 +143,20 @@ export declare const EventStreamConsumerAPI: GenService<{
148
143
  output: typeof EventStreamSegmentSchema;
149
144
  };
150
145
  /**
151
- * ReadEvents delivers the events of the requested message types from a
152
- * single stream, in order, starting at a specific offset and ending at the
153
- * end of the stream as it stands when the server reaches it.
146
+ * ReadEvents delivers events that match the request's filter from a single
147
+ * event stream, in order, beginning at the requested offset.
148
+ *
149
+ * The first segment begins at the requested offset. Each subsequent segment
150
+ * begins at the end offset of the previous segment. Delivery ends when the
151
+ * server reaches the end of the stream, at which point it closes the call.
154
152
  *
155
- * It is intended for a consumer whose checkpoint is behind the offset at
156
- * which [EventStreamConsumerAPI.WatchEvents] began delivering the stream.
157
- * It delivers what the caller has missed, then the server closes the call.
153
+ * An unrecognized stream ID results in NOT_FOUND with an attached
154
+ * [UnrecognizedEventStream] value. See [UnrecognizedEventStreamError].
158
155
  *
159
- * If the requested stream ID is not recognized the server MUST return a
160
- * NOT_FOUND error with an attached [UnrecognizedEventStream] value. See
161
- * [UnrecognizedEventStreamError].
156
+ * A requested offset beyond the end of the stream results in OUT_OF_RANGE.
157
+ * An offset equal to the end of the stream is valid and returns no events.
162
158
  *
163
- * If the requested offset is greater than the offset that the next event
164
- * appended to the stream will have, an OUT_OF_RANGE error occurs.
159
+ * A missing or invalid message filter results in INVALID_ARGUMENT.
165
160
  *
166
161
  * @generated from rpc dogma.messaging.v1.EventStreamConsumerAPI.ReadEvents
167
162
  */
@@ -4,10 +4,11 @@
4
4
  import { fileDesc, messageDesc, serviceDesc } from "@bufbuild/protobuf/codegenv2";
5
5
  import { file_github_com_dogmatiq_protobuf_envelopepb_envelope } from "../envelopepb/envelope_pb.js";
6
6
  import { file_github_com_dogmatiq_protobuf_uuidpb_uuid } from "../uuidpb/uuid_pb.js";
7
+ import { file_github_com_dogmatiq_protobuf_messaginggrpc_filter } from "./filter_pb.js";
7
8
  /**
8
9
  * Describes the file github.com/dogmatiq/protobuf/messaginggrpc/eventstreamconsumer.proto.
9
10
  */
10
- export const file_github_com_dogmatiq_protobuf_messaginggrpc_eventstreamconsumer = /*@__PURE__*/ fileDesc("CkRnaXRodWIuY29tL2RvZ21hdGlxL3Byb3RvYnVmL21lc3NhZ2luZ2dycGMvZXZlbnRzdHJlYW1jb25zdW1lci5wcm90bxISZG9nbWEubWVzc2FnaW5nLnYxIkcKEldhdGNoRXZlbnRzUmVxdWVzdBIxChBtZXNzYWdlX3R5cGVfaWRzGAEgAygLMhcuZG9nbWEucHJvdG9idWYudjEuVVVJRCKVAQoRUmVhZEV2ZW50c1JlcXVlc3QSMAoPZXZlbnRfc3RyZWFtX2lkGAEgASgLMhcuZG9nbWEucHJvdG9idWYudjEuVVVJRBIbCgxiZWdpbl9vZmZzZXQYAiABKARCBaoBAggCEjEKEG1lc3NhZ2VfdHlwZV9pZHMYAyADKAsyFy5kb2dtYS5wcm90b2J1Zi52MS5VVUlEIrMBChJFdmVudFN0cmVhbVNlZ21lbnQSMAoPZXZlbnRfc3RyZWFtX2lkGAEgASgLMhcuZG9nbWEucHJvdG9idWYudjEuVVVJRBIbCgxiZWdpbl9vZmZzZXQYAiABKARCBaoBAggCEhkKCmVuZF9vZmZzZXQYAyABKARCBaoBAggCEjMKCWVudmVsb3BlcxgEIAMoCzIgLmRvZ21hLnByb3RvYnVmLnYxLk11bHRpRW52ZWxvcGUy2AEKFkV2ZW50U3RyZWFtQ29uc3VtZXJBUEkSXwoLV2F0Y2hFdmVudHMSJi5kb2dtYS5tZXNzYWdpbmcudjEuV2F0Y2hFdmVudHNSZXF1ZXN0GiYuZG9nbWEubWVzc2FnaW5nLnYxLkV2ZW50U3RyZWFtU2VnbWVudDABEl0KClJlYWRFdmVudHMSJS5kb2dtYS5tZXNzYWdpbmcudjEuUmVhZEV2ZW50c1JlcXVlc3QaJi5kb2dtYS5tZXNzYWdpbmcudjEuRXZlbnRTdHJlYW1TZWdtZW50MAFCLFoqZ2l0aHViLmNvbS9kb2dtYXRpcS9wcm90b2J1Zi9tZXNzYWdpbmdncnBjYghlZGl0aW9uc3DpBw", [file_github_com_dogmatiq_protobuf_envelopepb_envelope, file_github_com_dogmatiq_protobuf_uuidpb_uuid]);
11
+ export const file_github_com_dogmatiq_protobuf_messaginggrpc_eventstreamconsumer = /*@__PURE__*/ fileDesc("CkRnaXRodWIuY29tL2RvZ21hdGlxL3Byb3RvYnVmL21lc3NhZ2luZ2dycGMvZXZlbnRzdHJlYW1jb25zdW1lci5wcm90bxISZG9nbWEubWVzc2FnaW5nLnYxIkcKEldhdGNoRXZlbnRzUmVxdWVzdBIxCgZmaWx0ZXIYASABKAsyIS5kb2dtYS5tZXNzYWdpbmcudjEuTWVzc2FnZUZpbHRlciKVAQoRUmVhZEV2ZW50c1JlcXVlc3QSMAoPZXZlbnRfc3RyZWFtX2lkGAEgASgLMhcuZG9nbWEucHJvdG9idWYudjEuVVVJRBIbCgxiZWdpbl9vZmZzZXQYAiABKARCBaoBAggCEjEKBmZpbHRlchgDIAEoCzIhLmRvZ21hLm1lc3NhZ2luZy52MS5NZXNzYWdlRmlsdGVyIq4BChJFdmVudFN0cmVhbVNlZ21lbnQSMAoPZXZlbnRfc3RyZWFtX2lkGAEgASgLMhcuZG9nbWEucHJvdG9idWYudjEuVVVJRBIbCgxiZWdpbl9vZmZzZXQYAiABKARCBaoBAggCEhkKCmVuZF9vZmZzZXQYAyABKARCBaoBAggCEi4KCWVudmVsb3BlcxgEIAMoCzIbLmRvZ21hLnByb3RvYnVmLnYxLkVudmVsb3BlMtgBChZFdmVudFN0cmVhbUNvbnN1bWVyQVBJEl8KC1dhdGNoRXZlbnRzEiYuZG9nbWEubWVzc2FnaW5nLnYxLldhdGNoRXZlbnRzUmVxdWVzdBomLmRvZ21hLm1lc3NhZ2luZy52MS5FdmVudFN0cmVhbVNlZ21lbnQwARJdCgpSZWFkRXZlbnRzEiUuZG9nbWEubWVzc2FnaW5nLnYxLlJlYWRFdmVudHNSZXF1ZXN0GiYuZG9nbWEubWVzc2FnaW5nLnYxLkV2ZW50U3RyZWFtU2VnbWVudDABQixaKmdpdGh1Yi5jb20vZG9nbWF0aXEvcHJvdG9idWYvbWVzc2FnaW5nZ3JwY2IIZWRpdGlvbnNw6Qc", [file_github_com_dogmatiq_protobuf_envelopepb_envelope, file_github_com_dogmatiq_protobuf_uuidpb_uuid, file_github_com_dogmatiq_protobuf_messaginggrpc_filter]);
11
12
  /**
12
13
  * Describes the message dogma.messaging.v1.WatchEventsRequest.
13
14
  * Use `create(WatchEventsRequestSchema)` to create a new message.
@@ -0,0 +1,6 @@
1
+ import type { MessageFilter } from "./filter_pb.js";
2
+ /**
3
+ * Throws an error if x matches no message types or contains an invalid message
4
+ * type ID.
5
+ */
6
+ export declare function validateMessageFilter(x: MessageFilter | undefined): void;
@@ -0,0 +1,23 @@
1
+ import { validateUUID } from "../uuidpb/uuid.js";
2
+ /**
3
+ * Throws an error if x matches no message types or contains an invalid message
4
+ * type ID.
5
+ */
6
+ export function validateMessageFilter(x) {
7
+ const types = x?.types;
8
+ if (types?.case === "all") {
9
+ return;
10
+ }
11
+ const ids = types?.case === "specific" ? types.value.ids : [];
12
+ if (ids.length === 0) {
13
+ throw new Error("message filter must match all message types or at least one specific type");
14
+ }
15
+ ids.forEach((id, i) => {
16
+ try {
17
+ validateUUID(id);
18
+ }
19
+ catch (cause) {
20
+ throw new Error(`invalid message type ID at index ${i}: ${cause.message}`, { cause });
21
+ }
22
+ });
23
+ }
@@ -0,0 +1,64 @@
1
+ import type { GenFile, GenMessage } from "@bufbuild/protobuf/codegenv2";
2
+ import type { Empty } from "@bufbuild/protobuf/wkt";
3
+ import type { UUID } from "../uuidpb/uuid_pb.js";
4
+ import type { Message } from "@bufbuild/protobuf";
5
+ /**
6
+ * Describes the file github.com/dogmatiq/protobuf/messaginggrpc/filter.proto.
7
+ */
8
+ export declare const file_github_com_dogmatiq_protobuf_messaginggrpc_filter: GenFile;
9
+ /**
10
+ * MessageFilter describes a set of messages to deliver.
11
+ *
12
+ * @generated from message dogma.messaging.v1.MessageFilter
13
+ */
14
+ export type MessageFilter = Message<"dogma.messaging.v1.MessageFilter"> & {
15
+ /**
16
+ * Types determines which message types match. One alternative must be set.
17
+ *
18
+ * @generated from oneof dogma.messaging.v1.MessageFilter.types
19
+ */
20
+ types: {
21
+ /**
22
+ * All matches all message types.
23
+ *
24
+ * @generated from field: google.protobuf.Empty all = 1;
25
+ */
26
+ value: Empty;
27
+ case: "all";
28
+ } | {
29
+ /**
30
+ * Specific matches the listed message types.
31
+ *
32
+ * @generated from field: dogma.messaging.v1.MessageFilter.MessageTypes specific = 2;
33
+ */
34
+ value: MessageFilter_MessageTypes;
35
+ case: "specific";
36
+ } | {
37
+ case: undefined;
38
+ value?: undefined;
39
+ };
40
+ };
41
+ /**
42
+ * Describes the message dogma.messaging.v1.MessageFilter.
43
+ * Use `create(MessageFilterSchema)` to create a new message.
44
+ */
45
+ export declare const MessageFilterSchema: GenMessage<MessageFilter>;
46
+ /**
47
+ * MessageTypes describes a set of specific message types.
48
+ *
49
+ * @generated from message dogma.messaging.v1.MessageFilter.MessageTypes
50
+ */
51
+ export type MessageFilter_MessageTypes = Message<"dogma.messaging.v1.MessageFilter.MessageTypes"> & {
52
+ /**
53
+ * Ids is the set of message type IDs that match the filter.
54
+ * The list must not be empty.
55
+ *
56
+ * @generated from field: repeated dogma.protobuf.v1.UUID ids = 1;
57
+ */
58
+ ids: UUID[];
59
+ };
60
+ /**
61
+ * Describes the message dogma.messaging.v1.MessageFilter.MessageTypes.
62
+ * Use `create(MessageFilter_MessageTypesSchema)` to create a new message.
63
+ */
64
+ export declare const MessageFilter_MessageTypesSchema: GenMessage<MessageFilter_MessageTypes>;
@@ -0,0 +1,20 @@
1
+ // @generated by protoc-gen-es v2.16.0 with parameter "target=ts,import_extension=js"
2
+ // @generated from file github.com/dogmatiq/protobuf/messaginggrpc/filter.proto (package dogma.messaging.v1, edition 2024)
3
+ /* eslint-disable */
4
+ import { fileDesc, messageDesc } from "@bufbuild/protobuf/codegenv2";
5
+ import { file_google_protobuf_empty } from "@bufbuild/protobuf/wkt";
6
+ import { file_github_com_dogmatiq_protobuf_uuidpb_uuid } from "../uuidpb/uuid_pb.js";
7
+ /**
8
+ * Describes the file github.com/dogmatiq/protobuf/messaginggrpc/filter.proto.
9
+ */
10
+ export const file_github_com_dogmatiq_protobuf_messaginggrpc_filter = /*@__PURE__*/ fileDesc("CjdnaXRodWIuY29tL2RvZ21hdGlxL3Byb3RvYnVmL21lc3NhZ2luZ2dycGMvZmlsdGVyLnByb3RvEhJkb2dtYS5tZXNzYWdpbmcudjEiuQEKDU1lc3NhZ2VGaWx0ZXISJQoDYWxsGAEgASgLMhYuZ29vZ2xlLnByb3RvYnVmLkVtcHR5SAASQgoIc3BlY2lmaWMYAiABKAsyLi5kb2dtYS5tZXNzYWdpbmcudjEuTWVzc2FnZUZpbHRlci5NZXNzYWdlVHlwZXNIABo0CgxNZXNzYWdlVHlwZXMSJAoDaWRzGAEgAygLMhcuZG9nbWEucHJvdG9idWYudjEuVVVJREIHCgV0eXBlc0IsWipnaXRodWIuY29tL2RvZ21hdGlxL3Byb3RvYnVmL21lc3NhZ2luZ2dycGNiCGVkaXRpb25zcOkH", [file_google_protobuf_empty, file_github_com_dogmatiq_protobuf_uuidpb_uuid]);
11
+ /**
12
+ * Describes the message dogma.messaging.v1.MessageFilter.
13
+ * Use `create(MessageFilterSchema)` to create a new message.
14
+ */
15
+ export const MessageFilterSchema = /*@__PURE__*/ messageDesc(file_github_com_dogmatiq_protobuf_messaginggrpc_filter, 0);
16
+ /**
17
+ * Describes the message dogma.messaging.v1.MessageFilter.MessageTypes.
18
+ * Use `create(MessageFilter_MessageTypesSchema)` to create a new message.
19
+ */
20
+ export const MessageFilter_MessageTypesSchema = /*@__PURE__*/ messageDesc(file_github_com_dogmatiq_protobuf_messaginggrpc_filter, 0, 0);
@@ -1,2 +1,4 @@
1
1
  export * from "./errors_pb.js";
2
2
  export * from "./eventstreamconsumer_pb.js";
3
+ export * from "./filter.js";
4
+ export * from "./filter_pb.js";
@@ -1,2 +1,4 @@
1
1
  export * from "./errors_pb.js";
2
2
  export * from "./eventstreamconsumer_pb.js";
3
+ export * from "./filter.js";
4
+ export * from "./filter_pb.js";
@@ -42,6 +42,12 @@ export function uuidEquals(a, b) {
42
42
  * (name-based) UUID.
43
43
  */
44
44
  export function validateUUID(x) {
45
+ if (x.upper < 0n ||
46
+ x.upper > MAX_UINT64 ||
47
+ x.lower < 0n ||
48
+ x.lower > MAX_UINT64) {
49
+ throw new Error("UUID components must be unsigned 64-bit integers");
50
+ }
45
51
  const version = (x.upper >> 8n) & 0xf0n;
46
52
  if (version !== 0x40n && version !== 0x50n) {
47
53
  throw new Error("UUID must use version 4 or 5");
@@ -82,6 +88,7 @@ export function parseUUID(str) {
82
88
  lower: BigInt("0x" + hex.slice(16)),
83
89
  });
84
90
  }
91
+ const MAX_UINT64 = 0xffffffffffffffffn;
85
92
  const PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
86
93
  // toBytes returns the 16-byte big-endian representation of a UUID.
87
94
  function toBytes(x) {
@@ -7,6 +7,9 @@ export declare const file_github_com_dogmatiq_protobuf_uuidpb_uuid: GenFile;
7
7
  /**
8
8
  * UUID is a size-optimized representation of an RFC 9562 UUID.
9
9
  *
10
+ * UUID values MUST use version 4 (random) or version 5 (name-based), and the
11
+ * RFC 9562 variant.
12
+ *
10
13
  * @generated from message dogma.protobuf.v1.UUID
11
14
  */
12
15
  export type UUID = Message<"dogma.protobuf.v1.UUID"> & {
@@ -9,21 +9,11 @@ import "github.com/dogmatiq/protobuf/identitypb/identity.proto";
9
9
  import "github.com/dogmatiq/protobuf/uuidpb/uuid.proto";
10
10
 
11
11
  // Envelope is a container for a Dogma message and its meta-data.
12
- //
13
- // It is wire-compatible with [MultiEnvelope].
14
12
  message Envelope {
15
13
  Header header = 1;
16
14
  Body body = 2;
17
15
  }
18
16
 
19
- // MultiEnvelope is an efficient encoding of an ordered sequence of [Envelope]
20
- // values that share common header context.
21
- //
22
- message MultiEnvelope {
23
- Header header = 1;
24
- repeated Body bodies = 2;
25
- }
26
-
27
17
  // Source identifies where a message originated.
28
18
  message Source {
29
19
  // Site is the (optional) identity of the "site" that the source
@@ -73,27 +63,32 @@ message Message {
73
63
  bytes data = 3 [features.field_presence = IMPLICIT];
74
64
  }
75
65
 
66
+ // Header contains origin and causal context that can be common to messages
67
+ // produced by the same cause.
68
+ //
69
+ // Extensions and baggage from the header and body are combined by message
70
+ // type. Body values take precedence over header values of the same type.
76
71
  message Header {
77
72
  // CausationId is the ID of the message that was the direct cause of the
78
- // messages.
73
+ // message.
79
74
  //
80
75
  // It is required. A message that was not caused by any other message is its
81
76
  // own cause, so its causation ID is its own message ID.
82
77
  UUID causation_id = 1;
83
78
 
84
- // CorrelationId is the ID of the first ancestor of the messages that was
79
+ // CorrelationId is the ID of the first ancestor of the message that was
85
80
  // itself not caused by another message, that is, it's the root of the message
86
- // chain that ultimately resulted in these messages.
81
+ // chain that ultimately resulted in this message.
87
82
  //
88
83
  // It is required. A message that was not caused by any other message is the
89
84
  // root of its own chain, so its correlation ID is its own message ID.
90
85
  UUID correlation_id = 2;
91
86
 
92
- // Source identifies the origin of the messages.
87
+ // Source identifies the origin of the message.
93
88
  Source source = 3;
94
89
 
95
- // Extensions is a set of extension values associated with the messages,
96
- // their causal chain, or the envelope itself.
90
+ // Extensions is a set of extension values associated with the message,
91
+ // its causal chain, or the envelope itself.
97
92
  repeated google.protobuf.Any extensions = 4;
98
93
 
99
94
  // Baggage is a set of extension values that are propagated through the
@@ -102,7 +97,7 @@ message Header {
102
97
  }
103
98
 
104
99
  // Body contains the metadata and encoded form of a single message in an
105
- // [Envelope] or [MultiEnvelope].
100
+ // [Envelope].
106
101
  message Body {
107
102
  // MessageId is a unique identifier for the message in this envelope.
108
103
  UUID message_id = 1;
@@ -11,6 +11,8 @@ import "github.com/dogmatiq/protobuf/uuidpb/uuid.proto";
11
11
  message Identity {
12
12
  // Name is the entity's human-readable name.
13
13
  //
14
+ // The name MUST be between 1 and 255 bytes when encoded as UTF-8.
15
+ //
14
16
  // The name should be unique enough to allow a human to identify the entity
15
17
  // without ambiguity. There is no hard requirement for uniqueness.
16
18
  //
@@ -11,3 +11,15 @@ message UnrecognizedEventStream {
11
11
  // EventStreamId is the ID of the unrecognized stream.
12
12
  dogma.protobuf.v1.UUID event_stream_id = 1;
13
13
  }
14
+
15
+ // EventStreamConsumerTooSlow is an error-details value for RESOURCE_EXHAUSTED
16
+ // errors returned by WatchEvents when a client cannot keep up and the server
17
+ // can no longer deliver a stream contiguously.
18
+ //
19
+ // A client that is behind the tail does not cause this error while contiguous
20
+ // delivery remains possible.
21
+ message EventStreamConsumerTooSlow {
22
+ // EventStreamId is the ID of the stream that could no longer be delivered
23
+ // contiguously.
24
+ dogma.protobuf.v1.UUID event_stream_id = 1;
25
+ }
@@ -5,54 +5,51 @@ option go_package = "github.com/dogmatiq/protobuf/messaginggrpc";
5
5
 
6
6
  import "github.com/dogmatiq/protobuf/envelopepb/envelope.proto";
7
7
  import "github.com/dogmatiq/protobuf/uuidpb/uuid.proto";
8
+ import "github.com/dogmatiq/protobuf/messaginggrpc/filter.proto";
8
9
 
9
10
  // EventStreamConsumerAPI is a service for consuming event messages from
10
11
  // offset-based, ordered event streams.
11
12
  service EventStreamConsumerAPI {
12
- // WatchEvents delivers events of the requested message types as they are
13
- // appended to any event stream offered by the server, for as long as the call
14
- // is open.
13
+ // WatchEvents delivers events from all streams available from the server.
14
+ // Events are delivered as they are appended, provided they match the
15
+ // request's filter.
15
16
  //
16
- // The feed starts at the tail of each stream. The first [EventStreamSegment]
17
- // the client receives for a stream tells it that the stream exists and where
18
- // in the stream the feed begins. After this first segment, each subsequent
19
- // segment for the same stream begins at the end offset of the one before it.
17
+ // The feed starts at the tail of each stream. Every available stream that
18
+ // already contains matching events is represented by at least one segment.
19
+ // The first [EventStreamSegment] received for a stream may be empty, but it
20
+ // tells the client that the stream exists and the offset at which delivery
21
+ // begins. Each subsequent segment for that stream begins at the end offset
22
+ // of the previous segment.
20
23
  //
21
- // The server MAY send a first segment for every stream it offers, or only for
22
- // those streams that contain events of a requested type. In the latter case
23
- // the server MUST decide from the events in the stream, not from the types
24
- // that the server is able to record.
24
+ // If the client cannot keep up and the server can no longer deliver a stream
25
+ // contiguously, the call ends with RESOURCE_EXHAUSTED and an attached
26
+ // [EventStreamConsumerTooSlow] value identifying the affected stream.
25
27
  //
26
- // If the server can no longer deliver a stream contiguously, for example
27
- // because the client has fallen too far behind, it closes the call. A client
28
- // that reconnects MUST expect to have missed events. On the new call, the
29
- // first segment it receives for each stream again tells it where the feed
30
- // begins, which is not necessarily where the previous call left off.
28
+ // A missing or invalid message filter results in INVALID_ARGUMENT.
31
29
  rpc WatchEvents(WatchEventsRequest) returns (stream EventStreamSegment);
32
30
 
33
- // ReadEvents delivers the events of the requested message types from a
34
- // single stream, in order, starting at a specific offset and ending at the
35
- // end of the stream as it stands when the server reaches it.
31
+ // ReadEvents delivers events that match the request's filter from a single
32
+ // event stream, in order, beginning at the requested offset.
36
33
  //
37
- // It is intended for a consumer whose checkpoint is behind the offset at
38
- // which [EventStreamConsumerAPI.WatchEvents] began delivering the stream.
39
- // It delivers what the caller has missed, then the server closes the call.
34
+ // The first segment begins at the requested offset. Each subsequent segment
35
+ // begins at the end offset of the previous segment. Delivery ends when the
36
+ // server reaches the end of the stream, at which point it closes the call.
40
37
  //
41
- // If the requested stream ID is not recognized the server MUST return a
42
- // NOT_FOUND error with an attached [UnrecognizedEventStream] value. See
43
- // [UnrecognizedEventStreamError].
38
+ // An unrecognized stream ID results in NOT_FOUND with an attached
39
+ // [UnrecognizedEventStream] value. See [UnrecognizedEventStreamError].
44
40
  //
45
- // If the requested offset is greater than the offset that the next event
46
- // appended to the stream will have, an OUT_OF_RANGE error occurs.
41
+ // A requested offset beyond the end of the stream results in OUT_OF_RANGE.
42
+ // An offset equal to the end of the stream is valid and returns no events.
43
+ //
44
+ // A missing or invalid message filter results in INVALID_ARGUMENT.
47
45
  rpc ReadEvents(ReadEventsRequest) returns (stream EventStreamSegment);
48
46
  }
49
47
 
50
48
  // WatchEventsRequest is the input to the [EventStreamConsumerAPI.WatchEvents]
51
49
  // method.
52
50
  message WatchEventsRequest {
53
- // MessageTypeIds is the set of message type IDs of the event types to
54
- // include in the segments. An empty set matches every event.
55
- repeated dogma.protobuf.v1.UUID message_type_ids = 1;
51
+ // Filter describes the events to deliver.
52
+ MessageFilter filter = 1;
56
53
  }
57
54
 
58
55
  // ReadEventsRequest is the input to the [EventStreamConsumerAPI.ReadEvents]
@@ -66,9 +63,8 @@ message ReadEventsRequest {
66
63
  // are delivered, then the call is closed.
67
64
  uint64 begin_offset = 2 [features.field_presence = IMPLICIT];
68
65
 
69
- // MessageTypeIds is the set of message type IDs of the event types to
70
- // include in the segments. An empty set matches every event.
71
- repeated dogma.protobuf.v1.UUID message_type_ids = 3;
66
+ // Filter describes the events to deliver.
67
+ MessageFilter filter = 3;
72
68
  }
73
69
 
74
70
  // EventStreamSegment is a contiguous piece of one event stream, the half-open
@@ -90,14 +86,13 @@ message EventStreamSegment {
90
86
  // EndOffset is the offset immediately after the range, exclusive.
91
87
  uint64 end_offset = 3 [features.field_presence = IMPLICIT];
92
88
 
93
- // Envelopes is a (possibly empty) list of multi-envelopes containing the
89
+ // Envelopes is a (possibly empty) list of envelopes containing the
94
90
  // events in the range that match the requested message types.
95
91
  //
96
92
  // Each envelope includes the [envelopepb.EventStreamPosition] extension.
97
93
  //
98
- // The events appear in ascending offset order, both within each
99
- // multi-envelope and across the list. Their offsets may have gaps, because
100
- // events of other types are omitted. There is no additional relationship
101
- // implied between envelopes that appear in the same segment.
102
- repeated dogma.protobuf.v1.MultiEnvelope envelopes = 4;
94
+ // The events appear in ascending offset order. Their offsets may have gaps,
95
+ // because events of other types are omitted. There is no additional
96
+ // relationship implied between envelopes that appear in the same segment.
97
+ repeated dogma.protobuf.v1.Envelope envelopes = 4;
103
98
  }
@@ -0,0 +1,26 @@
1
+ edition = "2024";
2
+ package dogma.messaging.v1;
3
+
4
+ option go_package = "github.com/dogmatiq/protobuf/messaginggrpc";
5
+
6
+ import "google/protobuf/empty.proto";
7
+ import "github.com/dogmatiq/protobuf/uuidpb/uuid.proto";
8
+
9
+ // MessageFilter describes a set of messages to deliver.
10
+ message MessageFilter {
11
+ // MessageTypes describes a set of specific message types.
12
+ message MessageTypes {
13
+ // Ids is the set of message type IDs that match the filter.
14
+ // The list must not be empty.
15
+ repeated dogma.protobuf.v1.UUID ids = 1;
16
+ }
17
+
18
+ // Types determines which message types match. One alternative must be set.
19
+ oneof types {
20
+ // All matches all message types.
21
+ google.protobuf.Empty all = 1;
22
+
23
+ // Specific matches the listed message types.
24
+ MessageTypes specific = 2;
25
+ }
26
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dogmatiq/protobuf",
3
- "version": "0.4.1",
3
+ "version": "0.5.0",
4
4
  "description": "Protocol Buffers and gRPC definitions for Dogma",
5
5
  "keywords": [
6
6
  "dogma",
@@ -62,7 +62,7 @@
62
62
  "@arethetypeswrong/cli": "^0.18.5",
63
63
  "@bufbuild/protobuf": "^2.16.0",
64
64
  "@bufbuild/protoc-gen-es": "^2.16.0",
65
- "@types/node": "^20.19.43",
65
+ "@types/node": "^26.6.6",
66
66
  "@vitest/coverage-v8": "^5.0.5",
67
67
  "prettier": "^3.9.10",
68
68
  "prettier-plugin-organize-imports": "^4.3.0",
package/uuidpb/uuid.proto CHANGED
@@ -4,6 +4,9 @@ package dogma.protobuf.v1;
4
4
  option go_package = "github.com/dogmatiq/protobuf/uuidpb";
5
5
 
6
6
  // UUID is a size-optimized representation of an RFC 9562 UUID.
7
+ //
8
+ // UUID values MUST use version 4 (random) or version 5 (name-based), and the
9
+ // RFC 9562 variant.
7
10
  message UUID {
8
11
  uint64 upper = 1 [features.field_presence = IMPLICIT];
9
12
  uint64 lower = 2 [features.field_presence = IMPLICIT];