@playpilot/tpi 8.10.4-beta.1 → 8.10.4-beta.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@playpilot/tpi",
3
- "version": "8.10.4-beta.1",
3
+ "version": "8.10.4-beta.3",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "dev": "vite dev",
@@ -6,6 +6,7 @@ import type { LinkInjectionResponse, LinkInjection } from '../types/injection'
6
6
  import { getFullUrlPath } from '../url'
7
7
  import { api } from './api'
8
8
  import { getApiToken } from '$lib/token'
9
+ import { getPageTextAndElements } from '$lib/injectionElements'
9
10
 
10
11
  let pollTimeout: ReturnType<typeof setTimeout> | null = null
11
12
 
@@ -13,9 +14,8 @@ let pollTimeout: ReturnType<typeof setTimeout> | null = null
13
14
  * Fetch link injections for a URL. This will be either a POST or a GET request depending on whether on not the AI should run.
14
15
  */
15
16
  export async function fetchLinkInjections(
16
- pageText: string | null,
17
- { url = getFullUrlPath(), hash = stringToHash(pageText || ''), params = {}, method = 'GET' }:
18
- { url?: string, hash?: string, params?: Record<string, any>, method?: 'GET' | 'POST' } = {},
17
+ { url = getFullUrlPath(), params = {}, method = 'GET' }:
18
+ { url?: string, params?: Record<string, any>, method?: 'GET' | 'POST' } = {},
19
19
  ): Promise<LinkInjectionResponse> {
20
20
  const apiToken = getApiToken()
21
21
  const isEditorialMode = isEditorialModeEnabled() ? await authorize() : false
@@ -26,13 +26,13 @@ export async function fetchLinkInjections(
26
26
  const apiUrl = `/external-pages/?api-token=${apiToken}&include_title_details=true${isEditorialMode ? '&editorial_mode_enabled=true' : ''}&language=${language}`
27
27
  let response: LinkInjectionResponse
28
28
 
29
- console.log('page text in fetchLinkInjections', { pageText })
30
-
31
29
  // We use separate requests when running the AI or setting the editor session vs when only getting the results.
32
30
  // For regular requests we use a GET endpoint, but when saving data we POST to the same url.
33
31
  if (method === 'POST') {
32
+ const { pageText } = getPageTextAndElements()
33
+
34
34
  if (params.run_ai) {
35
- params.hash = hash
35
+ params.hash = stringToHash(pageText || '')
36
36
  params.page_text = pageText
37
37
  }
38
38
 
@@ -62,7 +62,6 @@ export async function fetchLinkInjections(
62
62
  * The results return `injections_ready=false` while the injections are not yet ready.
63
63
  */
64
64
  export async function pollLinkInjections(
65
- pageText: string,
66
65
  { requireCompletedResult = false, runAiWhenRelevant = false, pollInterval = 3000, maxTries = 600, onpoll = () => null }:
67
66
  { requireCompletedResult?: boolean, runAiWhenRelevant?: boolean, pollInterval?: number, maxTries?: number, onpoll?: (_response: LinkInjectionResponse) => void } = {},
68
67
  ): Promise<LinkInjectionResponse> {
@@ -72,8 +71,6 @@ export async function pollLinkInjections(
72
71
  // This is mostly handy during HMR, but also during navigation changes
73
72
  if (pollTimeout) clearTimeout(pollTimeout)
74
73
 
75
- console.log('page text in pollLinkInjections', { pageText })
76
-
77
74
  const poll = async (resolve: Function, reject: Function): Promise<void> => {
78
75
  let response
79
76
  try {
@@ -81,19 +78,25 @@ export async function pollLinkInjections(
81
78
  // if it does. We're still interested in handling the response from here and don't want it to short circuit
82
79
  // just because the fetch failed.
83
80
  try {
84
- response = await fetchLinkInjections(pageText)
81
+ response = await fetchLinkInjections()
85
82
  } catch(error: any) {
83
+ console.error(error)
86
84
  if (error?.status === 404) {
87
85
  // If the GET endpoint returns a 404 no article has been generated yet and we can safely generate
88
86
  // a new article. We can safely throw here (meaning we poll again) as nothing will be returned.
89
87
  // The first POST indexes the article, the second runs the AI if needed.
90
- response = await fetchLinkInjections(pageText, { method: 'POST' })
91
- if (response.ai_enabled) response = await fetchLinkInjections(pageText, { params: { run_ai: true }, method: 'POST' })
88
+ response = await fetchLinkInjections({ method: 'POST' })
89
+ if (response.ai_enabled) response = await fetchLinkInjections({ params: { run_ai: true }, method: 'POST' })
92
90
  }
93
91
 
94
92
  throw new Error
95
93
  }
96
94
 
95
+ const { pageText } = getPageTextAndElements()
96
+
97
+ console.log(pageText)
98
+ console.log(document.querySelector('#content')?.innerText)
99
+
97
100
  if (response && runAiWhenRelevant) response = await runAiBasedOnResponse(pageText, response)
98
101
  if (requireCompletedResult && (response.ai_enabled && response.ai_running)) throw new Error
99
102
 
@@ -137,10 +140,10 @@ export async function runAiBasedOnResponse(pageText: string, response: LinkInjec
137
140
  if (response.ai_last_run && isCurrentTimeEqualToResponseTime) return response
138
141
  if (new Date(currentModifiedTime || 0) <= new Date(responseModifiedTime || 0)) return response
139
142
 
140
- return await fetchLinkInjections(pageText, { params: { run_ai: true }, method: 'POST' })
143
+ return await fetchLinkInjections({ params: { run_ai: true }, method: 'POST' })
141
144
  }
142
145
 
143
- export async function saveLinkInjections(linkInjections: LinkInjection[], pageText: string): Promise<LinkInjection[]> {
146
+ export async function saveLinkInjections(linkInjections: LinkInjection[]): Promise<LinkInjection[]> {
144
147
  const selector = window.PlayPilotLinkInjections?.selector
145
148
 
146
149
  // Only save manual injections, AI injections should be left intact and can't be saved over
@@ -160,7 +163,7 @@ export async function saveLinkInjections(linkInjections: LinkInjection[], pageTe
160
163
  removed: !!linkInjection.removed,
161
164
  }))
162
165
 
163
- const response = await fetchLinkInjections(pageText, {
166
+ const response = await fetchLinkInjections({
164
167
  method: 'POST',
165
168
  params: {
166
169
  private_token: getAuthToken(),
@@ -11,10 +11,9 @@ export const sessionPeriodMilliseconds = sessionPollPeriodMilliseconds * 2
11
11
  * where we save the session.
12
12
  * Along with that, we also return ai_enabled and injections_enabled as they are used to update the state of
13
13
  * the current editing session.
14
- * @param pageText Despite not being used, the request still requires a valid pageText string
15
14
  */
16
- export async function fetchAsSession(pageText: string): Promise<SessionResponse> {
17
- const { ai_enabled, injections_enabled, ai_last_run, session_id, session_last_ping } = await fetchLinkInjections(pageText)
15
+ export async function fetchAsSession(): Promise<SessionResponse> {
16
+ const { ai_enabled, injections_enabled, ai_last_run, session_id, session_last_ping } = await fetchLinkInjections()
18
17
 
19
18
  const isCurrentlyAllowToEdit = isAllowedToEdit(session_id || null, session_last_ping || null)
20
19
 
@@ -28,15 +27,14 @@ export async function fetchAsSession(pageText: string): Promise<SessionResponse>
28
27
 
29
28
  if (!isCurrentlyAllowToEdit) return response
30
29
 
31
- return await saveCurrentSession(pageText)
30
+ return await saveCurrentSession()
32
31
  }
33
32
 
34
33
  /**
35
34
  * Save the current users session id as the currently active session. This is always completed regardless of if the user
36
35
  * is the current owner of the session, so check that first if necessary!
37
- * @param pageText Despite not being used, the request still requires a valid pageText string
38
36
  */
39
- export async function saveCurrentSession(pageText: string): Promise<SessionResponse> {
37
+ export async function saveCurrentSession(): Promise<SessionResponse> {
40
38
  const sessionId = getSessionId()
41
39
  const now = new Date(Date.now()).toISOString()
42
40
 
@@ -45,7 +43,7 @@ export async function saveCurrentSession(pageText: string): Promise<SessionRespo
45
43
  session_last_ping: now.toString(),
46
44
  }
47
45
 
48
- const { ai_enabled, injections_enabled, ai_last_run } = await fetchLinkInjections(pageText, { params })
46
+ const { ai_enabled, injections_enabled, ai_last_run } = await fetchLinkInjections({ params })
49
47
 
50
48
  return {
51
49
  ai_enabled,
@@ -91,7 +91,7 @@ export function getPageText(elements: HTMLElement[]): string {
91
91
  return elements.map(element => element.innerText).join('\n\n')
92
92
  }
93
93
 
94
- export function getPageTextAndElements(config: ConfigResponse | null): { parentElement: HTMLElement, elements: HTMLElement[], pageText: string } {
94
+ export function getPageTextAndElements(config: ConfigResponse | null = window.PlayPilotLinkInjections?.config || null): { parentElement: HTMLElement, elements: HTMLElement[], pageText: string } {
95
95
  const parentElement = getLinkInjectionsParentElement()
96
96
  const elements = getLinkInjectionElements(parentElement, config?.exclude_elements_selector || '')
97
97
  const pageText = getPageText(elements)
package/src/main.ts CHANGED
@@ -3,7 +3,6 @@ import { fetchConfig } from '$lib/api/config'
3
3
  import { pollLinkInjections } from '$lib/api/externalPages'
4
4
  import { setConsent } from '$lib/consent'
5
5
  import { isCrawler } from '$lib/crawler'
6
- import { getPageTextAndElements } from '$lib/injectionElements'
7
6
  import { isUrlExcludedViaConfig } from '$lib/url'
8
7
 
9
8
  window.PlayPilotLinkInjections ||= {
@@ -70,9 +69,7 @@ window.PlayPilotLinkInjections ||= {
70
69
 
71
70
  if (isUrlExcludedViaConfig(this.config)) return
72
71
 
73
- const { pageText } = getPageTextAndElements(this.config)
74
-
75
- this.initial_link_injections_promise = pollLinkInjections(pageText, { maxTries: 1, runAiWhenRelevant: !isCrawler() })
72
+ this.initial_link_injections_promise = pollLinkInjections({ maxTries: 1, runAiWhenRelevant: !isCrawler() })
76
73
 
77
74
  this.mount()
78
75
  },
@@ -2,7 +2,6 @@
2
2
  import { onDestroy } from 'svelte'
3
3
  import { pollLinkInjections } from '$lib/api/externalPages'
4
4
  import { clearLinkInjections, injectLinksInDocument, separateLinkInjectionTypes } from '$lib/injection'
5
- import { getPageText, getPageTextAndElements } from '$lib/injectionElements'
6
5
  import { fireQueuedTrackingEvents, setTrackingSids, track } from '$lib/tracking'
7
6
  import { isUrlExcludedViaConfig } from '$lib/url'
8
7
  import { isCrawler } from '$lib/crawler'
@@ -11,6 +10,7 @@
11
10
  import { fetchConfig } from '$lib/api/config'
12
11
  import { insertExplore, insertExploreIntoNavigation } from '$lib/explore'
13
12
  import { authorize, getAuthToken, isEditorialModeEnabled, removeAuthCookie, setEditorialParamInUrl } from '$lib/api/auth'
13
+ import { getPageTextAndElements } from '$lib/injectionElements'
14
14
  import type { LinkInjectionResponse, LinkInjection, LinkInjectionTypes } from '$lib/types/injection'
15
15
  import Editor from './components/Editorial/Editor.svelte'
16
16
  import EditorTrigger from './components/Editorial/EditorTrigger.svelte'
@@ -22,8 +22,6 @@
22
22
  import PostersScrollReveal from './components/PostersScrollReveal.svelte'
23
23
  import TrackingPixelsForTitleDataPerLink from './components/TrackingPixelsForTitleDataPerLink.svelte'
24
24
 
25
- let elements: HTMLElement[] = $state([])
26
-
27
25
  let response: LinkInjectionResponse | null = $state(null)
28
26
  let isEditorialMode = $state(isEditorialModeEnabled())
29
27
  let hasAuthToken = $state(!!getAuthToken())
@@ -35,10 +33,6 @@
35
33
  // @ts-ignore It's ok if the response is empty
36
34
  const { ai_injections: aiInjections = [], manual_injections: manualInjections = [] } = $derived(response || {})
37
35
 
38
- const pageText = $derived(getPageText(elements))
39
-
40
- $inspect({ thing: 'pageText in page.svelte', pageText })
41
-
42
36
  const initializePromise = initialize()
43
37
 
44
38
  $effect(() => {
@@ -88,8 +82,6 @@
88
82
  if (isUrlExcluded) return
89
83
 
90
84
  if (config?.html_selector) window.PlayPilotLinkInjections.selector = config?.html_selector
91
-
92
- elements = getPageTextAndElements(window.PlayPilotLinkInjections.config).elements
93
85
  } catch(error) {
94
86
  // We return if the config did not get fetched properly, as we can't determine what should and should
95
87
  // get injected without it.
@@ -100,7 +92,7 @@
100
92
 
101
93
  try {
102
94
  response = await window.PlayPilotLinkInjections.initial_link_injections_promise || null
103
- response ||= await pollLinkInjections(pageText, { maxTries: 1, runAiWhenRelevant: !isCrawler() })
95
+ response ||= await pollLinkInjections({ maxTries: 1, runAiWhenRelevant: !isCrawler() })
104
96
 
105
97
  loading = false
106
98
 
@@ -116,7 +108,7 @@
116
108
 
117
109
  if (!isEditorialMode || isCrawler()) return
118
110
 
119
- response = await pollLinkInjections(pageText, { requireCompletedResult: true, onpoll: (update) => response = update })
111
+ response = await pollLinkInjections({ requireCompletedResult: true, onpoll: (update) => response = update })
120
112
  inject({ aiInjections, manualInjections })
121
113
  } catch(error: unknown) {
122
114
  console.error(error)
@@ -138,6 +130,7 @@
138
130
  }
139
131
 
140
132
  function inject(injections: LinkInjectionTypes = { aiInjections, manualInjections }): void {
133
+ const { elements } = getPageTextAndElements()
141
134
  const filteredInjections = injectLinksInDocument(elements, injections)
142
135
 
143
136
  if (JSON.stringify(filteredInjections) !== JSON.stringify(linkInjections)) {
@@ -208,7 +201,6 @@
208
201
  <svelte:boundary onerror={(error) => track(TrackingEvent.EditorError, null, { message: (error as Error).message })}>
209
202
  <Editor
210
203
  bind:linkInjections
211
- {pageText}
212
204
  {loading}
213
205
  onreinitialize={reinitializeEditor}
214
206
  injectionsEnabled={response?.injections_enabled}
@@ -33,7 +33,6 @@
33
33
 
34
34
  let {
35
35
  linkInjections = $bindable(),
36
- pageText = '',
37
36
  loading = false,
38
37
  injectionsEnabled = false,
39
38
  aiStatus = {},
@@ -46,6 +45,8 @@
46
45
  const position: Position = JSON.parse(localStorage.getItem(editorPositionKey) || '{ "x": 0, "y": 0 }')
47
46
  const height: number = parseInt(localStorage.getItem(editorHeightKey) || '0')
48
47
 
48
+
49
+
49
50
  let editorElement: HTMLElement | null = $state(null)
50
51
  let manualInjectionActive = $state(false)
51
52
  let saving = $state(false)
@@ -85,7 +86,7 @@
85
86
  saving = true
86
87
  hasError = false
87
88
 
88
- linkInjections = await saveLinkInjections(linkInjections, pageText)
89
+ linkInjections = await saveLinkInjections(linkInjections)
89
90
  initialStateString = linkInjectionsString
90
91
  } catch {
91
92
  hasError = true
@@ -177,7 +178,6 @@
177
178
 
178
179
  {#if !loading}
179
180
  <Session
180
- {pageText}
181
181
  onallow={() => allowEditing = true}
182
182
  ondisallow={() => allowEditing = false}
183
183
  ontakeover={onreinitialize}
@@ -231,7 +231,6 @@
231
231
  style:top="{scrollDistance}px"
232
232
  transition:fly={{ x: Math.min(window.innerWidth, 320), duration: 200, opacity: 1 }}>
233
233
  <ManualInjection
234
- {pageText}
235
234
  onclose={() => manualInjectionActive = false}
236
235
  onsave={(linkInjection) => linkInjections.push(linkInjection)} />
237
236
  </div>
@@ -4,7 +4,7 @@
4
4
  import { onMount } from 'svelte'
5
5
  import { playPilotBaseUrl } from '$lib/constants'
6
6
  import { generateInjectionKey } from '$lib/api/externalPages'
7
- import { getLinkInjectionsParentElement } from '$lib/injectionElements'
7
+ import { getLinkInjectionsParentElement, getPageTextAndElements } from '$lib/injectionElements'
8
8
  import { heading } from '$lib/actions/heading'
9
9
  import { findSurroundingPhrases, cleanPhrase } from '$lib/text'
10
10
  import { getIndexOfSelection } from '$lib/selection'
@@ -15,12 +15,13 @@
15
15
  import TitleSearch from './Search/TitleSearch.svelte'
16
16
 
17
17
  interface Props {
18
- pageText: string
19
18
  onsave: (linkInjection: LinkInjection) => void
20
19
  onclose?: () => void
21
20
  }
22
21
 
23
- const { pageText = '', onsave, onclose = () => null }: Props = $props()
22
+ const { onsave, onclose = () => null }: Props = $props()
23
+
24
+ const { pageText } = getPageTextAndElements()
24
25
 
25
26
  let currentSelection = $state('')
26
27
  let selectionSentence = $state('')
@@ -4,7 +4,6 @@
4
4
  import Alert from './Alert.svelte'
5
5
 
6
6
  interface Props {
7
- pageText?: string,
8
7
  onpoll?: ({ injectionsEnabled, aiEnabled }: { injectionsEnabled: boolean, aiEnabled: boolean }) => void,
9
8
  onallow?: () => void,
10
9
  ondisallow?: () => void,
@@ -12,7 +11,6 @@
12
11
  }
13
12
 
14
13
  const {
15
- pageText = '',
16
14
  onpoll = () => null,
17
15
  onallow = () => null,
18
16
  ondisallow = () => null,
@@ -36,7 +34,7 @@
36
34
  }
37
35
 
38
36
  async function setSession(): Promise<void> {
39
- const result = await fetchAsSession(pageText)
37
+ const result = await fetchAsSession()
40
38
 
41
39
  if (!result) return
42
40
 
@@ -49,7 +47,7 @@
49
47
  }
50
48
 
51
49
  function takeOverEditing(): void {
52
- saveCurrentSession(pageText)
50
+ saveCurrentSession()
53
51
  ontakeover()
54
52
  }
55
53
  </script>
@@ -21,6 +21,7 @@
21
21
 
22
22
  let { navigate = () => null, searchQuery = $bindable(''), filter = $bindable({}), children }: Props = $props()
23
23
 
24
+ // svelte-ignore non_reactive_update
24
25
  let element: HTMLElement | null = null
25
26
  let height: string | null = $state(null)
26
27
  let clientWidth: number = $state(window.innerWidth)
@@ -36,7 +36,7 @@ describe('$lib/api/externalPages', () => {
36
36
  it('Should call api with GET method with given url', async () => {
37
37
  vi.mocked(api).mockResolvedValueOnce('Some response')
38
38
 
39
- const response = await fetchLinkInjections('Some text', { url: 'https://some-url', hash: 'some-hash' })
39
+ const response = await fetchLinkInjections({ url: 'https://some-url' })
40
40
 
41
41
  expect(response).toBe('Some response')
42
42
  expect(api).toHaveBeenCalledWith(
@@ -50,7 +50,7 @@ describe('$lib/api/externalPages', () => {
50
50
  it('Should call api with POST method and full details when fetching with method set to POST', async () => {
51
51
  vi.mocked(api).mockResolvedValueOnce('Some response')
52
52
 
53
- const response = await fetchLinkInjections('Some text', { url: 'https://some-url', hash: 'some-hash', params: { key: 'value' }, method: 'POST' })
53
+ const response = await fetchLinkInjections({ url: 'https://some-url', params: { key: 'value' }, method: 'POST' })
54
54
 
55
55
  expect(response).toBe('Some response')
56
56
  expect(api).toHaveBeenCalledWith(
@@ -69,14 +69,14 @@ describe('$lib/api/externalPages', () => {
69
69
  it('Should call api with full details when fetching with run_ai as true and method is set to POST', async () => {
70
70
  vi.mocked(api).mockResolvedValueOnce('Some response')
71
71
 
72
- const response = await fetchLinkInjections('Some text', { url: 'https://some-url', hash: 'some-hash', params: { run_ai: true }, method: 'POST' })
72
+ const response = await fetchLinkInjections({ url: 'https://some-url', params: { run_ai: true }, method: 'POST' })
73
73
 
74
74
  expect(response).toBe('Some response')
75
75
  expect(api).toHaveBeenCalledWith(
76
76
  expect.stringContaining('api-token'),
77
77
  expect.objectContaining({
78
78
  body: {
79
- hash: 'some-hash',
79
+ hash: expect.any(String),
80
80
  url: 'https://some-url',
81
81
  run_ai: true,
82
82
  metadata: {
@@ -84,7 +84,7 @@ describe('$lib/api/externalPages', () => {
84
84
  content_modified_time: null,
85
85
  content_published_time: null,
86
86
  },
87
- page_text: 'Some text',
87
+ page_text: expect.any(String),
88
88
  },
89
89
  method: 'POST',
90
90
  }),
@@ -95,13 +95,13 @@ describe('$lib/api/externalPages', () => {
95
95
  // @ts-ignore
96
96
  window.PlayPilotLinkInjections = null
97
97
 
98
- await expect(async () => await fetchLinkInjections('Some text', { hash: 'some-hash' })).rejects.toThrowError('No token was provided')
98
+ await expect(async () => await fetchLinkInjections()).rejects.toThrowError('No token was provided')
99
99
  })
100
100
 
101
101
  it('Should throw when response was incorrect', async () => {
102
102
  vi.mocked(api).mockRejectedValueOnce({ ok: false })
103
103
 
104
- await expect(async () => await fetchLinkInjections('Some text', { hash: 'some-hash' })).rejects.toThrowError()
104
+ await expect(async () => await fetchLinkInjections()).rejects.toThrowError()
105
105
  })
106
106
 
107
107
  it('Should use given api token', async () => {
@@ -110,7 +110,7 @@ describe('$lib/api/externalPages', () => {
110
110
  // @ts-ignore
111
111
  window.PlayPilotLinkInjections = { token: 'token' }
112
112
 
113
- await fetchLinkInjections('Some text', { hash: 'some-hash' })
113
+ await fetchLinkInjections()
114
114
 
115
115
  expect(api).toHaveBeenCalledWith(
116
116
  expect.stringContaining('api-token=token'),
@@ -124,7 +124,7 @@ describe('$lib/api/externalPages', () => {
124
124
  // @ts-ignore
125
125
  window.PlayPilotLinkInjections = null
126
126
 
127
- await expect(async () => await fetchLinkInjections('Some text', { hash: 'some-hash' })).rejects.toThrowError()
127
+ await expect(async () => await fetchLinkInjections()).rejects.toThrowError()
128
128
  expect(api).not.toHaveBeenCalled()
129
129
  })
130
130
 
@@ -134,7 +134,7 @@ describe('$lib/api/externalPages', () => {
134
134
  vi.mocked(authorize).mockResolvedValueOnce(true)
135
135
  vi.mocked(isEditorialModeEnabled).mockResolvedValueOnce(true)
136
136
 
137
- const response = await fetchLinkInjections('Some text', { hash: 'some-hash' })
137
+ const response = await fetchLinkInjections()
138
138
 
139
139
  expect(response).toBe('Some response')
140
140
  expect(api).toHaveBeenCalledWith(
@@ -146,7 +146,7 @@ describe('$lib/api/externalPages', () => {
146
146
  it('Should call api with given language', async () => {
147
147
  vi.mocked(api).mockResolvedValueOnce('Some response')
148
148
 
149
- await fetchLinkInjections('Some text', { hash: 'some-hash' })
149
+ await fetchLinkInjections()
150
150
 
151
151
  expect(api).toHaveBeenCalledWith(
152
152
  expect.stringContaining(`&language=${Language.English}`),
@@ -156,7 +156,7 @@ describe('$lib/api/externalPages', () => {
156
156
  // @ts-ignore
157
157
  window.PlayPilotLinkInjections = { token: 'token', language: Language.Swedish }
158
158
 
159
- await fetchLinkInjections('Some text', { hash: 'some-hash' })
159
+ await fetchLinkInjections()
160
160
 
161
161
  expect(api).toHaveBeenCalledWith(
162
162
  expect.stringContaining(`&language=${Language.Swedish}`),
@@ -169,7 +169,7 @@ describe('$lib/api/externalPages', () => {
169
169
  it('Should poll endpoint while results are not yet ready when requireCompletedResults is true', async () => {
170
170
  vi.mocked(api).mockResolvedValueOnce({ ai_enabled: true, ai_running: true })
171
171
 
172
- pollLinkInjections('Some text', { requireCompletedResult: true, pollInterval: 500 })
172
+ pollLinkInjections({ requireCompletedResult: true, pollInterval: 500 })
173
173
 
174
174
  expect(api).toHaveBeenCalled()
175
175
 
@@ -184,7 +184,7 @@ describe('$lib/api/externalPages', () => {
184
184
  vi.mocked(api).mockResolvedValue({ ai_enabled: true, ai_running: true })
185
185
 
186
186
  const onpoll = vi.fn()
187
- pollLinkInjections('Some text', { requireCompletedResult: true, pollInterval: 500, onpoll })
187
+ pollLinkInjections({ requireCompletedResult: true, pollInterval: 500, onpoll })
188
188
 
189
189
  await waitFor(() => {
190
190
  expect(onpoll).toHaveBeenCalledTimes(1)
@@ -201,7 +201,7 @@ describe('$lib/api/externalPages', () => {
201
201
  const response = { ai_enabled: true, ai_running: false, manual_injections: [{ title: 'value' }] }
202
202
  vi.mocked(api).mockResolvedValueOnce(response)
203
203
 
204
- const result = await pollLinkInjections('Some text', { requireCompletedResult: true, pollInterval: 500 })
204
+ const result = await pollLinkInjections({ requireCompletedResult: true, pollInterval: 500 })
205
205
 
206
206
  expect(api).toHaveBeenCalled()
207
207
  expect(result).toEqual(response)
@@ -213,7 +213,7 @@ describe('$lib/api/externalPages', () => {
213
213
  it('Should stop polling if max limit was exceeded', async () => {
214
214
  vi.mocked(api).mockResolvedValueOnce({ ai_enabled: true, ai_running: true })
215
215
 
216
- await pollLinkInjections('Some text', { requireCompletedResult: true, pollInterval: 200, maxTries: 2 })
216
+ await pollLinkInjections({ requireCompletedResult: true, pollInterval: 200, maxTries: 2 })
217
217
 
218
218
  await new Promise(res => setTimeout(res, 1000)) // Wait for a potential poll
219
219
  expect(api).toHaveBeenCalledTimes(2)
@@ -222,7 +222,7 @@ describe('$lib/api/externalPages', () => {
222
222
  it('Should continue polling if endpoint throws error', async () => {
223
223
  vi.mocked(api).mockRejectedValueOnce({ ok: false })
224
224
 
225
- pollLinkInjections('Some text', { pollInterval: 500 })
225
+ pollLinkInjections({ pollInterval: 500 })
226
226
 
227
227
  expect(api).toHaveBeenCalled()
228
228
 
@@ -233,16 +233,16 @@ describe('$lib/api/externalPages', () => {
233
233
  it('Should return empty array if manual_injections are null', async () => {
234
234
  vi.mocked(api).mockResolvedValueOnce({ ai_enabled: null, ai_running: null })
235
235
 
236
- const result = await pollLinkInjections('Some text')
236
+ const result = await pollLinkInjections()
237
237
  expect(result).toEqual(expect.objectContaining({ manual_injections: [], ai_injections: [] }))
238
238
  })
239
239
 
240
240
  it('Should call api twice if runAiWhenRelevant is true and results expects new run', async () => {
241
- document.body.innerHTML = '<meta content="2025-04-20T00:00:00Z" property="article:modified_time">'
241
+ document.body.innerHTML = '<main>Some text</main> <meta content="2025-04-20T00:00:00Z" property="article:modified_time">'
242
242
 
243
243
  vi.mocked(api).mockResolvedValueOnce({ ai_enabled: true })
244
244
 
245
- await pollLinkInjections('Some text', { runAiWhenRelevant: true, maxTries: 1 })
245
+ await pollLinkInjections({ runAiWhenRelevant: true, maxTries: 1 })
246
246
 
247
247
  expect(api).toHaveBeenCalledTimes(2)
248
248
  })
@@ -250,7 +250,7 @@ describe('$lib/api/externalPages', () => {
250
250
  it('Should call api twice if initial response returns 404', async () => {
251
251
  vi.mocked(api).mockRejectedValueOnce({ status: 404 })
252
252
 
253
- await pollLinkInjections('Some text', { maxTries: 1 })
253
+ await pollLinkInjections({ maxTries: 1 })
254
254
 
255
255
  expect(api).toHaveBeenCalledTimes(2)
256
256
  expect(api).toHaveBeenCalledWith(
@@ -266,7 +266,7 @@ describe('$lib/api/externalPages', () => {
266
266
  it('Should call api three times if initial response returns 404 and second response returns ai_enabled set to true', async () => {
267
267
  vi.mocked(api).mockRejectedValueOnce({ status: 404 }).mockResolvedValueOnce({ ai_enabled: true })
268
268
 
269
- await pollLinkInjections('Some text', { maxTries: 1 })
269
+ await pollLinkInjections({ maxTries: 1 })
270
270
 
271
271
  expect(api).toHaveBeenCalledTimes(3)
272
272
  expect(api).toHaveBeenCalledWith(
@@ -287,6 +287,28 @@ describe('$lib/api/externalPages', () => {
287
287
  }),
288
288
  )
289
289
  })
290
+
291
+ it('Should use the given selector when present', async () => {
292
+ vi.mocked(api).mockRejectedValueOnce({ status: 404 }).mockResolvedValueOnce({ ai_enabled: true })
293
+
294
+ document.body.innerHTML = '<div class="some-element"><p>Here</p></div> <p>Not here</p>'
295
+
296
+ // @ts-ignore
297
+ window.PlayPilotLinkInjections = { token: 'token', selector: '.some-element' }
298
+
299
+ await pollLinkInjections({ runAiWhenRelevant: true, maxTries: 1 })
300
+
301
+ await waitFor(() => {
302
+ expect(api).toHaveBeenCalledWith(
303
+ expect.any(String),
304
+ expect.objectContaining({
305
+ body: expect.objectContaining({
306
+ page_text: 'Here',
307
+ }),
308
+ }),
309
+ )
310
+ })
311
+ })
290
312
  })
291
313
 
292
314
  describe('runAiBasedOnResponse', () => {
@@ -51,7 +51,6 @@ describe('main.ts', () => {
51
51
  describe('initialize', () => {
52
52
  it('Should not call setConsent when only a token is passed', () => {
53
53
  window.PlayPilotLinkInjections.initialize({ token: 'a' })
54
- console.log(window.PlayPilotLinkInjections)
55
54
 
56
55
  expect(setConsent).not.toHaveBeenCalled()
57
56
 
@@ -134,8 +134,8 @@ describe('$routes/+page.svelte', () => {
134
134
  render(page)
135
135
 
136
136
  await waitFor(() => {
137
- expect(pollLinkInjections).toHaveBeenCalledWith(expect.any(String), { maxTries: 1, runAiWhenRelevant: true })
138
- expect(pollLinkInjections).toHaveBeenCalledWith(expect.any(String), { requireCompletedResult: true, onpoll: expect.any(Function) })
137
+ expect(pollLinkInjections).toHaveBeenCalledWith({ maxTries: 1, runAiWhenRelevant: true })
138
+ expect(pollLinkInjections).toHaveBeenCalledWith({ requireCompletedResult: true, onpoll: expect.any(Function) })
139
139
  })
140
140
  })
141
141
 
@@ -158,7 +158,7 @@ describe('$routes/+page.svelte', () => {
158
158
  })
159
159
 
160
160
  await waitFor(() => {
161
- expect(pollLinkInjections).toHaveBeenCalledWith(expect.any(String), { requireCompletedResult: true, onpoll: expect.any(Function) })
161
+ expect(pollLinkInjections).toHaveBeenCalledWith({ requireCompletedResult: true, onpoll: expect.any(Function) })
162
162
  })
163
163
 
164
164
  expect(pollLinkInjections).toHaveBeenCalledTimes(2)
@@ -221,7 +221,7 @@ describe('$routes/+page.svelte', () => {
221
221
  render(page)
222
222
 
223
223
  await waitFor(() => {
224
- expect(pollLinkInjections).toHaveBeenCalledWith('Here', { maxTries: 1, runAiWhenRelevant: true })
224
+ expect(pollLinkInjections).toHaveBeenCalledWith({ maxTries: 1, runAiWhenRelevant: true })
225
225
  })
226
226
  })
227
227