effect-cf 0.18.0 → 0.20.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/CHANGELOG.md +18 -0
- package/README.md +16 -2
- package/dist/{Hyperdrive-C0vfrPeG.d.mts → Hyperdrive-RTjgirMV.d.mts} +7 -12
- package/dist/HyperdrivePg.d.mts +2 -6
- package/dist/HyperdrivePg.mjs +0 -1
- package/dist/index.d.mts +160 -106
- package/dist/index.mjs +550 -168
- package/package.json +8 -9
- package/src/AiGateway.ts +3 -0
- package/src/AnalyticsEngine.ts +6 -1
- package/src/Binding.ts +4 -2
- package/src/CloudflareOtlp.ts +20 -7
- package/src/ContainerNamespace.ts +3 -0
- package/src/D1.ts +1 -0
- package/src/DurableObject.ts +10 -2
- package/src/DurableObjectAlarm.ts +19 -0
- package/src/DurableObjectDefinition.ts +34 -6
- package/src/DurableObjectNamespace.ts +4 -1
- package/src/DurableObjectRpcWebSocket.ts +14 -0
- package/src/DurableObjectSqlite.ts +1 -0
- package/src/DurableObjectStorage.ts +2 -0
- package/src/DurableObjectWebSocket.ts +4 -1
- package/src/Email.ts +354 -10
- package/src/HyperdrivePg.ts +1 -1
- package/src/Kv.ts +3 -1
- package/src/QueueBinding.ts +2 -1
- package/src/QueueDefinition.ts +6 -1
- package/src/R2.ts +1 -0
- package/src/RpcDefinition.ts +2 -0
- package/src/ServiceBinding.ts +6 -1
- package/src/Worker.ts +10 -2
- package/src/WorkerDefinition.ts +28 -6
- package/src/WorkflowBinding.ts +5 -2
- package/src/WorkflowDefinition.ts +6 -1
- package/src/internal/Clock.ts +9 -0
- package/src/internal/RpcInvocation.ts +1 -0
- package/dist/Hyperdrive-89hg_Fck.mjs +0 -156
- /package/dist/{chunk-CfYAbeIz.mjs → rolldown-runtime-D7D4PA-g.mjs} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "effect-cf",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.0",
|
|
4
4
|
"description": "Effect-native primitives for Cloudflare Workers and bindings.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -36,27 +36,26 @@
|
|
|
36
36
|
"dev": "vp pack --watch",
|
|
37
37
|
"test": "vp test",
|
|
38
38
|
"check": "vp check",
|
|
39
|
-
"
|
|
39
|
+
"typecheck": "tsc --noEmit"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@cloudflare/containers": "catalog:",
|
|
43
43
|
"@cloudflare/vitest-pool-workers": "catalog:",
|
|
44
44
|
"@cloudflare/workers-types": "catalog:",
|
|
45
|
-
"@effect/platform-node": "4.0.0-beta.
|
|
45
|
+
"@effect/platform-node": "4.0.0-beta.105",
|
|
46
46
|
"@effect/sql-d1": "catalog:",
|
|
47
47
|
"@effect/sql-pg": "catalog:",
|
|
48
48
|
"@effect/sql-sqlite-do": "catalog:",
|
|
49
49
|
"@effect/vitest": "catalog:",
|
|
50
50
|
"effect": "catalog:",
|
|
51
|
-
"
|
|
52
|
-
"vite-plus": "0.1.20",
|
|
51
|
+
"vite-plus": "catalog:",
|
|
53
52
|
"vitest": "catalog:"
|
|
54
53
|
},
|
|
55
54
|
"peerDependencies": {
|
|
56
|
-
"@effect/sql-d1": "^4.0.0-beta.
|
|
57
|
-
"@effect/sql-pg": "^4.0.0-beta.
|
|
58
|
-
"@effect/sql-sqlite-do": "^4.0.0-beta.
|
|
59
|
-
"effect": "^4.0.0-beta.
|
|
55
|
+
"@effect/sql-d1": "^4.0.0-beta.105",
|
|
56
|
+
"@effect/sql-pg": "^4.0.0-beta.105",
|
|
57
|
+
"@effect/sql-sqlite-do": "^4.0.0-beta.105",
|
|
58
|
+
"effect": "^4.0.0-beta.105"
|
|
60
59
|
},
|
|
61
60
|
"peerDependenciesMeta": {
|
|
62
61
|
"@effect/sql-pg": {
|
package/src/AiGateway.ts
CHANGED
|
@@ -113,6 +113,7 @@ const tryAiGatewaySync = <A>(
|
|
|
113
113
|
const providerUrl = (baseUrl: string, path = "") => {
|
|
114
114
|
const url = new URL(baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`);
|
|
115
115
|
const normalizedPath = path.replace(/^\/+/, "");
|
|
116
|
+
|
|
116
117
|
return normalizedPath === "" ? url : new URL(normalizedPath, url);
|
|
117
118
|
};
|
|
118
119
|
|
|
@@ -141,6 +142,7 @@ export const makeClient = (definition: AiGatewayDefinition, gateway: AiGatewayBi
|
|
|
141
142
|
const baseUrl = yield* tryAiGatewayPromise(definition.binding, "getUrl", () =>
|
|
142
143
|
gateway.getUrl(options.provider),
|
|
143
144
|
);
|
|
145
|
+
|
|
144
146
|
return yield* tryAiGatewayPromise(definition.binding, "fetch", () =>
|
|
145
147
|
fetch(providerUrl(baseUrl, options.path).href, options.init),
|
|
146
148
|
);
|
|
@@ -244,6 +246,7 @@ export const makeHttpClient = (
|
|
|
244
246
|
getLog: (logId) =>
|
|
245
247
|
tryAiGatewayPromise(binding, "getLog", async () => {
|
|
246
248
|
const response = await request(providerUrl(gatewayUrl(definition), `logs/${logId}`).href);
|
|
249
|
+
|
|
247
250
|
return (await response.json()) as AiGatewayLog;
|
|
248
251
|
}),
|
|
249
252
|
unsafeRaw: Effect.fail(
|
package/src/AnalyticsEngine.ts
CHANGED
|
@@ -3,13 +3,13 @@ import type {
|
|
|
3
3
|
AnalyticsEngineDataset as CloudflareAnalyticsEngineDataset,
|
|
4
4
|
} from "@cloudflare/workers-types";
|
|
5
5
|
import {
|
|
6
|
+
type Redacted,
|
|
6
7
|
Config,
|
|
7
8
|
Context,
|
|
8
9
|
Data,
|
|
9
10
|
Effect,
|
|
10
11
|
Layer,
|
|
11
12
|
Option,
|
|
12
|
-
Redacted,
|
|
13
13
|
Result,
|
|
14
14
|
Schema as S,
|
|
15
15
|
} from "effect";
|
|
@@ -558,7 +558,9 @@ const writeChunks = (
|
|
|
558
558
|
const queryApiUrl = (definition: AnalyticsEngineQueryDefinition) => {
|
|
559
559
|
const baseUrl = new URL(definition.apiBaseUrl ?? defaultQueryApiBaseUrl);
|
|
560
560
|
const pathname = baseUrl.pathname.replace(/\/+$/, "");
|
|
561
|
+
|
|
561
562
|
baseUrl.pathname = `${pathname}/accounts/${definition.accountId}/analytics_engine/sql`;
|
|
563
|
+
|
|
562
564
|
return baseUrl.href;
|
|
563
565
|
};
|
|
564
566
|
|
|
@@ -613,6 +615,7 @@ const executeQueryRequest = (
|
|
|
613
615
|
|
|
614
616
|
if (response.status < 200 || response.status >= 300) {
|
|
615
617
|
const body = yield* responseText(definition, response, "queryErrorBody");
|
|
618
|
+
|
|
616
619
|
return yield* Effect.fail(
|
|
617
620
|
analyticsEngineQueryError(
|
|
618
621
|
definition,
|
|
@@ -647,6 +650,7 @@ const makeQueryClientWith = (
|
|
|
647
650
|
),
|
|
648
651
|
),
|
|
649
652
|
);
|
|
653
|
+
|
|
650
654
|
return yield* decodeQueryResponse(json);
|
|
651
655
|
});
|
|
652
656
|
const queryResult = <Row>(
|
|
@@ -783,6 +787,7 @@ export const queryLayerConfig = <Self>(
|
|
|
783
787
|
tag,
|
|
784
788
|
Effect.gen(function* () {
|
|
785
789
|
const definition = yield* config;
|
|
790
|
+
|
|
786
791
|
return yield* makeQueryClient(definition);
|
|
787
792
|
}),
|
|
788
793
|
);
|
package/src/Binding.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { Context, Data, Effect, Layer } from "effect";
|
|
2
2
|
|
|
3
|
-
import { WorkerEnvironment } from "./Environment";
|
|
4
|
-
import type { WorkerEnv } from "./Environment";
|
|
3
|
+
import { WorkerEnvironment, type WorkerEnv } from "./Environment";
|
|
5
4
|
|
|
6
5
|
/** Internal type id marker used by binding helper services. */
|
|
7
6
|
export const TypeId = "effect-cf/Binding" as const;
|
|
@@ -121,6 +120,7 @@ const getBinding = <Resource>(
|
|
|
121
120
|
Effect.gen(function* () {
|
|
122
121
|
if (!isPropertyTarget(env)) {
|
|
123
122
|
const actual = describeActual(env);
|
|
123
|
+
|
|
124
124
|
return yield* Effect.fail(
|
|
125
125
|
new BindingValidationError({
|
|
126
126
|
binding,
|
|
@@ -145,6 +145,7 @@ const getBinding = <Resource>(
|
|
|
145
145
|
if (!isResource(resource)) {
|
|
146
146
|
const expected = options?.expected ?? defaultExpected;
|
|
147
147
|
const actual = describeActual(resource);
|
|
148
|
+
|
|
148
149
|
return yield* Effect.fail(
|
|
149
150
|
new BindingValidationError({
|
|
150
151
|
binding,
|
|
@@ -187,6 +188,7 @@ export const layer = <Self, Resource, Service = Resource>(
|
|
|
187
188
|
Effect.gen(function* () {
|
|
188
189
|
const env = yield* WorkerEnvironment;
|
|
189
190
|
const resource = yield* getBinding(env, binding, isResource, options);
|
|
191
|
+
|
|
190
192
|
return wrap === undefined ? (resource as unknown as Service) : wrap(resource);
|
|
191
193
|
}),
|
|
192
194
|
);
|
package/src/CloudflareOtlp.ts
CHANGED
|
@@ -17,11 +17,19 @@ export type Signal = "logs" | "traces" | "metrics";
|
|
|
17
17
|
/** OTLP payload serialization used by the Effect OTLP exporters. */
|
|
18
18
|
export type Serialization = "json" | "protobuf";
|
|
19
19
|
|
|
20
|
-
/**
|
|
20
|
+
/**
|
|
21
|
+
* Resource metadata shared by Worker and Durable Object OTLP layers.
|
|
22
|
+
*
|
|
23
|
+
* Precedence follows Effect's `OtlpResource.fromConfig`: explicit
|
|
24
|
+
* `serviceName`/`serviceVersion` win over matching explicit `attributes`, and
|
|
25
|
+
* explicit attributes win over `OTEL_SERVICE_NAME`, `OTEL_SERVICE_VERSION`, and
|
|
26
|
+
* `OTEL_RESOURCE_ATTRIBUTES`. Omit an option to let operators set it from the
|
|
27
|
+
* environment.
|
|
28
|
+
*/
|
|
21
29
|
export interface ResourceOptions {
|
|
22
|
-
/** Explicit service name. OTEL
|
|
30
|
+
/** Explicit service name. Takes precedence over OTEL environment variables. */
|
|
23
31
|
readonly serviceName?: string;
|
|
24
|
-
/** Explicit service version. OTEL
|
|
32
|
+
/** Explicit service version. Takes precedence over OTEL environment variables. */
|
|
25
33
|
readonly serviceVersion?: string;
|
|
26
34
|
/** Additional resource attributes attached to exported telemetry. */
|
|
27
35
|
readonly attributes?: Record<string, unknown>;
|
|
@@ -89,11 +97,13 @@ const selectedSignals = (signals: ReadonlyArray<Signal> | undefined): ReadonlySe
|
|
|
89
97
|
|
|
90
98
|
const withDefinedAttributes = (attributes: Record<string, unknown>): Record<string, unknown> => {
|
|
91
99
|
const out: Record<string, unknown> = {};
|
|
100
|
+
|
|
92
101
|
for (const [key, value] of Object.entries(attributes)) {
|
|
93
102
|
if (value !== undefined) {
|
|
94
103
|
out[key] = value;
|
|
95
104
|
}
|
|
96
105
|
}
|
|
106
|
+
|
|
97
107
|
return out;
|
|
98
108
|
};
|
|
99
109
|
|
|
@@ -114,15 +124,14 @@ const serializationLayer = (serialization: Serialization | undefined) =>
|
|
|
114
124
|
|
|
115
125
|
type SignalLayer = ReturnType<typeof OtlpLogger.layerFromConfig>;
|
|
116
126
|
|
|
127
|
+
/** Merges a non-empty signal layer list; callers handle the empty case. */
|
|
117
128
|
const mergeSignalLayers = (layers: ReadonlyArray<SignalLayer>): SignalLayer => {
|
|
118
|
-
if (layers.length === 0) {
|
|
119
|
-
return Layer.empty;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
129
|
let merged = layers[0]!;
|
|
130
|
+
|
|
123
131
|
for (let index = 1; index < layers.length; index++) {
|
|
124
132
|
merged = Layer.mergeAll(merged, layers[index]!);
|
|
125
133
|
}
|
|
134
|
+
|
|
126
135
|
return merged;
|
|
127
136
|
};
|
|
128
137
|
|
|
@@ -164,6 +173,10 @@ const makeLayer = (
|
|
|
164
173
|
);
|
|
165
174
|
}
|
|
166
175
|
|
|
176
|
+
if (layers.length === 0) {
|
|
177
|
+
return Layer.empty;
|
|
178
|
+
}
|
|
179
|
+
|
|
167
180
|
return mergeSignalLayers(layers).pipe(
|
|
168
181
|
Layer.provide(cloudflareConfigProviderLayer),
|
|
169
182
|
Layer.provide(serializationLayer(options.serialization)),
|
|
@@ -243,6 +243,7 @@ export const makeClient =
|
|
|
243
243
|
options?: globalThis.DurableObjectNamespaceGetDurableObjectOptions,
|
|
244
244
|
): ContainerInstanceClient => {
|
|
245
245
|
const instance = getByName(name, options);
|
|
246
|
+
|
|
246
247
|
return {
|
|
247
248
|
unsafeRaw: Effect.flatMap(instance, (client) => client.unsafeRaw),
|
|
248
249
|
state: Effect.flatMap(instance, (client) => client.state),
|
|
@@ -296,6 +297,7 @@ export const Tag =
|
|
|
296
297
|
) =>
|
|
297
298
|
Effect.gen(function* () {
|
|
298
299
|
const namespace = yield* tag;
|
|
300
|
+
|
|
299
301
|
return yield* namespace.getByName(name, options);
|
|
300
302
|
});
|
|
301
303
|
|
|
@@ -322,6 +324,7 @@ export const Tag =
|
|
|
322
324
|
|
|
323
325
|
const unsafeRaw = Effect.fn(function* () {
|
|
324
326
|
const namespace = yield* tag;
|
|
327
|
+
|
|
325
328
|
return yield* namespace.unsafeRaw;
|
|
326
329
|
});
|
|
327
330
|
|
package/src/D1.ts
CHANGED
package/src/DurableObject.ts
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import { DurableObject as CloudflareDurableObject } from "cloudflare:workers";
|
|
2
|
-
import {
|
|
3
|
-
|
|
2
|
+
import {
|
|
3
|
+
ConfigProvider,
|
|
4
|
+
Effect,
|
|
5
|
+
Layer,
|
|
6
|
+
ManagedRuntime,
|
|
7
|
+
type Context,
|
|
8
|
+
type Scope,
|
|
9
|
+
type Schema as S,
|
|
10
|
+
} from "effect";
|
|
4
11
|
|
|
5
12
|
import { NativeRequest } from "./Worker";
|
|
6
13
|
import { WorkerConfig, WorkerEnvironment, type WorkerEnv } from "./Environment";
|
|
@@ -173,6 +180,7 @@ export const make = <
|
|
|
173
180
|
this.runtime = ManagedRuntime.make(runtimeLayer);
|
|
174
181
|
|
|
175
182
|
const initialize = options.initialize;
|
|
183
|
+
|
|
176
184
|
if (initialize !== undefined) {
|
|
177
185
|
state.waitUntil(this[RunSymbol](initialize, { eventLayer: false }));
|
|
178
186
|
}
|
|
@@ -356,6 +356,7 @@ const toRepeatEveryMillis = (input: Duration.Input | undefined) => {
|
|
|
356
356
|
return Effect.try({
|
|
357
357
|
try: () => {
|
|
358
358
|
const millis = Duration.toMillis(Duration.fromInputUnsafe(input));
|
|
359
|
+
|
|
359
360
|
if (!Number.isFinite(millis) || millis <= 0) {
|
|
360
361
|
throw new Error("Alarm repeatEvery must be a positive finite duration");
|
|
361
362
|
}
|
|
@@ -404,6 +405,7 @@ const toFailureRescheduleMillis = (input: Duration.Input) =>
|
|
|
404
405
|
Effect.try({
|
|
405
406
|
try: () => {
|
|
406
407
|
const millis = Duration.toMillis(Duration.fromInputUnsafe(input));
|
|
408
|
+
|
|
407
409
|
if (!Number.isFinite(millis) || millis <= 0) {
|
|
408
410
|
throw new Error("Alarm failure rescheduleAfter must be a positive finite duration");
|
|
409
411
|
}
|
|
@@ -430,6 +432,7 @@ export const processDue = <R = never, E = never, OnFailureR = never, OnFailureE
|
|
|
430
432
|
) =>
|
|
431
433
|
Effect.gen(function* () {
|
|
432
434
|
const durableObjectAlarm = yield* DurableObjectAlarm;
|
|
435
|
+
|
|
433
436
|
return yield* durableObjectAlarm.processDueAlarms(handle, options);
|
|
434
437
|
});
|
|
435
438
|
|
|
@@ -506,6 +509,7 @@ export const define = <const Definitions extends AlarmDefinitions>(definitions:
|
|
|
506
509
|
(event) =>
|
|
507
510
|
Effect.gen(function* () {
|
|
508
511
|
const definition = definitions[event.tag];
|
|
512
|
+
|
|
509
513
|
if (definition === undefined) {
|
|
510
514
|
return;
|
|
511
515
|
}
|
|
@@ -536,6 +540,7 @@ export const define = <const Definitions extends AlarmDefinitions>(definitions:
|
|
|
536
540
|
const action = getAlarmDefinitionFailureAction(definitions[failure.tag]);
|
|
537
541
|
const optionAction =
|
|
538
542
|
options?.onFailure === undefined ? undefined : yield* options.onFailure(failure);
|
|
543
|
+
|
|
539
544
|
return action ?? optionAction;
|
|
540
545
|
}),
|
|
541
546
|
},
|
|
@@ -602,6 +607,7 @@ export class DurableObjectAlarm extends Context.Service<DurableObjectAlarm, Alar
|
|
|
602
607
|
|
|
603
608
|
if (next === undefined) {
|
|
604
609
|
yield* state.storage.deleteAlarm();
|
|
610
|
+
|
|
605
611
|
return;
|
|
606
612
|
}
|
|
607
613
|
|
|
@@ -625,7 +631,9 @@ export class DurableObjectAlarm extends Context.Service<DurableObjectAlarm, Alar
|
|
|
625
631
|
row.run_at,
|
|
626
632
|
row.payload,
|
|
627
633
|
);
|
|
634
|
+
|
|
628
635
|
yield* cursor.rowsWritten;
|
|
636
|
+
|
|
629
637
|
return;
|
|
630
638
|
}
|
|
631
639
|
|
|
@@ -642,6 +650,7 @@ export class DurableObjectAlarm extends Context.Service<DurableObjectAlarm, Alar
|
|
|
642
650
|
row.repeat_every_ms,
|
|
643
651
|
row.payload,
|
|
644
652
|
);
|
|
653
|
+
|
|
645
654
|
yield* cursor.rowsWritten;
|
|
646
655
|
},
|
|
647
656
|
);
|
|
@@ -660,7 +669,9 @@ export class DurableObjectAlarm extends Context.Service<DurableObjectAlarm, Alar
|
|
|
660
669
|
row.run_at,
|
|
661
670
|
row.payload,
|
|
662
671
|
);
|
|
672
|
+
|
|
663
673
|
yield* cursor.rowsWritten;
|
|
674
|
+
|
|
664
675
|
return;
|
|
665
676
|
}
|
|
666
677
|
|
|
@@ -678,11 +689,13 @@ export class DurableObjectAlarm extends Context.Service<DurableObjectAlarm, Alar
|
|
|
678
689
|
row.repeat_every_ms,
|
|
679
690
|
row.payload,
|
|
680
691
|
);
|
|
692
|
+
|
|
681
693
|
yield* cursor.rowsWritten;
|
|
682
694
|
});
|
|
683
695
|
|
|
684
696
|
const cancelAlarm = Effect.fn("DurableObjectAlarm.cancelAlarm")(function* (input: AlarmRef) {
|
|
685
697
|
const ref = yield* decodeAlarmRef(input);
|
|
698
|
+
|
|
686
699
|
yield* state.storage.transaction(() =>
|
|
687
700
|
Effect.gen(function* () {
|
|
688
701
|
yield* ensureTable(state);
|
|
@@ -780,15 +793,18 @@ export class DurableObjectAlarm extends Context.Service<DurableObjectAlarm, Alar
|
|
|
780
793
|
: yield* toFailureRescheduleMillis(actionRetryDelay);
|
|
781
794
|
|
|
782
795
|
yield* rescheduleFailedAlarm(row, retryDelay);
|
|
796
|
+
|
|
783
797
|
return "continue" as const;
|
|
784
798
|
}
|
|
785
799
|
|
|
786
800
|
if (actionMode === "skip-and-advance-repeat") {
|
|
787
801
|
yield* acknowledgeAlarm(row);
|
|
802
|
+
|
|
788
803
|
return "continue" as const;
|
|
789
804
|
}
|
|
790
805
|
|
|
791
806
|
yield* reconcileAlarm();
|
|
807
|
+
|
|
792
808
|
return "stop" as const;
|
|
793
809
|
};
|
|
794
810
|
|
|
@@ -797,6 +813,7 @@ export class DurableObjectAlarm extends Context.Service<DurableObjectAlarm, Alar
|
|
|
797
813
|
|
|
798
814
|
if (Exit.isFailure(eventExit)) {
|
|
799
815
|
const action = yield* handleFailure(row, undefined, eventExit.cause);
|
|
816
|
+
|
|
800
817
|
if (action === "stop") {
|
|
801
818
|
return yield* Effect.failCause(eventExit.cause);
|
|
802
819
|
}
|
|
@@ -808,6 +825,7 @@ export class DurableObjectAlarm extends Context.Service<DurableObjectAlarm, Alar
|
|
|
808
825
|
|
|
809
826
|
if (Exit.isFailure(handleExit)) {
|
|
810
827
|
const action = yield* handleFailure(row, event, handleExit.cause);
|
|
828
|
+
|
|
811
829
|
if (action === "stop") {
|
|
812
830
|
return yield* Effect.failCause(handleExit.cause);
|
|
813
831
|
}
|
|
@@ -819,6 +837,7 @@ export class DurableObjectAlarm extends Context.Service<DurableObjectAlarm, Alar
|
|
|
819
837
|
}
|
|
820
838
|
|
|
821
839
|
yield* reconcileAlarm();
|
|
840
|
+
|
|
822
841
|
return { failed, handled };
|
|
823
842
|
});
|
|
824
843
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { Context, Effect, type Layer } from "effect";
|
|
2
|
-
import type { Schema as S } from "effect";
|
|
1
|
+
import { Context, Effect, type Layer, type Schema as S } from "effect";
|
|
3
2
|
|
|
4
|
-
import * as Binding from "./Binding";
|
|
3
|
+
import type * as Binding from "./Binding";
|
|
5
4
|
import * as DurableObjectEntrypoint from "./DurableObject";
|
|
6
5
|
import type { DurableObjectHandler } from "./DurableObject";
|
|
7
6
|
import * as DurableObjectNamespace from "./DurableObjectNamespace";
|
|
@@ -10,6 +9,13 @@ import type * as Rpc from "./Rpc";
|
|
|
10
9
|
import * as RpcDefinition from "./RpcDefinition";
|
|
11
10
|
import type { WorkerEnvironment } from "./Environment";
|
|
12
11
|
|
|
12
|
+
/**
|
|
13
|
+
* The client tuple types are re-established by the final `TagClass` cast, so
|
|
14
|
+
* these internal invocations erase the binding client's generic argument
|
|
15
|
+
* tuples instead of instantiating them at `never`.
|
|
16
|
+
*/
|
|
17
|
+
type UnsafeInvoke<E> = (...args: ReadonlyArray<unknown>) => Effect.Effect<unknown, E>;
|
|
18
|
+
|
|
13
19
|
export type ServiceFreeSchema = S.Codec<any, any, never, never>;
|
|
14
20
|
|
|
15
21
|
export interface Method<
|
|
@@ -232,6 +238,7 @@ export const Tag =
|
|
|
232
238
|
methods: MethodsShape & NoReservedMethods<MethodsShape>,
|
|
233
239
|
) => {
|
|
234
240
|
const definition = makeDefinition<Id, MethodsShape>(id, methods);
|
|
241
|
+
|
|
235
242
|
type SelfDefinition = Definition<Id, MethodsShape>;
|
|
236
243
|
type ClientApi = Api<SelfDefinition>;
|
|
237
244
|
const tag = Context.Service<
|
|
@@ -254,16 +261,19 @@ export const Tag =
|
|
|
254
261
|
options?: globalThis.DurableObjectNamespaceNewUniqueIdOptions,
|
|
255
262
|
) {
|
|
256
263
|
const namespace = yield* tag;
|
|
264
|
+
|
|
257
265
|
return yield* namespace.newUniqueId(options);
|
|
258
266
|
});
|
|
259
267
|
|
|
260
268
|
const idFromName = Effect.fnUntraced(function* (name: string) {
|
|
261
269
|
const namespace = yield* tag;
|
|
270
|
+
|
|
262
271
|
return yield* namespace.idFromName(name);
|
|
263
272
|
});
|
|
264
273
|
|
|
265
274
|
const idFromString = Effect.fnUntraced(function* (value: string) {
|
|
266
275
|
const namespace = yield* tag;
|
|
276
|
+
|
|
267
277
|
return yield* namespace.idFromString(value);
|
|
268
278
|
});
|
|
269
279
|
|
|
@@ -272,6 +282,7 @@ export const Tag =
|
|
|
272
282
|
options?: globalThis.DurableObjectNamespaceGetDurableObjectOptions,
|
|
273
283
|
) {
|
|
274
284
|
const namespace = yield* tag;
|
|
285
|
+
|
|
275
286
|
return yield* namespace.get(objectId, options);
|
|
276
287
|
});
|
|
277
288
|
|
|
@@ -280,11 +291,13 @@ export const Tag =
|
|
|
280
291
|
options?: globalThis.DurableObjectNamespaceGetDurableObjectOptions,
|
|
281
292
|
) {
|
|
282
293
|
const namespace = yield* tag;
|
|
294
|
+
|
|
283
295
|
return yield* namespace.getByName(name, options);
|
|
284
296
|
});
|
|
285
297
|
|
|
286
298
|
const jurisdiction = Effect.fnUntraced(function* (value: globalThis.DurableObjectJurisdiction) {
|
|
287
299
|
const namespace = yield* tag;
|
|
300
|
+
|
|
288
301
|
return yield* namespace.jurisdiction(value);
|
|
289
302
|
});
|
|
290
303
|
|
|
@@ -295,6 +308,7 @@ export const Tag =
|
|
|
295
308
|
) =>
|
|
296
309
|
Effect.gen(function* () {
|
|
297
310
|
const namespace = yield* tag;
|
|
311
|
+
|
|
298
312
|
return yield* namespace.fetch(stub, input, init);
|
|
299
313
|
});
|
|
300
314
|
|
|
@@ -305,7 +319,12 @@ export const Tag =
|
|
|
305
319
|
) =>
|
|
306
320
|
Effect.gen(function* () {
|
|
307
321
|
const namespace = yield* tag;
|
|
308
|
-
|
|
322
|
+
|
|
323
|
+
return yield* (namespace.rpc as UnsafeInvoke<DurableObjectNamespace.DurableObjectRpcError>)(
|
|
324
|
+
stub,
|
|
325
|
+
method,
|
|
326
|
+
...args,
|
|
327
|
+
);
|
|
309
328
|
});
|
|
310
329
|
|
|
311
330
|
const call = <Method extends keyof ClientApi>(
|
|
@@ -315,7 +334,10 @@ export const Tag =
|
|
|
315
334
|
) =>
|
|
316
335
|
Effect.gen(function* () {
|
|
317
336
|
const namespace = yield* tag;
|
|
318
|
-
|
|
337
|
+
|
|
338
|
+
return yield* (
|
|
339
|
+
namespace.call as UnsafeInvoke<DurableObjectNamespace.DurableObjectRpcError>
|
|
340
|
+
)(stub, method, ...args);
|
|
319
341
|
});
|
|
320
342
|
|
|
321
343
|
const scopedCall = <Method extends keyof ClientApi>(
|
|
@@ -325,11 +347,15 @@ export const Tag =
|
|
|
325
347
|
) =>
|
|
326
348
|
Effect.gen(function* () {
|
|
327
349
|
const namespace = yield* tag;
|
|
328
|
-
|
|
350
|
+
|
|
351
|
+
return yield* (
|
|
352
|
+
namespace.scopedCall as UnsafeInvoke<DurableObjectNamespace.DurableObjectRpcError>
|
|
353
|
+
)(stub, method, ...args);
|
|
329
354
|
});
|
|
330
355
|
|
|
331
356
|
const unsafeRaw = Effect.fnUntraced(function* () {
|
|
332
357
|
const namespace = yield* tag;
|
|
358
|
+
|
|
333
359
|
return yield* namespace.unsafeRaw;
|
|
334
360
|
});
|
|
335
361
|
|
|
@@ -374,10 +400,12 @@ const wrapHandlers = <ROut, const Self extends Definition.Any>(
|
|
|
374
400
|
RpcDefinition.Definition.MethodNames<Self>
|
|
375
401
|
>) {
|
|
376
402
|
const handler = handlers[key];
|
|
403
|
+
|
|
377
404
|
wrapped[key] = (...args: Array<unknown>) =>
|
|
378
405
|
Effect.gen(function* () {
|
|
379
406
|
const decodedArgs = yield* RpcDefinition.decodeArgs(definition, key, args);
|
|
380
407
|
const value = yield* handler(...decodedArgs);
|
|
408
|
+
|
|
381
409
|
return yield* RpcDefinition.encodeSuccess(definition, key, value);
|
|
382
410
|
});
|
|
383
411
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Data, Effect, type Context, type Scope } from "effect";
|
|
2
2
|
|
|
3
3
|
import * as Binding from "./Binding";
|
|
4
4
|
import * as CloudflareRpc from "./Rpc";
|
|
@@ -511,6 +511,7 @@ export const makeClient = <
|
|
|
511
511
|
const methodName = String(method);
|
|
512
512
|
const result = yield* rpc(stub, method, ...args);
|
|
513
513
|
const value = yield* CloudflareRpc.scoped(result);
|
|
514
|
+
|
|
514
515
|
return yield* decodeSuccess<Method>(methodName, value);
|
|
515
516
|
});
|
|
516
517
|
|
|
@@ -598,6 +599,7 @@ export const makeDirectMethods = <
|
|
|
598
599
|
fetch: (input: RequestInfo | URL, init?: RequestInit) =>
|
|
599
600
|
Effect.gen(function* () {
|
|
600
601
|
const stub = yield* getStub();
|
|
602
|
+
|
|
601
603
|
return yield* helpers.fetch(stub, input, init);
|
|
602
604
|
}),
|
|
603
605
|
} as Record<string, unknown>;
|
|
@@ -606,6 +608,7 @@ export const makeDirectMethods = <
|
|
|
606
608
|
client[methodName] = (...args: Array<unknown>) =>
|
|
607
609
|
Effect.gen(function* () {
|
|
608
610
|
const stub = yield* getStub();
|
|
611
|
+
|
|
609
612
|
return yield* (
|
|
610
613
|
helpers.call as (
|
|
611
614
|
stub: DurableObjectStubClient<Api>,
|
|
@@ -70,18 +70,23 @@ export const layer = (options: LayerOptions = {}) =>
|
|
|
70
70
|
|
|
71
71
|
const reserveClientId = (socket: DurableWebSocket) => {
|
|
72
72
|
const attachment = readAttachment(socket.raw, attachmentKey);
|
|
73
|
+
|
|
73
74
|
if (attachment !== undefined) {
|
|
74
75
|
nextClientId = Math.max(nextClientId, attachment + 1);
|
|
76
|
+
|
|
75
77
|
return attachment;
|
|
76
78
|
}
|
|
77
79
|
|
|
78
80
|
const id = nextClientId++;
|
|
81
|
+
|
|
79
82
|
writeAttachment(socket.raw, attachmentKey, id);
|
|
83
|
+
|
|
80
84
|
return id;
|
|
81
85
|
};
|
|
82
86
|
|
|
83
87
|
const register = (socket: DurableWebSocket) => {
|
|
84
88
|
const existing = connectionsBySocket.get(socket.raw);
|
|
89
|
+
|
|
85
90
|
if (existing !== undefined) {
|
|
86
91
|
return existing;
|
|
87
92
|
}
|
|
@@ -95,12 +100,14 @@ export const layer = (options: LayerOptions = {}) =>
|
|
|
95
100
|
connectionsBySocket.set(socket.raw, connection);
|
|
96
101
|
connectionsById.set(connection.id, connection);
|
|
97
102
|
clientIds.add(connection.id);
|
|
103
|
+
|
|
98
104
|
return connection;
|
|
99
105
|
};
|
|
100
106
|
|
|
101
107
|
const unregister = (socket: DurableWebSocket) =>
|
|
102
108
|
Effect.sync(() => {
|
|
103
109
|
const connection = connectionsBySocket.get(socket.raw);
|
|
110
|
+
|
|
104
111
|
if (connection === undefined) {
|
|
105
112
|
return;
|
|
106
113
|
}
|
|
@@ -119,11 +126,13 @@ export const layer = (options: LayerOptions = {}) =>
|
|
|
119
126
|
Effect.sync(() => {
|
|
120
127
|
try {
|
|
121
128
|
const encoded = connection.parser.encode(response);
|
|
129
|
+
|
|
122
130
|
if (encoded !== undefined) {
|
|
123
131
|
connection.socket.raw.send(encoded);
|
|
124
132
|
}
|
|
125
133
|
} catch (cause) {
|
|
126
134
|
const encoded = connection.parser.encode(RpcMessage.ResponseDefectEncoded(cause));
|
|
135
|
+
|
|
127
136
|
if (encoded !== undefined) {
|
|
128
137
|
connection.socket.raw.send(encoded);
|
|
129
138
|
}
|
|
@@ -137,11 +146,13 @@ export const layer = (options: LayerOptions = {}) =>
|
|
|
137
146
|
disconnects,
|
|
138
147
|
send: (clientId, response) => {
|
|
139
148
|
const connection = connectionsById.get(clientId);
|
|
149
|
+
|
|
140
150
|
return connection === undefined ? Effect.void : send(connection, response);
|
|
141
151
|
},
|
|
142
152
|
end: (clientId) =>
|
|
143
153
|
Effect.sync(() => {
|
|
144
154
|
const connection = connectionsById.get(clientId);
|
|
155
|
+
|
|
145
156
|
connection?.socket.raw.close();
|
|
146
157
|
}),
|
|
147
158
|
clientIds: Effect.sync(() => clientIds),
|
|
@@ -167,8 +178,10 @@ export const layer = (options: LayerOptions = {}) =>
|
|
|
167
178
|
});
|
|
168
179
|
|
|
169
180
|
const run = writeRequest;
|
|
181
|
+
|
|
170
182
|
if (run === undefined) {
|
|
171
183
|
yield* send(connection, RpcMessage.ResponseDefectEncoded("RPC server is not ready"));
|
|
184
|
+
|
|
172
185
|
return;
|
|
173
186
|
}
|
|
174
187
|
|
|
@@ -178,6 +191,7 @@ export const layer = (options: LayerOptions = {}) =>
|
|
|
178
191
|
}).pipe(
|
|
179
192
|
Effect.catch((error) => {
|
|
180
193
|
const connection = register(socket);
|
|
194
|
+
|
|
181
195
|
return send(connection, error);
|
|
182
196
|
}),
|
|
183
197
|
),
|
|
@@ -219,11 +219,13 @@ const schemaBackedSyncKvStorage = <Key, Value, EncodedValue>(
|
|
|
219
219
|
Effect.gen(function* () {
|
|
220
220
|
const encodedKey = yield* encodeKey(key);
|
|
221
221
|
const encodedValue = yield* encodeValue(value);
|
|
222
|
+
|
|
222
223
|
yield* tryStorageSync("kv.put", () => kv.put(encodedKey, encodedValue));
|
|
223
224
|
}),
|
|
224
225
|
delete: (key) =>
|
|
225
226
|
Effect.gen(function* () {
|
|
226
227
|
const encodedKey = yield* encodeKey(key);
|
|
228
|
+
|
|
227
229
|
return yield* tryStorageSync("kv.delete", () => kv.delete(encodedKey));
|
|
228
230
|
}),
|
|
229
231
|
list: (options) =>
|