@smthrs/notifications 0.0.0-stage → 1.0.0-rc.3

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.
Files changed (137) hide show
  1. package/CHANGELOG.md +127 -0
  2. package/LICENSE +21 -0
  3. package/README.md +91 -2
  4. package/dist/cjs/AlertPolicy.d.ts +284 -0
  5. package/dist/cjs/AlertPolicy.d.ts.map +1 -0
  6. package/dist/cjs/AlertPolicy.js +124 -0
  7. package/dist/cjs/AlertPolicy.js.map +7 -0
  8. package/dist/cjs/AlertRuntime.d.ts +90 -0
  9. package/dist/cjs/AlertRuntime.d.ts.map +1 -0
  10. package/dist/cjs/AlertRuntime.js +166 -0
  11. package/dist/cjs/AlertRuntime.js.map +7 -0
  12. package/dist/cjs/AlertSink.d.ts +101 -0
  13. package/dist/cjs/AlertSink.d.ts.map +1 -0
  14. package/dist/cjs/AlertSink.js +44 -0
  15. package/dist/cjs/AlertSink.js.map +7 -0
  16. package/dist/cjs/Alerts.d.ts +57 -0
  17. package/dist/cjs/Alerts.d.ts.map +1 -0
  18. package/dist/cjs/Alerts.js +46 -0
  19. package/dist/cjs/Alerts.js.map +7 -0
  20. package/dist/cjs/Notification.d.ts +214 -0
  21. package/dist/cjs/Notification.d.ts.map +1 -0
  22. package/dist/cjs/Notification.js +62 -0
  23. package/dist/cjs/Notification.js.map +7 -0
  24. package/dist/cjs/NotificationEvent.d.ts +214 -0
  25. package/dist/cjs/NotificationEvent.d.ts.map +1 -0
  26. package/dist/cjs/NotificationEvent.js +84 -0
  27. package/dist/cjs/NotificationEvent.js.map +7 -0
  28. package/dist/cjs/NotificationQueue.d.ts +193 -0
  29. package/dist/cjs/NotificationQueue.d.ts.map +1 -0
  30. package/dist/cjs/NotificationQueue.js +478 -0
  31. package/dist/cjs/NotificationQueue.js.map +7 -0
  32. package/dist/cjs/NotificationState.d.ts +169 -0
  33. package/dist/cjs/NotificationState.d.ts.map +1 -0
  34. package/dist/cjs/NotificationState.js +110 -0
  35. package/dist/cjs/NotificationState.js.map +7 -0
  36. package/dist/cjs/Projection.d.ts +27 -0
  37. package/dist/cjs/Projection.d.ts.map +1 -0
  38. package/dist/cjs/Projection.js +54 -0
  39. package/dist/cjs/Projection.js.map +7 -0
  40. package/dist/cjs/SteerPayload.d.ts +177 -0
  41. package/dist/cjs/SteerPayload.d.ts.map +1 -0
  42. package/dist/cjs/SteerPayload.js +61 -0
  43. package/dist/cjs/SteerPayload.js.map +7 -0
  44. package/dist/cjs/index.d.ts +41 -0
  45. package/dist/cjs/index.d.ts.map +1 -0
  46. package/dist/cjs/index.js +47 -0
  47. package/dist/cjs/index.js.map +7 -0
  48. package/dist/cjs/internal/foldCache.d.ts +36 -0
  49. package/dist/cjs/internal/foldCache.d.ts.map +1 -0
  50. package/dist/cjs/internal/foldCache.js +54 -0
  51. package/dist/cjs/internal/foldCache.js.map +7 -0
  52. package/dist/cjs/layerWebhook.d.ts +53 -0
  53. package/dist/cjs/layerWebhook.d.ts.map +1 -0
  54. package/dist/cjs/layerWebhook.js +107 -0
  55. package/dist/cjs/layerWebhook.js.map +7 -0
  56. package/dist/cjs/package.json +1 -0
  57. package/dist/esm/AlertPolicy.d.ts +284 -0
  58. package/dist/esm/AlertPolicy.d.ts.map +1 -0
  59. package/dist/esm/AlertPolicy.js +284 -0
  60. package/dist/esm/AlertPolicy.js.map +1 -0
  61. package/dist/esm/AlertRuntime.d.ts +90 -0
  62. package/dist/esm/AlertRuntime.d.ts.map +1 -0
  63. package/dist/esm/AlertRuntime.js +191 -0
  64. package/dist/esm/AlertRuntime.js.map +1 -0
  65. package/dist/esm/AlertSink.d.ts +101 -0
  66. package/dist/esm/AlertSink.d.ts.map +1 -0
  67. package/dist/esm/AlertSink.js +63 -0
  68. package/dist/esm/AlertSink.js.map +1 -0
  69. package/dist/esm/Alerts.d.ts +57 -0
  70. package/dist/esm/Alerts.d.ts.map +1 -0
  71. package/dist/esm/Alerts.js +57 -0
  72. package/dist/esm/Alerts.js.map +1 -0
  73. package/dist/esm/Notification.d.ts +214 -0
  74. package/dist/esm/Notification.d.ts.map +1 -0
  75. package/dist/esm/Notification.js +121 -0
  76. package/dist/esm/Notification.js.map +1 -0
  77. package/dist/esm/NotificationEvent.d.ts +214 -0
  78. package/dist/esm/NotificationEvent.d.ts.map +1 -0
  79. package/dist/esm/NotificationEvent.js +158 -0
  80. package/dist/esm/NotificationEvent.js.map +1 -0
  81. package/dist/esm/NotificationQueue.d.ts +193 -0
  82. package/dist/esm/NotificationQueue.d.ts.map +1 -0
  83. package/dist/esm/NotificationQueue.js +626 -0
  84. package/dist/esm/NotificationQueue.js.map +1 -0
  85. package/dist/esm/NotificationState.d.ts +169 -0
  86. package/dist/esm/NotificationState.d.ts.map +1 -0
  87. package/dist/esm/NotificationState.js +191 -0
  88. package/dist/esm/NotificationState.js.map +1 -0
  89. package/dist/esm/Projection.d.ts +27 -0
  90. package/dist/esm/Projection.d.ts.map +1 -0
  91. package/dist/esm/Projection.js +43 -0
  92. package/dist/esm/Projection.js.map +1 -0
  93. package/dist/esm/SteerPayload.d.ts +177 -0
  94. package/dist/esm/SteerPayload.d.ts.map +1 -0
  95. package/dist/esm/SteerPayload.js +139 -0
  96. package/dist/esm/SteerPayload.js.map +1 -0
  97. package/dist/esm/index.d.ts +41 -0
  98. package/dist/esm/index.d.ts.map +1 -0
  99. package/dist/esm/index.js +41 -0
  100. package/dist/esm/index.js.map +1 -0
  101. package/dist/esm/internal/foldCache.d.ts +36 -0
  102. package/dist/esm/internal/foldCache.d.ts.map +1 -0
  103. package/dist/esm/internal/foldCache.js +58 -0
  104. package/dist/esm/internal/foldCache.js.map +1 -0
  105. package/dist/esm/layerWebhook.d.ts +53 -0
  106. package/dist/esm/layerWebhook.d.ts.map +1 -0
  107. package/dist/esm/layerWebhook.js +118 -0
  108. package/dist/esm/layerWebhook.js.map +1 -0
  109. package/docs/README.md +144 -0
  110. package/docs/api.md +439 -0
  111. package/docs/concepts/admission-and-promotion.md +144 -0
  112. package/docs/concepts/alerting.md +138 -0
  113. package/docs/concepts/journal-records.md +132 -0
  114. package/docs/guides/alert-on-a-stuck-run.md +140 -0
  115. package/docs/guides/drain-at-a-turn-boundary.md +95 -0
  116. package/docs/guides/handle-a-full-queue.md +102 -0
  117. package/docs/guides/report-pending-notifications.md +92 -0
  118. package/docs/guides/send-alerts-to-a-webhook.md +130 -0
  119. package/docs/guides/steer-a-run.md +127 -0
  120. package/docs/guides/testing.md +161 -0
  121. package/docs/installation.md +119 -0
  122. package/docs/quickstart.md +159 -0
  123. package/docs/troubleshooting.md +254 -0
  124. package/package.json +182 -3
  125. package/src/AlertPolicy.ts +377 -0
  126. package/src/AlertRuntime.ts +280 -0
  127. package/src/AlertSink.ts +105 -0
  128. package/src/Alerts.ts +71 -0
  129. package/src/Notification.ts +171 -0
  130. package/src/NotificationEvent.ts +213 -0
  131. package/src/NotificationQueue.ts +857 -0
  132. package/src/NotificationState.ts +295 -0
  133. package/src/Projection.ts +46 -0
  134. package/src/SteerPayload.ts +198 -0
  135. package/src/index.ts +47 -0
  136. package/src/internal/foldCache.ts +72 -0
  137. package/src/layerWebhook.ts +161 -0
