@connectedxm/client 8.1.8 → 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/`
|
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;
|
|
@@ -3949,6 +3951,7 @@ interface UpdateSelfNotificationPreferencesParams extends MutationParams {
|
|
|
3949
3951
|
chatPush?: boolean;
|
|
3950
3952
|
chatUnreadPush?: boolean;
|
|
3951
3953
|
chatUnreadEmail?: boolean;
|
|
3954
|
+
activityMentionPush?: boolean;
|
|
3952
3955
|
eventReminderEmail?: boolean;
|
|
3953
3956
|
activityNotificationPreference?: OrganizationActivityPreference;
|
|
3954
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) => {
|