@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.
Files changed (2) hide show
  1. package/README.md +158 -5
  2. package/package.json +4 -5
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @archipelago-js/client
2
2
 
3
- Core client utilities for Archipelago islands.
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
- ## API
11
+ ## Quick Start
12
12
 
13
- - `islandFetch(component, operation, payload, options)`
14
- - `buildIslandPayload(payload, fixedParams, overridePayload)`
15
- - `parseIslandResponse(value)`
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.2.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/robrace/archipelago.git",
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/robrace/archipelago/issues"
34
+ "url": "https://github.com/Catchhook/archipelago-js-client/issues"
36
35
  },
37
- "homepage": "https://github.com/robrace/archipelago/tree/main/packages/client"
36
+ "homepage": "https://github.com/Catchhook/archipelago-js-client"
38
37
  }