Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .changeset/verify-pack-client-workspace.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
'seamless-cli': patch
---

`seamless verify --local` packs `@seamless-auth/client` alongside `@seamless-auth/react`
when the React SDK checkout is an npm workspace.

The client SDK repo is becoming a workspace that publishes a framework-agnostic
`@seamless-auth/client` next to `@seamless-auth/react`, and the react tarball depends on
the client one. Packing only the root of such a checkout would produce the private root
package, and installing the react tarball alone would go to the registry for a client
version that is not published yet. The harness now reads `workspaces` from the checkout's
`package.json`, packs both packages when it finds one, and the react image installs every
tarball in one `npm install` so the dependency resolves from the sibling file. A checkout
that predates the workspace is packed as before. The version line reported for
`@seamless-auth/react` reads the react package's own manifest in a workspace.
39 changes: 39 additions & 0 deletions src/commands/verify.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,45 @@ describe("runVerify — local mode", () => {
expect(exitSpy).not.toHaveBeenCalled();
});

it("packs the react SDK as a single package when the checkout has no workspaces", async () => {
await runVerify(["--local"]);

const packs = callsFor("npm").filter((a) => a[0] === "pack");
expect(packs).toEqual([["pack", "--pack-destination", expect.stringContaining("react-vendor")]]);
});

it("packs the client core alongside react when the checkout is a workspace", async () => {
vi.mocked(fs.readFileSync).mockImplementation((p: never) => {
const s = String(p);
if (s.endsWith("registry.json")) return REGISTRY_JSON as never;
if (s.endsWith("template.json"))
return JSON.stringify({ verify: { flows: ["oauth"] } }) as never;
if (s === "/fake/reactsdk/package.json")
return JSON.stringify({ private: true, workspaces: ["packages/*"] }) as never;
if (s === "/fake/reactsdk/packages/react/package.json")
return JSON.stringify({ version: "0.13.0" }) as never;
return PKG_JSON as never;
});

await runVerify(["--local"]);

const packs = callsFor("npm").filter((a) => a[0] === "pack");
expect(packs).toEqual([
[
"pack",
"-w",
"@seamless-auth/client",
"-w",
"@seamless-auth/react",
"--pack-destination",
expect.stringContaining("react-vendor"),
],
]);
// The version line reads the react package's manifest, not the private root.
expect(logSpy.mock.calls.flat().join("\n")).toContain("@seamless-auth/react");
expect(logSpy.mock.calls.flat().join("\n")).toContain("0.13.0");
});

it("falls back to sibling checkouts when the dir env vars are unset", async () => {
// Exercises the default path.resolve(...) branches for the templates root and
// the react SDK dir; everything "exists" so resolution succeeds.
Expand Down
31 changes: 27 additions & 4 deletions src/commands/verify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,13 +178,31 @@ function cleanVendor(): void {
}
}

// Build + pack the local @seamless-auth/react into ./react-vendor so the react
// service installs it over the published version (--local browser runs).
// The client SDK repo became an npm workspace publishing @seamless-auth/client
// alongside @seamless-auth/react. The react tarball depends on the client one,
// so both are packed into the vendor dir and the image installs them together.
// A checkout that predates the workspace still packs its single root package.
function reactSdkWorkspaces(sdkDir: string): string[] {
const workspaces = readJson(path.join(sdkDir, "package.json"))?.workspaces;
return Array.isArray(workspaces) && workspaces.length > 0
? ["@seamless-auth/client", "@seamless-auth/react"]
: [];
}

// Build + pack the local @seamless-auth/react (and its client core when the
// checkout is a workspace) into ./react-vendor so the react service installs it
// over the published version (--local browser runs).
async function packLocalReactSdk(env: NodeJS.ProcessEnv): Promise<void> {
const sdkDir = resolveReactSdkDir();
console.log(kleur.cyan("→ Building & packing local @seamless-auth/react…"));
await runCommand("npm", ["run", "build"], sdkDir, env);
await runCommand("npm", ["pack", "--pack-destination", REACT_VENDOR_DIR], sdkDir, env);
const workspaceArgs = reactSdkWorkspaces(sdkDir).flatMap((pkg) => ["-w", pkg]);
await runCommand(
"npm",
["pack", ...workspaceArgs, "--pack-destination", REACT_VENDOR_DIR],
sdkDir,
env,
);
}

// Each adapter image installs core plus its own framework package, so the
Expand Down Expand Up @@ -299,7 +317,12 @@ function collectPackageVersions(
}
if (opts.react) {
try {
push("@seamless-auth/react", readPkgVersion(path.join(resolveReactSdkDir(), "package.json")));
const sdkDir = resolveReactSdkDir();
const reactPkg =
reactSdkWorkspaces(sdkDir).length > 0
? path.join(sdkDir, "packages", "react", "package.json")
: path.join(sdkDir, "package.json");
push("@seamless-auth/react", readPkgVersion(reactPkg));
} catch {
// React SDK checkout unavailable.
}
Expand Down
10 changes: 6 additions & 4 deletions verify/docker-compose.verify.yml
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,10 @@ services:
# Gated behind the `react` profile so the api/adapter-only runs skip the build.
#
# The inline Dockerfile mirrors the starter's own Dockerfile, but additionally
# installs a locally-built @seamless-auth/react tarball when present (--local
# mode packs it into ./react-vendor; that dir is empty otherwise → published SDK).
# installs locally-built @seamless-auth/react (and @seamless-auth/client)
# tarballs when present (--local mode packs them into ./react-vendor; that dir
# is empty otherwise → published SDK). Installed in one command so npm resolves
# react's client dependency from the sibling tarball rather than the registry.
react:
profiles: ['react']
build:
Expand All @@ -169,8 +171,8 @@ services:
RUN npm ci
COPY . .
COPY --from=sdk . /tmp/sdk/
RUN if ls /tmp/sdk/seamless-auth-react-*.tgz >/dev/null 2>&1; then \
npm install /tmp/sdk/seamless-auth-react-*.tgz; \
RUN if ls /tmp/sdk/*.tgz >/dev/null 2>&1; then \
npm install /tmp/sdk/*.tgz; \
fi
RUN npm run build
FROM nginx:alpine
Expand Down
Loading