@alchemy.run/pkg 2.0.0-beta.80 → 2.0.0-beta.82

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.
Files changed (53) hide show
  1. package/dist/Manifest.d.ts.map +1 -1
  2. package/dist/Manifest.js.map +1 -1
  3. package/dist/Protocol.d.ts +1 -1
  4. package/dist/Protocol.d.ts.map +1 -1
  5. package/dist/Protocol.js +3 -12
  6. package/dist/Protocol.js.map +1 -1
  7. package/dist/Registry/Bindings.d.ts.map +1 -1
  8. package/dist/Registry/Bindings.js.map +1 -1
  9. package/dist/Registry/GitHub.d.ts +2 -2
  10. package/dist/Registry/GitHub.d.ts.map +1 -1
  11. package/dist/Registry/GitHub.js +10 -37
  12. package/dist/Registry/GitHub.js.map +1 -1
  13. package/dist/Registry/Handler.d.ts +1 -1
  14. package/dist/Registry/Handler.d.ts.map +1 -1
  15. package/dist/Registry/Handler.js +25 -33
  16. package/dist/Registry/Handler.js.map +1 -1
  17. package/dist/Registry/KVCache.d.ts.map +1 -1
  18. package/dist/Registry/KVCache.js +1 -1
  19. package/dist/Registry/KVCache.js.map +1 -1
  20. package/dist/Registry/Sweep.d.ts.map +1 -1
  21. package/dist/Registry/Sweep.js +3 -8
  22. package/dist/Registry/Sweep.js.map +1 -1
  23. package/dist/Registry/Tags.d.ts.map +1 -1
  24. package/dist/Registry/Tags.js +1 -1
  25. package/dist/Registry/Tags.js.map +1 -1
  26. package/dist/Registry.d.ts +23 -1
  27. package/dist/Registry.d.ts.map +1 -1
  28. package/dist/Registry.js +1 -1
  29. package/dist/Registry.js.map +1 -1
  30. package/dist/cli/bin.d.ts +1 -1
  31. package/dist/cli/bin.d.ts.map +1 -1
  32. package/dist/cli/bin.js +6 -10
  33. package/dist/cli/bin.js.map +1 -1
  34. package/dist/cli/pack.d.ts +11 -0
  35. package/dist/cli/pack.d.ts.map +1 -1
  36. package/dist/cli/pack.js +97 -24
  37. package/dist/cli/pack.js.map +1 -1
  38. package/dist/cli/publish.d.ts.map +1 -1
  39. package/dist/cli/publish.js +2 -2
  40. package/dist/cli/publish.js.map +1 -1
  41. package/package.json +26 -25
  42. package/src/Manifest.ts +1 -3
  43. package/src/Protocol.ts +12 -28
  44. package/src/Registry/Bindings.ts +1 -4
  45. package/src/Registry/GitHub.ts +25 -84
  46. package/src/Registry/Handler.ts +44 -100
  47. package/src/Registry/KVCache.ts +6 -17
  48. package/src/Registry/Sweep.ts +13 -26
  49. package/src/Registry/Tags.ts +8 -16
  50. package/src/Registry.ts +5 -15
  51. package/src/cli/bin.ts +9 -19
  52. package/src/cli/pack.ts +126 -74
  53. package/src/cli/publish.ts +11 -23
@@ -5,17 +5,17 @@ import * as Clock from "effect/Clock";
5
5
  import * as Duration from "effect/Duration";
6
6
  import * as Effect from "effect/Effect";
7
7
  import { Hex } from "effect/encoding";
8
- import * as Layer from "effect/Layer";
9
- import * as Path from "effect/Path";
10
- import * as Result from "effect/Result";
11
- import * as Schema from "effect/Schema";
8
+ import * as HttpApiBuilder from "effect/http-api/HttpApiBuilder";
12
9
  import * as Etag from "effect/http/Etag";
13
10
  import * as FetchHttpClient from "effect/http/FetchHttpClient";
14
11
  import * as HttpPlatform from "effect/http/HttpPlatform";
15
12
  import * as HttpRouter from "effect/http/HttpRouter";
16
13
  import { HttpServerRequest } from "effect/http/HttpServerRequest";
17
14
  import * as HttpServerResponse from "effect/http/HttpServerResponse";
