alchemy 2.0.0-beta.45 → 2.0.0-beta.47
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/bin/exec.js +18 -10
- package/bin/exec.js.map +1 -1
- package/lib/AlchemyContext.d.ts +0 -1
- package/lib/AlchemyContext.d.ts.map +1 -1
- package/lib/AlchemyContext.js.map +1 -1
- package/lib/Apply.js +2 -2
- package/lib/Apply.js.map +1 -1
- package/lib/Cli/commands/_shared.d.ts +7 -1
- package/lib/Cli/commands/_shared.d.ts.map +1 -1
- package/lib/Cli/commands/_shared.js +1 -0
- package/lib/Cli/commands/_shared.js.map +1 -1
- package/lib/Cli/commands/cloudflare.d.ts.map +1 -1
- package/lib/Cli/commands/cloudflare.js +5 -1
- package/lib/Cli/commands/cloudflare.js.map +1 -1
- package/lib/Cli/commands/login.d.ts +1 -1
- package/lib/Cli/commands/login.d.ts.map +1 -1
- package/lib/Cli/commands/login.js +45 -42
- package/lib/Cli/commands/login.js.map +1 -1
- package/lib/Cli/commands/logs.js +1 -1
- package/lib/Cli/commands/logs.js.map +1 -1
- package/lib/Cli/commands/state.js +1 -1
- package/lib/Cli/commands/state.js.map +1 -1
- package/lib/Cli/commands/tail.js +1 -1
- package/lib/Cli/commands/tail.js.map +1 -1
- package/lib/Cloudflare/AiGateway/AiGateway.d.ts +112 -1
- package/lib/Cloudflare/AiGateway/AiGateway.d.ts.map +1 -1
- package/lib/Cloudflare/AiGateway/AiGateway.js +112 -1
- package/lib/Cloudflare/AiGateway/AiGateway.js.map +1 -1
- package/lib/Cloudflare/AiGateway/AiGatewayBinding.d.ts +43 -10
- package/lib/Cloudflare/AiGateway/AiGatewayBinding.d.ts.map +1 -1
- package/lib/Cloudflare/AiGateway/AiGatewayBinding.js +38 -10
- package/lib/Cloudflare/AiGateway/AiGatewayBinding.js.map +1 -1
- package/lib/Cloudflare/AiGateway/LanguageModel.d.ts +35 -0
- package/lib/Cloudflare/AiGateway/LanguageModel.d.ts.map +1 -0
- package/lib/Cloudflare/AiGateway/LanguageModel.js +576 -0
- package/lib/Cloudflare/AiGateway/LanguageModel.js.map +1 -0
- package/lib/Cloudflare/AiGateway/index.d.ts +1 -0
- package/lib/Cloudflare/AiGateway/index.d.ts.map +1 -1
- package/lib/Cloudflare/AiGateway/index.js +1 -0
- package/lib/Cloudflare/AiGateway/index.js.map +1 -1
- package/lib/Cloudflare/Providers.d.ts.map +1 -1
- package/lib/Cloudflare/Providers.js +5 -1
- package/lib/Cloudflare/Providers.js.map +1 -1
- package/lib/Cloudflare/Queue/QueueConsumer.d.ts.map +1 -1
- package/lib/Cloudflare/Queue/QueueConsumer.js +1 -2
- package/lib/Cloudflare/Queue/QueueConsumer.js.map +1 -1
- package/lib/Cloudflare/StateStore/Api.d.ts +1 -1
- package/lib/Cloudflare/StateStore/Api.js +1 -1
- package/lib/Cloudflare/StateStore/State.d.ts +11 -65
- package/lib/Cloudflare/StateStore/State.d.ts.map +1 -1
- package/lib/Cloudflare/StateStore/State.js +313 -368
- package/lib/Cloudflare/StateStore/State.js.map +1 -1
- package/lib/Cloudflare/StateStore/Store.d.ts.map +1 -1
- package/lib/Cloudflare/StateStore/Store.js +16 -29
- package/lib/Cloudflare/StateStore/Store.js.map +1 -1
- package/lib/Cloudflare/Vectorize/VectorizeIndex.d.ts +114 -0
- package/lib/Cloudflare/Vectorize/VectorizeIndex.d.ts.map +1 -0
- package/lib/Cloudflare/Vectorize/VectorizeIndex.js +157 -0
- package/lib/Cloudflare/Vectorize/VectorizeIndex.js.map +1 -0
- package/lib/Cloudflare/Vectorize/VectorizeIndexBinding.d.ts +38 -0
- package/lib/Cloudflare/Vectorize/VectorizeIndexBinding.d.ts.map +1 -0
- package/lib/Cloudflare/Vectorize/VectorizeIndexBinding.js +44 -0
- package/lib/Cloudflare/Vectorize/VectorizeIndexBinding.js.map +1 -0
- package/lib/Cloudflare/Vectorize/VectorizeMetadataIndex.d.ts +72 -0
- package/lib/Cloudflare/Vectorize/VectorizeMetadataIndex.d.ts.map +1 -0
- package/lib/Cloudflare/Vectorize/VectorizeMetadataIndex.js +131 -0
- package/lib/Cloudflare/Vectorize/VectorizeMetadataIndex.js.map +1 -0
- package/lib/Cloudflare/Vectorize/index.d.ts +4 -0
- package/lib/Cloudflare/Vectorize/index.d.ts.map +1 -0
- package/lib/Cloudflare/Vectorize/index.js +4 -0
- package/lib/Cloudflare/Vectorize/index.js.map +1 -0
- package/lib/Cloudflare/Workers/DurableObjectBridge.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/DurableObjectBridge.js +10 -1
- package/lib/Cloudflare/Workers/DurableObjectBridge.js.map +1 -1
- package/lib/Cloudflare/Workers/LocalWorkerProvider.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/LocalWorkerProvider.js +13 -1
- package/lib/Cloudflare/Workers/LocalWorkerProvider.js.map +1 -1
- package/lib/Cloudflare/Workers/Rpc.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/Rpc.js +20 -2
- package/lib/Cloudflare/Workers/Rpc.js.map +1 -1
- package/lib/Cloudflare/Workers/WorkerAsyncBindings.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/WorkerAsyncBindings.js +8 -0
- package/lib/Cloudflare/Workers/WorkerAsyncBindings.js.map +1 -1
- package/lib/Cloudflare/Workers/WorkerBinding.d.ts +2 -1
- package/lib/Cloudflare/Workers/WorkerBinding.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/WorkerBinding.js.map +1 -1
- package/lib/Cloudflare/Workers/WorkerBridge.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/WorkerBridge.js +20 -2
- package/lib/Cloudflare/Workers/WorkerBridge.js.map +1 -1
- package/lib/Cloudflare/index.d.ts +1 -0
- package/lib/Cloudflare/index.d.ts.map +1 -1
- package/lib/Cloudflare/index.js +1 -0
- package/lib/Cloudflare/index.js.map +1 -1
- package/lib/Output.js +2 -2
- package/lib/Output.js.map +1 -1
- package/lib/Plan.d.ts +2 -2
- package/lib/Plan.d.ts.map +1 -1
- package/lib/Plan.js +3 -3
- package/lib/Plan.js.map +1 -1
- package/lib/Stack.d.ts.map +1 -1
- package/lib/Stack.js +6 -0
- package/lib/Stack.js.map +1 -1
- package/lib/State/HttpStateStore.d.ts +4 -0
- package/lib/State/HttpStateStore.d.ts.map +1 -1
- package/lib/State/HttpStateStore.js +13 -0
- package/lib/State/HttpStateStore.js.map +1 -1
- package/lib/State/InMemoryState.d.ts +2 -1
- package/lib/State/InMemoryState.d.ts.map +1 -1
- package/lib/State/InMemoryState.js +3 -3
- package/lib/State/InMemoryState.js.map +1 -1
- package/lib/State/LocalState.d.ts.map +1 -1
- package/lib/State/LocalState.js +5 -1
- package/lib/State/LocalState.js.map +1 -1
- package/lib/State/State.d.ts +2 -2
- package/lib/State/State.d.ts.map +1 -1
- package/lib/State/State.js.map +1 -1
- package/lib/Util/poll.d.ts +30 -0
- package/lib/Util/poll.d.ts.map +1 -0
- package/lib/Util/poll.js +26 -0
- package/lib/Util/poll.js.map +1 -0
- package/lib/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +7 -7
- package/src/AlchemyContext.ts +0 -1
- package/src/Apply.ts +2 -2
- package/src/Cli/commands/_shared.ts +7 -1
- package/src/Cli/commands/cloudflare.ts +5 -3
- package/src/Cli/commands/login.ts +69 -60
- package/src/Cli/commands/logs.ts +1 -1
- package/src/Cli/commands/state.ts +1 -1
- package/src/Cli/commands/tail.ts +1 -1
- package/src/Cloudflare/AiGateway/AiGateway.ts +112 -1
- package/src/Cloudflare/AiGateway/AiGatewayBinding.ts +52 -10
- package/src/Cloudflare/AiGateway/LanguageModel.ts +907 -0
- package/src/Cloudflare/AiGateway/index.ts +1 -0
- package/src/Cloudflare/Providers.ts +7 -0
- package/src/Cloudflare/Queue/QueueConsumer.ts +0 -2
- package/src/Cloudflare/StateStore/Api.ts +1 -1
- package/src/Cloudflare/StateStore/State.ts +452 -508
- package/src/Cloudflare/StateStore/Store.ts +22 -35
- package/src/Cloudflare/Vectorize/VectorizeIndex.ts +261 -0
- package/src/Cloudflare/Vectorize/VectorizeIndexBinding.ts +103 -0
- package/src/Cloudflare/Vectorize/VectorizeMetadataIndex.ts +208 -0
- package/src/Cloudflare/Vectorize/index.ts +3 -0
- package/src/Cloudflare/Workers/DurableObjectBridge.ts +10 -1
- package/src/Cloudflare/Workers/LocalWorkerProvider.ts +13 -1
- package/src/Cloudflare/Workers/Rpc.ts +38 -9
- package/src/Cloudflare/Workers/WorkerAsyncBindings.ts +7 -0
- package/src/Cloudflare/Workers/WorkerBinding.ts +2 -0
- package/src/Cloudflare/Workers/WorkerBridge.ts +20 -2
- package/src/Cloudflare/index.ts +1 -0
- package/src/Output.ts +2 -2
- package/src/Plan.ts +4 -4
- package/src/Stack.ts +6 -0
- package/src/State/HttpStateStore.ts +28 -0
- package/src/State/InMemoryState.ts +87 -85
- package/src/State/LocalState.ts +15 -1
- package/src/State/State.ts +5 -4
- package/src/Util/poll.ts +47 -0
|
@@ -232,7 +232,19 @@ export const LocalWorkerProvider = () =>
|
|
|
232
232
|
}) {
|
|
233
233
|
const name = yield* createWorkerName(id, props.name);
|
|
234
234
|
const compatibility = getCompatibility(props);
|
|
235
|
-
const workerBindings: BindingHook<BindingServices>[] = [
|
|
235
|
+
const workerBindings: BindingHook<BindingServices>[] = [
|
|
236
|
+
Text.local("ALCHEMY_PHASE", "runtime"),
|
|
237
|
+
Text.local("ALCHEMY_STACK_NAME", stack.name),
|
|
238
|
+
Text.local("ALCHEMY_STAGE", stack.stage),
|
|
239
|
+
...Object.entries(props.env ?? {}).map(([key, value]) => {
|
|
240
|
+
const unredacted = Redacted.isRedacted(value)
|
|
241
|
+
? Redacted.value(value)
|
|
242
|
+
: value;
|
|
243
|
+
return typeof unredacted === "string"
|
|
244
|
+
? Text.local(key, unredacted)
|
|
245
|
+
: Json.local(key, unredacted);
|
|
246
|
+
}),
|
|
247
|
+
];
|
|
236
248
|
const durableObjectNamespaces: Record<string, string> = {};
|
|
237
249
|
const hyperdrives: Record<string, Required<HyperdriveOrigin>> = {};
|
|
238
250
|
for (const { data } of bindings) {
|
|
@@ -223,19 +223,48 @@ export const makeRpcStub = <Shape>(
|
|
|
223
223
|
return target[prop];
|
|
224
224
|
}
|
|
225
225
|
return (...args: any[]) =>
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
226
|
+
asEffectOrStream(
|
|
227
|
+
Effect.gen(function* () {
|
|
228
|
+
const stub = isLazy
|
|
229
|
+
? yield* stubSource as Effect.Effect<any>
|
|
230
|
+
: stubSource;
|
|
231
|
+
return yield* Effect.tryPromise({
|
|
232
|
+
try: () => (stub as any)[prop](...args),
|
|
233
|
+
catch: (cause) =>
|
|
234
|
+
new RpcCallError({ method: String(prop), cause }),
|
|
235
|
+
}).pipe(Effect.flatMap(decodeRpcResult));
|
|
236
|
+
}),
|
|
237
|
+
);
|
|
235
238
|
},
|
|
236
239
|
}) as Shape;
|
|
237
240
|
};
|
|
238
241
|
|
|
242
|
+
// Effect's internal Stream brand. `Stream.isStream` recognises a value by
|
|
243
|
+
// this property and reads its `channel`. A `makeRpcStub` method can't know
|
|
244
|
+
// synchronously whether the remote method returns a value or a `Stream`
|
|
245
|
+
// (the call is async), yet its declared type mirrors the DO `Shape`
|
|
246
|
+
// verbatim — value methods are typed `Effect<A>`, streaming methods `Stream<A>`.
|
|
247
|
+
// We satisfy BOTH by handing back the call `Effect` augmented with the Stream
|
|
248
|
+
// brand + channel, so the single return value can be `yield*`-ed / `.pipe`d as
|
|
249
|
+
// an Effect (value methods, e.g. `stub.put(k, v).pipe(Effect.orDie)`) AND piped
|
|
250
|
+
// through `Stream.*` combinators (streaming methods, e.g.
|
|
251
|
+
// `stub.tick(n).pipe(Stream.map(...))`).
|
|
252
|
+
const StreamTypeId = "~effect/Stream";
|
|
253
|
+
|
|
254
|
+
const asEffectOrStream = (
|
|
255
|
+
call: Effect.Effect<unknown, unknown>,
|
|
256
|
+
): Effect.Effect<unknown, unknown> => {
|
|
257
|
+
const streamForm = Stream.unwrap(
|
|
258
|
+
Effect.map(call, (value) =>
|
|
259
|
+
Stream.isStream(value) ? value : Stream.succeed(value),
|
|
260
|
+
),
|
|
261
|
+
);
|
|
262
|
+
return Object.assign(call, {
|
|
263
|
+
[StreamTypeId]: streamForm[StreamTypeId],
|
|
264
|
+
channel: streamForm.channel,
|
|
265
|
+
});
|
|
266
|
+
};
|
|
267
|
+
|
|
239
268
|
export const toRpcStream = (stream: Stream.Stream<any, any, any>) =>
|
|
240
269
|
Effect.scoped(
|
|
241
270
|
Effect.gen(function* () {
|
|
@@ -19,6 +19,7 @@ import { isImages } from "../Images/Images.ts";
|
|
|
19
19
|
import { isKVNamespace } from "../KV/KVNamespace.ts";
|
|
20
20
|
import { isQueue } from "../Queue/Queue.ts";
|
|
21
21
|
import { isR2Bucket } from "../R2/R2Bucket.ts";
|
|
22
|
+
import { isVectorizeIndex } from "../Vectorize/VectorizeIndex.ts";
|
|
22
23
|
import { isAssets } from "./Assets.ts";
|
|
23
24
|
import { isDurableObjectNamespaceLike } from "./DurableObjectNamespace.ts";
|
|
24
25
|
import type { WorkerBindingProps } from "./Worker.ts";
|
|
@@ -190,6 +191,12 @@ const toBinding = (
|
|
|
190
191
|
name: bindingName,
|
|
191
192
|
service: binding.workerName,
|
|
192
193
|
};
|
|
194
|
+
} else if (isVectorizeIndex(binding)) {
|
|
195
|
+
return {
|
|
196
|
+
type: "vectorize",
|
|
197
|
+
name: bindingName,
|
|
198
|
+
indexName: binding.indexName,
|
|
199
|
+
};
|
|
193
200
|
} else {
|
|
194
201
|
return {
|
|
195
202
|
type: "json",
|
|
@@ -17,6 +17,7 @@ import { Images } from "../Images/Images.ts";
|
|
|
17
17
|
import type { KVNamespace } from "../KV/KVNamespace.ts";
|
|
18
18
|
import type { Queue } from "../Queue/Queue.ts";
|
|
19
19
|
import type { R2Bucket } from "../R2/R2Bucket.ts";
|
|
20
|
+
import type { VectorizeIndex } from "../Vectorize/VectorizeIndex.ts";
|
|
20
21
|
import type { Assets } from "./Assets.ts";
|
|
21
22
|
import type { DurableObjectNamespaceLike } from "./DurableObjectNamespace.ts";
|
|
22
23
|
import { makeRpcStub } from "./Rpc.ts";
|
|
@@ -50,6 +51,7 @@ export type WorkerBindingResource =
|
|
|
50
51
|
| BrowserRendering
|
|
51
52
|
| Images
|
|
52
53
|
| Hyperdrive
|
|
54
|
+
| VectorizeIndex
|
|
53
55
|
| Worker
|
|
54
56
|
| DurableObjectNamespaceLike<any>;
|
|
55
57
|
|
|
@@ -141,8 +141,17 @@ export const makeWorkerBridge = (
|
|
|
141
141
|
),
|
|
142
142
|
);
|
|
143
143
|
}
|
|
144
|
+
const result = dispatcher(...args);
|
|
145
|
+
// A streaming RPC method returns a `Stream` directly rather
|
|
146
|
+
// than an `Effect`. Lift it into the success channel so the
|
|
147
|
+
// inner effect resolves to the `Stream` and `handleRpcExit`
|
|
148
|
+
// can encode it as a stream envelope. Anything else is the
|
|
149
|
+
// `Effect` it claims to be (its resolved value may itself be
|
|
150
|
+
// a `Stream`, which `handleRpcExit` also handles).
|
|
144
151
|
return Effect.succeed([
|
|
145
|
-
|
|
152
|
+
Stream.isStream(result)
|
|
153
|
+
? Effect.succeed(result)
|
|
154
|
+
: (result as Effect.Effect<any>),
|
|
146
155
|
Context.empty(),
|
|
147
156
|
] as const);
|
|
148
157
|
}),
|
|
@@ -274,7 +283,16 @@ export const makeRpcProxy = (
|
|
|
274
283
|
),
|
|
275
284
|
);
|
|
276
285
|
}
|
|
277
|
-
|
|
286
|
+
const result = dispatcher(...args);
|
|
287
|
+
// A streaming RPC method returns a `Stream` directly rather than
|
|
288
|
+
// an `Effect`. Lift it into the success channel so the resulting
|
|
289
|
+
// `Exit.value` is the `Stream` and `handleRpcExit` can encode it
|
|
290
|
+
// as a stream envelope. Anything else is the `Effect` it claims
|
|
291
|
+
// to be and is run normally (its resolved value may itself be a
|
|
292
|
+
// `Stream`, which `handleRpcExit` also handles).
|
|
293
|
+
return Stream.isStream(result)
|
|
294
|
+
? Effect.succeed(result)
|
|
295
|
+
: (result as Effect.Effect<any>);
|
|
278
296
|
}),
|
|
279
297
|
processEvent,
|
|
280
298
|
)
|
package/src/Cloudflare/index.ts
CHANGED
|
@@ -18,6 +18,7 @@ export * from "./R2/index.ts";
|
|
|
18
18
|
export * from "./SecretsStore/index.ts";
|
|
19
19
|
export * from "./StateStore/index.ts";
|
|
20
20
|
export * from "./Tunnel/index.ts";
|
|
21
|
+
export * from "./Vectorize/index.ts";
|
|
21
22
|
export * from "./VpcService/index.ts";
|
|
22
23
|
export * from "./Website/index.ts";
|
|
23
24
|
export * from "./Workers/index.ts";
|
package/src/Output.ts
CHANGED
|
@@ -549,7 +549,7 @@ export const evaluate: <A, Req = never>(
|
|
|
549
549
|
} else if (isNamedExpr(expr)) {
|
|
550
550
|
return yield* evaluate(expr.expr, upstream);
|
|
551
551
|
} else if (isRefExpr(expr)) {
|
|
552
|
-
const state = yield* State.State;
|
|
552
|
+
const state = yield* yield* State.State;
|
|
553
553
|
const stack = expr.stack ?? (yield* Stack).name;
|
|
554
554
|
const stage = expr.stage ?? (yield* Stage);
|
|
555
555
|
const resource = yield* state.get({
|
|
@@ -572,7 +572,7 @@ export const evaluate: <A, Req = never>(
|
|
|
572
572
|
// task's output value, otherwise undefined.
|
|
573
573
|
return (resource as any).attr ?? (resource as any).output;
|
|
574
574
|
} else if (isStackRefExpr(expr)) {
|
|
575
|
-
const state = yield* State.State;
|
|
575
|
+
const state = yield* yield* State.State;
|
|
576
576
|
const stack = expr.stack;
|
|
577
577
|
const stage = expr.stage ?? (yield* Stage);
|
|
578
578
|
const output = yield* state.getOutput({ stack, stage });
|
package/src/Plan.ts
CHANGED
|
@@ -4,6 +4,7 @@ import * as Effect from "effect/Effect";
|
|
|
4
4
|
import * as Option from "effect/Option";
|
|
5
5
|
import * as Redacted from "effect/Redacted";
|
|
6
6
|
import { asEffect } from ".//Util/types.ts";
|
|
7
|
+
import { isAction, type ActionLike } from "./Action.ts";
|
|
7
8
|
import {
|
|
8
9
|
AdoptPolicy,
|
|
9
10
|
OwnedBySomeoneElse,
|
|
@@ -41,19 +42,18 @@ import {
|
|
|
41
42
|
} from "./Resource.ts";
|
|
42
43
|
import { type StackSpec } from "./Stack.ts";
|
|
43
44
|
import {
|
|
45
|
+
isActionState,
|
|
44
46
|
State,
|
|
47
|
+
type ActionState,
|
|
45
48
|
type CreatedResourceState,
|
|
46
49
|
type CreatingResourceState,
|
|
47
|
-
isActionState,
|
|
48
50
|
type RanActionState,
|
|
49
51
|
type ReplacedResourceState,
|
|
50
52
|
type ReplacingResourceState,
|
|
51
53
|
type ResourceState,
|
|
52
|
-
type ActionState,
|
|
53
54
|
type UpdatedResourceState,
|
|
54
55
|
type UpdatingReourceState,
|
|
55
56
|
} from "./State/index.ts";
|
|
56
|
-
import { isAction, type ActionLike } from "./Action.ts";
|
|
57
57
|
import { hashInput } from "./Util/hash.ts";
|
|
58
58
|
import { findCycleMembers } from "./Util/scc.ts";
|
|
59
59
|
|
|
@@ -236,7 +236,7 @@ export const make = <A>(
|
|
|
236
236
|
): Effect.Effect<Plan<A>, never, State> =>
|
|
237
237
|
// @ts-expect-error
|
|
238
238
|
Effect.gen(function* () {
|
|
239
|
-
const state = yield* State;
|
|
239
|
+
const state = yield* yield* State;
|
|
240
240
|
|
|
241
241
|
const resources = Object.values(stack.resources);
|
|
242
242
|
const actions = Object.values(stack.actions ?? {});
|
package/src/Stack.ts
CHANGED
|
@@ -125,7 +125,10 @@ export const Stack: Context.ServiceClass<
|
|
|
125
125
|
// by default, reference the stack at the "current" stage of the importer
|
|
126
126
|
Output.stackRef<A>(stackName).pipe(effectClass),
|
|
127
127
|
{
|
|
128
|
+
stackName,
|
|
128
129
|
stage: createStageProxy(stackName),
|
|
130
|
+
state: options?.state,
|
|
131
|
+
providers: options?.providers,
|
|
129
132
|
make: <Req = never>(
|
|
130
133
|
options: StackProps<NoInfer<Req>>,
|
|
131
134
|
eff: Effect.Effect<A, never, Req>,
|
|
@@ -140,7 +143,10 @@ export const Stack: Context.ServiceClass<
|
|
|
140
143
|
}),
|
|
141
144
|
(eff) =>
|
|
142
145
|
Object.assign(eff, {
|
|
146
|
+
stackName,
|
|
143
147
|
stage: createStageProxy(stackName),
|
|
148
|
+
state: options?.state,
|
|
149
|
+
providers: options?.providers,
|
|
144
150
|
}),
|
|
145
151
|
);
|
|
146
152
|
},
|
|
@@ -39,6 +39,34 @@ export interface HttpStateStoreProps extends HttpStateStoreCredentials {
|
|
|
39
39
|
) => HttpClientRequest.HttpClientRequest;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
export const checkHttpStateStoreAuth = ({
|
|
43
|
+
url,
|
|
44
|
+
authToken,
|
|
45
|
+
}: {
|
|
46
|
+
url: string;
|
|
47
|
+
authToken: string;
|
|
48
|
+
}) =>
|
|
49
|
+
Effect.gen(function* () {
|
|
50
|
+
const apiClient = yield* HttpApiClient.make(StateApi, {
|
|
51
|
+
baseUrl: url,
|
|
52
|
+
transformClient: HttpClient.mapRequest((req) =>
|
|
53
|
+
req.pipe(HttpClientRequest.bearerToken(authToken)),
|
|
54
|
+
),
|
|
55
|
+
});
|
|
56
|
+
return yield* apiClient.state.listStacks().pipe(
|
|
57
|
+
Effect.map(() => true),
|
|
58
|
+
Effect.catchTag("Unauthorized", () => Effect.succeed(false)),
|
|
59
|
+
Effect.retry({
|
|
60
|
+
while: (error) =>
|
|
61
|
+
error._tag === "HttpClientError" &&
|
|
62
|
+
!!error.response &&
|
|
63
|
+
// worker can 404 for a bit on first deploy
|
|
64
|
+
(error.response.status === 404 || error.response.status >= 500),
|
|
65
|
+
schedule: Schedule.fixed(200),
|
|
66
|
+
}),
|
|
67
|
+
);
|
|
68
|
+
});
|
|
69
|
+
|
|
42
70
|
export const makeHttpStateStore = ({
|
|
43
71
|
url,
|
|
44
72
|
authToken,
|
|
@@ -18,8 +18,8 @@ export const inMemoryState = (
|
|
|
18
18
|
) =>
|
|
19
19
|
Layer.effect(
|
|
20
20
|
State,
|
|
21
|
-
Effect.
|
|
22
|
-
recordStateStoreInit,
|
|
21
|
+
Effect.cached(
|
|
22
|
+
InMemoryService(initialState, initialOutputs).pipe(recordStateStoreInit),
|
|
23
23
|
),
|
|
24
24
|
);
|
|
25
25
|
|
|
@@ -27,87 +27,89 @@ export const InMemoryService = (
|
|
|
27
27
|
state: Record<StackId, Record<StageId, Record<Fqn, ResourceState>>> = {},
|
|
28
28
|
outputs: Record<StackId, Record<StageId, unknown>> = {},
|
|
29
29
|
) =>
|
|
30
|
-
State.of(
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
get: ({
|
|
39
|
-
stack,
|
|
40
|
-
stage,
|
|
41
|
-
fqn,
|
|
42
|
-
}: {
|
|
43
|
-
stack: string;
|
|
44
|
-
stage: string;
|
|
45
|
-
fqn: string;
|
|
46
|
-
}) => Effect.succeed(state[stack]?.[stage]?.[fqn]),
|
|
47
|
-
getReplacedResources: ({
|
|
48
|
-
stack,
|
|
49
|
-
stage,
|
|
50
|
-
}: {
|
|
51
|
-
stack: string;
|
|
52
|
-
stage: string;
|
|
53
|
-
}) =>
|
|
54
|
-
Effect.succeed(
|
|
55
|
-
Array.from(Object.values(state[stack]?.[stage] ?? {}) ?? []).filter(
|
|
56
|
-
(s) => s.status === "replaced",
|
|
30
|
+
State.of(
|
|
31
|
+
Effect.succeed({
|
|
32
|
+
id: "inmemory",
|
|
33
|
+
getVersion: () => Effect.succeed(STATE_STORE_VERSION),
|
|
34
|
+
listStacks: () => Effect.succeed(Array.from(Object.keys(state))),
|
|
35
|
+
listStages: (stack: string) =>
|
|
36
|
+
Effect.succeed(
|
|
37
|
+
Array.from(stack in state ? Object.keys(state[stack]) : []),
|
|
57
38
|
),
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
stage
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
stack
|
|
105
|
-
stage: string;
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
39
|
+
get: ({
|
|
40
|
+
stack,
|
|
41
|
+
stage,
|
|
42
|
+
fqn,
|
|
43
|
+
}: {
|
|
44
|
+
stack: string;
|
|
45
|
+
stage: string;
|
|
46
|
+
fqn: string;
|
|
47
|
+
}) => Effect.succeed(state[stack]?.[stage]?.[fqn]),
|
|
48
|
+
getReplacedResources: ({
|
|
49
|
+
stack,
|
|
50
|
+
stage,
|
|
51
|
+
}: {
|
|
52
|
+
stack: string;
|
|
53
|
+
stage: string;
|
|
54
|
+
}) =>
|
|
55
|
+
Effect.succeed(
|
|
56
|
+
Array.from(Object.values(state[stack]?.[stage] ?? {}) ?? []).filter(
|
|
57
|
+
(s) => s.status === "replaced",
|
|
58
|
+
),
|
|
59
|
+
),
|
|
60
|
+
set: <V extends PersistedState>({
|
|
61
|
+
stack,
|
|
62
|
+
stage,
|
|
63
|
+
fqn,
|
|
64
|
+
value,
|
|
65
|
+
}: {
|
|
66
|
+
stack: string;
|
|
67
|
+
stage: string;
|
|
68
|
+
fqn: string;
|
|
69
|
+
value: V;
|
|
70
|
+
}) =>
|
|
71
|
+
Effect.sync(() => {
|
|
72
|
+
const stackState = (state[stack] ??= {});
|
|
73
|
+
const stageState = (stackState[stage] ??= {});
|
|
74
|
+
stageState[fqn] = value as ResourceState;
|
|
75
|
+
return value;
|
|
76
|
+
}),
|
|
77
|
+
delete: ({
|
|
78
|
+
stack,
|
|
79
|
+
stage,
|
|
80
|
+
fqn,
|
|
81
|
+
}: {
|
|
82
|
+
stack: string;
|
|
83
|
+
stage: string;
|
|
84
|
+
fqn: string;
|
|
85
|
+
}) => Effect.sync(() => delete state[stack]?.[stage]?.[fqn]),
|
|
86
|
+
deleteStack: ({ stack, stage }: { stack: string; stage?: string }) =>
|
|
87
|
+
Effect.sync(() => {
|
|
88
|
+
if (stage === undefined) {
|
|
89
|
+
delete state[stack];
|
|
90
|
+
} else {
|
|
91
|
+
delete state[stack]?.[stage];
|
|
92
|
+
}
|
|
93
|
+
}),
|
|
94
|
+
list: ({ stack, stage }: { stack: string; stage: string }) =>
|
|
95
|
+
Effect.succeed(
|
|
96
|
+
Array.from(Object.keys(state[stack]?.[stage] ?? {}) ?? []),
|
|
97
|
+
),
|
|
98
|
+
getOutput: ({ stack, stage }: { stack: string; stage: string }) =>
|
|
99
|
+
Effect.succeed(outputs[stack]?.[stage]),
|
|
100
|
+
setOutput: ({
|
|
101
|
+
stack,
|
|
102
|
+
stage,
|
|
103
|
+
value,
|
|
104
|
+
}: {
|
|
105
|
+
stack: string;
|
|
106
|
+
stage: string;
|
|
107
|
+
value: unknown;
|
|
108
|
+
}) =>
|
|
109
|
+
Effect.sync(() => {
|
|
110
|
+
const stackOutputs = (outputs[stack] ??= {});
|
|
111
|
+
stackOutputs[stage] = value;
|
|
112
|
+
return value;
|
|
113
|
+
}),
|
|
114
|
+
}),
|
|
115
|
+
);
|
package/src/State/LocalState.ts
CHANGED
|
@@ -10,7 +10,21 @@ import { State, StateStoreError, type StateService } from "./State.ts";
|
|
|
10
10
|
import { encodeState, reviveState } from "./StateEncoding.ts";
|
|
11
11
|
|
|
12
12
|
export const localState = () =>
|
|
13
|
-
Layer.effect(
|
|
13
|
+
Layer.effect(
|
|
14
|
+
State,
|
|
15
|
+
Effect.gen(function* () {
|
|
16
|
+
const context = yield* Effect.context<
|
|
17
|
+
FileSystem.FileSystem | Path.Path
|
|
18
|
+
>();
|
|
19
|
+
|
|
20
|
+
const make = makeLocalState().pipe(
|
|
21
|
+
recordStateStoreInit,
|
|
22
|
+
Effect.provideContext(context),
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
return yield* Effect.cached(make);
|
|
26
|
+
}),
|
|
27
|
+
);
|
|
14
28
|
|
|
15
29
|
export const makeLocalState = () =>
|
|
16
30
|
Effect.gen(function* () {
|
package/src/State/State.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as Context from "effect/Context";
|
|
2
2
|
import * as Data from "effect/Data";
|
|
3
3
|
import * as Effect from "effect/Effect";
|
|
4
|
-
import type { ReplacedResourceState, ResourceState } from "./ResourceState.ts";
|
|
5
4
|
import type { ActionState } from "./ActionState.ts";
|
|
5
|
+
import type { ReplacedResourceState, ResourceState } from "./ResourceState.ts";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Anything persistable under an FQN. Resources are discriminated by status
|
|
@@ -23,9 +23,10 @@ export class StateStoreError extends Data.TaggedError("StateStoreError")<{
|
|
|
23
23
|
cause?: Error;
|
|
24
24
|
}> {}
|
|
25
25
|
|
|
26
|
-
export class State extends Context.Service<
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
export class State extends Context.Service<
|
|
27
|
+
State,
|
|
28
|
+
Effect.Effect<StateService>
|
|
29
|
+
>()("alchemy/State") {}
|
|
29
30
|
|
|
30
31
|
/**
|
|
31
32
|
* State service interface.
|
package/src/Util/poll.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import * as Data from "effect/Data";
|
|
2
|
+
import * as Effect from "effect/Effect";
|
|
3
|
+
import * as Predicate from "effect/Predicate";
|
|
4
|
+
import * as Schedule from "effect/Schedule";
|
|
5
|
+
|
|
6
|
+
export class PredicateFailed extends Data.TaggedError("PredicateFailed")<{
|
|
7
|
+
message: string;
|
|
8
|
+
actual: unknown;
|
|
9
|
+
}> {}
|
|
10
|
+
|
|
11
|
+
export const isPredicateFailed = (e: unknown): e is PredicateFailed =>
|
|
12
|
+
Predicate.isTagged(e, "PredicateFailed");
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Retries an effect until a predicate is met.
|
|
16
|
+
* @param input - The input to the poll function.
|
|
17
|
+
* @param input.description - The description of what is being polled; used in the error message if the predicate fails.
|
|
18
|
+
* @param input.effect - The effect to execute until the predicate is met.
|
|
19
|
+
* @param input.predicate - The predicate to check if the effect has met the desired state.
|
|
20
|
+
* @param input.schedule - The schedule to use for retries; defaults to every 3 seconds.
|
|
21
|
+
* @param input.times - The maximum number of times to poll; defaults to 50.
|
|
22
|
+
* @returns The value that satisfies the predicate.
|
|
23
|
+
*/
|
|
24
|
+
export const poll = Effect.fn("poll")(
|
|
25
|
+
<A, E, R>(input: {
|
|
26
|
+
description?: string;
|
|
27
|
+
effect: Effect.Effect<A, E, R>;
|
|
28
|
+
predicate: (value: A) => boolean;
|
|
29
|
+
schedule?: Schedule.Schedule<unknown, unknown, never>;
|
|
30
|
+
times?: number;
|
|
31
|
+
}) =>
|
|
32
|
+
input.effect.pipe(
|
|
33
|
+
Effect.filterOrFail(
|
|
34
|
+
input.predicate,
|
|
35
|
+
(actual) =>
|
|
36
|
+
new PredicateFailed({
|
|
37
|
+
message: `Predicate failed: ${input.description ?? "<no description>"}`,
|
|
38
|
+
actual,
|
|
39
|
+
}),
|
|
40
|
+
),
|
|
41
|
+
Effect.retry({
|
|
42
|
+
while: isPredicateFailed,
|
|
43
|
+
schedule: input.schedule ?? Schedule.spaced("3 seconds"),
|
|
44
|
+
times: input.times ?? 50,
|
|
45
|
+
}),
|
|
46
|
+
),
|
|
47
|
+
);
|