| @@ -7,6 +7,7 @@ · 6 unmodified lines |
| 7 | * name (`@executor-js/sdk`) instead of relying on workspace resolution. | 7 | * name (`@executor-js/sdk`) instead of relying on workspace resolution. |
| 8 | */ | 8 | */ |
| 9 | import { $ } from "bun"; | 9 | import { $ } from "bun"; |
| | 10 | import { Schema } from "effect"; |
| 10 | import { existsSync, readdirSync } from "node:fs"; | 11 | import { existsSync, readdirSync } from "node:fs"; |
| 11 | import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; | 12 | import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; |
| 12 | import { tmpdir } from "node:os"; | 13 | import { tmpdir } from "node:os"; |
| @@ -14,10 +15,15 @@ import { dirname, join, resolve } from "node:path"; · 1 unmodified line |
| 14 | import { fileURLToPath } from "node:url"; | 15 | import { fileURLToPath } from "node:url"; |
| 15 | | 16 | |
| 16 | const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), ".."); | 17 | const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), ".."); |
| 17 | const documentedPackages = ["@executor-js/sdk", "@executor-js/plugin-openapi"] as const; | 18 | const documentedPackages = [ |
| | 19 | "@executor-js/sdk", |
| | 20 | "@executor-js/product-access", |
| | 21 | "@executor-js/plugin-openapi", |
| | 22 | ] as const; |
| 18 | const publicPackageDirs = [ | 23 | const publicPackageDirs = [ |
| 19 | "packages/core/fumadb", | 24 | "packages/core/fumadb", |
| 20 | "packages/core/sdk", | 25 | "packages/core/sdk", |
| | 26 | "packages/core/product-access", |
| 21 | "packages/core/config", | 27 | "packages/core/config", |
| 22 | "packages/plugins/openapi", | 28 | "packages/plugins/openapi", |
| 23 | ] as const; | 29 | ] as const; |
| @@ -27,6 +33,21 @@ const readPackageName = async (pkgDir: string): Promise<string> => { · 3 unmodified lines |
| 27 | return (JSON.parse(raw) as { name: string }).name; | 33 | return (JSON.parse(raw) as { name: string }).name; |
| 28 | }; | 34 | }; |
| 29 | | 35 | |
| | 36 | // `effect` is the SDK's (and product-access's) required peer, and the docs |
| | 37 | // install command tells consumers to add it themselves. `npm install |
| | 38 | // --legacy-peer-deps` below skips auto-installing peers, so the fixture must |
| | 39 | // declare it like a real consumer would — pinned from the workspace catalog |
| | 40 | // so it matches what the packed manifests resolved `catalog:` to. |
| | 41 | const decodeCatalog = Schema.decodeUnknownSync( |
| | 42 | Schema.Struct({ catalog: Schema.Struct({ effect: Schema.NonEmptyString }) }), |
| | 43 | ); |
| | 44 | |
| | 45 | const readCatalogEffectVersion = async (): Promise<string> => { |
| | 46 | const raw = await readFile(join(repoRoot, "package.json"), "utf8"); |
| | 47 | const manifest = decodeCatalog(JSON.parse(raw)); |
| | 48 | return manifest.catalog.effect; |
| | 49 | }; |
| | 50 | |
| 30 | const findTarball = (pkgDir: string, packageName: string): string => { | 51 | const findTarball = (pkgDir: string, packageName: string): string => { |
| 31 | const tarball = readdirSync(pkgDir).find((entry) => entry.endsWith(".tgz")); | 52 | const tarball = readdirSync(pkgDir).find((entry) => entry.endsWith(".tgz")); |
| 32 | if (!tarball) { | 53 | if (!tarball) { |
| @@ -48,7 +69,7 @@ for (const relDir of publicPackageDirs) { · 15 unmodified lines |
| 48 | const tmp = await mkdtemp(join(tmpdir(), "executor-docs-install-")); | 69 | const tmp = await mkdtemp(join(tmpdir(), "executor-docs-install-")); |
| 49 | | 70 | |
| 50 | try { | 71 | try { |
| 51 | const dependencies: Record<string, string> = {}; | 72 | const dependencies: Record<string, string> = { effect: await readCatalogEffectVersion() }; |
| 52 | const overrides: Record<string, string> = {}; | 73 | const overrides: Record<string, string> = {}; |
| 53 | for (const [name, tarball] of tarballs) { | 74 | for (const [name, tarball] of tarballs) { |
| 54 | overrides[name] = `file:${tarball}`; | 75 | overrides[name] = `file:${tarball}`; |
| @@ -89,9 +110,23 @@ try { · 34 unmodified lines |
| 89 | } | 110 | } |
| 90 | | 111 | |
| 91 | console.log(`[docs-smoke] import documented SDK packages`); | 112 | console.log(`[docs-smoke] import documented SDK packages`); |
| 92 | await $`node --input-type=module --eval ${`const sdk = await import("@executor-js/sdk"); const openapi = await import("@executor-js/plugin-openapi"); if (typeof sdk.createExecutor !== "function") throw new Error("missing createExecutor"); if (typeof openapi.openApiPlugin !== "function") throw new Error("missing openApiPlugin");`}`.cwd( | 113 | // The documented minimal embed: the SDK root export is the Promise façade |
| 93 | tmp, | 114 | // and ships no access posture of its own, so the probe constructs a |
| 94 | ); | 115 | // subject-less workspace-service executor with the posture from |
| | 116 | // `@executor-js/product-access` (ephemeral in-memory backend, nothing |
| | 117 | // invoked over the network) and closes it. |
| | 118 | const importProbe = [ |
| | 119 | `const sdk = await import("@executor-js/sdk");`, |
| | 120 | `const access = await import("@executor-js/product-access");`, |
| | 121 | `const openapi = await import("@executor-js/plugin-openapi");`, |
| | 122 | `if (typeof sdk.createExecutor !== "function") throw new Error("missing createExecutor");`, |
| | 123 | `if (typeof access.workspaceServiceAccess !== "function") throw new Error("missing workspaceServiceAccess");`, |
| | 124 | `if (typeof openapi.openApiPlugin !== "function") throw new Error("missing openApiPlugin");`, |
| | 125 | `const executor = await sdk.createExecutor({ access: access.workspaceServiceAccess(), onElicitation: "accept-all" });`, |
| | 126 | `if (typeof executor.close !== "function") throw new Error("missing executor.close");`, |
| | 127 | `await executor.close();`, |
| | 128 | ].join("\n"); |
| | 129 | await $`node --input-type=module --eval ${importProbe}`.cwd(tmp); |
| 95 | } finally { | 130 | } finally { |
| 96 | await rm(tmp, { recursive: true, force: true }); | 131 | await rm(tmp, { recursive: true, force: true }); |
| 97 | } | 132 | } |