@fungi.computer/carrier 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -2
- package/dist/internal/post-sql.js +8 -4
- package/dist/post-outbox.d.ts +14 -2
- package/dist/post-outbox.js +67 -14
- package/dist/post.d.ts +1 -1
- package/dist/post.js +5 -4
- package/docs/api/post.md +24 -45
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -97,7 +97,8 @@ In a Durable Object, `ctx.storage` is already the SQLite owner.
|
|
|
97
97
|
`createMemoryOutbox()` stores encoded string rows in process memory for tests
|
|
98
98
|
and quick starts. It provides `put`, `read`, key-ordered `list(limit, afterKey)`
|
|
99
99
|
and `delete`. Each instance is independent; it is not durable storage. BirdDog’s
|
|
100
|
-
[quick start](
|
|
100
|
+
[quick start](https://docs.fungi.computer/bird-dog/quickstart/) uses this
|
|
101
|
+
helper.
|
|
101
102
|
|
|
102
103
|
```ts
|
|
103
104
|
import { createMemoryOutbox } from "@fungi.computer/carrier/testing";
|
|
@@ -214,6 +215,12 @@ ambiguous first dispatch has zero. Post `attempts` counts receiver invocations.
|
|
|
214
215
|
|
|
215
216
|
## Post
|
|
216
217
|
|
|
218
|
+
Post uses Almanac's shared pure backoff calculation with its retained policy: 2
|
|
219
|
+
seconds initially, doubling to a 5-minute cap, with at most 20 receiver
|
|
220
|
+
attempts. The synchronous and SQL adapters share that default policy; explicit
|
|
221
|
+
SQL retry policies retain their own delays. Almanac supplies arithmetic only,
|
|
222
|
+
not a second scheduler or retry owner.
|
|
223
|
+
|
|
217
224
|
`@fungi.computer/carrier/post` delivers typed, point-to-point envelopes between
|
|
218
225
|
named Durable Objects. `definePostRoute` pairs each address schema and event
|
|
219
226
|
schema with its Durable Object namespace, name function and fixed `addressKind`
|
|
@@ -264,7 +271,11 @@ its reason and its attempt count. `deadLetters` returns `letters` and
|
|
|
264
271
|
|
|
265
272
|
The same retry and receipt machinery is exported as `makePostOutbox` for a
|
|
266
273
|
string-backed outbox. [BirdDog](../bird-dog/README.md) uses it for agent reply
|
|
267
|
-
delivery.
|
|
274
|
+
delivery. `postOutboxPending` reports whether such an outbox still owes any
|
|
275
|
+
message a delivery attempt; receipts and exhausted dead letters owe nothing. Its
|
|
276
|
+
`nextWakeAt()` reads the earliest owed delivery across the retained rows,
|
|
277
|
+
including future retries. Delivered receipts, exhausted dead letters and
|
|
278
|
+
malformed rows do not request a wake.
|
|
268
279
|
|
|
269
280
|
## Post transactions on D1 and PostgreSQL
|
|
270
281
|
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import * as Schema from "effect/Schema";
|
|
2
|
+
import { backoffDelay } from "@fungi.computer/almanac";
|
|
2
3
|
import { defineIdFamily } from "@fungi.computer/cairn";
|
|
3
4
|
import { PostEnvelopeSchema, PostError, } from "../post.js";
|
|
4
5
|
import { deliverPost, postDisposition, deliveryReceipt, } from "./post-execution.js";
|
|
5
|
-
import { maxDeliveryAttempts,
|
|
6
|
+
import { maxDeliveryAttempts, postRetryPolicy } from "../post-outbox.js";
|
|
6
7
|
const ids = defineIdFamily({
|
|
7
8
|
family: "post",
|
|
8
9
|
version: 1,
|
|
@@ -133,10 +134,13 @@ export function openSqlPost(database, options) {
|
|
|
133
134
|
maxDelayMs: Schema.Int.check(Schema.isGreaterThan(0)),
|
|
134
135
|
}))(options.retry ?? {
|
|
135
136
|
maxAttempts: maxDeliveryAttempts,
|
|
136
|
-
baseDelayMs:
|
|
137
|
-
maxDelayMs:
|
|
137
|
+
baseDelayMs: backoffDelay(1, postRetryPolicy),
|
|
138
|
+
maxDelayMs: backoffDelay(maxDeliveryAttempts, postRetryPolicy),
|
|
139
|
+
});
|
|
140
|
+
const delay = (attempt) => backoffDelay(attempt, {
|
|
141
|
+
baseMs: policy.baseDelayMs,
|
|
142
|
+
maxMs: policy.maxDelayMs,
|
|
138
143
|
});
|
|
139
|
-
const delay = (attempt) => Math.min(policy.baseDelayMs * 2 ** Math.max(0, attempt - 1), policy.maxDelayMs);
|
|
140
144
|
const retrySql = Array.from({ length: policy.maxAttempts }, (_, index) => `WHEN ${String(index)} THEN ${String(delay(index + 1))}`).join(" ");
|
|
141
145
|
for (const route of Object.values(options.routes)) {
|
|
142
146
|
// Synchronous owner hooks cannot participate in an asynchronous transaction.
|
package/dist/post-outbox.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import * as Effect from "effect/Effect";
|
|
2
2
|
import * as Schema from "effect/Schema";
|
|
3
|
+
import { type RetryPolicy } from "@fungi.computer/almanac";
|
|
3
4
|
/** Maximum receiver attempts retained from BirdDog. */
|
|
4
5
|
export declare const maxDeliveryAttempts = 20;
|
|
5
|
-
/** BirdDog's retained
|
|
6
|
-
export declare const
|
|
6
|
+
/** BirdDog's retained receiver retry policy, shared by every Post adapter. */
|
|
7
|
+
export declare const postRetryPolicy: RetryPolicy;
|
|
7
8
|
/** Durable, string-backed rows. A delivered receipt must outlive source replay. */
|
|
8
9
|
export type PostOutboxStore<E> = Readonly<{
|
|
9
10
|
/** Commit one pending row or completion receipt. */
|
|
@@ -24,6 +25,8 @@ export type PostOutbox<M, E> = Readonly<{
|
|
|
24
25
|
enqueue: (message: M, at?: number) => Effect.Effect<number | undefined, E>;
|
|
25
26
|
/** Attempt one bounded page and arrange the next wake. */
|
|
26
27
|
drain: () => Effect.Effect<void, E>;
|
|
28
|
+
/** Earliest owed delivery, including a future retry; absent when none remain. */
|
|
29
|
+
nextWakeAt: () => Effect.Effect<number | undefined, E>;
|
|
27
30
|
/** Inspect exhausted messages in an ordered page. */
|
|
28
31
|
deadLetters: (limit: number, afterKey?: string) => Effect.Effect<M[], E>;
|
|
29
32
|
}>;
|
|
@@ -38,3 +41,12 @@ export declare function makePostOutbox<M, E>(options: Readonly<{
|
|
|
38
41
|
invalidRow: () => E;
|
|
39
42
|
encodingFailed: () => E;
|
|
40
43
|
}>): PostOutbox<M, E>;
|
|
44
|
+
/**
|
|
45
|
+
* Whether any message in an outbox still awaits a delivery attempt. Delivered
|
|
46
|
+
* receipts and exhausted dead letters are retained but owe nothing more, so a
|
|
47
|
+
* host may let their owner rest.
|
|
48
|
+
*/
|
|
49
|
+
export declare function postOutboxPending<M, E>(options: Readonly<{
|
|
50
|
+
message: Schema.Codec<M>;
|
|
51
|
+
outbox: Pick<PostOutboxStore<E>, "list">;
|
|
52
|
+
}>): Effect.Effect<boolean, E>;
|
package/dist/post-outbox.js
CHANGED
|
@@ -1,28 +1,34 @@
|
|
|
1
1
|
import * as Effect from "effect/Effect";
|
|
2
2
|
import * as Schema from "effect/Schema";
|
|
3
|
+
import { backoffDelay } from "@fungi.computer/almanac";
|
|
3
4
|
const PageSize = 64;
|
|
4
5
|
/** Maximum receiver attempts retained from BirdDog. */
|
|
5
6
|
export const maxDeliveryAttempts = 20;
|
|
6
|
-
/** BirdDog's retained
|
|
7
|
-
export const
|
|
7
|
+
/** BirdDog's retained receiver retry policy, shared by every Post adapter. */
|
|
8
|
+
export const postRetryPolicy = {
|
|
9
|
+
baseMs: 2_000,
|
|
10
|
+
maxMs: 300_000,
|
|
11
|
+
};
|
|
8
12
|
const earlier = (a, b) => b === undefined ? a : a === undefined ? b : Math.min(a, b);
|
|
9
13
|
const logDeliveryFailure = (cause) => {
|
|
10
14
|
console.error("Carrier Post outbox delivery failed", {
|
|
11
15
|
failureKind: cause instanceof Error ? "Error" : "NonErrorThrown",
|
|
12
16
|
});
|
|
13
17
|
};
|
|
18
|
+
/** One durable row: a message still owed delivery, or its delivered receipt. */
|
|
19
|
+
function postRowText(message) {
|
|
20
|
+
return Schema.fromJsonString(Schema.Union([
|
|
21
|
+
Schema.Struct({
|
|
22
|
+
mail: message,
|
|
23
|
+
attempts: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
|
|
24
|
+
nextAttemptAt: Schema.optional(Schema.Number.check(Schema.isGreaterThanOrEqualTo(0))),
|
|
25
|
+
}),
|
|
26
|
+
Schema.Struct({ state: Schema.Literal("delivered") }),
|
|
27
|
+
]));
|
|
28
|
+
}
|
|
14
29
|
/** The same bounded delivery and receipt machinery serves Post and BirdDog. */
|
|
15
30
|
export function makePostOutbox(options) {
|
|
16
|
-
const
|
|
17
|
-
mail: options.message,
|
|
18
|
-
attempts: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
|
|
19
|
-
nextAttemptAt: Schema.optional(Schema.Number.check(Schema.isGreaterThanOrEqualTo(0))),
|
|
20
|
-
});
|
|
21
|
-
const Row = Schema.Union([
|
|
22
|
-
Pending,
|
|
23
|
-
Schema.Struct({ state: Schema.Literal("delivered") }),
|
|
24
|
-
]);
|
|
25
|
-
const Text = Schema.fromJsonString(Row);
|
|
31
|
+
const Text = postRowText(options.message);
|
|
26
32
|
const decode = (value) => Schema.decodeUnknownEffect(Text)(value).pipe(Effect.mapError(options.invalidRow));
|
|
27
33
|
const encode = (row) => Effect.try({
|
|
28
34
|
try: () => Schema.encodeSync(Text)(row),
|
|
@@ -60,7 +66,7 @@ export function makePostOutbox(options) {
|
|
|
60
66
|
return undefined;
|
|
61
67
|
}
|
|
62
68
|
const attempts = row.attempts + 1;
|
|
63
|
-
const nextAttemptAt = now +
|
|
69
|
+
const nextAttemptAt = now + backoffDelay(attempts, postRetryPolicy);
|
|
64
70
|
yield* options.outbox.put(stored.key, yield* encode({ mail: row.mail, attempts, nextAttemptAt }));
|
|
65
71
|
return attempts < maxDeliveryAttempts ? nextAttemptAt : undefined;
|
|
66
72
|
});
|
|
@@ -95,5 +101,52 @@ export function makePostOutbox(options) {
|
|
|
95
101
|
}
|
|
96
102
|
return dead;
|
|
97
103
|
});
|
|
98
|
-
|
|
104
|
+
const nextWakeAt = () => Effect.gen(function* () {
|
|
105
|
+
let afterKey;
|
|
106
|
+
let due;
|
|
107
|
+
for (;;) {
|
|
108
|
+
const rows = yield* options.outbox.list(PageSize, afterKey);
|
|
109
|
+
for (const stored of rows) {
|
|
110
|
+
// Malformed rows are discarded by drain, not deliverable work.
|
|
111
|
+
const decoded = yield* Effect.result(decode(stored.value));
|
|
112
|
+
if (decoded._tag === "Failure")
|
|
113
|
+
continue;
|
|
114
|
+
const row = decoded.success;
|
|
115
|
+
if (!("state" in row) && row.attempts < maxDeliveryAttempts)
|
|
116
|
+
due = earlier(due, row.nextAttemptAt ?? options.now());
|
|
117
|
+
}
|
|
118
|
+
const lastKey = rows.at(-1)?.key;
|
|
119
|
+
if (rows.length < PageSize || lastKey === undefined)
|
|
120
|
+
return due;
|
|
121
|
+
afterKey = lastKey;
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
return { enqueue, drain, deadLetters, nextWakeAt };
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Whether any message in an outbox still awaits a delivery attempt. Delivered
|
|
128
|
+
* receipts and exhausted dead letters are retained but owe nothing more, so a
|
|
129
|
+
* host may let their owner rest.
|
|
130
|
+
*/
|
|
131
|
+
export function postOutboxPending(options) {
|
|
132
|
+
const Text = postRowText(options.message);
|
|
133
|
+
return Effect.gen(function* () {
|
|
134
|
+
let afterKey;
|
|
135
|
+
for (;;) {
|
|
136
|
+
const rows = yield* options.outbox.list(PageSize, afterKey);
|
|
137
|
+
for (const stored of rows) {
|
|
138
|
+
// A malformed row is not deliverable; the next drain discards it.
|
|
139
|
+
const decoded = yield* Effect.result(Schema.decodeUnknownEffect(Text)(stored.value));
|
|
140
|
+
if (decoded._tag === "Failure")
|
|
141
|
+
continue;
|
|
142
|
+
const row = decoded.success;
|
|
143
|
+
if (!("state" in row) && row.attempts < maxDeliveryAttempts)
|
|
144
|
+
return true;
|
|
145
|
+
}
|
|
146
|
+
const lastKey = rows.at(-1)?.key;
|
|
147
|
+
if (rows.length < PageSize || lastKey === undefined)
|
|
148
|
+
return false;
|
|
149
|
+
afterKey = lastKey;
|
|
150
|
+
}
|
|
151
|
+
});
|
|
99
152
|
}
|
package/dist/post.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { type PostDeliveryResult } from "./internal/post-contract.js";
|
|
|
3
3
|
export { PostError, PostDeliveryResultSchema, } from "./internal/post-contract.js";
|
|
4
4
|
export type { PostDeliveryResult } from "./internal/post-contract.js";
|
|
5
5
|
import type { WatchdogRunningJob, WatchdogSqliteOwner, WatchdogRuntime, WatchdogTransactionalProjection } from "@fungi.computer/watchdog";
|
|
6
|
-
export { makePostOutbox,
|
|
6
|
+
export { makePostOutbox, postOutboxPending } from "./post-outbox.js";
|
|
7
7
|
export type { PostOutbox, PostOutboxStore } from "./post-outbox.js";
|
|
8
8
|
/** Wire envelope parsed at the durable row and receiver boundaries. */
|
|
9
9
|
export declare const PostEnvelopeSchema: Schema.Struct<{
|
package/dist/post.js
CHANGED
|
@@ -3,8 +3,9 @@ import { PostError, } from "./internal/post-contract.js";
|
|
|
3
3
|
export { PostError, PostDeliveryResultSchema, } from "./internal/post-contract.js";
|
|
4
4
|
import { deliverPost, postDisposition, deliveryReceipt, } from "./internal/post-execution.js";
|
|
5
5
|
import { defineIdFamily } from "@fungi.computer/cairn";
|
|
6
|
-
import {
|
|
7
|
-
|
|
6
|
+
import { backoffDelay } from "@fungi.computer/almanac";
|
|
7
|
+
import { maxDeliveryAttempts, postRetryPolicy } from "./post-outbox.js";
|
|
8
|
+
export { makePostOutbox, postOutboxPending } from "./post-outbox.js";
|
|
8
9
|
const Key = Schema.String.check(Schema.isMinLength(1), Schema.isMaxLength(256));
|
|
9
10
|
const JobPayload = Schema.Struct({ postKey: Key });
|
|
10
11
|
const PageLimit = Schema.Int.check(Schema.isGreaterThanOrEqualTo(1), Schema.isLessThanOrEqualTo(64));
|
|
@@ -252,7 +253,7 @@ export function createPost(options) {
|
|
|
252
253
|
return undefined;
|
|
253
254
|
}
|
|
254
255
|
const attempts = row.attempts + 1;
|
|
255
|
-
const nextAttemptAt = options.now() +
|
|
256
|
+
const nextAttemptAt = options.now() + backoffDelay(attempts, postRetryPolicy);
|
|
256
257
|
const started = { ...row, attempts, nextAttemptAt };
|
|
257
258
|
// Preserve recovery evidence before invoking the receiver. A failed receipt
|
|
258
259
|
// write leaves this row available for retry after Watchdog settles the job.
|
|
@@ -298,7 +299,7 @@ export function createPost(options) {
|
|
|
298
299
|
finish(postKey, current, disposition, deliveryReceipt(evidence));
|
|
299
300
|
}
|
|
300
301
|
else {
|
|
301
|
-
const due = options.now() +
|
|
302
|
+
const due = options.now() + backoffDelay(current.attempts, postRetryPolicy);
|
|
302
303
|
if (due !== current.nextAttemptAt) {
|
|
303
304
|
putRow(postKey, { ...current, nextAttemptAt: due });
|
|
304
305
|
retryAt = due;
|
package/docs/api/post.md
CHANGED
|
@@ -118,6 +118,7 @@ Durable, string-backed rows. A delivered receipt must outlive source replay.
|
|
|
118
118
|
type PostOutbox<M, E> = Readonly<{
|
|
119
119
|
enqueue: (message, at?) => Effect.Effect<number | undefined, E>;
|
|
120
120
|
drain: () => Effect.Effect<void, E>;
|
|
121
|
+
nextWakeAt: () => Effect.Effect<number | undefined, E>;
|
|
121
122
|
deadLetters: (limit, afterKey?) => Effect.Effect<M[], E>;
|
|
122
123
|
}>;
|
|
123
124
|
```
|
|
@@ -368,33 +369,42 @@ Persisted Post row parser.
|
|
|
368
369
|
|
|
369
370
|
## Functions
|
|
370
371
|
|
|
371
|
-
###
|
|
372
|
+
### makePostOutbox()
|
|
372
373
|
|
|
373
374
|
```ts
|
|
374
|
-
function
|
|
375
|
+
function makePostOutbox<M, E>(options): PostOutbox<M, E>;
|
|
375
376
|
```
|
|
376
377
|
|
|
377
|
-
|
|
378
|
+
The same bounded delivery and receipt machinery serves Post and BirdDog.
|
|
379
|
+
|
|
380
|
+
#### Type Parameters
|
|
381
|
+
|
|
382
|
+
| Type Parameter |
|
|
383
|
+
| -------------- |
|
|
384
|
+
| `M` |
|
|
385
|
+
| `E` |
|
|
378
386
|
|
|
379
387
|
#### Parameters
|
|
380
388
|
|
|
381
|
-
| Parameter
|
|
382
|
-
|
|
|
383
|
-
| `
|
|
389
|
+
| Parameter | Type |
|
|
390
|
+
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
391
|
+
| `options` | `Readonly`\<\{ `message`: `Schema.Codec`\<`M`\>; `key`: (`message`) => `string`; `outbox`: [`PostOutboxStore`](#postoutboxstore)\<`E`\>; `deliver`: (`message`) => `Effect.Effect`\<`boolean`, `E`\>; `scheduleWake`: (`at`) => `Effect.Effect`\<`void`, `E`\>; `now`: () => `number`; `invalidRow`: () => `E`; `encodingFailed`: () => `E`; \}\> |
|
|
384
392
|
|
|
385
393
|
#### Returns
|
|
386
394
|
|
|
387
|
-
`
|
|
395
|
+
[`PostOutbox`](#postoutbox)\<`M`, `E`\>
|
|
388
396
|
|
|
389
397
|
---
|
|
390
398
|
|
|
391
|
-
###
|
|
399
|
+
### postOutboxPending()
|
|
392
400
|
|
|
393
401
|
```ts
|
|
394
|
-
function
|
|
402
|
+
function postOutboxPending<M, E>(options): Effect<boolean, E>;
|
|
395
403
|
```
|
|
396
404
|
|
|
397
|
-
|
|
405
|
+
Whether any message in an outbox still awaits a delivery attempt. Delivered
|
|
406
|
+
receipts and exhausted dead letters are retained but owe nothing more, so a host
|
|
407
|
+
may let their owner rest.
|
|
398
408
|
|
|
399
409
|
#### Type Parameters
|
|
400
410
|
|
|
@@ -405,13 +415,13 @@ The same bounded delivery and receipt machinery serves Post and BirdDog.
|
|
|
405
415
|
|
|
406
416
|
#### Parameters
|
|
407
417
|
|
|
408
|
-
| Parameter | Type
|
|
409
|
-
| --------- |
|
|
410
|
-
| `options` | `Readonly`\<\{ `message`: `Schema.Codec`\<`M`\>; `
|
|
418
|
+
| Parameter | Type |
|
|
419
|
+
| --------- | --------------------------------------------------------------------------------------------------------------------------------- |
|
|
420
|
+
| `options` | `Readonly`\<\{ `message`: `Schema.Codec`\<`M`\>; `outbox`: `Pick`\<[`PostOutboxStore`](#postoutboxstore)\<`E`\>, `"list"`\>; \}\> |
|
|
411
421
|
|
|
412
422
|
#### Returns
|
|
413
423
|
|
|
414
|
-
|
|
424
|
+
`Effect`\<`boolean`, `E`\>
|
|
415
425
|
|
|
416
426
|
---
|
|
417
427
|
|
|
@@ -537,37 +547,6 @@ Preserve each route's inferred address and event types in a registry.
|
|
|
537
547
|
|
|
538
548
|
---
|
|
539
549
|
|
|
540
|
-
### migratePostEvents()
|
|
541
|
-
|
|
542
|
-
```ts
|
|
543
|
-
function migratePostEvents<A, AI, B, BI>(options): void;
|
|
544
|
-
```
|
|
545
|
-
|
|
546
|
-
Migrate persisted ordinary events once, before opening their producer. Carrier
|
|
547
|
-
preserves delivery evidence, ordering and Watchdog job identities. A failed
|
|
548
|
-
decoder or transform rolls back both rows and the migration receipt.
|
|
549
|
-
|
|
550
|
-
#### Type Parameters
|
|
551
|
-
|
|
552
|
-
| Type Parameter |
|
|
553
|
-
| -------------- |
|
|
554
|
-
| `A` |
|
|
555
|
-
| `AI` |
|
|
556
|
-
| `B` |
|
|
557
|
-
| `BI` |
|
|
558
|
-
|
|
559
|
-
#### Parameters
|
|
560
|
-
|
|
561
|
-
| Parameter | Type |
|
|
562
|
-
| --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
563
|
-
| `options` | `Readonly`\<\{ `owner`: `WatchdogSqliteOwner`; `id`: `string`; `kind`: `string`; `toKind?`: `string`; `from`: `Schema.Codec`\<`A`, `AI`\>; `to`: `Schema.Codec`\<`B`, `BI`\>; `migrate`: (`event`) => `B`; \}\> |
|
|
564
|
-
|
|
565
|
-
#### Returns
|
|
566
|
-
|
|
567
|
-
`void`
|
|
568
|
-
|
|
569
|
-
---
|
|
570
|
-
|
|
571
550
|
### createPost()
|
|
572
551
|
|
|
573
552
|
```ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fungi.computer/carrier",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"author": "Levi <levi@technomancy.dev>",
|
|
5
5
|
"description": "Durable email submission and recipient delivery tracking",
|
|
6
6
|
"type": "module",
|
|
@@ -48,8 +48,9 @@
|
|
|
48
48
|
],
|
|
49
49
|
"dependencies": {
|
|
50
50
|
"@cloudflare/workers-types": "5.20260828.1",
|
|
51
|
-
"@fungi.computer/
|
|
52
|
-
"@fungi.computer/
|
|
51
|
+
"@fungi.computer/almanac": "0.2.0",
|
|
52
|
+
"@fungi.computer/cairn": "0.1.0",
|
|
53
|
+
"@fungi.computer/watchdog": "0.1.0",
|
|
53
54
|
"effect": "4.0.2",
|
|
54
55
|
"pg": "8.23.0",
|
|
55
56
|
"@types/pg": "8.23.1"
|