fa-mcp-sdk 0.2.182 → 0.2.192
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/cli-template/.claude/agents/fa-mcp-sdk.md +158 -0
- package/cli-template/FA-MCP-SDK-DOC/00-FA-MCP-SDK-index.md +216 -0
- package/cli-template/FA-MCP-SDK-DOC/01-getting-started.md +209 -0
- package/cli-template/FA-MCP-SDK-DOC/02-tools-and-api.md +321 -0
- package/cli-template/FA-MCP-SDK-DOC/03-configuration.md +415 -0
- package/cli-template/FA-MCP-SDK-DOC/04-authentication.md +544 -0
- package/cli-template/FA-MCP-SDK-DOC/05-ad-authorization.md +476 -0
- package/cli-template/FA-MCP-SDK-DOC/06-utilities.md +394 -0
- package/cli-template/FA-MCP-SDK-DOC/07-testing-and-operations.md +171 -0
- package/dist/core/_types_/types.d.ts +0 -5
- package/dist/core/_types_/types.d.ts.map +1 -1
- package/dist/core/index.d.ts +2 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +2 -0
- package/dist/core/index.js.map +1 -1
- package/dist/core/web/home-api.js +1 -1
- package/dist/core/web/home-api.js.map +1 -1
- package/dist/core/web/openapi.d.ts +64 -0
- package/dist/core/web/openapi.d.ts.map +1 -0
- package/dist/core/web/openapi.js +235 -0
- package/dist/core/web/openapi.js.map +1 -0
- package/dist/core/web/server-http.d.ts.map +1 -1
- package/dist/core/web/server-http.js +11 -9
- package/dist/core/web/server-http.js.map +1 -1
- package/dist/core/web/static/home/index.html +4 -2
- package/dist/core/web/static/home/script.js +2 -2
- package/package.json +9 -12
- package/src/template/api/router.ts +66 -4
- package/src/template/start.ts +0 -5
- package/cli-template/FA-MCP-SDK.md +0 -2540
- package/src/template/api/swagger.ts +0 -167
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
# Configuration, Cache, and Database
|
|
2
|
+
|
|
3
|
+
## Configuration Management
|
|
4
|
+
|
|
5
|
+
### Using `appConfig`
|
|
6
|
+
|
|
7
|
+
Access configuration in your code:
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
import { appConfig } from 'fa-mcp-sdk';
|
|
11
|
+
|
|
12
|
+
// Access configuration values
|
|
13
|
+
const serverPort = appConfig.webServer.port;
|
|
14
|
+
const dbEnabled = appConfig.isMainDBUsed;
|
|
15
|
+
const transport = appConfig.mcp.transportType; // 'stdio' | 'http'
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
### Configuration Files
|
|
19
|
+
|
|
20
|
+
**`config/default.yaml`** - Base configuration:
|
|
21
|
+
```yaml
|
|
22
|
+
accessPoints:
|
|
23
|
+
myService:
|
|
24
|
+
title: 'My remote service'
|
|
25
|
+
host: <host>
|
|
26
|
+
port: 9999
|
|
27
|
+
token: '***'
|
|
28
|
+
noConsul: true # Use if the service developers do not provide registration in consul
|
|
29
|
+
consulServiceName: <consulServiceName>
|
|
30
|
+
|
|
31
|
+
# --------------------------------------------------
|
|
32
|
+
# CACHING Reduces API calls by caching responses
|
|
33
|
+
# --------------------------------------------------
|
|
34
|
+
cache:
|
|
35
|
+
# Default Cache TTL in seconds
|
|
36
|
+
ttlSeconds: 300
|
|
37
|
+
# Default maximum number of cached items
|
|
38
|
+
maxItems: 1000
|
|
39
|
+
|
|
40
|
+
consul:
|
|
41
|
+
check:
|
|
42
|
+
interval: '10s'
|
|
43
|
+
timeout: '5s'
|
|
44
|
+
deregistercriticalserviceafter: '3m'
|
|
45
|
+
agent:
|
|
46
|
+
# Credentials for getting information about services in the DEV DC
|
|
47
|
+
dev:
|
|
48
|
+
dc: '{{consul.agent.dev.dc}}'
|
|
49
|
+
host: '{{consul.agent.dev.host}}'
|
|
50
|
+
port: 443
|
|
51
|
+
secure: true
|
|
52
|
+
# Token for getting information about DEV services
|
|
53
|
+
token: '***'
|
|
54
|
+
# Credentials for getting information about services in the PROD DC
|
|
55
|
+
prd:
|
|
56
|
+
dc: '{{consul.agent.prd.dc}}'
|
|
57
|
+
host: '{{consul.agent.prd.host}}'
|
|
58
|
+
port: 443
|
|
59
|
+
secure: true
|
|
60
|
+
# Token for obtaining information about PROD services
|
|
61
|
+
token: '***'
|
|
62
|
+
# Credentials for registering the service with Consul
|
|
63
|
+
reg:
|
|
64
|
+
# The host of the consul agent where the service will be registered. If not specified, the server on which the service is running is used
|
|
65
|
+
host: null
|
|
66
|
+
port: 8500
|
|
67
|
+
secure: false
|
|
68
|
+
# Token for registering the service in the consul agent
|
|
69
|
+
token: '***'
|
|
70
|
+
service:
|
|
71
|
+
enable: {{consul.service.enable}} # true - Allows registration of the service with the consul
|
|
72
|
+
name: <name> # <name> will be replaced by <package.json>.name at initialization
|
|
73
|
+
instance: '{{SERVICE_INSTANCE}}' # This value will be specified as a suffix in the id of the service
|
|
74
|
+
version: <version> # <version> will be replaced by <package.json>.version at initialization
|
|
75
|
+
description: <description> # <description> will be replaced by <package.json>.description at initialization
|
|
76
|
+
tags: [] # If null or empty array - Will be pulled up from package.keywords at initialization
|
|
77
|
+
meta:
|
|
78
|
+
# "Home" page link template
|
|
79
|
+
who: 'http://{address}:{port}/'
|
|
80
|
+
envCode: # Used to generate the service ID
|
|
81
|
+
prod: {{consul.envCode.prod}} # Production environment code
|
|
82
|
+
dev: {{consul.envCode.dev}} # Development environment code
|
|
83
|
+
|
|
84
|
+
db:
|
|
85
|
+
postgres:
|
|
86
|
+
dbs:
|
|
87
|
+
main:
|
|
88
|
+
label: 'My Database'
|
|
89
|
+
host: '' # To exclude the use of the database, you need to set host = ''
|
|
90
|
+
port: 5432
|
|
91
|
+
database: <database>
|
|
92
|
+
user: <user>
|
|
93
|
+
password: <password>
|
|
94
|
+
usedExtensions: []
|
|
95
|
+
|
|
96
|
+
logger:
|
|
97
|
+
level: info
|
|
98
|
+
useFileLogger: {{logger.useFileLogger}} # To use or not to use logging to a file
|
|
99
|
+
# Absolute path to the folder where logs will be written. Default <proj_root>/../logs
|
|
100
|
+
dir: '{{logger.dir}}'
|
|
101
|
+
|
|
102
|
+
mcp:
|
|
103
|
+
transportType: http # stdio | http
|
|
104
|
+
# Response format configuration.
|
|
105
|
+
# - structuredContent - default - the response in result.structuredContent returns JSON
|
|
106
|
+
# - text - in the response, serialized JSON is returned in result.content[0].text
|
|
107
|
+
toolAnswerAs: text # text | structuredContent
|
|
108
|
+
rateLimit:
|
|
109
|
+
maxRequests: 100
|
|
110
|
+
windowMs: 60000 # 1 minute
|
|
111
|
+
|
|
112
|
+
swagger:
|
|
113
|
+
servers: # An array of servers that will be added to swagger docs
|
|
114
|
+
# - url: http://localhost:9020
|
|
115
|
+
# description: "Development server (localhost)"
|
|
116
|
+
# - url: http://0.0.0.0:9020
|
|
117
|
+
# description: "Development server (all interfaces)"
|
|
118
|
+
# - url: http://<prod_server_host_or_ip>:{{port}}
|
|
119
|
+
# description: "PROD server"
|
|
120
|
+
- url: https://{{mcp.domain}}
|
|
121
|
+
description: "PROD server"
|
|
122
|
+
|
|
123
|
+
uiColor:
|
|
124
|
+
# Font color of the header and a number of interface elements on the HOME page
|
|
125
|
+
primary: '#0f65dc'
|
|
126
|
+
|
|
127
|
+
webServer:
|
|
128
|
+
host: '0.0.0.0'
|
|
129
|
+
port: {{port}}
|
|
130
|
+
# array of hosts that CORS skips
|
|
131
|
+
originHosts: ['localhost', '0.0.0.0']
|
|
132
|
+
# Authentication is configured here only when accessing the MCP server
|
|
133
|
+
# Authentication in services that enable tools, resources, and prompts
|
|
134
|
+
# is implemented more deeply. To do this, you need to use the information passed in HTTP headers
|
|
135
|
+
# You can also use a custom authorization function
|
|
136
|
+
auth:
|
|
137
|
+
enabled: false # Enables/disables authorization
|
|
138
|
+
# ========================================================================
|
|
139
|
+
# PERMANENT SERVER TOKENS
|
|
140
|
+
# Static tokens for server-to-server communication
|
|
141
|
+
# CPU cost: O(1) - fastest authentication method
|
|
142
|
+
#
|
|
143
|
+
# To enable this authentication, you need to set auth.enabled = true
|
|
144
|
+
# and set one token of at least 20 characters in length
|
|
145
|
+
# ========================================================================
|
|
146
|
+
permanentServerTokens: [ ] # Add your server tokens here: ['token1', 'token2']
|
|
147
|
+
|
|
148
|
+
# ========================================================================
|
|
149
|
+
# JWT TOKEN WITH SYMMETRIC ENCRYPTION
|
|
150
|
+
# Custom JWT tokens with AES-256 encryption
|
|
151
|
+
# CPU cost: Medium - decryption + JSON parsing
|
|
152
|
+
#
|
|
153
|
+
# To enable this authentication, you need to set auth.enabled = true and set
|
|
154
|
+
# encryptKey to at least 20 characters
|
|
155
|
+
# ========================================================================
|
|
156
|
+
jwtToken:
|
|
157
|
+
# Symmetric encryption key to generate a token for this MCP (minimum 8 chars)
|
|
158
|
+
encryptKey: '***'
|
|
159
|
+
# If webServer.auth.enabled and the parameter true, the service name and the service specified in the token will be checked
|
|
160
|
+
checkMCPName: true
|
|
161
|
+
|
|
162
|
+
# ========================================================================
|
|
163
|
+
# Basic Authentication - Base64 encoded username:password
|
|
164
|
+
# CPU cost: Medium - Base64 decoding + string comparison
|
|
165
|
+
# To enable this authentication, you need to set auth.enabled = true
|
|
166
|
+
# and set username and password to valid values
|
|
167
|
+
# ========================================================================
|
|
168
|
+
basic:
|
|
169
|
+
username: ''
|
|
170
|
+
password: '***'
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
**`config/local.yaml`** - local overrides. Usually contains secrets.
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## Cache Management
|
|
178
|
+
|
|
179
|
+
### `getCache(options?): CacheManager`
|
|
180
|
+
|
|
181
|
+
Get or create a global cache instance for your MCP server.
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
import { getCache, CacheManager } from 'fa-mcp-sdk';
|
|
185
|
+
|
|
186
|
+
// Create default cache instance
|
|
187
|
+
const cache = getCache();
|
|
188
|
+
|
|
189
|
+
// Create cache with custom options
|
|
190
|
+
const customCache = getCache({
|
|
191
|
+
ttlSeconds: 600, // Default TTL: 10 minutes
|
|
192
|
+
maxItems: 5000, // Max cached items
|
|
193
|
+
checkPeriod: 300, // Cleanup interval in seconds
|
|
194
|
+
verbose: true // Enable debug logging
|
|
195
|
+
});
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Cache Methods
|
|
199
|
+
|
|
200
|
+
The `CacheManager` provides the following methods:
|
|
201
|
+
|
|
202
|
+
| Method | Description | Example |
|
|
203
|
+
|--------|-------------|---------|
|
|
204
|
+
| `get<T>(key)` | Get value from cache | `const user = cache.get<User>('user:123');` |
|
|
205
|
+
| `set<T>(key, value, ttl?)` | Set value in cache | `cache.set('user:123', userData, 300);` |
|
|
206
|
+
| `has(key)` | Check if key exists | `if (cache.has('user:123')) { ... }` |
|
|
207
|
+
| `del(key)` | Delete key from cache | `cache.del('user:123');` |
|
|
208
|
+
| `take<T>(key)` | Get and delete (single use) | `const otp = cache.take<string>('otp:123');` |
|
|
209
|
+
| `mget<T>(keys[])` | Get multiple values | `const users = cache.mget(['user:1', 'user:2']);` |
|
|
210
|
+
| `mset(items[])` | Set multiple values | `cache.mset([{key: 'a', val: 1}, {key: 'b', val: 2}]);` |
|
|
211
|
+
| `getOrSet<T>(key, factory, ttl?)` | Get or compute value | `const data = await cache.getOrSet('key', () => fetchData());` |
|
|
212
|
+
| `keys()` | List all keys | `const allKeys = cache.keys();` |
|
|
213
|
+
| `flush()` | Clear all entries | `cache.flush();` |
|
|
214
|
+
| `ttl(key, seconds)` | Update key TTL | `cache.ttl('user:123', 600);` |
|
|
215
|
+
| `getTtl(key)` | Get remaining TTL | `const remaining = cache.getTtl('user:123');` |
|
|
216
|
+
| `getStats()` | Get cache statistics | `const stats = cache.getStats();` |
|
|
217
|
+
| `close()` | Close cache resources | `cache.close();` |
|
|
218
|
+
|
|
219
|
+
### Usage Examples
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
import { getCache } from 'fa-mcp-sdk';
|
|
223
|
+
|
|
224
|
+
const cache = getCache();
|
|
225
|
+
|
|
226
|
+
// Basic caching
|
|
227
|
+
cache.set('user:123', { name: 'John', email: 'john@example.com' });
|
|
228
|
+
const user = cache.get<User>('user:123');
|
|
229
|
+
|
|
230
|
+
// Cache with TTL (time to live)
|
|
231
|
+
cache.set('session:abc', sessionData, 1800); // 30 minutes
|
|
232
|
+
|
|
233
|
+
// Single-use values (OTP, tokens)
|
|
234
|
+
cache.set('otp:user123', '123456', 300);
|
|
235
|
+
const otp = cache.take('otp:user123'); // Gets and deletes
|
|
236
|
+
|
|
237
|
+
// Get-or-set pattern
|
|
238
|
+
const expensiveData = await cache.getOrSet(
|
|
239
|
+
'computation:key',
|
|
240
|
+
async () => {
|
|
241
|
+
// This function runs only on cache miss
|
|
242
|
+
return await performExpensiveOperation();
|
|
243
|
+
},
|
|
244
|
+
3600 // Cache for 1 hour
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
// Batch operations
|
|
248
|
+
const userData = cache.mget(['user:1', 'user:2', 'user:3']);
|
|
249
|
+
cache.mset([
|
|
250
|
+
{ key: 'user:1', val: user1Data },
|
|
251
|
+
{ key: 'user:2', val: user2Data, ttl: 600 }
|
|
252
|
+
]);
|
|
253
|
+
|
|
254
|
+
// Cache monitoring
|
|
255
|
+
const stats = cache.getStats();
|
|
256
|
+
console.log(`Hit rate: ${(stats.hitRate * 100).toFixed(1)}%`);
|
|
257
|
+
console.log(`Keys: ${stats.keys}, Memory: ${stats.vsize} bytes`);
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
## Database Integration
|
|
263
|
+
|
|
264
|
+
To disable the use of the database, you need to set appConfig.db.postgres.dbs.main.host to an empty value.
|
|
265
|
+
In this case, when the configuration is formed, appConfig.isMainDBUsed is set to false.
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
If you enable database support (`isMainDBUsed: true` in config):
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
import {
|
|
272
|
+
queryMAIN,
|
|
273
|
+
execMAIN,
|
|
274
|
+
oneRowMAIN,
|
|
275
|
+
getMainDBConnectionStatus
|
|
276
|
+
} from 'fa-mcp-sdk';
|
|
277
|
+
|
|
278
|
+
// Check database connection. If there is no connection, the application stops
|
|
279
|
+
await checkMainDB();
|
|
280
|
+
|
|
281
|
+
// queryMAIN - the main function of executing SQL queries to the main database
|
|
282
|
+
|
|
283
|
+
// Function Signature:
|
|
284
|
+
const queryMAIN = async <R extends QueryResultRow = any> (
|
|
285
|
+
arg: string | IQueryPgArgsCOptional,
|
|
286
|
+
sqlValues?: any[],
|
|
287
|
+
throwError = false,
|
|
288
|
+
): Promise<QueryResult<R> | undefined> {...}
|
|
289
|
+
|
|
290
|
+
// Types used:
|
|
291
|
+
export interface IQueryPgArgs {
|
|
292
|
+
connectionId: string,
|
|
293
|
+
poolConfig?: PoolConfig & IDbOptionsPg,
|
|
294
|
+
client?: IPoolPg,
|
|
295
|
+
sqlText: string,
|
|
296
|
+
sqlValues?: any[],
|
|
297
|
+
throwError?: boolean,
|
|
298
|
+
prefix?: string,
|
|
299
|
+
registerTypesFunctions?: IRegisterTypeFn[],
|
|
300
|
+
}
|
|
301
|
+
export interface IQueryPgArgsCOptional extends Omit<IQueryPgArgs, 'connectionId'> {
|
|
302
|
+
connectionId?: string
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Examples of use
|
|
306
|
+
const users1 = await queryMAIN('SELECT * FROM users WHERE active = $1', [true]);
|
|
307
|
+
// Alternative use case
|
|
308
|
+
const users2 = await queryMAIN({ sqlText: 'SELECT * FROM users WHERE active = $1', sqlValues: [true] });
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
// execMAIN - execute SQL commands without returning result set
|
|
312
|
+
// Function Signature:
|
|
313
|
+
const execMAIN = async (
|
|
314
|
+
arg: string | IQueryPgArgsCOptional,
|
|
315
|
+
): Promise<number | undefined> {...}
|
|
316
|
+
|
|
317
|
+
// Examples:
|
|
318
|
+
await execMAIN('INSERT INTO logs (message, created_at) VALUES ($1, $2)',
|
|
319
|
+
['Server started', new Date()]);
|
|
320
|
+
await execMAIN({ sqlText: 'UPDATE users SET active = $1 WHERE id = $2', sqlValues: [false, userId] });
|
|
321
|
+
|
|
322
|
+
// queryRsMAIN - execute SQL and return rows array directly
|
|
323
|
+
// Function Signature:
|
|
324
|
+
const queryRsMAIN = async <R extends QueryResultRow = any> (
|
|
325
|
+
arg: string | IQueryPgArgsCOptional,
|
|
326
|
+
sqlValues?: any[],
|
|
327
|
+
throwError = false,
|
|
328
|
+
): Promise<R[] | undefined> {...}
|
|
329
|
+
|
|
330
|
+
// Example:
|
|
331
|
+
const users = await queryRsMAIN<User>('SELECT * FROM users WHERE active = $1', [true]);
|
|
332
|
+
|
|
333
|
+
// oneRowMAIN - execute SQL and return single row
|
|
334
|
+
// Function Signature:
|
|
335
|
+
const oneRowMAIN = async <R extends QueryResultRow = any> (
|
|
336
|
+
arg: string | IQueryPgArgsCOptional,
|
|
337
|
+
sqlValues?: any[],
|
|
338
|
+
throwError = false,
|
|
339
|
+
): Promise<R | undefined> {...}
|
|
340
|
+
|
|
341
|
+
// Example:
|
|
342
|
+
const user = await oneRowMAIN<User>('SELECT * FROM users WHERE id = $1', [userId]);
|
|
343
|
+
|
|
344
|
+
// getMainDBConnectionStatus - check database connection status
|
|
345
|
+
// Function Signature:
|
|
346
|
+
const getMainDBConnectionStatus = async (): Promise<string> {...}
|
|
347
|
+
|
|
348
|
+
// Possible return values: 'connected' | 'disconnected' | 'error' | 'db_not_used'
|
|
349
|
+
const status = await getMainDBConnectionStatus();
|
|
350
|
+
|
|
351
|
+
// checkMainDB - verify database connectivity (stops application if failed)
|
|
352
|
+
// Function Signature:
|
|
353
|
+
const checkMainDB = async (): Promise<void> {...}
|
|
354
|
+
|
|
355
|
+
// Example:
|
|
356
|
+
await checkMainDB(); // Throws or exits process if DB connection fails
|
|
357
|
+
|
|
358
|
+
// getInsertSqlMAIN - generate INSERT SQL statement
|
|
359
|
+
// Function Signature:
|
|
360
|
+
const getInsertSqlMAIN = async <U extends TDBRecord = TDBRecord> (arg: {
|
|
361
|
+
commonSchemaAndTable: string,
|
|
362
|
+
recordset: TRecordSet<U>,
|
|
363
|
+
excludeFromInsert?: string[],
|
|
364
|
+
addOutputInserted?: boolean,
|
|
365
|
+
isErrorOnConflict?: boolean,
|
|
366
|
+
keepSerialFields?: boolean,
|
|
367
|
+
}): Promise<string> {...}
|
|
368
|
+
|
|
369
|
+
// Example:
|
|
370
|
+
const insertSql = await getInsertSqlMAIN({
|
|
371
|
+
commonSchemaAndTable: 'public.users',
|
|
372
|
+
recordset: [{ name: 'John', email: 'john@example.com' }],
|
|
373
|
+
addOutputInserted: true
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
// getMergeSqlMAIN - generate UPSERT (INSERT...ON CONFLICT) SQL statement
|
|
377
|
+
// Function Signature:
|
|
378
|
+
const getMergeSqlMAIN = async <U extends TDBRecord = TDBRecord> (arg: {
|
|
379
|
+
commonSchemaAndTable: string,
|
|
380
|
+
recordset: TRecordSet<U>,
|
|
381
|
+
conflictFields?: string[],
|
|
382
|
+
omitFields?: string[],
|
|
383
|
+
updateFields?: string[],
|
|
384
|
+
fieldsExcludedFromUpdatePart?: string[],
|
|
385
|
+
noUpdateIfNull?: boolean,
|
|
386
|
+
mergeCorrection?: (_sql: string) => string,
|
|
387
|
+
returning?: string,
|
|
388
|
+
}): Promise<string> {...}
|
|
389
|
+
|
|
390
|
+
// Example:
|
|
391
|
+
const mergeSql = await getMergeSqlMAIN({
|
|
392
|
+
commonSchemaAndTable: 'public.users',
|
|
393
|
+
recordset: [{ id: 1, name: 'John Updated', email: 'john@example.com' }],
|
|
394
|
+
conflictFields: ['email'],
|
|
395
|
+
returning: '*'
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
// mergeByBatch - execute merge operations in batches
|
|
399
|
+
// Function Signature:
|
|
400
|
+
const mergeByBatch = async <U extends TDBRecord = TDBRecord> (arg: {
|
|
401
|
+
recordset: TRecordSet<U>,
|
|
402
|
+
getMergeSqlFn: Function
|
|
403
|
+
batchSize?: number
|
|
404
|
+
}): Promise<any[]> {...}
|
|
405
|
+
|
|
406
|
+
// Example:
|
|
407
|
+
const results = await mergeByBatch({
|
|
408
|
+
recordset: largeDataSet,
|
|
409
|
+
getMergeSqlFn: (batch) => getMergeSqlMAIN({
|
|
410
|
+
commonSchemaAndTable: 'public.users',
|
|
411
|
+
recordset: batch
|
|
412
|
+
}),
|
|
413
|
+
batchSize: 500
|
|
414
|
+
});
|
|
415
|
+
```
|