Skip to content
Open
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
18 changes: 9 additions & 9 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,26 +76,26 @@ src/components/
└── Flow/ # Multi-step flow orchestration
```

### API Layer (`@gusto/embedded-api`)
### API Layer (`@gusto/embedded-api-v-2025-11-15`)

All API calls go through `@gusto/embedded-api` with React Query hooks and Zod schema validation.
All API calls go through `@gusto/embedded-api-v-2025-11-15` with React Query hooks and Zod schema validation.

Import paths:

- `@gusto/embedded-api/react-query/<operation>` — React Query hooks
- `@gusto/embedded-api/models/components/<name>` — Entity types
- `@gusto/embedded-api/models/operations/<name>` — Request/response types
- `@gusto/embedded-api/models/errors/<name>` — Error types
- `@gusto/embedded-api-v-2025-11-15/react-query/<operation>` — React Query hooks
- `@gusto/embedded-api-v-2025-11-15/models/components/<name>` — Entity types
- `@gusto/embedded-api-v-2025-11-15/models/operations/<name>` — Request/response types
- `@gusto/embedded-api-v-2025-11-15/models/errors/<name>` — Error types

Hook naming: `use<Resource><Action>Suspense` (queries), `use<Resource><Action>Mutation` (mutations)

#### Auto-invalidation on mutation success

The `QueryClient` produced by `createSdkQueryClient` (in `src/contexts/ApiProvider/createSdkQueryClient.ts`) sets a global mutation default: on any successful mutation under the `['@gusto/embedded-api']` key, it invalidates **every** SDK query. Both `ApiProvider` (production) and `GustoTestProvider` (tests) use this factory, so the behavior is identical in both environments.
The `QueryClient` produced by `createSdkQueryClient` (in `src/contexts/ApiProvider/createSdkQueryClient.ts`) sets a global mutation default: on any successful mutation under the `['@gusto/embedded-api-v-2025-11-15']` key, it invalidates **every** SDK query. Both `ApiProvider` (production) and `GustoTestProvider` (tests) use this factory, so the behavior is identical in both environments.

Implications when writing SDK code:

- **Do not call `queryClient.invalidateQueries(...)` after a successful `@gusto/embedded-api` mutation.** It's redundant — the global `onSuccess` already invalidated the entire SDK namespace. Just `await mutateAsync(...)` and the next render's queries refetch automatically.
- **Do not call `queryClient.invalidateQueries(...)` after a successful `@gusto/embedded-api-v-2025-11-15` mutation.** It's redundant — the global `onSuccess` already invalidated the entire SDK namespace. Just `await mutateAsync(...)` and the next render's queries refetch automatically.
- This is why `usePaymentMethodList`, `useEmployeeCompensation`, etc. don't manually invalidate after their delete/update mutations.
- If a partner brings their own `QueryClient` to `ApiProvider`, the defaults are **not** applied to it — they're responsible for matching the contract if they want this behavior. Don't paper over that with manual invalidation in hooks; treat it as their responsibility.
- If you need to invalidate _more narrowly_ (e.g. you only want one query to refetch, not the whole namespace), that's a code smell — most likely the global invalidate is already doing what you want.
Expand All @@ -112,7 +112,7 @@ All user-facing text uses i18next. Run `npm run i18n:generate` after changing tr

### Partner hooks (`composeErrorHandler` / `composeSubmitHandler`)

Exported headless hooks build `errorHandling` with **`composeErrorHandler`** (not a React hook). For multi-form screens, **`composeSubmitHandler`** coordinates validation + ordered submits and returns `{ handleSubmit, errorHandling }` aggregated across those forms. The result plugs back into `composeErrorHandler` when partners need extra `@gusto/embedded-api` queries or screen-level submit state in the same error surface — see [docs/hooks/hooks.md](docs/hooks/hooks.md).
Exported headless hooks build `errorHandling` with **`composeErrorHandler`** (not a React hook). For multi-form screens, **`composeSubmitHandler`** coordinates validation + ordered submits and returns `{ handleSubmit, errorHandling }` aggregated across those forms. The result plugs back into `composeErrorHandler` when partners need extra `@gusto/embedded-api-v-2025-11-15` queries or screen-level submit state in the same error surface — see [docs/hooks/hooks.md](docs/hooks/hooks.md).

## PR and Commit Conventions

Expand Down
18 changes: 9 additions & 9 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,26 +76,26 @@ src/components/
└── Flow/ # Multi-step flow orchestration
```

### API Layer (`@gusto/embedded-api`)
### API Layer (`@gusto/embedded-api-v-2025-11-15`)

All API calls go through `@gusto/embedded-api` with React Query hooks and Zod schema validation.
All API calls go through `@gusto/embedded-api-v-2025-11-15` with React Query hooks and Zod schema validation.

Import paths:

