@latlng/sdk 0.1.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 (41) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +343 -0
  3. package/dist/auth/token.d.ts +2 -0
  4. package/dist/auth/token.d.ts.map +1 -0
  5. package/dist/client.d.ts +385 -0
  6. package/dist/client.d.ts.map +1 -0
  7. package/dist/errors/index.d.ts +66 -0
  8. package/dist/errors/index.d.ts.map +1 -0
  9. package/dist/http/routes.d.ts +41 -0
  10. package/dist/http/routes.d.ts.map +1 -0
  11. package/dist/http/transport.d.ts +26 -0
  12. package/dist/http/transport.d.ts.map +1 -0
  13. package/dist/index.d.ts +12 -0
  14. package/dist/index.d.ts.map +1 -0
  15. package/dist/index.js +1455 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/internal/assert.d.ts +6 -0
  18. package/dist/internal/assert.d.ts.map +1 -0
  19. package/dist/internal/json.d.ts +3 -0
  20. package/dist/internal/json.d.ts.map +1 -0
  21. package/dist/internal/response-parsers.d.ts +26 -0
  22. package/dist/internal/response-parsers.d.ts.map +1 -0
  23. package/dist/routing/read-preference.d.ts +7 -0
  24. package/dist/routing/read-preference.d.ts.map +1 -0
  25. package/dist/routing/replicas.d.ts +22 -0
  26. package/dist/routing/replicas.d.ts.map +1 -0
  27. package/dist/types/events.d.ts +122 -0
  28. package/dist/types/events.d.ts.map +1 -0
  29. package/dist/types/json.d.ts +7 -0
  30. package/dist/types/json.d.ts.map +1 -0
  31. package/dist/types/models.d.ts +408 -0
  32. package/dist/types/models.d.ts.map +1 -0
  33. package/dist/types/requests.d.ts +96 -0
  34. package/dist/types/requests.d.ts.map +1 -0
  35. package/dist/types/responses.d.ts +108 -0
  36. package/dist/types/responses.d.ts.map +1 -0
  37. package/dist/ws/socket.d.ts +66 -0
  38. package/dist/ws/socket.d.ts.map +1 -0
  39. package/dist/ws/subscriptions.d.ts +46 -0
  40. package/dist/ws/subscriptions.d.ts.map +1 -0
  41. package/package.json +49 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 TobiLG <github@tobilg.com>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,343 @@
