@stack-spot/portal-network 0.39.2 → 0.40.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/CHANGELOG.md +7 -0
- package/dist/api/ai.d.ts +445 -542
- package/dist/api/ai.d.ts.map +1 -1
- package/dist/api/ai.js +181 -318
- package/dist/api/ai.js.map +1 -1
- package/dist/client/ai.d.ts +89 -0
- package/dist/client/ai.d.ts.map +1 -0
- package/dist/client/ai.js +108 -0
- package/dist/client/ai.js.map +1 -0
- package/dist/client/cloud-account.d.ts +5 -5
- package/dist/client/cloud-platform.d.ts +13 -13
- package/dist/client/content.d.ts +5 -5
- package/dist/client/notification.d.ts +1 -1
- package/dist/client/types.d.ts +17 -0
- package/dist/client/types.d.ts.map +1 -1
- package/dist/client/workflow.d.ts +8 -8
- package/dist/error/StackspotAPIError.d.ts +1 -1
- package/dist/error/StackspotAPIError.d.ts.map +1 -1
- package/dist/error/StreamCanceledError.d.ts +5 -0
- package/dist/error/StreamCanceledError.d.ts.map +1 -0
- package/dist/error/StreamCanceledError.js +9 -0
- package/dist/error/StreamCanceledError.js.map +1 -0
- package/dist/error/StreamError.d.ts +5 -0
- package/dist/error/StreamError.d.ts.map +1 -0
- package/dist/error/StreamError.js +7 -0
- package/dist/error/StreamError.js.map +1 -0
- package/dist/error/StreamJsonError.d.ts +5 -0
- package/dist/error/StreamJsonError.d.ts.map +1 -0
- package/dist/error/StreamJsonError.js +9 -0
- package/dist/error/StreamJsonError.js.map +1 -0
- package/dist/index.d.ts +6 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/network/NetworkClient.d.ts +17 -1
- package/dist/network/NetworkClient.d.ts.map +1 -1
- package/dist/network/NetworkClient.js +29 -0
- package/dist/network/NetworkClient.js.map +1 -1
- package/dist/network/types.d.ts +5 -3
- package/dist/network/types.d.ts.map +1 -1
- package/dist/utils/StreamedJson.d.ts +48 -0
- package/dist/utils/StreamedJson.d.ts.map +1 -0
- package/dist/utils/StreamedJson.js +162 -0
- package/dist/utils/StreamedJson.js.map +1 -0
- package/package.json +2 -1
- package/src/api/ai.ts +652 -935
- package/src/client/ai.ts +69 -0
- package/src/client/types.ts +18 -1
- package/src/error/StackspotAPIError.ts +1 -1
- package/src/error/StreamCanceledError.ts +10 -0
- package/src/error/StreamError.ts +7 -0
- package/src/error/StreamJsonError.ts +10 -0
- package/src/index.ts +7 -1
- package/src/network/NetworkClient.ts +30 -1
- package/src/network/types.ts +6 -3
- package/src/utils/StreamedJson.tsx +135 -0
package/src/client/ai.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { HttpError } from '@oazapfts/runtime'
|
|
2
|
+
import {
|
|
3
|
+
ChatResponse3,
|
|
4
|
+
conversationHistoryV1ConversationsConversationIdGet,
|
|
5
|
+
defaults,
|
|
6
|
+
deleteConversationV1ConversationsConversationIdDelete,
|
|
7
|
+
downloadConversationV1ConversationsConversationIdDownloadGet,
|
|
8
|
+
findKnowledgeObjectByCustomIdV1KnowledgeSourcesSlugObjectsCustomIdGet,
|
|
9
|
+
getQuickCommandV1QuickCommandsSlugGet,
|
|
10
|
+
HttpValidationError,
|
|
11
|
+
listAiStacksV1AiStacksGet,
|
|
12
|
+
listAllV1QuickCommandsAllGet,
|
|
13
|
+
listAssociationV1WorkspaceWorkspaceIdGet,
|
|
14
|
+
listConversationsV1ConversationsGet,
|
|
15
|
+
listKnowledgeSourcesV1KnowledgeSourcesGet,
|
|
16
|
+
postEventV1EventsPost,
|
|
17
|
+
quickActionsV1QuickActionsPost,
|
|
18
|
+
updateTitleV1ConversationsConversationIdPatch,
|
|
19
|
+
} from '../api/ai'
|
|
20
|
+
import apis from '../apis.json'
|
|
21
|
+
import { StackspotAPIError } from '../error/StackspotAPIError'
|
|
22
|
+
import { ReactQueryNetworkClient } from '../network/ReactQueryNetworkClient'
|
|
23
|
+
import { removeAuthorizationParam } from '../utils/remove-authorization-param'
|
|
24
|
+
import { StreamedJson } from '../utils/StreamedJson'
|
|
25
|
+
import { FixedChatRequest } from './types'
|
|
26
|
+
|
|
27
|
+
class AIClient extends ReactQueryNetworkClient {
|
|
28
|
+
constructor() {
|
|
29
|
+
super(apis.ai.url, defaults)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
protected buildStackSpotError(error: HttpError): StackspotAPIError {
|
|
33
|
+
return new StackspotAPIError({
|
|
34
|
+
status: error.status,
|
|
35
|
+
headers: error.headers,
|
|
36
|
+
stack: error.stack,
|
|
37
|
+
message: (error.data as HttpValidationError | undefined)?.detail?.map(d => d.msg)?.join('\n'),
|
|
38
|
+
})
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
runQuickAction = this.mutation(removeAuthorizationParam(quickActionsV1QuickActionsPost))
|
|
42
|
+
aiStacks = this.query(removeAuthorizationParam(listAiStacksV1AiStacksGet))
|
|
43
|
+
workspace = this.query(removeAuthorizationParam(listAssociationV1WorkspaceWorkspaceIdGet))
|
|
44
|
+
quickCommands = this.query(removeAuthorizationParam(listAllV1QuickCommandsAllGet))
|
|
45
|
+
quickCommand = this.query(removeAuthorizationParam(getQuickCommandV1QuickCommandsSlugGet))
|
|
46
|
+
knowledgeSources = this.query(removeAuthorizationParam(listKnowledgeSourcesV1KnowledgeSourcesGet))
|
|
47
|
+
knowledgeSource = this.query(removeAuthorizationParam(findKnowledgeObjectByCustomIdV1KnowledgeSourcesSlugObjectsCustomIdGet))
|
|
48
|
+
chats = this.infiniteQuery(removeAuthorizationParam(listConversationsV1ConversationsGet))
|
|
49
|
+
chat = this.query(removeAuthorizationParam(conversationHistoryV1ConversationsConversationIdGet))
|
|
50
|
+
deleteChat = this.mutation(removeAuthorizationParam(deleteConversationV1ConversationsConversationIdDelete))
|
|
51
|
+
downloadChats = this.mutation(removeAuthorizationParam(downloadConversationV1ConversationsConversationIdDownloadGet))
|
|
52
|
+
renameChat = this.mutation(removeAuthorizationParam(updateTitleV1ConversationsConversationIdPatch))
|
|
53
|
+
createEvent = this.mutation(removeAuthorizationParam(postEventV1EventsPost))
|
|
54
|
+
|
|
55
|
+
sendChatMessage(request: FixedChatRequest, minChangeIntervalMS?: number): StreamedJson<ChatResponse3> {
|
|
56
|
+
const abortController = new AbortController()
|
|
57
|
+
const headers = {
|
|
58
|
+
'Content-Type': 'application/json',
|
|
59
|
+
'Accept': 'text/event-stream',
|
|
60
|
+
}
|
|
61
|
+
const events = this.stream(
|
|
62
|
+
this.resolveURL('v3/chat'),
|
|
63
|
+
{ method: 'post', body: JSON.stringify(request), headers, signal: abortController.signal },
|
|
64
|
+
)
|
|
65
|
+
return new StreamedJson(events, abortController, minChangeIntervalMS)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export const aiClient = new AIClient()
|
package/src/client/types.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { RequestOpts } from '@oazapfts/runtime'
|
|
2
2
|
import { AccountScmInfoSaveRequest, AccountScmInfoUpdateRequest, AccountScmStatusResponse } from '../api/account'
|
|
3
|
+
import { ChatRequest2 } from '../api/ai'
|
|
3
4
|
import { ConnectAccountRequestV2, ManagedAccountProvisionRequest } from '../api/cloudAccount'
|
|
4
5
|
import { AllocationCostRequest, AllocationCostResponse, ChargePeriod, getAllocationCostFilters, ManagedService, ServiceResource } from '../api/cloudServices'
|
|
5
6
|
import { FullInputContextResponse, InputConditionResponse, InputValuesContextResponse, PluginForAppCreationV2Response, PluginInputValuesInConsolidatedContextResponse, ValueByEnvResponse } from '../api/workspaceManager'
|
|
@@ -154,6 +155,23 @@ Omit<PluginInputValuesInConsolidatedContextResponse, 'inputs' | 'input' | 'type'
|
|
|
154
155
|
condition?: FixedInputConditionResponse,
|
|
155
156
|
}
|
|
156
157
|
|
|
158
|
+
export interface FixedChatRequest extends ChatRequest2 {
|
|
159
|
+
context?: {
|
|
160
|
+
workspace?: string,
|
|
161
|
+
conversation_id?: string,
|
|
162
|
+
stack_id?: string,
|
|
163
|
+
language?: string,
|
|
164
|
+
project_recent_files?: string,
|
|
165
|
+
knowledge_sources?: string[],
|
|
166
|
+
agent_id?: string,
|
|
167
|
+
agent_built_in?: boolean,
|
|
168
|
+
platform?: string,
|
|
169
|
+
platform_version?: string,
|
|
170
|
+
stackspot_ai_version?: string,
|
|
171
|
+
os?: string,
|
|
172
|
+
},
|
|
173
|
+
}
|
|
174
|
+
|
|
157
175
|
export type OazapftsFunction<Variables = any, Result = any> = (variables: Variables, opts?: RequestOpts) => Promise<Result>
|
|
158
176
|
|
|
159
177
|
type Unpromisify<T> = T extends Promise<infer R> ? Unpromisify<R> : T
|
|
@@ -167,4 +185,3 @@ export type FixVariables<
|
|
|
167
185
|
>
|
|
168
186
|
|
|
169
187
|
export type ReplaceResult<T extends (...args: any[]) => Promise<any>, Fix> = (...args: Parameters<T>) => Promise<Fix>
|
|
170
|
-
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { StreamError } from './StreamError'
|
|
2
|
+
|
|
3
|
+
export class StreamCanceledError extends StreamError {
|
|
4
|
+
constructor() {
|
|
5
|
+
super(language => language === 'pt'
|
|
6
|
+
? 'Erro: a mensagem foi abortada pelo usuário.'
|
|
7
|
+
: 'Error: the message was aborted by the user.',
|
|
8
|
+
)
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { StreamError } from './StreamError'
|
|
2
|
+
|
|
3
|
+
export class StreamJsonError extends StreamError {
|
|
4
|
+
constructor() {
|
|
5
|
+
super(language => language === 'pt'
|
|
6
|
+
? 'Erro no formato do stream. O conteúdo não é um JSON válido.'
|
|
7
|
+
: 'Stream format error. The streamed content is not a valid JSON.',
|
|
8
|
+
)
|
|
9
|
+
}
|
|
10
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
export { accountClient } from './client/account'
|
|
2
|
+
export { aiClient } from './client/ai'
|
|
2
3
|
export { cloudAccountClient } from './client/cloud-account'
|
|
4
|
+
export { cloudPlatformClient } from './client/cloud-platform'
|
|
3
5
|
export { cloudServicesClient } from './client/cloud-services'
|
|
4
6
|
export { contentClient } from './client/content'
|
|
5
7
|
export { eventBusClient } from './client/event-bus'
|
|
6
8
|
export { notificationClient } from './client/notification'
|
|
7
|
-
export { cloudPlatformClient } from './client/cloud-platform'
|
|
8
9
|
export { runtimeManagerClient } from './client/runtime-manager'
|
|
9
10
|
export { secretsClient } from './client/secrets'
|
|
10
11
|
export * from './client/types'
|
|
@@ -14,7 +15,12 @@ export { workspaceManagerClient } from './client/workspace-manager'
|
|
|
14
15
|
export { workspaceSearchClient } from './client/workspace-search'
|
|
15
16
|
export { DefaultAPIError } from './error/DefaultAPIError'
|
|
16
17
|
export { StackspotAPIError } from './error/StackspotAPIError'
|
|
18
|
+
export { StreamCanceledError } from './error/StreamCanceledError'
|
|
19
|
+
export { StreamError } from './error/StreamError'
|
|
20
|
+
export { StreamJsonError } from './error/StreamJsonError'
|
|
17
21
|
export { NetworkClient } from './network/NetworkClient'
|
|
18
22
|
export { queryClient } from './network/react-query-client'
|
|
19
23
|
export { OperationResult, OperationVariables, UseQueryObjectOptions } from './network/types'
|
|
24
|
+
export { StreamedJson } from './utils/StreamedJson'
|
|
20
25
|
export { useExtendedList } from './utils/use-extended-list'
|
|
26
|
+
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { AuthenticationError } from '@stack-spot/auth'
|
|
2
2
|
import { requestPermission } from '@stack-spot/opa'
|
|
3
|
-
import {
|
|
3
|
+
import { events } from 'fetch-event-stream'
|
|
4
|
+
import { StackspotAPIError } from '../error/StackspotAPIError'
|
|
5
|
+
import { Env, FetchEventStream, HTTPMethod, SessionManager } from './types'
|
|
4
6
|
|
|
5
7
|
/**
|
|
6
8
|
* A set of methods for performing network requests to an API.
|
|
@@ -94,6 +96,33 @@ export abstract class NetworkClient {
|
|
|
94
96
|
}
|
|
95
97
|
}
|
|
96
98
|
|
|
99
|
+
/**
|
|
100
|
+
* Reads an EventSource from the endpoint. Differently than the original specification, this allows common HTTP requests with method and
|
|
101
|
+
* body to be made.
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* ```
|
|
105
|
+
* let events = this.stream('url', options)
|
|
106
|
+
* for await (let event of events) {
|
|
107
|
+
* console.log('<<', event.data)
|
|
108
|
+
* }
|
|
109
|
+
* ```
|
|
110
|
+
* @param input the url or request object.
|
|
111
|
+
* @param init the fetch options.
|
|
112
|
+
* @returns a promise with a FetchEventStream, which is an AsyncGenerator.
|
|
113
|
+
*/
|
|
114
|
+
protected async stream(input: string | URL | Request, init?: RequestInit): Promise<FetchEventStream> {
|
|
115
|
+
const response = await this.fetch(input, init)
|
|
116
|
+
if (!response.ok) {
|
|
117
|
+
let message = `Failed to get stream response. Error status: ${response.status}.`
|
|
118
|
+
try {
|
|
119
|
+
message = await response.text()
|
|
120
|
+
} catch { /* empty */ }
|
|
121
|
+
throw new StackspotAPIError({ status: response.status, headers: response.headers, message })
|
|
122
|
+
}
|
|
123
|
+
return events(response, init?.signal)
|
|
124
|
+
}
|
|
125
|
+
|
|
97
126
|
/**
|
|
98
127
|
* Checks whether or not the current account is freemium.
|
|
99
128
|
* @returns true if it's a freemium account, false otherwise.
|
package/src/network/types.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { RequestOpts } from '@oazapfts/runtime'
|
|
2
2
|
import { Session } from '@stack-spot/auth'
|
|
3
3
|
import { InfiniteData, MutateOptions, UseInfiniteQueryOptions, UseInfiniteQueryResult, UseMutationOptions, UseMutationResult, UseQueryOptions, UseQueryResult } from '@tanstack/react-query'
|
|
4
|
+
import { ServerSentEventMessage } from 'fetch-event-stream'
|
|
4
5
|
import { StackspotAPIError } from '../error/StackspotAPIError'
|
|
5
6
|
|
|
6
7
|
export interface SessionManager {
|
|
@@ -13,6 +14,8 @@ export type Env = 'dev' | 'stg' | 'prd'
|
|
|
13
14
|
|
|
14
15
|
export type HTTPMethod = 'post' | 'patch' | 'delete' | 'put' | 'get'
|
|
15
16
|
|
|
17
|
+
export type FetchEventStream = AsyncGenerator<ServerSentEventMessage, void, unknown>
|
|
18
|
+
|
|
16
19
|
export interface OperationConfig<Args extends [AbortSignal, Record<string, any>] | [AbortSignal], Result> {
|
|
17
20
|
/**
|
|
18
21
|
* The operation name. To be used as key in React Query.
|
|
@@ -161,7 +164,7 @@ export interface QueryObject<Variables, Result> extends OperationObject<Variable
|
|
|
161
164
|
*
|
|
162
165
|
* @param args if this query accepts variables, the first argument should be the variables and the second the query options
|
|
163
166
|
* (optional). If it doesn't accept variables, the single optional argument must be the query options.
|
|
164
|
-
* @returns the response data
|
|
167
|
+
* @returns the response data.
|
|
165
168
|
* @throws `StackspotAPIError`
|
|
166
169
|
* @throws `Promise<Result>`
|
|
167
170
|
*/
|
|
@@ -169,7 +172,7 @@ export interface QueryObject<Variables, Result> extends OperationObject<Variable
|
|
|
169
172
|
...args: Variables extends void
|
|
170
173
|
? [options?: UseQueryObjectOptions<Result>]
|
|
171
174
|
: [variables: Variables, options?: UseQueryObjectOptions<Result>]
|
|
172
|
-
) => Result
|
|
175
|
+
) => Result,
|
|
173
176
|
/**
|
|
174
177
|
* Same as `query`, but a React Hook instead.
|
|
175
178
|
* @param args if this query accepts variables, the first argument should be the variables and the second the query options
|
|
@@ -220,7 +223,7 @@ export interface InfiniteQueryObject<Variables, Result, Accumulator extends keyo
|
|
|
220
223
|
? [variables?: Variables, options?: UseInfiniteQueryObjectOptions<Result>]
|
|
221
224
|
: [variables: Variables, options?: UseInfiniteQueryObjectOptions<Result>]
|
|
222
225
|
) => Readonly<[
|
|
223
|
-
(Accumulator extends keyof Result ? Result[Accumulator] : Result)
|
|
226
|
+
(Accumulator extends keyof Result ? Result[Accumulator] : Result),
|
|
224
227
|
UseInfiniteQueryResult<InfiniteData<Result>, StackspotAPIError>,
|
|
225
228
|
]>,
|
|
226
229
|
/**
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { CompletablePromise } from '@stack-spot/opa'
|
|
2
|
+
import { StreamCanceledError } from '../error/StreamCanceledError'
|
|
3
|
+
import { StreamError } from '../error/StreamError'
|
|
4
|
+
import { StreamJsonError } from '../error/StreamJsonError'
|
|
5
|
+
import { FetchEventStream } from '../network/types'
|
|
6
|
+
|
|
7
|
+
type StreamingStatus = 'pending' | 'success' | 'error'
|
|
8
|
+
type OnChangeListener<T> = (value: Partial<T>) => void
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* An object represented by a JSON stream. This can be watched as the stream runs.
|
|
12
|
+
*/
|
|
13
|
+
export class StreamedJson<T> {
|
|
14
|
+
private onChangeListeners: OnChangeListener<T>[] = []
|
|
15
|
+
private error: StreamError | undefined
|
|
16
|
+
private data: Partial<T> = {}
|
|
17
|
+
private fullPromise = new CompletablePromise<T>()
|
|
18
|
+
private abortController: AbortController | undefined
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @param response the fetch response.
|
|
22
|
+
* @param minChangeIntervalMS a stream can be too fast. This sets a minimum interval between running the listeners. The default is 50ms.
|
|
23
|
+
*/
|
|
24
|
+
constructor(eventsPromise: Promise<FetchEventStream>, abortController: AbortController, minChangeIntervalMS = 50) {
|
|
25
|
+
this.abortController = abortController
|
|
26
|
+
this.run(eventsPromise, minChangeIntervalMS)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
private async run(eventsPromise: Promise<FetchEventStream>, minChangeIntervalMS: number) {
|
|
30
|
+
let lastChangeCall = 0
|
|
31
|
+
try {
|
|
32
|
+
const events = await eventsPromise
|
|
33
|
+
let flushed = true
|
|
34
|
+
for await (const event of events) {
|
|
35
|
+
if (this.error) return
|
|
36
|
+
if (event.data) {
|
|
37
|
+
const json = JSON.parse(event.data)
|
|
38
|
+
StreamedJson.merge(json, this.data)
|
|
39
|
+
if (new Date().getTime() - lastChangeCall >= minChangeIntervalMS) {
|
|
40
|
+
this.onChangeListeners.forEach(l => l(this.data))
|
|
41
|
+
lastChangeCall = new Date().getTime()
|
|
42
|
+
flushed = true
|
|
43
|
+
} else {
|
|
44
|
+
flushed = false
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (!flushed) this.onChangeListeners.forEach(l => l(this.data))
|
|
49
|
+
} catch (error: any) {
|
|
50
|
+
if (error instanceof DOMException && error.name === 'AbortError') this.fail(new StreamCanceledError())
|
|
51
|
+
if (error instanceof SyntaxError) this.fail(new StreamJsonError())
|
|
52
|
+
else if (error instanceof StreamError) this.fail(error)
|
|
53
|
+
else this.fail(new StreamError(error?.message || `${error}`))
|
|
54
|
+
}
|
|
55
|
+
if (!this.error) this.complete()
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private static merge(source: Record<string, any>, target: Record<string, any>) {
|
|
59
|
+
Object.keys(source).forEach((k) => {
|
|
60
|
+
if (typeof source[k] !== typeof target[k]) target[k] = source[k]
|
|
61
|
+
else if (typeof source[k] === 'string') target[k] += source[k]
|
|
62
|
+
else if (Array.isArray(source[k])) target[k].push(...source[k])
|
|
63
|
+
else if (typeof source[k] === 'number') parseFloat(target[k] + source[k])
|
|
64
|
+
else if (typeof source[k] === 'object') this.merge(source[k], target[k])
|
|
65
|
+
else target[k] = source[k]
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
private clear() {
|
|
70
|
+
this.onChangeListeners = []
|
|
71
|
+
this.abortController = undefined
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
private fail(error: any) {
|
|
75
|
+
this.error = error
|
|
76
|
+
this.fullPromise.reject(this.error)
|
|
77
|
+
this.clear()
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
private complete() {
|
|
81
|
+
this.fullPromise.resolve(this.data as T)
|
|
82
|
+
this.clear()
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Returns the full value of the object once the stream finishes.
|
|
87
|
+
*/
|
|
88
|
+
getValue(): Promise<T> {
|
|
89
|
+
return this.fullPromise.promise
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Returns the streamed object with everything already streamed. This will be the complete object if the stream has finished.
|
|
94
|
+
*/
|
|
95
|
+
getPartialValue(): Partial<T> {
|
|
96
|
+
return this.data
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Watches the object as it's streamed. This doesn't wait for the value to be complete.
|
|
101
|
+
*
|
|
102
|
+
* The listener is called whenever the value changes.
|
|
103
|
+
*
|
|
104
|
+
* @param listener the function to call with the new value.
|
|
105
|
+
* @returns a function that, when called, removes the listener.
|
|
106
|
+
*/
|
|
107
|
+
onChange(listener: (value: Partial<T>) => void) {
|
|
108
|
+
if (this.fullPromise.resolved) {
|
|
109
|
+
listener(this.data)
|
|
110
|
+
return () => {}
|
|
111
|
+
}
|
|
112
|
+
this.onChangeListeners.push(listener)
|
|
113
|
+
return () => {
|
|
114
|
+
const index = this.onChangeListeners?.findIndex(l => l === listener)
|
|
115
|
+
if (index !== undefined && index >= 0) this.onChangeListeners?.splice(index, 1)
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
getStatus(): StreamingStatus {
|
|
120
|
+
if (this.error) return 'error'
|
|
121
|
+
if (this.fullPromise.resolved) return 'success'
|
|
122
|
+
return 'pending'
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
getError() {
|
|
126
|
+
return this.error
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* If this is a stream and it's not yet finished, calling this function cancels the stream.
|
|
131
|
+
*/
|
|
132
|
+
cancel() {
|
|
133
|
+
this.abortController?.abort(new StreamCanceledError())
|
|
134
|
+
}
|
|
135
|
+
}
|