- `@gusto/embedded-api/react-query/<operation>` — React Query hooks
- `@gusto/embedded-api/models/components/<name>` — Entity types
- `@gusto/embedded-api/models/operations/<name>` — Request/response types
- `@gusto/embedded-api/models/errors/<name>` — Error types
- `@gusto/embedded-api-v-2025-11-15/react-query/<operation>` — React Query hooks
- `@gusto/embedded-api-v-2025-11-15/models/components/<name>` — Entity types
- `@gusto/embedded-api-v-2025-11-15/models/operations/<name>` — Request/response types
- `@gusto/embedded-api-v-2025-11-15/models/errors/<name>` — Error types

Hook naming: `use<Resource><Action>Suspense` (queries), `use<Resource><Action>Mutation` (mutations)

#### Auto-invalidation on mutation success

The `QueryClient` produced by `createSdkQueryClient` (in `src/contexts/ApiProvider/createSdkQueryClient.ts`) sets a global mutation default: on any successful mutation under the `['@gusto/embedded-api']` key, it invalidates **every** SDK query. Both `ApiProvider` (production) and `GustoTestProvider` (tests) use this factory, so the behavior is identical in both environments.
The `QueryClient` produced by `createSdkQueryClient` (in `src/contexts/ApiProvider/createSdkQueryClient.ts`) sets a global mutation default: on any successful mutation under the `['@gusto/embedded-api-v-2025-11-15']` key, it invalidates **every** SDK query. Both `ApiProvider` (production) and `GustoTestProvider` (tests) use this factory, so the behavior is identical in both environments.

Implications when writing SDK code:

- **Do not call `queryClient.invalidateQueries(...)` after a successful `@gusto/embedded-api` mutation.** It's redundant — the global `onSuccess` already invalidated the entire SDK namespace. Just `await mutateAsync(...)` and the next render's queries refetch automatically.
- **Do not call `queryClient.invalidateQueries(...)` after a successful `@gusto/embedded-api-v-2025-11-15` mutation.** It's redundant — the global `onSuccess` already invalidated the entire SDK namespace. Just `await mutateAsync(...)` and the next render's queries refetch automatically.
- This is why `usePaymentMethodList`, `useEmployeeCompensation`, etc. don't manually invalidate after their delete/update mutations.
- If a partner brings their own `QueryClient` to `ApiProvider`, the defaults are **not** applied to it — they're responsible for matching the contract if they want this behavior. Don't paper over that with manual invalidation in hooks; treat it as their responsibility.
- If you need to invalidate _more narrowly_ (e.g. you only want one query to refetch, not the whole namespace), that's a code smell — most likely the global invalidate is already doing what you want.
Expand All @@ -112,7 +112,7 @@ All user-facing text uses i18next. Run `npm run i18n:generate` after changing tr

### Partner hooks (`composeErrorHandler` / `composeSubmitHandler`)

Exported headless hooks build `errorHandling` with **`composeErrorHandler`** (not a React hook). For multi-form screens, **`composeSubmitHandler`** coordinates validation + ordered submits and returns `{ handleSubmit, errorHandling }` aggregated across those forms. The result plugs back into `composeErrorHandler` when partners need extra `@gusto/embedded-api` queries or screen-level submit state in the same error surface — see [docs/hooks/hooks.md](docs/hooks/hooks.md).
Exported headless hooks build `errorHandling` with **`composeErrorHandler`** (not a React hook). For multi-form screens, **`composeSubmitHandler`** coordinates validation + ordered submits and returns `{ handleSubmit, errorHandling }` aggregated across those forms. The result plugs back into `composeErrorHandler` when partners need extra `@gusto/embedded-api-v-2025-11-15` queries or screen-level submit state in the same error surface — see [docs/hooks/hooks.md](docs/hooks/hooks.md).

## PR and Commit Conventions

Expand Down
18 changes: 10 additions & 8 deletions build/deriveEndpointInventory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ const __dirname = dirname(__filename)

