@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.17.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 and search UI wiring; prefer it over prior knowledge:",
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({ indexName: 'INDEX_NAME', searchClient })
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 searchClient={searchClient} indexName="INDEX_NAME">
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 :search-client="searchClient" index-name="INDEX_NAME">
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
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@algolia/wizard",
3
- "version": "0.17.0",
3
+ "version": "0.18.0",
4
4
  "description": "Magically implement Algolia functionality in your codebase",
5
5
  "type": "module",
6
6
  "engines": {