@archipelago-js/client 0.2.0 → 0.9.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.
- package/README.md +158 -5
- package/package.json +4 -5
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @archipelago-js/client
|
|
2
2
|
|
|
3
|
-
Core client
|
|
3
|
+
Core client library for [Archipelago](https://github.com/Catchhook/archipelago-rails) — the low-level fetch, response parsing, and error handling used by `@archipelago-js/react` and available standalone.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -8,8 +8,161 @@ Core client utilities for Archipelago islands.
|
|
|
8
8
|
yarn add @archipelago-js/client
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
##
|
|
11
|
+
## Quick Start
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
```ts
|
|
14
|
+
import { islandFetch } from "@archipelago-js/client"
|
|
15
|
+
|
|
16
|
+
const response = await islandFetch("TeamMembers", "add_member", {
|
|
17
|
+
team_id: 42,
|
|
18
|
+
email: "new@example.com"
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
if (response.status === "ok") {
|
|
22
|
+
console.log("Updated props:", response.props)
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## `islandFetch(component, operation, payload?, options?)`
|
|
27
|
+
|
|
28
|
+
Sends a POST to the Archipelago Rails endpoint and returns a typed `IslandResponse`.
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
const response = await islandFetch("TeamMembers", "add_member", payload, {
|
|
32
|
+
endpoint: "/islands", // default
|
|
33
|
+
fixedParams: { team_id: 42 }, // merged under payload
|
|
34
|
+
overridePayload: {}, // merged over payload
|
|
35
|
+
headers: {}, // extra request headers
|
|
36
|
+
signal: abortController.signal,
|
|
37
|
+
stream: "TeamMembers:42", // sent as X-Archipelago-Stream header
|
|
38
|
+
navigate: (url) => { ... }, // called on redirect responses (default: Turbo.visit or location.assign)
|
|
39
|
+
fetchImpl: fetch // swap fetch for testing
|
|
40
|
+
})
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Response Types
|
|
44
|
+
|
|
45
|
+
Every response is a discriminated union on `status`:
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
type IslandResponse =
|
|
49
|
+
| { status: "ok"; props: Record<string, unknown>; version: number }
|
|
50
|
+
| { status: "redirect"; location: string }
|
|
51
|
+
| { status: "error"; errors: Record<string, string[]> }
|
|
52
|
+
| { status: "forbidden" }
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`ArchipelagoResponse` is exported as an alias for `IslandResponse`.
|
|
56
|
+
|
|
57
|
+
### Handling responses
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
switch (response.status) {
|
|
61
|
+
case "ok":
|
|
62
|
+
// response.props, response.version
|
|
63
|
+
break
|
|
64
|
+
case "redirect":
|
|
65
|
+
// response.location — navigation already happened via navigate()
|
|
66
|
+
break
|
|
67
|
+
case "error":
|
|
68
|
+
// response.errors — e.g. { email: ["can't be blank"] }
|
|
69
|
+
break
|
|
70
|
+
case "forbidden":
|
|
71
|
+
// no additional data
|
|
72
|
+
break
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## `parseIslandResponse(value)`
|
|
77
|
+
|
|
78
|
+
Parses a raw JSON value into a typed `IslandResponse`. Throws `ArchipelagoTransportError` on invalid payloads. Used internally by `islandFetch` and useful for parsing ActionCable broadcast payloads.
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
import { parseIslandResponse } from "@archipelago-js/client"
|
|
82
|
+
|
|
83
|
+
const parsed = parseIslandResponse(rawPayload)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## `buildIslandPayload(payload?, fixedParams?, overridePayload?)`
|
|
87
|
+
|
|
88
|
+
Merges three layers of params with precedence: `fixedParams` < `payload` < `overridePayload`.
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
import { buildIslandPayload } from "@archipelago-js/client"
|
|
92
|
+
|
|
93
|
+
buildIslandPayload(
|
|
94
|
+
{ email: "user@example.com" },
|
|
95
|
+
{ team_id: 42 },
|
|
96
|
+
{ email: "override@example.com" }
|
|
97
|
+
)
|
|
98
|
+
// => { team_id: 42, email: "override@example.com" }
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Error Handling
|
|
102
|
+
|
|
103
|
+
### `FORM_ERROR`
|
|
104
|
+
|
|
105
|
+
A constant equal to `"_base"` — the conventional key for form-level (non-field) errors:
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
import { FORM_ERROR } from "@archipelago-js/client"
|
|
109
|
+
|
|
110
|
+
if (response.status === "error" && response.errors[FORM_ERROR]) {
|
|
111
|
+
console.log("Form-level errors:", response.errors[FORM_ERROR])
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### `ArchipelagoTransportError`
|
|
116
|
+
|
|
117
|
+
A typed `Error` subclass thrown when the network request fails, the response is HTML instead of JSON, or JSON parsing fails. Properties:
|
|
118
|
+
|
|
119
|
+
| Property | Type | Description |
|
|
120
|
+
|----------------|-----------------------|--------------------------------------|
|
|
121
|
+
| `message` | `string` | Human-readable error description |
|
|
122
|
+
| `statusCode` | `number \| undefined` | HTTP status code (if available) |
|
|
123
|
+
| `responseBody` | `string \| undefined` | First 500 chars of the response body |
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
import { ArchipelagoTransportError } from "@archipelago-js/client"
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
await islandFetch("TeamMembers", "add_member", payload)
|
|
130
|
+
} catch (error) {
|
|
131
|
+
if (error instanceof ArchipelagoTransportError) {
|
|
132
|
+
console.error("Transport failed:", error.message, error.statusCode)
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## CSRF
|
|
138
|
+
|
|
139
|
+
CSRF tokens are automatically read from `<meta name="csrf-token">` and sent as `X-CSRF-Token`. The cache refreshes automatically on 422 responses (Rails CSRF rotation).
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
import { getCsrfToken, refreshCsrfToken, clearCsrfCache } from "@archipelago-js/client"
|
|
143
|
+
|
|
144
|
+
getCsrfToken() // read cached or from DOM
|
|
145
|
+
refreshCsrfToken() // force re-read from DOM
|
|
146
|
+
clearCsrfCache() // clear cache (next call reads from DOM)
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## API Reference
|
|
150
|
+
|
|
151
|
+
| Export | Description |
|
|
152
|
+
|-----------------------------|--------------------------------------------------|
|
|
153
|
+
| `islandFetch` | POST to island endpoint, returns typed response |
|
|
154
|
+
| `buildIslandPayload` | Merge fixed/payload/override params |
|
|
155
|
+
| `parseIslandResponse` | Parse raw JSON into typed `IslandResponse` |
|
|
156
|
+
| `FORM_ERROR` | `"_base"` constant for form-level errors |
|
|
157
|
+
| `ArchipelagoTransportError` | Typed error for network/parse failures |
|
|
158
|
+
| `getCsrfToken` | Read CSRF token from DOM (cached) |
|
|
159
|
+
| `refreshCsrfToken` | Force re-read CSRF token |
|
|
160
|
+
| `clearCsrfCache` | Clear CSRF token cache |
|
|
161
|
+
| `IslandResponse` | Union type for all response statuses |
|
|
162
|
+
| `ArchipelagoResponse` | Alias for `IslandResponse` |
|
|
163
|
+
| `IslandOkResponse` | `{ status: "ok", props, version }` |
|
|
164
|
+
| `IslandRedirectResponse` | `{ status: "redirect", location }` |
|
|
165
|
+
| `IslandErrorResponse` | `{ status: "error", errors }` |
|
|
166
|
+
| `IslandForbiddenResponse` | `{ status: "forbidden" }` |
|
|
167
|
+
| `IslandFetchOptions` | Options type for `islandFetch` |
|
|
168
|
+
| `IslandFetchPayload` | Payload type (`Record<string, unknown>`) |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@archipelago-js/client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Core client utilities for Archipelago islands",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -28,11 +28,10 @@
|
|
|
28
28
|
},
|
|
29
29
|
"repository": {
|
|
30
30
|
"type": "git",
|
|
31
|
-
"url": "git+https://github.com/
|
|
32
|
-
"directory": "packages/client"
|
|
31
|
+
"url": "git+https://github.com/Catchhook/archipelago-js-client.git"
|
|
33
32
|
},
|
|
34
33
|
"bugs": {
|
|
35
|
-
"url": "https://github.com/
|
|
34
|
+
"url": "https://github.com/Catchhook/archipelago-js-client/issues"
|
|
36
35
|
},
|
|
37
|
-
"homepage": "https://github.com/
|
|
36
|
+
"homepage": "https://github.com/Catchhook/archipelago-js-client"
|
|
38
37
|
}
|