18
- import * as HttpApiBuilder from "effect/http-api/HttpApiBuilder";
15
+ import * as Layer from "effect/Layer";
16
+ import * as Path from "effect/Path";
17
+ import * as Result from "effect/Result";
18
+ import * as Schema from "effect/Schema";
19
19
  import { ManifestJson, type ManifestPackage } from "../Manifest.ts";
20
20
  import {
21
21
  BadRequest,
@@ -28,14 +28,7 @@ import {
28
28
  RunRef,
29
29
  Upstream,
30
30
  } from "../Protocol.ts";
31
- import {
32
- Bucket,
33
- Cache,
34
- COMMENT_MARKER,
35
- Database,
36
- RegistryConfig,
37
- tarballKey,
38
- } from "./Bindings.ts";
31
+ import { Bucket, Cache, COMMENT_MARKER, Database, RegistryConfig, tarballKey } from "./Bindings.ts";
39
32
  import * as GitHub from "./GitHub.ts";
40
33
  import * as KVCache from "./KVCache.ts";
41
34
  import { sweep } from "./Sweep.ts";
@@ -98,12 +91,10 @@ export const tagsFor = (run: Run): string[] => {
98
91
 
99
92
  /** The tag install commands are written against. */
100
93
  export const installTag = (run: Run) =>
101
- run.pr !== null
102
- ? `pr:${run.pr}:${run.headSha.slice(0, SHORT)}`
103
- : run.headSha.slice(0, SHORT);
94
+ run.pr !== null ? `pr:${run.pr}:${run.headSha.slice(0, SHORT)}` : run.headSha.slice(0, SHORT);
104
95
 
105
96
  const upstream = (e: { readonly _tag: string; readonly message?: string }) =>
106
- new Upstream({ message: GitHub.describe(e) });
97
+ Upstream.make({ message: GitHub.describe(e) });
107
98
 
108
99
  /**
109
100
  * Storage failures are not part of the protocol: they are defects, which
@@ -121,19 +112,17 @@ const lookupRun = Effect.fn("lookupRun")(function* (ref: RunRef) {
121
112
  const github = yield* GitHub.GitHubApp;
122
113
 
123
114
  if (!policy.repos.includes(ref.repo)) {
124
- return yield* new Forbidden({ message: `${ref.repo} may not publish` });
115
+ return yield* Forbidden.make({ message: `${ref.repo} may not publish` });
125
116
  }
126
- const data = yield* github
127
- .getRun(ref.repo, ref.runId)
128
- .pipe(Effect.mapError(upstream));
117
+ const data = yield* github.getRun(ref.repo, ref.runId).pipe(Effect.mapError(upstream));
129
118
  if (
130
119
  data.status !== "in_progress" ||
131
120
  (data.run_attempt !== undefined && data.run_attempt !== ref.attempt)
132
121
  ) {
133
- return yield* new RunNotInProgress({ message: "run is not in progress" });
122
+ return yield* RunNotInProgress.make({ message: "run is not in progress" });
134
123
  }
135
124
  if (data.event !== "push" && data.event !== "pull_request") {
136
- return yield* new BadRequest({
125
+ return yield* BadRequest.make({
137
126
  message: `unsupported event ${data.event}`,
138
127
  });
139
128
  }
@@ -141,7 +130,7 @@ const lookupRun = Effect.fn("lookupRun")(function* (ref: RunRef) {
141
130
  let pr: number | null = null;
142
131
  if (data.event === "pull_request") {
143
132
  if (data.head_branch === null) {
144
- return yield* new BadRequest({
133
+ return yield* BadRequest.make({
145
134
  message: `pull request run ${ref.runId} has no head branch`,
146
135
  });
147
136
  }
@@ -151,22 +140,17 @@ const lookupRun = Effect.fn("lookupRun")(function* (ref: RunRef) {
151
140
  // repository already published; matching the head repository keeps a
152
141
  // run's publication on its own pull request.
153
142
  const pulls = yield* github
154
- .pullRequestsForCommit(
155
- ref.repo,
156
- headRepo,
157
- data.head_branch,
158
- data.head_sha,
159
- )
143
+ .pullRequestsForCommit(ref.repo, headRepo, data.head_branch, data.head_sha)
160
144
  .pipe(Effect.mapError(upstream));
161
145
  const first = pulls[0];
162
146
  if (first === undefined) {
163
- return yield* new BadRequest({
147
+ return yield* BadRequest.make({
164
148
  message: `no open pull request from ${headRepo} has head ${data.head_sha}`,
165
149
  });
166
150
  }
167
151
  pr = first.number;
168
152
  } else if (headRepo !== ref.repo) {
169
- return yield* new BadRequest({
153
+ return yield* BadRequest.make({
170
154
  message: `push run ${ref.runId} is for ${headRepo}, not ${ref.repo}`,
171
155
  });
172
156
  }
@@ -185,56 +169,44 @@ const lookupRun = Effect.fn("lookupRun")(function* (ref: RunRef) {
185
169
  * run, and GitHub reports the run's artifacts to the App, so a matching
186
170
  * name means this run vouched for exactly this manifest text.
187
171
  */
188
- const requireVouched = Effect.fn("requireVouched")(function* (
189
- run: Run,
190
- manifestText: string,
191
- ) {
172
+ const requireVouched = Effect.fn("requireVouched")(function* (run: Run, manifestText: string) {
192
173
  const github = yield* GitHub.GitHubApp;
193
174
  const expected = manifestArtifactName(yield* sha256(manifestText));
194
175
  const artifacts = yield* github
195
176
  .listRunArtifacts(run.repo, run.runId, expected)
196
177
  .pipe(Effect.mapError(upstream));
197
178
  if (!artifacts.some((a) => a.name === expected && !a.expired)) {
198
- return yield* new Forbidden({
179
+ return yield* Forbidden.make({
199
180
  message: `run ${run.runId} has not vouched for this manifest (no artifact ${expected})`,
200
181
  });
201
182
  }
202
183
  });
203
184
 
204
- const publish = Effect.fn("publish")(function* (
205
- run: Run,
206
- manifestText: string,
207
- ) {
185
+ const publish = Effect.fn("publish")(function* (run: Run, manifestText: string) {
208
186
  const { policy } = yield* RegistryConfig;
209
187
  const r2 = yield* Cloudflare.R2.ReadWriteBucket(Bucket);
210
188
  const github = yield* GitHub.GitHubApp;
211
189
  const base = yield* origin;
212
190
 
213
191
  yield* requireVouched(run, manifestText);
214
- const manifest = yield* Schema.decodeUnknownEffect(ManifestJson)(
215
- manifestText,
216
- ).pipe(
217
- Effect.mapError(
218
- (e) => new BadRequest({ message: `invalid manifest: ${String(e)}` }),
219
- ),
192
+ const manifest = yield* Schema.decodeUnknownEffect(ManifestJson)(manifestText).pipe(
193
+ Effect.mapError((e) => BadRequest.make({ message: `invalid manifest: ${String(e)}` })),
220
194
  );
221
195
  // The run vouched for the manifest, but the tags come from the run's
222
196
  // head, so the packed checkout has to be that commit: a `pull_request`
223
197
  // job that packs the synthetic merge commit would otherwise publish bytes
224
198
  // built from a commit that exists nowhere under the commit's name.
225
199
  if (manifest.head !== run.headSha) {
226
- return yield* new BadRequest({
200
+ return yield* BadRequest.make({
227
201
  message: `manifest was packed at ${manifest.head}, but the run's head is ${run.headSha}`,
228
202
  });
229
203
  }
230
204
  const packages = manifest.packages;
231
205
  const maxSize = policy.maxPackageSize;
232
206
  const tooLarge =
233
- maxSize === undefined
234
- ? undefined
235
- : packages.find((pkg) => BigInt(pkg.size) > maxSize);
207
+ maxSize === undefined ? undefined : packages.find((pkg) => BigInt(pkg.size) > maxSize);
236
208
  if (tooLarge !== undefined) {
237
- return yield* new PackageTooLarge({
209
+ return yield* PackageTooLarge.make({
238
210
  message: `${tooLarge.name} exceeds ${maxSize} bytes`,
239
211
  });
240
212
  }
@@ -242,17 +214,11 @@ const publish = Effect.fn("publish")(function* (
242
214
  const missing = yield* Effect.filter(
243
215
  packages,
244
216
  (pkg) =>
245
- r2
246
- .head(tarballKey(pkg.name, pkg.sha256))
247
- .pipe(Effect.map((object) => object === null)),
217
+ r2.head(tarballKey(pkg.name, pkg.sha256)).pipe(Effect.map((object) => object === null)),
248
218
  { concurrency: 8 },
249
- ).pipe(
250
- Effect.map((packages) =>
251
- packages.map(({ name, sha256 }) => ({ name, sha256 })),
252
- ),
253
- );
219
+ ).pipe(Effect.map((packages) => packages.map(({ name, sha256 }) => ({ name, sha256 }))));
254
220
  if (missing.length > 0) {
255
- return yield* new MissingTarballs({ missing });
221
+ return yield* MissingTarballs.make({ missing });
256
222
  }
257
223
 
258
224
  const now = yield* Clock.currentTimeMillis;
@@ -297,9 +263,7 @@ const publish = Effect.fn("publish")(function* (
297
263
  })
298
264
  .pipe(
299
265
  Effect.catch((e) =>
300
- Effect.logWarning(
301
- `check run on ${run.repo}@${run.headSha} failed: ${GitHub.describe(e)}`,
302
- ),
266
+ Effect.logWarning(`check run on ${run.repo}@${run.headSha} failed: ${GitHub.describe(e)}`),
303
267
  ),
304
268
  );
305
269
  if (run.pr !== null) {
@@ -311,9 +275,7 @@ const publish = Effect.fn("publish")(function* (
311
275
  .upsertComment(run.repo, run.pr, COMMENT_MARKER, body)
312
276
  .pipe(
313
277
  Effect.catch((e) =>
314
- Effect.logWarning(
315
- `comment on ${run.repo}#${run.pr} failed: ${GitHub.describe(e)}`,
316
- ),
278
+ Effect.logWarning(`comment on ${run.repo}#${run.pr} failed: ${GitHub.describe(e)}`),
317
279
  ),
318
280
  );
319
281
  }
@@ -334,11 +296,11 @@ const uploadTarball = Effect.fn("uploadTarball")(function* (
334
296
 
335
297
  const contentLength = Number(request.headers["content-length"] ?? 0);
336
298
  if (!Number.isSafeInteger(contentLength) || contentLength <= 0) {
337
- return yield* new BadRequest({ message: "Content-Length is required" });
299
+ return yield* BadRequest.make({ message: "Content-Length is required" });
338
300
  }
339
301
  const maxSize = policy.maxPackageSize;
340
302
  if (maxSize !== undefined && BigInt(contentLength) > maxSize) {
341
- return yield* new PackageTooLarge({
303
+ return yield* PackageTooLarge.make({
342
304
  message: `tarball exceeds ${maxSize} bytes`,
343
305
  });
344
306
  }
@@ -353,11 +315,7 @@ const uploadTarball = Effect.fn("uploadTarball")(function* (
353
315
  // The params schema already constrained this to 64 hex chars.
354
316
  sha256: Result.getOrThrow(Hex.decode(sha256)),
355
317
  })
356
- .pipe(
357
- Effect.mapError(
358
- (e) => new BadRequest({ message: `upload rejected: ${String(e)}` }),
359
- ),
360
- );
318
+ .pipe(Effect.mapError((e) => BadRequest.make({ message: `upload rejected: ${String(e)}` })));
361
319
  return { name, sha256, size: contentLength, uploaded: true };
362
320
  });
363
321
 
@@ -380,9 +338,7 @@ export const parseInstallPath = (
380
338
  // Malformed percent-encoding is a path nothing can be published under.
381
339
  return undefined;
382
340
  }
383
- const tarball = path.match(
384
- /^(@[^/]+\/[^/@]+|[^/@]+)\/-\/([a-f0-9]{64})\.tgz$/,
385
- );
341
+ const tarball = path.match(/^(@[^/]+\/[^/@]+|[^/@]+)\/-\/([a-f0-9]{64})\.tgz$/);
386
342
  if (tarball) {
387
343
  return {
388
344
  kind: "tarball",
@@ -403,8 +359,7 @@ export const parseInstallPath = (
403
359
  const qualify = (name: string, scope: string | undefined) =>
404
360
  scope !== undefined && !name.startsWith("@") ? `${scope}/${name}` : name;
405
361
 
406
- const notFound = (message: string) =>
407
- HttpServerResponse.json({ error: message }, { status: 404 });
362
+ const notFound = (message: string) => HttpServerResponse.json({ error: message }, { status: 404 });
408
363
 
409
364
  /**
410
365
  * Install URLs: `/<name>/<tag>` redirects to the immutable tarball URL,
@@ -417,16 +372,10 @@ const install = Effect.gen(function* () {
417
372
  const r2 = yield* Cloudflare.R2.ReadWriteBucket(Bucket);
418
373
 
419
374
  if (request.method !== "GET" && request.method !== "HEAD") {
420
- return yield* HttpServerResponse.json(
421
- { error: "method not allowed" },
422
- { status: 405 },
423
- );
375
+ return yield* HttpServerResponse.json({ error: "method not allowed" }, { status: 405 });
424
376
  }
425
377
  const host = request.headers.host ?? "localhost";
426
- const target = parseInstallPath(
427
- new URL(request.url, `https://${host}`).pathname,
428
- aliases[host],
429
- );
378
+ const target = parseInstallPath(new URL(request.url, `https://${host}`).pathname, aliases[host]);
430
379
  if (target === undefined) {
431
380
  return yield* notFound("not found");
432
381
  }
@@ -490,9 +439,7 @@ export const handler = Effect.gen(function* () {
490
439
 
491
440
  // Built once per isolate. The D1 client memoizes its connection on each
492
441
  // event's scope, so its layer is safe to share.
493
- const github = yield* GitHub.GitHubApp.pipe(
494
- Effect.provide(GitHub.GitHubAppLive),
495
- );
442
+ const github = yield* GitHub.GitHubApp.pipe(Effect.provide(GitHub.GitHubAppLive));
496
443
  const services = Layer.mergeAll(
497
444
  Cloudflare.R2.ReadWriteBucketBinding,
498
445
  Cloudflare.KV.ReadWriteNamespaceBinding,
@@ -509,23 +456,20 @@ export const handler = Effect.gen(function* () {
509
456
  (ref) => Effect.map(lookupRun(ref), (value) => ({ value, ttl: RUN_CACHE })),
510
457
  );
511
458
 
512
- yield* Cloudflare.Workers.cron(config.cron, () =>
513
- sweep.pipe(Effect.provide(services)),
514
- );
459
+ yield* Cloudflare.Workers.cron(config.cron, () => sweep.pipe(Effect.provide(services)));
515
460
 
516
461
  const registry = HttpApiBuilder.group(PkgApi, "Registry", (handlers) =>
517
462
  handlers
518
463
  .handle("publish", ({ payload }) =>
519
- Effect.flatMap(runs(payload.run), (run) =>
520
- publish(run, payload.manifest),
521
- ).pipe(Effect.catchTag(storageFailures, (e) => Effect.die(e))),
464
+ Effect.flatMap(runs(payload.run), (run) => publish(run, payload.manifest)).pipe(
465
+ Effect.catchTag(storageFailures, (e) => Effect.die(e)),
466
+ ),
522
467
  )
523
468
  // Raw so the body streams into R2 instead of being buffered.
524
469
  .handleRaw("uploadTarball", ({ query, params, request }) =>
525
- Effect.andThen(
526
- runs(query),
527
- uploadTarball(request, params.name, params.sha256),
528
- ).pipe(Effect.catchTag("R2Error", (e) => Effect.die(e))),
470
+ Effect.andThen(runs(query), uploadTarball(request, params.name, params.sha256)).pipe(
471
+ Effect.catchTag("R2Error", (e) => Effect.die(e)),
472
+ ),
529
473
  ),
530
474
  );
531
475
 
@@ -7,8 +7,7 @@ import { Cache } from "./Bindings.ts";
7
7
  /** KV's floor for both `expirationTtl` and `cacheTtl`. */
8
8
  const MIN_TTL = Duration.minutes(1);
9
9
 
10
- const seconds = (duration: Duration.Duration) =>
11
- Math.floor(Duration.toSeconds(duration));
10
+ const seconds = (duration: Duration.Duration) => Math.floor(Duration.toSeconds(duration));
12
11
 
13
12
  /**
14
13
  * A lookup-through cache on the registry's KV namespace, shared by every
@@ -28,9 +27,7 @@ const seconds = (duration: Duration.Duration) =>
28
27
  export const make = <K, A, I, E, R>(
29
28
  key: (input: K) => string,
30
29
  value: Schema.Codec<A, I>,
31
- lookup: (
32
- input: K,
33
- ) => Effect.Effect<{ value: A; ttl: Duration.Duration }, E, R>,
30
+ lookup: (input: K) => Effect.Effect<{ value: A; ttl: Duration.Duration }, E, R>,
34
31
  ) => {
35
32
  const json = Schema.fromJsonString(value);
36
33
  const decode = Schema.decodeUnknownEffect(json);
@@ -40,13 +37,9 @@ export const make = <K, A, I, E, R>(
40
37
  const kv = yield* Cloudflare.KV.ReadWriteNamespace(Cache);
41
38
  const id = key(input);
42
39
  const hit = yield* kv.get(id, { cacheTtl: seconds(MIN_TTL) }).pipe(
43
- Effect.flatMap((text) =>
44
- text === null ? Effect.succeed(undefined) : decode(text),
45
- ),
40
+ Effect.flatMap((text) => (text === null ? Effect.succeed(undefined) : decode(text))),
46
41
  Effect.catch((e) =>
47
- Effect.logWarning(`cache read of ${id} failed: ${e}`).pipe(
48
- Effect.as(undefined),
49
- ),
42
+ Effect.logWarning(`cache read of ${id} failed: ${e}`).pipe(Effect.as(undefined)),
50
43
  ),
51
44
  );
52
45
  if (hit !== undefined) {
@@ -55,12 +48,8 @@ export const make = <K, A, I, E, R>(
55
48
  const fresh = yield* lookup(input);
56
49
  if (Duration.isGreaterThanOrEqualTo(fresh.ttl, MIN_TTL)) {
57
50
  yield* encode(fresh.value).pipe(
58
- Effect.flatMap((text) =>
59
- kv.put(id, text, { expirationTtl: seconds(fresh.ttl) }),
60
- ),
61
- Effect.catch((e) =>
62
- Effect.logWarning(`cache write of ${id} failed: ${e}`),
63
- ),
51
+ Effect.flatMap((text) => kv.put(id, text, { expirationTtl: seconds(fresh.ttl) })),
52
+ Effect.catch((e) => Effect.logWarning(`cache write of ${id} failed: ${e}`)),
64
53
  );
65
54
  }
66
55
  return fresh.value;
@@ -4,13 +4,7 @@ import * as Duration from "effect/Duration";
4
4
  import * as Effect from "effect/Effect";
5
5
  import * as Option from "effect/Option";
6
6
  import * as Stream from "effect/Stream";
7
- import {
8
- Bucket,
9
- ORPHAN_GRACE,
10
- RegistryConfig,
11
- SWEEP_LOOKAHEAD,
12
- tarballKey,
13
- } from "./Bindings.ts";
7
+ import { Bucket, ORPHAN_GRACE, RegistryConfig, SWEEP_LOOKAHEAD, tarballKey } from "./Bindings.ts";
14
8
  import * as GitHub from "./GitHub.ts";
15
9
  import * as Tags from "./Tags.ts";
16
10
 
@@ -37,15 +31,13 @@ export const sweep = Effect.gen(function* () {
37
31
  const [repo, number] = ref.split("#");
38
32
  return github.getPullRequest(repo!, Number(number)).pipe(
39
33
  Effect.map((pr) =>
40
- pr.state === "open"
41
- ? undefined
42
- : Date.parse(pr.merged_at ?? pr.closed_at ?? "") || now,
34
+ pr.state === "open" ? undefined : Date.parse(pr.merged_at ?? pr.closed_at ?? "") || now,
43
35
  ),
44
36
  Effect.map((closedAt) => [ref, { closedAt }] as const),
45
37
  Effect.catch((e) =>
46
- Effect.logWarning(
47
- `pull request ${ref} lookup failed: ${GitHub.describe(e)}`,
48
- ).pipe(Effect.as([ref, undefined] as const)),
38
+ Effect.logWarning(`pull request ${ref} lookup failed: ${GitHub.describe(e)}`).pipe(
39
+ Effect.as([ref, undefined] as const),
40
+ ),
49
41
  ),
50
42
  );
51
43
  },
@@ -85,20 +77,15 @@ export const sweep = Effect.gen(function* () {
85
77
  );
86
78
 
87
79
  // Uploads that never got tagged.
88
- const orphans = yield* Stream.paginate(
89
- undefined as string | undefined,
90
- (cursor) =>
91
- r2
92
- .list({ cursor, limit: 500 })
93
- .pipe(
94
- Effect.map(
95
- (page) =>
96
- [
97
- page.objects,
98
- page.truncated ? Option.some(page.cursor) : Option.none(),
99
- ] as const,
100
- ),
80
+ const orphans = yield* Stream.paginate(undefined as string | undefined, (cursor) =>
81
+ r2
82
+ .list({ cursor, limit: 500 })
83
+ .pipe(
84
+ Effect.map(
85
+ (page) =>
86
+ [page.objects, page.truncated ? Option.some(page.cursor) : Option.none()] as const,
101
87
  ),
88
+ ),
102
89
  ).pipe(
103
90
  Stream.filter((object) => {
104
91
  const match = object.key.match(/^(.+)\/([a-f0-9]{64})\.tgz$/);
@@ -9,7 +9,7 @@ import * as SqlClient from "effect/sql/SqlClient";
9
9
  * D1 binding.
10
10
  */
11
11
 
12
- const LinkedPrs = Schema.fromJsonString(Schema.Array(Schema.String));
12
+ const LinkedPrs = Schema.String.pipe(Schema.Array, Schema.fromJsonString);
13
13
 
14
14
  export const TagRow = Schema.Struct({
15
15
  package: Schema.String,
@@ -71,9 +71,7 @@ export const resolve = (pkg: string, tag: string) =>
71
71
  Effect.map(get(pkg, tag), (row) => row?.sha256);
72
72
 
73
73
  /** Rows tied to pull requests whose expiry falls before `before`. */
74
- export const dueLinked = Effect.fn("Tags.dueLinked")(function* (
75
- before: number,
76
- ) {
74
+ export const dueLinked = Effect.fn("Tags.dueLinked")(function* (before: number) {
77
75
  const sql = yield* SqlClient.SqlClient;
78
76
  return yield* decodeTags(
79
77
  yield* sql`
@@ -94,9 +92,7 @@ export const setExpiry = Effect.fn("Tags.setExpiry")(function* (
94
92
  });
95
93
 
96
94
  /** Delete every expired row and return the tarballs those rows pointed at. */
97
- export const deleteExpired = Effect.fn("Tags.deleteExpired")(function* (
98
- now: number,
99
- ) {
95
+ export const deleteExpired = Effect.fn("Tags.deleteExpired")(function* (now: number) {
100
96
  const sql = yield* SqlClient.SqlClient;
101
97
  return yield* decodeTarballs(
102
98
  yield* sql`
@@ -106,12 +102,8 @@ export const deleteExpired = Effect.fn("Tags.deleteExpired")(function* (
106
102
  });
107
103
 
108
104
  /** Every tarball some tag still points at, as `<package>/<sha256>`. */
109
- export const referencedTarballs = Effect.fn("Tags.referencedTarballs")(
110
- function* () {
111
- const sql = yield* SqlClient.SqlClient;
112
- const rows = yield* decodeTarballs(
113
- yield* sql`SELECT DISTINCT package, sha256 FROM tags`,
114
- );
115
- return new Set(rows.map((row) => `${row.package}/${row.sha256}`));
116
- },
117
- );
105
+ export const referencedTarballs = Effect.fn("Tags.referencedTarballs")(function* () {
106
+ const sql = yield* SqlClient.SqlClient;
107
+ const rows = yield* decodeTarballs(yield* sql`SELECT DISTINCT package, sha256 FROM tags`);
108
+ return new Set(rows.map((row) => `${row.package}/${row.sha256}`));
109
+ });
package/src/Registry.ts CHANGED
@@ -20,14 +20,7 @@ import {
20
20
  } from "./Registry/Bindings.ts";
21
21
  import { handler } from "./Registry/Handler.ts";
22
22
 
23
- export {
24
- Bucket,
25
- Cache,
26
- Database,
27
- Policy,
28
- RegistryConfig,
29
- Settings,
30
- } from "./Registry/Bindings.ts";
23
+ export { Bucket, Cache, Database, Policy, RegistryConfig, Settings } from "./Registry/Bindings.ts";
31
24
 
32
25
  export interface RegistryOptions {
33
26
  /**
@@ -99,9 +92,9 @@ const RegistryConfigFromEnv = Layer.effect(
99
92
  RegistryConfig,
100
93
  Effect.gen(function* () {
101
94
  const env = yield* Cloudflare.Workers.WorkerEnvironment;
102
- const settings = yield* Schema.decodeUnknownEffect(Settings)(
103
- env[SETTINGS_ENV],
104
- ).pipe(Effect.orDie);
95
+ const settings = yield* Schema.decodeUnknownEffect(Settings)(env[SETTINGS_ENV]).pipe(
96
+ Effect.orDie,
97
+ );
105
98
  return {
106
99
  ...settings,
107
100
  worker: {},
@@ -165,10 +158,7 @@ export default RegistryLive.pipe(Layer.provide(RegistryConfigFromEnv));
165
158
  * @product Workers
166
159
  * @category Workers & Compute
167
160
  */
168
- export const PkgRegistry = <const Id extends string>(
169
- id: Id,
170
- options: RegistryOptions,
171
- ) =>
161
+ export const PkgRegistry = <const Id extends string>(id: Id, options: RegistryOptions) =>
172
162
  Registry.pipe(
173
163
  Effect.provide(
174
164
  RegistryLive.pipe(
package/src/cli/bin.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import { PlatformServices, runMain } from "alchemy/Util/PlatformServices";
3
+ import { CliConfig, CliError, Command, Flag, GlobalFlag } from "effect/cli";
3
4
  import * as Config from "effect/Config";
4
5
  import * as Effect from "effect/Effect";
5
- import * as Option from "effect/Option";
6
- import * as Layer from "effect/Layer";
7
- import { CliConfig, CliError, Command, Flag, GlobalFlag } from "effect/cli";
8
6
  import * as FetchHttpClient from "effect/http/FetchHttpClient";
7
+ import * as Layer from "effect/Layer";
8
+ import * as Option from "effect/Option";
9
9
  import pkg from "../../package.json" with { type: "json" };
10
10
  import { Group, pack } from "./pack.ts";
11
11
  import { publish } from "./publish.ts";
@@ -38,16 +38,12 @@ export const packCommand = Command.make(
38
38
  registry: registryFlag,
39
39
  out: outFlag,
40
40
  since: Flag.String("since").pipe(
41
- Flag.withDescription(
42
- "Compare HEAD with this Git ref; defaults to the PR base in Actions",
43
- ),
41
+ Flag.withDescription("Compare HEAD with this Git ref; defaults to the PR base in Actions"),
44
42
  Flag.optional,
45
43
  ),
46
44
  all: Flag.Boolean("all").pipe(
47
45
  Flag.withDefault(false),
48
- Flag.withDescription(
49
- "Pack every configured package regardless of changes",
50
- ),
46
+ Flag.withDescription("Pack every configured package regardless of changes"),
51
47
  ),
52
48
  rebuildAllPaths: Flag.String("rebuild-all-path").pipe(
53
49
  Flag.withDescription(
@@ -98,17 +94,13 @@ export const publishCommand = Command.make(
98
94
  Command.withDescription(
99
95
  "Publish a pkg pack directory from the current GitHub Actions job, after its manifest artifact has been uploaded",
100
96
  ),
101
- Command.withExamples([
102
- { command: "pkg publish --registry https://pkg.alchemy.run" },
103
- ]),
97
+ Command.withExamples([{ command: "pkg publish --registry https://pkg.alchemy.run" }]),
104
98
  );
105
99
 
106
100
  export const root = Command.make("pkg", {}, () =>
107
- Effect.fail(new CliError.ShowHelp({ commandPath: ["pkg"], errors: [] })),
101
+ Effect.fail(CliError.ShowHelp.make({ commandPath: ["pkg"], errors: [] })),
108
102
  ).pipe(
109
- Command.withDescription(
110
- "Pack and publish preview packages for pull requests.",
111
- ),
103
+ Command.withDescription("Pack and publish preview packages for pull requests."),
112
104
  Command.withSubcommands([packCommand, publishCommand]),
113
105
  );
114
106
 
@@ -117,9 +109,7 @@ Command.run(root, { version: pkg.version }).pipe(
117
109
  Layer.mergeAll(
118
110
  PlatformServices,
119
111
  FetchHttpClient.layer,
120
- CliConfig.layer({
121
- builtIns: [GlobalFlag.Help, GlobalFlag.Version],
122
- }),
112
+ CliConfig.layer({ builtIns: [GlobalFlag.Help, GlobalFlag.Version] }),
123
113
  ),
124
114
  ),
125
115
  runMain,