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
103 changes: 103 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ This focus helps guide our project decisions as a community and what we choose t
- [Available commands](#available-commands)
- [Project structure](#project-structure)
- [Local connector CLI](#local-connector-cli)
- [Mock connector (for local development)](#mock-connector-for-local-development)
- [Code style](#code-style)
- [TypeScript](#typescript)
- [Server API patterns](#server-api-patterns)
Expand Down Expand Up @@ -104,6 +105,10 @@ pnpm dev # Start development server
pnpm build # Production build
pnpm preview # Preview production build

# Connector
pnpm npmx-connector # Start the real connector (requires npm login)
pnpm mock-connector # Start the mock connector (no npm login needed)

# Code Quality
pnpm lint # Run linter (oxlint + oxfmt)
pnpm lint:fix # Auto-fix lint issues
Expand Down Expand Up @@ -157,6 +162,36 @@ pnpm npmx-connector

The connector will check your npm authentication, generate a connection token, and listen for requests from npmx.dev.

### Mock connector (for local development)

If you're working on admin features (org management, package access controls, operations queue) and don't want to use your real npm account, you can run the mock connector instead:

```bash
pnpm mock-connector
```

This starts a mock connector server pre-populated with sample data (orgs, teams, members, packages). No npm login is required — operations succeed immediately without making real npm CLI calls.

The mock connector prints a connection URL to the terminal, just like the real connector. Click it (or paste the token manually) to connect the UI.

**Options:**

```bash
pnpm mock-connector # default: port 31415, user "mock-user", sample data
pnpm mock-connector --port 9999 # custom port
pnpm mock-connector --user alice # custom username
pnpm mock-connector --empty # start with no pre-populated data
```

**Default sample data:**

- **@nuxt**: 4 members (mock-user, danielroe, pi0, antfu), 3 teams (core, docs, triage)
- **@unjs**: 2 members (mock-user, pi0), 1 team (maintainers)
- **Packages**: @nuxt/kit, @nuxt/schema, @unjs/nitro with team-based access controls

> [!TIP]
> Run `pnpm dev` in a separate terminal to start the Nuxt dev server, then click the connection URL from the mock connector to connect.

## Code style

When committing changes, try to keep an eye out for unintended formatting updates. These can make a pull request look noisier than it really is and slow down the review process. Sometimes IDEs automatically reformat files on save, which can unintentionally introduce extra changes.
Expand Down Expand Up @@ -752,6 +787,74 @@ You need to either:
1. Add a fixture file for that package/endpoint
2. Update the mock handlers in `test/fixtures/mock-routes.cjs` (client) or `modules/runtime/server/cache.ts` (server)

### Testing connector features

Features that require authentication through the local connector (org management, package collaborators, operations queue) are tested using a mock connector server.

#### Architecture

The mock connector infrastructure is shared between the CLI, E2E tests, and Vitest component tests:

```
cli/src/
├── types.ts # ConnectorEndpoints contract (shared by real + mock)
├── mock-state.ts # MockConnectorStateManager (canonical source)
├── mock-app.ts # H3 mock app + MockConnectorServer class
└── mock-server.ts # CLI entry point (pnpm mock-connector)

test/test-utils/ # Re-exports from cli/src/ for test convenience
test/e2e/helpers/ # E2E-specific wrappers (fixtures, global setup)
```
Comment on lines +798 to +807
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add language identifier to the fenced code block.

The code block showing the directory structure is missing a language identifier, which violates the markdownlint rule MD040. Add a language identifier to maintain consistency with the project's documentation standards.

📝 Proposed fix
-```
+```text
 cli/src/
 ├── types.ts           # ConnectorEndpoints contract (shared by real + mock)
 ├── mock-state.ts      # MockConnectorStateManager (canonical source)
🧰 Tools
🪛 markdownlint-cli2 (0.20.0)

[warning] 798-798: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


Both the real server (`cli/src/server.ts`) and the mock server (`cli/src/mock-app.ts`) conform to the `ConnectorEndpoints` interface defined in `cli/src/types.ts`. This ensures the API contract is enforced by TypeScript. When adding a new endpoint, update `ConnectorEndpoints` first, then implement it in both servers.

#### Vitest component tests (`test/nuxt/`)

- Mock the `useConnector` composable with reactive state
- Use `document.body` queries for components using Teleport
- See `test/nuxt/components/HeaderConnectorModal.spec.ts` for an example

```typescript
// Create mock state
const mockState = ref({ connected: false, npmUser: null, ... })

// Mock the composable
vi.mock('~/composables/useConnector', () => ({
useConnector: () => ({
isConnected: computed(() => mockState.value.connected),
// ... other properties
}),
}))
```

#### Playwright E2E tests (`test/e2e/`)

- A mock HTTP server starts automatically via Playwright's global setup
- Use the `mockConnector` fixture to set up test data and the `gotoConnected` helper to navigate with authentication

```typescript
test('shows org members', async ({ page, gotoConnected, mockConnector }) => {
// Set up test data
await mockConnector.setOrgData('@testorg', {
users: { testuser: 'owner', member1: 'admin' },
})

// Navigate with connector authentication
await gotoConnected('/@testorg')

// Test assertions
await expect(page.getByRole('link', { name: '@testuser' })).toBeVisible()
})
```

The mock connector supports test endpoints for state manipulation:

- `/__test__/reset` - Reset all mock state
- `/__test__/org` - Set org users, teams, and team members
- `/__test__/user-orgs` - Set user's organizations
- `/__test__/user-packages` - Set user's packages
- `/__test__/package` - Set package collaborators

## Submitting changes

### Before submitting
Expand Down
2 changes: 1 addition & 1 deletion app/composables/useConnector.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { PendingOperation, OperationStatus, OperationType } from '../../cli/src/types'
import type { PendingOperation, OperationStatus, OperationType } from '#cli/types'
import { $fetch } from 'ofetch'

export interface NewOperation {
Expand Down
6 changes: 2 additions & 4 deletions cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,7 @@
"npmx-connector": "./dist/cli.mjs"
},
"exports": {
".": {
"import": "./dist/index.mjs",
"types": "./dist/index.d.mts"
}
".": "./dist/index.mjs"
},
Comment on lines 19 to 21
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

cat -n cli/package.json | head -50

Repository: npmx-dev/npmx.dev

Length of output: 1657


🏁 Script executed:

rg "from\s+['\"]npmx-connector['\"]|import.*npmx-connector" --type ts --type js

Repository: npmx-dev/npmx.dev

Length of output: 43


🏁 Script executed:

fd tsconfig -E node_modules

Repository: npmx-dev/npmx.dev

Length of output: 92


🏁 Script executed:

ls -la cli/dist/ 2>/dev/null || echo "dist directory not found or empty"

Repository: npmx-dev/npmx.dev

Length of output: 94


🏁 Script executed:

git log --oneline -n 20 cli/package.json 2>/dev/null | head -10

Repository: npmx-dev/npmx.dev

Length of output: 136


🏁 Script executed:

git show HEAD:cli/package.json 2>/dev/null | sed -n '19,25p'

Repository: npmx-dev/npmx.dev

Length of output: 152


🏁 Script executed:

cat -n cli/tsconfig.json

Repository: npmx-dev/npmx.dev

Length of output: 532


🏁 Script executed:

git diff HEAD~1 cli/package.json 2>/dev/null || echo "Unable to get diff"

Repository: npmx-dev/npmx.dev

Length of output: 79


🏁 Script executed:

git status cli/package.json

Repository: npmx-dev/npmx.dev

Length of output: 126


🌐 Web query:

tsdown TypeScript declarations .d.ts output

💡 Result:

To make tsdown emit TypeScript declaration files (.d.ts):

  • Install TypeScript (required for d.ts generation). (tsdown.dev)
  • Enable dts output in one of these ways:
    • If your package.json has "types" / "typings", tsdown enables dts automatically. (tsdown.dev)
    • Or explicitly:
      • CLI: tsdown --dts (tsdown.dev)
      • Config:
        // tsdown.config.ts
        import { defineConfig } from 'tsdown'
        
        export default defineConfig({
          dts: true,
        })
        (tsdown.dev)

Common dts-related options/notes:

  • Declaration maps: set compilerOptions.declarationMap: true in tsconfig.json, or dts: { sourcemap: true } in tsdown config. (tsdown.dev)
  • tsdown generates/bundles .d.ts via rolldown-plugin-dts. (tsdown.dev)
  • Performance: if tsconfig.json enables isolatedDeclarations, tsdown uses a faster path; otherwise it falls back to the TypeScript compiler. (tsdown.dev)

Citations:


🏁 Script executed:

git log --all --oneline cli/package.json 2>/dev/null | head -20

Repository: npmx-dev/npmx.dev

Length of output: 1108


🏁 Script executed:

git show HEAD~5:cli/package.json 2>/dev/null | grep -A 5 "exports" || echo "Cannot access earlier commit"

Repository: npmx-dev/npmx.dev

Length of output: 89


🏁 Script executed:

fd tsdown.config -E node_modules

Repository: npmx-dev/npmx.dev

Length of output: 81


🏁 Script executed:

git show 588e1a1:cli/package.json 2>/dev/null | grep -A 5 "exports" || echo "Cannot access that commit"

Repository: npmx-dev/npmx.dev

Length of output: 169


🏁 Script executed:

git show 1f03429:cli/package.json 2>/dev/null | grep -A 5 "exports" || echo "Cannot access that commit"

Repository: npmx-dev/npmx.dev

Length of output: 169


🏁 Script executed:

cat -n cli/tsdown.config.ts

Repository: npmx-dev/npmx.dev

Length of output: 299


Restore types entry to exports object for TypeScript consumers.

The exports field was simplified from { "import": "./dist/index.mjs", "types": "./dist/index.d.mts" } to a single path. Since tsdown is configured to generate .d.ts files (dts: true), but the types entry is missing from exports, TypeScript consumers cannot resolve type definitions. Modern TypeScript respects the package.json exports field and requires explicit type entries. This breaks type resolution for external consumers of the published package. Restore the object format with separate import and types entries.

"files": [
"dist"
Expand All @@ -29,6 +26,7 @@
"build": "tsdown",
"dev": "NPMX_CLI_DEV=true node src/cli.ts",
"dev:debug": "DEBUG=npmx-connector NPMX_CLI_DEV=true node src/cli.ts",
"dev:mock": "NPMX_CLI_DEV=true node src/mock-server.ts",
"test:types": "tsc --noEmit"
},
"dependencies": {
Expand Down
Loading
Loading