1
+ # latlng TypeScript SDK
2
+
3
+ TypeScript SDK for the `latlng` server.
4
+
5
+ ## Scope
6
+
7
+ This package targets the current operational `latlng` HTTP and WebSocket surfaces.
8
+
9
+ Current scope:
10
+
11
+ - typed HTTP client for CRUD, search, admin/info, hooks, and channels
12
+ - typed WebSocket client for `auth`, `subscribe`, `psubscribe`, `ping`, and `quit`
13
+ - optional configuration-driven read routing across leader and follower URLs
14
+ - browser and Node support
15
+
16
+ Explicit non-goals:
17
+
18
+ - no Cap'n Proto client
19
+ - no server-side replication management
20
+ - no Worker-specific runtime bundle
21
+
22
+ ## Install
23
+
24
+ ```sh
25
+ npm install @latlng/sdk
26
+ ```
27
+
28
+ ## Quickstart
29
+
30
+ ```ts
31
+ import { LatLngClient, point } from "@latlng/sdk";
32
+
33
+ const client = new LatLngClient({
34
+ leaderUrl: "http://127.0.0.1:7421",
35
+ token: "dev-token",
36
+ });
37
+
38
+ await client.setObject("fleet", "truck-1", point(52.52, 13.405));
39
+ const object = await client.get("fleet", "truck-1");
40
+ const nearby = await client.nearby("fleet", {
41
+ lat: 52.52,
42
+ lon: 13.405,
43
+ meters: 500,
44
+ });
45
+ ```
46
+
47
+ Collections can also be created explicitly and remain present even when empty until they are dropped:
48
+
49
+ ```ts
50
+ await client.createCollection("fleet");
51
+ const collection = await client.getCollection("fleet");
52
+ ```
53
+
54
+ ## Client Configuration
55
+
56
+ ```ts
57
+ const client = new LatLngClient({
58
+ leaderUrl: "http://127.0.0.1:7421",
59
+ token: "dev-token",
60
+ timeoutMs: 5_000,
61
+ headers: { "x-client": "worker-a" },
62
+ });
63
+ ```
64
+
65
+ Supported options:
66
+
67
+ | Option | Purpose |
68
+ | --- | --- |
69
+ | `leaderUrl` | Base HTTP URL for the leader server. |
70
+ | `token` | Bearer token or JWT sent to HTTP and WebSocket requests. |
71
+ | `timeoutMs` | Per-request HTTP timeout. |
72
+ | `headers` | Extra headers merged into every HTTP request. |
73
+ | `fetch` | Custom fetch implementation for tests or custom runtimes. |
74
+ | `webSocketFactory` | Custom WebSocket factory for Node or tests. |
75
+ | `readReplicas` | Optional follower URLs used for eligible reads. |
76
+ | `readPreference` | One of `leader`, `leaderPreferred`, `followerPreferred`, or `roundRobinFollowers`. |
77
+ | `replicaStatusTtlMs` | Cache TTL for follower health/status probes. |
78
+
79
+ ## API Surface
80
+
81
+ The SDK exposes the native HTTP API as typed methods on `LatLngClient`.
82
+
83
+ Connection and server status:
84
+
85
+ | Method | Description |
86
+ | --- | --- |
87
+ | `ping()` | Check that the leader is reachable and authenticated. |
88
+ | `healthz()` | Read the health endpoint. |
89
+ | `server()` | Read server status, replication role, sequence, and version metadata. |
90
+ | `info(section?)` | Read the broader server info response. |
91
+ | `metrics()` | Read Prometheus text exposition metrics. |
92
+
93
+ Collections:
94
+
95
+ | Method | Description |
96
+ | --- | --- |
97
+ | `collections(matchPattern?)` | List visible collections. |
98
+ | `createCollection(collection)` | Create an empty collection. |
99
+ | `getCollection(collection)` | Read collection metadata or `null`. |
100
+ | `rename(collection, newName, options?)` | Rename a collection, optionally with `nx`. |
101
+ | `dropCollection(collection)` | Drop a collection explicitly. |
102
+ | `bounds(collection)` | Read collection bounds. |
103
+ | `stats(collection)` | Read collection statistics. |
104
+
105
+ Objects, fields, TTLs, and JSON paths:
106
+
107
+ | Method | Description |
108
+ | --- | --- |
109
+ | `setObject(collection, id, object, options?)` | Store point, bounds, geohash, GeoJSON, or string objects. |
110
+ | `setPoint(collection, id, coordinates, options?)` | Convenience wrapper for point writes. |
111
+ | `get(collection, id, options?)` | Read an object or `null`; can include fields and alternate output formats. |
112
+ | `delete(collection, id)` | Delete one object. |
113
+ | `deleteMatching(collection, options?)` | Delete objects matching an ID pattern. |
114
+ | `setFields(collection, id, fields, options?)` | Update one or more fields. |
115
+ | `getField(collection, id, field)` | Read one field value. |
116
+ | `expire(collection, id, seconds)` | Set or replace an object TTL. |
117
+ | `persist(collection, id)` | Remove an object TTL. |
118
+ | `ttl(collection, id)` | Read remaining TTL seconds or `null`. |
119
+ | `setJson(collection, id, path, value, options?)` | Set a JSON path value. |
120
+ | `getJson(collection, id, path)` | Read a JSON path value. |
121
+ | `deleteJson(collection, id, path)` | Delete a JSON path value. |
122
+
123
+ Search:
124
+
125
+ | Method | Description |
126
+ | --- | --- |
127
+ | `nearby(collection, query)` | Search around a point/radius. |
128
+ | `within(collection, request)` | Search objects contained by an area. |
129
+ | `intersects(collection, request)` | Search objects intersecting an area. |
130
+ | `scan(collection, options?)` | Scan IDs/objects with match and field filters. |
131
+ | `search(collection, options?)` | Text/string search with match and field filters. |
132
+
133
+ Search options include `cursor`, `limit`, `nofields`, `matchPattern`, `sort`,
134
+ `whereFilters`, `whereInFilters`, `whereExprFilters`, `clip`, and output formats
135
+ such as `objects`, `points`, `bounds`, `ids`, `count`, or geohashes.
136
+
137
+ ```ts
138
+ const trucks = await client.scan("fleet", {
139
+ limit: 100,
140
+ nofields: true,
141
+ whereFilters: [
142
+ { field: "speed", comparison: { type: "range", min: 10, max: 80 } },
143
+ ],
144
+ output: "ids",
145
+ });
146
+ ```
147
+
148
+ Channels, hooks, and geofence events:
149
+
150
+ | Method | Description |
151
+ | --- | --- |
152
+ | `setChannel(request)` | Create or replace an in-server geofence channel. |
153
+ | `channels(matchPattern?)` | List channel names. |
154
+ | `getChannel(name)` | Read a full channel definition or `null`. |
155
+ | `deleteChannel(name)` | Delete a channel. |
156
+ | `setHook(request)` | Create or replace a webhook hook. |
157
+ | `hooks(matchPattern?)` | List hook summaries. |
158
+ | `getHook(name)` | Read a full hook definition or `null`. |
159
+ | `deleteHook(name)` | Delete a hook. |
160
+ | `webhookQueue()` | Read durable webhook queue stats. |
161
+ | `connectWebSocket(options?)` | Connect to the leader WebSocket endpoint for channel subscriptions. |
162
+
163
+ Admin and runtime operations:
164
+
165
+ | Method | Description |
166
+ | --- | --- |
167
+ | `configGet(name)` | Read a runtime config value. |
168
+ | `configSet(name, value)` | Update a runtime config value. |
169
+ | `configRewrite()` | Persist current runtime config to disk. |
170
+ | `readonly(enabled)` | Toggle server read-only mode. |
171
+ | `timeout(request)` | Configure per-command server timeouts. |
172
+ | `aofshrink()` | Compact AOF persistence and return before/after stats. |
173
+ | `gc()` | Trigger server maintenance. |
174
+ | `flushdb()` | Delete all data. |
175
+
176
+ ## Value Helpers
177
+
178
+ The package exports constructors for common wire shapes:
179
+
180
+ ```ts
181
+ import {
182
+ bounds,
183
+ fieldEntries,
184
+ geojson,
185
+ hash,
186
+ jsonField,
187
+ numberField,
188
+ point,
189
+ stringObject,
190
+ textField,
191
+ } from "@latlng/sdk";
192
+
193
+ await client.setObject("fleet", "truck-1", point(52.52, 13.405), {
194
+ fields: {
195
+ speed: numberField(42),
196
+ status: textField("moving"),
197
+ payload: jsonField(JSON.stringify({ driver: "a" })),
198
+ },
199
+ });
200
+
201
+ await client.setObject("zones", "berlin", bounds({
202
+ min_lat: 52.3,
203
+ min_lon: 13.0,
204
+ max_lat: 52.7,
205
+ max_lon: 13.8,
206
+ }));
207
+
208
+ const fields = fieldEntries({ status: textField("ready") });
209
+ await client.setFields("fleet", "truck-1", fields);
210
+
211
+ await client.setObject("messages", "msg-1", stringObject("dispatch ready"));
212
+ await client.setObject(
213
+ "areas",
214
+ "geojson-1",
215
+ geojson({ type: "Point", coordinates: [13.405, 52.52] }),
216
+ );
217
+ await client.setObject("cells", "u33dc", hash("u33dc"));
218
+ ```
219
+
220
+ ## WebSocket Example
221
+
222
+ ```ts
223
+ const ws = await client.connectWebSocket();
224
+ const subscription = await ws.psubscribe(["fleet*"]);
225
+
226
+ subscription.on("event", (event) => {
227
+ console.log(event.detect, event.id);
228
+ });
229
+ ```
230
+
231
+ Subscriptions are also async iterables:
232
+
233
+ ```ts
234
+ for await (const event of subscription) {
235
+ console.log(event.channel, event.detect, event.id);
236
+ }
237
+ ```
238
+
239
+ `LatLngWebSocketClient` exposes `ping()`, `subscribe(channels)`,
240
+ `psubscribe(patterns)`, `quit()`, and `close()`. The helper
241
+ `parseGeofenceEvent()` is exported for callers that need to parse event payloads
242
+ outside the WebSocket client.
243
+
244
+ ## Read Replica Routing
245
+
246
+ ```ts
247
+ const client = new LatLngClient({
248
+ leaderUrl: "http://leader:7421",
249
+ readReplicas: [
250
+ "http://follower-1:7421",
251
+ "http://follower-2:7421",
252
+ ],
253
+ readPreference: "followerPreferred",
254
+ token: "dev-token",
255
+ });
256
+ ```
257
+
258
+ Followers are only used for reads when they report:
259
+
260
+ - `leader === false`
261
+ - `caught_up_once === true`
262
+
263
+ Writes and admin calls always target the configured leader URL.
264
+
265
+ Read routing only applies to normal read methods. Leader-only status/admin calls,
266
+ WebSocket connections, writes, config updates, compaction, and webhook queue
267
+ inspection target the leader.
268
+
269
+ ## Authentication
270
+
271
+ The SDK transports bearer tokens. It does not mint, refresh, or introspect tokens.
272
+
273
+ In practice:
274
+
275
+ - local/dev commonly uses the static bearer token
276
+ - production deployments typically use JWTs issued by an external service or IdP
277
+ - claims-based authorization is enforced by the server, not by the SDK
278
+
279
+ The current auth/authz model, claim schema, and config reference are documented in [docs/auth.md](https://github.com/tobilg/latlng/blob/main/docs/auth.md).
280
+
281
+ ## Errors
282
+
283
+ The SDK exports typed error classes:
284
+
285
+ | Error | Meaning |
286
+ | --- | --- |
287
+ | `LatLngError` | Base SDK error. |
288
+ | `TimeoutError` | Request exceeded `timeoutMs`. |
289
+ | `HttpError` | Non-success HTTP response with status and payload context. |
290
+ | `AuthError` | Authentication or authorization failure. |
291
+ | `ServerUnavailableError` | Server unavailable response. |
292
+
293
+ ```ts
294
+ import { AuthError, LatLngClient } from "@latlng/sdk";
295
+
296
+ const client = new LatLngClient({
297
+ leaderUrl: "http://127.0.0.1:7421",
298
+ token: "dev-token",
299
+ });
300
+
301
+ try {
302
+ await client.ping();
303
+ } catch (error) {
304
+ if (error instanceof AuthError) {
305
+ console.error("invalid token");
306
+ }
307
+ }
308
+ ```
309
+
310
+ ## API Reference
311
+
312
+ Run `npm run docs:api` to generate TypeDoc reference pages for all exported
313
+ classes, helpers, request types, response types, search models, geofence event
314
+ models, and error classes.
315
+
316
+ ## Development
317
+
318
+ ```sh
319
+ npm ci
320
+ npm run build
321
+ npm run docs:api
322
+ npm run test:unit
323
+ npm run test:integration
324
+ ```
325
+
326
+ The integration suite starts local `latlng-server` processes and exercises the SDK against the real HTTP, WebSocket, and leader/follower surfaces.
327
+
328
+ ## Release Notes
329
+
330
+ Current package toolchain:
331
+
332
+ - `typescript@6.0.3`
333
+ - `vite@8.0.11`
334
+ - `vitest@4.1.5`
335
+
336
+ Subdirectory publish flow:
337
+
338
+ ```sh
339
+ npm ci
340
+ npm run test
341
+ npm run docs:api
342
+ npm publish --access public
343
+ ```
@@ -0,0 +1,2 @@
1
+ export declare function withBearerToken(headers: Headers, token: string | undefined): void;
2
+ //# sourceMappingURL=token.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token.d.ts","sourceRoot":"","sources":["../../src/auth/token.ts"],"names":[],"mappings":"AAAA,wBAAgB,eAAe,CAC7B,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,MAAM,GAAG,SAAS,GACxB,IAAI,CAIN"}