mikser-io-sdk-api 2.3.0 → 2.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 +25 -127
- package/index.d.ts +7 -0
- package/package.json +8 -2
- package/src/entities.js +22 -1
package/README.md
CHANGED
|
@@ -8,6 +8,14 @@ For semantic search against the `vector` plugin, install [mikser-io-sdk-vector](
|
|
|
8
8
|
|
|
9
9
|
Zero dependencies. Runs anywhere `fetch` is available (modern browsers, Node 18+, Deno, Bun, Workers).
|
|
10
10
|
|
|
11
|
+
> **Using Vue, React, or Svelte?** You probably want one of the framework SDKs — they wrap this package in framework-idiomatic primitives (`useDocument` / `useDocuments`, multilingual `useHref`, live SSE-driven updates) so you don't write a watch loop or lifecycle plumbing yourself:
|
|
12
|
+
>
|
|
13
|
+
> - [`mikser-io-sdk-vue`](https://github.com/almero-digital-marketing/mikser-io-sdk-vue) — Vue 3 composables + vue-router integration
|
|
14
|
+
> - [`mikser-io-sdk-react`](https://github.com/almero-digital-marketing/mikser-io-sdk-react) — React 18+ / 19+ hooks + React Router v6+
|
|
15
|
+
> - [`mikser-io-sdk-svelte`](https://github.com/almero-digital-marketing/mikser-io-sdk-svelte) — Svelte 5 (runes) + SvelteKit
|
|
16
|
+
>
|
|
17
|
+
> Use **this** package directly when you're writing a custom adapter for another framework (Solid, Qwik, vanilla JS, server-side Node) or when you need the lower-level surface (`list`, `urlFor`, `watch`, `render`).
|
|
18
|
+
|
|
11
19
|
## Install
|
|
12
20
|
|
|
13
21
|
```bash
|
|
@@ -206,9 +214,9 @@ Equivalent to:
|
|
|
206
214
|
// 4. abort on dispose
|
|
207
215
|
```
|
|
208
216
|
|
|
209
|
-
…but with race-safe cleanup (no `mounted` flag needed in caller code), unified error routing via `onError`, and a single dispose path. This is the building block the framework
|
|
217
|
+
…but with race-safe cleanup (no `mounted` flag needed in caller code), unified error routing via `onError`, and a single dispose path. This is the building block the [framework SDKs](#framework-integration) (Vue / React / Svelte) consume internally — and the surface to use directly if you're writing a custom adapter.
|
|
210
218
|
|
|
211
|
-
`live()` keeps an internal `items` array, patches it on each event, and hands the whole array to `onChange` every time. That's the simplest contract for
|
|
219
|
+
`live()` keeps an internal `items` array, patches it on each event, and hands the whole array to `onChange` every time. That's the simplest contract for frameworks with state-replace semantics (the callback just overwrites state). If you need per-event deltas — animated reveals, audit logs, derived counters — use `watch()` directly.
|
|
212
220
|
|
|
213
221
|
### `update(payload)` / `delete(payload)` — writes
|
|
214
222
|
|
|
@@ -443,142 +451,32 @@ Two SDKs, one mental model, one server. The vector store gives you ranked semant
|
|
|
443
451
|
|
|
444
452
|
### Framework integration
|
|
445
453
|
|
|
446
|
-
All the boilerplate (initial fetch, watch loop, race-safe cleanup) lives inside `
|
|
454
|
+
All the boilerplate (initial fetch, watch loop, race-safe cleanup) lives inside `live()`. A framework adapter is ~5 lines — it gives the SDK a callback and calls dispose on unmount. For the three major frameworks, those adapters are already published:
|
|
447
455
|
|
|
448
|
-
|
|
456
|
+
| Framework | Package | Primitive |
|
|
457
|
+
|---|---|---|
|
|
458
|
+
| Vue 3 | [`mikser-io-sdk-vue`](https://github.com/almero-digital-marketing/mikser-io-sdk-vue) | `useDocument(id)` / `useDocuments(query)` returning Vue refs; vue-router integration via `createMikserRouter` |
|
|
459
|
+
| React 18+ / 19+ | [`mikser-io-sdk-react`](https://github.com/almero-digital-marketing/mikser-io-sdk-react) | `useDocument(id)` / `useDocuments(query)` hooks; React Router integration via `useMikserRoutes` → `useRoutes` |
|
|
460
|
+
| Svelte 5 | [`mikser-io-sdk-svelte`](https://github.com/almero-digital-marketing/mikser-io-sdk-svelte) | `useDocument(() => id)` / `useDocuments(() => query)` runes-backed reactives; SvelteKit `entries()` integration via `generateMikserRoutes` |
|
|
449
461
|
|
|
450
|
-
|
|
451
|
-
// mikser.js — single source of truth for the configured client
|
|
452
|
-
import { createClient } from 'mikser-io-sdk-api'
|
|
453
|
-
export const docs = createClient({ baseUrl: 'https://cms.example.com' })
|
|
454
|
-
.entities('public')
|
|
455
|
-
```
|
|
462
|
+
All three share the same conceptual surface — single-document subscription, list subscription, multilingual `useHref` / `useAlternates`, asset resolution via `useAsset` — wrapped in each framework's idiomatic shape. They all peer-depend on this package and consume `live()` internally; nothing about their behaviour is duplicated logic. If you have one of those three frameworks, prefer the matching SDK over hand-rolling against `live()`.
|
|
456
463
|
|
|
457
|
-
|
|
464
|
+
The shape adapts to **any** framework with a setup-and-cleanup lifecycle — Solid (`createSignal` + `onCleanup`), Qwik (`useTask$`), Lit, or vanilla JS. For those, the adapter pattern is the same five lines: instantiate state, call `live(filter, setState)`, store the returned `dispose`, call it on teardown.
|
|
458
465
|
|
|
459
466
|
```js
|
|
460
|
-
//
|
|
461
|
-
|
|
462
|
-
import { docs } from './mikser'
|
|
463
|
-
|
|
464
|
-
export function useLiveEntities(filter, options) {
|
|
465
|
-
const [items, setItems] = useState([])
|
|
466
|
-
useEffect(
|
|
467
|
-
() => docs.live(filter, setItems, options), // returns dispose
|
|
468
|
-
[JSON.stringify(filter)],
|
|
469
|
-
)
|
|
470
|
-
return items
|
|
471
|
-
}
|
|
472
|
-
```
|
|
467
|
+
// vanilla adapter shape — works in any environment
|
|
468
|
+
const documents = createClient({ baseUrl }).entities('public')
|
|
473
469
|
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
export function ArticleList() {
|
|
479
|
-
const articles = useLiveEntities(
|
|
480
|
-
{ type: 'document', 'meta.collection': 'articles', 'meta.published': true },
|
|
481
|
-
{ sort: { 'meta.date': -1 }, limit: 20 },
|
|
482
|
-
)
|
|
483
|
-
return (
|
|
484
|
-
<ul>
|
|
485
|
-
{articles.map(a => <li key={a.id}>{a.meta.title}</li>)}
|
|
486
|
-
</ul>
|
|
487
|
-
)
|
|
488
|
-
}
|
|
489
|
-
```
|
|
490
|
-
|
|
491
|
-
#### Vue 3 (composable, Composition API)
|
|
492
|
-
|
|
493
|
-
```js
|
|
494
|
-
// useLiveEntities.js
|
|
495
|
-
import { ref, onMounted, onUnmounted } from 'vue'
|
|
496
|
-
import { docs } from './mikser'
|
|
497
|
-
|
|
498
|
-
export function useLiveEntities(filter, options) {
|
|
499
|
-
const items = ref([])
|
|
500
|
-
let dispose
|
|
501
|
-
onMounted (() => { dispose = docs.live(filter, v => items.value = v, options) })
|
|
502
|
-
onUnmounted(() => dispose?.())
|
|
503
|
-
return { items }
|
|
504
|
-
}
|
|
505
|
-
```
|
|
506
|
-
|
|
507
|
-
```vue
|
|
508
|
-
<!-- ArticleList.vue -->
|
|
509
|
-
<script setup>
|
|
510
|
-
import { useLiveEntities } from './useLiveEntities'
|
|
511
|
-
|
|
512
|
-
const { items: articles } = useLiveEntities(
|
|
513
|
-
{ type: 'document', 'meta.collection': 'articles', 'meta.published': true },
|
|
470
|
+
const dispose = documents.live(
|
|
471
|
+
{ 'meta.published': true },
|
|
472
|
+
(items) => render(items), // your update callback
|
|
514
473
|
{ sort: { 'meta.date': -1 }, limit: 20 },
|
|
515
474
|
)
|
|
516
|
-
</script>
|
|
517
475
|
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
<li v-for="a in articles" :key="a.id">{{ a.meta.title }}</li>
|
|
521
|
-
</ul>
|
|
522
|
-
</template>
|
|
523
|
-
```
|
|
524
|
-
|
|
525
|
-
#### Svelte (writable store — works in Svelte 3, 4, and 5)
|
|
526
|
-
|
|
527
|
-
Svelte's `writable(initial, start)` pattern is a perfect fit: `start` runs when the store gains its first subscriber and the returned `stop` runs when the last one disappears. The store lifecycle and `live()`'s dispose function line up exactly.
|
|
528
|
-
|
|
529
|
-
```js
|
|
530
|
-
// liveEntities.js
|
|
531
|
-
import { writable } from 'svelte/store'
|
|
532
|
-
import { docs } from './mikser'
|
|
533
|
-
|
|
534
|
-
export function liveEntities(filter, options) {
|
|
535
|
-
return writable([], (set) => docs.live(filter, set, options))
|
|
536
|
-
}
|
|
537
|
-
```
|
|
538
|
-
|
|
539
|
-
```svelte
|
|
540
|
-
<!-- ArticleList.svelte -->
|
|
541
|
-
<script>
|
|
542
|
-
import { liveEntities } from './liveEntities'
|
|
543
|
-
|
|
544
|
-
const articles = liveEntities(
|
|
545
|
-
{ type: 'document', 'meta.collection': 'articles', 'meta.published': true },
|
|
546
|
-
{ sort: { 'meta.date': -1 }, limit: 20 },
|
|
547
|
-
)
|
|
548
|
-
</script>
|
|
549
|
-
|
|
550
|
-
<ul>
|
|
551
|
-
{#each $articles as a (a.id)}
|
|
552
|
-
<li>{a.meta.title}</li>
|
|
553
|
-
{/each}
|
|
554
|
-
</ul>
|
|
555
|
-
```
|
|
556
|
-
|
|
557
|
-
If you're on Svelte 5 and prefer runes over stores:
|
|
558
|
-
|
|
559
|
-
```svelte
|
|
560
|
-
<!-- ArticleList.svelte (Svelte 5 runes) -->
|
|
561
|
-
<script>
|
|
562
|
-
import { onMount } from 'svelte'
|
|
563
|
-
import { docs } from './mikser'
|
|
564
|
-
|
|
565
|
-
let articles = $state([])
|
|
566
|
-
const filter = { type: 'document', 'meta.collection': 'articles', 'meta.published': true }
|
|
567
|
-
|
|
568
|
-
onMount(() => docs.live(filter, v => articles = v, {
|
|
569
|
-
sort: { 'meta.date': -1 }, limit: 20,
|
|
570
|
-
}))
|
|
571
|
-
</script>
|
|
572
|
-
|
|
573
|
-
<ul>
|
|
574
|
-
{#each articles as a (a.id)}
|
|
575
|
-
<li>{a.meta.title}</li>
|
|
576
|
-
{/each}
|
|
577
|
-
</ul>
|
|
476
|
+
// later, on teardown
|
|
477
|
+
dispose()
|
|
578
478
|
```
|
|
579
479
|
|
|
580
|
-
The same shape adapts to Solid (`createSignal` + `onCleanup`), Qwik (`useTask$`), or vanilla JS — anywhere with a setup-and-cleanup lifecycle. The SDK doesn't care.
|
|
581
|
-
|
|
582
480
|
## Configure
|
|
583
481
|
|
|
584
482
|
```js
|
package/index.d.ts
CHANGED
|
@@ -115,6 +115,13 @@ export interface EntitiesClient {
|
|
|
115
115
|
urlFor(query?: ListQuery): string
|
|
116
116
|
/** Iterate result pages — yields each envelope until hasNext is false. */
|
|
117
117
|
pages<T = unknown>(query?: ListQuery): AsyncGenerator<ListEnvelope<T>>
|
|
118
|
+
/**
|
|
119
|
+
* One-shot: fetch every matching entity into a flat array.
|
|
120
|
+
* Auto-paginates internally; `limit` controls per-page batch size
|
|
121
|
+
* (default 1000), not total cap. Right for SSG enumeration; wrong
|
|
122
|
+
* for catalogs too large to hold in memory.
|
|
123
|
+
*/
|
|
124
|
+
listAll<T = unknown>(query?: ListQuery): Promise<T[]>
|
|
118
125
|
/**
|
|
119
126
|
* Open an SSE stream and yield events as matching entities change.
|
|
120
127
|
* Compose with list() for initial state, then watch() for updates.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mikser-io-sdk-api",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.0",
|
|
4
4
|
"description": "Client SDK for mikser-io's api plugin — query the document catalog from the browser or Node",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -18,7 +18,13 @@
|
|
|
18
18
|
"README.md",
|
|
19
19
|
"LICENSE"
|
|
20
20
|
],
|
|
21
|
-
"scripts": {
|
|
21
|
+
"scripts": {
|
|
22
|
+
"test": "vitest",
|
|
23
|
+
"test:run": "vitest run"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"vitest": "^2.0.0"
|
|
27
|
+
},
|
|
22
28
|
"repository": {
|
|
23
29
|
"type": "git",
|
|
24
30
|
"url": "git+https://github.com/almero-digital-marketing/mikser-io-sdk-api.git"
|
package/src/entities.js
CHANGED
|
@@ -64,6 +64,27 @@ export function createEntitiesClient({ baseUrl, basePath, fetch: doFetch, header
|
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
+
/**
|
|
68
|
+
* One-shot: fetch every entity matching the filter into a flat
|
|
69
|
+
* array. Auto-paginates internally via pages() — `limit` in the
|
|
70
|
+
* query controls the per-page batch size (default 1000), not the
|
|
71
|
+
* total cap.
|
|
72
|
+
*
|
|
73
|
+
* Right when: SSG route enumeration, sitemap generation, build-
|
|
74
|
+
* time indexing — anything that needs the whole filtered set in
|
|
75
|
+
* memory at once.
|
|
76
|
+
*
|
|
77
|
+
* Wrong when: the catalog is large enough that loading all of it
|
|
78
|
+
* is wasteful. Use pages() directly and stream-process there.
|
|
79
|
+
*/
|
|
80
|
+
async function listAll(query = {}) {
|
|
81
|
+
const items = []
|
|
82
|
+
for await (const env of pages({ limit: 1000, ...query })) {
|
|
83
|
+
items.push(...env.items)
|
|
84
|
+
}
|
|
85
|
+
return items
|
|
86
|
+
}
|
|
87
|
+
|
|
67
88
|
/**
|
|
68
89
|
* Subscribe to changes — opens an SSE stream and yields events
|
|
69
90
|
* for each matching entity change (CREATE / UPDATE / DELETE).
|
|
@@ -264,6 +285,6 @@ export function createEntitiesClient({ baseUrl, basePath, fetch: doFetch, header
|
|
|
264
285
|
return res.arrayBuffer()
|
|
265
286
|
}
|
|
266
287
|
|
|
267
|
-
return { list, urlFor, pages, watch, live, update, delete: remove, render }
|
|
288
|
+
return { list, listAll, urlFor, pages, watch, live, update, delete: remove, render }
|
|
268
289
|
}
|
|
269
290
|
}
|