@geolonia/geonicdb-sdk 0.2.1 → 0.4.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 +118 -1
- package/geonicdb.cjs +447 -53
- package/geonicdb.cjs.map +4 -4
- package/geonicdb.iife.js +447 -53
- package/geonicdb.iife.js.map +4 -4
- package/geonicdb.mjs +447 -53
- package/geonicdb.mjs.map +4 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -153,7 +153,8 @@ const result = await db.request('GET', '/ngsi-ld/v1/subscriptions');
|
|
|
153
153
|
db.subscribe({ entityTypes: ['Room'] });
|
|
154
154
|
|
|
155
155
|
db.on('entityUpdated', (event) => {
|
|
156
|
-
|
|
156
|
+
// event.entity contains the complete NGSI-LD entity ({ id, type, ...attributes })
|
|
157
|
+
console.log(`${event.entityId} updated:`, event.entity);
|
|
157
158
|
});
|
|
158
159
|
|
|
159
160
|
db.on('error', (err) => console.error(err));
|
|
@@ -175,6 +176,57 @@ await db.connect();
|
|
|
175
176
|
| `reconnecting` | `{ attempt, delay }` | Auto-reconnect starting |
|
|
176
177
|
| `error` | `Error` | Error occurred |
|
|
177
178
|
| `tokenRefresh` | `RefreshedCredentials` | Bearer token was refreshed |
|
|
179
|
+
| `cacheHit` | `CacheEvent` | Request served from in-memory cache (304 received) |
|
|
180
|
+
| `cacheMiss` | `CacheEvent` | Cache miss — fresh response fetched from origin |
|
|
181
|
+
| `cacheInvalidated` | `CacheEvent` | Cache entry dropped (e.g. after a WebSocket entity event) |
|
|
182
|
+
|
|
183
|
+
## Client-side Cache & Polling
|
|
184
|
+
|
|
185
|
+
The SDK includes an in-memory cache that handles `ETag` / `If-None-Match`
|
|
186
|
+
negotiation transparently. Subsequent reads of an unchanged resource are
|
|
187
|
+
served as `304 Not Modified` — the SDK presents them to your code as a normal
|
|
188
|
+
`200` response with the cached body.
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
// First call: fresh fetch, ETag stored
|
|
192
|
+
const rooms1 = await db.getEntities({ type: 'Room' });
|
|
193
|
+
|
|
194
|
+
// Second call: server returns 304, SDK returns the cached body
|
|
195
|
+
const rooms2 = await db.getEntities({ type: 'Room' });
|
|
196
|
+
|
|
197
|
+
// Concurrent calls to the same path are deduplicated:
|
|
198
|
+
const [a, b, c] = await Promise.all([
|
|
199
|
+
db.getEntities({ type: 'Room' }),
|
|
200
|
+
db.getEntities({ type: 'Room' }),
|
|
201
|
+
db.getEntities({ type: 'Room' }),
|
|
202
|
+
]); // → only one HTTP request hits the network
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Cache is enabled by default; pass `cache: false` to opt out.
|
|
206
|
+
|
|
207
|
+
### Polling
|
|
208
|
+
|
|
209
|
+
`db.poll(params, options)` repeats `getEntities()` at an interval and reports
|
|
210
|
+
back whether the data actually changed. Internally it leverages the cache's
|
|
211
|
+
ETag negotiation, so unchanged ticks transfer almost no bytes.
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
const handle = db.poll({ type: 'Room' }, {
|
|
215
|
+
interval: 5000,
|
|
216
|
+
onData: (rooms) => render(rooms),
|
|
217
|
+
onNoChange: () => {}, // server returned 304
|
|
218
|
+
onError: (err) => console.error(err),
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
// Stop later
|
|
222
|
+
handle.stop();
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### WebSocket-driven cache invalidation
|
|
226
|
+
|
|
227
|
+
When the SDK receives `entityCreated` / `entityUpdated` / `entityDeleted`
|
|
228
|
+
WebSocket events, it drops affected cache entries automatically. The next
|
|
229
|
+
read re-validates with the server.
|
|
178
230
|
|
|
179
231
|
## API Reference
|
|
180
232
|
|
|
@@ -192,6 +244,9 @@ await db.connect();
|
|
|
192
244
|
| `tenant` | `string` | No | Tenant name (or use `data-tenant` attribute on script tag) |
|
|
193
245
|
| `baseUrl` | `string` | No | API base URL (auto-detected from script `src` if omitted) |
|
|
194
246
|
| `wsEndpoint` | `string` | No | WebSocket endpoint URL (auto-detected from `baseUrl` if omitted) |
|
|
247
|
+
| `debug` | `boolean` | No | Enable debug logging to console (default: `false`) |
|
|
248
|
+
| `cache` | `boolean` | No | Enable in-memory cache with ETag/304 + request dedup (default: `true`) |
|
|
249
|
+
| `cacheMaxEntries` | `number` | No | LRU cache capacity when `cache` is enabled (default: `1000`) |
|
|
195
250
|
|
|
196
251
|
### Authentication
|
|
197
252
|
|
|
@@ -276,6 +331,35 @@ await db.connect();
|
|
|
276
331
|
| `request(method, path, body?)` | `string, string, unknown` | `Promise<unknown>` | Authenticated request with automatic JSON parsing |
|
|
277
332
|
| `requestRaw(method, path, body?)` | `string, string, unknown` | `Promise<Response>` | Authenticated request returning raw `Response` (for accessing headers) |
|
|
278
333
|
|
|
334
|
+
### Cache & Polling
|
|
335
|
+
|
|
336
|
+
| Method | Parameters | Returns | Description |
|
|
337
|
+
|--------|-----------|---------|-------------|
|
|
338
|
+
| `poll(params, options)` | `GetEntitiesParams \| undefined, PollOptions<T>` | `PollHandle` | ETag-based polling — fires `onData` only when the data actually changes. `options` is **required** (`onData` must be specified to receive data) |
|
|
339
|
+
| `clearCache()` | — | `void` | Drop all cached responses |
|
|
340
|
+
|
|
341
|
+
#### `PollOptions<T>`
|
|
342
|
+
|
|
343
|
+
| Property | Type | Description |
|
|
344
|
+
|----------|------|-------------|
|
|
345
|
+
| `interval` | `number` | Polling interval in milliseconds (default: `5000`) |
|
|
346
|
+
| `onData` | `(data: T) => void` | Called whenever the server returns fresh data (200) |
|
|
347
|
+
| `onNoChange` | `() => void` | Called when the server returns 304 Not Modified |
|
|
348
|
+
| `onError` | `(err: Error) => void` | Called on any fetch/parse failure |
|
|
349
|
+
|
|
350
|
+
#### `PollHandle`
|
|
351
|
+
|
|
352
|
+
| Method | Parameters | Returns | Description |
|
|
353
|
+
|--------|-----------|---------|-------------|
|
|
354
|
+
| `stop()` | — | `void` | Stop the polling timer |
|
|
355
|
+
|
|
356
|
+
#### `CacheEvent`
|
|
357
|
+
|
|
358
|
+
| Property | Type | Description |
|
|
359
|
+
|----------|------|-------------|
|
|
360
|
+
| `key` | `string` | Cache key (`METHOD:path`) |
|
|
361
|
+
| `path` | `string` | Request path |
|
|
362
|
+
|
|
279
363
|
### WebSocket
|
|
280
364
|
|
|
281
365
|
| Method | Parameters | Returns | Description |
|
|
@@ -300,6 +384,39 @@ await db.connect();
|
|
|
300
384
|
| `on(event, listener)` | `string, Function` | `this` | Register an event listener |
|
|
301
385
|
| `off(event, listener)` | `string, Function` | `this` | Remove an event listener |
|
|
302
386
|
|
|
387
|
+
## Error Handling
|
|
388
|
+
|
|
389
|
+
All async methods throw typed errors extending `GeonicDBError`. Use `instanceof` to distinguish error types.
|
|
390
|
+
|
|
391
|
+
```typescript
|
|
392
|
+
import { GeonicDBError, AuthenticationError, NotFoundError } from '@geolonia/geonicdb-sdk';
|
|
393
|
+
|
|
394
|
+
try {
|
|
395
|
+
await db.getEntity('urn:ngsi-ld:Room:404');
|
|
396
|
+
} catch (err) {
|
|
397
|
+
if (err instanceof AuthenticationError) {
|
|
398
|
+
// 401 — re-login needed
|
|
399
|
+
} else if (err instanceof NotFoundError) {
|
|
400
|
+
// 404 — entity not found
|
|
401
|
+
} else if (err instanceof GeonicDBError) {
|
|
402
|
+
console.error(err.message, err.statusCode);
|
|
403
|
+
} else {
|
|
404
|
+
console.error(err);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
| Class | Status | Description |
|
|
410
|
+
|-------|--------|-------------|
|
|
411
|
+
| `GeonicDBError` | — | Base class (`statusCode` property) |
|
|
412
|
+
| `AuthenticationError` | 401 | Invalid credentials or expired token |
|
|
413
|
+
| `AuthorizationError` | 403 | Insufficient permissions |
|
|
414
|
+
| `NotFoundError` | 404 | Resource not found |
|
|
415
|
+
| `ConflictError` | 409 | Entity already exists |
|
|
416
|
+
| `ValidationError` | 422 | Bad request payload |
|
|
417
|
+
| `RateLimitError` | 429 | Rate limited (`retryAfter` property) |
|
|
418
|
+
| `NetworkError` | — | Fetch failure, DNS error, timeout |
|
|
419
|
+
|
|
303
420
|
## License
|
|
304
421
|
|
|
305
422
|
MIT
|