alchemy 2.0.0-beta.39 → 2.0.0-beta.40
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 +5 -26
- package/bin/exec.js.map +1 -1
- package/lib/Auth/Lock.d.ts +4 -3
- package/lib/Auth/Lock.d.ts.map +1 -1
- package/lib/Auth/Lock.js +6 -5
- package/lib/Auth/Lock.js.map +1 -1
- package/lib/Axiom/Chart.d.ts +52 -2
- package/lib/Axiom/Chart.d.ts.map +1 -1
- package/lib/Bundle/Bundle.d.ts +7 -4
- package/lib/Bundle/Bundle.d.ts.map +1 -1
- package/lib/Bundle/Bundle.js +37 -14
- package/lib/Bundle/Bundle.js.map +1 -1
- package/lib/Cloudflare/AiGateway/AiGateway.d.ts +11 -11
- package/lib/Cloudflare/AiGateway/AiGateway.d.ts.map +1 -1
- package/lib/Cloudflare/AiGateway/AiGateway.js +28 -63
- package/lib/Cloudflare/AiGateway/AiGateway.js.map +1 -1
- package/lib/Cloudflare/Bridge.d.ts +3 -0
- package/lib/Cloudflare/Bridge.d.ts.map +1 -0
- package/lib/Cloudflare/Bridge.js +3 -0
- package/lib/Cloudflare/Bridge.js.map +1 -0
- package/lib/Cloudflare/Container/StartContainer.d.ts +2 -2
- package/lib/Cloudflare/Container/StartContainer.d.ts.map +1 -1
- package/lib/Cloudflare/Container/StartContainer.js +3 -3
- package/lib/Cloudflare/Container/StartContainer.js.map +1 -1
- package/lib/Cloudflare/Local/Sidecar.d.ts +45 -21
- package/lib/Cloudflare/Local/Sidecar.d.ts.map +1 -1
- package/lib/Cloudflare/Local/Sidecar.js +30 -6
- package/lib/Cloudflare/Local/Sidecar.js.map +1 -1
- package/lib/Cloudflare/Local/SidecarHandlers.d.ts +5 -2
- package/lib/Cloudflare/Local/SidecarHandlers.d.ts.map +1 -1
- package/lib/Cloudflare/Local/SidecarHandlers.js +323 -70
- package/lib/Cloudflare/Local/SidecarHandlers.js.map +1 -1
- package/lib/Cloudflare/Local/SidecarServer.js +9 -5
- package/lib/Cloudflare/Local/SidecarServer.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/Store.d.ts +1 -1
- package/lib/Cloudflare/Workers/HttpServer.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/HttpServer.js +3 -25
- package/lib/Cloudflare/Workers/HttpServer.js.map +1 -1
- package/lib/Cloudflare/Workers/LocalWorkerProvider.d.ts +1 -2
- package/lib/Cloudflare/Workers/LocalWorkerProvider.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/LocalWorkerProvider.js +16 -97
- package/lib/Cloudflare/Workers/LocalWorkerProvider.js.map +1 -1
- package/lib/Cloudflare/Workers/Vite.d.ts +9 -0
- package/lib/Cloudflare/Workers/Vite.d.ts.map +1 -0
- package/lib/Cloudflare/Workers/Vite.js +95 -0
- package/lib/Cloudflare/Workers/Vite.js.map +1 -0
- package/lib/Cloudflare/Workers/Worker.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/Worker.js +9 -95
- package/lib/Cloudflare/Workers/Worker.js.map +1 -1
- package/lib/Cloudflare/Workers/WorkerBundle.d.ts +1 -2
- package/lib/Cloudflare/Workers/WorkerBundle.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/WorkerBundle.js +5 -4
- package/lib/Cloudflare/Workers/WorkerBundle.js.map +1 -1
- package/lib/Diff.d.ts +15 -1
- package/lib/Diff.d.ts.map +1 -1
- package/lib/Diff.js +13 -7
- package/lib/Diff.js.map +1 -1
- package/lib/Http.d.ts.map +1 -1
- package/lib/Http.js +12 -7
- package/lib/Http.js.map +1 -1
- package/lib/Sidecar/Lock.d.ts +4 -5
- package/lib/Sidecar/Lock.d.ts.map +1 -1
- package/lib/Sidecar/Lock.js +22 -85
- package/lib/Sidecar/Lock.js.map +1 -1
- package/lib/Sidecar/RpcHandler.d.ts +4 -1
- package/lib/Sidecar/RpcHandler.d.ts.map +1 -1
- package/lib/Sidecar/RpcHandler.js +21 -2
- package/lib/Sidecar/RpcHandler.js.map +1 -1
- package/lib/Util/PlatformServices.d.ts.map +1 -1
- package/lib/Util/PlatformServices.js +0 -2
- package/lib/Util/PlatformServices.js.map +1 -1
- package/lib/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +17 -12
- package/src/AWS/EC2/hosted.ts +1 -1
- package/src/AWS/ECS/Task.ts +1 -1
- package/src/AWS/Lambda/Function.ts +1 -1
- package/src/Auth/Lock.ts +6 -5
- package/src/Axiom/Chart.ts +56 -2
- package/src/Bundle/Bundle.ts +41 -17
- package/src/Cloudflare/AiGateway/AiGateway.ts +43 -76
- package/src/Cloudflare/Bridge.ts +2 -0
- package/src/Cloudflare/Container/ContainerApplication.ts +1 -1
- package/src/Cloudflare/Container/StartContainer.ts +10 -3
- package/src/Cloudflare/Local/Sidecar.ts +50 -21
- package/src/Cloudflare/Local/SidecarHandlers.ts +395 -91
- package/src/Cloudflare/Local/SidecarServer.ts +12 -4
- package/src/Cloudflare/StateStore/Api.ts +1 -1
- package/src/Cloudflare/Workers/HttpServer.ts +5 -37
- package/src/Cloudflare/Workers/LocalWorkerProvider.ts +18 -111
- package/src/Cloudflare/Workers/Vite.ts +114 -0
- package/src/Cloudflare/Workers/Worker.ts +27 -109
- package/src/Cloudflare/Workers/WorkerBundle.ts +7 -7
- package/src/Diff.ts +29 -8
- package/src/Http.ts +14 -10
- package/src/Sidecar/Lock.ts +30 -154
- package/src/Sidecar/RpcHandler.ts +67 -40
- package/src/Util/PlatformServices.ts +0 -2
package/src/Sidecar/Lock.ts
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
|
+
import * as lockfile from "@alchemy.run/node-utils";
|
|
1
2
|
import * as Context from "effect/Context";
|
|
2
|
-
import * as Duration from "effect/Duration";
|
|
3
3
|
import * as Effect from "effect/Effect";
|
|
4
|
-
import * as Fiber from "effect/Fiber";
|
|
5
|
-
import * as FileSystem from "effect/FileSystem";
|
|
4
|
+
import type * as Fiber from "effect/Fiber";
|
|
6
5
|
import * as Layer from "effect/Layer";
|
|
7
|
-
import * as Option from "effect/Option";
|
|
8
|
-
import * as Schedule from "effect/Schedule";
|
|
9
6
|
import * as Schema from "effect/Schema";
|
|
10
7
|
import type * as Scope from "effect/Scope";
|
|
11
8
|
import * as RpcPaths from "./RpcPaths.ts";
|
|
@@ -13,13 +10,7 @@ import * as RpcPaths from "./RpcPaths.ts";
|
|
|
13
10
|
export class LockError extends Schema.TaggedErrorClass<LockError>()(
|
|
14
11
|
"LockError",
|
|
15
12
|
{
|
|
16
|
-
reason: Schema.Literals([
|
|
17
|
-
"Cancelled",
|
|
18
|
-
"Conflict",
|
|
19
|
-
"Invalid",
|
|
20
|
-
"Timeout",
|
|
21
|
-
"PlatformError",
|
|
22
|
-
]),
|
|
13
|
+
reason: Schema.Literals(["Conflict", "PlatformError"]),
|
|
23
14
|
message: Schema.String,
|
|
24
15
|
cause: Schema.optional(Schema.DefectWithStack),
|
|
25
16
|
},
|
|
@@ -30,161 +21,46 @@ export class Lock extends Context.Service<
|
|
|
30
21
|
{
|
|
31
22
|
readonly check: Effect.Effect<boolean>;
|
|
32
23
|
readonly acquire: Effect.Effect<
|
|
33
|
-
Fiber.Fiber<
|
|
24
|
+
Fiber.Fiber<never, LockError>,
|
|
34
25
|
LockError,
|
|
35
26
|
Scope.Scope
|
|
36
27
|
>;
|
|
37
28
|
}
|
|
38
29
|
>()("Lock") {}
|
|
39
30
|
|
|
40
|
-
const
|
|
41
|
-
const
|
|
31
|
+
const STALE_MS = 10_000;
|
|
32
|
+
const UPDATE_MS = 1_000;
|
|
42
33
|
|
|
43
34
|
const make = Effect.gen(function* () {
|
|
44
|
-
const fs = yield* FileSystem.FileSystem;
|
|
45
35
|
const paths = yield* RpcPaths.RpcPaths;
|
|
46
36
|
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
)
|
|
68
|
-
: Effect.succeed(pid);
|
|
69
|
-
}),
|
|
70
|
-
);
|
|
71
|
-
|
|
72
|
-
const isLockProcessAlive = readLockFile.pipe(
|
|
73
|
-
Effect.flatMap((pid) =>
|
|
74
|
-
Effect.sync(() => {
|
|
75
|
-
try {
|
|
76
|
-
process.kill(pid, 0);
|
|
77
|
-
return true;
|
|
78
|
-
} catch {
|
|
79
|
-
return false;
|
|
80
|
-
}
|
|
37
|
+
const check = Effect.tryPromise(() =>
|
|
38
|
+
lockfile.check(paths.lock, { stale: STALE_MS, realpath: false }),
|
|
39
|
+
).pipe(Effect.orElseSucceed(() => false));
|
|
40
|
+
|
|
41
|
+
const acquire = Effect.gen(function* () {
|
|
42
|
+
yield* Effect.acquireRelease(
|
|
43
|
+
Effect.tryPromise({
|
|
44
|
+
try: () =>
|
|
45
|
+
lockfile.lock(paths.lock, {
|
|
46
|
+
stale: STALE_MS,
|
|
47
|
+
update: UPDATE_MS,
|
|
48
|
+
retries: 0,
|
|
49
|
+
realpath: false,
|
|
50
|
+
}),
|
|
51
|
+
catch: (cause) =>
|
|
52
|
+
new LockError({
|
|
53
|
+
reason: "Conflict",
|
|
54
|
+
message: "Lock already held by another process",
|
|
55
|
+
cause,
|
|
56
|
+
}),
|
|
81
57
|
}),
|
|
82
|
-
|
|
83
|
-
Effect.orElseSucceed(() => false),
|
|
84
|
-
);
|
|
85
|
-
|
|
86
|
-
const isLockFileStale = fs.stat(paths.lock).pipe(
|
|
87
|
-
Effect.map((stat) => {
|
|
88
|
-
const mtime = Option.getOrElse(stat.mtime, () => new Date(0));
|
|
89
|
-
return mtime.getTime() < Date.now() - Duration.toMillis(LOCK_TTL);
|
|
90
|
-
}),
|
|
91
|
-
Effect.orElseSucceed(() => true),
|
|
92
|
-
);
|
|
93
|
-
|
|
94
|
-
const isLockValid = Effect.zipWith(
|
|
95
|
-
isLockProcessAlive,
|
|
96
|
-
isLockFileStale,
|
|
97
|
-
(alive, stale) => alive && !stale,
|
|
98
|
-
{
|
|
99
|
-
concurrent: true,
|
|
100
|
-
},
|
|
101
|
-
);
|
|
102
|
-
|
|
103
|
-
const makeLockFile = fs
|
|
104
|
-
.writeFileString(paths.lock, process.pid.toString(), { flag: "wx" })
|
|
105
|
-
.pipe(
|
|
106
|
-
Effect.mapError((e) =>
|
|
107
|
-
e.reason._tag === "AlreadyExists"
|
|
108
|
-
? new LockError({
|
|
109
|
-
reason: "Conflict",
|
|
110
|
-
message: "Lock already held by another process",
|
|
111
|
-
})
|
|
112
|
-
: new LockError({
|
|
113
|
-
reason: "PlatformError",
|
|
114
|
-
message: "Failed to create lock file",
|
|
115
|
-
cause: e,
|
|
116
|
-
}),
|
|
117
|
-
),
|
|
58
|
+
(release) => Effect.promise(() => release().catch(() => {})),
|
|
118
59
|
);
|
|
60
|
+
return yield* Effect.never;
|
|
61
|
+
}).pipe(Effect.forkScoped);
|
|
119
62
|
|
|
120
|
-
|
|
121
|
-
Effect.flatMap((pid) =>
|
|
122
|
-
pid === process.pid
|
|
123
|
-
? Effect.void
|
|
124
|
-
: Effect.fail(
|
|
125
|
-
new LockError({
|
|
126
|
-
reason: "Conflict",
|
|
127
|
-
message: "Lock not held by this process",
|
|
128
|
-
}),
|
|
129
|
-
),
|
|
130
|
-
),
|
|
131
|
-
);
|
|
132
|
-
|
|
133
|
-
const removeLock = fs.remove(paths.lock, { force: true }).pipe(
|
|
134
|
-
Effect.mapError(
|
|
135
|
-
(e) =>
|
|
136
|
-
new LockError({
|
|
137
|
-
reason: "PlatformError",
|
|
138
|
-
message: "Failed to remove lock file",
|
|
139
|
-
cause: e,
|
|
140
|
-
}),
|
|
141
|
-
),
|
|
142
|
-
);
|
|
143
|
-
|
|
144
|
-
const releaseLock = assertOwnLock.pipe(Effect.andThen(() => removeLock));
|
|
145
|
-
|
|
146
|
-
const acquireLock = makeLockFile.pipe(
|
|
147
|
-
Effect.catchIf(
|
|
148
|
-
(e) => e.reason === "Conflict",
|
|
149
|
-
(e) =>
|
|
150
|
-
isLockValid.pipe(
|
|
151
|
-
Effect.flatMap((valid) =>
|
|
152
|
-
valid
|
|
153
|
-
? Effect.fail(e)
|
|
154
|
-
: removeLock.pipe(Effect.andThen(() => makeLockFile)),
|
|
155
|
-
),
|
|
156
|
-
),
|
|
157
|
-
),
|
|
158
|
-
);
|
|
159
|
-
|
|
160
|
-
const touchLock = assertOwnLock.pipe(
|
|
161
|
-
Effect.flatMap(() => {
|
|
162
|
-
const now = Date.now();
|
|
163
|
-
return fs.utimes(paths.lock, now, now).pipe(
|
|
164
|
-
Effect.mapError(
|
|
165
|
-
(e) =>
|
|
166
|
-
new LockError({
|
|
167
|
-
reason: "PlatformError",
|
|
168
|
-
message: "Failed to update lock file",
|
|
169
|
-
cause: e,
|
|
170
|
-
}),
|
|
171
|
-
),
|
|
172
|
-
);
|
|
173
|
-
}),
|
|
174
|
-
);
|
|
175
|
-
|
|
176
|
-
return Lock.of({
|
|
177
|
-
check: isLockValid,
|
|
178
|
-
acquire: acquireLock.pipe(
|
|
179
|
-
Effect.flatMap(() =>
|
|
180
|
-
touchLock.pipe(
|
|
181
|
-
Effect.repeat(Schedule.spaced(TOUCH_INTERVAL)),
|
|
182
|
-
Effect.ensuring(releaseLock.pipe(Effect.ignore)),
|
|
183
|
-
Effect.forkScoped,
|
|
184
|
-
),
|
|
185
|
-
),
|
|
186
|
-
),
|
|
187
|
-
});
|
|
63
|
+
return Lock.of({ check, acquire });
|
|
188
64
|
});
|
|
189
65
|
|
|
190
66
|
export const LockLive = Layer.effect(Lock, make);
|
|
@@ -2,14 +2,18 @@ import type { RpcCompatible } from "capnweb";
|
|
|
2
2
|
import * as Cause from "effect/Cause";
|
|
3
3
|
import * as Effect from "effect/Effect";
|
|
4
4
|
import * as Exit from "effect/Exit";
|
|
5
|
+
import { flow } from "effect/Function";
|
|
6
|
+
import * as Redacted from "effect/Redacted";
|
|
5
7
|
import * as Schema from "effect/Schema";
|
|
6
8
|
|
|
7
9
|
type RpcHandler<Args extends Array<any>, Success, Error> = (
|
|
8
10
|
...args: Args
|
|
9
11
|
) => Effect.Effect<Success, Error>;
|
|
10
12
|
|
|
11
|
-
type
|
|
12
|
-
|
|
13
|
+
type SerializedArgs<T> = string & { __serializedArgs: T };
|
|
14
|
+
|
|
15
|
+
type SerializedRpcHandler<Args, Success, Error> = (
|
|
16
|
+
args: SerializedArgs<Args>,
|
|
13
17
|
) => Promise<SerializedExit<RpcCompatible<Success>, RpcCompatible<Error>>>;
|
|
14
18
|
|
|
15
19
|
type SerializedCause<Error> =
|
|
@@ -60,45 +64,59 @@ export type RpcHandlerDecoders<T extends RpcHandlers> = {
|
|
|
60
64
|
: never;
|
|
61
65
|
};
|
|
62
66
|
|
|
63
|
-
const serializeRpcHandler =
|
|
64
|
-
<Args
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
_tag: "Failure",
|
|
80
|
-
cause: exit.cause.reasons.map((reason): SerializedCause<Error> => {
|
|
81
|
-
switch (reason._tag) {
|
|
82
|
-
case "Fail":
|
|
83
|
-
return {
|
|
84
|
-
_tag: "Fail",
|
|
85
|
-
error: Schema.encodeSync(schema.error)(reason.error),
|
|
86
|
-
};
|
|
87
|
-
case "Die":
|
|
88
|
-
return {
|
|
89
|
-
_tag: "Die",
|
|
90
|
-
defect: Schema.encodeSync(Schema.DefectWithStack)(
|
|
91
|
-
reason.defect,
|
|
92
|
-
),
|
|
93
|
-
};
|
|
94
|
-
case "Interrupt":
|
|
95
|
-
return { _tag: "Interrupt", fiberId: reason.fiberId };
|
|
67
|
+
const serializeRpcHandler = <Args extends Array<any>, Success, Error>(
|
|
68
|
+
handler: RpcHandler<Args, Success, Error>,
|
|
69
|
+
schema: RpcHandlerEncoder<Success, Error>,
|
|
70
|
+
): SerializedRpcHandler<Args, Success, Error> =>
|
|
71
|
+
flow(
|
|
72
|
+
(args) =>
|
|
73
|
+
Effect.sync(
|
|
74
|
+
() =>
|
|
75
|
+
JSON.parse(args, (_, value) => {
|
|
76
|
+
if (
|
|
77
|
+
typeof value === "object" &&
|
|
78
|
+
value !== null &&
|
|
79
|
+
"_tag" in value &&
|
|
80
|
+
value._tag === "Redacted"
|
|
81
|
+
) {
|
|
82
|
+
return Redacted.make(value.value);
|
|
96
83
|
}
|
|
97
|
-
|
|
84
|
+
return value;
|
|
85
|
+
}) as Args,
|
|
86
|
+
),
|
|
87
|
+
Effect.flatMap((args) => handler(...args)),
|
|
88
|
+
Effect.exit,
|
|
89
|
+
Effect.map((exit): SerializedExit<Success, Error> => {
|
|
90
|
+
if (exit._tag === "Success") {
|
|
91
|
+
return {
|
|
92
|
+
_tag: "Success",
|
|
93
|
+
value: Schema.encodeSync(schema.success)(exit.value),
|
|
98
94
|
} as const;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
_tag: "Failure",
|
|
98
|
+
cause: exit.cause.reasons.map((reason): SerializedCause<Error> => {
|
|
99
|
+
switch (reason._tag) {
|
|
100
|
+
case "Fail":
|
|
101
|
+
return {
|
|
102
|
+
_tag: "Fail",
|
|
103
|
+
error: Schema.encodeSync(schema.error)(reason.error),
|
|
104
|
+
};
|
|
105
|
+
case "Die":
|
|
106
|
+
return {
|
|
107
|
+
_tag: "Die",
|
|
108
|
+
defect: Schema.encodeSync(Schema.DefectWithStack)(
|
|
109
|
+
reason.defect,
|
|
110
|
+
),
|
|
111
|
+
};
|
|
112
|
+
case "Interrupt":
|
|
113
|
+
return { _tag: "Interrupt", fiberId: reason.fiberId };
|
|
114
|
+
}
|
|
115
|
+
}),
|
|
116
|
+
} as const;
|
|
117
|
+
}),
|
|
118
|
+
Effect.runPromise,
|
|
119
|
+
);
|
|
102
120
|
|
|
103
121
|
export const serializeRpcHandlers = <T extends RpcHandlers>(
|
|
104
122
|
handlers: T,
|
|
@@ -119,7 +137,16 @@ const deserializeRpcHandler =
|
|
|
119
137
|
(...args) =>
|
|
120
138
|
Effect.promise(async () => {
|
|
121
139
|
try {
|
|
122
|
-
|
|
140
|
+
const serializedArgs = JSON.stringify(args, (_, value) => {
|
|
141
|
+
if (Redacted.isRedacted(value)) {
|
|
142
|
+
return {
|
|
143
|
+
_tag: "Redacted",
|
|
144
|
+
value: Redacted.value(value),
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
return value;
|
|
148
|
+
}) as SerializedArgs<Args>;
|
|
149
|
+
return await handler(serializedArgs);
|
|
123
150
|
} catch (error) {
|
|
124
151
|
console.error("Error calling handler", error);
|
|
125
152
|
throw error;
|
|
@@ -9,7 +9,6 @@ import type { HttpServer } from "effect/unstable/http/HttpServer";
|
|
|
9
9
|
import type { ServeError } from "effect/unstable/http/HttpServerError";
|
|
10
10
|
import type { ChildProcessSpawner } from "effect/unstable/process/ChildProcessSpawner";
|
|
11
11
|
import type { WebSocketConstructor } from "effect/unstable/socket/Socket";
|
|
12
|
-
import { installLocalhostDns } from "./LocalhostDns.ts";
|
|
13
12
|
|
|
14
13
|
const isBun = typeof Bun !== "undefined";
|
|
15
14
|
|
|
@@ -53,7 +52,6 @@ export const runMain = <E, A>(
|
|
|
53
52
|
readonly teardown?: Teardown | undefined;
|
|
54
53
|
},
|
|
55
54
|
): void => {
|
|
56
|
-
installLocalhostDns();
|
|
57
55
|
if (isBun) {
|
|
58
56
|
void import("@effect/platform-bun/BunRuntime").then((BunRuntime) =>
|
|
59
57
|
BunRuntime.runMain(effect, options),
|