diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 522f10b..6fe92a5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,14 +2,14 @@ name: CI on: push: + pull_request: jobs: - lint: - name: "Lint Code" + build: + name: "Build & Test" runs-on: ubuntu-latest permissions: contents: read - packages: write steps: - uses: actions/checkout@v5 - uses: pnpm/action-setup@v4 @@ -20,5 +20,24 @@ jobs: with: node-version: ${{ vars.NODEJS_VERSION }} cache: 'pnpm' + - run: pnpm install - - run: pnpm run lint \ No newline at end of file + + # Drift check: regenerate the low-level layer from the vendored spec and + # fail if the committed output is stale. The generated code under + # `src/gen/` must always match `spec/client.yaml`. + - name: Generate client layer + run: pnpm generate + - name: Verify generated code is up to date + run: | + git diff --exit-code src/gen || { + echo "::error::Generated code in src/gen is out of date. Run 'pnpm generate' and commit the result." + exit 1 + } + + - name: Build + run: pnpm run build + - name: Lint + run: pnpm run lint + - name: Test + run: pnpm test diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index e05b6e8..b27e305 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -25,3 +25,4 @@ jobs: env: LUNOGRAM_API_KEY: ${{ secrets.LUNOGRAM_API_KEY }} LUNOGRAM_API_URL: ${{ secrets.LUNOGRAM_API_URL }} + LUNOGRAM_PROJECT_ID: ${{ secrets.LUNOGRAM_PROJECT_ID }} diff --git a/README.md b/README.md index d0fc288..c4f9268 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,8 @@ npm install @lunogram/js-sdk ```typescript import { Lunogram } from '@lunogram/js-sdk' -const lunogram = new Lunogram('your-api-key') +// The project UUID is required and scopes every request to that project. +const lunogram = new Lunogram('your-api-key', 'your-project-uuid') // Identify a user await lunogram.user.upsert({ @@ -46,6 +47,32 @@ await lunogram.organization.upsert({ }) ``` +## Project Scoping + +Every Lunogram Client API request is scoped to a single **project**. You supply +the project UUID once, when constructing the client, and the SDK injects it into +every request path automatically — you never pass it per call. + +All Client API endpoints live under `/api/client/projects/{projectId}/...`: + +| Operation | Path | +| ------------------------ | ----------------------------------------------------------------- | +| Users | `/api/client/projects/{projectId}/users` | +| User events | `/api/client/projects/{projectId}/users/events` | +| User scheduled | `/api/client/projects/{projectId}/users/scheduled` | +| User inbox | `/api/client/projects/{projectId}/users/inbox` | +| User devices | `/api/client/projects/{projectId}/users/devices` | +| Organizations | `/api/client/projects/{projectId}/organizations` | +| Organization members | `/api/client/projects/{projectId}/organizations/users` | +| Organization events | `/api/client/projects/{projectId}/organizations/events` | +| Organization scheduled | `/api/client/projects/{projectId}/organizations/scheduled` | +| Organization inbox | `/api/client/projects/{projectId}/organizations/inbox` | +| Push (VAPID key) | `/api/client/projects/{projectId}/push/vapid` | +| Sessions | `/api/client/projects/{projectId}/auth-methods/{authMethodId}/sessions` | + +The `projectId` must be a valid UUID; the client throws a `ValidationError` at +construction time if it is missing, empty, or malformed. + ## Identity Model Users and organizations are identified by an array of `ExternalID` objects: @@ -69,7 +96,10 @@ The SDK automatically generates an anonymous identifier for each session. When y ```typescript import { BrowserClient } from '@lunogram/js-sdk' -const client = new BrowserClient({ apiKey: 'your-api-key' }) +const client = new BrowserClient({ + apiKey: 'your-api-key', + projectId: 'your-project-uuid', +}) // anonymousId is auto-generated client.user.anonymousId @@ -100,7 +130,7 @@ The SDK is also exposed on `window.Lunogram`: ```html