@yielded/agent-platform-cloudflare 0.1.0-beta.174 → 0.1.0-beta.175
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/dist/{Alarm-4J9B8Fg8.mjs → Alarm-B44efvB1.mjs} +111 -74
- package/dist/Alarm-B44efvB1.mjs.map +1 -0
- package/dist/Alarm.d.mts +2 -0
- package/dist/Alarm.mjs +1 -1
- package/dist/CloudflareScheduling.mjs +2 -2
- package/dist/CloudflareSubscriptions.mjs +2 -2
- package/dist/{CloudflareThreadClient-DU548EEj.mjs → CloudflareThreadClient-Ai3MTs3X.mjs} +4 -4
- package/dist/CloudflareThreadClient-Ai3MTs3X.mjs.map +1 -0
- package/dist/{CloudflareThreadClient-lTm6baDP.d.mts → CloudflareThreadClient-HO_3rkM7.d.mts} +362 -362
- package/dist/CloudflareThreadClient.d.mts +1 -1
- package/dist/CloudflareThreadClient.mjs +1 -1
- package/dist/{ThreadObject-BLYgzcdT.d.mts → ThreadObject-CxNlhoPL.d.mts} +13 -13
- package/dist/{ThreadObject-C2LF3btz.mjs → ThreadObject-DGvlHWYi.mjs} +42 -43
- package/dist/ThreadObject-DGvlHWYi.mjs.map +1 -0
- package/dist/ThreadObject.d.mts +1 -1
- package/dist/ThreadObject.mjs +1 -1
- package/dist/WakeScheduler.mjs +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +3 -3
- package/dist/{prepared-admission-BoUPHSNQ.mjs → prepared-admission-DYdffQ8K.mjs} +2 -2
- package/dist/{prepared-admission-BoUPHSNQ.mjs.map → prepared-admission-DYdffQ8K.mjs.map} +1 -1
- package/package.json +1 -1
- package/src/Alarm.ts +211 -116
- package/src/CloudflareThreadClient.ts +3 -2
- package/src/ThreadObject.ts +17 -22
- package/src/internal/layers.ts +55 -34
- package/src/internal/message-delivery.ts +61 -37
- package/dist/Alarm-4J9B8Fg8.mjs.map +0 -1
- package/dist/CloudflareThreadClient-DU548EEj.mjs.map +0 -1
- package/dist/ThreadObject-C2LF3btz.mjs.map +0 -1
package/src/internal/layers.ts
CHANGED
|
@@ -134,7 +134,7 @@ import * as DueQueue from "./due-queue.ts";
|
|
|
134
134
|
import { LiveTextHub } from "./live-text.ts";
|
|
135
135
|
import {
|
|
136
136
|
guardedMessageDeliveryStoreLayer,
|
|
137
|
-
|
|
137
|
+
lazyThreadMessageDeliveryLayer,
|
|
138
138
|
} from "./message-delivery.ts";
|
|
139
139
|
import { cloudflarePreparedInputAdmissionLayer } from "./prepared-admission.ts";
|
|
140
140
|
import { ProgressWaitRegistry } from "./progress-wait.ts";
|
|
@@ -625,10 +625,14 @@ const sharedLayer = <A, E, R, PE = never, PR = never>(
|
|
|
625
625
|
verifyOnOpen: config.verifyOnOpen,
|
|
626
626
|
};
|
|
627
627
|
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
Layer.
|
|
628
|
+
// Memoize these shared compositions within this Object's build, so repeated
|
|
629
|
+
// consumers reuse their contexts as well as the scoped leaf services.
|
|
630
|
+
const infrastructure = Layer.suspend(() =>
|
|
631
|
+
Layer.mergeAll(
|
|
632
|
+
options.lifecyclePublication === undefined ? Layer.empty : lifecyclePublicationLayer,
|
|
633
|
+
storageConfigLayer(storageOptions),
|
|
634
|
+
Layer.effect(SqlClient)(SqlClient),
|
|
635
|
+
),
|
|
632
636
|
);
|
|
633
637
|
|
|
634
638
|
// Preserve the database owner's reader gate and rollback invalidators, while
|
|
@@ -661,7 +665,7 @@ const sharedLayer = <A, E, R, PE = never, PR = never>(
|
|
|
661
665
|
}),
|
|
662
666
|
).pipe(Layer.provide(sqlOwnerLayer));
|
|
663
667
|
|
|
664
|
-
const localInfrastructure = Layer.mergeAll(infrastructure, sourceOwner);
|
|
668
|
+
const localInfrastructure = Layer.suspend(() => Layer.mergeAll(infrastructure, sourceOwner));
|
|
665
669
|
|
|
666
670
|
const sourceProgress = Layer.effect(SqlStorageProgress)(
|
|
667
671
|
Effect.gen(function* () {
|
|
@@ -709,41 +713,57 @@ const sharedLayer = <A, E, R, PE = never, PR = never>(
|
|
|
709
713
|
|
|
710
714
|
// The same local ports serve routed decorators and owner-side RPC execution.
|
|
711
715
|
// The RPC executor must never receive routed ports and bounce requests between Objects.
|
|
712
|
-
const rawLocalPorts = Layer.
|
|
713
|
-
Layer.
|
|
714
|
-
|
|
716
|
+
const rawLocalPorts = Layer.suspend(() =>
|
|
717
|
+
Layer.mergeAll(threadStoreLayer, submissionLedgerLayer).pipe(
|
|
718
|
+
Layer.provide(localInfrastructure),
|
|
719
|
+
Layer.provide(sourceProgress),
|
|
720
|
+
),
|
|
721
|
+
);
|
|
722
|
+
|
|
723
|
+
const base = Layer.suspend(() =>
|
|
724
|
+
Layer.mergeAll(DurableAlarmService.layer, ProgressWaitRegistry.layer),
|
|
715
725
|
);
|
|
716
726
|
|
|
717
|
-
const base = Layer.mergeAll(DurableAlarmService.layer, ProgressWaitRegistry.layer);
|
|
718
727
|
const wakes = cloudflareWakeSchedulerLayer.pipe(Layer.provide(base));
|
|
719
728
|
|
|
720
|
-
const messageStore =
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
729
|
+
const messageStore = Layer.suspend(() =>
|
|
730
|
+
guardedMessageDeliveryStoreLayer.pipe(
|
|
731
|
+
Layer.provide(
|
|
732
|
+
doMessageDeliveryStoreLayer().pipe(
|
|
733
|
+
Layer.provide(localInfrastructure),
|
|
734
|
+
Layer.provide(sourceProgress),
|
|
735
|
+
),
|
|
725
736
|
),
|
|
737
|
+
Layer.provide(wakes),
|
|
726
738
|
),
|
|
727
|
-
Layer.provide(wakes),
|
|
728
739
|
);
|
|
729
740
|
|
|
730
|
-
const messageRecovery =
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
741
|
+
const messageRecovery = Layer.suspend(() =>
|
|
742
|
+
lazyThreadMessageDeliveryLayer(
|
|
743
|
+
// Four parallel attempts per wave. Native completion stops new wake-driven waves;
|
|
744
|
+
// retained retry deadlines schedule future alarms.
|
|
745
|
+
MessageDeliveryDriver.layer({ batchSize: 4, concurrency: 4 }).pipe(
|
|
746
|
+
Layer.provide(cloudflarePreparedInputAdmissionLayer),
|
|
747
|
+
Layer.provide(CloudflareThreadClient.layer),
|
|
748
|
+
Layer.provide(messageStore),
|
|
749
|
+
Layer.provide(wakes),
|
|
750
|
+
),
|
|
751
|
+
).pipe(Layer.provide(messageStore)),
|
|
738
752
|
);
|
|
739
753
|
|
|
740
|
-
const publication =
|
|
741
|
-
|
|
742
|
-
|
|
754
|
+
const publication =
|
|
755
|
+
options.publication === undefined
|
|
756
|
+
? ThreadPublication.layer
|
|
757
|
+
: options.publication.pipe(
|
|
758
|
+
Layer.provide(Layer.mergeAll(rawLocalPorts, Layer.effect(SqlClient)(SqlClient))),
|
|
759
|
+
);
|
|
743
760
|
|
|
744
|
-
const projection =
|
|
745
|
-
|
|
746
|
-
|
|
761
|
+
const projection =
|
|
762
|
+
options.projection === undefined
|
|
763
|
+
? ThreadProjectionMaintenance.layer
|
|
764
|
+
: options.projection.pipe(
|
|
765
|
+
Layer.provide(Layer.mergeAll(rawLocalPorts, Layer.effect(SqlClient)(SqlClient))),
|
|
766
|
+
);
|
|
747
767
|
|
|
748
768
|
const localPorts =
|
|
749
769
|
options.publication === undefined &&
|
|
@@ -947,13 +967,14 @@ const sharedLayer = <A, E, R, PE = never, PR = never>(
|
|
|
947
967
|
}),
|
|
948
968
|
Context.add(SubmissionLedger, {
|
|
949
969
|
...ledger,
|
|
950
|
-
...(options.hostLanesForMutation === undefined
|
|
970
|
+
...(options.hostLanesForMutation === undefined &&
|
|
971
|
+
options.lifecyclePublication === undefined
|
|
951
972
|
? {}
|
|
952
973
|
: {
|
|
953
|
-
admit: (
|
|
974
|
+
admit: (...args: Parameters<SubmissionLedger["Service"]["admit"]>) =>
|
|
954
975
|
observeMutation(
|
|
955
|
-
{ _tag: "Admission", request },
|
|
956
|
-
ledger.admit(
|
|
976
|
+
{ _tag: "Admission", request: args[0] },
|
|
977
|
+
ledger.admit(...args),
|
|
957
978
|
false,
|
|
958
979
|
),
|
|
959
980
|
}),
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
MessageDeliveryStore,
|
|
6
6
|
} from "@yielded/agent/message-delivery";
|
|
7
7
|
import { WakeScheduler } from "@yielded/agent/wake-scheduler";
|
|
8
|
-
import { Clock, Context, Effect, Layer, Option } from "effect";
|
|
8
|
+
import { Clock, Context, Duration, Effect, Exit, Layer, Option } from "effect";
|
|
9
9
|
|
|
10
10
|
import { DurableAlarmError, ThreadMessageDelivery, ThreadMutationGate } from "../Alarm.ts";
|
|
11
11
|
import { ThreadObjectPlacement } from "../CloudflareBindings.ts";
|
|
@@ -76,45 +76,69 @@ export const guardedMessageDeliveryStoreLayer = Layer.effect(
|
|
|
76
76
|
}),
|
|
77
77
|
);
|
|
78
78
|
|
|
79
|
-
/**
|
|
80
|
-
export const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
79
|
+
/** Build the driver in the Object's Scope only when retained delivery work is due. */
|
|
80
|
+
export const lazyThreadMessageDeliveryLayer = <E, R>(
|
|
81
|
+
driver: Layer.Layer<MessageDeliveryDriver, E, R>,
|
|
82
|
+
) =>
|
|
83
|
+
Layer.fromBuildMemo((memoMap, scope) =>
|
|
84
|
+
Effect.gen(function* () {
|
|
85
|
+
const context = yield* Effect.context<R>();
|
|
86
|
+
const store = yield* MessageDeliveryStore;
|
|
84
87
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
// Failed builds release their Layer scopes; let a later wave build again.
|
|
89
|
+
const service = yield* Effect.cachedWithTTL(
|
|
90
|
+
Layer.buildWithMemoMap(driver, memoMap, scope).pipe(
|
|
91
|
+
Effect.provide(context),
|
|
92
|
+
Effect.map(Context.get(MessageDeliveryDriver)),
|
|
93
|
+
Effect.mapError((cause) =>
|
|
94
|
+
DurableAlarmError.make({
|
|
95
|
+
operation: "prepare message delivery",
|
|
96
|
+
message: "Durable message recovery remains pending",
|
|
97
|
+
cause,
|
|
98
|
+
}),
|
|
99
|
+
),
|
|
100
|
+
),
|
|
101
|
+
(exit) => (Exit.isSuccess(exit) ? Duration.infinity : Duration.zero),
|
|
102
|
+
);
|
|
90
103
|
|
|
91
|
-
|
|
92
|
-
|
|
104
|
+
const failure = (operation: string) => () =>
|
|
105
|
+
DurableAlarmError.make({
|
|
106
|
+
operation,
|
|
107
|
+
message: "Durable message recovery remains pending",
|
|
108
|
+
});
|
|
93
109
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
// The assembled driver has four permits: a wave is one parallel attempt window.
|
|
97
|
-
const keys = yield* store.due(yield* Clock.currentTimeMillis, 4);
|
|
98
|
-
const records = yield* Effect.forEach(keys, (key) => store.get(key));
|
|
110
|
+
const prepare = Effect.gen(function* () {
|
|
111
|
+
const deadline = yield* store.nextDeadline();
|
|
99
112
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
)
|
|
105
|
-
run: Effect.forEach(keys, (key) => driver.process(key), {
|
|
106
|
-
concurrency: 4,
|
|
107
|
-
discard: true,
|
|
108
|
-
}).pipe(
|
|
109
|
-
Effect.andThen(store.nextDeadline()),
|
|
110
|
-
Effect.map(Option.fromNullishOr),
|
|
111
|
-
Effect.mapError(failure("dispatch message delivery")),
|
|
112
|
-
),
|
|
113
|
-
};
|
|
114
|
-
}).pipe(Effect.mapError(failure("prepare message delivery")));
|
|
113
|
+
if (deadline === null || deadline > (yield* Clock.currentTimeMillis))
|
|
114
|
+
return { timeoutMillis: 1, run: Effect.succeed(Option.fromNullishOr(deadline)) };
|
|
115
|
+
// The assembled driver has four permits: a wave is one parallel attempt window.
|
|
116
|
+
const keys = yield* store.due(yield* Clock.currentTimeMillis, 4);
|
|
117
|
+
const records = yield* Effect.forEach(keys, (key) => store.get(key));
|
|
115
118
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
119
|
+
return {
|
|
120
|
+
timeoutMillis: Math.max(
|
|
121
|
+
1,
|
|
122
|
+
...records.map((record) => record?.policy.attemptTimeoutMillis ?? 1),
|
|
123
|
+
),
|
|
124
|
+
run: Effect.flatMap(service, (driver) =>
|
|
125
|
+
Effect.forEach(keys, (key) => driver.process(key), {
|
|
126
|
+
concurrency: 4,
|
|
127
|
+
discard: true,
|
|
128
|
+
}),
|
|
129
|
+
).pipe(
|
|
130
|
+
Effect.andThen(store.nextDeadline()),
|
|
131
|
+
Effect.map(Option.fromNullishOr),
|
|
132
|
+
Effect.mapError(failure("dispatch message delivery")),
|
|
133
|
+
),
|
|
134
|
+
};
|
|
135
|
+
}).pipe(Effect.mapError(failure("prepare message delivery")));
|
|
136
|
+
|
|
137
|
+
return Context.make(ThreadMessageDelivery, { prepare });
|
|
138
|
+
}),
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
/** Message progress shares the native alarm slot without blocking source runtime work. */
|
|
142
|
+
export const threadMessageDeliveryLayer = lazyThreadMessageDeliveryLayer(
|
|
143
|
+
Layer.effect(MessageDeliveryDriver, MessageDeliveryDriver),
|
|
120
144
|
);
|