@algolia/wizard 0.17.0 → 0.18.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/dist/main.js
CHANGED
|
@@ -3604,7 +3604,7 @@ async function runAnalysis(mode, extraInstructions = []) {
|
|
|
3604
3604
|
// package.json
|
|
3605
3605
|
var package_default = {
|
|
3606
3606
|
name: "@algolia/wizard",
|
|
3607
|
-
version: "0.
|
|
3607
|
+
version: "0.18.0",
|
|
3608
3608
|
description: "Magically implement Algolia functionality in your codebase",
|
|
3609
3609
|
type: "module",
|
|
3610
3610
|
engines: {
|
|
@@ -4429,7 +4429,7 @@ function searchInstructions(input) {
|
|
|
4429
4429
|
"Implement an in-app Algolia search experience.",
|
|
4430
4430
|
`Build the search UI for ${input.searchUiTarget}.`,
|
|
4431
4431
|
...doc ? [
|
|
4432
|
-
"Follow the Algolia SDK reference below for client setup
|
|
4432
|
+
"Follow the Algolia SDK reference below for client setup, search UI wiring, and Insights instrumentation \u2014 Insights is required, not optional; prefer the reference over prior knowledge:",
|
|
4433
4433
|
doc
|
|
4434
4434
|
] : [
|
|
4435
4435
|
"No bundled Algolia SDK reference exists for this stack, so rely on the project's own conventions and Algolia's official client for its language. Do not invent APIs \u2014 keep to the documented search endpoint and its parameters."
|
|
@@ -27,6 +27,7 @@ Always install the **latest stable** within the major (use a caret range like `^
|
|
|
27
27
|
client cache and causes re-renders.
|
|
28
28
|
- For InstantSearch, import the client from `algoliasearch/lite` (smaller bundle and
|
|
29
29
|
correct types — see `instantsearch-setup.md`).
|
|
30
|
+
- Always enable **Insights** on the InstantSearch root with `insights: true`. Without it there are no click/conversion events, so many Algolia features will not work as expected.
|
|
30
31
|
|
|
31
32
|
## Files
|
|
32
33
|
|
|
@@ -22,7 +22,11 @@ import { searchBox, hits } from 'instantsearch.js/es/widgets'
|
|
|
22
22
|
|
|
23
23
|
const searchClient = algoliasearch(APP_ID, SEARCH_ONLY_KEY)
|
|
24
24
|
|
|
25
|
-
const search = instantsearch({
|
|
25
|
+
const search = instantsearch({
|
|
26
|
+
indexName: 'INDEX_NAME',
|
|
27
|
+
searchClient,
|
|
28
|
+
insights: true,
|
|
29
|
+
})
|
|
26
30
|
|
|
27
31
|
search.addWidgets([
|
|
28
32
|
searchBox({ container: '#searchbox' }),
|
|
@@ -32,6 +36,122 @@ search.addWidgets([
|
|
|
32
36
|
search.start()
|
|
33
37
|
```
|
|
34
38
|
|
|
39
|
+
## Insights (click analytics) — always enable it
|
|
40
|
+
|
|
41
|
+
The `insights` option turns on the Insights middleware (`instantsearch.js` v4.55+),
|
|
42
|
+
which adds `clickAnalytics: true` to every query and keeps the `userToken` in sync
|
|
43
|
+
between search and event calls. Never set `clickAnalytics` or `userToken` on the `configure` widget by hand.
|
|
44
|
+
|
|
45
|
+
Use `insights: true`. The middleware loads `search-insights` from the jsDelivr CDN
|
|
46
|
+
itself; do not install the package or pass `insightsClient`.
|
|
47
|
+
|
|
48
|
+
If a Content-Security-Policy blocks jsDelivr, the CDN script cannot load. In that
|
|
49
|
+
case, install `search-insights` from npm and pass its default export as
|
|
50
|
+
`insightsClient` in the `insights` option:
|
|
51
|
+
|
|
52
|
+
```js
|
|
53
|
+
import aa from 'search-insights'
|
|
54
|
+
|
|
55
|
+
const search = instantsearch({
|
|
56
|
+
indexName: 'INDEX_NAME',
|
|
57
|
+
searchClient,
|
|
58
|
+
insights: { insightsClient: aa },
|
|
59
|
+
})
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The middleware calls `aa('init', ...)` itself with the app ID and search key, so no
|
|
63
|
+
manual `init` call is needed. Keep using the `insightsInitParams` option (for
|
|
64
|
+
example `useCookie`) the same way as with the CDN path.
|
|
65
|
+
|
|
66
|
+
The middleware sets an anonymous `userToken` itself, so events fire with no extra
|
|
67
|
+
code. That token lives in memory only — a page reload starts a new one, which is
|
|
68
|
+
enough for search→click attribution within a session but not across sessions, and not
|
|
69
|
+
enough for Personalization to build a profile. To persist it in the first-party
|
|
70
|
+
`_ALGOLIA` cookie, switch to the object form — only where the app's cookie-consent
|
|
71
|
+
policy allows it:
|
|
72
|
+
|
|
73
|
+
```js
|
|
74
|
+
insights: {
|
|
75
|
+
insightsInitParams: {
|
|
76
|
+
useCookie: true
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Click and conversion events
|
|
82
|
+
|
|
83
|
+
The `hits` and `infiniteHits` widgets send `view` and `click` events on their own. A
|
|
84
|
+
custom `item` template whose click target is your own element gets `sendEvent` as the
|
|
85
|
+
second argument:
|
|
86
|
+
|
|
87
|
+
```js
|
|
88
|
+
hits({
|
|
89
|
+
container: '#hits',
|
|
90
|
+
templates: {
|
|
91
|
+
item(hit, { html, sendEvent }) {
|
|
92
|
+
return html`<a
|
|
93
|
+
href="${hit.url}"
|
|
94
|
+
onClick="${() => sendEvent('click', hit, 'Hit Clicked')}"
|
|
95
|
+
>${hit.name}</a
|
|
96
|
+
>`
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
})
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Conversions are business actions (add to cart, signup, booking), so wire them at the
|
|
103
|
+
real action, not at render:
|
|
104
|
+
|
|
105
|
+
```js
|
|
106
|
+
sendEvent('conversion', hit, 'Product Added To Cart', {
|
|
107
|
+
eventSubtype: 'addToCart',
|
|
108
|
+
objectData: [{ price: hit.price, quantity: 1 }],
|
|
109
|
+
value: hit.price,
|
|
110
|
+
currency: 'USD',
|
|
111
|
+
})
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Where the app has no such action on a search hit, leave a TODO at the best
|
|
115
|
+
candidate instead of inventing one.
|
|
116
|
+
|
|
117
|
+
Connector-based custom widgets get `sendEvent` in their render options, and unlike
|
|
118
|
+
the widgets above they send **no** click events automatically — wire them yourself.
|
|
119
|
+
|
|
120
|
+
### Authenticated users
|
|
121
|
+
|
|
122
|
+
Anonymous tokens need no code. When the app already has a stable user id, link it
|
|
123
|
+
after login and clear it on logout — the anonymous `userToken` keeps flowing
|
|
124
|
+
alongside it (Personalization uses that one, not the authenticated token):
|
|
125
|
+
|
|
126
|
+
```js
|
|
127
|
+
if (typeof window !== 'undefined') {
|
|
128
|
+
window.aa('setAuthenticatedUserToken', userId)
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
```js
|
|
133
|
+
if (typeof window !== 'undefined') {
|
|
134
|
+
window.aa('setAuthenticatedUserToken', undefined) // on logout
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
The middleware installs `window.aa` as a queue before the CDN script arrives, so these
|
|
139
|
+
calls need no load-order guard. Under a server-side rendering framework (Angular
|
|
140
|
+
Universal, SvelteKit), `window` does not exist on the server, so `window.aa` throws
|
|
141
|
+
unless you add this guard or call it only from client-side code. In TypeScript
|
|
142
|
+
nothing augments `Window`, so declare it once:
|
|
143
|
+
`declare global { interface Window { aa: (method: string, ...args: unknown[]) => void } }`.
|
|
144
|
+
|
|
145
|
+
With the npm `insightsClient` fallback, call the same method on the imported `aa`
|
|
146
|
+
function instead of `window.aa`:
|
|
147
|
+
|
|
148
|
+
```js
|
|
149
|
+
import aa from 'search-insights'
|
|
150
|
+
|
|
151
|
+
aa('setAuthenticatedUserToken', userId)
|
|
152
|
+
aa('setAuthenticatedUserToken', undefined) // on logout
|
|
153
|
+
```
|
|
154
|
+
|
|
35
155
|
## Frameworks without their own flavor
|
|
36
156
|
|
|
37
157
|
Only React and Vue have a maintained InstantSearch wrapper. For any other
|
|
@@ -28,7 +28,11 @@ const searchClient = algoliasearch(
|
|
|
28
28
|
|
|
29
29
|
export function Search() {
|
|
30
30
|
return (
|
|
31
|
-
<InstantSearch
|
|
31
|
+
<InstantSearch
|
|
32
|
+
searchClient={searchClient}
|
|
33
|
+
indexName="INDEX_NAME"
|
|
34
|
+
insights={true}
|
|
35
|
+
>
|
|
32
36
|
<SearchBox />
|
|
33
37
|
<Hits />
|
|
34
38
|
</InstantSearch>
|
|
@@ -37,3 +41,111 @@ export function Search() {
|
|
|
37
41
|
```
|
|
38
42
|
|
|
39
43
|
Do not inline `searchClient={algoliasearch(...)}` — keep the stable reference above.
|
|
44
|
+
|
|
45
|
+
## Insights (click analytics) — always enable it
|
|
46
|
+
|
|
47
|
+
The `insights` prop turns on the Insights middleware, which adds
|
|
48
|
+
`clickAnalytics: true` to every query and keeps the `userToken` in sync between search
|
|
49
|
+
and event calls. Never set `clickAnalytics` or `userToken` on `<Configure>` by hand.
|
|
50
|
+
|
|
51
|
+
Use `insights={true}`. The middleware loads `search-insights` from the jsDelivr CDN
|
|
52
|
+
itself; do not install the package or pass `insightsClient`.
|
|
53
|
+
|
|
54
|
+
If a Content-Security-Policy blocks jsDelivr, the CDN script cannot load. In that
|
|
55
|
+
case, install `search-insights` from npm and pass its default export as
|
|
56
|
+
`insightsClient`:
|
|
57
|
+
|
|
58
|
+
```tsx
|
|
59
|
+
import aa from 'search-insights'
|
|
60
|
+
|
|
61
|
+
<InstantSearch insights={{ insightsClient: aa }} />
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
The middleware calls `aa('init', ...)` itself with the app ID and search key, so no
|
|
65
|
+
manual `init` call is needed. Keep using the `insightsInitParams` option (for
|
|
66
|
+
example `useCookie`) the same way as with the CDN path.
|
|
67
|
+
|
|
68
|
+
The middleware sets an anonymous `userToken` itself, so events fire with no extra
|
|
69
|
+
code. That token lives in memory only — a page reload starts a new one, which is
|
|
70
|
+
enough for search→click attribution within a session but not across sessions, and not
|
|
71
|
+
enough for Personalization to build a profile. To persist it in the first-party
|
|
72
|
+
`_ALGOLIA` cookie, switch to the object form — only where the app's cookie-consent
|
|
73
|
+
policy allows it:
|
|
74
|
+
|
|
75
|
+
```tsx
|
|
76
|
+
<InstantSearch insights={{ insightsInitParams: { useCookie: true } }} />
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Click and conversion events
|
|
80
|
+
|
|
81
|
+
`<Hits>` and `<InfiniteHits>` send `view` and `click` events on their own. Two cases
|
|
82
|
+
need explicit wiring:
|
|
83
|
+
|
|
84
|
+
- A custom `hitComponent` whose click target is your own element — it receives a
|
|
85
|
+
`sendEvent` prop.
|
|
86
|
+
- `useHits` / `useInfiniteHits`: `view` is automatic, `click` is **not**.
|
|
87
|
+
|
|
88
|
+
```tsx
|
|
89
|
+
function Hit({ hit, sendEvent }) {
|
|
90
|
+
return (
|
|
91
|
+
<a href={hit.url} onClick={() => sendEvent('click', hit, 'Hit Clicked')}>
|
|
92
|
+
{hit.name}
|
|
93
|
+
</a>
|
|
94
|
+
)
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Conversions are business actions (add to cart, signup, booking), so wire them at the
|
|
99
|
+
real action, not at render:
|
|
100
|
+
|
|
101
|
+
```tsx
|
|
102
|
+
sendEvent('conversion', hit, 'Product Added To Cart', {
|
|
103
|
+
eventSubtype: 'addToCart',
|
|
104
|
+
objectData: [{ price: hit.price, quantity: 1 }],
|
|
105
|
+
value: hit.price,
|
|
106
|
+
currency: 'USD',
|
|
107
|
+
})
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Where the app has no such action on a search hit, leave a TODO at the best
|
|
111
|
+
candidate instead of inventing one.
|
|
112
|
+
|
|
113
|
+
### Authenticated users
|
|
114
|
+
|
|
115
|
+
Anonymous tokens need no code. When the app already has a stable user id, link it
|
|
116
|
+
after login and clear it on logout — the anonymous `userToken` keeps flowing
|
|
117
|
+
alongside it (Personalization uses that one, not the authenticated token):
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
// `search-insights` is CDN-loaded, so nothing augments Window — declare it once.
|
|
121
|
+
declare global {
|
|
122
|
+
interface Window {
|
|
123
|
+
aa: (method: string, ...args: unknown[]) => void
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (typeof window !== 'undefined') {
|
|
128
|
+
window.aa('setAuthenticatedUserToken', userId)
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
if (typeof window !== 'undefined') {
|
|
134
|
+
window.aa('setAuthenticatedUserToken', undefined) // on logout
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
The middleware installs `window.aa` as a queue before the CDN script arrives, so these
|
|
139
|
+
calls need no load-order guard. Under Next.js SSR (and React Server Components),
|
|
140
|
+
`window` does not exist on the server, so `window.aa` throws unless you add this
|
|
141
|
+
guard or call it only from a client-side hook such as `useEffect`.
|
|
142
|
+
|
|
143
|
+
With the npm `insightsClient` fallback, call the same method on the imported `aa`
|
|
144
|
+
function instead of `window.aa`:
|
|
145
|
+
|
|
146
|
+
```ts
|
|
147
|
+
import aa from 'search-insights'
|
|
148
|
+
|
|
149
|
+
aa('setAuthenticatedUserToken', userId)
|
|
150
|
+
aa('setAuthenticatedUserToken', undefined) // on logout
|
|
151
|
+
```
|
|
@@ -17,7 +17,11 @@ const searchClient = algoliasearch(APP_ID, SEARCH_ONLY_KEY)
|
|
|
17
17
|
|
|
18
18
|
```vue
|
|
19
19
|
<template>
|
|
20
|
-
<ais-instant-search
|
|
20
|
+
<ais-instant-search
|
|
21
|
+
:search-client="searchClient"
|
|
22
|
+
index-name="INDEX_NAME"
|
|
23
|
+
:insights="true"
|
|
24
|
+
>
|
|
21
25
|
<ais-search-box />
|
|
22
26
|
<ais-hits />
|
|
23
27
|
</ais-instant-search>
|
|
@@ -39,3 +43,99 @@ Register the widgets via the InstantSearch Vue plugin in your app entry:
|
|
|
39
43
|
import InstantSearch from 'vue-instantsearch/vue3/es'
|
|
40
44
|
app.use(InstantSearch)
|
|
41
45
|
```
|
|
46
|
+
|
|
47
|
+
## Insights (click analytics) — always enable it
|
|
48
|
+
|
|
49
|
+
The `insights` prop turns on the Insights middleware, which adds
|
|
50
|
+
`clickAnalytics: true` to every query and keeps the `userToken` in sync between search
|
|
51
|
+
and event calls. Never set `clickAnalytics` or `userToken` on `ais-configure` by hand.
|
|
52
|
+
|
|
53
|
+
Use `:insights="true"`. The middleware loads `search-insights` from the jsDelivr CDN itself; do not install
|
|
54
|
+
the package or pass `insightsClient`.
|
|
55
|
+
|
|
56
|
+
If a Content-Security-Policy blocks jsDelivr, the CDN script cannot load. In that
|
|
57
|
+
case, install `search-insights` from npm and pass its default export as
|
|
58
|
+
`insightsClient`:
|
|
59
|
+
|
|
60
|
+
```js
|
|
61
|
+
import aa from 'search-insights'
|
|
62
|
+
const insights = { insightsClient: aa } // then :insights="insights"
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
The middleware calls `aa('init', ...)` itself with the app ID and search key, so no
|
|
66
|
+
manual `init` call is needed.
|
|
67
|
+
|
|
68
|
+
The middleware sets an anonymous `userToken` itself, so events fire with no extra
|
|
69
|
+
code. That token lives in memory only — a page reload starts a new one, which is
|
|
70
|
+
enough for search→click attribution within a session but not across sessions, and not
|
|
71
|
+
enough for Personalization to build a profile. To persist it in the first-party
|
|
72
|
+
`_ALGOLIA` cookie, bind the object form — only where the app's cookie-consent policy
|
|
73
|
+
allows it:
|
|
74
|
+
|
|
75
|
+
```js
|
|
76
|
+
const insights = { insightsInitParams: { useCookie: true } } // then :insights="insights"
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Click and conversion events
|
|
80
|
+
|
|
81
|
+
`ais-hits` sends `view` and `click` events on its own. A custom `item` slot whose
|
|
82
|
+
click target is your own element needs the slot's `sendEvent`:
|
|
83
|
+
|
|
84
|
+
```vue
|
|
85
|
+
<ais-hits>
|
|
86
|
+
<template v-slot:item="{ item, sendEvent }">
|
|
87
|
+
<a :href="item.url" @click="sendEvent('click', item, 'Hit Clicked')">
|
|
88
|
+
{{ item.name }}
|
|
89
|
+
</a>
|
|
90
|
+
</template>
|
|
91
|
+
</ais-hits>
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Conversions are business actions (add to cart, signup, booking), so wire them at the
|
|
95
|
+
real action, not at render:
|
|
96
|
+
|
|
97
|
+
```js
|
|
98
|
+
sendEvent('conversion', item, 'Product Added To Cart', {
|
|
99
|
+
eventSubtype: 'addToCart',
|
|
100
|
+
objectData: [{ price: item.price, quantity: 1 }],
|
|
101
|
+
value: item.price,
|
|
102
|
+
currency: 'USD',
|
|
103
|
+
})
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Where the app has no such action on a search hit, leave a TODO at the best
|
|
107
|
+
candidate instead of inventing one.
|
|
108
|
+
|
|
109
|
+
### Authenticated users
|
|
110
|
+
|
|
111
|
+
Anonymous tokens need no code. When the app already has a stable user id, link it
|
|
112
|
+
after login and clear it on logout — the anonymous `userToken` keeps flowing
|
|
113
|
+
alongside it (Personalization uses that one, not the authenticated token):
|
|
114
|
+
|
|
115
|
+
```js
|
|
116
|
+
if (typeof window !== 'undefined') {
|
|
117
|
+
window.aa('setAuthenticatedUserToken', userId)
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
```js
|
|
122
|
+
if (typeof window !== 'undefined') {
|
|
123
|
+
window.aa('setAuthenticatedUserToken', undefined) // on logout
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
The middleware installs `window.aa` as a queue before the CDN script arrives, so these
|
|
128
|
+
calls need no load-order guard. Under Nuxt SSR, `window` does not exist on the server,
|
|
129
|
+
so `window.aa` throws unless you add this guard or call it only from a client-side
|
|
130
|
+
hook such as `onMounted`. In TypeScript nothing augments `Window`, so declare it
|
|
131
|
+
once: `declare global { interface Window { aa: (method: string, ...args: unknown[]) => void } }`.
|
|
132
|
+
|
|
133
|
+
With the npm `insightsClient` fallback, call the same method on the imported `aa`
|
|
134
|
+
function instead of `window.aa`:
|
|
135
|
+
|
|
136
|
+
```js
|
|
137
|
+
import aa from 'search-insights'
|
|
138
|
+
|
|
139
|
+
aa('setAuthenticatedUserToken', userId)
|
|
140
|
+
aa('setAuthenticatedUserToken', undefined) // on logout
|
|
141
|
+
```
|