verify-package.ts 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. #!/usr/bin/env bun
  2. import { mkdtemp, rm, writeFile } from "node:fs/promises"
  3. import { tmpdir } from "node:os"
  4. import path from "node:path"
  5. import { pack } from "./pack.js"
  6. const run = async (command: ReadonlyArray<string>, cwd: string) => {
  7. const process = Bun.spawn(command, { cwd, env: globalThis.process.env, stdout: "inherit", stderr: "inherit" })
  8. const exitCode = await process.exited
  9. if (exitCode !== 0) throw new Error(`${command.join(" ")} exited with code ${exitCode}`)
  10. }
  11. export const verifyPackage = async (archive: string) => {
  12. const directory = await mkdtemp(path.join(tmpdir(), "http-recorder-consumer-"))
  13. try {
  14. await writeFile(
  15. path.join(directory, "package.json"),
  16. JSON.stringify({ name: "http-recorder-consumer", private: true, type: "module" }),
  17. )
  18. await writeFile(
  19. path.join(directory, "consumer.ts"),
  20. `import { HttpRecorder } from "@opencode-ai/http-recorder"
  21. import { NodeSocket } from "@effect/platform-node"
  22. import { Layer } from "effect"
  23. import { HttpClient } from "effect/unstable/http"
  24. import { Socket } from "effect/unstable/socket"
  25. const options: HttpRecorder.RecorderOptions = { redact: { jsonFields: ["access_token"] } }
  26. HttpRecorder.http("consumer", options) satisfies Layer.Layer<HttpClient.HttpClient>
  27. HttpRecorder.socket("consumer/socket", options).pipe(
  28. Layer.provide(NodeSocket.layerWebSocket("wss://example.test")),
  29. ) satisfies Layer.Layer<Socket.Socket>
  30. `,
  31. )
  32. await writeFile(
  33. path.join(directory, "tsconfig.json"),
  34. JSON.stringify({
  35. compilerOptions: {
  36. target: "ES2022",
  37. module: "NodeNext",
  38. moduleResolution: "NodeNext",
  39. strict: true,
  40. noEmit: true,
  41. // Required by effect@4.0.0-beta.74: its schema.d.ts references an undeclared SchemaErrorTypeId.
  42. skipLibCheck: true,
  43. lib: ["ES2022", "DOM", "ESNext.Disposable"],
  44. },
  45. include: ["consumer.ts"],
  46. }),
  47. )
  48. await run(["npm", "install", archive, "typescript@5.8.2"], directory)
  49. await run(
  50. [
  51. "node",
  52. "--input-type=module",
  53. "-e",
  54. 'import("@opencode-ai/http-recorder").then((module) => { const root = Object.keys(module).sort(); const namespace = Object.keys(module.HttpRecorder).sort(); if (JSON.stringify(root) !== JSON.stringify(["HttpRecorder"])) throw new Error(`Unexpected root exports: ${root}`); if (JSON.stringify(namespace) !== JSON.stringify(["http", "socket"])) throw new Error(`Unexpected namespace exports: ${namespace}`) })',
  55. ],
  56. directory,
  57. )
  58. await run([path.join(directory, "node_modules", ".bin", "tsc"), "--noEmit"], directory)
  59. } finally {
  60. await rm(directory, { recursive: true, force: true })
  61. }
  62. }
  63. if (import.meta.main) {
  64. const archive = await pack()
  65. try {
  66. await verifyPackage(archive)
  67. } finally {
  68. await Bun.file(archive).delete()
  69. }
  70. }