woodsportal-client-sdk 1.1.5-dev.0 → 4.0.0-dev.1
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.
- package/CHANGELOG.md +109 -0
- package/README.md +171 -77
- package/dist/adapters/angular/index.d.ts +68 -5
- package/dist/adapters/angular/index.js +8 -7
- package/dist/adapters/angular/index.js.map +1 -1
- package/dist/adapters/react/index.d.ts +68 -5
- package/dist/adapters/react/index.js +7 -6
- package/dist/adapters/react/index.js.map +1 -1
- package/dist/adapters/vue/index.d.ts +68 -5
- package/dist/adapters/vue/index.js +8 -7
- package/dist/adapters/vue/index.js.map +1 -1
- package/dist/auth-utils-VT7HSLMA.js +3 -0
- package/dist/{auth-utils-A4WPJMPK.js.map → auth-utils-VT7HSLMA.js.map} +1 -1
- package/dist/authentication-BfYhAeMs.d.ts +463 -0
- package/dist/cache-purge-G5WkHckd.d.ts +236 -0
- package/dist/chunk-3FUHGFAQ.js +167 -0
- package/dist/chunk-3FUHGFAQ.js.map +1 -0
- package/dist/chunk-4CEUGRRD.js +2034 -0
- package/dist/chunk-4CEUGRRD.js.map +1 -0
- package/dist/chunk-4CTHZLKL.js +20 -0
- package/dist/chunk-4CTHZLKL.js.map +1 -0
- package/dist/chunk-5TLHHOP5.js +1424 -0
- package/dist/chunk-5TLHHOP5.js.map +1 -0
- package/dist/chunk-7KE6XWM5.js +95 -0
- package/dist/chunk-7KE6XWM5.js.map +1 -0
- package/dist/{chunk-Y5MRAAGK.js → chunk-AYTO6ND7.js} +3 -3
- package/dist/chunk-AYTO6ND7.js.map +1 -0
- package/dist/chunk-DZKNBN72.js +399 -0
- package/dist/chunk-DZKNBN72.js.map +1 -0
- package/dist/chunk-F7B4FHUQ.js +504 -0
- package/dist/chunk-F7B4FHUQ.js.map +1 -0
- package/dist/entries/auth.d.ts +68 -0
- package/dist/entries/auth.js +13 -0
- package/dist/entries/auth.js.map +1 -0
- package/dist/entries/crm.d.ts +218 -0
- package/dist/entries/crm.js +24 -0
- package/dist/entries/crm.js.map +1 -0
- package/dist/index-CCwMopD8.d.ts +38 -0
- package/dist/index.d.ts +415 -406
- package/dist/index.js +23 -1720
- package/dist/index.js.map +1 -1
- package/dist/use-sync-DpazhM4d.d.ts +60 -0
- package/dist/use-uploader-2F1zc7Cl.d.ts +23 -0
- package/package.json +53 -8
- package/dist/auth-utils-A4WPJMPK.js +0 -4
- package/dist/chunk-J7MDPY5P.js +0 -54
- package/dist/chunk-J7MDPY5P.js.map +0 -1
- package/dist/chunk-NB7AINV4.js +0 -35
- package/dist/chunk-NB7AINV4.js.map +0 -1
- package/dist/chunk-RDCT25UV.js +0 -1066
- package/dist/chunk-RDCT25UV.js.map +0 -1
- package/dist/chunk-Y5MRAAGK.js.map +0 -1
- package/dist/chunk-YLZA5S7A.js +0 -102
- package/dist/chunk-YLZA5S7A.js.map +0 -1
- package/dist/use-sync-LbURBOs_.d.ts +0 -29
package/CHANGELOG.md
CHANGED
|
@@ -5,10 +5,119 @@ All notable changes to **woodsportal-client-sdk** are documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [4.0.0] - 2026-06-06
|
|
9
|
+
|
|
10
|
+
### Removed (breaking)
|
|
11
|
+
|
|
12
|
+
- **`state/legacy/`** — legacy table UI store (`use-table-ui`, `create-store`, `table-stores`, `resolve-table-list-params`).
|
|
13
|
+
- **Flat root `api.*` shims** — only `api.auth`, `api.crm`, `api.navigation` remain.
|
|
14
|
+
- **Top-level navigation exports** — `url`, `routeParam`, `breadcrumbsDetails` (use `api.navigation.*`).
|
|
15
|
+
- **`config` export** — use `hubContext` / `setHubContext`.
|
|
16
|
+
- **`store.useTable` / `store.legacyTableUi`** — use `store.tableUi` or `useTableUi()` from framework adapters.
|
|
17
|
+
|
|
18
|
+
### Changed (breaking)
|
|
19
|
+
|
|
20
|
+
- **Table UI state:** canonical `state/crm/table-ui.ts` + `table-ui-actions.ts`; reactive hook **`useTableUi()`** (replaces `useLegacyTableUi`).
|
|
21
|
+
- **List mutations:** `api.crm.objects.list()` and `api.crm.pipelines.list()` **require** `payload.tableParams` (no global store fallback).
|
|
22
|
+
- **`setObjectsData`** accepts `{ stageId }` from resolved list params (board append no longer reads global legacy store).
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
|
|
26
|
+
- `store.tableUi: { store, actions }` on root `store` export.
|
|
27
|
+
- `features/crm/helpers/normalize-table-list-params.ts` — validates required `tableParams`.
|
|
28
|
+
- Tests updated for nested-only API surface.
|
|
29
|
+
|
|
30
|
+
### Migration (3.x → 4.0)
|
|
31
|
+
|
|
32
|
+
| 3.x | 4.0 |
|
|
33
|
+
|-----|-----|
|
|
34
|
+
| `useLegacyTableUi()` | `useTableUi()` |
|
|
35
|
+
| `store.legacyTableUi()` | `store.tableUi.actions` / `useTableUi()` |
|
|
36
|
+
| `api.login()` | `api.auth.login()` |
|
|
37
|
+
| `url.makeLink()` | `api.navigation.url.makeLink()` |
|
|
38
|
+
| `list({ hubspotObjectTypeId })` | `list({ hubspotObjectTypeId, tableParams: getTableParam() })` |
|
|
39
|
+
|
|
40
|
+
See [docs/PUBLIC-API-NAMING.md](docs/PUBLIC-API-NAMING.md).
|
|
41
|
+
|
|
42
|
+
## [3.0.0] - 2026-06-06
|
|
43
|
+
|
|
44
|
+
### Changed (breaking)
|
|
45
|
+
|
|
46
|
+
- **Nested public API:** `api.auth`, `api.crm`, `api.navigation` replace the flat `api.*` spread as the canonical surface. See [docs/PUBLIC-API-NAMING.md](docs/PUBLIC-API-NAMING.md).
|
|
47
|
+
- **Mutation aliases:** primary alias matches nested leaf (`api.crm.objects.list()` → `{ list, mutate, isLoading }`).
|
|
48
|
+
- **Session helpers:** `api.auth.session.isAuthenticated()`, `refreshAccessToken()`, `isAccessTokenExpired()` (flat names deprecated).
|
|
49
|
+
- **Profile update:** nested key `api.auth.updateProfile()` (was `profileUpdate`).
|
|
50
|
+
- **Store:** `store.legacyTableUi` (was `store.useTable` — shim retained until 4.0).
|
|
51
|
+
- **Hub context:** prefer `hubContext` export; `config` deprecated.
|
|
52
|
+
|
|
53
|
+
### Added
|
|
54
|
+
|
|
55
|
+
- [docs/PUBLIC-API-NAMING.md](docs/PUBLIC-API-NAMING.md) — full 2.x→3.x migration matrix.
|
|
56
|
+
- `src/test/apis/nested-api.test.ts`, `flat-api-governance.test.ts` — nested paths + shim registry guards.
|
|
57
|
+
- Concurrent `me()` dedup (in-flight share).
|
|
58
|
+
|
|
59
|
+
### Deprecated (remove in 4.0)
|
|
60
|
+
|
|
61
|
+
- Flat root `api.*` keys (`api.objects`, `api.login`, …) — delegate to nested paths.
|
|
62
|
+
- Top-level `url`, `routeParam`, `breadcrumbsDetails` — use `api.navigation.*`.
|
|
63
|
+
- `store.useTable`, `config` export.
|
|
64
|
+
|
|
65
|
+
### Migration
|
|
66
|
+
|
|
67
|
+
See [docs/PUBLIC-API-NAMING.md](docs/PUBLIC-API-NAMING.md) for the full table. Examples:
|
|
68
|
+
|
|
69
|
+
| 2.x | 3.0 |
|
|
70
|
+
|-----|-----|
|
|
71
|
+
| `api.login()` | `api.auth.login()` |
|
|
72
|
+
| `api.objects()` / `{ getObjects }` | `api.crm.objects.list()` / `{ list }` |
|
|
73
|
+
| `api.getAccessToken()` | `api.auth.session.getAccessToken()` |
|
|
74
|
+
| `url.makeLink()` | `api.navigation.url.makeLink()` |
|
|
75
|
+
|
|
76
|
+
## [2.0.0] - 2026-06-05
|
|
77
|
+
|
|
78
|
+
### Changed (breaking)
|
|
79
|
+
|
|
80
|
+
- **Folder structure:** layered layout — `core/`, `features/auth|crm|navigation`, `state/crm/`. See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md).
|
|
81
|
+
- **Subpath entries:** `woodsportal-client-sdk/auth` and `/crm` no longer re-import the monolithic `index.ts` graph.
|
|
82
|
+
- **Navigation URL helpers:** `url.useMakeLink` → `url.makeLink`, `url.useUpdateLink` → `url.updateLink` (factory functions; not React hooks).
|
|
83
|
+
|
|
84
|
+
### Removed (breaking)
|
|
85
|
+
|
|
86
|
+
- `clint` namespace export
|
|
87
|
+
- `http-clint.ts`, `localStoraget.ts` typo shims
|
|
88
|
+
- `routing/route-param.ts` (use `routeParam` from main or `/crm` entry)
|
|
89
|
+
|
|
90
|
+
### Migration
|
|
91
|
+
|
|
92
|
+
| Before (1.x) | After (2.0) |
|
|
93
|
+
| ----------------------------------------------------- | ---------------------------- |
|
|
94
|
+
| `url.useMakeLink()` | `url.makeLink()` |
|
|
95
|
+
| `url.useUpdateLink()` | `url.updateLink()` |
|
|
96
|
+
| `import … from 'woodsportal-client-sdk'` (deep paths) | Use documented subpaths only |
|
|
97
|
+
| `clint` | `Client` from main export |
|
|
98
|
+
|
|
99
|
+
### Added
|
|
100
|
+
|
|
101
|
+
- [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md), [CONTRIBUTING.md](CONTRIBUTING.md), [SECURITY.md](SECURITY.md)
|
|
102
|
+
- CRM entry bundle budget in `npm run size:check`
|
|
103
|
+
- `examples/smoke-consumer/smoke-auth-only.mjs` — auth subpath isolation smoke
|
|
104
|
+
|
|
8
105
|
## [Unreleased]
|
|
9
106
|
|
|
10
107
|
### Added
|
|
11
108
|
|
|
109
|
+
- **Client-lane MFA APIs:** `verifyOtp`, `sendMfaOtp`, pending passkey MFA step, enrollment (TOTP, phone verify, WebAuthn/passkeys), passwordless passkey login, and `getMfaStatus` / `setMfaPreferences`.
|
|
110
|
+
- **Client-lane security settings APIs:** `getSecurityOverview`, `getSecurityLoginActivity`, `getSecuritySessions`, `revokeSecuritySession`, `revokeOtherSecuritySessions`.
|
|
111
|
+
- **MFA-aware login:** `api.login()` stores only the temp access JWT when `twoFactorRequired` is true (no refresh token until MFA completes).
|
|
112
|
+
- Exported TypeScript types for MFA and security DTOs (`MfaMethod`, `SecurityOverview`, `ActiveSession`, etc.).
|
|
113
|
+
- **Docs:** [`docs/MFA-SECURITY-SDK.md`](docs/MFA-SECURITY-SDK.md) — full SDK method → API reference with examples; JSDoc on all MFA/security facades for IDE hover.
|
|
114
|
+
|
|
115
|
+
### Changed
|
|
116
|
+
|
|
117
|
+
- **Source layout:** production code under `src/main/`, unit tests under `src/test/` (Spring Boot / Maven mirror). Update local imports or docs that referenced `src/` directly.
|
|
118
|
+
|
|
119
|
+
### Added
|
|
120
|
+
|
|
12
121
|
- **`formatHubSpotActivityDateTime`**, **`formatHubSpotActivityDateTimeParts`**, **`formatGmtOffset`**, **`normalizeToTimestamp`**, and **`DEFAULT_HUBSPOT_TIMEZONE`** (`Asia/Kolkata`) for HubSpot-style activity timestamps (e.g. `May 27, 2026 at 11:31 PM GMT+5:30`).
|
|
13
122
|
|
|
14
123
|
### Added
|
package/README.md
CHANGED
|
@@ -2,13 +2,30 @@
|
|
|
2
2
|
|
|
3
3
|
TypeScript/JavaScript **ESM** client for the **WoodsPortal** HTTP API: authentication, SSO, users, pipelines, HubSpot-aligned objects, notes, emails, uploads, and files.
|
|
4
4
|
|
|
5
|
-
|
|
|
6
|
-
|
|
7
|
-
| **npm**
|
|
8
|
-
| **Source**
|
|
9
|
-
| **Issues**
|
|
10
|
-
| **Runtime** | Node **≥ 18**; **ESM only** (`"type": "module"` in consuming apps is recommended)
|
|
11
|
-
| **License** | **ISC** — see [`LICENSE`](./LICENSE)
|
|
5
|
+
| | |
|
|
6
|
+
| ----------- | --------------------------------------------------------------------------------------------------------------------------------- |
|
|
7
|
+
| **npm** | [`woodsportal-client-sdk`](https://www.npmjs.com/package/woodsportal-client-sdk) |
|
|
8
|
+
| **Source** | [`Digital-Woods/digitalwoods.io-woodsportal-client-sdk`](https://github.com/Digital-Woods/digitalwoods.io-woodsportal-client-sdk) |
|
|
9
|
+
| **Issues** | [GitHub Issues](https://github.com/Digital-Woods/digitalwoods.io-woodsportal-client-sdk/issues) |
|
|
10
|
+
| **Runtime** | Node **≥ 18**; **ESM only** (`"type": "module"` in consuming apps is recommended) |
|
|
11
|
+
| **License** | **ISC** — see [`LICENSE`](./LICENSE) |
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Project layout (Spring Boot–style)
|
|
16
|
+
|
|
17
|
+
Production and test sources are split like WoodsPortal Java services:
|
|
18
|
+
|
|
19
|
+
| Path | Role |
|
|
20
|
+
| ----------- | ----------------------------------------------------------------------------------------------------------------- |
|
|
21
|
+
| `src/main/` | Library source — `core/`, `features/`, `state/`, `adapters/` (see [docs/ARCHITECTURE.md](./docs/ARCHITECTURE.md)) |
|
|
22
|
+
| `src/test/` | Unit tests mirroring `src/main/` package paths |
|
|
23
|
+
|
|
24
|
+
Example: `src/main/client/auth-headers.ts` ↔ `src/test/client/login-session.test.ts`.
|
|
25
|
+
|
|
26
|
+
See [`src/test/README.md`](src/test/README.md). Run tests with `npm test`.
|
|
27
|
+
|
|
28
|
+
**Contributors:** [docs/DEVELOPER-GUIDE.md](./docs/DEVELOPER-GUIDE.md) · [CONTRIBUTING.md](./CONTRIBUTING.md)
|
|
12
29
|
|
|
13
30
|
---
|
|
14
31
|
|
|
@@ -19,7 +36,7 @@ TypeScript/JavaScript **ESM** client for the **WoodsPortal** HTTP API: authentic
|
|
|
19
36
|
3. **Use HTTPS** for `baseURL` in every deployed environment.
|
|
20
37
|
4. **Do not log** passwords, refresh tokens, access tokens, or full API error bodies in production telemetry.
|
|
21
38
|
5. **Handle errors** with `try/await catch` around `mutate()`, and map user-visible text with **`getFormErrors`** / **`getFieldErrors`** when the API returns validation payloads.
|
|
22
|
-
6. **Align hub context** with how your shell stores HubSpot data (`utils/
|
|
39
|
+
6. **Align hub context** with how your shell stores HubSpot data (`core/utils/hub-context.ts` reads hub / dev-portal identifiers from app storage).
|
|
23
40
|
|
|
24
41
|
---
|
|
25
42
|
|
|
@@ -42,70 +59,71 @@ The SDK uses one shared Axios instance. If **`initializeHttpClient` has never be
|
|
|
42
59
|
Call **`initializeHttpClient` once** during application bootstrap (before any `api.*` calls):
|
|
43
60
|
|
|
44
61
|
```typescript
|
|
45
|
-
import { initializeHttpClient } from
|
|
62
|
+
import { initializeHttpClient } from 'woodsportal-client-sdk'
|
|
46
63
|
|
|
47
64
|
initializeHttpClient({
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
})
|
|
65
|
+
baseURL: process.env.VITE_API_BASE_URL!, // example: Vite — use your env mechanism
|
|
66
|
+
timeout: 50_000,
|
|
67
|
+
hubId: process.env.VITE_HUB_ID,
|
|
68
|
+
devPortalId: process.env.VITE_DEV_PORTAL_ID,
|
|
69
|
+
skipCurrentPublicPath: () => false,
|
|
70
|
+
routes: {
|
|
71
|
+
unauthorized: '/unauthorized',
|
|
72
|
+
login: '/login'
|
|
73
|
+
},
|
|
74
|
+
onLogout: async () => {
|
|
75
|
+
// Clear cookies / storage and route to login — implementation is app-specific
|
|
76
|
+
}
|
|
77
|
+
})
|
|
61
78
|
```
|
|
62
79
|
|
|
63
|
-
Hub and dev-portal identifiers are **also** read from browser storage in `
|
|
80
|
+
Hub and dev-portal identifiers are **also** read from browser storage in `core/utils/hub-context.ts`. Keep that storage in sync with your HubSpot / portal shell so authenticated routes resolve the correct tenant context.
|
|
64
81
|
|
|
65
82
|
---
|
|
66
83
|
|
|
67
|
-
## Public API
|
|
84
|
+
## Public API (3.0)
|
|
68
85
|
|
|
69
|
-
| Export
|
|
70
|
-
|
|
71
|
-
| **`api`**
|
|
72
|
-
| **`
|
|
73
|
-
| **`
|
|
74
|
-
| **`
|
|
75
|
-
| **`
|
|
76
|
-
| **`initializeHttpClient`**
|
|
77
|
-
| **`getFormErrors`**, **`getFieldErrors`** | Map Axios errors to form-level / field-level messages.
|
|
78
|
-
| **Types** | `LoginPayload`, `MutationOptions`, `ChangePasswordPayload`, … |
|
|
86
|
+
| Export | Purpose |
|
|
87
|
+
| ----------------------------------------- | -------------------------------------------------------------------------- |
|
|
88
|
+
| **`api.auth`** | Login, MFA, security, SSO, users, session helpers (`api.auth.session.*`). |
|
|
89
|
+
| **`api.crm`** | Pipelines, objects, notes, emails, files, uploads, cache purge. |
|
|
90
|
+
| **`api.navigation`** | URL factories (`makeLink`, `updateLink`), route params, breadcrumbs. |
|
|
91
|
+
| **`store`** | `storage`, CRM nanostores (`table`, `user`, …), `tableUi` (pagination UI). |
|
|
92
|
+
| **`hubContext`** | Hub / portal identifiers from browser storage. |
|
|
93
|
+
| **`initializeHttpClient`** | Axios base URL, timeouts, hub headers, auth callbacks. |
|
|
94
|
+
| **`getFormErrors`**, **`getFieldErrors`** | Map Axios errors to form-level / field-level messages. |
|
|
79
95
|
|
|
80
|
-
|
|
96
|
+
**4.0:** nested `api.*` only; `useTableUi()` + required `tableParams` on list mutations. See [CHANGELOG.md](./CHANGELOG.md).
|
|
97
|
+
|
|
98
|
+
Naming rules and migration: [docs/PUBLIC-API-NAMING.md](./docs/PUBLIC-API-NAMING.md).
|
|
81
99
|
|
|
82
100
|
---
|
|
83
101
|
|
|
84
|
-
## Mutation-style methods (`api.login`, …)
|
|
102
|
+
## Mutation-style methods (`api.auth.login`, …)
|
|
85
103
|
|
|
86
|
-
Most
|
|
104
|
+
Most nested factories wrap **`createMutation`**: invoke once with optional **`MutationOptions`**, then call the returned **`mutate`** or the **leaf alias** (e.g. **`login`**, **`list`**).
|
|
87
105
|
|
|
88
106
|
**Loading:** **`isLoading()`** returns whether **any** in-flight call exists for that factory (overlapping calls are supported).
|
|
89
107
|
|
|
90
108
|
**Errors:** **`onError`** runs when the request fails; the returned promise **still rejects** — use `try/catch` or `.catch()` in addition to `onError` when you need local control flow.
|
|
91
109
|
|
|
92
110
|
```typescript
|
|
93
|
-
import type { LoginPayload } from
|
|
94
|
-
import { api } from
|
|
95
|
-
|
|
96
|
-
const { login, mutate, isLoading } = api.login({
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
})
|
|
107
|
-
|
|
108
|
-
await login({ username:
|
|
111
|
+
import type { LoginPayload } from 'woodsportal-client-sdk'
|
|
112
|
+
import { api } from 'woodsportal-client-sdk'
|
|
113
|
+
|
|
114
|
+
const { login, mutate, isLoading } = api.auth.login({
|
|
115
|
+
onSuccess: async (data, payload) => {
|
|
116
|
+
// Persist session in app state if needed; tokens are handled inside the SDK login path
|
|
117
|
+
},
|
|
118
|
+
onError: (error, payload) => {
|
|
119
|
+
// Log a redacted message; map to UI state — avoid logging credentials
|
|
120
|
+
},
|
|
121
|
+
onLoadingChange: (loading) => {
|
|
122
|
+
// Drive a global or local spinner
|
|
123
|
+
}
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
await login({ username: 'user@example.com', password: '…' })
|
|
109
127
|
// `mutate` is identical to `login` here
|
|
110
128
|
```
|
|
111
129
|
|
|
@@ -114,23 +132,23 @@ await login({ username: "user@example.com", password: "…" });
|
|
|
114
132
|
## React example (login form)
|
|
115
133
|
|
|
116
134
|
```tsx
|
|
117
|
-
import type { LoginPayload } from
|
|
118
|
-
import { api } from
|
|
135
|
+
import type { LoginPayload } from 'woodsportal-client-sdk'
|
|
136
|
+
import { api } from 'woodsportal-client-sdk'
|
|
119
137
|
|
|
120
138
|
const { login } = api.login({
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
})
|
|
139
|
+
onSuccess: () => undefined,
|
|
140
|
+
onError: () => undefined,
|
|
141
|
+
onLoadingChange: () => undefined
|
|
142
|
+
})
|
|
125
143
|
|
|
126
144
|
export async function submitLogin(e: React.FormEvent<HTMLFormElement>) {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
145
|
+
e.preventDefault()
|
|
146
|
+
const formData = new FormData(e.currentTarget)
|
|
147
|
+
const payload: LoginPayload = {
|
|
148
|
+
username: String(formData.get('username') ?? ''),
|
|
149
|
+
password: String(formData.get('password') ?? '')
|
|
150
|
+
}
|
|
151
|
+
await login(payload)
|
|
134
152
|
}
|
|
135
153
|
```
|
|
136
154
|
|
|
@@ -178,8 +196,8 @@ npm link woodsportal-client-sdk
|
|
|
178
196
|
Consume with the same import paths as npm:
|
|
179
197
|
|
|
180
198
|
```ts
|
|
181
|
-
import { api } from
|
|
182
|
-
import { useTable, useSync } from
|
|
199
|
+
import { api } from 'woodsportal-client-sdk'
|
|
200
|
+
import { useTable, useSync } from 'woodsportal-client-sdk/react'
|
|
183
201
|
// import { useTable, useSync } from "woodsportal-client-sdk/vue";
|
|
184
202
|
// import { useTable, useSync } from "woodsportal-client-sdk/angular";
|
|
185
203
|
```
|
|
@@ -189,11 +207,11 @@ After `npm run build`, the main entry and framework adapters (`/react`, `/vue`,
|
|
|
189
207
|
### React
|
|
190
208
|
|
|
191
209
|
```tsx
|
|
192
|
-
import { useTable } from
|
|
210
|
+
import { useTable } from 'woodsportal-client-sdk/react'
|
|
193
211
|
|
|
194
212
|
function ObjectsTable() {
|
|
195
|
-
|
|
196
|
-
|
|
213
|
+
const table = useTable()
|
|
214
|
+
// table.tableData, table.setTableData(...)
|
|
197
215
|
}
|
|
198
216
|
```
|
|
199
217
|
|
|
@@ -203,9 +221,9 @@ Call composables inside `setup()` (or `<script setup>`):
|
|
|
203
221
|
|
|
204
222
|
```vue
|
|
205
223
|
<script setup lang="ts">
|
|
206
|
-
import { useTable } from
|
|
224
|
+
import { useTable } from 'woodsportal-client-sdk/vue'
|
|
207
225
|
|
|
208
|
-
const table = useTable()
|
|
226
|
+
const table = useTable()
|
|
209
227
|
</script>
|
|
210
228
|
```
|
|
211
229
|
|
|
@@ -214,17 +232,93 @@ const table = useTable();
|
|
|
214
232
|
Call composables in an injection context (constructor, field initializer, or `runInInjectionContext`):
|
|
215
233
|
|
|
216
234
|
```typescript
|
|
217
|
-
import { Component } from
|
|
218
|
-
import { useTable } from
|
|
235
|
+
import { Component } from '@angular/core'
|
|
236
|
+
import { useTable } from 'woodsportal-client-sdk/angular'
|
|
219
237
|
|
|
220
|
-
@Component({
|
|
238
|
+
@Component({
|
|
239
|
+
/* ... */
|
|
240
|
+
})
|
|
221
241
|
export class ObjectsTableComponent {
|
|
222
|
-
|
|
242
|
+
readonly table = useTable()
|
|
223
243
|
}
|
|
224
244
|
```
|
|
225
245
|
|
|
226
246
|
---
|
|
227
247
|
|
|
248
|
+
## Cache purge (CRM Sync)
|
|
249
|
+
|
|
250
|
+
Prefer **`POST /api/{hubId}/{portalId}/cache-purge-jobs`** over habitual `cache=false` on list reads. Requires `FEATURE_CACHE_PURGE_API_ENABLED` on the API.
|
|
251
|
+
|
|
252
|
+
| Export | Use |
|
|
253
|
+
| ------------------------------------------------------- | --------------------------------------------------- |
|
|
254
|
+
| `createCachePurgeJob` | POST + optional warm job poll |
|
|
255
|
+
| `buildCrmListPurgeTarget` / `buildCrmSinglePurgeTarget` | List or detail scope |
|
|
256
|
+
| `buildEngagementPurgeTarget` | `notes` / `emails` / `files` (requires `recordIds`) |
|
|
257
|
+
| `purgeCrmListCache` / `purgeEngagementCaches` | Convenience wrappers returning `PurgeResult` |
|
|
258
|
+
| `purgeCrmObjectDataCache` | Legacy list-only boolean shorthand |
|
|
259
|
+
|
|
260
|
+
API guide: `woodsportal-api/docs/CACHE-PURGE-API.md` in the monorepo. Types: `src/types/cache-purge.ts`; helpers: `src/utils/cache/`.
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## MFA & login (client lane)
|
|
265
|
+
|
|
266
|
+
When `POST /api/auth/login` returns `twoFactorRequired: true`, the SDK stores **only** the temporary access JWT — **not** the refresh token. Complete MFA with `api.verifyOtp()` (or pending passkey verify); on success the SDK persists the full session.
|
|
267
|
+
|
|
268
|
+
**Full guide:** [`docs/MFA-SECURITY-SDK.md`](docs/MFA-SECURITY-SDK.md) (every method, payload, and example).
|
|
269
|
+
|
|
270
|
+
Backend reference: `woodsportal-api/docs/MFA-FRONTEND-DEVELOPER-GUIDE.md`.
|
|
271
|
+
|
|
272
|
+
### Login & MFA step (unauthenticated)
|
|
273
|
+
|
|
274
|
+
| SDK method | HTTP | Purpose |
|
|
275
|
+
| --------------------------------------------------------------------- | --------------------------------------------------------------- | ---------------------------------------------------------- |
|
|
276
|
+
| `login({ username, password })` | `POST /api/auth/login?hubId=` | Password login; may return `twoFactorRequired` |
|
|
277
|
+
| `verifyOtp({ token, otp, method })` | `POST /api/auth/verify-otp?hubId=` | Complete OTP/TOTP/backup MFA step; full session on success |
|
|
278
|
+
| `sendMfaOtp({ token, method })` | `POST /api/auth/mfa/pending/otp/send` | Resend OTP or switch to email/SMS on MFA gate |
|
|
279
|
+
| `pendingPasskeyOptions({ token, portalId? })` | `POST /api/auth/mfa/pending/passkey/authenticate/options` | Start passkey MFA-step ceremony |
|
|
280
|
+
| `pendingPasskeyVerify({ token, challengeId, credential, portalId? })` | `POST /api/auth/mfa/pending/passkey/authenticate/verify?hubId=` | Finish passkey MFA step; full session on success |
|
|
281
|
+
| `passkeyLoginOptions({ email, hubId?, portalId? })` | `POST /api/auth/passkey/login/options?hubId=` | Passwordless passkey login start |
|
|
282
|
+
| `passkeyLoginVerify({ challengeId, credential, portalId? })` | `POST /api/auth/passkey/login/verify?hubId=` | Passwordless login finish; may still require MFA |
|
|
283
|
+
|
|
284
|
+
### MFA enrollment (authenticated)
|
|
285
|
+
|
|
286
|
+
| SDK method | HTTP | Purpose |
|
|
287
|
+
| --------------------------------------------------------------------------- | ------------------------------------------------------------ | ---------------------------------------- |
|
|
288
|
+
| `getMfaStatus({ portalId? })` | `GET /api/auth/mfa/status?portalId=` | Enrollment + policy snapshot |
|
|
289
|
+
| `setMfaPreferences({ defaultMethod, portalId? })` | `PUT /api/auth/mfa/preferences?portalId=` | Set scoped default MFA method |
|
|
290
|
+
| `startPhoneVerify({ phone })` | `POST /api/auth/mfa/phone/verify/start` | Send phone verification OTP (E.164) |
|
|
291
|
+
| `confirmPhoneVerify({ phone, code })` | `POST /api/auth/mfa/phone/verify/confirm` | Confirm phone; enables SMS at login |
|
|
292
|
+
| `totpEnrollStart({ portalId? })` | `POST /api/auth/mfa/totp/enroll/start?portalId=` | Start TOTP; returns QR/`otpauthUri` |
|
|
293
|
+
| `totpEnrollVerify({ code, portalId? })` | `POST /api/auth/mfa/totp/enroll/verify?portalId=` | Confirm TOTP; backup codes returned once |
|
|
294
|
+
| `totpDisable({ password })` | `POST /api/auth/mfa/totp/disable` | Disable TOTP for current scope |
|
|
295
|
+
| `webauthnRegisterOptions({ portalId? })` | `POST /api/auth/mfa/webauthn/register/options?portalId=` | Passkey registration ceremony |
|
|
296
|
+
| `webauthnRegisterVerify({ challengeId, credential, nickname?, portalId? })` | `POST /api/auth/mfa/webauthn/register/verify?portalId=` | Complete passkey registration |
|
|
297
|
+
| `webauthnAuthOptions({ portalId? })` | `POST /api/auth/mfa/webauthn/authenticate/options?portalId=` | Logged-in passkey re-verify |
|
|
298
|
+
| `webauthnAuthVerify({ challengeId, credential, portalId? })` | `POST /api/auth/mfa/webauthn/authenticate/verify?portalId=` | Complete logged-in passkey verify |
|
|
299
|
+
| `listWebauthnCredentials({ portalId? })` | `GET /api/auth/mfa/webauthn/credentials?portalId=` | List passkeys |
|
|
300
|
+
| `deleteWebauthnCredential({ credentialRecordId, portalId? })` | `DELETE /api/auth/mfa/webauthn/credentials/{id}?portalId=` | Remove a passkey |
|
|
301
|
+
|
|
302
|
+
WebAuthn ceremonies use `@simplewebauthn/browser` in the host app; the SDK transports credential JSON only.
|
|
303
|
+
|
|
304
|
+
---
|
|
305
|
+
|
|
306
|
+
## Security settings (client lane)
|
|
307
|
+
|
|
308
|
+
Use dedicated security endpoints for the account Security page — **not** `GET /me` + `GET /mfa/status`. Full contract: `woodsportal-api/docs/SECURITY-FRONTEND-DEVELOPER-GUIDE.md`. **Examples:** [`docs/MFA-SECURITY-SDK.md`](docs/MFA-SECURITY-SDK.md).
|
|
309
|
+
|
|
310
|
+
| SDK method | HTTP | Purpose |
|
|
311
|
+
| ---------------------------------------------------------- | ---------------------------------------------------- | --------------------------------------------- |
|
|
312
|
+
| `getSecurityOverview({ portalId? })` | `GET /api/auth/security/overview?portalId=` | Password age, MFA methods, policy flags |
|
|
313
|
+
| `getSecurityLoginActivity({ page?, limit?, sort? })` | `GET /api/auth/security/login-activity` | Paginated login history |
|
|
314
|
+
| `getSecuritySessions({ currentFamilyId?, refreshToken? })` | `GET /api/auth/security/sessions` | Active sessions; pass refresh to mark current |
|
|
315
|
+
| `revokeSecuritySession({ familyId, refreshToken? })` | `POST /api/auth/security/sessions/{familyId}/revoke` | Sign out one device |
|
|
316
|
+
| `revokeOtherSecuritySessions({ refreshToken? })` | `POST /api/auth/security/sessions/revoke-others` | Sign out all other devices |
|
|
317
|
+
|
|
318
|
+
Pass `refreshToken` (or use `getRefreshToken()` from SDK cookies) so the API can mark the current session when listing or revoking others.
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
228
322
|
## Security & privacy
|
|
229
323
|
|
|
230
324
|
- Send credentials and tokens **only over HTTPS** in production.
|
|
@@ -1,21 +1,80 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { M as MultiObjectTableState, U as UploaderState } from '../../use-uploader-2F1zc7Cl.js';
|
|
2
|
+
import { E as EmailState, N as NoteState, S as SyncState, a as TableState, T as TableUiState, U as UserState } from '../../use-sync-DpazhM4d.js';
|
|
2
3
|
|
|
3
4
|
declare const useTable: () => TableState & {
|
|
4
|
-
|
|
5
|
+
setObjectsQueryParams(params: any): void;
|
|
6
|
+
setMultiObjectsQueryParams(hubspotObjectTypeId: string, params: any): void;
|
|
7
|
+
setObjectsData(response: any, context?: {
|
|
8
|
+
stageId?: string | number;
|
|
9
|
+
}): Promise<void>;
|
|
5
10
|
setTableData(response: any, payload: any): void;
|
|
6
11
|
modifiedObjectsData(results: any): void;
|
|
7
12
|
clearTablePrependData(): void;
|
|
8
13
|
setTablePrependData(response: any, props?: any): Promise<void>;
|
|
9
14
|
};
|
|
15
|
+
declare const useTableUi: () => TableUiState & {
|
|
16
|
+
setTableUniqueId(v: string | null): void;
|
|
17
|
+
setSort(v: string): void;
|
|
18
|
+
setLimit(v: number): void;
|
|
19
|
+
setAfter(v: string): void;
|
|
20
|
+
setPage(v: number | string): void;
|
|
21
|
+
setNextPage(v: number | string): void;
|
|
22
|
+
setStageId(v: number | string): void;
|
|
23
|
+
setTotalItems(v: number): void;
|
|
24
|
+
setNumOfPages(v: number): void;
|
|
25
|
+
setCurrentPage(v: number): void;
|
|
26
|
+
setSearch(v: string): void;
|
|
27
|
+
setFilterPropertyName(v: string): void;
|
|
28
|
+
setFilterOperator(v: string): void;
|
|
29
|
+
setFilterValue(v: string): void;
|
|
30
|
+
setIsPrimaryCompany(v: boolean | null): void;
|
|
31
|
+
setTableFilterData(v: Record<string, unknown>): void;
|
|
32
|
+
setTableDefPermissions(v: Record<string, unknown>): void;
|
|
33
|
+
setView(mView: string | null): void;
|
|
34
|
+
changePipeline(mView: string | null): void;
|
|
35
|
+
setSelectedPipeline(pipelines: any[], pipeLineId?: string): void;
|
|
36
|
+
resetTableParam(): void;
|
|
37
|
+
getTableParam(companyAsMediator?: boolean, currentPageOverride?: number): {
|
|
38
|
+
after: string | number;
|
|
39
|
+
} | {
|
|
40
|
+
after?: string | undefined;
|
|
41
|
+
limit: number;
|
|
42
|
+
page: string | number;
|
|
43
|
+
};
|
|
44
|
+
buildListTableParams(options?: {
|
|
45
|
+
companyAsMediator?: boolean;
|
|
46
|
+
currentPageOverride?: number;
|
|
47
|
+
}): {
|
|
48
|
+
after: string | number;
|
|
49
|
+
} | {
|
|
50
|
+
after?: string | undefined;
|
|
51
|
+
limit: number;
|
|
52
|
+
page: string | number;
|
|
53
|
+
};
|
|
54
|
+
setGridData(type: string, deals: any[]): Promise<any[]>;
|
|
55
|
+
setDefaultPipeline(data: any, hubspotObjectTypeId: string): any;
|
|
56
|
+
};
|
|
57
|
+
declare const useMultiObjectActions: () => MultiObjectTableState & {
|
|
58
|
+
setMultiObjectData(response: any, payload: any): void;
|
|
59
|
+
clearMultiObjectPrependData(hubspotObjectTypeId?: string): void;
|
|
60
|
+
setMultiObjectPrependData(response: any, props?: any): Promise<void>;
|
|
61
|
+
};
|
|
10
62
|
declare const useNote: () => NoteState & {
|
|
63
|
+
setListQueryParams(params: any): void;
|
|
11
64
|
setNotes(response: any, payload: any): void;
|
|
12
65
|
setPrependNote(response: any): Promise<void>;
|
|
13
|
-
|
|
66
|
+
clearPrependNotes(): void;
|
|
67
|
+
updatePrependNote(response: any): Promise<any>;
|
|
14
68
|
};
|
|
15
69
|
declare const useEmail: () => EmailState & {
|
|
70
|
+
setListQueryParams(params: any): void;
|
|
16
71
|
setEmails(response: any, payload: any): void;
|
|
17
72
|
setPrependEmail(response: any): Promise<void>;
|
|
18
|
-
|
|
73
|
+
clearPrependEmails(): void;
|
|
74
|
+
updatePrependEmail(response: any): Promise<any>;
|
|
75
|
+
};
|
|
76
|
+
declare const useUser: () => UserState & {
|
|
77
|
+
setProfile(response: any): void;
|
|
19
78
|
};
|
|
20
79
|
declare const useSync: () => SyncState & {
|
|
21
80
|
setIsSyncLoading(status: boolean): void;
|
|
@@ -23,5 +82,9 @@ declare const useSync: () => SyncState & {
|
|
|
23
82
|
setApiSync(status: boolean): void;
|
|
24
83
|
setSyncDisable(status: boolean): void;
|
|
25
84
|
};
|
|
85
|
+
declare const useUploader: () => UploaderState & {
|
|
86
|
+
setAttachment(response: any): void;
|
|
87
|
+
clearAttachments(): void;
|
|
88
|
+
};
|
|
26
89
|
|
|
27
|
-
export { useEmail, useNote, useSync, useTable };
|
|
90
|
+
export { useEmail, useMultiObjectActions, useNote, useSync, useTable, useTableUi, useUploader, useUser };
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { bindStoreWithActions } from '../../chunk-
|
|
2
|
-
import { createAdapterHooks } from '../../chunk-
|
|
3
|
-
import '../../chunk-
|
|
4
|
-
import '../../chunk-
|
|
1
|
+
import { bindStoreWithActions } from '../../chunk-AYTO6ND7.js';
|
|
2
|
+
import { createAdapterHooks } from '../../chunk-4CTHZLKL.js';
|
|
3
|
+
import '../../chunk-7KE6XWM5.js';
|
|
4
|
+
import '../../chunk-5TLHHOP5.js';
|
|
5
|
+
import '../../chunk-3FUHGFAQ.js';
|
|
5
6
|
import { inject, DestroyRef, signal } from '@angular/core';
|
|
6
7
|
|
|
7
8
|
function createAngularStoreComposable(store, actions) {
|
|
@@ -26,9 +27,9 @@ function createAngularStoreComposable(store, actions) {
|
|
|
26
27
|
};
|
|
27
28
|
}
|
|
28
29
|
|
|
29
|
-
// src/adapters/angular/index.ts
|
|
30
|
-
var { useTable, useNote, useEmail, useSync } = createAdapterHooks(createAngularStoreComposable);
|
|
30
|
+
// src/main/adapters/angular/index.ts
|
|
31
|
+
var { useTable, useTableUi, useMultiObjectActions, useNote, useEmail, useUser, useSync, useUploader } = createAdapterHooks(createAngularStoreComposable);
|
|
31
32
|
|
|
32
|
-
export { useEmail, useNote, useSync, useTable };
|
|
33
|
+
export { useEmail, useMultiObjectActions, useNote, useSync, useTable, useTableUi, useUploader, useUser };
|
|
33
34
|
//# sourceMappingURL=index.js.map
|
|
34
35
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/adapters/shared/createAngularComposable.ts","../../../src/adapters/angular/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../../src/main/adapters/shared/createAngularComposable.ts","../../../src/main/adapters/angular/index.ts"],"names":[],"mappings":";;;;;;;AAGO,SAAS,4BAAA,CAA6E,OAAkC,OAAA,EAAmB;AAC9I,EAAA,OAAO,SAAS,QAAA,GAA8B;AAC1C,IAAA,MAAM,UAAA,GAAa,OAAO,UAAU,CAAA;AACpC,IAAA,MAAM,OAAA,GAAU,oBAAA,CAAqB,KAAA,EAAO,OAAO,CAAA;AACnD,IAAA,MAAM,QAAA,GAAW,MAAA,CAAO,OAAA,CAAQ,WAAA,EAAa,CAAA;AAE7C,IAAA,UAAA,CAAW,SAAA;AAAA,MACP,OAAA,CAAQ,UAAU,MAAM;AACpB,QAAA,QAAA,CAAS,GAAA,CAAI,OAAA,CAAQ,WAAA,EAAa,CAAA;AAAA,MACtC,CAAC;AAAA,KACL;AAEA,IAAA,OAAO,IAAI,KAAA,CAAM,EAAC,EAAwB;AAAA,MACtC,GAAA,CAAI,SAAS,IAAA,EAAM;AACf,QAAA,MAAM,GAAA,GAAM,OAAO,IAAI,CAAA;AACvB,QAAA,IAAI,OAAO,OAAA,EAAS;AAChB,UAAA,OAAQ,QAAoC,GAAG,CAAA;AAAA,QACnD;AACA,QAAA,OAAO,QAAA,GAAW,GAAmB,CAAA;AAAA,MACzC;AAAA,KACH,CAAA;AAAA,EACL,CAAA;AACJ;;;ACtBO,IAAM,EAAE,QAAA,EAAU,UAAA,EAAY,qBAAA,EAAuB,OAAA,EAAS,QAAA,EAAU,OAAA,EAAS,OAAA,EAAS,WAAA,EAAY,GACzG,kBAAA,CAAmB,4BAA4B","file":"index.js","sourcesContent":["import { DestroyRef, inject, signal } from '@angular/core'\nimport { bindStoreWithActions, type SubscribableStore } from './bindStoreWithActions'\n\nexport function createAngularStoreComposable<TState extends object, TActions extends object>(store: SubscribableStore<TState>, actions: TActions) {\n return function useStore(): TState & TActions {\n const destroyRef = inject(DestroyRef)\n const binding = bindStoreWithActions(store, actions)\n const snapshot = signal(binding.getSnapshot())\n\n destroyRef.onDestroy(\n binding.subscribe(() => {\n snapshot.set(binding.getSnapshot())\n })\n )\n\n return new Proxy({} as TState & TActions, {\n get(_target, prop) {\n const key = String(prop)\n if (key in actions) {\n return (actions as Record<string, unknown>)[key]\n }\n return snapshot()[key as keyof TState]\n }\n })\n }\n}\n","import { createAdapterHooks } from '../shared/createAdapterHooks'\nimport { createAngularStoreComposable } from '../shared/createAngularComposable'\n\nexport const { useTable, useTableUi, useMultiObjectActions, useNote, useEmail, useUser, useSync, useUploader } =\n createAdapterHooks(createAngularStoreComposable)\n"]}
|