@microsoft/agents-hosting 0.6.21-g3c2261b2fc → 1.0.0-ge4831811bf
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/package.json +2 -2
- package/dist/src/app/adaptiveCards/adaptiveCardsActions.d.ts +9 -6
- package/dist/src/app/adaptiveCards/adaptiveCardsActions.js +3 -3
- package/dist/src/app/adaptiveCards/adaptiveCardsActions.js.map +1 -1
- package/dist/src/app/adaptiveCards/query.d.ts +1 -1
- package/dist/src/app/agentApplication.d.ts +10 -10
- package/dist/src/app/agentApplication.js +9 -9
- package/dist/src/app/agentApplicationBuilder.d.ts +1 -1
- package/dist/src/app/agentApplicationBuilder.js +1 -1
- package/dist/src/app/agentApplicationOptions.d.ts +10 -1
- package/dist/src/app/appMemory.d.ts +1 -1
- package/dist/src/app/appRoute.d.ts +1 -1
- package/dist/src/app/extensions.d.ts +1 -1
- package/dist/src/app/extensions.js +1 -1
- package/dist/src/app/turnState.d.ts +6 -6
- package/dist/src/app/turnState.js +6 -6
- package/dist/src/app/turnStateProperty.d.ts +2 -2
- package/dist/src/app/turnStateProperty.js +2 -2
- package/dist/src/auth/authConfiguration.d.ts +8 -0
- package/dist/src/auth/authConfiguration.js +14 -6
- package/dist/src/auth/authConfiguration.js.map +1 -1
- package/dist/src/auth/jwt-middleware.js +1 -1
- package/dist/src/auth/jwt-middleware.js.map +1 -1
- package/dist/src/auth/msalTokenProvider.js +4 -4
- package/dist/src/auth/msalTokenProvider.js.map +1 -1
- package/dist/src/cloudAdapter.d.ts +5 -2
- package/dist/src/cloudAdapter.js +19 -11
- package/dist/src/cloudAdapter.js.map +1 -1
- package/dist/src/connector-client/connectorClient.d.ts +9 -7
- package/dist/src/connector-client/connectorClient.js +27 -11
- package/dist/src/connector-client/connectorClient.js.map +1 -1
- package/dist/src/headerPropagation.d.ts +71 -0
- package/dist/src/headerPropagation.js +76 -0
- package/dist/src/headerPropagation.js.map +1 -0
- package/dist/src/oauth/userTokenClient.js +34 -17
- package/dist/src/oauth/userTokenClient.js.map +1 -1
- package/dist/src/state/agentState.d.ts +2 -2
- package/dist/src/state/agentState.js +2 -2
- package/dist/src/state/agentStatePropertyAccesor.d.ts +18 -21
- package/dist/src/state/agentStatePropertyAccesor.js +18 -21
- package/dist/src/state/agentStatePropertyAccesor.js.map +1 -1
- package/dist/src/storage/fileStorage.d.ts +12 -10
- package/dist/src/storage/fileStorage.js +12 -10
- package/dist/src/storage/fileStorage.js.map +1 -1
- package/dist/src/transcript/transcriptLogger.d.ts +1 -1
- package/package.json +2 -2
- package/src/app/adaptiveCards/adaptiveCardsActions.ts +9 -6
- package/src/app/adaptiveCards/query.ts +1 -1
- package/src/app/agentApplication.ts +10 -10
- package/src/app/agentApplicationBuilder.ts +1 -1
- package/src/app/agentApplicationOptions.ts +11 -1
- package/src/app/appMemory.ts +1 -1
- package/src/app/appRoute.ts +1 -1
- package/src/app/extensions.ts +1 -1
- package/src/app/turnState.ts +6 -6
- package/src/app/turnStateProperty.ts +2 -2
- package/src/auth/authConfiguration.ts +20 -5
- package/src/auth/jwt-middleware.ts +1 -1
- package/src/auth/msalTokenProvider.ts +4 -4
- package/src/cloudAdapter.ts +24 -12
- package/src/connector-client/connectorClient.ts +30 -12
- package/src/headerPropagation.ts +129 -0
- package/src/oauth/userTokenClient.ts +36 -17
- package/src/state/agentState.ts +2 -2
- package/src/state/agentStatePropertyAccesor.ts +18 -21
- package/src/storage/fileStorage.ts +12 -10
- package/src/transcript/transcriptLogger.ts +1 -1
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A class that implements the HeaderPropagationCollection interface.
|
|
8
|
+
* It filters the incoming request headers based on the definition provided and loads them into the outgoing headers collection.
|
|
9
|
+
*/
|
|
10
|
+
export class HeaderPropagation implements HeaderPropagationCollection {
|
|
11
|
+
private _incomingRequests: Record<string, string>
|
|
12
|
+
private _outgoingHeaders: Record<string, string> = {}
|
|
13
|
+
|
|
14
|
+
private _headersToPropagate = ['x-ms-correlation-id']
|
|
15
|
+
|
|
16
|
+
public get incoming (): Record<string, string> {
|
|
17
|
+
return this._incomingRequests
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
public get outgoing (): Record<string, string> {
|
|
21
|
+
return this._outgoingHeaders
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
constructor (headers: Record<string, string | string[] | undefined>) {
|
|
25
|
+
if (!headers) {
|
|
26
|
+
throw new Error('Headers must be provided.')
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
this._incomingRequests = this.normalizeHeaders(headers)
|
|
30
|
+
this.propagate(this._headersToPropagate)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
propagate (headers: string[]) {
|
|
34
|
+
for (const key of headers ?? []) {
|
|
35
|
+
const lowerKey = key.toLowerCase()
|
|
36
|
+
if (this._incomingRequests[lowerKey] && !this._outgoingHeaders[lowerKey]) {
|
|
37
|
+
this._outgoingHeaders[lowerKey] = this._incomingRequests[lowerKey]
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
add (headers: Record<string, string>) {
|
|
43
|
+
for (const [key, value] of Object.entries(headers ?? {})) {
|
|
44
|
+
const lowerKey = key.toLowerCase()
|
|
45
|
+
if (!this._incomingRequests[lowerKey] && !this._outgoingHeaders[lowerKey]) {
|
|
46
|
+
this._outgoingHeaders[lowerKey] = value
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
concat (headers: Record<string, string>) {
|
|
52
|
+
for (const [key, value] of Object.entries(headers ?? {})) {
|
|
53
|
+
const lowerKey = key.toLowerCase()
|
|
54
|
+
if (this._incomingRequests[lowerKey] && !this._headersToPropagate.includes(lowerKey)) {
|
|
55
|
+
this._outgoingHeaders[lowerKey] = `${this._outgoingHeaders[lowerKey] ?? this._incomingRequests[lowerKey]} ${value}`.trim()
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
override (headers: Record<string, string>) {
|
|
61
|
+
for (const [key, value] of Object.entries(headers ?? {})) {
|
|
62
|
+
const lowerKey = key.toLowerCase()
|
|
63
|
+
if (!this._headersToPropagate.includes(lowerKey)) {
|
|
64
|
+
this._outgoingHeaders[lowerKey] = value
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Normalizes the headers by lowercasing the keys and ensuring the values are strings.
|
|
71
|
+
* @param headers The headers to normalize.
|
|
72
|
+
* @returns A new object with normalized headers.
|
|
73
|
+
*/
|
|
74
|
+
private normalizeHeaders (headers: Record<string, string | string[] | undefined>) {
|
|
75
|
+
return Object.entries(headers).reduce((acc, [key, value]) => {
|
|
76
|
+
if (value) {
|
|
77
|
+
acc[key.toLowerCase()] = Array.isArray(value) ? value.join(' ') : value
|
|
78
|
+
}
|
|
79
|
+
return acc
|
|
80
|
+
}, {} as Record<string, string>)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* A function type that defines how headers should be propagated.
|
|
86
|
+
*/
|
|
87
|
+
export interface HeaderPropagationDefinition {
|
|
88
|
+
(headers: HeaderPropagationCollection): void
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Defines the interface for managing header propagation.
|
|
93
|
+
*/
|
|
94
|
+
export interface HeaderPropagationCollection {
|
|
95
|
+
/**
|
|
96
|
+
* The collection of incoming headers from the incoming request.
|
|
97
|
+
* @remarks This collection is built based on the headers received in the request.
|
|
98
|
+
*/
|
|
99
|
+
incoming: Record<string, string>
|
|
100
|
+
/**
|
|
101
|
+
* The collection of headers that will be propagated to outgoing requests.
|
|
102
|
+
* @remarks This collection is built based on the incoming headers and the definition provided.
|
|
103
|
+
*/
|
|
104
|
+
outgoing: Record<string, string>
|
|
105
|
+
/**
|
|
106
|
+
* Propagates the incoming header value to the outgoing collection based on the header definition key.
|
|
107
|
+
* @param headers List of header keys to propagate.
|
|
108
|
+
* @remarks If the header does not exist in the incoming headers, it will be ignored.
|
|
109
|
+
*/
|
|
110
|
+
propagate(headers: string[]): void
|
|
111
|
+
/**
|
|
112
|
+
* Adds a header definition to the outgoing collection.
|
|
113
|
+
* @param headers Headers to add to the outgoing collection.
|
|
114
|
+
* @remarks If the header already exists, it will not be added.
|
|
115
|
+
*/
|
|
116
|
+
add(headers: Record<string, string>): void
|
|
117
|
+
/**
|
|
118
|
+
* Concatenates a header definition to the outgoing collection.
|
|
119
|
+
* @param headers Headers to concatenate to the outgoing collection.
|
|
120
|
+
* @remarks If the header does not exist in the incoming headers, it will be ignored. Unless the header is already present in the outgoing collection.
|
|
121
|
+
*/
|
|
122
|
+
concat(headers: Record<string, string>): void
|
|
123
|
+
/**
|
|
124
|
+
* Overrides a header definition in the outgoing collection.
|
|
125
|
+
* @param headers Headers to override in the outgoing collection.
|
|
126
|
+
* @remarks If the header does not exist in the incoming headers, it will be added to the outgoing collection.
|
|
127
|
+
*/
|
|
128
|
+
override(headers: Record<string, string>): void
|
|
129
|
+
}
|
|
@@ -22,43 +22,62 @@ export class UserTokenClient {
|
|
|
22
22
|
*/
|
|
23
23
|
constructor (private msAppId: string) {
|
|
24
24
|
const baseURL = 'https://api.botframework.com'
|
|
25
|
-
|
|
25
|
+
this.client = axios.create({
|
|
26
26
|
baseURL,
|
|
27
27
|
headers: {
|
|
28
28
|
Accept: 'application/json',
|
|
29
29
|
'User-Agent': getProductInfo(),
|
|
30
30
|
}
|
|
31
31
|
})
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
|
|
33
|
+
this.client.interceptors.request.use((config) => {
|
|
34
|
+
const { method, url, data, headers, params } = config
|
|
35
|
+
const { Authorization, authorization, ...headersToLog } = headers || {}
|
|
36
|
+
logger.debug('Request: ', {
|
|
37
|
+
host: this.client.getUri(),
|
|
38
|
+
url,
|
|
39
|
+
data,
|
|
40
|
+
method,
|
|
41
|
+
params,
|
|
42
|
+
headers: headersToLog
|
|
43
|
+
})
|
|
44
|
+
return config
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
this.client.interceptors.response.use(
|
|
34
48
|
(config) => {
|
|
35
|
-
const { status, statusText, config: requestConfig } = config
|
|
49
|
+
const { status, statusText, config: requestConfig, headers } = config
|
|
50
|
+
const { Authorization, authorization, ...headersToLog } = headers || {}
|
|
51
|
+
const { token, ...redactedData } = requestConfig?.data || {}
|
|
36
52
|
logger.debug('Response: ', {
|
|
37
53
|
status,
|
|
38
54
|
statusText,
|
|
39
|
-
host:
|
|
55
|
+
host: this.client.getUri(),
|
|
40
56
|
url: requestConfig?.url,
|
|
41
|
-
data:
|
|
57
|
+
data: redactedData,
|
|
42
58
|
method: requestConfig?.method,
|
|
59
|
+
headers: headersToLog
|
|
43
60
|
})
|
|
44
61
|
return config
|
|
45
62
|
},
|
|
46
63
|
(error) => {
|
|
47
64
|
const { code, status, message, stack, response } = error
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
65
|
+
const { headers } = response || {}
|
|
66
|
+
const errorDetails = {
|
|
67
|
+
code,
|
|
68
|
+
host: this.client.getUri(),
|
|
69
|
+
url: error.config.url,
|
|
70
|
+
method: error.config.method,
|
|
71
|
+
data: error.config.data,
|
|
72
|
+
message: message + JSON.stringify(response?.data),
|
|
73
|
+
headers,
|
|
74
|
+
stack,
|
|
75
|
+
}
|
|
76
|
+
logger.debug('Response error: ', errorDetails)
|
|
77
|
+
if (errorDetails.url === '/api/usertoken/GetToken' && status !== 404) {
|
|
58
78
|
return Promise.reject(errorDetails)
|
|
59
79
|
}
|
|
60
80
|
})
|
|
61
|
-
this.client = axiosInstance
|
|
62
81
|
}
|
|
63
82
|
|
|
64
83
|
/**
|
package/src/state/agentState.ts
CHANGED
|
@@ -44,8 +44,8 @@ export interface CustomKey {
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
/**
|
|
47
|
-
* Manages the state of an Agent across turns in a conversation.
|
|
48
|
-
*
|
|
47
|
+
* @summary Manages the state of an Agent across turns in a conversation.
|
|
48
|
+
* @remarks
|
|
49
49
|
* AgentState provides functionality to persist and retrieve state data using
|
|
50
50
|
* a storage provider. It handles caching state in the turn context for performance,
|
|
51
51
|
* calculating change hashes to detect modifications, and managing property accessors
|
|
@@ -73,7 +73,7 @@ export interface StatePropertyAccessor<T = any> {
|
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
/**
|
|
76
|
-
* Provides typed access to an Agent state property with automatic state loading and persistence management.
|
|
76
|
+
* @summary Provides typed access to an Agent state property with automatic state loading and persistence management.
|
|
77
77
|
*
|
|
78
78
|
* @remarks
|
|
79
79
|
* AgentStatePropertyAccessor simplifies working with persisted state by abstracting
|
|
@@ -86,9 +86,9 @@ export interface StatePropertyAccessor<T = any> {
|
|
|
86
86
|
* - **Memory Management**: Efficient in-memory caching with explicit persistence control
|
|
87
87
|
* - **Custom Keys**: Support for custom storage keys for advanced scenarios
|
|
88
88
|
*
|
|
89
|
-
*
|
|
89
|
+
* ### Key Features
|
|
90
90
|
*
|
|
91
|
-
*
|
|
91
|
+
* #### Type Safety
|
|
92
92
|
* The accessor provides compile-time type checking when using TypeScript:
|
|
93
93
|
* ```typescript
|
|
94
94
|
* interface UserProfile {
|
|
@@ -98,7 +98,7 @@ export interface StatePropertyAccessor<T = any> {
|
|
|
98
98
|
* const userProfile = userState.createProperty<UserProfile>("userProfile");
|
|
99
99
|
* ```
|
|
100
100
|
*
|
|
101
|
-
*
|
|
101
|
+
* #### Automatic Default Value Handling
|
|
102
102
|
* When a property doesn't exist, default values are automatically cloned and stored:
|
|
103
103
|
* ```typescript
|
|
104
104
|
* // If userProfile doesn't exist, the default will be cloned and saved
|
|
@@ -108,7 +108,7 @@ export interface StatePropertyAccessor<T = any> {
|
|
|
108
108
|
* });
|
|
109
109
|
* ```
|
|
110
110
|
*
|
|
111
|
-
*
|
|
111
|
+
* #### Explicit Persistence Control
|
|
112
112
|
* Changes are kept in memory until explicitly persisted:
|
|
113
113
|
* ```typescript
|
|
114
114
|
* // Modify the state
|
|
@@ -120,9 +120,9 @@ export interface StatePropertyAccessor<T = any> {
|
|
|
120
120
|
* await userState.saveChanges(context);
|
|
121
121
|
* ```
|
|
122
122
|
*
|
|
123
|
-
*
|
|
123
|
+
* ### Usage Examples
|
|
124
124
|
*
|
|
125
|
-
*
|
|
125
|
+
* @example Basic Usage
|
|
126
126
|
* ```typescript
|
|
127
127
|
* // Create a property accessor
|
|
128
128
|
* const userProfile = userState.createProperty<UserProfile>("userProfile");
|
|
@@ -141,7 +141,7 @@ export interface StatePropertyAccessor<T = any> {
|
|
|
141
141
|
* await userState.saveChanges(context); // Persist to storage
|
|
142
142
|
* ```
|
|
143
143
|
*
|
|
144
|
-
*
|
|
144
|
+
* @example Working with Primitive Types
|
|
145
145
|
* ```typescript
|
|
146
146
|
* const counterProperty = userState.createProperty<number>("counter");
|
|
147
147
|
*
|
|
@@ -151,7 +151,7 @@ export interface StatePropertyAccessor<T = any> {
|
|
|
151
151
|
* await userState.saveChanges(context);
|
|
152
152
|
* ```
|
|
153
153
|
*
|
|
154
|
-
*
|
|
154
|
+
* @example Conditional Logic
|
|
155
155
|
* ```typescript
|
|
156
156
|
* const settingsProperty = userState.createProperty<Settings>("settings");
|
|
157
157
|
*
|
|
@@ -163,7 +163,7 @@ export interface StatePropertyAccessor<T = any> {
|
|
|
163
163
|
* }
|
|
164
164
|
* ```
|
|
165
165
|
*
|
|
166
|
-
*
|
|
166
|
+
* @example Custom Storage Keys
|
|
167
167
|
* ```typescript
|
|
168
168
|
* // Store state with a custom key for multi-tenant scenarios
|
|
169
169
|
* const customKey = { key: `tenant_${tenantId}` };
|
|
@@ -171,7 +171,7 @@ export interface StatePropertyAccessor<T = any> {
|
|
|
171
171
|
* await dataProperty.set(context, updatedData, customKey);
|
|
172
172
|
* ```
|
|
173
173
|
*
|
|
174
|
-
*
|
|
174
|
+
* ### Important Notes
|
|
175
175
|
*
|
|
176
176
|
* - **Thread Safety**: This class is not thread-safe. Ensure proper synchronization in concurrent scenarios.
|
|
177
177
|
* - **Memory Usage**: State objects are kept in memory until the context is disposed.
|
|
@@ -206,20 +206,19 @@ export class AgentStatePropertyAccessor<T = any> implements StatePropertyAccesso
|
|
|
206
206
|
constructor (protected readonly state: AgentState, public readonly name: string) { }
|
|
207
207
|
|
|
208
208
|
/**
|
|
209
|
-
* Deletes the property from the state storage.
|
|
210
|
-
*
|
|
209
|
+
* @summary Deletes the property from the state storage.
|
|
210
|
+
* @remarks
|
|
211
211
|
* This operation removes the property from the in-memory state object but does not
|
|
212
212
|
* automatically persist the change to the underlying storage. You must call
|
|
213
213
|
* `state.saveChanges(context)` afterwards to persist the deletion.
|
|
214
214
|
*
|
|
215
|
-
* @remarks
|
|
216
215
|
* - If the property doesn't exist, this operation is a no-op
|
|
217
216
|
* - The deletion only affects the in-memory state until `saveChanges()` is called
|
|
218
217
|
* - After deletion, subsequent `get()` calls will return `undefined` (or the default value if provided)
|
|
219
218
|
*
|
|
220
219
|
* @param context The turn context for the current conversation turn
|
|
221
220
|
* @param customKey Optional custom key for accessing state in a specific storage location.
|
|
222
|
-
*
|
|
221
|
+
* Useful for multi-tenant scenarios or when state needs to be partitioned.
|
|
223
222
|
*
|
|
224
223
|
* @returns A promise that resolves when the delete operation is complete
|
|
225
224
|
*
|
|
@@ -252,15 +251,14 @@ export class AgentStatePropertyAccessor<T = any> implements StatePropertyAccesso
|
|
|
252
251
|
}
|
|
253
252
|
|
|
254
253
|
/**
|
|
255
|
-
* Retrieves the value of the property from state storage.
|
|
256
|
-
*
|
|
254
|
+
* @summary Retrieves the value of the property from state storage.
|
|
255
|
+
* @remarks
|
|
257
256
|
* This method provides intelligent default value handling:
|
|
258
257
|
* - If the property exists, its value is returned
|
|
259
258
|
* - If the property doesn't exist and a default value is provided, the default is deep cloned,
|
|
260
259
|
* stored in state, and returned
|
|
261
260
|
* - If the property doesn't exist and no default is provided, `undefined` is returned
|
|
262
261
|
*
|
|
263
|
-
* @remarks
|
|
264
262
|
* **Deep Cloning**: Default values are deep cloned using JSON serialization to prevent
|
|
265
263
|
* reference sharing issues. This means:
|
|
266
264
|
* - Functions, symbols, and circular references will be lost
|
|
@@ -331,13 +329,12 @@ export class AgentStatePropertyAccessor<T = any> implements StatePropertyAccesso
|
|
|
331
329
|
}
|
|
332
330
|
|
|
333
331
|
/**
|
|
334
|
-
* Sets the value of the property in state storage.
|
|
335
|
-
*
|
|
332
|
+
* @summary Sets the value of the property in state storage.
|
|
333
|
+
* @remarks
|
|
336
334
|
* This operation updates the property in the in-memory state object but does not
|
|
337
335
|
* automatically persist the change to the underlying storage. You must call
|
|
338
336
|
* `state.saveChanges(context)` afterwards to persist the changes.
|
|
339
337
|
*
|
|
340
|
-
* @remarks
|
|
341
338
|
* **Memory vs Storage**: Changes are immediately reflected in memory and will be
|
|
342
339
|
* available to subsequent `get()` calls within the same context, but are not
|
|
343
340
|
* persisted to storage until `saveChanges()` is called.
|
|
@@ -8,7 +8,7 @@ import fs from 'fs'
|
|
|
8
8
|
import { Storage, StoreItem } from './storage'
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
* A file-based storage implementation that persists data to the local filesystem.
|
|
11
|
+
* @summary A file-based storage implementation that persists data to the local filesystem.
|
|
12
12
|
*
|
|
13
13
|
* @remarks
|
|
14
14
|
* FileStorage stores all data in a single JSON file named 'state.json' within a specified folder.
|
|
@@ -19,6 +19,16 @@ import { Storage, StoreItem } from './storage'
|
|
|
19
19
|
* can be any JSON-serializable data. All operations are synchronous file I/O operations
|
|
20
20
|
* wrapped in Promise interfaces to match the Storage contract.
|
|
21
21
|
*
|
|
22
|
+
* ### Warning
|
|
23
|
+
* This implementation does not provide:
|
|
24
|
+
* - Thread safety for concurrent access
|
|
25
|
+
* - Optimistic concurrency control (eTag support)
|
|
26
|
+
* - Atomic operations across multiple keys
|
|
27
|
+
* - Scale for large datasets
|
|
28
|
+
*
|
|
29
|
+
* For production scenarios requiring these features, consider using
|
|
30
|
+
* database-backed storage implementations instead.
|
|
31
|
+
*
|
|
22
32
|
* @example
|
|
23
33
|
* ```typescript
|
|
24
34
|
* const storage = new FileStorage('./data');
|
|
@@ -37,15 +47,7 @@ import { Storage, StoreItem } from './storage'
|
|
|
37
47
|
* await storage.delete(['conversation456']);
|
|
38
48
|
* ```
|
|
39
49
|
*
|
|
40
|
-
|
|
41
|
-
* This implementation does not provide:
|
|
42
|
-
* - Thread safety for concurrent access
|
|
43
|
-
* - Optimistic concurrency control (eTag support)
|
|
44
|
-
* - Atomic operations across multiple keys
|
|
45
|
-
* - Scale for large datasets
|
|
46
|
-
*
|
|
47
|
-
* For production scenarios requiring these features, consider using
|
|
48
|
-
* database-backed storage implementations instead.
|
|
50
|
+
|
|
49
51
|
*/
|
|
50
52
|
export class FileStorage implements Storage {
|
|
51
53
|
private _folder: string
|