@caruuto/caruuto-js 0.4.3 → 0.6.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/index.js +2 -1
- package/lib/constants.js +3 -0
- package/lib/extensions/ai.js +397 -0
- package/lib/extensions/email.js +8 -53
- package/lib/extensions/tokens.js +6 -29
- package/lib/extensions/utils.js +32 -0
- package/lib/extensions.js +5 -3
- package/lib/filters.js +0 -15
- package/lib/helpers.js +26 -59
- package/lib/performFetch.js +45 -74
- package/lib/terminators.js +6 -124
- package/package.json +6 -24
- package/.editorconfig +0 -9
- package/.eslintignore +0 -1
- package/.eslintrc.js +0 -19
- package/.prettierignore +0 -1
- package/scripts/coverage.js +0 -13
- package/x.js +0 -53
package/index.js
CHANGED
package/lib/constants.js
ADDED
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
const undici = require('undici')
|
|
2
|
+
const { API_KEY_PREFIX } = require('../constants')
|
|
3
|
+
|
|
4
|
+
module.exports = function (CaruutoClient, clientInstance) {
|
|
5
|
+
CaruutoClient.prototype.ai = {
|
|
6
|
+
/**
|
|
7
|
+
* Send a message and receive a complete blocking response.
|
|
8
|
+
*
|
|
9
|
+
* @param {Object} opts
|
|
10
|
+
* @param {string} opts.projectId - Caruuto project ID (UUID)
|
|
11
|
+
* @param {string} opts.message - User's message
|
|
12
|
+
* @param {string} [opts.conversationId] - Existing conversation ID (omit to start new)
|
|
13
|
+
* @param {string} [opts.clientSessionId] - Client's anonymous user token
|
|
14
|
+
* @param {string} [opts.source] - 'widget' | 'api' | 'embed'
|
|
15
|
+
* @param {string} [opts.referrerUrl] - URL where the chat is embedded
|
|
16
|
+
* @param {string} [opts.leadEmail] - Capture a lead email on this turn
|
|
17
|
+
* @param {string} [opts.leadName] - Lead name (paired with leadEmail)
|
|
18
|
+
* @param {string} [opts.type] - Restrict retrieval to a single knowledge type
|
|
19
|
+
* @param {number} [opts.maxItems] - Max knowledge items to retrieve (1–10)
|
|
20
|
+
* @returns {Promise<{ answer, conversation_id, context, entities, _meta, from_cache? }>}
|
|
21
|
+
*/
|
|
22
|
+
query: async function (opts = {}) {
|
|
23
|
+
const body = buildRequestBody(opts, false)
|
|
24
|
+
const response = await doFetch(
|
|
25
|
+
this,
|
|
26
|
+
`${this.options.caruutoUrl}/api/ai/external`,
|
|
27
|
+
body
|
|
28
|
+
)
|
|
29
|
+
return response.status === 204 ? undefined : response.json()
|
|
30
|
+
}.bind(clientInstance),
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Send a message and receive a streaming response.
|
|
34
|
+
* Returns an async generator. Iterate with `for await`.
|
|
35
|
+
*
|
|
36
|
+
* Yields:
|
|
37
|
+
* { type: 'text', text: string }
|
|
38
|
+
* { type: 'done', conversation_id: string, context: [], entities: [], _meta: {}, from_cache?: boolean }
|
|
39
|
+
*
|
|
40
|
+
* @param {Object} opts - Same options as query()
|
|
41
|
+
* @returns {AsyncGenerator}
|
|
42
|
+
*/
|
|
43
|
+
stream: async function* (opts = {}) {
|
|
44
|
+
const body = buildRequestBody(opts, true)
|
|
45
|
+
const response = await doFetch(
|
|
46
|
+
this,
|
|
47
|
+
`${this.options.caruutoUrl}/api/ai/external`,
|
|
48
|
+
body
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
const reader = response.body.getReader()
|
|
52
|
+
const decoder = new TextDecoder()
|
|
53
|
+
let buffer = ''
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
while (true) {
|
|
57
|
+
const { done, value } = await reader.read()
|
|
58
|
+
if (done) {
|
|
59
|
+
break
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
buffer += decoder.decode(value, { stream: true })
|
|
63
|
+
const lines = buffer.split('\n')
|
|
64
|
+
buffer = lines.pop()
|
|
65
|
+
|
|
66
|
+
for (const line of lines) {
|
|
67
|
+
if (!line.startsWith('data: ')) {
|
|
68
|
+
continue
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const raw = line.slice(6).trim()
|
|
72
|
+
if (!raw) {
|
|
73
|
+
continue
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
let parsed
|
|
77
|
+
try {
|
|
78
|
+
parsed = JSON.parse(raw)
|
|
79
|
+
} catch (_) {
|
|
80
|
+
continue
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (parsed.error) {
|
|
84
|
+
throw new Error(parsed.error)
|
|
85
|
+
} else if (parsed.done) {
|
|
86
|
+
yield { type: 'done', ...parsed }
|
|
87
|
+
} else if (parsed.text !== undefined) {
|
|
88
|
+
yield { type: 'text', text: parsed.text }
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
} finally {
|
|
93
|
+
reader.releaseLock()
|
|
94
|
+
}
|
|
95
|
+
}.bind(clientInstance),
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Explicitly end a conversation, marking it as 'ended'.
|
|
99
|
+
* Call this when the user closes the chat widget or navigates away.
|
|
100
|
+
*
|
|
101
|
+
* @param {Object} opts
|
|
102
|
+
* @param {string} opts.conversationId - The conversation to end
|
|
103
|
+
* @param {string} opts.projectId - The project the conversation belongs to
|
|
104
|
+
* @returns {Promise<{ id, status, turn_count, total_input_tokens, total_output_tokens, total_tokens, started_at, ended_at }>}
|
|
105
|
+
*/
|
|
106
|
+
endConversation: async function ({ conversationId, projectId } = {}) {
|
|
107
|
+
if (!conversationId) {
|
|
108
|
+
throw new Error('conversationId is required')
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (!projectId) {
|
|
112
|
+
throw new Error('projectId is required')
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const url = `${this.options.caruutoUrl}/api/ai/conversations/${conversationId}/end`
|
|
116
|
+
const response = await doFetch(this, url, { project_id: projectId })
|
|
117
|
+
return response.json()
|
|
118
|
+
}.bind(clientInstance),
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Retrieve assembled context for a message without calling the LLM.
|
|
122
|
+
* Use this when you want to call your own LLM (e.g. with AI SDK + tools)
|
|
123
|
+
* and need Caruuto to handle retrieval, entity detection, and prompt assembly.
|
|
124
|
+
*
|
|
125
|
+
* On a cache hit, returns { from_cache: true, cached_answer, conversation_id, _meta }.
|
|
126
|
+
* On a miss, returns { from_cache: false, system_prompt, history, context, entities,
|
|
127
|
+
* conversation_id, _meta }.
|
|
128
|
+
*
|
|
129
|
+
* @param {Object} opts - Same options as query(), minus stream/lead fields
|
|
130
|
+
* @returns {Promise<Object>}
|
|
131
|
+
*/
|
|
132
|
+
context: async function (opts = {}) {
|
|
133
|
+
const {
|
|
134
|
+
projectId,
|
|
135
|
+
message,
|
|
136
|
+
conversationId,
|
|
137
|
+
clientSessionId,
|
|
138
|
+
source,
|
|
139
|
+
referrerUrl,
|
|
140
|
+
type,
|
|
141
|
+
maxItems
|
|
142
|
+
} = opts
|
|
143
|
+
|
|
144
|
+
const body = { project_id: projectId, message }
|
|
145
|
+
|
|
146
|
+
if (conversationId) body.conversation_id = conversationId
|
|
147
|
+
if (clientSessionId) body.client_session_id = clientSessionId
|
|
148
|
+
if (source) body.source = source
|
|
149
|
+
if (referrerUrl) body.referrer_url = referrerUrl
|
|
150
|
+
if (type) body.type = type
|
|
151
|
+
if (maxItems !== undefined) body.max_items = maxItems
|
|
152
|
+
|
|
153
|
+
const response = await doFetch(
|
|
154
|
+
this,
|
|
155
|
+
`${this.options.caruutoUrl}/api/ai/context`,
|
|
156
|
+
body
|
|
157
|
+
)
|
|
158
|
+
return response.json()
|
|
159
|
+
}.bind(clientInstance),
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Persist a completed turn (user message + assistant response) to Caruuto.
|
|
163
|
+
* Call this after your LLM stream completes with the full response text and
|
|
164
|
+
* token usage. Caruuto writes to ai_conversation_messages, updates token
|
|
165
|
+
* totals on the conversation, and caches the answer if it's the first turn.
|
|
166
|
+
*
|
|
167
|
+
* @param {Object} opts
|
|
168
|
+
* @param {string} opts.conversationId - From context() response
|
|
169
|
+
* @param {string} opts.projectId
|
|
170
|
+
* @param {string} opts.userContent - The user's message text
|
|
171
|
+
* @param {string} opts.assistantContent - The full assistant response
|
|
172
|
+
* @param {string} [opts.modelId] - Model used (e.g. 'gpt-4o-mini')
|
|
173
|
+
* @param {number} [opts.inputTokens]
|
|
174
|
+
* @param {number} [opts.outputTokens]
|
|
175
|
+
* @param {Object[]} [opts.contextItems] - From context() response
|
|
176
|
+
* @param {string[]} [opts.entitiesReferenced] - Entity IDs from context()
|
|
177
|
+
* @param {string} [opts.leadEmail]
|
|
178
|
+
* @param {string} [opts.leadName]
|
|
179
|
+
* @returns {Promise<{ ok: boolean, turn_count: number }>}
|
|
180
|
+
*/
|
|
181
|
+
/**
|
|
182
|
+
* Load a conversation and its messages, formatted as AI SDK UIMessage objects.
|
|
183
|
+
* Use this to restore a prior conversation into the chat UI.
|
|
184
|
+
*
|
|
185
|
+
* @param {Object} opts
|
|
186
|
+
* @param {string} opts.conversationId - The Caruuto conversation ID
|
|
187
|
+
* @param {string} opts.projectId
|
|
188
|
+
* @returns {Promise<{ id, status, turn_count, started_at, ended_at, messages }>}
|
|
189
|
+
*/
|
|
190
|
+
/**
|
|
191
|
+
* Set a conversation's visibility to 'public' or 'private'.
|
|
192
|
+
*
|
|
193
|
+
* @param {Object} opts
|
|
194
|
+
* @param {string} opts.conversationId
|
|
195
|
+
* @param {string} opts.projectId
|
|
196
|
+
* @param {'public'|'private'} opts.visibility
|
|
197
|
+
* @returns {Promise<{ id, visibility, share_url }>}
|
|
198
|
+
*/
|
|
199
|
+
/**
|
|
200
|
+
* Fork a conversation or question cache entry into a new conversation.
|
|
201
|
+
*
|
|
202
|
+
* @param {Object} opts
|
|
203
|
+
* @param {string} opts.sourceId - Conversation ID or question cache entry ID
|
|
204
|
+
* @param {string} opts.projectId
|
|
205
|
+
* @param {'conversation'|'question_cache'} opts.source
|
|
206
|
+
* @returns {Promise<{ id: string }>} New conversation ID
|
|
207
|
+
*/
|
|
208
|
+
forkConversation: async function ({ sourceId, projectId, source = 'conversation' } = {}) {
|
|
209
|
+
if (!sourceId) throw new Error('sourceId is required')
|
|
210
|
+
if (!projectId) throw new Error('projectId is required')
|
|
211
|
+
|
|
212
|
+
const url = `${this.options.caruutoUrl}/api/ai/conversations/${sourceId}/fork`
|
|
213
|
+
const response = await doFetch(this, url, { project_id: projectId, source })
|
|
214
|
+
return response.json()
|
|
215
|
+
}.bind(clientInstance),
|
|
216
|
+
|
|
217
|
+
shareConversation: async function ({ conversationId, projectId, visibility = 'public' } = {}) {
|
|
218
|
+
if (!conversationId) throw new Error('conversationId is required')
|
|
219
|
+
if (!projectId) throw new Error('projectId is required')
|
|
220
|
+
|
|
221
|
+
const url = `${this.options.caruutoUrl}/api/ai/conversations/${conversationId}/share`
|
|
222
|
+
const response = await doFetch(this, url, { project_id: projectId, visibility })
|
|
223
|
+
return response.json()
|
|
224
|
+
}.bind(clientInstance),
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Fetch a publicly shared conversation without authentication.
|
|
228
|
+
* Resolves with null if the conversation is private or not found.
|
|
229
|
+
*
|
|
230
|
+
* @param {Object} opts
|
|
231
|
+
* @param {string} opts.conversationId
|
|
232
|
+
* @returns {Promise<{ id, turn_count, started_at, ended_at, messages } | null>}
|
|
233
|
+
*/
|
|
234
|
+
getPublicConversation: async function ({ conversationId } = {}) {
|
|
235
|
+
if (!conversationId) throw new Error('conversationId is required')
|
|
236
|
+
|
|
237
|
+
const fetch = this.options.fetch || undici.fetch
|
|
238
|
+
const url = `${this.options.caruutoUrl}/api/ai/conversations/${conversationId}/public`
|
|
239
|
+
|
|
240
|
+
const response = await fetch(url, { method: 'GET' })
|
|
241
|
+
|
|
242
|
+
if (response.status === 403 || response.status === 404) {
|
|
243
|
+
return null
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if (!response.ok) {
|
|
247
|
+
const err = new Error('Failed to fetch public conversation')
|
|
248
|
+
err.status = response.status
|
|
249
|
+
throw err
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return response.json()
|
|
253
|
+
}.bind(clientInstance),
|
|
254
|
+
|
|
255
|
+
loadConversation: async function ({ conversationId, projectId } = {}) {
|
|
256
|
+
if (!conversationId) {
|
|
257
|
+
throw new Error('conversationId is required')
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (!projectId) {
|
|
261
|
+
throw new Error('projectId is required')
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const url = `${this.options.caruutoUrl}/api/ai/conversations/${conversationId}?project_id=${encodeURIComponent(projectId)}`
|
|
265
|
+
const response = await doGet(this, url)
|
|
266
|
+
return response.json()
|
|
267
|
+
}.bind(clientInstance),
|
|
268
|
+
|
|
269
|
+
saveTurn: async function ({
|
|
270
|
+
conversationId,
|
|
271
|
+
projectId,
|
|
272
|
+
userContent,
|
|
273
|
+
assistantContent,
|
|
274
|
+
modelId,
|
|
275
|
+
inputTokens,
|
|
276
|
+
outputTokens,
|
|
277
|
+
contextItems,
|
|
278
|
+
entitiesReferenced,
|
|
279
|
+
leadEmail,
|
|
280
|
+
leadName
|
|
281
|
+
} = {}) {
|
|
282
|
+
if (!conversationId) {
|
|
283
|
+
throw new Error('conversationId is required')
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if (!projectId) {
|
|
287
|
+
throw new Error('projectId is required')
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const body = {
|
|
291
|
+
project_id: projectId,
|
|
292
|
+
user_content: userContent,
|
|
293
|
+
assistant_content: assistantContent
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (modelId) body.model_id = modelId
|
|
297
|
+
if (inputTokens !== undefined) body.input_tokens = inputTokens
|
|
298
|
+
if (outputTokens !== undefined) body.output_tokens = outputTokens
|
|
299
|
+
if (contextItems) body.context_items = contextItems
|
|
300
|
+
if (entitiesReferenced) body.entities_referenced = entitiesReferenced
|
|
301
|
+
if (leadEmail) body.lead_email = leadEmail
|
|
302
|
+
if (leadName) body.lead_name = leadName
|
|
303
|
+
|
|
304
|
+
const url = `${this.options.caruutoUrl}/api/ai/conversations/${conversationId}/turn`
|
|
305
|
+
const response = await doFetch(this, url, body)
|
|
306
|
+
return response.json()
|
|
307
|
+
}.bind(clientInstance)
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// ---------------------------------------------------------------------------
|
|
312
|
+
// Helpers (module-private)
|
|
313
|
+
// ---------------------------------------------------------------------------
|
|
314
|
+
|
|
315
|
+
function buildRequestBody(opts, stream) {
|
|
316
|
+
const {
|
|
317
|
+
projectId,
|
|
318
|
+
message,
|
|
319
|
+
conversationId,
|
|
320
|
+
clientSessionId,
|
|
321
|
+
source,
|
|
322
|
+
referrerUrl,
|
|
323
|
+
leadEmail,
|
|
324
|
+
leadName,
|
|
325
|
+
type,
|
|
326
|
+
maxItems
|
|
327
|
+
} = opts
|
|
328
|
+
|
|
329
|
+
const body = { project_id: projectId, message, stream }
|
|
330
|
+
|
|
331
|
+
if (conversationId) body.conversation_id = conversationId
|
|
332
|
+
if (clientSessionId) body.client_session_id = clientSessionId
|
|
333
|
+
if (source) body.source = source
|
|
334
|
+
if (referrerUrl) body.referrer_url = referrerUrl
|
|
335
|
+
if (leadEmail) body.lead_email = leadEmail
|
|
336
|
+
if (leadName) body.lead_name = leadName
|
|
337
|
+
if (type) body.type = type
|
|
338
|
+
if (maxItems !== undefined) body.max_items = maxItems
|
|
339
|
+
|
|
340
|
+
return body
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
async function doGet(client, url) {
|
|
344
|
+
const fetch = client.options.fetch || undici.fetch
|
|
345
|
+
|
|
346
|
+
const response = await fetch(url, {
|
|
347
|
+
method: 'GET',
|
|
348
|
+
headers: {
|
|
349
|
+
authorization: `${API_KEY_PREFIX} ${client.options.caruutoApiKey}`
|
|
350
|
+
}
|
|
351
|
+
})
|
|
352
|
+
|
|
353
|
+
if (!response.ok) {
|
|
354
|
+
const err = new Error('Caruuto AI request failed')
|
|
355
|
+
err.status = response.status
|
|
356
|
+
|
|
357
|
+
try {
|
|
358
|
+
const data = await response.clone().json()
|
|
359
|
+
if (data.error) {
|
|
360
|
+
err.message = data.error
|
|
361
|
+
}
|
|
362
|
+
} catch (_) {}
|
|
363
|
+
|
|
364
|
+
throw err
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
return response
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
async function doFetch(client, url, body) {
|
|
371
|
+
const fetch = client.options.fetch || undici.fetch
|
|
372
|
+
|
|
373
|
+
const response = await fetch(url, {
|
|
374
|
+
method: 'POST',
|
|
375
|
+
headers: {
|
|
376
|
+
'content-type': 'application/json',
|
|
377
|
+
authorization: `${API_KEY_PREFIX} ${client.options.caruutoApiKey}`
|
|
378
|
+
},
|
|
379
|
+
body: JSON.stringify(body)
|
|
380
|
+
})
|
|
381
|
+
|
|
382
|
+
if (!response.ok) {
|
|
383
|
+
const err = new Error('Caruuto AI request failed')
|
|
384
|
+
err.status = response.status
|
|
385
|
+
|
|
386
|
+
try {
|
|
387
|
+
const data = await response.clone().json()
|
|
388
|
+
if (data.error) {
|
|
389
|
+
err.message = data.error
|
|
390
|
+
}
|
|
391
|
+
} catch (_) {}
|
|
392
|
+
|
|
393
|
+
throw err
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
return response
|
|
397
|
+
}
|
package/lib/extensions/email.js
CHANGED
|
@@ -1,57 +1,12 @@
|
|
|
1
|
+
const { makeEndpointMethod } = require('./utils')
|
|
2
|
+
|
|
1
3
|
module.exports = function (CaruutoClient, clientInstance) {
|
|
2
4
|
CaruutoClient.prototype.email = {
|
|
3
|
-
find:
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
method: 'GET',
|
|
10
|
-
uri: this._buildURL({ useParams: true })
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
this._setHeader('content-type', 'application/json')
|
|
14
|
-
|
|
15
|
-
return this._createRequestObject(requestPayload)
|
|
16
|
-
}.bind(clientInstance),
|
|
17
|
-
queue: function (data) {
|
|
18
|
-
this.endpoint = 'email'
|
|
19
|
-
|
|
20
|
-
const requestPayload = {
|
|
21
|
-
body: data,
|
|
22
|
-
method: 'POST',
|
|
23
|
-
uri: this._buildURL()
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
this._setHeader('content-type', 'application/json')
|
|
27
|
-
|
|
28
|
-
return this._createRequestObject(requestPayload)
|
|
29
|
-
}.bind(clientInstance),
|
|
30
|
-
update: function (data) {
|
|
31
|
-
this.endpoint = 'email'
|
|
32
|
-
|
|
33
|
-
const requestPayload = {
|
|
34
|
-
body: data,
|
|
35
|
-
method: 'PUT',
|
|
36
|
-
uri: this._buildURL()
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
this._setHeader('content-type', 'application/json')
|
|
40
|
-
|
|
41
|
-
return this._createRequestObject(requestPayload)
|
|
42
|
-
}.bind(clientInstance),
|
|
43
|
-
delete: function (data) {
|
|
44
|
-
this.endpoint = 'email'
|
|
45
|
-
|
|
46
|
-
const requestPayload = {
|
|
47
|
-
body: data,
|
|
48
|
-
method: 'DELETE',
|
|
49
|
-
uri: this._buildURL()
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
this._setHeader('content-type', 'application/json')
|
|
53
|
-
|
|
54
|
-
return this._createRequestObject(requestPayload)
|
|
55
|
-
}.bind(clientInstance)
|
|
5
|
+
find: makeEndpointMethod('email', 'GET', { query: true }).bind(
|
|
6
|
+
clientInstance
|
|
7
|
+
),
|
|
8
|
+
queue: makeEndpointMethod('email', 'POST').bind(clientInstance),
|
|
9
|
+
update: makeEndpointMethod('email', 'PUT').bind(clientInstance),
|
|
10
|
+
delete: makeEndpointMethod('email', 'DELETE').bind(clientInstance)
|
|
56
11
|
}
|
|
57
12
|
}
|
package/lib/extensions/tokens.js
CHANGED
|
@@ -1,33 +1,10 @@
|
|
|
1
|
+
const { makeEndpointMethod } = require('./utils')
|
|
2
|
+
|
|
1
3
|
module.exports = function (CaruutoClient, clientInstance) {
|
|
2
4
|
CaruutoClient.prototype.tokens = {
|
|
3
|
-
create:
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
body: data,
|
|
8
|
-
method: 'POST',
|
|
9
|
-
uri: this._buildURL()
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
this._setHeader('content-type', 'application/json')
|
|
13
|
-
|
|
14
|
-
return this._createRequestObject(requestPayload)
|
|
15
|
-
}.bind(clientInstance),
|
|
16
|
-
verify: function (token) {
|
|
17
|
-
this.endpoint = 'tokens'
|
|
18
|
-
|
|
19
|
-
const requestPayload = {
|
|
20
|
-
body: {
|
|
21
|
-
token,
|
|
22
|
-
verify: true
|
|
23
|
-
},
|
|
24
|
-
method: 'POST',
|
|
25
|
-
uri: this._buildURL()
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
this._setHeader('content-type', 'application/json')
|
|
29
|
-
|
|
30
|
-
return this._createRequestObject(requestPayload)
|
|
31
|
-
}.bind(clientInstance)
|
|
5
|
+
create: makeEndpointMethod('tokens', 'POST').bind(clientInstance),
|
|
6
|
+
verify: makeEndpointMethod('tokens', 'POST', {
|
|
7
|
+
transform: token => ({ token, verify: true })
|
|
8
|
+
}).bind(clientInstance)
|
|
32
9
|
}
|
|
33
10
|
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates an endpoint method bound to a given HTTP method and endpoint name.
|
|
3
|
+
*
|
|
4
|
+
* @param {string} endpoint - The API endpoint path segment (e.g. 'email', 'tokens')
|
|
5
|
+
* @param {string} httpMethod - HTTP verb ('GET', 'POST', 'PUT', 'DELETE')
|
|
6
|
+
* @param {Object} [opts]
|
|
7
|
+
* @param {boolean} [opts.query] - If true, the argument is treated as a query filter (GET style)
|
|
8
|
+
* @param {Function} [opts.transform] - Transform the argument before using it as the request body
|
|
9
|
+
*/
|
|
10
|
+
function makeEndpointMethod(endpoint, httpMethod, opts = {}) {
|
|
11
|
+
const { transform, query: isQuery = false } = opts
|
|
12
|
+
|
|
13
|
+
return function (data) {
|
|
14
|
+
this.endpoint = endpoint
|
|
15
|
+
|
|
16
|
+
const requestPayload = { method: httpMethod }
|
|
17
|
+
|
|
18
|
+
if (isQuery) {
|
|
19
|
+
this.query = data
|
|
20
|
+
requestPayload.uri = this._buildURL({ useParams: true })
|
|
21
|
+
} else {
|
|
22
|
+
requestPayload.body = transform ? transform(data) : data
|
|
23
|
+
requestPayload.uri = this._buildURL()
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
this._setHeader('content-type', 'application/json')
|
|
27
|
+
|
|
28
|
+
return this._createRequestObject(requestPayload)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
module.exports = { makeEndpointMethod }
|
package/lib/extensions.js
CHANGED
|
@@ -33,11 +33,15 @@ module.exports = function (CaruutoClient, clientInstance) {
|
|
|
33
33
|
```
|
|
34
34
|
*/
|
|
35
35
|
|
|
36
|
+
const aiExtension = require('./extensions/ai')
|
|
36
37
|
const emailExtension = require('./extensions/email')
|
|
37
38
|
const tokensExtension = require('./extensions/tokens')
|
|
38
39
|
|
|
39
|
-
// Load all extensions from the lib/extensions directory/
|
|
40
40
|
module.exports = function (CaruutoClient, clientInstance) {
|
|
41
|
+
if (typeof aiExtension === 'function') {
|
|
42
|
+
aiExtension(CaruutoClient, clientInstance)
|
|
43
|
+
}
|
|
44
|
+
|
|
41
45
|
if (typeof emailExtension === 'function') {
|
|
42
46
|
emailExtension(CaruutoClient, clientInstance)
|
|
43
47
|
}
|
|
@@ -45,6 +49,4 @@ module.exports = function (CaruutoClient, clientInstance) {
|
|
|
45
49
|
if (typeof tokensExtension === 'function') {
|
|
46
50
|
tokensExtension(CaruutoClient, clientInstance)
|
|
47
51
|
}
|
|
48
|
-
|
|
49
|
-
// Add any other extensions here.
|
|
50
52
|
}
|
package/lib/filters.js
CHANGED
|
@@ -25,21 +25,6 @@ module.exports = function (CaruutoClient) {
|
|
|
25
25
|
return this
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
/**
|
|
29
|
-
* Select a bucket
|
|
30
|
-
*
|
|
31
|
-
* @param {String} bucket
|
|
32
|
-
* @return API
|
|
33
|
-
* @api public
|
|
34
|
-
*/
|
|
35
|
-
CaruutoClient.prototype.inMedia = function (bucket) {
|
|
36
|
-
bucket = bucket || true
|
|
37
|
-
|
|
38
|
-
this.mediaBucket = bucket
|
|
39
|
-
|
|
40
|
-
return this
|
|
41
|
-
}
|
|
42
|
-
|
|
43
28
|
/**
|
|
44
29
|
* Select a document limit
|
|
45
30
|
*
|
package/lib/helpers.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
const querystring = require('query-string')
|
|
2
|
-
const url = require('node:url')
|
|
3
2
|
|
|
4
3
|
module.exports = function (CaruutoClient) {
|
|
5
4
|
/**
|
|
@@ -35,7 +34,6 @@ module.exports = function (CaruutoClient) {
|
|
|
35
34
|
* @api private
|
|
36
35
|
*/
|
|
37
36
|
CaruutoClient.prototype._buildURL = function (options) {
|
|
38
|
-
// console.log('options :>> ', options)
|
|
39
37
|
options = options || {}
|
|
40
38
|
|
|
41
39
|
options.version = this.customVersion || `v${this.options.version || 0}`
|
|
@@ -43,28 +41,13 @@ module.exports = function (CaruutoClient) {
|
|
|
43
41
|
let url = ''
|
|
44
42
|
|
|
45
43
|
url += this.options.caruutoUrl
|
|
46
|
-
// url += ':' + this.options.port
|
|
47
44
|
|
|
48
|
-
if (this.
|
|
49
|
-
url +=
|
|
50
|
-
'/media' +
|
|
51
|
-
(typeof this.mediaBucket === 'string' ? '/' + this.mediaBucket : '')
|
|
52
|
-
|
|
53
|
-
if (this.terminator === 'uploadFile') {
|
|
54
|
-
url += '/upload'
|
|
55
|
-
}
|
|
56
|
-
} else if (this.collection) {
|
|
57
|
-
url += '/' + options.version + '/' + this.collection
|
|
45
|
+
if (this.collection) {
|
|
46
|
+
url += '/' + options.version + '/' + encodeURIComponent(this.collection)
|
|
58
47
|
} else if (this.searchPhrase) {
|
|
59
48
|
url += '/' + options.version
|
|
60
49
|
} else {
|
|
61
|
-
url +=
|
|
62
|
-
'/' +
|
|
63
|
-
(this.customVersion !== undefined
|
|
64
|
-
? this.customVersion
|
|
65
|
-
: options.version) +
|
|
66
|
-
'/' +
|
|
67
|
-
this.endpoint
|
|
50
|
+
url += '/' + options.version + '/' + encodeURIComponent(this.endpoint)
|
|
68
51
|
}
|
|
69
52
|
|
|
70
53
|
if (options.signUrl) {
|
|
@@ -75,28 +58,16 @@ module.exports = function (CaruutoClient) {
|
|
|
75
58
|
url += '/sync'
|
|
76
59
|
}
|
|
77
60
|
|
|
78
|
-
// if (options.config) {
|
|
79
|
-
// url += '/config'
|
|
80
|
-
// }
|
|
81
|
-
|
|
82
|
-
if (options.status) {
|
|
83
|
-
url += '/status'
|
|
84
|
-
}
|
|
85
|
-
|
|
86
61
|
if (options.collections) {
|
|
87
62
|
url = this.options.caruutoUrl + '/api/contentTypes'
|
|
88
63
|
}
|
|
89
64
|
|
|
90
|
-
// if (options.stats) {
|
|
91
|
-
// url += '/stats'
|
|
92
|
-
// }
|
|
93
|
-
|
|
94
65
|
if (this.count) {
|
|
95
66
|
url += '/count'
|
|
96
67
|
}
|
|
97
68
|
|
|
98
69
|
if (options.id) {
|
|
99
|
-
url += '/' + options.id
|
|
70
|
+
url += '/' + encodeURIComponent(options.id)
|
|
100
71
|
}
|
|
101
72
|
|
|
102
73
|
if (this.searchPhrase) {
|
|
@@ -122,14 +93,6 @@ module.exports = function (CaruutoClient) {
|
|
|
122
93
|
params.page = this.page
|
|
123
94
|
}
|
|
124
95
|
|
|
125
|
-
if (this.compose !== undefined) {
|
|
126
|
-
params.compose = this.compose
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
if (typeof this.history !== 'undefined') {
|
|
130
|
-
params.includeHistory = this.history
|
|
131
|
-
}
|
|
132
|
-
|
|
133
96
|
if (this.sort) {
|
|
134
97
|
params.sort = JSON.stringify(this.sort)
|
|
135
98
|
}
|
|
@@ -158,7 +121,7 @@ module.exports = function (CaruutoClient) {
|
|
|
158
121
|
* @api private
|
|
159
122
|
*/
|
|
160
123
|
CaruutoClient.prototype._createRequestObject = function (options) {
|
|
161
|
-
const parsedUri =
|
|
124
|
+
const parsedUri = new URL(options.uri)
|
|
162
125
|
const requestObject = {
|
|
163
126
|
...options,
|
|
164
127
|
uri: {
|
|
@@ -175,7 +138,9 @@ module.exports = function (CaruutoClient) {
|
|
|
175
138
|
}
|
|
176
139
|
|
|
177
140
|
if (typeof this.options.callback === 'function') {
|
|
178
|
-
return
|
|
141
|
+
return Promise.resolve(
|
|
142
|
+
this.options.callback(requestObject, this)
|
|
143
|
+
).finally(() => this._reset())
|
|
179
144
|
}
|
|
180
145
|
|
|
181
146
|
return requestObject
|
|
@@ -186,14 +151,18 @@ module.exports = function (CaruutoClient) {
|
|
|
186
151
|
* @param {Object} object The object to encode
|
|
187
152
|
* @return {Object} The encoded object
|
|
188
153
|
*/
|
|
189
|
-
CaruutoClient.prototype._encodeObjectKeys = function (object) {
|
|
154
|
+
CaruutoClient.prototype._encodeObjectKeys = function (object, _depth) {
|
|
155
|
+
if ((_depth || 0) > 10) {
|
|
156
|
+
return {}
|
|
157
|
+
}
|
|
158
|
+
|
|
190
159
|
return Object.keys(object).reduce((result, key) => {
|
|
191
160
|
if (
|
|
192
161
|
object[key] &&
|
|
193
162
|
typeof object[key] === 'object' &&
|
|
194
163
|
!Array.isArray(object[key])
|
|
195
164
|
) {
|
|
196
|
-
result[key] = this._encodeObjectKeys(object[key])
|
|
165
|
+
result[key] = this._encodeObjectKeys(object[key], (_depth || 0) + 1)
|
|
197
166
|
} else {
|
|
198
167
|
result[key] =
|
|
199
168
|
typeof object[key] === 'string'
|
|
@@ -205,19 +174,6 @@ module.exports = function (CaruutoClient) {
|
|
|
205
174
|
}, {})
|
|
206
175
|
}
|
|
207
176
|
|
|
208
|
-
/**
|
|
209
|
-
* Logs a message
|
|
210
|
-
*
|
|
211
|
-
* @param {String} message
|
|
212
|
-
* @return undefined
|
|
213
|
-
* @api private
|
|
214
|
-
*/
|
|
215
|
-
CaruutoClient.prototype._log = function (message) {
|
|
216
|
-
if (console && console.log) {
|
|
217
|
-
console.log(`[Caruuto JS] ${message}`)
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
177
|
/**
|
|
222
178
|
* Clear any saved options and parameters
|
|
223
179
|
*
|
|
@@ -225,9 +181,20 @@ module.exports = function (CaruutoClient) {
|
|
|
225
181
|
* @api private
|
|
226
182
|
*/
|
|
227
183
|
CaruutoClient.prototype._reset = function () {
|
|
228
|
-
this.
|
|
184
|
+
this.collection = undefined
|
|
185
|
+
this.count = undefined
|
|
229
186
|
this.customVersion = undefined
|
|
187
|
+
this.endpoint = undefined
|
|
188
|
+
this.fields = undefined
|
|
189
|
+
this.headers = undefined
|
|
190
|
+
this.limit = undefined
|
|
191
|
+
this.page = undefined
|
|
192
|
+
this.params = {}
|
|
230
193
|
this.property = undefined
|
|
194
|
+
this.query = undefined
|
|
195
|
+
this.searchPhrase = undefined
|
|
196
|
+
this.sort = undefined
|
|
197
|
+
this.terminator = undefined
|
|
231
198
|
}
|
|
232
199
|
|
|
233
200
|
/**
|
package/lib/performFetch.js
CHANGED
|
@@ -1,93 +1,61 @@
|
|
|
1
|
-
// const crossFetch = require('cross-fetch')
|
|
2
1
|
const debug = require('debug')('caruuto-js')
|
|
3
2
|
const undici = require('undici')
|
|
3
|
+
const { API_KEY_PREFIX } = require('./constants')
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
function buildRequestOptions(client, requestObject) {
|
|
6
|
+
const headers = Object.assign({}, requestObject.headers)
|
|
7
7
|
|
|
8
|
-
if (
|
|
9
|
-
|
|
10
|
-
// } else if (typeof fetch === 'undefined') {
|
|
11
|
-
// _fetch = crossFetch
|
|
12
|
-
} else {
|
|
13
|
-
_fetch = undici.fetch
|
|
8
|
+
if (client.options.caruutoApiKey) {
|
|
9
|
+
headers.authorization = `${API_KEY_PREFIX} ${client.options.caruutoApiKey}`
|
|
14
10
|
}
|
|
15
11
|
|
|
16
|
-
|
|
12
|
+
if (
|
|
13
|
+
headers['content-type'] !== 'application/json' &&
|
|
14
|
+
client.terminator !== 'uploadFile'
|
|
15
|
+
) {
|
|
16
|
+
headers['content-type'] = 'application/json'
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const options = { headers, method: requestObject.method }
|
|
20
|
+
|
|
21
|
+
if (
|
|
22
|
+
requestObject.body &&
|
|
23
|
+
requestObject.method !== 'GET' &&
|
|
24
|
+
requestObject.method !== 'HEAD'
|
|
25
|
+
) {
|
|
26
|
+
if (client.terminator === 'uploadFile') {
|
|
27
|
+
options.body = requestObject.body
|
|
28
|
+
} else if (typeof requestObject.body === 'object') {
|
|
29
|
+
options.body = JSON.stringify(requestObject.body)
|
|
30
|
+
} else {
|
|
31
|
+
options.body = requestObject.body
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return options
|
|
17
36
|
}
|
|
18
37
|
|
|
19
38
|
module.exports = function (CaruutoClient) {
|
|
20
39
|
/**
|
|
21
|
-
* Makes a request to API and handles possible failures
|
|
40
|
+
* Makes a request to the API and handles possible failures.
|
|
22
41
|
*
|
|
23
|
-
* @param {Object}
|
|
24
|
-
* @return
|
|
42
|
+
* @param {Object} requestObject
|
|
43
|
+
* @return Promise
|
|
25
44
|
* @api private
|
|
26
45
|
*/
|
|
27
46
|
CaruutoClient.prototype._processRequest = async function (requestObject) {
|
|
28
|
-
const fetch =
|
|
47
|
+
const fetch = this.options.fetch || undici.fetch
|
|
48
|
+
const options = buildRequestOptions(this, requestObject)
|
|
49
|
+
const uri = requestObject.uri.href
|
|
29
50
|
|
|
30
|
-
|
|
31
|
-
headers: Object.assign({}, requestObject.headers),
|
|
32
|
-
method: requestObject.method,
|
|
33
|
-
uri: requestObject.uri.href
|
|
34
|
-
}
|
|
51
|
+
debug(`Querying URI: ${requestObject.uri.path}`)
|
|
35
52
|
|
|
36
|
-
|
|
37
|
-
if (this.options.caruutoApiKey) {
|
|
38
|
-
options.headers.authorization = `Api-Key-v1 ${this.options.caruutoApiKey}`
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
if (requestObject.body) {
|
|
42
|
-
// If the body is a FormData object, we need to handle it differently
|
|
43
|
-
if (this.terminator === 'uploadFile') {
|
|
44
|
-
options.body = requestObject.body
|
|
45
|
-
} else if (typeof requestObject.body === 'object') {
|
|
46
|
-
// If the body is an object, we need to stringify it
|
|
47
|
-
options.body = JSON.stringify(requestObject.body)
|
|
48
|
-
} else {
|
|
49
|
-
options.body = requestObject.body
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
if (
|
|
54
|
-
options.headers['content-type'] !== 'application/json' &&
|
|
55
|
-
this.terminator !== 'uploadFile'
|
|
56
|
-
) {
|
|
57
|
-
options.headers['content-type'] = 'application/json'
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// if (this.terminator === 'uploadFile') {
|
|
61
|
-
// If we are uploading a file, we need to set the content type to multipart/form-data
|
|
62
|
-
// options.headers['content-type'] = 'multipart/form-data'
|
|
63
|
-
// }
|
|
64
|
-
|
|
65
|
-
// If the request is a GET request, we don't need to set the body
|
|
66
|
-
if (requestObject.method === 'GET' || requestObject.method === 'HEAD') {
|
|
67
|
-
delete options.body
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// if (
|
|
71
|
-
// requestObject.headers &&
|
|
72
|
-
// requestObject.headers['content-type'] &&
|
|
73
|
-
// requestObject.headers['content-type'] !== 'application/json'
|
|
74
|
-
// ) {
|
|
75
|
-
// options.json = false
|
|
76
|
-
// } else {
|
|
77
|
-
// options.json = true
|
|
78
|
-
// }
|
|
79
|
-
|
|
80
|
-
debug(`Querying URI: ${decodeURIComponent(options.uri)}`)
|
|
81
|
-
|
|
82
|
-
// Make the request using fetch
|
|
83
|
-
const response = await fetch(options.uri, options).catch(err => {
|
|
84
|
-
return Promise.reject(err)
|
|
85
|
-
})
|
|
53
|
+
const response = await fetch(uri, options).catch(err => Promise.reject(err))
|
|
86
54
|
|
|
87
55
|
if (response.status >= 400) {
|
|
88
56
|
const err = new Error()
|
|
89
57
|
err.status = response.status
|
|
90
|
-
err.uri =
|
|
58
|
+
err.uri = requestObject.uri.path
|
|
91
59
|
|
|
92
60
|
try {
|
|
93
61
|
const responseData = await response.json()
|
|
@@ -99,12 +67,15 @@ module.exports = function (CaruutoClient) {
|
|
|
99
67
|
if (responseData.errors) {
|
|
100
68
|
err.errors = responseData.errors
|
|
101
69
|
}
|
|
102
|
-
} catch (
|
|
103
|
-
|
|
104
|
-
}
|
|
70
|
+
} catch (_) {}
|
|
71
|
+
|
|
105
72
|
return Promise.reject(err)
|
|
106
73
|
}
|
|
107
74
|
|
|
108
|
-
|
|
75
|
+
if (response.status === 204) {
|
|
76
|
+
return undefined
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return response.json()
|
|
109
80
|
}
|
|
110
81
|
}
|
package/lib/terminators.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
// const FormData = require('form-data')
|
|
2
1
|
const undici = require('undici')
|
|
3
2
|
const { FormData } = undici
|
|
4
3
|
|
|
5
4
|
module.exports = function (CaruutoClient) {
|
|
6
5
|
/**
|
|
7
|
-
* Create one/multiple documents
|
|
6
|
+
* Create one/multiple documents
|
|
8
7
|
*
|
|
9
8
|
* @param {Object} data
|
|
10
9
|
* @return Promise
|
|
@@ -20,16 +19,10 @@ module.exports = function (CaruutoClient) {
|
|
|
20
19
|
|
|
21
20
|
this._setHeader('content-type', 'application/json')
|
|
22
21
|
|
|
23
|
-
// if (this._isValidHook()) {
|
|
24
|
-
// requestPayload.body = data
|
|
25
|
-
|
|
26
|
-
// this._setHeader('content-type', 'text/plain')
|
|
27
|
-
// } else {
|
|
28
22
|
requestPayload.body =
|
|
29
23
|
data instanceof Array
|
|
30
24
|
? data.map(this._stripReservedProperties.bind(this))
|
|
31
25
|
: this._stripReservedProperties(data)
|
|
32
|
-
// }
|
|
33
26
|
|
|
34
27
|
return this._createRequestObject(requestPayload)
|
|
35
28
|
}
|
|
@@ -48,14 +41,6 @@ module.exports = function (CaruutoClient) {
|
|
|
48
41
|
uri: this._buildURL()
|
|
49
42
|
}
|
|
50
43
|
|
|
51
|
-
// if (!this._isValidHook()) {
|
|
52
|
-
// if (this.isClient) {
|
|
53
|
-
// if (!this.isClient.id && !this.isClient.self) {
|
|
54
|
-
// throw new Error(
|
|
55
|
-
// 'Unable run delete on all clients. Please use the whereClientIs() filter.'
|
|
56
|
-
// )
|
|
57
|
-
// }
|
|
58
|
-
// } else {
|
|
59
44
|
if (this.query === undefined) {
|
|
60
45
|
throw new Error('Unable to find query for delete')
|
|
61
46
|
}
|
|
@@ -63,8 +48,6 @@ module.exports = function (CaruutoClient) {
|
|
|
63
48
|
requestPayload.body = {
|
|
64
49
|
query: this.query
|
|
65
50
|
}
|
|
66
|
-
// }
|
|
67
|
-
// }
|
|
68
51
|
|
|
69
52
|
return this._createRequestObject(requestPayload)
|
|
70
53
|
}
|
|
@@ -87,8 +70,6 @@ module.exports = function (CaruutoClient) {
|
|
|
87
70
|
uri: this._buildURL({
|
|
88
71
|
useParams: true
|
|
89
72
|
})
|
|
90
|
-
}).then(response => {
|
|
91
|
-
return response
|
|
92
73
|
})
|
|
93
74
|
}
|
|
94
75
|
|
|
@@ -107,36 +88,6 @@ module.exports = function (CaruutoClient) {
|
|
|
107
88
|
})
|
|
108
89
|
}
|
|
109
90
|
|
|
110
|
-
/**
|
|
111
|
-
* Get the config for a collection if one is specified, or for main API if not
|
|
112
|
-
*
|
|
113
|
-
* @return Promise
|
|
114
|
-
* @api public
|
|
115
|
-
*/
|
|
116
|
-
CaruutoClient.prototype.getConfig = function () {
|
|
117
|
-
this.terminator = 'getConfig'
|
|
118
|
-
|
|
119
|
-
return this._createRequestObject({
|
|
120
|
-
method: 'GET',
|
|
121
|
-
uri: this._buildURL({ config: true })
|
|
122
|
-
})
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Get the languages supported by the API
|
|
127
|
-
*
|
|
128
|
-
* @return Promise
|
|
129
|
-
* @api public
|
|
130
|
-
*/
|
|
131
|
-
// CaruutoClient.prototype.getLanguages = function () {
|
|
132
|
-
// this.terminator = 'getLanguages'
|
|
133
|
-
|
|
134
|
-
// return this._createRequestObject({
|
|
135
|
-
// method: 'GET',
|
|
136
|
-
// uri: this._buildURL({ languages: true })
|
|
137
|
-
// })
|
|
138
|
-
// }
|
|
139
|
-
|
|
140
91
|
/**
|
|
141
92
|
* Get a signed URL for media upload
|
|
142
93
|
*
|
|
@@ -153,21 +104,6 @@ module.exports = function (CaruutoClient) {
|
|
|
153
104
|
})
|
|
154
105
|
}
|
|
155
106
|
|
|
156
|
-
/**
|
|
157
|
-
* Get the status of the API
|
|
158
|
-
*
|
|
159
|
-
* @return Promise
|
|
160
|
-
* @api public
|
|
161
|
-
*/
|
|
162
|
-
CaruutoClient.prototype.getStatus = function () {
|
|
163
|
-
this.terminator = 'getStatus'
|
|
164
|
-
|
|
165
|
-
return this._createRequestObject({
|
|
166
|
-
method: 'POST',
|
|
167
|
-
uri: this._buildURL({ status: true })
|
|
168
|
-
})
|
|
169
|
-
}
|
|
170
|
-
|
|
171
107
|
/**
|
|
172
108
|
* Upload a file to the media bucket.
|
|
173
109
|
*
|
|
@@ -192,13 +128,11 @@ module.exports = function (CaruutoClient) {
|
|
|
192
128
|
|
|
193
129
|
const requestPayload = {
|
|
194
130
|
method: 'POST',
|
|
195
|
-
uri: this.
|
|
131
|
+
uri: this.options.caruutoUrl + '/api/media/upload'
|
|
196
132
|
}
|
|
197
133
|
|
|
198
134
|
const formData = new FormData()
|
|
199
135
|
|
|
200
|
-
console.log(directory, fileName, mimeType, buffer, contentLength)
|
|
201
|
-
|
|
202
136
|
try {
|
|
203
137
|
formData.append(
|
|
204
138
|
'file',
|
|
@@ -233,25 +167,6 @@ module.exports = function (CaruutoClient) {
|
|
|
233
167
|
|
|
234
168
|
this._setHeader('content-type', 'application/json')
|
|
235
169
|
|
|
236
|
-
// if (this._isValidHook()) {
|
|
237
|
-
// requestPayload.body = update
|
|
238
|
-
|
|
239
|
-
// this._setHeader('content-type', 'text/plain')
|
|
240
|
-
// } else {
|
|
241
|
-
// if (this.isClient) {
|
|
242
|
-
// if (!this.isClient.id && !this.isClient.self) {
|
|
243
|
-
// throw new Error(
|
|
244
|
-
// 'Unable to run update on all clients. Please use whereClientIs() or whereClientIsSelf() filters.'
|
|
245
|
-
// )
|
|
246
|
-
// }
|
|
247
|
-
|
|
248
|
-
// // Remove `clientId` from the payload.
|
|
249
|
-
// if (update.clientId) {
|
|
250
|
-
// delete update.clientId
|
|
251
|
-
// }
|
|
252
|
-
|
|
253
|
-
// requestPayload.body = update
|
|
254
|
-
// } else {
|
|
255
170
|
if (this.query === undefined) {
|
|
256
171
|
throw new Error('Unable to find query for update')
|
|
257
172
|
}
|
|
@@ -260,8 +175,6 @@ module.exports = function (CaruutoClient) {
|
|
|
260
175
|
query: this.query,
|
|
261
176
|
update: this._stripReservedProperties(update)
|
|
262
177
|
}
|
|
263
|
-
// }
|
|
264
|
-
// }
|
|
265
178
|
|
|
266
179
|
return this._createRequestObject(requestPayload)
|
|
267
180
|
}
|
|
@@ -302,44 +215,13 @@ module.exports = function (CaruutoClient) {
|
|
|
302
215
|
return this._createRequestObject(requestPayload)
|
|
303
216
|
}
|
|
304
217
|
|
|
305
|
-
|
|
306
|
-
* Create a new token.
|
|
307
|
-
* @param {Object} data
|
|
308
|
-
* @api public
|
|
309
|
-
*/
|
|
218
|
+
// Requires createClient() — delegates to the tokens extension.
|
|
310
219
|
CaruutoClient.prototype.createToken = function (data) {
|
|
311
|
-
this.
|
|
312
|
-
|
|
313
|
-
const requestPayload = {
|
|
314
|
-
body: data,
|
|
315
|
-
method: 'POST',
|
|
316
|
-
uri: this._buildURL()
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
this._setHeader('content-type', 'application/json')
|
|
320
|
-
|
|
321
|
-
return this._createRequestObject(requestPayload)
|
|
220
|
+
return this.tokens.create(data)
|
|
322
221
|
}
|
|
323
222
|
|
|
324
|
-
|
|
325
|
-
* Verify a token.
|
|
326
|
-
* @param {Object} data
|
|
327
|
-
* @api public
|
|
328
|
-
*/
|
|
223
|
+
// Requires createClient() — delegates to the tokens extension.
|
|
329
224
|
CaruutoClient.prototype.verifyToken = function (token) {
|
|
330
|
-
this.
|
|
331
|
-
|
|
332
|
-
const requestPayload = {
|
|
333
|
-
body: {
|
|
334
|
-
token,
|
|
335
|
-
verify: true
|
|
336
|
-
},
|
|
337
|
-
method: 'POST',
|
|
338
|
-
uri: this._buildURL()
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
this._setHeader('content-type', 'application/json')
|
|
342
|
-
|
|
343
|
-
return this._createRequestObject(requestPayload)
|
|
225
|
+
return this.tokens.verify(token)
|
|
344
226
|
}
|
|
345
227
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@caruuto/caruuto-js",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "A high-level library for interacting with Caruuto",
|
|
5
5
|
"exports": "./index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -10,24 +10,20 @@
|
|
|
10
10
|
"author": "Jim Lambie <jim@27.works>",
|
|
11
11
|
"license": "GPL",
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"cross-fetch": "^4.0.0",
|
|
14
13
|
"debug": "^2.6.1",
|
|
15
14
|
"encoding": "^0.1.13",
|
|
16
|
-
"got": "^11.0.0",
|
|
17
|
-
"human-interval": "^2.0.1",
|
|
18
|
-
"mocha": "^10.2.0",
|
|
19
15
|
"query-string": "5.0.1",
|
|
20
16
|
"undici": "6.21.3"
|
|
21
17
|
},
|
|
22
18
|
"repository": {
|
|
23
19
|
"type": "git",
|
|
24
|
-
"url": "https://github.com/Caruuto/caruuto-js.git"
|
|
20
|
+
"url": "git+https://github.com/Caruuto/caruuto-js.git"
|
|
25
21
|
},
|
|
26
22
|
"bugs": {
|
|
27
23
|
"url": "https://github.com/Caruuto/caruuto-js/issues"
|
|
28
24
|
},
|
|
29
25
|
"engines": {
|
|
30
|
-
"node": ">=
|
|
26
|
+
"node": ">=18"
|
|
31
27
|
},
|
|
32
28
|
"husky": {
|
|
33
29
|
"hooks": {
|
|
@@ -42,30 +38,16 @@
|
|
|
42
38
|
},
|
|
43
39
|
"homepage": "https://github.com/Caruuto/caruuto-js",
|
|
44
40
|
"devDependencies": {
|
|
45
|
-
"chai": "^5.2.0",
|
|
46
41
|
"coveralls": "^3.0.2",
|
|
47
42
|
"eslint": "8.5.0",
|
|
48
|
-
"eslint-config-next": "13.0.5",
|
|
49
43
|
"eslint-config-prettier": "^8.6.0",
|
|
50
44
|
"eslint-plugin-prettier": "^4.2.1",
|
|
51
|
-
"eslint-plugin-react": "^7.32.2",
|
|
52
|
-
"form-data": "^4.0.2",
|
|
53
45
|
"husky": "^8.0.0",
|
|
54
46
|
"lint-staged": "^12.5.0",
|
|
55
|
-
"
|
|
56
|
-
"nock": "^12.0.0",
|
|
47
|
+
"mocha": "^10.2.0",
|
|
57
48
|
"nyc": "^15.0.1",
|
|
58
49
|
"prettier": "2.8.4",
|
|
59
|
-
"prettier-standard": "^15.0.1",
|
|
60
50
|
"should": "^9.0.2",
|
|
61
|
-
"sinon": "^4.5.0"
|
|
62
|
-
"standard": "^16.0.3",
|
|
63
|
-
"supertest": "^4.0.0",
|
|
64
|
-
"underscore": "^1.8.3"
|
|
65
|
-
},
|
|
66
|
-
"standard.options": {
|
|
67
|
-
"ignore": [],
|
|
68
|
-
"plugins": [],
|
|
69
|
-
"envs": []
|
|
51
|
+
"sinon": "^4.5.0"
|
|
70
52
|
}
|
|
71
|
-
}
|
|
53
|
+
}
|
package/.editorconfig
DELETED
package/.eslintignore
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
node_modules
|
package/.eslintrc.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
module.exports = {
|
|
2
|
-
env: {
|
|
3
|
-
es2021: true,
|
|
4
|
-
mocha: true,
|
|
5
|
-
node: true
|
|
6
|
-
},
|
|
7
|
-
extends: ['eslint:recommended', 'plugin:prettier/recommended'],
|
|
8
|
-
overrides: [],
|
|
9
|
-
parserOptions: {
|
|
10
|
-
ecmaVersion: 12,
|
|
11
|
-
sourceType: 'module'
|
|
12
|
-
},
|
|
13
|
-
plugins: [],
|
|
14
|
-
rules: {
|
|
15
|
-
'no-redeclare': 0,
|
|
16
|
-
'prettier/prettier': 0,
|
|
17
|
-
quotes: ['error', 'single']
|
|
18
|
-
}
|
|
19
|
-
}
|
package/.prettierignore
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
node_modules
|
package/scripts/coverage.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
#! /usr/bin/env node
|
|
2
|
-
|
|
3
|
-
const exec = require('child_process').exec
|
|
4
|
-
|
|
5
|
-
if (process.env.CI) {
|
|
6
|
-
exec(
|
|
7
|
-
'cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js',
|
|
8
|
-
(err, out) => {
|
|
9
|
-
if (err) console.log(err)
|
|
10
|
-
console.log(out)
|
|
11
|
-
}
|
|
12
|
-
)
|
|
13
|
-
}
|
package/x.js
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
const { createClient } = require('./index')
|
|
2
|
-
|
|
3
|
-
// https://caruuto.27.works/api
|
|
4
|
-
// EYeRSSEtmcVF9OFhVHjcf1TcPLHbTpPgBbHVHIce1buYKHOCNB1A2NC4UoThmQhk
|
|
5
|
-
|
|
6
|
-
;(async () => {
|
|
7
|
-
const x = createClient(
|
|
8
|
-
'https://caruuto.27.works/api',
|
|
9
|
-
'EYeRSSEtmcVF9OFhVHjcf1TcPLHbTpPgBbHVHIce1buYKHOCNB1A2NC4UoThmQhk',
|
|
10
|
-
{ reservedProperties: ['created_at'] }
|
|
11
|
-
)
|
|
12
|
-
|
|
13
|
-
// const signedUrl = await x.inMedia('radical').getSignedUrl('test.jpg')
|
|
14
|
-
|
|
15
|
-
// console.log(signedUrl)
|
|
16
|
-
|
|
17
|
-
// const y = await x.insertDataPoint({
|
|
18
|
-
// memberId: '0bcdd373-fbb6-4998-b0ed-57c842709acc',
|
|
19
|
-
// optionId: '10',
|
|
20
|
-
// questionId: 'f373b0a2-9c87-41f1-af49-f643da8f836c'
|
|
21
|
-
// })
|
|
22
|
-
|
|
23
|
-
// x.search({ phrase: 'grower' })
|
|
24
|
-
// const y = await x.find()
|
|
25
|
-
// console.log('y :>> ', y)
|
|
26
|
-
|
|
27
|
-
// const z = await x.tokens.create({
|
|
28
|
-
// email: 'jameslambie@gmail.com',
|
|
29
|
-
// expiry: 7200000,
|
|
30
|
-
// type: 'RESET'
|
|
31
|
-
// })
|
|
32
|
-
// const z = await x.verifyToken('q8ug8epjx5hzcg48ku')
|
|
33
|
-
// console.log('z :>> ', z)
|
|
34
|
-
|
|
35
|
-
// console.log('p :>> ', await x.create({ x: 1 }))
|
|
36
|
-
|
|
37
|
-
// console.log('x :>> ', x.email)
|
|
38
|
-
// console.log('x :>> ', x.email.queue)
|
|
39
|
-
// const xx = await x.email.queue({
|
|
40
|
-
// to: 'x@x.com',
|
|
41
|
-
// from: 'z@z.com',
|
|
42
|
-
// templateId: 'test'
|
|
43
|
-
// })
|
|
44
|
-
|
|
45
|
-
// console.log('xx :>> ', xx)
|
|
46
|
-
|
|
47
|
-
x.in('retailers')
|
|
48
|
-
console.log('x :>> ', x)
|
|
49
|
-
|
|
50
|
-
const zz = await x.find({ sent: true })
|
|
51
|
-
|
|
52
|
-
console.log('zz :>> ', zz)
|
|
53
|
-
})()
|