const ROOT = join(__dirname, '..')
const SRC_DIR = join(ROOT, 'src')
const FUNCS_DIR = join(ROOT, 'node_modules/@gusto/embedded-api/src/funcs')
const OPS_DIR = join(ROOT, 'node_modules/@gusto/embedded-api/src/models/operations')
const FUNCS_DIR = join(ROOT, 'node_modules/@gusto/embedded-api-v-2025-11-15/src/funcs')
const OPS_DIR = join(ROOT, 'node_modules/@gusto/embedded-api-v-2025-11-15/src/models/operations')
const COMPONENTS_DIR = join(ROOT, 'src/components')
const JSON_OUTPUT_PATH = join(ROOT, 'docs/reference/endpoint-inventory.json')
const MD_OUTPUT_PATH = join(ROOT, 'docs/reference/endpoint-reference.md')
Expand Down Expand Up @@ -208,11 +208,11 @@ function collectTransitiveApiImports(
const funcNames = new Set<string>()

function followSpec(spec: string, getResolved: () => SourceFile | undefined) {
if (spec.startsWith('@gusto/embedded-api/react-query/')) {
const name = spec.slice('@gusto/embedded-api/react-query/'.length)
if (spec.startsWith('@gusto/embedded-api-v-2025-11-15/react-query/')) {
const name = spec.slice('@gusto/embedded-api-v-2025-11-15/react-query/'.length)
if (!name.startsWith('_')) funcNames.add(name)
} else if (spec.startsWith('@gusto/embedded-api/funcs/')) {
funcNames.add(spec.slice('@gusto/embedded-api/funcs/'.length))
} else if (spec.startsWith('@gusto/embedded-api-v-2025-11-15/funcs/')) {
funcNames.add(spec.slice('@gusto/embedded-api-v-2025-11-15/funcs/'.length))
} else if (spec.startsWith('.') || spec.startsWith('@/hooks/')) {
// Follow relative imports (catches ../shared/ hooks) and cross-cutting utility hooks.
// Deliberately skip @/components/, @/contexts/, @/helpers/ etc. to avoid
Expand Down Expand Up @@ -706,7 +706,9 @@ function validateEndpoints(
}

if (invalid.length > 0) {
console.error('WARNING: Some inventory endpoints were not found in @gusto/embedded-api:')
console.error(
'WARNING: Some inventory endpoints were not found in @gusto/embedded-api-v-2025-11-15:',
)
for (const ep of invalid) console.error(` ${ep}`)
console.error('')
}
Expand Down Expand Up @@ -778,7 +780,7 @@ function verify() {
console.error('This can happen when:')
console.error(' - A component added or removed an API hook/function import')
console.error(' - A flow added or removed a block component')
console.error(' - The @gusto/embedded-api package was updated')
console.error(' - The @gusto/embedded-api-v-2025-11-15 package was updated')
console.error('')
if (committedJson !== freshJson) printDiff(JSON_OUTPUT_PATH, freshJson)
if (committedMd !== freshMd) printDiff(MD_OUTPUT_PATH, freshMd)
Expand Down
12 changes: 6 additions & 6 deletions docs/hooks/hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ interface HookErrorHandling {

### Multi-hook screens

When a screen pulls from more than one SDK hook (or mixes SDK hooks with additional `@gusto/embedded-api` queries), combine their error state into one banner and one retry/dismiss flow using `composeErrorHandler` / `composeSubmitHandler`. See [Composing Multiple Hooks](#composing-multiple-hooks).
When a screen pulls from more than one SDK hook (or mixes SDK hooks with additional `@gusto/embedded-api-v-2025-11-15` queries), combine their error state into one banner and one retry/dismiss flow using `composeErrorHandler` / `composeSubmitHandler`. See [Composing Multiple Hooks](#composing-multiple-hooks).

### SDKError shape

Expand Down Expand Up @@ -554,7 +554,7 @@ Error codes for each hook are exported alongside the hook:

## Composing Multiple Hooks

A screen that combines multiple SDK hooks, or mixes SDK hooks with additional `@gusto/embedded-api` queries, produces multiple `errorHandling` objects and (for form screens) multiple submit flows. Two small helpers stitch them together:
A screen that combines multiple SDK hooks, or mixes SDK hooks with additional `@gusto/embedded-api-v-2025-11-15` queries, produces multiple `errorHandling` objects and (for form screens) multiple submit flows. Two small helpers stitch them together:

- **`composeErrorHandler([sources])`** — merges many error sources into a single `HookErrorHandling`.
- **`composeSubmitHandler([forms], onAllValid)`** — coordinates validation and ordered submits across forms, and returns `{ handleSubmit, errorHandling }` where `errorHandling` is built from those forms via `composeErrorHandler` under the hood.
Expand All @@ -564,11 +564,11 @@ A screen that combines multiple SDK hooks, or mixes SDK hooks with additional `@
Use `composeErrorHandler` to produce a single `errorHandling` bag for any screen that reads from multiple sources. It accepts any mix of:

- **SDK hook results** — objects with an `errorHandling` property (e.g., `useEmployeeDetailsForm`, `useCompensationForm`, or the return value of `composeSubmitHandler`).
- **`@gusto/embedded-api` React Query results** — objects with `error` and `refetch` properties.
- **`@gusto/embedded-api-v-2025-11-15` React Query results** — objects with `error` and `refetch` properties.

```tsx
import { composeErrorHandler, useEmployeeDetailsForm } from '@gusto/embedded-react-sdk'
import { useEmployeeFormsList } from '@gusto/embedded-api/react-query/employeeFormsList'
import { useEmployeeFormsList } from '@gusto/embedded-api-v-2025-11-15/react-query/employeeFormsList'

function EmployeeProfileView({ companyId, employeeId }: { companyId: string; employeeId: string }) {
const employeeDetails = useEmployeeDetailsForm({ companyId, employeeId })
Expand All @@ -591,7 +591,7 @@ function EmployeeProfileView({ companyId, employeeId }: { companyId: string; emp
}
```

`employeeDetails` is an SDK hook result (its `errorHandling` is delegated into), while `formsListQuery` is a raw `@gusto/embedded-api` query (its `error` is normalized and its `refetch` is wired into `retryQueries`). The same call works for any combination of the two shapes.
`employeeDetails` is an SDK hook result (its `errorHandling` is delegated into), while `formsListQuery` is a raw `@gusto/embedded-api-v-2025-11-15` query (its `error` is normalized and its `refetch` is wired into `retryQueries`). The same call works for any combination of the two shapes.

The returned `errorHandling` has the same shape as any SDK hook's `errorHandling`:

Expand All @@ -616,7 +616,7 @@ const { handleSubmit, errorHandling } = composeSubmitHandler(
)
```

If the same screen also has extra `@gusto/embedded-api` queries that should feed the same error banner, pass the `composeSubmitHandler` result back into `composeErrorHandler` alongside those queries — the result already satisfies `composeErrorHandler`'s input shape:
If the same screen also has extra `@gusto/embedded-api-v-2025-11-15` queries that should feed the same error banner, pass the `composeSubmitHandler` result back into `composeErrorHandler` alongside those queries — the result already satisfies `composeErrorHandler`'s input shape:

```tsx
const submitResult = composeSubmitHandler([employeeDetails, compensation], onAllValid)
Expand Down
2 changes: 1 addition & 1 deletion docs/hooks/useEmployeeStateTaxesForm.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ The non-primitive types in the Ready state are all re-exported from `@gusto/embe
| Type | What it is |
| ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `UseEmployeeStateTaxesFormReady` | The full Ready-state object (the discriminated `isLoading: false` branch). Use this as the prop type for components that receive a ready form, so you don't have to repeat the `Extract<...>` narrowing. |
| `EmployeeStateTaxesList` | API record for one state's tax answers, re-exported from `@gusto/embedded-api`. Each entry in `data.employeeStateTaxes` is one of these. |
| `EmployeeStateTaxesList` | API record for one state's tax answers, re-exported from `@gusto/embedded-api-v-2025-11-15`. Each entry in `data.employeeStateTaxes` is one of these. |
| `StateTaxFieldsGroup` | One state's render-ready bundle: `{ state, questions: StateTaxQuestionFieldEntry[] }`. The full shape is documented under [Fields shape](#fields-shape). |
| `StateTaxQuestionFieldEntry` | The discriminated entry for a single question — `type` + metadata + bound `Field` component. See [Fields shape](#fields-shape). |
| `EmployeeStateTaxesFieldsMetadata` | Static field metadata keyed by full form path (`states.<STATE>.<camelKey>`), with `isRequired` / `isDisabled` / option lists. Same shape as other SDK form hooks' `fieldsMetadata`. |
Expand Down
4 changes: 2 additions & 2 deletions docs/reference/jobs-and-compensations.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ A consolidated reference for the Embedded API's `Job` and `Compensation` resourc

All items in this doc are tied to verified behavior in:

- **`@gusto/embedded-api`** generated types (`JobsCreateRequestBody`, `JobsUpdateRequestBody`, `CompensationsRequestBody`, `CompensationsUpdateRequestBody`, `Job`, `Compensation`)
- **`@gusto/embedded-api-v-2025-11-15`** generated types (`JobsCreateRequestBody`, `JobsUpdateRequestBody`, `CompensationsRequestBody`, `CompensationsUpdateRequestBody`, `Job`, `Compensation`)
- **gws-flows** Rails controllers, Stimulus controllers, and ERB form views
- **zenpayroll** model code, validators, and partner facades (`JobFacade`, `CompensationFacade`)

Expand Down Expand Up @@ -46,7 +46,7 @@ Key consequences:

## 2. Endpoint reference

All hooks live under `@gusto/embedded-api/react-query/`. Mapping:
All hooks live under `@gusto/embedded-api-v-2025-11-15/react-query/`. Mapping:

| HTTP / Path | Purpose | React Query hook |
| ---------------------------------------------- | -------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
Expand Down
10 changes: 5 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@
"typescript": "^5.8.3"
},
"dependencies": {
"@gusto/embedded-api": "0.13.0",
"@gusto/embedded-api-v-2025-11-15": "^0.0.2",
"@hookform/error-message": "^2.0.1",
"@hookform/resolvers": "^5.2.2",
"@internationalized/date": "^3.12.1",
Expand Down
Loading
Loading