@@ -0,0 +1,214 @@
1
+ /**
2
+ * Durable notification payloads and their admission classification.
3
+ *
4
+ * @since 0.1.0
5
+ */
6
+ import { Schema } from "effect";
7
+ /**
8
+ * Producer-supplied attribution carried as data alongside the authenticated
9
+ * source actor. These labels never authorize a notification or a model action.
10
+ * They pass through the journal's ordinary redaction policy.
11
+ *
12
+ * @category models
13
+ * @since 1.0.0
14
+ */
15
+ export declare const Attribution: Schema.$Record<Schema.String, Schema.String>;
16
+ /**
17
+ * Durable origin of a notification: who said it, from where, and at which
18
+ * turn.
19
+ *
20
+ * Provenance travels with the notification because the run that receives one
21
+ * is not the run that wrote it. An operator steer, a parent run's event, and a
22
+ * webhook all arrive on the same queue, and only these fields say which is
23
+ * which after the fact.
24
+ *
25
+ * @category models
26
+ * @since 0.1.0
27
+ */
28
+ export declare const Provenance: Schema.Struct<{
29
+ readonly sourceRunId: Schema.String;
30
+ readonly sourceLineageId: Schema.String;
31
+ readonly sourceTurn: Schema.Int;
32
+ readonly sourceActor: Schema.String;
33
+ readonly attribution: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
34
+ }>;
35
+ /**
36
+ * The decoded form of {@link Provenance}.
37
+ *
38
+ * @category models
39
+ * @since 0.1.0
40
+ */
41
+ export type Provenance = typeof Provenance.Type;
42
+ /**
43
+ * A human message that must reach the model before the current turn closes.
44
+ *
45
+ * `id` is the caller's idempotency key: admitting it twice with the same
46
+ * content is one notification, and admitting it with different content is a
47
+ * producer bug the queue refuses. New admissions fingerprint the validated
48
+ * content before journal redaction, so redaction does not change retry identity.
49
+ *
50
+ * @category models
51
+ * @since 0.1.0
52
+ */
53
+ export declare const HumanSteer: Schema.TaggedStruct<"human-steer", {
54
+ readonly delivery: Schema.Literal<"steer">;
55
+ readonly id: Schema.NonEmptyString;
56
+ readonly targetLineageId: Schema.NonEmptyString;
57
+ readonly provenance: Schema.Struct<{
58
+ readonly sourceRunId: Schema.String;
59
+ readonly sourceLineageId: Schema.String;
60
+ readonly sourceTurn: Schema.Int;
61
+ readonly sourceActor: Schema.String;
62
+ readonly attribution: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
63
+ }>;
64
+ readonly payload: Schema.Codec<Schema.Json, Schema.Json, never, never>;
65
+ }>;
66
+ /**
67
+ * The decoded form of {@link HumanSteer}.
68
+ *
69
+ * @category models
70
+ * @since 0.1.0
71
+ */
72
+ export type HumanSteer = typeof HumanSteer.Type;
73
+ /**
74
+ * A human message that waits for the run to become idle.
75
+ *
76
+ * The delivery class is the whole difference from {@link HumanSteer}: a
77
+ * follow-up is not urgent enough to interrupt the turn in flight, so it is
78
+ * promoted one at a time when the run would otherwise have nothing to do.
79
+ *
80
+ * @category models
81
+ * @since 0.1.0
82
+ */
83
+ export declare const HumanFollowup: Schema.TaggedStruct<"human-followup", {
84
+ readonly delivery: Schema.Literal<"queue">;
85
+ readonly id: Schema.NonEmptyString;
86
+ readonly targetLineageId: Schema.NonEmptyString;
87
+ readonly provenance: Schema.Struct<{
88
+ readonly sourceRunId: Schema.String;
89
+ readonly sourceLineageId: Schema.String;
90
+ readonly sourceTurn: Schema.Int;
91
+ readonly sourceActor: Schema.String;
92
+ readonly attribution: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
93
+ }>;
94
+ readonly payload: Schema.Codec<Schema.Json, Schema.Json, never, never>;
95
+ }>;
96
+ /**
97
+ * The decoded form of {@link HumanFollowup}.
98
+ *
99
+ * @category models
100
+ * @since 0.1.0
101
+ */
102
+ export type HumanFollowup = typeof HumanFollowup.Type;
103
+ /**
104
+ * A machine-originated event. Events sharing a coalescing key and a target
105
+ * lineage collapse to the most recent one while pending.
106
+ *
107
+ * Coalescing is what keeps a chatty producer from filling the queue: ten
108
+ * updates about one condition are one pending notification carrying the latest
109
+ * of them. The key is scoped to `targetLineageId`, so two branches reporting
110
+ * one condition under the same ordinary key each keep their own event. An
111
+ * event with no key never coalesces.
112
+ *
113
+ * @category models
114
+ * @since 0.1.0
115
+ */
116
+ export declare const SystemEvent: Schema.TaggedStruct<"system-event", {
117
+ readonly delivery: Schema.Literal<"queue">;
118
+ readonly coalescingKey: Schema.optional<Schema.String>;
119
+ readonly id: Schema.NonEmptyString;
120
+ readonly targetLineageId: Schema.NonEmptyString;
121
+ readonly provenance: Schema.Struct<{
122
+ readonly sourceRunId: Schema.String;
123
+ readonly sourceLineageId: Schema.String;
124
+ readonly sourceTurn: Schema.Int;
125
+ readonly sourceActor: Schema.String;
126
+ readonly attribution: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
127
+ }>;
128
+ readonly payload: Schema.Codec<Schema.Json, Schema.Json, never, never>;
129
+ }>;
130
+ /**
131
+ * The decoded form of {@link SystemEvent}.
132
+ *
133
+ * @category models
134
+ * @since 0.1.0
135
+ */
136
+ export type SystemEvent = typeof SystemEvent.Type;
137
+ /**
138
+ * Any durable notification retained by the pending queue.
139
+ *
140
+ * `NotificationQueue.admit` decodes its argument against this union before it
141
+ * journals anything, so a value that does not match it is refused rather than
142
+ * acknowledged and then skipped by every replay. Journal-backed queues deeply
143
+ * freeze decoded notifications, including payloads, before returning them from
144
+ * `pending` or `drain`.
145
+ *
146
+ * @category models
147
+ * @since 0.1.0
148
+ */
149
+ export declare const Notification: Schema.Union<readonly [Schema.TaggedStruct<"human-steer", {
150
+ readonly delivery: Schema.Literal<"steer">;
151
+ readonly id: Schema.NonEmptyString;
152
+ readonly targetLineageId: Schema.NonEmptyString;
153
+ readonly provenance: Schema.Struct<{
154
+ readonly sourceRunId: Schema.String;
155
+ readonly sourceLineageId: Schema.String;
156
+ readonly sourceTurn: Schema.Int;
157
+ readonly sourceActor: Schema.String;
158
+ readonly attribution: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
159
+ }>;
160
+ readonly payload: Schema.Codec<Schema.Json, Schema.Json, never, never>;
161
+ }>, Schema.TaggedStruct<"human-followup", {
162
+ readonly delivery: Schema.Literal<"queue">;
163
+ readonly id: Schema.NonEmptyString;
164
+ readonly targetLineageId: Schema.NonEmptyString;
165
+ readonly provenance: Schema.Struct<{
166
+ readonly sourceRunId: Schema.String;
167
+ readonly sourceLineageId: Schema.String;
168
+ readonly sourceTurn: Schema.Int;
169
+ readonly sourceActor: Schema.String;
170
+ readonly attribution: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
171
+ }>;
172
+ readonly payload: Schema.Codec<Schema.Json, Schema.Json, never, never>;
173
+ }>, Schema.TaggedStruct<"system-event", {
174
+ readonly delivery: Schema.Literal<"queue">;
175
+ readonly coalescingKey: Schema.optional<Schema.String>;
176
+ readonly id: Schema.NonEmptyString;
177
+ readonly targetLineageId: Schema.NonEmptyString;
178
+ readonly provenance: Schema.Struct<{
179
+ readonly sourceRunId: Schema.String;
180
+ readonly sourceLineageId: Schema.String;
181
+ readonly sourceTurn: Schema.Int;
182
+ readonly sourceActor: Schema.String;
183
+ readonly attribution: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
184
+ }>;
185
+ readonly payload: Schema.Codec<Schema.Json, Schema.Json, never, never>;
186
+ }>]>;
187
+ /**
188
+ * Any durable notification retained by the pending queue.
189
+ *
190
+ * @category models
191
+ * @since 0.1.0
192
+ */
193
+ export type Notification = typeof Notification.Type;
194
+ /**
195
+ * How a notification is allowed to reach the model.
196
+ *
197
+ * `steer` notifications are promoted in a batch at turn close; `queue`
198
+ * notifications are promoted one at a time when the run would otherwise idle.
199
+ *
200
+ * @param notification the notification to classify
201
+ * @category getters
202
+ * @since 0.1.0
203
+ */
204
+ export declare const admissionClass: (notification: Notification) => "steer" | "queue";
205
+ /**
206
+ * The key a pending notification coalesces on, or `null` when it must never
207
+ * be coalesced. Only system events coalesce, and only when they declare a key.
208
+ *
209
+ * @param notification the notification to key
210
+ * @category getters
211
+ * @since 0.1.0
212
+ */
213
+ export declare const coalesceKey: (notification: Notification) => string | null;
214
+ //# sourceMappingURL=Notification.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Notification.d.ts","sourceRoot":"","sources":["../../src/Notification.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAE/B;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW,8CAA8C,CAAA;AAEtE;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,UAAU;;;;;;EAMrB,CAAA;AAEF;;;;;GAKG;AACH,MAAM,MAAM,UAAU,GAAG,OAAO,UAAU,CAAC,IAAI,CAAA;AAS/C;;;;;;;;;;GAUG;AACH,eAAO,MAAM,UAAU;;;;;;;;;;;;EAGrB,CAAA;AAEF;;;;;GAKG;AACH,MAAM,MAAM,UAAU,GAAG,OAAO,UAAU,CAAC,IAAI,CAAA;AAE/C;;;;;;;;;GASG;AACH,eAAO,MAAM,aAAa;;;;;;;;;;;;EAGxB,CAAA;AAEF;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GAAG,OAAO,aAAa,CAAC,IAAI,CAAA;AAErD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,WAAW;;;;;;;;;;;;;EAItB,CAAA;AAEF;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GAAG,OAAO,WAAW,CAAC,IAAI,CAAA;AAEjD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAAyD,CAAA;AAElF;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG,OAAO,YAAY,CAAC,IAAI,CAAA;AAEnD;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc,GAAI,cAAc,YAAY,KAAG,OAAO,GAAG,OAAgC,CAAA;AAEtG;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW,GAAI,cAAc,YAAY,KAAG,MAAM,GAAG,IACgB,CAAA"}
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var Notification_exports = {};
20
+ __export(Notification_exports, {
21
+ Attribution: () => Attribution,
22
+ HumanFollowup: () => HumanFollowup,
23
+ HumanSteer: () => HumanSteer,
24
+ Notification: () => Notification,
25
+ Provenance: () => Provenance,
26
+ SystemEvent: () => SystemEvent,
27
+ admissionClass: () => admissionClass,
28
+ coalesceKey: () => coalesceKey
29
+ });
30
+ module.exports = __toCommonJS(Notification_exports);
31
+ var import_effect = require("effect");
32
+ const Attribution = import_effect.Schema.Record(import_effect.Schema.String, import_effect.Schema.String);
33
+ const Provenance = import_effect.Schema.Struct({
34
+ sourceRunId: import_effect.Schema.String,
35
+ sourceLineageId: import_effect.Schema.String,
36
+ sourceTurn: import_effect.Schema.Int.pipe(import_effect.Schema.check(import_effect.Schema.isGreaterThanOrEqualTo(0))),
37
+ sourceActor: import_effect.Schema.String,
38
+ attribution: import_effect.Schema.optional(Attribution)
39
+ });
40
+ const common = {
41
+ id: import_effect.Schema.NonEmptyString,
42
+ targetLineageId: import_effect.Schema.NonEmptyString,
43
+ provenance: Provenance,
44
+ payload: import_effect.Schema.Json
45
+ };
46
+ const HumanSteer = import_effect.Schema.TaggedStruct("human-steer", {
47
+ ...common,
48
+ delivery: import_effect.Schema.Literal("steer")
49
+ });
50
+ const HumanFollowup = import_effect.Schema.TaggedStruct("human-followup", {
51
+ ...common,
52
+ delivery: import_effect.Schema.Literal("queue")
53
+ });
54
+ const SystemEvent = import_effect.Schema.TaggedStruct("system-event", {
55
+ ...common,
56
+ delivery: import_effect.Schema.Literal("queue"),
57
+ coalescingKey: import_effect.Schema.optional(import_effect.Schema.String)
58
+ });
59
+ const Notification = import_effect.Schema.Union([HumanSteer, HumanFollowup, SystemEvent]);
60
+ const admissionClass = (notification) => notification.delivery;
61
+ const coalesceKey = (notification) => notification._tag === "system-event" ? notification.coalescingKey ?? null : null;
62
+ //# sourceMappingURL=Notification.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/Notification.ts"],
4
+ "sourcesContent": ["/**\n * Durable notification payloads and their admission classification.\n *\n * @since 0.1.0\n */\n\nimport { Schema } from \"effect\"\n\n/**\n * Producer-supplied attribution carried as data alongside the authenticated\n * source actor. These labels never authorize a notification or a model action.\n * They pass through the journal's ordinary redaction policy.\n *\n * @category models\n * @since 1.0.0\n */\nexport const Attribution = Schema.Record(Schema.String, Schema.String)\n\n/**\n * Durable origin of a notification: who said it, from where, and at which\n * turn.\n *\n * Provenance travels with the notification because the run that receives one\n * is not the run that wrote it. An operator steer, a parent run's event, and a\n * webhook all arrive on the same queue, and only these fields say which is\n * which after the fact.\n *\n * @category models\n * @since 0.1.0\n */\nexport const Provenance = Schema.Struct({\n sourceRunId: Schema.String,\n sourceLineageId: Schema.String,\n sourceTurn: Schema.Int.pipe(Schema.check(Schema.isGreaterThanOrEqualTo(0))),\n sourceActor: Schema.String,\n attribution: Schema.optional(Attribution)\n})\n\n/**\n * The decoded form of {@link Provenance}.\n *\n * @category models\n * @since 0.1.0\n */\nexport type Provenance = typeof Provenance.Type\n\nconst common = {\n id: Schema.NonEmptyString,\n targetLineageId: Schema.NonEmptyString,\n provenance: Provenance,\n payload: Schema.Json\n}\n\n/**\n * A human message that must reach the model before the current turn closes.\n *\n * `id` is the caller's idempotency key: admitting it twice with the same\n * content is one notification, and admitting it with different content is a\n * producer bug the queue refuses. New admissions fingerprint the validated\n * content before journal redaction, so redaction does not change retry identity.\n *\n * @category models\n * @since 0.1.0\n */\nexport const HumanSteer = Schema.TaggedStruct(\"human-steer\", {\n ...common,\n delivery: Schema.Literal(\"steer\")\n})\n\n/**\n * The decoded form of {@link HumanSteer}.\n *\n * @category models\n * @since 0.1.0\n */\nexport type HumanSteer = typeof HumanSteer.Type\n\n/**\n * A human message that waits for the run to become idle.\n *\n * The delivery class is the whole difference from {@link HumanSteer}: a\n * follow-up is not urgent enough to interrupt the turn in flight, so it is\n * promoted one at a time when the run would otherwise have nothing to do.\n *\n * @category models\n * @since 0.1.0\n */\nexport const HumanFollowup = Schema.TaggedStruct(\"human-followup\", {\n ...common,\n delivery: Schema.Literal(\"queue\")\n})\n\n/**\n * The decoded form of {@link HumanFollowup}.\n *\n * @category models\n * @since 0.1.0\n */\nexport type HumanFollowup = typeof HumanFollowup.Type\n\n/**\n * A machine-originated event. Events sharing a coalescing key and a target\n * lineage collapse to the most recent one while pending.\n *\n * Coalescing is what keeps a chatty producer from filling the queue: ten\n * updates about one condition are one pending notification carrying the latest\n * of them. The key is scoped to `targetLineageId`, so two branches reporting\n * one condition under the same ordinary key each keep their own event. An\n * event with no key never coalesces.\n *\n * @category models\n * @since 0.1.0\n */\nexport const SystemEvent = Schema.TaggedStruct(\"system-event\", {\n ...common,\n delivery: Schema.Literal(\"queue\"),\n coalescingKey: Schema.optional(Schema.String)\n})\n\n/**\n * The decoded form of {@link SystemEvent}.\n *\n * @category models\n * @since 0.1.0\n */\nexport type SystemEvent = typeof SystemEvent.Type\n\n/**\n * Any durable notification retained by the pending queue.\n *\n * `NotificationQueue.admit` decodes its argument against this union before it\n * journals anything, so a value that does not match it is refused rather than\n * acknowledged and then skipped by every replay. Journal-backed queues deeply\n * freeze decoded notifications, including payloads, before returning them from\n * `pending` or `drain`.\n *\n * @category models\n * @since 0.1.0\n */\nexport const Notification = Schema.Union([HumanSteer, HumanFollowup, SystemEvent])\n\n/**\n * Any durable notification retained by the pending queue.\n *\n * @category models\n * @since 0.1.0\n */\nexport type Notification = typeof Notification.Type\n\n/**\n * How a notification is allowed to reach the model.\n *\n * `steer` notifications are promoted in a batch at turn close; `queue`\n * notifications are promoted one at a time when the run would otherwise idle.\n *\n * @param notification the notification to classify\n * @category getters\n * @since 0.1.0\n */\nexport const admissionClass = (notification: Notification): \"steer\" | \"queue\" => notification.delivery\n\n/**\n * The key a pending notification coalesces on, or `null` when it must never\n * be coalesced. Only system events coalesce, and only when they declare a key.\n *\n * @param notification the notification to key\n * @category getters\n * @since 0.1.0\n */\nexport const coalesceKey = (notification: Notification): string | null =>\n notification._tag === \"system-event\" ? notification.coalescingKey ?? null : null\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAMA,oBAAuB;AAUhB,MAAM,cAAc,qBAAO,OAAO,qBAAO,QAAQ,qBAAO,MAAM;AAc9D,MAAM,aAAa,qBAAO,OAAO;AAAA,EACtC,aAAa,qBAAO;AAAA,EACpB,iBAAiB,qBAAO;AAAA,EACxB,YAAY,qBAAO,IAAI,KAAK,qBAAO,MAAM,qBAAO,uBAAuB,CAAC,CAAC,CAAC;AAAA,EAC1E,aAAa,qBAAO;AAAA,EACpB,aAAa,qBAAO,SAAS,WAAW;AAC1C,CAAC;AAUD,MAAM,SAAS;AAAA,EACb,IAAI,qBAAO;AAAA,EACX,iBAAiB,qBAAO;AAAA,EACxB,YAAY;AAAA,EACZ,SAAS,qBAAO;AAClB;AAaO,MAAM,aAAa,qBAAO,aAAa,eAAe;AAAA,EAC3D,GAAG;AAAA,EACH,UAAU,qBAAO,QAAQ,OAAO;AAClC,CAAC;AAoBM,MAAM,gBAAgB,qBAAO,aAAa,kBAAkB;AAAA,EACjE,GAAG;AAAA,EACH,UAAU,qBAAO,QAAQ,OAAO;AAClC,CAAC;AAuBM,MAAM,cAAc,qBAAO,aAAa,gBAAgB;AAAA,EAC7D,GAAG;AAAA,EACH,UAAU,qBAAO,QAAQ,OAAO;AAAA,EAChC,eAAe,qBAAO,SAAS,qBAAO,MAAM;AAC9C,CAAC;AAsBM,MAAM,eAAe,qBAAO,MAAM,CAAC,YAAY,eAAe,WAAW,CAAC;AAoB1E,MAAM,iBAAiB,CAAC,iBAAkD,aAAa;AAUvF,MAAM,cAAc,CAAC,iBAC1B,aAAa,SAAS,iBAAiB,aAAa,iBAAiB,OAAO;",
6
+ "names": []
7
+ }
@@ -0,0 +1,214 @@
1
+ /**
2
+ * Journal event types owned by the notification queue.
3
+ *
4
+ * @since 0.1.0
5
+ */
6
+ import { JournalEvent } from "@smthrs/journal";
7
+ import * as Option from "effect/Option";
8
+ import * as Schema from "effect/Schema";
9
+ /**
10
+ * The journal `eventType` recorded for one admission decision.
11
+ *
12
+ * The spelling is slashes and a PascalCase leaf, where every other event type
13
+ * in this repository is dot-separated and lowercase (`control.run.parked`,
14
+ * `flows.alerts.delivered`). The value is already durable in every engine
15
+ * database, and a rename would silently stop matching the projections that
16
+ * consumers key on, so it stays as written. A foreign projection must match
17
+ * this literal rather than the repository convention, which is why the two
18
+ * spellings are frozen by `test/WireFormat.test.ts`.
19
+ *
20
+ * @category constants
21
+ * @since 0.1.0
22
+ */
23
+ export declare const AdmittedEventType = "flows/notifications/Admitted";
24
+ /**
25
+ * The journal `eventType` recorded when pending notifications are promoted.
26
+ *
27
+ * Spelled like {@link AdmittedEventType}, and frozen for the same reason.
28
+ *
29
+ * @category constants
30
+ * @since 0.1.0
31
+ */
32
+ export declare const PromotedEventType = "flows/notifications/Promoted";
33
+ /**
34
+ * What a durable admission record says was decided.
35
+ *
36
+ * This is the one declaration of the admission vocabulary: it validates every
37
+ * journal entry through {@link fromEntry}, and `NotificationState` types its
38
+ * public signatures against it, so a fourth decision cannot be added to one
39
+ * half and forgotten in the other.
40
+ *
41
+ * @category models
42
+ * @since 1.0.0
43
+ */
44
+ export declare const AdmissionDecision: Schema.Literals<readonly ["admitted", "coalesced", "rejected-full"]>;
45
+ /**
46
+ * The decoded form of {@link AdmissionDecision}.
47
+ *
48
+ * @category models
49
+ * @since 1.0.0
50
+ */
51
+ export type AdmissionDecision = typeof AdmissionDecision.Type;
52
+ /**
53
+ * A durable admission record: the notification a run was told about, and what
54
+ * the queue decided to do with it.
55
+ *
56
+ * A `rejected-full` decision is never written. The queue refuses a full queue
57
+ * in the receipt alone, so the notification id stays admissible once a
58
+ * boundary drains. The literal remains in {@link AdmissionDecision} because a
59
+ * reader must stay total over a record any writer could have produced.
60
+ *
61
+ * @category models
62
+ * @since 0.1.0
63
+ */
64
+ export declare const Admitted: Schema.Struct<{
65
+ readonly notification: Schema.Union<readonly [Schema.TaggedStruct<"human-steer", {
66
+ readonly delivery: Schema.Literal<"steer">;
67
+ readonly id: Schema.NonEmptyString;
68
+ readonly targetLineageId: Schema.NonEmptyString;
69
+ readonly provenance: Schema.Struct<{
70
+ readonly sourceRunId: Schema.String;
71
+ readonly sourceLineageId: Schema.String;
72
+ readonly sourceTurn: Schema.Int;
73
+ readonly sourceActor: Schema.String;
74
+ readonly attribution: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
75
+ }>;
76
+ readonly payload: Schema.Codec<Schema.Json, Schema.Json, never, never>;
77
+ }>, Schema.TaggedStruct<"human-followup", {
78
+ readonly delivery: Schema.Literal<"queue">;
79
+ readonly id: Schema.NonEmptyString;
80
+ readonly targetLineageId: Schema.NonEmptyString;
81
+ readonly provenance: Schema.Struct<{
82
+ readonly sourceRunId: Schema.String;
83
+ readonly sourceLineageId: Schema.String;
84
+ readonly sourceTurn: Schema.Int;
85
+ readonly sourceActor: Schema.String;
86
+ readonly attribution: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
87
+ }>;
88
+ readonly payload: Schema.Codec<Schema.Json, Schema.Json, never, never>;
89
+ }>, Schema.TaggedStruct<"system-event", {
90
+ readonly delivery: Schema.Literal<"queue">;
91
+ readonly coalescingKey: Schema.optional<Schema.String>;
92
+ readonly id: Schema.NonEmptyString;
93
+ readonly targetLineageId: Schema.NonEmptyString;
94
+ readonly provenance: Schema.Struct<{
95
+ readonly sourceRunId: Schema.String;
96
+ readonly sourceLineageId: Schema.String;
97
+ readonly sourceTurn: Schema.Int;
98
+ readonly sourceActor: Schema.String;
99
+ readonly attribution: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
100
+ }>;
101
+ readonly payload: Schema.Codec<Schema.Json, Schema.Json, never, never>;
102
+ }>]>;
103
+ readonly decision: Schema.Literals<readonly ["admitted", "coalesced", "rejected-full"]>;
104
+ /** SHA-256 of canonical validated input, absent in legacy admission rows. */
105
+ readonly fingerprint: Schema.optional<Schema.String>;
106
+ readonly version: Schema.optional<Schema.Number>;
107
+ }>;
108
+ /**
109
+ * The decoded form of {@link Admitted}.
110
+ *
111
+ * @category models
112
+ * @since 0.1.0
113
+ */
114
+ export type Admitted = typeof Admitted.Type;
115
+ /**
116
+ * A durable promotion record: which notifications one boundary of one lineage
117
+ * delivered.
118
+ *
119
+ * The triple `(runId, targetLineageId, boundary)` is the unit of promotion, so
120
+ * a second lineage draining the same boundary name is a separate record rather
121
+ * than a repeat of the first.
122
+ *
123
+ * @category models
124
+ * @since 0.1.0
125
+ */
126
+ export declare const Promoted: Schema.Struct<{
127
+ readonly boundary: Schema.String;
128
+ readonly targetLineageId: Schema.String;
129
+ readonly ids: Schema.$Array<Schema.String>;
130
+ }>;
131
+ /**
132
+ * The decoded form of {@link Promoted}.
133
+ *
134
+ * @category models
135
+ * @since 0.1.0
136
+ */
137
+ export type Promoted = typeof Promoted.Type;
138
+ /**
139
+ * Any journal event owned by this package.
140
+ *
141
+ * The two members are told apart with {@link isAdmitted} and
142
+ * {@link isPromoted} rather than a stored discriminant, because the discriminant
143
+ * would be a durable field this vocabulary does not have.
144
+ *
145
+ * @category models
146
+ * @since 0.1.0
147
+ */
148
+ export type Event = Admitted | Promoted;
149
+ /**
150
+ * Whether an owned event is an admission record.
151
+ *
152
+ * @param event an owned event
153
+ * @category refinements
154
+ * @since 1.0.0
155
+ */
156
+ export declare const isAdmitted: (event: Event) => event is Admitted;
157
+ /**
158
+ * Whether an owned event is a promotion record.
159
+ *
160
+ * @param event an owned event
161
+ * @category refinements
162
+ * @since 1.0.0
163
+ */
164
+ export declare const isPromoted: (event: Event) => event is Promoted;
165
+ /**
166
+ * The journal source every admission of one notification is written under.
167
+ *
168
+ * @param notificationId the admitted notification's id
169
+ * @category constructors
170
+ * @since 1.0.0
171
+ */
172
+ export declare const admissionSourceId: (notificationId: string) => JournalEvent.SourceId;
173
+ /**
174
+ * The identity of one drain: the lineage and the boundary, each encoded so a
175
+ * value containing a slash cannot forge another pair's identity.
176
+ *
177
+ * @param targetLineageId the lineage the boundary closed on
178
+ * @param boundary the boundary name
179
+ * @category constructors
180
+ * @since 1.0.0
181
+ */
182
+ export declare const drainKey: (targetLineageId: string, boundary: string) => string;
183
+ /**
184
+ * The journal source one drain's promotion record is written under.
185
+ *
186
+ * @param targetLineageId the lineage the boundary closed on
187
+ * @param boundary the boundary name
188
+ * @category constructors
189
+ * @since 1.0.0
190
+ */
191
+ export declare const drainSourceId: (targetLineageId: string, boundary: string) => JournalEvent.SourceId;
192
+ /**
193
+ * Decodes an owned notification event from a journal entry.
194
+ *
195
+ * Foreign entries and structurally invalid payloads decode to `None` so a
196
+ * projection over a shared journal stays total.
197
+ *
198
+ * The event type alone does not make an entry owned: any producer on a shared
199
+ * journal can choose one. An admission counts only under
200
+ * {@link admissionSourceId} for the notification it carries, and a promotion
201
+ * only under the `/notifications/drain/` source family the queue writes, so
202
+ * another producer's record with the same event type stays out of the
203
+ * projection. This scopes records; it does not authenticate the writer. The
204
+ * journal fences writes on run ownership, not on source, so a writer that
205
+ * sets this package's source id is read as this package. The drain check is
206
+ * a prefix because records written before the lineage joined the identity
207
+ * carry the boundary alone.
208
+ *
209
+ * @param entry one journal entry, owned or foreign
210
+ * @category constructors
211
+ * @since 0.1.0
212
+ */
213
+ export declare const fromEntry: (entry: JournalEvent.Entry) => Option.Option<Event>;
214
+ //# sourceMappingURL=NotificationEvent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NotificationEvent.d.ts","sourceRoot":"","sources":["../../src/NotificationEvent.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAC9C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAGvC;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,iBAAiB,iCAAiC,CAAA;AAE/D;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,iCAAiC,CAAA;AAE/D;;;;;;;;;;GAUG;AACH,eAAO,MAAM,iBAAiB,sEAA8D,CAAA;AAE5F;;;;;GAKG;AACH,MAAM,MAAM,iBAAiB,GAAG,OAAO,iBAAiB,CAAC,IAAI,CAAA;AAE7D;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAGnB,6EAA6E;;;EAG7E,CAAA;AAEF;;;;;GAKG;AACH,MAAM,MAAM,QAAQ,GAAG,OAAO,QAAQ,CAAC,IAAI,CAAA;AAE3C;;;;;;;;;;GAUG;AACH,eAAO,MAAM,QAAQ;;;;EAInB,CAAA;AAEF;;;;;GAKG;AACH,MAAM,MAAM,QAAQ,GAAG,OAAO,QAAQ,CAAC,IAAI,CAAA;AAE3C;;;;;;;;;GASG;AACH,MAAM,MAAM,KAAK,GAAG,QAAQ,GAAG,QAAQ,CAAA;AAEvC;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,GAAI,OAAO,KAAK,KAAG,KAAK,IAAI,QAAmC,CAAA;AAEtF;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,GAAI,OAAO,KAAK,KAAG,KAAK,IAAI,QAA8B,CAAA;AAEjF;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,GAAI,gBAAgB,MAAM,KAAG,YAAY,CAAC,QACC,CAAA;AAEzE;;;;;;;;GAQG;AACH,eAAO,MAAM,QAAQ,GAAI,iBAAiB,MAAM,EAAE,UAAU,MAAM,KAAG,MACK,CAAA;AAE1E;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,GAAI,iBAAiB,MAAM,EAAE,UAAU,MAAM,KAAG,YAAY,CAAC,QACG,CAAA;AAQ1F;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,SAAS,GAAI,OAAO,YAAY,CAAC,KAAK,KAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAWxE,CAAA"}
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var NotificationEvent_exports = {};
30
+ __export(NotificationEvent_exports, {
31
+ AdmissionDecision: () => AdmissionDecision,
32
+ Admitted: () => Admitted,
33
+ AdmittedEventType: () => AdmittedEventType,
34
+ Promoted: () => Promoted,
35
+ PromotedEventType: () => PromotedEventType,
36
+ admissionSourceId: () => admissionSourceId,
37
+ drainKey: () => drainKey,
38
+ drainSourceId: () => drainSourceId,
39
+ fromEntry: () => fromEntry,
40
+ isAdmitted: () => isAdmitted,
41
+ isPromoted: () => isPromoted
42
+ });
43
+ module.exports = __toCommonJS(NotificationEvent_exports);
44
+ var import_journal = require("@smthrs/journal");
45
+ var Option = __toESM(require("effect/Option"), 1);
46
+ var Schema = __toESM(require("effect/Schema"), 1);
47
+ var Notification = __toESM(require("./Notification.js"), 1);
48
+ const AdmittedEventType = "flows/notifications/Admitted";
49
+ const PromotedEventType = "flows/notifications/Promoted";
50
+ const AdmissionDecision = Schema.Literals(["admitted", "coalesced", "rejected-full"]);
51
+ const Admitted = Schema.Struct({
52
+ notification: Notification.Notification,
53
+ decision: AdmissionDecision,
54
+ /** SHA-256 of canonical validated input, absent in legacy admission rows. */
55
+ fingerprint: Schema.optional(Schema.String),
56
+ version: Schema.optional(Schema.Number)
57
+ });
58
+ const Promoted = Schema.Struct({
59
+ boundary: Schema.String,
60
+ targetLineageId: Schema.String,
61
+ ids: Schema.Array(Schema.String)
62
+ });
63
+ const isAdmitted = (event) => "notification" in event;
64
+ const isPromoted = (event) => !isAdmitted(event);
65
+ const admissionSourceId = (notificationId) => import_journal.JournalEvent.SourceId.make(`${admissionSourcePrefix}${notificationId}`);
66
+ const drainKey = (targetLineageId, boundary) => `${encodeURIComponent(targetLineageId)}/${encodeURIComponent(boundary)}`;
67
+ const drainSourceId = (targetLineageId, boundary) => import_journal.JournalEvent.SourceId.make(`${drainSourcePrefix}${drainKey(targetLineageId, boundary)}`);
68
+ const admissionSourcePrefix = "/notifications/admission/";
69
+ const drainSourcePrefix = "/notifications/drain/";
70
+ const decodeAdmitted = Schema.decodeUnknownOption(Admitted);
71
+ const decodePromoted = Schema.decodeUnknownOption(Promoted);
72
+ const fromEntry = (entry) => {
73
+ if (entry.eventType === AdmittedEventType) {
74
+ return Option.filter(
75
+ decodeAdmitted(entry.payload),
76
+ (event) => entry.sourceId === admissionSourceId(event.notification.id)
77
+ );
78
+ }
79
+ if (entry.eventType === PromotedEventType && entry.sourceId.startsWith(drainSourcePrefix)) {
80
+ return decodePromoted(entry.payload);
81
+ }
82
+ return Option.none();
83
+ };
84
+ //# sourceMappingURL=NotificationEvent.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/NotificationEvent.ts"],
4
+ "sourcesContent": ["/**\n * Journal event types owned by the notification queue.\n *\n * @since 0.1.0\n */\n\nimport { JournalEvent } from \"@smthrs/journal\"\nimport * as Option from \"effect/Option\"\nimport * as Schema from \"effect/Schema\"\nimport * as Notification from \"./Notification.ts\"\n\n/**\n * The journal `eventType` recorded for one admission decision.\n *\n * The spelling is slashes and a PascalCase leaf, where every other event type\n * in this repository is dot-separated and lowercase (`control.run.parked`,\n * `flows.alerts.delivered`). The value is already durable in every engine\n * database, and a rename would silently stop matching the projections that\n * consumers key on, so it stays as written. A foreign projection must match\n * this literal rather than the repository convention, which is why the two\n * spellings are frozen by `test/WireFormat.test.ts`.\n *\n * @category constants\n * @since 0.1.0\n */\nexport const AdmittedEventType = \"flows/notifications/Admitted\"\n\n/**\n * The journal `eventType` recorded when pending notifications are promoted.\n *\n * Spelled like {@link AdmittedEventType}, and frozen for the same reason.\n *\n * @category constants\n * @since 0.1.0\n */\nexport const PromotedEventType = \"flows/notifications/Promoted\"\n\n/**\n * What a durable admission record says was decided.\n *\n * This is the one declaration of the admission vocabulary: it validates every\n * journal entry through {@link fromEntry}, and `NotificationState` types its\n * public signatures against it, so a fourth decision cannot be added to one\n * half and forgotten in the other.\n *\n * @category models\n * @since 1.0.0\n */\nexport const AdmissionDecision = Schema.Literals([\"admitted\", \"coalesced\", \"rejected-full\"])\n\n/**\n * The decoded form of {@link AdmissionDecision}.\n *\n * @category models\n * @since 1.0.0\n */\nexport type AdmissionDecision = typeof AdmissionDecision.Type\n\n/**\n * A durable admission record: the notification a run was told about, and what\n * the queue decided to do with it.\n *\n * A `rejected-full` decision is never written. The queue refuses a full queue\n * in the receipt alone, so the notification id stays admissible once a\n * boundary drains. The literal remains in {@link AdmissionDecision} because a\n * reader must stay total over a record any writer could have produced.\n *\n * @category models\n * @since 0.1.0\n */\nexport const Admitted = Schema.Struct({\n notification: Notification.Notification,\n decision: AdmissionDecision,\n /** SHA-256 of canonical validated input, absent in legacy admission rows. */\n fingerprint: Schema.optional(Schema.String),\n version: Schema.optional(Schema.Number)\n})\n\n/**\n * The decoded form of {@link Admitted}.\n *\n * @category models\n * @since 0.1.0\n */\nexport type Admitted = typeof Admitted.Type\n\n/**\n * A durable promotion record: which notifications one boundary of one lineage\n * delivered.\n *\n * The triple `(runId, targetLineageId, boundary)` is the unit of promotion, so\n * a second lineage draining the same boundary name is a separate record rather\n * than a repeat of the first.\n *\n * @category models\n * @since 0.1.0\n */\nexport const Promoted = Schema.Struct({\n boundary: Schema.String,\n targetLineageId: Schema.String,\n ids: Schema.Array(Schema.String)\n})\n\n/**\n * The decoded form of {@link Promoted}.\n *\n * @category models\n * @since 0.1.0\n */\nexport type Promoted = typeof Promoted.Type\n\n/**\n * Any journal event owned by this package.\n *\n * The two members are told apart with {@link isAdmitted} and\n * {@link isPromoted} rather than a stored discriminant, because the discriminant\n * would be a durable field this vocabulary does not have.\n *\n * @category models\n * @since 0.1.0\n */\nexport type Event = Admitted | Promoted\n\n/**\n * Whether an owned event is an admission record.\n *\n * @param event an owned event\n * @category refinements\n * @since 1.0.0\n */\nexport const isAdmitted = (event: Event): event is Admitted => \"notification\" in event\n\n/**\n * Whether an owned event is a promotion record.\n *\n * @param event an owned event\n * @category refinements\n * @since 1.0.0\n */\nexport const isPromoted = (event: Event): event is Promoted => !isAdmitted(event)\n\n/**\n * The journal source every admission of one notification is written under.\n *\n * @param notificationId the admitted notification's id\n * @category constructors\n * @since 1.0.0\n */\nexport const admissionSourceId = (notificationId: string): JournalEvent.SourceId =>\n JournalEvent.SourceId.make(`${admissionSourcePrefix}${notificationId}`)\n\n/**\n * The identity of one drain: the lineage and the boundary, each encoded so a\n * value containing a slash cannot forge another pair's identity.\n *\n * @param targetLineageId the lineage the boundary closed on\n * @param boundary the boundary name\n * @category constructors\n * @since 1.0.0\n */\nexport const drainKey = (targetLineageId: string, boundary: string): string =>\n `${encodeURIComponent(targetLineageId)}/${encodeURIComponent(boundary)}`\n\n/**\n * The journal source one drain's promotion record is written under.\n *\n * @param targetLineageId the lineage the boundary closed on\n * @param boundary the boundary name\n * @category constructors\n * @since 1.0.0\n */\nexport const drainSourceId = (targetLineageId: string, boundary: string): JournalEvent.SourceId =>\n JournalEvent.SourceId.make(`${drainSourcePrefix}${drainKey(targetLineageId, boundary)}`)\n\nconst admissionSourcePrefix = \"/notifications/admission/\"\nconst drainSourcePrefix = \"/notifications/drain/\"\n\nconst decodeAdmitted = Schema.decodeUnknownOption(Admitted)\nconst decodePromoted = Schema.decodeUnknownOption(Promoted)\n\n/**\n * Decodes an owned notification event from a journal entry.\n *\n * Foreign entries and structurally invalid payloads decode to `None` so a\n * projection over a shared journal stays total.\n *\n * The event type alone does not make an entry owned: any producer on a shared\n * journal can choose one. An admission counts only under\n * {@link admissionSourceId} for the notification it carries, and a promotion\n * only under the `/notifications/drain/` source family the queue writes, so\n * another producer's record with the same event type stays out of the\n * projection. This scopes records; it does not authenticate the writer. The\n * journal fences writes on run ownership, not on source, so a writer that\n * sets this package's source id is read as this package. The drain check is\n * a prefix because records written before the lineage joined the identity\n * carry the boundary alone.\n *\n * @param entry one journal entry, owned or foreign\n * @category constructors\n * @since 0.1.0\n */\nexport const fromEntry = (entry: JournalEvent.Entry): Option.Option<Event> => {\n if (entry.eventType === AdmittedEventType) {\n return Option.filter(\n decodeAdmitted(entry.payload),\n (event) => entry.sourceId === admissionSourceId(event.notification.id)\n )\n }\n if (entry.eventType === PromotedEventType && entry.sourceId.startsWith(drainSourcePrefix)) {\n return decodePromoted(entry.payload)\n }\n return Option.none()\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAMA,qBAA6B;AAC7B,aAAwB;AACxB,aAAwB;AACxB,mBAA8B;AAgBvB,MAAM,oBAAoB;AAU1B,MAAM,oBAAoB;AAa1B,MAAM,oBAAoB,OAAO,SAAS,CAAC,YAAY,aAAa,eAAe,CAAC;AAsBpF,MAAM,WAAW,OAAO,OAAO;AAAA,EACpC,cAAc,aAAa;AAAA,EAC3B,UAAU;AAAA;AAAA,EAEV,aAAa,OAAO,SAAS,OAAO,MAAM;AAAA,EAC1C,SAAS,OAAO,SAAS,OAAO,MAAM;AACxC,CAAC;AAqBM,MAAM,WAAW,OAAO,OAAO;AAAA,EACpC,UAAU,OAAO;AAAA,EACjB,iBAAiB,OAAO;AAAA,EACxB,KAAK,OAAO,MAAM,OAAO,MAAM;AACjC,CAAC;AA6BM,MAAM,aAAa,CAAC,UAAoC,kBAAkB;AAS1E,MAAM,aAAa,CAAC,UAAoC,CAAC,WAAW,KAAK;AASzE,MAAM,oBAAoB,CAAC,mBAChC,4BAAa,SAAS,KAAK,GAAG,qBAAqB,GAAG,cAAc,EAAE;AAWjE,MAAM,WAAW,CAAC,iBAAyB,aAChD,GAAG,mBAAmB,eAAe,CAAC,IAAI,mBAAmB,QAAQ,CAAC;AAUjE,MAAM,gBAAgB,CAAC,iBAAyB,aACrD,4BAAa,SAAS,KAAK,GAAG,iBAAiB,GAAG,SAAS,iBAAiB,QAAQ,CAAC,EAAE;AAEzF,MAAM,wBAAwB;AAC9B,MAAM,oBAAoB;AAE1B,MAAM,iBAAiB,OAAO,oBAAoB,QAAQ;AAC1D,MAAM,iBAAiB,OAAO,oBAAoB,QAAQ;AAuBnD,MAAM,YAAY,CAAC,UAAoD;AAC5E,MAAI,MAAM,cAAc,mBAAmB;AACzC,WAAO,OAAO;AAAA,MACZ,eAAe,MAAM,OAAO;AAAA,MAC5B,CAAC,UAAU,MAAM,aAAa,kBAAkB,MAAM,aAAa,EAAE;AAAA,IACvE;AAAA,EACF;AACA,MAAI,MAAM,cAAc,qBAAqB,MAAM,SAAS,WAAW,iBAAiB,GAAG;AACzF,WAAO,eAAe,MAAM,OAAO;AAAA,EACrC;AACA,SAAO,OAAO,KAAK;AACrB;",
6
+ "names": []
7
+ }