@connectedxm/client 8.1.7 → 8.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: applying-backend-diff
|
|
3
|
+
description: Use when the user shares a backend PR, patch URL, or diff and asks to mirror, port, or add those API endpoint changes into the @connectedxm/client TypeScript SDK (queries/mutations files under src/queries and src/mutations). Triggered by phrases like "update client sdk with this diff", "mirror this endpoint", "add this to client-sdk", "port these endpoints", or sharing a backend PR while in the client-sdk repo or one of its worktrees.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Applying a Backend Diff to client-sdk
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
Backend adds or changes a client API endpoint → client-sdk needs a matching query or mutation file. Unlike admin-sdk, this SDK **is** the source package (`@connectedxm/client`) — there's no downstream OpenAPI generator. The strict file shape still matters because the TanStack Query wrappers (`useConnectedSingleQuery`, `useConnectedInfiniteQuery`, `useConnectedCursorQuery`, `useConnectedMutation`) and downstream consumers expect the canonical triple: query-key helper, async function, hook.
|
|
11
|
+
|
|
12
|
+
The repo's [CLAUDE.md](../../../CLAUDE.md) is the source of truth for conventions; this skill is the procedural recipe for going from a backend diff to a working, lint-clean SDK change.
|
|
13
|
+
|
|
14
|
+
## When to use
|
|
15
|
+
|
|
16
|
+
- User shares a backend PR URL (github.com, patch-diff.githubusercontent.com) and asks to update the client SDK.
|
|
17
|
+
- User pastes a diff and asks to mirror endpoints in client-sdk.
|
|
18
|
+
- User asks to "add this endpoint" to client-sdk and provides backend code.
|
|
19
|
+
|
|
20
|
+
**Don't use** for:
|
|
21
|
+
- Pure refactors, dependency bumps, or test-only changes.
|
|
22
|
+
- Edits to `dist/` (build output — never hand-edit).
|
|
23
|
+
- WebSocket-only protocol changes that don't touch HTTP endpoints (those need wiring in `src/websockets/`, not new query/mutation files — see step 6).
|
|
24
|
+
|
|
25
|
+
## Workflow
|
|
26
|
+
|
|
27
|
+
### 1. Get the diff
|
|
28
|
+
|
|
29
|
+
If the user gave a GitHub PR URL or a `patch-diff.githubusercontent.com` URL, fetch via `gh` CLI rather than WebFetch — patch-diff URLs redirect through auth and frequently 401:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
gh pr view <num> --repo <owner>/<repo> --json title,body,files,additions,deletions
|
|
33
|
+
gh api repos/<owner>/<repo>/pulls/<num>/files --jq '.[] | {path: .filename, patch: .patch}'
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
The PR description often names the pattern to mirror ("mirrors the existing X(...)") — read it before scanning code.
|
|
37
|
+
|
|
38
|
+
### 2. Extract, per endpoint
|
|
39
|
+
|
|
40
|
+
- HTTP method + URL path (e.g. `GET /payments/:paymentId/tax-metadata`)
|
|
41
|
+
- Path / query params and their types
|
|
42
|
+
- Request body shape (mutations only)
|
|
43
|
+
- Response `data` shape — including all branches (soft-skip messages, error messages, success payload)
|
|
44
|
+
- Any **new** custom HTTP status codes the backend returns for "this user can't do this" (e.g. a new `468 ERR_XYZ`) — these must be added to `CUSTOM_ERROR_CODES` (see step 5).
|
|
45
|
+
|
|
46
|
+
### 3. Find the canonical example to mirror
|
|
47
|
+
|
|
48
|
+
Locate the parent resource directory by grepping for an existing endpoint on the same resource:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
grep -rln "/payments/" src/queries/ src/mutations/
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Then mirror the closest-matching file:
|
|
55
|
+
|
|
56
|
+
| Endpoint type | Canonical example | Wrapper helper |
|
|
57
|
+
|---|---|---|
|
|
58
|
+
| Single GET | `src/queries/<resource>/useGetX.ts` | `useConnectedSingleQuery` |
|
|
59
|
+
| Paginated list | `src/queries/<resource>/useGetXs.ts` | `useConnectedInfiniteQuery` |
|
|
60
|
+
| Cursor list | `src/queries/streams/useGetStreamChatMessages.ts` | `useConnectedCursorQuery` |
|
|
61
|
+
| Mutation | `src/mutations/<resource>/useUpdateX.ts` / `useCreateX.ts` / `useDeleteX.ts` | `useConnectedMutation` |
|
|
62
|
+
|
|
63
|
+
### 4. Reuse existing types
|
|
64
|
+
|
|
65
|
+
Before adding a new interface to `src/interfaces.ts`, grep for it:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
grep -n "^export.*<TypeName>\b" src/interfaces.ts
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Most domain models (`Payment`, `Activity`, `Account`, etc.) are already exported from `@interfaces`. For genuinely dynamic response blobs (raw third-party data, free-form metadata), `Record<string, any>` is fine — don't invent a misleading concrete interface just to "stay consistent."
|
|
72
|
+
|
|
73
|
+
### 5. Create the file with exact shape
|
|
74
|
+
|
|
75
|
+
Get this wrong and consumers either fail to import the hook or it silently bypasses the cache-invalidation contract other code relies on.
|
|
76
|
+
|
|
77
|
+
**Query file exports, in order:**
|
|
78
|
+
1. `*_QUERY_KEY` — array key, nested under parent resource's key (e.g. `[...PAYMENT_QUERY_KEY(id), "TAX_METADATA"]`). Note the convention: list-level keys are bare arrays (e.g. `["PAYMENTS"]`); single-resource keys spread their parent.
|
|
79
|
+
2. `SET_*_QUERY_DATA` — cache setter using `GetBaseSingleQueryKeys` / `GetBaseInfiniteQueryKeys`
|
|
80
|
+
3. Params interface extending `SingleQueryParams` / `InfiniteQueryParams` / `CursorQueryParams`
|
|
81
|
+
4. `async` API function returning `Promise<ConnectedXMResponse<T>>`
|
|
82
|
+
5. `use*` hook calling the appropriate `useConnected*Query`
|
|
83
|
+
|
|
84
|
+
**Mutation file exports, in order:**
|
|
85
|
+
1. Params interface extending `MutationParams`
|
|
86
|
+
2. `async` API function — after a successful response (`data.status === "ok"`), call relevant `SET_*_QUERY_DATA` setters and `queryClient.invalidateQueries(...)` so consumers see fresh data without a refetch round-trip
|
|
87
|
+
3. `use*` hook calling `useConnectedMutation`
|
|
88
|
+
|
|
89
|
+
Path aliases `@src/*` and `@interfaces` are configured — use them.
|
|
90
|
+
|
|
91
|
+
**If the backend introduced a new "user can't do this" HTTP status code (in the 453–467 family or beyond):** add the constant and append it to `CUSTOM_ERROR_CODES` in `src/utilities/GetErrorMessage.ts`. Codes not in that array get retried 3× and surface through the wrong handler (404/401/generic) instead of `onModuleForbidden`. This step is silently load-bearing — it's easy to miss because lint and types pass without it.
|
|
92
|
+
|
|
93
|
+
### 6. WebSocket events (only if backend added one)
|
|
94
|
+
|
|
95
|
+
If the backend diff introduces a new `ReceivedWSMessage` type, wire it in **both** places:
|
|
96
|
+
|
|
97
|
+
1. The `switch` in `src/hooks/useConnectedWebsocket.tsx`
|
|
98
|
+
2. A handler file under `src/websockets/{chat,threads,stream,...}/` that mutates the React Query cache directly
|
|
99
|
+
|
|
100
|
+
Missing either side means the event is received but ignored, or referenced but undefined.
|
|
101
|
+
|
|
102
|
+
### 7. Update barrels by hand
|
|
103
|
+
|
|
104
|
+
**Important difference from admin-sdk:** this repo has no `npm run exports` script. Barrel `index.ts` files are hand-maintained. After creating a new file under `src/queries/<resource>/` or `src/mutations/<resource>/`, append the corresponding `export * from "./useGetX";` line to that directory's `index.ts`. Keep the existing alphabetical ordering.
|
|
105
|
+
|
|
106
|
+
The top-level `src/queries/index.ts` and `src/mutations/index.ts` re-export the per-resource barrels — only edit those if you added a **new** resource directory.
|
|
107
|
+
|
|
108
|
+
### 8. Verify
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
npm run lint # runs tsc --noEmit + eslint over src/**/*.ts
|
|
112
|
+
npm test # vitest run (CI mode)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Both must pass clean before reporting completion. `tsup` (`npm run build`) is the bundler — `tsc` in `lint` is no-emit and catches type errors.
|
|
116
|
+
|
|
117
|
+
### 9. Version bump (only if the user is opening the PR now)
|
|
118
|
+
|
|
119
|
+
CI rejects PRs to `main` that don't bump `version` in `package.json` to a strictly greater plain semver (`x.y.z`, no pre-release tags). Ask the user which bump (patch/minor/major) before editing. If they're staging the work on a feature branch and not ready to PR, don't bump yet.
|
|
120
|
+
|
|
121
|
+
## Common mistakes
|
|
122
|
+
|
|
123
|
+
| Mistake | Fix |
|
|
124
|
+
|---|---|
|
|
125
|
+
| File deviates from canonical shape (wrong export order, missing `SET_*_QUERY_DATA`, return type not `Promise<ConnectedXMResponse<T>>`) | Diff your new file line-by-line against the closest existing file in the same directory |
|
|
126
|
+
| Adding a new interface that already exists in `interfaces.ts` | Grep `src/interfaces.ts` first; reuse types verbatim |
|
|
127
|
+
| Forgetting to add the new export line to the resource's `index.ts` barrel | `npm run lint` passes (file compiles in isolation) but consumers can't import the new hook from `@connectedxm/client`. Always edit the barrel. |
|
|
128
|
+
| New backend status code in 453+ range not appended to `CUSTOM_ERROR_CODES` | Surfaces wrong handler + silent 3× retry. Edit `src/utilities/GetErrorMessage.ts`. |
|
|
129
|
+
| New WS event type wired only in the switch OR only in a handler file | Wire **both** sides; the event is silently dropped otherwise. |
|
|
130
|
+
| Mutation function doesn't invalidate / set related cache after success | Consumers see stale data until next refetch. Mirror the invalidation pattern from `useUpdateActivity.ts`. |
|
|
131
|
+
| Using WebFetch on `github.com/.../pull/N.diff` | Use `gh pr view` and `gh api repos/.../pulls/N/files` instead |
|
|
132
|
+
| `cd`-ing out of the current worktree | Stay in the worktree directory; use absolute paths |
|
|
133
|
+
| Bumping `package.json` version on a work-in-progress branch | Only bump right before opening the PR to `main`; otherwise it churns merge conflicts |
|
|
134
|
+
|
|
135
|
+
## Red flags — stop and reconsider
|
|
136
|
+
|
|
137
|
+
- About to write a new interface without grepping `src/interfaces.ts` first
|
|
138
|
+
- About to skip editing the per-resource `index.ts` because "it's just an export line"
|
|
139
|
+
- New 4xx status code in the backend diff but not touching `CUSTOM_ERROR_CODES`
|
|
140
|
+
- File shape doesn't match the canonical example (different export order, missing `SET_*_QUERY_DATA`, return type not `Promise<ConnectedXMResponse<T>>`)
|
|
141
|
+
- Skipping `npm run lint` or `npm test` because "the change is small"
|
|
142
|
+
- Hand-editing anything under `dist/`
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ship-to-prod
|
|
3
|
+
description: Open a release PR into `main` in this repo. Handles the full flow — resolves the source branch from the user's prompt or the current checkout (never assumes `staging`), gathers commits, pulls Linear ticket references from commit messages and the PRs that merged in, drafts a title and body that match the repo's PR template, picks reviewers, and (after the user confirms) creates the PR with `gh pr create`. Use this whenever the user says anything along the lines of "open a release PR", "ship to prod", "cut a release", "PR into main", "promote this branch", "ship this branch", "release this", or any variant of opening/preparing a PR that targets `main`. Prefer this skill over running `gh pr create` directly — the manual flow misses Linear references, the version-bump check, and reviewer conventions.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Release PR into main
|
|
7
|
+
|
|
8
|
+
Use this when the user wants to open a release PR into `main` in the `connectedxm/client-sdk` repo (npm package `@connectedxm/client`). The head branch is whatever the user names in their prompt; if they don't name one, fall back to the currently checked-out branch — never assume `staging`. (Production is `main` here — the backend repo uses `prod`, but this SDK ships from `main`.)
|
|
9
|
+
|
|
10
|
+
Pushing to `main` triggers `publish.yml`, which publishes `@connectedxm/client` to npm. There is no manual gate — merging this PR auto-publishes the package, which then lands in `client-web`, `client-mobile`, and any other consumer on next install.
|
|
11
|
+
|
|
12
|
+
## What you're producing
|
|
13
|
+
|
|
14
|
+
A draft PR (or, on confirmation, an actual PR via `gh pr create`) with:
|
|
15
|
+
|
|
16
|
+
1. **Base/head**: base `main`, head = the source branch resolved in step 1 — never invent a branch that the user didn't name and isn't the current checkout
|
|
17
|
+
2. **Title** that reflects what's in the diff (see "Writing the title")
|
|
18
|
+
3. **Body** matching `.github/pull_request_template.md`, with Linear refs harvested from commits AND from any sub-PRs they reference, plus the Semantic Versioning section ticked
|
|
19
|
+
4. **Reviewers** drawn from the repo's collaborators (excluding the PR author)
|
|
20
|
+
|
|
21
|
+
The user has asked to **always confirm before creating** the PR. Show the draft, wait for the go-ahead, then push (if needed) and call `gh pr create`.
|
|
22
|
+
|
|
23
|
+
## Workflow
|
|
24
|
+
|
|
25
|
+
### 1. Resolve the branch and sync
|
|
26
|
+
|
|
27
|
+
Pick the head branch for the release PR:
|
|
28
|
+
|
|
29
|
+
- If the user named a branch in their prompt, use that.
|
|
30
|
+
- Otherwise, use the currently checked-out branch (`git rev-parse --abbrev-ref HEAD`).
|
|
31
|
+
|
|
32
|
+
If the resolved branch is `main`, stop and ask the user which branch to ship — you can't PR `main` into `main`. Treat the resolved name as `<source>` everywhere below.
|
|
33
|
+
|
|
34
|
+
Then sync and check there's something to ship:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
git fetch origin main <source>
|
|
38
|
+
git log --oneline origin/main..origin/<source>
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
If the log is empty, tell the user `<source>` has nothing ahead of `main` — there's no PR to open. Stop.
|
|
42
|
+
|
|
43
|
+
If `<source>` is behind `origin/<source>` locally, that's fine — the PR is opened against the remote refs, not your working copy. But mention it so the user knows.
|
|
44
|
+
|
|
45
|
+
If `<source>` exists only locally (not on `origin`), you'll need to push it before `gh pr create` will work. Note that for step 10; don't push yet.
|
|
46
|
+
|
|
47
|
+
If the branch already has an open PR against `main`, mention it (`gh pr list --head <source> --base main --state open`) and ask whether to add commits to that one or close it first. Don't open a duplicate.
|
|
48
|
+
|
|
49
|
+
### 2. Check the version bump (hard gate)
|
|
50
|
+
|
|
51
|
+
`version-check.yml` rejects any PR to `main` whose `package.json` version isn't strictly greater than main's, in plain `x.y.z` semver (no pre-release tags, no build metadata).
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
git show origin/main:package.json | node -p "JSON.parse(require('fs').readFileSync(0,'utf8')).version"
|
|
55
|
+
git show origin/<source>:package.json | node -p "JSON.parse(require('fs').readFileSync(0,'utf8')).version"
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Compare the two. If `<source>`'s version is **not** strictly greater than main's, stop and tell the user — they need to land a version bump on `<source>` first (recent precedent: `chore: Bump package version to X.Y.Z` commits) before this release PR can pass CI. Don't open the PR; don't bump for them.
|
|
59
|
+
|
|
60
|
+
**Watch for pre-release tags.** A branch sometimes carries something like `8.1.7-beta.2` mid-cycle. That string is rejected by `version-check.yml` outright (it's not plain `x.y.z`), so even if it's numerically greater than main, the workflow will fail. Flag this to the user — they need a plain `x.y.z` bump first.
|
|
61
|
+
|
|
62
|
+
If the bump looks right, note which kind it is (major/minor/patch) — you'll tick the matching box in the body's Semantic Versioning section.
|
|
63
|
+
|
|
64
|
+
### 3. Gather the commits
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
git log origin/main..origin/<source> --pretty=format:'%H%x09%s%x09%b%x1e'
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
The `%x1e` (ASCII record separator) lets you split multi-line commit bodies cleanly. You want the subject AND the body — Linear references like `closes ENG-1901` usually live in the body, not the subject.
|
|
71
|
+
|
|
72
|
+
Skip merge commits whose subject starts with `Merge branch` (e.g. `Merge branch 'staging'`, `Merge branch 'main'`) — they're noise from local merges, not a unit of work.
|
|
73
|
+
|
|
74
|
+
### 4. Resolve sub-PRs referenced in commit subjects
|
|
75
|
+
|
|
76
|
+
Squash-merged PRs leave a trail like `feat(activations): expose imageUpload flag and completion image fields (#561)`. The `(#NNNN)` is a real PR with its own body that often holds the Linear reference. Pull each one:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
gh api repos/connectedxm/client-sdk/pulls/<NNNN> --jq '{title, body}'
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Do this for every `(#NNNN)` you find in the commit subjects in step 3. These bodies feed into the Linear extraction below — and sometimes into the PR description summary, when the squashed commit message is terse.
|
|
83
|
+
|
|
84
|
+
### 5. Extract Linear ticket references
|
|
85
|
+
|
|
86
|
+
Linear refs in this repo look like `[A-Z]{2,5}-\d+` — both `ENG` and `CXM` show up regularly (the repo's PR template even defaults to `closes CXM-[Linear Ticket Number]`). They appear as:
|
|
87
|
+
|
|
88
|
+
- `closes ENG-1234`
|
|
89
|
+
- `ref CXM-1901`
|
|
90
|
+
- bare `ENG-1804` inside a Linear Issues bullet
|
|
91
|
+
|
|
92
|
+
**Important: don't confuse `(#561)` (a GitHub PR number) with `ENG-1234` (a Linear ticket).** Only the latter goes in the Linear Issues section.
|
|
93
|
+
|
|
94
|
+
**`linear-check.yml` is a hard gate.** It runs on every PR to `main` and requires at least one literal `ENG-\d+` somewhere in the PR body. The template's default `closes CXM-…` placeholder does **not** satisfy it. If you can only find `CXM-…` refs in the commits/sub-PRs, the workflow will fail — flag this to the user before opening so they can decide whether to add an `ENG-…` ref or update the workflow.
|
|
95
|
+
|
|
96
|
+
Collect refs from:
|
|
97
|
+
|
|
98
|
+
- the body of every commit in step 3
|
|
99
|
+
- the title and body of every sub-PR resolved in step 4
|
|
100
|
+
|
|
101
|
+
**Only include refs you actually saw in those sources for this PR.** Don't carry refs over from prior release PRs, recent conversation context, or memory of past releases — even if a ticket "feels relevant," it doesn't go in the list unless it's literally present in the commits/PRs you just gathered. Re-listing a ticket that was already shipped in a previous release is a real failure mode and confuses Linear's auto-close on merge.
|
|
102
|
+
|
|
103
|
+
Dedupe. Preserve the verb the user wrote when possible — if a sub-PR said `closes ENG-1901`, your output should say `closes ENG-1901`, not `ref`. Only fall back to `ref` if the original said `ref` or no verb at all.
|
|
104
|
+
|
|
105
|
+
### 6. Pick reviewers
|
|
106
|
+
|
|
107
|
+
There's no CODEOWNERS file in this repo. Build the candidate list at runtime:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
gh api repos/connectedxm/client-sdk/collaborators \
|
|
111
|
+
--jq '[.[] | select(.permissions.push == true) | .login]'
|
|
112
|
+
gh api user --jq '.login' # you, the PR author — exclude from candidates
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Use the unfiltered `collaborators` endpoint — do **not** add `?affiliation=direct`, because that excludes org admins (e.g., `swiftoO` is an admin and won't appear with the direct filter). The unfiltered endpoint plus a `permissions.push == true` jq filter gives you the right candidate set.
|
|
116
|
+
|
|
117
|
+
Take everyone with push permission, drop the author. Default to requesting all of them. If the user said something specific about reviewers in their prompt ("just Tyler", "skip Spencer"), honor that and don't ask.
|
|
118
|
+
|
|
119
|
+
If only one candidate remains, just use them — don't bother the user about it.
|
|
120
|
+
|
|
121
|
+
### 7. Write the title
|
|
122
|
+
|
|
123
|
+
Look at what's actually in the diff before reaching for a template. The right title depends on the shape of the changes.
|
|
124
|
+
|
|
125
|
+
**One dominant change:** copy the conventional-commit subject from the main commit/PR. Example: `feat(EventConfig): add BUILD_MODE flag`.
|
|
126
|
+
|
|
127
|
+
**Several unrelated changes:** use a short generic title like `Release fixes` or a noun phrase that captures the theme. Look at recent release PRs (`gh pr list --base main --state merged --limit 10`) to match the house style — they oscillate between conventional-commit subjects (`feat(EventConfig): add BUILD_MODE flag`) and short noun phrases (`Image upload on activation`), both are fine.
|
|
128
|
+
|
|
129
|
+
**Version-bump-only releases.** When the only thing in the diff is `chore: Bump package version to X.Y.Z`, a short title like `Release X.Y.Z` is fine.
|
|
130
|
+
|
|
131
|
+
Keep it under ~70 characters.
|
|
132
|
+
|
|
133
|
+
### 8. Write the body
|
|
134
|
+
|
|
135
|
+
Use this exact template — it matches `.github/pull_request_template.md`:
|
|
136
|
+
|
|
137
|
+
```markdown
|
|
138
|
+
### Description
|
|
139
|
+
|
|
140
|
+
<2-4 sentence summary of what's shipping. Group by theme if there are several unrelated changes — bullet list is fine when that's clearer than prose. Lead with SDK-visible changes (new hooks, new fields on response/params types, new error codes, new interface members, new provider callbacks), since this PR triggers an npm publish of `@connectedxm/client` and the change will land in `client-web`, `client-mobile`, and any other consumer on next install.>
|
|
141
|
+
|
|
142
|
+
### Linear Issues
|
|
143
|
+
|
|
144
|
+
- closes ENG-1234
|
|
145
|
+
- ref CXM-1901
|
|
146
|
+
<one line per ticket; use the verb the source used. Must include at least one ENG-#### or linear-check.yml will fail.>
|
|
147
|
+
|
|
148
|
+
### Testing
|
|
149
|
+
|
|
150
|
+
<Generated testing plan — see "Writing the testing plan" below. Do NOT use the template's default `I have manually tested the changes / New integration tests have been added` checkboxes; replace them with concrete steps grounded in the actual diff.>
|
|
151
|
+
|
|
152
|
+
### Semantic Versioning
|
|
153
|
+
|
|
154
|
+
Indicate the appropriate version bump for this PR:
|
|
155
|
+
|
|
156
|
+
- [ ] Breaking changes - Major version bump
|
|
157
|
+
- [ ] New features / improvements - Minor version bump
|
|
158
|
+
- [ ] Bug fixes - Patch version bump
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
**Tick the Semantic Versioning box** that matches the actual version delta you noted in step 2 (`X.Y.0` → minor, `X.Y.Z` patch bump only → patch, major version bump → major). If multiple things shipped, tick the highest-impact box (one breaking change makes the whole release major).
|
|
162
|
+
|
|
163
|
+
**Writing the testing plan.** Replace the template's default Testing checkboxes with a per-PR plan derived from what actually changed. The reviewer should be able to read the plan and know exactly what to verify before merging triggers the npm publish. Aim for 3–7 concrete checkbox items.
|
|
164
|
+
|
|
165
|
+
Walk the file diff (`git diff --name-only origin/main..origin/<source>`) and turn it into specific verification steps. This is a TypeScript React-hooks SDK consumed by other apps (notably `client-web` and `client-mobile`), so most verification happens via type-check + a smoke pass from a consumer mounting `ConnectedProvider`.
|
|
166
|
+
|
|
167
|
+
- **Query files** (`src/queries/<resource>/use*.ts`) → "Confirm `useGet*` returns the new field/shape; spot-check the `*_QUERY_KEY` helper and that 401/403/404 plus custom codes 453–467 route through `onNotAuthorized` / `onNotFound` / `onModuleForbidden` as expected (the custom codes must be in `CUSTOM_ERROR_CODES` in `utilities/GetErrorMessage.ts` or they'll silently retry 3×)."
|
|
168
|
+
- **Mutation files** (`src/mutations/<resource>/use*.ts`) → "Run the mutation from a consumer app and confirm the listed query keys invalidate / `SET_*_QUERY_DATA` updates as expected; on failure confirm `onMutationError` fires."
|
|
169
|
+
- **Interface changes** (`src/interfaces.ts`) → "Run `npm run lint` (which runs `tsc` + ESLint) and `npm run build`; `npm pack` the SDK and install into `client-web` (and `client-mobile` if relevant) — confirm the new fields surface in IDE completions and `tsc` is clean on the consumer side."
|
|
170
|
+
- **Param/input shape changes** → "Confirm callers in `client-web` / `client-mobile` still type-check against the new params; if a field became required, the consumer-side update needs to land before publish."
|
|
171
|
+
- **`ConnectedProvider` / context changes** (`src/ConnectedProvider.tsx`, `src/hooks/useConnected.ts`) → "Mount a consumer with the updated provider and confirm `clientApiParams`, `organizationId`, `getToken`, `getExecuteAs`, `locale`, and the `onNotAuthorized` / `onModuleForbidden` / `onNotFound` / `onMutationError` callbacks still wire through. Remember `react-use-websocket` is passed in as a prop (optional peer dep) — don't add a hard import."
|
|
172
|
+
- **`ClientAPI` / axios changes** (`src/ClientAPI.ts`) → "Confirm `GetClientAPI(params)` returns an axios instance with the expected `baseURL`, `organization`, `authorization`, `api-key`, and `executeAs` headers (when set)."
|
|
173
|
+
- **Query wrapper changes** (`src/queries/useConnectedSingleQuery.ts`, `useConnectedInfiniteQuery.ts`, `useConnectedCursorQuery.ts`, `src/mutations/useConnectedMutation.ts`) → "Confirm the wrapper still injects `clientApiParams` from context, prepends the locale (and `search` for infinite/cursor) to the query key, and routes 401/403/404 plus 453–467 to the right context callbacks without retrying."
|
|
174
|
+
- **WebSocket pipeline changes** (`src/websockets/useConnectedWebsocket.tsx`, `src/websockets/{chat,threads,stream}/`) → "From a consumer, open a socket and confirm incoming `chat.message.*` / `thread.message.*` / `stream.chat.*` / `stream.connected` / `stream.disconnected` / `pulse` events route to the right handler and mutate the React Query cache as expected. New event types must be wired up in **both** the `useConnectedWebsocket` switch and a new handler file — verify both."
|
|
175
|
+
- **Custom error code changes** (`src/utilities/GetErrorMessage.ts`) → "Confirm any new status added in the 453–467 range is included in `CUSTOM_ERROR_CODES` so the query wrappers treat it like 403 (calls `onModuleForbidden`, does not retry). A missing entry causes a silent 3× retry and surfaces the wrong handler."
|
|
176
|
+
- **Index/barrel changes** (`src/index.ts`, per-folder `index.ts`) → "Run `npm run build` and inspect `dist/index.d.ts` to confirm new symbols are exported. The single import surface is `src/index.ts` (re-exports `hooks`, `interfaces`, `utilities`, `queries`, `mutations`, `ClientAPI`, `ConnectedProvider`) — keep that shape."
|
|
177
|
+
- **CI workflow changes** (`.github/workflows/`) → "Confirm `tests.yml`, `linear-check.yml`, `version-check.yml`, `publish.yml`, or `approve.yml` pass on this branch."
|
|
178
|
+
- **Publish gate** → "After merge, confirm `publish.yml` runs cleanly on `main` and `@connectedxm/client@X.Y.Z` is live on npm."
|
|
179
|
+
|
|
180
|
+
Group related items where it tightens the plan. If a single domain dominates the diff (e.g., the whole PR is interface additions for activations), it's fine to have all 3–7 items in that domain. If you genuinely cannot derive a plan from the diff (rare — usually means something's wrong with how you read the commits), fall back to the original checkbox pair, but flag this to the user when presenting the draft.
|
|
181
|
+
|
|
182
|
+
Format as a markdown checklist:
|
|
183
|
+
|
|
184
|
+
```markdown
|
|
185
|
+
### Testing
|
|
186
|
+
|
|
187
|
+
- [ ] Run `npm run lint` and `npm run build` and confirm both pass cleanly.
|
|
188
|
+
- [ ] `npm pack` the SDK and install into a `client-web` checkout — confirm `tsc` passes on the consumer with the new types and the new fields surface in IDE completions.
|
|
189
|
+
- [ ] On a staging consumer, mount `ConnectedProvider` and exercise the affected mutation/query — confirm the new fields land in the cache and the relevant `*_QUERY_KEY` invalidates correctly.
|
|
190
|
+
- [ ] Open a WebSocket from a consumer (if the diff touches `src/websockets/`) and confirm the new/changed event type routes to the right handler.
|
|
191
|
+
- [ ] After merge, confirm `publish.yml` succeeds and `@connectedxm/client@X.Y.Z` appears on npm.
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
If there are zero Linear references, omit the bullet list and write `- (none)` under the Linear Issues heading rather than deleting the section — keeps the template recognizable. (But: `linear-check.yml` requires at least one `ENG-####` in the body, so this case will fail CI. Flag it to the user before opening.)
|
|
195
|
+
|
|
196
|
+
### 9. Show the draft and wait
|
|
197
|
+
|
|
198
|
+
Print the title, body, reviewer list, the version delta from step 2, AND the push state from step 1 back to the user, clearly labelled. If a push is needed (`<source>` not on origin or local commits ahead of `origin/<source>`), state explicitly that creating the PR will push first. Ask whether to proceed, modify, or cancel. Don't run `git push` or `gh pr create` until they've said yes.
|
|
199
|
+
|
|
200
|
+
The reason for confirming: the title and body are interpretive — you're summarizing several commits' worth of work. The user knows things you don't (which change is the headline feature, which is incidental cleanup, whether the Semantic Versioning box is right, whether something is already covered by another ticket). Five seconds of their attention up front beats editing the PR after the fact — especially since merging this PR triggers an npm publish.
|
|
201
|
+
|
|
202
|
+
### 10. Push the branch (if needed) and create the PR
|
|
203
|
+
|
|
204
|
+
After confirmation:
|
|
205
|
+
|
|
206
|
+
```bash
|
|
207
|
+
# Only if step 1 said the branch needed pushing:
|
|
208
|
+
git push -u origin <source> # first push
|
|
209
|
+
# OR
|
|
210
|
+
git push # subsequent push
|
|
211
|
+
|
|
212
|
+
# Then:
|
|
213
|
+
gh pr create \
|
|
214
|
+
--base main \
|
|
215
|
+
--head <source> \
|
|
216
|
+
--title "<title>" \
|
|
217
|
+
--reviewer "<comma,separated,logins>" \
|
|
218
|
+
--body "$(cat <<'EOF'
|
|
219
|
+
<body here>
|
|
220
|
+
EOF
|
|
221
|
+
)"
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Return the PR URL from the command output so the user can click straight to it.
|
|
225
|
+
|
|
226
|
+
## Things to watch out for
|
|
227
|
+
|
|
228
|
+
- **`main`, not `prod`.** This repo's production branch is `main` (the backend repo uses `prod`). Don't paste backend instincts here — `gh pr create --base prod` will fail.
|
|
229
|
+
- **Don't assume `staging`.** Most feature work now branches off `main` and PRs directly back into `main`. Resolve the head branch from the user's prompt or the current checkout — never default to `staging`.
|
|
230
|
+
- **Merging this PR publishes `@connectedxm/client` to npm.** `publish.yml` runs on push to `main` and publishes the package directly. There is no manual gate. Take the testing plan seriously — the change lands in `client-web`, `client-mobile`, and any other consumer on next install.
|
|
231
|
+
- **The version bump is a hard gate.** If `<source>`'s `package.json` version isn't strictly greater than main's in plain `x.y.z` semver, `version-check.yml` will fail. Pre-release tags like `8.1.7-beta.2` are explicitly rejected. Don't open the PR until the bump is on the source branch in plain semver.
|
|
232
|
+
- **`linear-check.yml` requires `ENG-####` in the body.** A `CXM-####`-only body will fail CI — and the repo's PR template defaults to `closes CXM-[Linear Ticket Number]`, which is a known footgun. If you only found `CXM` refs in the source commits/PRs, surface that to the user before opening.
|
|
233
|
+
- **Don't paste `<!-- CURSOR_SUMMARY -->` blocks** from sub-PR bodies into the new PR body. Those are auto-generated by the Cursor Bugbot reviewer and re-including them looks weird.
|
|
234
|
+
- **Don't include the HTML comment placeholders** from the PR template (`<!-- A brief description -->`) in your output — replace them with real content.
|
|
235
|
+
- **Keep the Semantic Versioning section.** Don't drop it from the body — the template explicitly includes it. Tick the box that matches the actual version bump.
|
|
236
|
+
- **`closes` vs `ref`**: `closes` auto-closes the Linear ticket on merge to `main`. `ref` just links it. Preserve whichever the source commit/PR used. When in doubt, `ref` is the safer default — it doesn't change ticket state.
|
|
237
|
+
- **Bot reviews are not human reviews.** Don't infer reviewer preferences from `github-actions` or `cursor` reviews on past PRs — those are automated. Look at human reviewers (`authorAssociation: MEMBER`) only.
|
|
238
|
+
- **The author isn't a reviewer.** GitHub will reject `--reviewer <self>`. Always exclude `gh api user --jq '.login'` from the list.
|
|
239
|
+
- **Don't push without confirmation.** Pushing exposes work to the team and triggers CI. Bundle it with PR creation behind the user's go-ahead.
|
|
240
|
+
- **No second SDK to publish.** Unlike `admin-sdk`, this repo publishes exactly one package (`@connectedxm/client`). There is no generated `openapi.json` and no `sdks/typescript/` output — don't mention them in the body or testing plan.
|
|
241
|
+
|
|
242
|
+
## Why this exists
|
|
243
|
+
|
|
244
|
+
Opening these PRs by hand consistently misses Linear references buried in sub-PR bodies, drops the Semantic Versioning section, and either skips the version-bump check or has to back-patch it after CI fails. Because merging this PR auto-publishes the SDK to npm, the cost of a botched release is higher than for an app deploy — there's no easy "revert" once a version is live. Hitting the gh API once per sub-PR, comparing versions up front, and templating the body is mechanical work that's easy to get wrong when done manually but easy to get right in a script-shaped flow.
|
package/dist/index.d.ts
CHANGED
|
@@ -704,7 +704,8 @@ declare enum NotificationType {
|
|
|
704
704
|
GROUP_INVITATION = "GROUP_INVITATION",
|
|
705
705
|
GROUP_REQUEST_ACCEPTED = "GROUP_REQUEST_ACCEPTED",
|
|
706
706
|
CONTENT = "CONTENT",
|
|
707
|
-
SUPPORT_TICKET_MESSAGE = "SUPPORT_TICKET_MESSAGE"
|
|
707
|
+
SUPPORT_TICKET_MESSAGE = "SUPPORT_TICKET_MESSAGE",
|
|
708
|
+
MENTION = "MENTION"
|
|
708
709
|
}
|
|
709
710
|
interface BaseNotification {
|
|
710
711
|
id: string;
|
|
@@ -1572,6 +1573,7 @@ interface NotificationPreferences {
|
|
|
1572
1573
|
chatPush: boolean;
|
|
1573
1574
|
chatUnreadPush: boolean;
|
|
1574
1575
|
chatUnreadEmail: boolean;
|
|
1576
|
+
activityMentionPush: boolean;
|
|
1575
1577
|
eventReminderEmail: boolean;
|
|
1576
1578
|
activityNotificationPreference: OrganizationActivityPreference;
|
|
1577
1579
|
organizationAnnouncementEmail: boolean;
|
|
@@ -3070,6 +3072,7 @@ interface EventConfig {
|
|
|
3070
3072
|
HAS_QUESTIONS_STEP: boolean;
|
|
3071
3073
|
HAS_COUPONS: boolean;
|
|
3072
3074
|
ACTIVATIONS_DESCRIPTION: string | null;
|
|
3075
|
+
ACTIVATIONS_LABEL: string;
|
|
3073
3076
|
SHOW_ACTIVITIES: boolean;
|
|
3074
3077
|
SHOW_ACTIVATIONS: boolean;
|
|
3075
3078
|
SHOW_SESSIONS: boolean;
|
|
@@ -3948,6 +3951,7 @@ interface UpdateSelfNotificationPreferencesParams extends MutationParams {
|
|
|
3948
3951
|
chatPush?: boolean;
|
|
3949
3952
|
chatUnreadPush?: boolean;
|
|
3950
3953
|
chatUnreadEmail?: boolean;
|
|
3954
|
+
activityMentionPush?: boolean;
|
|
3951
3955
|
eventReminderEmail?: boolean;
|
|
3952
3956
|
activityNotificationPreference?: OrganizationActivityPreference;
|
|
3953
3957
|
organizationAnnouncementPush?: boolean;
|
package/dist/index.js
CHANGED
|
@@ -8404,6 +8404,7 @@ var NotificationType = /* @__PURE__ */ ((NotificationType2) => {
|
|
|
8404
8404
|
NotificationType2["GROUP_REQUEST_ACCEPTED"] = "GROUP_REQUEST_ACCEPTED";
|
|
8405
8405
|
NotificationType2["CONTENT"] = "CONTENT";
|
|
8406
8406
|
NotificationType2["SUPPORT_TICKET_MESSAGE"] = "SUPPORT_TICKET_MESSAGE";
|
|
8407
|
+
NotificationType2["MENTION"] = "MENTION";
|
|
8407
8408
|
return NotificationType2;
|
|
8408
8409
|
})(NotificationType || {});
|
|
8409
8410
|
var isTypeNotification = (notification) => {
|