web-dc-api 0.1.5 → 0.1.6

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.
Files changed (148) hide show
  1. package/dist/cjs/index.js +1 -1
  2. package/dist/dc.min.js +1 -1
  3. package/dist/esm/index.js +1 -1
  4. package/dist/index.d.ts +934 -878
  5. package/package.json +4 -8
  6. package/dist/cjs/helia-core-B1Xqha7a.js +0 -1
  7. package/dist/cjs/helia-core-D8Uv1KjQ.js +0 -1
  8. package/dist/cjs/polkadot-api-7PhQf3ws.js +0 -1
  9. package/dist/cjs/polkadot-api-CtrJVWuZ.js +0 -1
  10. package/dist/esm/chunks/helia-core-BxMqyK2Y.js +0 -1
  11. package/dist/esm/chunks/helia-core-DMXRpcO-.js +0 -1
  12. package/dist/esm/chunks/polkadot-api-5Y9Bw8VT.js +0 -1
  13. package/dist/esm/chunks/polkadot-api-D69Ioun_.js +0 -1
  14. package/lib/common/blowfish/block.ts +0 -259
  15. package/lib/common/blowfish/cipher.ts +0 -144
  16. package/lib/common/blowfish/const.ts +0 -195
  17. package/lib/common/chain.ts +0 -469
  18. package/lib/common/commonclient.ts +0 -202
  19. package/lib/common/constants.ts +0 -55
  20. package/lib/common/dc-key/ed25519.ts +0 -343
  21. package/lib/common/dc-key/keyManager.ts +0 -424
  22. package/lib/common/dcapi.ts +0 -98
  23. package/lib/common/dcutil.ts +0 -627
  24. package/lib/common/define.ts +0 -70
  25. package/lib/common/error.ts +0 -67
  26. package/lib/common/grpc-dc.ts +0 -104
  27. package/lib/common/module-system.ts +0 -184
  28. package/lib/common/service-worker.ts +0 -234
  29. package/lib/common/types/types.ts +0 -344
  30. package/lib/dc.ts +0 -701
  31. package/lib/implements/account/client.ts +0 -185
  32. package/lib/implements/account/manager.ts +0 -683
  33. package/lib/implements/aiproxy/client.ts +0 -357
  34. package/lib/implements/aiproxy/manager.ts +0 -670
  35. package/lib/implements/cache/client.ts +0 -105
  36. package/lib/implements/cache/manager.ts +0 -127
  37. package/lib/implements/comment/client.ts +0 -982
  38. package/lib/implements/comment/manager.ts +0 -1151
  39. package/lib/implements/dc/client.ts +0 -51
  40. package/lib/implements/dc/manager.ts +0 -33
  41. package/lib/implements/file/client.ts +0 -253
  42. package/lib/implements/file/file-cache-manager.ts +0 -142
  43. package/lib/implements/file/manager.ts +0 -1240
  44. package/lib/implements/file/seekableFileStream.ts +0 -344
  45. package/lib/implements/file/streamwriter.ts +0 -322
  46. package/lib/implements/keyvalue/client.ts +0 -376
  47. package/lib/implements/keyvalue/manager.ts +0 -759
  48. package/lib/implements/message/client.ts +0 -250
  49. package/lib/implements/message/manager.ts +0 -215
  50. package/lib/implements/threaddb/cbor/coding.ts +0 -62
  51. package/lib/implements/threaddb/cbor/event.ts +0 -336
  52. package/lib/implements/threaddb/cbor/node.ts +0 -542
  53. package/lib/implements/threaddb/cbor/record.ts +0 -398
  54. package/lib/implements/threaddb/common/AsyncMutex.ts +0 -24
  55. package/lib/implements/threaddb/common/addrinfo.ts +0 -135
  56. package/lib/implements/threaddb/common/dispatcher.ts +0 -81
  57. package/lib/implements/threaddb/common/idbstore-adapter.ts +0 -260
  58. package/lib/implements/threaddb/common/json-patcher.ts +0 -204
  59. package/lib/implements/threaddb/common/key.ts +0 -290
  60. package/lib/implements/threaddb/common/level-adapter.ts +0 -235
  61. package/lib/implements/threaddb/common/lineReader.ts +0 -79
  62. package/lib/implements/threaddb/common/logstore.ts +0 -215
  63. package/lib/implements/threaddb/common/transformed-datastore.ts +0 -308
  64. package/lib/implements/threaddb/core/app.ts +0 -206
  65. package/lib/implements/threaddb/core/core.ts +0 -230
  66. package/lib/implements/threaddb/core/db.ts +0 -249
  67. package/lib/implements/threaddb/core/event.ts +0 -54
  68. package/lib/implements/threaddb/core/head.ts +0 -89
  69. package/lib/implements/threaddb/core/identity.ts +0 -171
  70. package/lib/implements/threaddb/core/logstore.ts +0 -137
  71. package/lib/implements/threaddb/core/options.ts +0 -14
  72. package/lib/implements/threaddb/core/record.ts +0 -54
  73. package/lib/implements/threaddb/db/collection.ts +0 -1910
  74. package/lib/implements/threaddb/db/db.ts +0 -698
  75. package/lib/implements/threaddb/db/json2Query.ts +0 -192
  76. package/lib/implements/threaddb/db/query.ts +0 -524
  77. package/lib/implements/threaddb/dbclient.ts +0 -543
  78. package/lib/implements/threaddb/dbmanager.ts +0 -1906
  79. package/lib/implements/threaddb/lsstoreds/addr_book.ts +0 -549
  80. package/lib/implements/threaddb/lsstoreds/cache.ts +0 -36
  81. package/lib/implements/threaddb/lsstoreds/cyclic_batch.ts +0 -87
  82. package/lib/implements/threaddb/lsstoreds/global.ts +0 -151
  83. package/lib/implements/threaddb/lsstoreds/headbook.ts +0 -373
  84. package/lib/implements/threaddb/lsstoreds/keybook.ts +0 -297
  85. package/lib/implements/threaddb/lsstoreds/logstore.ts +0 -29
  86. package/lib/implements/threaddb/lsstoreds/metadata.ts +0 -223
  87. package/lib/implements/threaddb/net/define.ts +0 -149
  88. package/lib/implements/threaddb/net/grpcClient.ts +0 -589
  89. package/lib/implements/threaddb/net/grpcserver.ts +0 -146
  90. package/lib/implements/threaddb/net/net.ts +0 -2047
  91. package/lib/implements/threaddb/pb/lstore.proto +0 -38
  92. package/lib/implements/threaddb/pb/lstore.ts +0 -393
  93. package/lib/implements/threaddb/pb/lstore_pb.d.ts +0 -433
  94. package/lib/implements/threaddb/pb/lstore_pb.js +0 -1085
  95. package/lib/implements/threaddb/pb/net.proto +0 -194
  96. package/lib/implements/threaddb/pb/net_pb.d.ts +0 -2349
  97. package/lib/implements/threaddb/pb/net_pb.js +0 -5525
  98. package/lib/implements/threaddb/pb/proto-custom-types.ts +0 -212
  99. package/lib/implements/util/client.ts +0 -72
  100. package/lib/implements/util/manager.ts +0 -146
  101. package/lib/implements/wallet/manager.ts +0 -671
  102. package/lib/index.ts +0 -57
  103. package/lib/interfaces/DCContext.ts +0 -51
  104. package/lib/interfaces/aiproxy-interface.ts +0 -145
  105. package/lib/interfaces/auth-interface.ts +0 -118
  106. package/lib/interfaces/cache-interface.ts +0 -22
  107. package/lib/interfaces/client-interface.ts +0 -11
  108. package/lib/interfaces/comment-interface.ts +0 -167
  109. package/lib/interfaces/components/news-component.ts +0 -0
  110. package/lib/interfaces/database-interface.ts +0 -169
  111. package/lib/interfaces/file-interface.ts +0 -120
  112. package/lib/interfaces/index.ts +0 -10
  113. package/lib/interfaces/keyvalue-interface.ts +0 -156
  114. package/lib/interfaces/message-interface.ts +0 -22
  115. package/lib/interfaces/util-interface.ts +0 -31
  116. package/lib/modules/aiproxy-module.ts +0 -246
  117. package/lib/modules/auth-module.ts +0 -753
  118. package/lib/modules/cache-module.ts +0 -99
  119. package/lib/modules/client-module.ts +0 -71
  120. package/lib/modules/comment-module.ts +0 -429
  121. package/lib/modules/components/news-components.ts +0 -390
  122. package/lib/modules/database-module.ts +0 -598
  123. package/lib/modules/file-module.ts +0 -291
  124. package/lib/modules/index.ts +0 -13
  125. package/lib/modules/keyvalue-module.ts +0 -379
  126. package/lib/modules/message-module.ts +0 -107
  127. package/lib/modules/util-module.ts +0 -148
  128. package/lib/polyfills/process-env-browser.ts +0 -1
  129. package/lib/proto/datasource.ts +0 -93
  130. package/lib/proto/dcnet.proto +0 -1601
  131. package/lib/proto/dcnet_proto.d.ts +0 -22857
  132. package/lib/proto/dcnet_proto.js +0 -55204
  133. package/lib/proto/dcnet_proto_sparse.js +0 -55166
  134. package/lib/proto/oidfetch.proto +0 -25
  135. package/lib/proto/oidfetch_proto.d.ts +0 -585
  136. package/lib/proto/oidfetch_proto.js +0 -1247
  137. package/lib/serverless/babel-browser.ts +0 -39
  138. package/lib/serverless/base_entity.ts +0 -78
  139. package/lib/serverless/base_repository.ts +0 -414
  140. package/lib/serverless/browser_schema_extractor.ts +0 -283
  141. package/lib/serverless/decorator_factory.ts +0 -322
  142. package/lib/util/BrowserLineReader.ts +0 -73
  143. package/lib/util/base64.ts +0 -105
  144. package/lib/util/bcrypt.ts +0 -206
  145. package/lib/util/curve25519Encryption.ts +0 -418
  146. package/lib/util/dccrypt.ts +0 -73
  147. package/lib/util/logger.ts +0 -104
  148. package/lib/util/utils.ts +0 -289
@@ -1,1906 +0,0 @@
1
- import { PeerId } from '@libp2p/interface';
2
- import { multiaddr, Multiaddr as TMultiaddr } from '@multiformats/multiaddr';
3
- import { ThreadID } from '@textile/threads-id';
4
- import { peerIdFromPrivateKey, peerIdFromString } from "@libp2p/peer-id";
5
- import { Key } from 'interface-datastore';
6
- import { DB as ThreadDb } from './db/db';
7
- import { Errors, Transaction } from './core/db';
8
- import { Net } from './core/app';
9
- import { ChainUtil } from "../../common/chain";
10
- import { Ed25519PrivKey } from "../../common/dc-key/ed25519";
11
- import type { Connection } from '@libp2p/interface'
12
- import { keys } from "@libp2p/crypto";
13
- import { SymmetricKey, Key as ThreadKey } from './common/key';
14
- import { extractPeerIdFromMultiaddr } from "../../common/dc-key/keyManager";
15
- import {IDBInfo, ThreadMuliaddr} from './core/core'
16
-
17
- import {StoreunitInfo} from '../../common/chain';
18
- import { createTransformedDatastore, PrefixTransform,TransformedDatastore} from './common/transformed-datastore'
19
- import {NewOptions,ICollectionConfig,ManagedOptions,ThreadInfo,Context} from './core/core';
20
- import {TxnDatastoreExtended,pullThreadBackgroundTimeout,PullTimeout} from './core/db';
21
- import type { DCConnectInfo } from "../../common/types/types";
22
- import { uint32ToLittleEndianBytes,uint64ToLittleEndianBytes } from "../../util/utils";
23
- import {DcUtil} from '../../common/dcutil';
24
- import {Type} from '../../common/constants';
25
- import { NewThreadOptions } from './core/options';
26
- import {ThreadToken} from './core/identity';
27
- import { DBGrpcClient } from "./net/grpcClient";
28
- import type { Client } from "../../common/dcapi";
29
- import { jsonStringify } from '../../util/utils';
30
- import {Protocol} from './net/define';
31
- import {parseJsonToQuery} from './db/json2Query';
32
- import * as buffer from "buffer/";
33
- import { dial_timeout } from '../../common/define';
34
- import multibase, {decode as multibaseDecode} from 'multibase';
35
- import {net as net_pb} from "./pb/net_pb";
36
- import { LineReader } from './common/lineReader';
37
- import { FileManager } from '../file/manager';
38
- import {newIterator} from './db/collection';
39
- import { Query } from './db/query';
40
- import { DCContext } from '../../interfaces';
41
- const { Buffer } = buffer;
42
-
43
- export const ThreadProtocol = "/dc/" + Protocol.name + "/" + Protocol.version
44
-
45
-
46
- // 常量
47
- export const MaxLoadConcurrency = 100;
48
- export const dsManagerBaseKey = new Key('/manager');
49
-
50
-
51
-
52
-
53
- function newGrpcClient(client: Client,net:Net): DBGrpcClient {
54
- if (client.p2pNode == null || client.p2pNode.peerId == null) {
55
- throw new Error("p2pNode is null or node privateKey is null");
56
- }
57
- const grpcClient = new DBGrpcClient(
58
- client.p2pNode,
59
- client.peerAddr,
60
- client.token,
61
- net,
62
- client.protocol
63
- );
64
- return grpcClient
65
- }
66
-
67
-
68
-
69
- // 管理器类
70
- export class DBManager {
71
- private store: TxnDatastoreExtended;
72
- private network: Net;
73
- private dc: DcUtil;
74
- private connectedDc:DCConnectInfo;
75
- private opts: NewOptions;
76
- private dbs: Map<string, ThreadDb>;
77
- private lock: AsyncLock;
78
- public chainUtil: ChainUtil;
79
- private storagePrefix: string;
80
- private context: DCContext;
81
-
82
- constructor(
83
- store: TxnDatastoreExtended, //实际上是一个LevelDatastore实例,用levelDatastoreAdapter包装
84
- network: Net,
85
- dc: DcUtil,
86
- connectedDc:DCConnectInfo,
87
- opts: NewOptions = {} ,
88
- chainUtil: ChainUtil,
89
- storagePrefix: string,
90
- context: DCContext
91
- ) {
92
-
93
- this.store = store;
94
- this.network = network;
95
- this.dc = dc;
96
- this.storagePrefix = storagePrefix;
97
- this.opts = opts;
98
- this.dbs = new Map();
99
- this.lock = new AsyncLock();
100
- this.chainUtil = chainUtil;
101
- this.connectedDc = connectedDc;
102
- this.context = context
103
- }
104
- async loadDbs():Promise<void>{
105
- // Query for existing databases
106
- console.log("manager: loading dbs");
107
- try {
108
- const q = { prefix: dsManagerBaseKey.toString(), keysOnly: true };
109
- const results = this.store.query(q);
110
- // Create a map to track loaded databases and prevent duplicates
111
- const loaded = new Map<string, boolean>();
112
- // Process each result
113
- for await (const result of results) {
114
- try {
115
- // Parse the key to extract thread ID
116
- const parts = result.key.toString().split('/');
117
- if (parts.length < 3) {
118
- continue;
119
- }
120
- const id = ThreadID.fromString(parts[2]||"");
121
- // Check if already loaded
122
- if (loaded.has(id.toString())) {
123
- continue;
124
- }
125
- // Mark as loaded
126
- loaded.set(id.toString(), true)
127
- // Wrap and create database
128
- const [store, opts, err] = await this.wrapDB(
129
- this.store,
130
- id,
131
- this.opts,
132
- "",
133
- []
134
- );
135
-
136
- if (err) {
137
- continue;
138
- }
139
- const db = await ThreadDb.newDB(store, this.network, id, opts);
140
- // Add to map of databases
141
- this.dbs.set(id.toString(), db);
142
- } catch (err) {
143
- console.error(`Error loading database: ${err instanceof Error ? err.message : String(err)}`);
144
- }
145
- }
146
- }catch (err) {
147
- console.error(`Failed to load databases: ${err instanceof Error ? err.message : String(err)}`);
148
- throw err;
149
- }
150
- }
151
-
152
-
153
- /**
154
- * Gets the log key for a thread. If it doesn't exist, creates a new one.
155
- * @param tid Thread ID
156
- * @returns Promise resolving to the private key
157
- * @throws Error if key operations fail
158
- */
159
- async getLogKey(tid: ThreadID): Promise<Ed25519PrivKey> {
160
- const storageKey = `${this.storagePrefix}_${tid.toString()}_logkey`;
161
-
162
- try {
163
- // Try to get existing key from localStorage
164
- const storedBytes = await this.store.get(new Key(storageKey));
165
- if (storedBytes) {
166
- const key = Ed25519PrivKey.fromString(Buffer.from(storedBytes).toString('hex'));
167
- return key;
168
- } else {
169
- // Create new key if none exists
170
- const key = await this.newLogKey();
171
- this.store.put(new Key(storageKey), Buffer.from(key.raw));
172
- return key;
173
- }
174
- } catch (err : any) {
175
- if (err.code === 'ERR_NOT_FOUND') {
176
- // Create new key if none exists
177
- const key = await this.newLogKey();
178
- this.store.put(new Key(storageKey), Buffer.from(key.raw));
179
- return key;
180
- }
181
- throw new Error(`Failed to get/create log key: ${err}`);
182
- }
183
- }
184
-
185
-
186
-
187
- /**
188
- * Creates a new log key
189
- * @private
190
- */
191
- private async newLogKey(): Promise<Ed25519PrivKey> {
192
- try {
193
- //生成临时ed25519公私钥对
194
- const keyPair = await keys.generateKeyPair("Ed25519");
195
- // 获取私钥
196
- const privateKey = new Ed25519PrivKey(keyPair.raw)
197
- return privateKey;
198
- } catch (err) {
199
- throw new Error(`Failed to generate log key: ${err}`);
200
- }
201
- }
202
-
203
- // 新增静态方法
204
- /**
205
- * FromAddr returns ID from a multiaddress if present.
206
- * @param addr Multiaddr instance
207
- * @returns ID instance
208
- */
209
- static fromAddr(addr: TMultiaddr): ThreadID {
210
- try {
211
- // 获取协议值
212
- const parts = addr.toString().split('/')
213
- const index = parts.indexOf(Protocol.name)
214
- if (index === -1 || index === parts.length - 1) {
215
- throw new Error('thread protocol not found in multiaddr')
216
- }
217
-
218
- const idstr = parts[index + 1] || ""
219
- return ThreadID.fromString(idstr)
220
- } catch (err:any) {
221
-
222
- throw new Error(`Failed to extract ID from multiaddr: ${err.message}`)
223
- }
224
- }
225
-
226
- /**
227
- * ToAddr returns ID wrapped as a multiaddress.
228
- * @returns Multiaddr instance
229
- */
230
- toAddr(): TMultiaddr {
231
- try {
232
- const addr = multiaddr(`/${Protocol.name}/${this.toString()}`)
233
- return addr
234
- } catch (err:any) {
235
- // This should not happen with valid IDs
236
- throw new Error(`Failed to create multiaddr: ${err.message}`)
237
- }
238
- }
239
-
240
- async newDBFromAddr(
241
- addr: ThreadMuliaddr,
242
- key: ThreadKey,
243
- opts: NewOptions = {}
244
- ): Promise<ThreadDb> {
245
- const id = addr.id;
246
-
247
- // return await this.lock.acquire('dbs', async () => {
248
-
249
- if (this.dbs.has(id.toString())) {
250
-
251
- throw Errors.ErrDBExists;
252
- }
253
-
254
- if (opts.name && !isValidName(opts.name)) {
255
- throw Errors.ErrInvalidName;
256
- }
257
-
258
- if (key.defined() && !key.canRead()) {
259
- throw Errors.ErrThreadReadKeyRequired;
260
- }
261
-
262
- await this.network.addThread(addr, {
263
- token: opts.token ,
264
- logKey: opts.logKey,
265
- threadKey:key,
266
- });
267
-
268
- const collections = opts.collections || [];
269
- const name = opts.name || '';
270
-
271
- const [store, dbOpts, err] = await this.wrapDB(this.store,id, this.opts,name, collections);
272
- if (err) {
273
- throw new Error(`wrapping db: ${err.message}`);
274
- }
275
- const db = await ThreadDb.newDB(store, this.network, id, dbOpts);
276
-
277
- this.dbs.set(id.toString(), db);
278
-
279
- if (opts.block) {
280
- await this.network.pullThread(id,pullThreadBackgroundTimeout, { token: opts.token });
281
- } else {
282
- // Background pull
283
- this.pullThreadBackground(id, opts.token);
284
- }
285
- return db;
286
- // });
287
- }
288
-
289
- private async pullThreadBackground(id: ThreadID, token?: ThreadToken) {
290
- try {
291
- await this.network.pullThread( id,pullThreadBackgroundTimeout, { token: token });
292
- } catch (err) {
293
- console.error(`Error pulling thread ${id}:`, err);
294
- }
295
- }
296
-
297
-
298
-
299
- /**
300
- * Preloads a database from DC network to local
301
- * Generally happens when a new device first logs in to sync previously created databases
302
- *
303
- * @param threadid Thread ID string
304
- * @param fid Content ID string of the file to preload
305
- * @param dbname Database name
306
- * @param dbAddr Database address string
307
- * @param b32Rk Base32-encoded read key
308
- * @param b32Sk Base32-encoded secret key
309
- * @param block Whether to block until syncing is complete
310
- * @param jsonCollections JSON string of collection configurations
311
- * @returns Promise that resolves when preloading is complete
312
- */
313
- async preloadDBFromDC(
314
- threadid: string,
315
- fid: string,
316
- dbname: string,
317
- dbAddr: string,
318
- b32Rk: string,
319
- b32Sk: string,
320
- block: boolean,
321
- jsonCollections: string
322
- ): Promise<void> {
323
- console.debug(`manager: preloading DB from DC ${threadid}`);
324
-
325
- // Check if DBManager exists
326
- if (!this) {
327
- throw Errors.ErrNoDbManager;
328
- }
329
-
330
- // Decode thread ID
331
- let tID: ThreadID;
332
- try {
333
- tID = await this.decodeThreadId(threadid);
334
- } catch (err) {
335
- throw err;
336
- }
337
-
338
- // Get log key
339
- let logKey: Ed25519PrivKey;
340
- try {
341
- logKey = await this.getLogKey(tID);
342
- } catch (err) {
343
- throw err;
344
- }
345
-
346
- // Create peer ID from log key
347
- let lid: PeerId;
348
- try {
349
- lid = peerIdFromPrivateKey(logKey);
350
- } catch (err) {
351
- throw err;
352
- }
353
-
354
- // Begin connection in background
355
- await this.dc._connectToObjNodes(tID.toString()).catch(err =>
356
- console.error(`Error connecting to object nodes: ${err.message}`)
357
- );
358
- const ctx = createContext(30000);
359
- try {
360
- await this.addLogToThreadStart(ctx, tID, lid);
361
- } catch (err:any) {
362
- console.warn(`Warning: could not add log to thread: ${err.message}`);
363
- }
364
-
365
- // Generate thread key
366
- let threadKey: ThreadKey;
367
- try {
368
- const sk = SymmetricKey.fromString(b32Sk);
369
- const rk = SymmetricKey.fromString(b32Rk);
370
- threadKey = new ThreadKey(sk, rk);
371
- } catch (err) {
372
- throw err;
373
- }
374
-
375
- let connectedPeerId: PeerId | undefined;
376
- let multiAddr: TMultiaddr | undefined;
377
-
378
- // Try to connect using provided dbAddr
379
- let connectedFlag = false;
380
- if (dbAddr.length > 10) {
381
- try {
382
- // Try to parse address info and connect
383
- const peerAddrInfo = multiaddr(dbAddr);
384
-
385
- try {
386
- const conn = await this.dc.dcNodeClient?.libp2p.dial(peerAddrInfo, {
387
- signal: AbortSignal.timeout(3000)
388
- });
389
-
390
- if (conn) {
391
- connectedFlag = true;
392
- multiAddr = multiaddr(dbAddr);
393
- }
394
- } catch (err) {
395
- // Connection failed
396
- }
397
- } catch (err) {
398
- // Invalid address
399
- }
400
- }
401
-
402
- // If direct connection failed, connect through object nodes
403
- if (!connectedFlag) {
404
- try {
405
- const [connectedAddr, peers] = await this.dc?._connectToObjNodes(tID.toString());
406
- if (!connectedAddr && peers) {
407
- // 有peers但是没有connectedAddr
408
- throw Errors.ErrNoThreadOnDc;
409
- }
410
-
411
- const conns = this.dc.dcNodeClient?.libp2p.getConnections(connectedPeerId);
412
- if (!conns || conns.length == 0) {
413
- throw Errors.ErrNoThreadOnDc;
414
- }
415
- } catch (err) {
416
- throw err;
417
- }
418
- } else {
419
- // Start connection in background
420
- this.dc._connectToObjNodes(tID.toString()).catch(err =>
421
- console.error(`Error connecting to object nodes: ${err.message}`)
422
- );
423
- }
424
-
425
- // Parse collection info
426
- let collectionInfos: ICollectionConfig[] = [];
427
- try {
428
- collectionInfos = JSON.parse(jsonCollections);
429
- } catch (err) {
430
- throw err;
431
- }
432
-
433
- // Create collection configurations
434
- const collections = collectionInfos.map(info => ({
435
- name: info.name,
436
- schema: info.schema,
437
- indexes: info.indexes || []
438
- }));
439
-
440
- // Create options for new database
441
- const dbOpts: NewOptions = {
442
- name: dbname,
443
- collections: collections,
444
- key: threadKey,
445
- logKey: logKey,
446
- block: block
447
- };
448
-
449
- // Delete existing database if it exists
450
- try {
451
- await this.deleteDB(tID, true);
452
- } catch (err) {
453
- // Ignore specific errors
454
- if (err !== Errors.ErrDBNotFound && err !== Errors.ErrThreadNotFound) {
455
- throw err;
456
- }
457
- }
458
- if (this.dc.dcNodeClient == null) {
459
- throw Errors.ErrNoDcNodeClient;
460
- }
461
- // Create context with extended timeout for file download
462
- const tctx = createContext(PullTimeout * 30);
463
- const fileManager = new FileManager(
464
- this.dc,
465
- this.connectedDc,
466
- this.chainUtil,
467
- this.dc.dcNodeClient,
468
- this.context
469
- );
470
- const fileStream = await fileManager.createSeekableFileStream(fid, "");
471
-
472
- if (fileStream == null) {
473
- throw Errors.ErrFileNotFound;
474
- }
475
-
476
- // Preload database from reader
477
- const threadMultiaddr = new ThreadMuliaddr(multiAddr!, tID);
478
- await this.preloadDBFromReader(tctx, fileStream.createReadableStream(), threadMultiaddr, threadKey, dbOpts);
479
- }
480
-
481
-
482
-
483
- /**
484
- * 从读取器预加载数据库
485
- * @param ctx 上下文
486
- * @param ioReader 数据流读取器
487
- * @param addr 线程地址
488
- * @param key 线程密钥
489
- * @param opts 管理选项
490
- */
491
- async preloadDBFromReader(
492
- ctx: Context,
493
- ioReader: ReadableStream<Uint8Array>,
494
- addr: ThreadMuliaddr,
495
- key: ThreadKey,
496
- opts: NewOptions = {}
497
- ): Promise<void> {
498
- console.debug("manager: preloading db from reader");
499
- let id: ThreadID;
500
- try {
501
- id = DBManager.fromAddr(addr.addr);
502
- } catch (err) {
503
- throw err;
504
- }
505
- // 检查数据库是否已存在
506
- // await this.lock.acquire('dbs', async () => {
507
- if (this.dbs.has(id.toString())) {
508
- throw Errors.ErrDBExists;
509
- }
510
- //});
511
- if (opts.name && !isValidName(opts.name)) {
512
- throw Errors.ErrInvalidName;
513
- }
514
- // 验证密钥
515
- if (key.defined() && !key.canRead()) {
516
- throw Errors.ErrThreadReadKeyRequired;
517
- }
518
- // 添加线程到网络
519
- console.debug(`manager: adding thread to net ${id}`);
520
- try {
521
- await this.network.addThread(addr, {
522
- threadKey: key,
523
- logKey: opts.logKey,
524
- token: opts.token
525
- });
526
- } catch (err) {
527
- throw err;
528
- }
529
- console.debug(`manager: added thread to net ${id}`);
530
-
531
- // 包装数据库
532
- let store: TxnDatastoreExtended;
533
- let dbOpts: NewOptions;
534
- try {
535
- const collections = opts.collections || [];
536
- const name = opts.name || '';
537
- [store, dbOpts] = await this.wrapDB(this.store, id, this.opts, name, collections);
538
- } catch (err) {
539
- throw err;
540
- }
541
-
542
- // 创建新数据库
543
- let db: ThreadDb;
544
- try {
545
- db = await ThreadDb.newDB(store, this.network, id, dbOpts);
546
- } catch (err) {
547
- throw err;
548
- }
549
-
550
- // 添加数据库到管理器
551
- // await this.lock.acquire('dbs', async () => {
552
- this.dbs.set(id.toString(), db);
553
- // });
554
-
555
- // 导入数据库状态
556
- const readKey = key.read();
557
- if (!readKey) {
558
- throw new Error(`read key not found for thread ${id}`);
559
- }
560
-
561
- // 创建行读取器
562
- const lineReader = new LineReader(ioReader);
563
- const textDecoder = new TextDecoder();
564
-
565
- // 读取第一行并更新线程信息的日志头
566
- let stateValue = "";
567
- try {
568
- const value = await lineReader.readLine();
569
- if (value) {
570
- stateValue = value;
571
- }
572
-
573
- } catch (err) {
574
- throw err;
575
- }
576
- if (stateValue == "") {
577
- throw new Error(`empty state value for thread ${id}`);
578
- }
579
- // 移除头部32位hash
580
- stateValue = stateValue.slice(32);
581
-
582
- // 更新线程信息的日志头
583
- const logs = stateValue.split(';');
584
- const pbLogs: net_pb.pb.Log[] = [];
585
-
586
- for (const log of logs) {
587
- try {
588
- // 解码 multibase 格式
589
- const data = multibaseDecode(log);
590
- // 解析 protobuf
591
- const pbLog = net_pb.pb.Log.decode(data);
592
- pbLogs.push(pbLog);
593
- } catch (err) {
594
- // 忽略错误,继续处理
595
- continue;
596
- }
597
- }
598
-
599
- // 预加载日志
600
- try {
601
- await this.network.preLoadLogs(id, pbLogs);
602
- } catch (err) {
603
- await this.deleteDB(id, false);
604
- throw err;
605
- }
606
-
607
- // 导入数据库状态
608
- try {
609
- await this.importDBStateFromReader(id, lineReader, readKey);
610
- } catch (err) {
611
- await this.deleteDB(id, false);
612
- throw err;
613
- }
614
- }
615
-
616
- /**
617
- * Browser-compatible version to export DB to file
618
- * @param ctx Context
619
- * @param id ThreadID
620
- * @param fileName Suggested file name for download
621
- * @param readKey Optional encryption key
622
- * @returns Promise resolving to ThreadInfo
623
- */
624
- async exportDBToFile(
625
- ctx: Context,
626
- id: ThreadID,
627
- fileName: string,
628
- readKey?: SymmetricKey
629
- ): Promise<ThreadInfo> {
630
- console.debug(`manager: exporting db ${id.toString()} to file download`);
631
-
632
- // Get thread logs similar to original function
633
- let logState = "";
634
- let logs: net_pb.pb.ILog[];
635
- let threadInfo: ThreadInfo;
636
-
637
- [logs, threadInfo] = await this.network.getPbLogs(id);
638
-
639
- // Build log state string
640
- for (let i = 0; logs &&i < logs.length; i++) {
641
- if (!logs[i]) {
642
- continue; // Skip undefined logs
643
- }
644
- const log = logs[i] as net_pb.pb.ILog;
645
- const logBytes = net_pb.pb.Log.encode(log).finish();
646
- const mbaseLog = multibase.encode('base64', logBytes);
647
-
648
- if (i === 0) {
649
- logState = mbaseLog.toString();
650
- } else {
651
- logState = `${logState};${mbaseLog.toString()}`;
652
- }
653
- }
654
-
655
- // Create content in memory instead of writing to file
656
- let content = logState + "\n";
657
-
658
- // Get database
659
- const db = this.dbs.get(id.toString());
660
- if (!db) {
661
- throw Errors.ErrDBNotFound;
662
- }
663
-
664
- // Create transaction
665
- const txn = await db.datastore.newTransactionExtended(true);
666
-
667
- try {
668
- // Similar query logic, but accumulating in memory
669
- const q = new Query();
670
- const baseKey = new Key('/');
671
- const i = await newIterator(txn, baseKey, q);
672
-
673
- for await (const res of i.iter.next()) {
674
- if (res.error) {
675
- throw res.error;
676
- }
677
-
678
- let line: string;
679
- if (readKey) {
680
- const encBytes = await readKey.encrypt(res.entry.value);
681
- const mValue = multibase.encode('base64', encBytes);
682
- line = `${res.entry.key}|${mValue.toString()}`;
683
- } else {
684
- const mValue = multibase.encode('base64', res.entry.value);
685
- line = `${res.entry.key}|${mValue.toString()}`;
686
- }
687
-
688
- content += line + "\n";
689
- }
690
-
691
- i.close();
692
- txn.discard();
693
-
694
-
695
- // Create blob and trigger download
696
- const blob = new Blob([content], { type: 'text/plain' });
697
- const url = URL.createObjectURL(blob);
698
-
699
- // Create download link
700
- const a = document.createElement('a');
701
- a.href = url;
702
- a.download = fileName || `db-export-${id.toString().substring(0, 8)}.txt`;
703
-
704
- // Append to body, click and remove
705
- document.body.appendChild(a);
706
- a.click();
707
-
708
- // Cleanup
709
- setTimeout(() => {
710
- document.body.removeChild(a);
711
- URL.revokeObjectURL(url);
712
- }, 100);
713
-
714
- return threadInfo;
715
- } catch (err) {
716
- txn.discard();
717
- throw err;
718
- }
719
- }
720
-
721
-
722
-
723
-
724
-
725
- /**
726
- * 从读取器导入数据库状态
727
- * @param id 线程ID
728
- * @param reader 可读流读取器
729
- * @param readKey 用于解密的对称密钥
730
- */
731
- async importDBStateFromReader(
732
- id: ThreadID,
733
- lineReader: LineReader,
734
- readKey: SymmetricKey
735
- ): Promise<void> {
736
- console.debug("manager: importing db state from reader");
737
-
738
- // 检查数据库是否存在
739
- let db: ThreadDb | undefined;
740
- // await this.lock.acquire('dbs', async () => {
741
- db = this.dbs.get(id.toString());
742
- // });
743
-
744
- if (!db) {
745
- throw Errors.ErrDBNotFound;
746
- }
747
-
748
- // 获取索引函数
749
- const indexFunc = db.defaultIndexFunc();
750
-
751
- // 设置行读取
752
- const textDecoder = new TextDecoder();
753
- let done = false;
754
- let line:string|null = ""
755
- while (true) {
756
- line = await lineReader.readLine();
757
- if (!line) {
758
- break
759
- }
760
- // 创建事务
761
- let txn:Transaction;
762
- try {
763
- txn = await db.datastore.newTransactionExtended(false);
764
- } catch (err) {
765
- throw new Error(`创建事务错误: ${err instanceof Error ? err.message : String(err)}`);
766
- }
767
-
768
- try {
769
- // 解析键值对
770
- const kv = line.split('|');
771
- if (kv.length !== 2) {
772
- txn.discard();
773
- throw new Error('无效的记录格式');
774
- }
775
-
776
- const key = kv[0];
777
- const mValue = kv[1] || '';
778
-
779
- // 使用multibase解码值
780
- let encValue: Uint8Array;
781
- try {
782
- const decoded = multibaseDecode(mValue);
783
- encValue = decoded;
784
- } catch (err) {
785
- await txn.discard();
786
- throw new Error(`multibase解码失败: ${err instanceof Error ? err.message : String(err)}`);
787
- }
788
-
789
- // 如有需要解密记录
790
- let decValue = encValue;
791
- if (readKey) {
792
- try {
793
- decValue = await readKey.decrypt(encValue);
794
- } catch (err) {
795
- await txn.discard();
796
- throw new Error(`解密值失败: ${err instanceof Error ? err.message : String(err)}`);
797
- }
798
- }
799
-
800
- // 创建数据存储键
801
- const setKey = new Key(key||"");
802
-
803
- // 检查键是否已存在
804
- try {
805
- const exists = await txn.has(setKey);
806
- if (exists) {
807
- txn.discard();
808
- continue; // 跳过此记录
809
- }
810
- } catch (err) {
811
- txn.discard();
812
- throw new Error(`检查键存在性失败: ${err instanceof Error ? err.message : String(err)}`);
813
- }
814
-
815
- // 存储值
816
- try {
817
- await txn.put(setKey, decValue);
818
- } catch (err) {
819
- txn.discard();
820
- throw new Error(`存储值失败: ${err instanceof Error ? err.message : String(err)}`);
821
- }
822
-
823
- // 从键中提取集合名称(倒数第二个部分)
824
- const parts = key?.split('/');
825
- if (!parts ||parts.length < 2) {
826
- txn.discard();
827
- throw new Error('无效的键格式: 未找到集合名称');
828
- }
829
-
830
- const collection = parts[parts.length - 2]||"";
831
-
832
- // 应用索引
833
- try {
834
- await indexFunc(collection, setKey, txn, decValue);
835
- } catch (err) {
836
- txn.discard();
837
- throw new Error(`应用索引失败: ${err instanceof Error ? err.message : String(err)}`);
838
- }
839
-
840
- // 提交事务
841
- try {
842
- await txn.commit();
843
- } catch (err) {
844
- txn.discard();
845
- throw new Error(`提交事务失败: ${err instanceof Error ? err.message : String(err)}`);
846
- }
847
- } catch (err) {
848
- // 确保在任何失败时丢弃事务
849
- try {
850
- txn.discard();
851
- } catch {
852
- // 忽略丢弃时的错误
853
- }
854
- throw err;
855
- }
856
- }
857
- }
858
-
859
-
860
-
861
- /**
862
- * wrapDB 复制管理器的基本配置,
863
- * 使用 ID 前缀包装数据存储,
864
- * 并将指定的集合配置与基本配置合并
865
- */
866
- async wrapDB(
867
- store: TxnDatastoreExtended,
868
- id: ThreadID,
869
- base: NewOptions,
870
- name: string,
871
- collections: ICollectionConfig[]
872
- ): Promise<[TxnDatastoreExtended, NewOptions, Error | null]> {
873
- const isValid = await this.validateThreadId(id.toString());
874
- if (!isValid) {
875
- return [null as unknown as TxnDatastoreExtended, null as unknown as NewOptions, new Error('Invalid Thread ID')];
876
- }
877
- // 创建前缀转换器并包装数据存储
878
- const prefix = dsManagerBaseKey.child(new Key(id.toString())).toString();
879
- const transform = new PrefixTransform(prefix);
880
- //const wrappedStore = new TransformedDatastore(store, transform);
881
- const wrappedStore = createTransformedDatastore(store, transform);
882
- // 创建新的选项对象
883
- const opts: NewOptions = {
884
- name: name,
885
- collections: [...(base.collections || []), ...collections],
886
- eventCodec: base.eventCodec,
887
- debug: base.debug
888
- };
889
-
890
- return [wrappedStore, opts, null];
891
- }
892
-
893
- async listDBs(): Promise<Map<ThreadID, ThreadDb>> {
894
- const dbs = new Map();
895
- // await this.lock.acquire('dbs', async () => {
896
- for (const [idStr, db] of this.dbs) {
897
- const id = ThreadID.fromString(idStr);
898
- await this.network.getThread(id);
899
- dbs.set(id, db);
900
- }
901
- // });
902
- return dbs;
903
- }
904
-
905
-
906
- async ifSyncDBToDCSuccess(tId: string): Promise<boolean> {
907
- try {
908
- const [storeUnit,err] = await this.chainUtil.objectState(tId);
909
- if (!storeUnit || err) return false;
910
-
911
- return new Promise((resolve) => {
912
- const timeout = setTimeout(() => resolve(false), PullTimeout);
913
-
914
- const checkPeers = async () => {
915
- for (const pid of Object.keys(storeUnit.peers)) {
916
- try {
917
- const peerId = peerIdFromString(pid);
918
- const threadId = ThreadID.fromString(tId);
919
- const remoteInfo = await this.network.getThreadFromPeer(threadId, peerId,{});
920
- const localInfo = await this.network.getThread( threadId);
921
- if (this.compareThreadSync(localInfo, remoteInfo, storeUnit)) {
922
- clearTimeout(timeout);
923
- resolve(true);
924
- return;
925
- }
926
- } catch {
927
- continue;
928
- }
929
- }
930
- resolve(false);
931
- };
932
-
933
- checkPeers();
934
- });
935
- } catch {
936
- return false;
937
- }
938
- }
939
-
940
- private compareThreadSync(local: ThreadInfo, remote: ThreadInfo, storeUnit: StoreunitInfo): boolean {
941
- for (const logInfo of local.logs) {
942
- if (!storeUnit.logs.has(logInfo.id.toString())) {
943
- continue;
944
- }
945
- if (!logInfo.head){
946
- continue;
947
- }
948
- const remoteLog = remote.logs.find(l => l.id === logInfo.id);
949
- if (!remoteLog?.head) {
950
- return false;
951
- }
952
- if (!remoteLog || logInfo.head.counter > remoteLog.head.counter) {
953
- return false;
954
- }
955
- }
956
- return true;
957
- }
958
-
959
- async ifDbInitSuccess(tid: ThreadID): Promise<boolean> {
960
- try {
961
- const logKey = await this.getLogKey(tid);
962
- const lid = peerIdFromPrivateKey(logKey);
963
- const [threadInfo,err] = await this.chainUtil.objectState(tid.toString());
964
- if (!threadInfo || err) {
965
- return false;
966
- }
967
- const exist = threadInfo?threadInfo?.logs.has(lid.toString()):false;
968
- return exist;
969
- } catch {
970
- return false;
971
- }
972
- }
973
-
974
- async syncDBFromDC(
975
- ctx: Context,
976
- threadid: string,
977
- dbname: string,
978
- dbAddr: string,
979
- b32Rk: string,
980
- b32Sk: string,
981
- block: boolean,
982
- collectionInfos: ICollectionConfig[]
983
- ): Promise<Error | null> {
984
- try {
985
- const tID = await this.decodeThreadId(threadid);
986
- const logKey = await this.getLogKey(tID);
987
- const lid = peerIdFromPrivateKey(logKey);
988
- await this.dc._connectToObjNodes(threadid);
989
- // await this.addLogToThreadStart(ctx,tID, lid); //移动到首次上报数据这边,避免空log上链
990
- const sk = SymmetricKey.fromString(b32Sk);
991
- const rk = SymmetricKey.fromString(b32Rk);
992
- const threadKey = new ThreadKey(sk, rk);
993
- let connectedFlag = false ;
994
- let connectedConn :Connection | undefined;
995
- let fullMultiAddr :TMultiaddr | undefined;
996
- let threadAddr :TMultiaddr;
997
- let connectedPeerId :PeerId;
998
- let dbMultiAddr :TMultiaddr;
999
- if (dbAddr.length > 0) {
1000
- try {
1001
- //
1002
- connectedConn = await this.dc.dcNodeClient?.libp2p.dial(multiaddr(dbAddr), {
1003
- signal: AbortSignal.timeout(dial_timeout)
1004
- });
1005
- } catch (error) {
1006
- const errMsg = (error as any).message;
1007
- console.log("connect to %s catch return, error:%s",dbAddr, errMsg);
1008
- }
1009
- }
1010
-
1011
- if (connectedConn) {//连接成功
1012
- connectedPeerId = connectedConn?.remotePeer;
1013
- dbMultiAddr = connectedConn.remoteAddr;
1014
-
1015
- }else{//从区块链中获取节点信息,再连接
1016
- const [connectedAddr, peers] = await this.dc._connectToObjNodes(threadid);
1017
- if (!connectedAddr) {
1018
- throw new Error("connect to obj nodes failed");
1019
- }
1020
- dbMultiAddr = connectedAddr;
1021
-
1022
-
1023
- }
1024
-
1025
-
1026
-
1027
- const collections = collectionInfos.map(info => ({
1028
- name: info.name,
1029
- schema: info.schema,
1030
- indexes: info.indexes || []
1031
- }));
1032
-
1033
- const dbOpts: NewOptions = {
1034
- name: dbname,
1035
- collections: collections,
1036
- key: threadKey,
1037
- logKey: logKey,
1038
- block: block,
1039
- };
1040
-
1041
-
1042
-
1043
- // Delete existing database if present
1044
- try {
1045
- await this.deleteDB(tID, false);
1046
- } catch (error) {
1047
- const errMsg = (error as any).message;
1048
- if ( errMsg != Errors.ErrDBNotFound.message && errMsg != Errors.ErrThreadNotFound.message) {
1049
- throw error;
1050
- }
1051
- }
1052
- const threadMultiaddr = new ThreadMuliaddr(dbMultiAddr, tID);
1053
- await this.newDBFromAddr(threadMultiaddr, threadKey, dbOpts);
1054
- return null;
1055
- } catch (error) {
1056
- const errMsg = (error as any).message;
1057
- if (errMsg == Errors.ErrorThreadIDValidation.message) {
1058
- return errMsg;
1059
- }
1060
- return error as Error;
1061
- }
1062
- }
1063
-
1064
- async getDBRecordsCount(threadid: string): Promise<number> {
1065
- let count = 0;
1066
- try {
1067
- const tid = await this.decodeThreadId(threadid);
1068
- const threadInfo = await this.network.getThread( tid);
1069
- if (!threadInfo) {
1070
- return count;
1071
- }
1072
- for (const logInfo of threadInfo.logs ) {
1073
- if (!logInfo.head) {
1074
- continue;
1075
- }
1076
- if (count < logInfo.head.counter) {
1077
- count = logInfo.head.counter;
1078
- }
1079
- }
1080
- } catch (error) {
1081
- console.error(`Error getting records count for thread ${threadid}:`, error);
1082
- }
1083
- return count;
1084
- }
1085
-
1086
-
1087
- async addLogToThread(ctx: Context, id: ThreadID, lid: PeerId): Promise<void> {
1088
- let blockHeight: number;
1089
- try {
1090
- blockHeight = (await this.chainUtil.getBlockHeight())||0;
1091
- } catch (err) {
1092
- throw err;
1093
- }
1094
-
1095
- // 生成用户签名
1096
- const hValue: Uint8Array = uint32ToLittleEndianBytes(
1097
- blockHeight ? blockHeight : 0
1098
- );
1099
-
1100
- const peerIdValue: Uint8Array = new TextEncoder().encode(
1101
- this.connectedDc.nodeAddr?.getPeerId() || ""
1102
- );
1103
-
1104
- const preSign = new Uint8Array([
1105
- ...new TextEncoder().encode(id.toString()),
1106
- ...new TextEncoder().encode(lid.toString()),
1107
- ...hValue,
1108
- ...peerIdValue,
1109
- ]);
1110
-
1111
- let signature: Uint8Array;
1112
- try {
1113
- signature = await this.context.sign(preSign);
1114
- } catch (err) {
1115
- throw err;
1116
- }
1117
- if (!this.connectedDc?.client) {
1118
- throw Errors.ErrNoDcPeerConnected
1119
- }
1120
-
1121
- const opts: NewThreadOptions = {
1122
- token: new ThreadToken(this.connectedDc.client.token),
1123
- blockHeight: blockHeight,
1124
- signature: signature,
1125
- };
1126
- const dbClient = newGrpcClient(this.connectedDc.client,this.network);
1127
- await dbClient.addLogToThread(id.toString(),lid.toString(),opts);
1128
- }
1129
-
1130
-
1131
-
1132
- async addLogToThreadStart(
1133
- ctx: Context|null,
1134
- id: ThreadID,
1135
- lid: PeerId
1136
- ) : Promise<void> {
1137
-
1138
- if (!ctx){
1139
- ctx = createContext(30000);
1140
- }
1141
- const abortController = new AbortController();
1142
- const signal = ctx?.signal || abortController.signal;
1143
- const [storeUnit,err] = await this.chainUtil.objectState(id.toString());
1144
- if (storeUnit && !err) {
1145
- const userPubkey = this.context.getPublicKey();
1146
- let findFlag = false;
1147
- for (const user of storeUnit.users) {
1148
- //移除0x前缀
1149
- const noPrefixUser = user.replace("0x", "");
1150
- if (noPrefixUser === userPubkey.toString()) {
1151
- findFlag = true;
1152
- break;
1153
- }
1154
- }
1155
- if (!findFlag) {
1156
- throw new Error('user not in the thread');
1157
- }
1158
- //判断是否已经上报过了
1159
- const exist = storeUnit?storeUnit.logs.has(lid.toString()):false;
1160
- if (exist) {
1161
- return;
1162
- }
1163
- }
1164
-
1165
- // 处理超时
1166
- let timeoutId: ReturnType<typeof setTimeout> | null = null;
1167
- if (ctx.deadline) {
1168
- const timeout = ctx.deadline.getTime() - Date.now();
1169
- if (timeout > 0) {
1170
- timeoutId = setTimeout(() => {
1171
- abortController.abort();
1172
- }, timeout);
1173
- }
1174
- }
1175
- let count = 0;
1176
- const maxCount = 10;
1177
- let endFlag = false;
1178
- try{
1179
- await this.addLogToThread(ctx, id, lid);
1180
- }catch (error) {//允许报错
1181
- }
1182
- let stopped = false;
1183
- const tick = async () => {
1184
- if (signal.aborted || stopped) {
1185
- if (timeoutId) clearTimeout(timeoutId);
1186
- return;
1187
- }
1188
-
1189
- if (await this.ifDbInitSuccess(id)) {
1190
- if (timeoutId) clearTimeout(timeoutId);
1191
- endFlag = true;
1192
- stopped = true;
1193
- return;
1194
- }
1195
-
1196
- if (count >= maxCount) {
1197
- try {
1198
- await this.addLogToThread(ctx, id, lid);
1199
- } catch (error) {
1200
-
1201
- }
1202
- count = 0;
1203
- } else {
1204
- count++;
1205
- }
1206
-
1207
- setTimeout(tick, 1000); // 递归调度
1208
- };
1209
- tick();
1210
- await new Promise<void>((resolve) => {
1211
- // Add a resolving condition to the interval
1212
- const checkFlag = setInterval(() => {
1213
- if (endFlag) {
1214
- clearInterval(checkFlag);
1215
- resolve();
1216
- }
1217
- }, 1000);
1218
-
1219
- // Also make sure the abort signal resolves the promise
1220
- signal.addEventListener('abort', () => {
1221
- clearInterval(checkFlag);
1222
- resolve();
1223
- });
1224
- });
1225
-
1226
- }
1227
-
1228
-
1229
- async newDB(
1230
- dbname: string,
1231
- b32Rk: string,
1232
- b32Sk: string,
1233
- collectionInfos: ICollectionConfig[]
1234
- ): Promise<[string, Error | null]> {
1235
- if (!this.connectedDc?.client) {
1236
- return ['', Errors.ErrNoDcPeerConnected];
1237
- }
1238
- try {
1239
- const dbClient = newGrpcClient(this.connectedDc.client,this.network);
1240
- const tidStr = await dbClient.requestThreadID();
1241
- const threadID = await this.decodeThreadId(tidStr);
1242
- const logKey = await this.getLogKey(threadID);
1243
- const lpk = logKey.publicKey;
1244
- const lid = peerIdFromPrivateKey(logKey)
1245
- const sk = SymmetricKey.fromString(b32Sk);
1246
- const rk = SymmetricKey.fromString(b32Rk);
1247
- const threadKey = new ThreadKey(sk, rk);
1248
- const blockHeight = (await this.chainUtil.getBlockHeight())||0;
1249
-
1250
- const hValue: Uint8Array = uint32ToLittleEndianBytes(
1251
- blockHeight ? blockHeight : 0
1252
- );
1253
- if (!this.connectedDc?.nodeAddr) {
1254
- return ['', Errors.ErrNodeAddrIsNull];
1255
- }
1256
- const rPeerId = await extractPeerIdFromMultiaddr(this.connectedDc.nodeAddr);
1257
- const peerIdValue: Uint8Array = new TextEncoder().encode(rPeerId.toString());
1258
- const sizeValue: Uint8Array = uint64ToLittleEndianBytes(50<<20); //数据库固定大小50M
1259
- const tidUnit8Array = new TextEncoder().encode(tidStr);
1260
-
1261
- const typeValue: Uint8Array = uint32ToLittleEndianBytes(Type.Threaddbtype);
1262
- const preSign = new Uint8Array([
1263
- ...tidUnit8Array,
1264
- ...sizeValue,
1265
- ...hValue,
1266
- ...typeValue,
1267
- ...peerIdValue
1268
- ]);
1269
- const signature = await this.context.sign(preSign);
1270
-
1271
- // Create thread options
1272
- const opts: NewThreadOptions = {
1273
- threadKey: threadKey,
1274
- logKey: logKey,
1275
- token: new ThreadToken(this.connectedDc.client.token),
1276
- blockHeight: blockHeight,
1277
- signature: signature,
1278
- };
1279
- const threadInfo = await dbClient.createThread(threadID.toString(), opts);
1280
- const collections = collectionInfos.map(info => ({
1281
- name: info.name,
1282
- schema: info.schema,
1283
- indexes: info.indexes || []
1284
- }));
1285
-
1286
-
1287
-
1288
- const dbOpts: NewOptions = {
1289
- name: dbname,
1290
- collections: collections,
1291
- key: threadKey,
1292
- logKey: logKey,
1293
- block: true,
1294
- };
1295
-
1296
- // Try creating database
1297
- const errors: string[] = [];
1298
- for (const multiAddr of threadInfo.addrs) {
1299
- try {
1300
-
1301
- await this.newDBFromAddr(multiAddr, threadKey, dbOpts);
1302
- break;
1303
- } catch (error:any) {
1304
- errors.push(error.message);
1305
- }
1306
- }
1307
-
1308
- if (errors.length === threadInfo.addrs.length) {
1309
- throw new Error(`create db failed:${errors.join(',')}`);
1310
- }
1311
- const ctx = createContext(30000);
1312
- await this.addLogToThreadStart(ctx,threadID, lid);
1313
- return [threadID.toString(), null];
1314
- } catch (error) {
1315
- return ['', error as Error];
1316
- }
1317
-
1318
- }
1319
-
1320
- async refreshDBFromDC(threadId:string): Promise<Error | null> {
1321
- try {
1322
- const tId = await this.decodeThreadId(threadId);
1323
- await this.network.pullThread( tId,600, { multiPeersFlag: true });
1324
- return null;
1325
- } catch (error) {
1326
- return error as Error;
1327
- }
1328
- }
1329
-
1330
- async syncDBToDC(threadId:string): Promise<Error | null> {
1331
- if (!this.network) {
1332
- return Errors.ErrP2pNetworkNotInit;
1333
- }
1334
- try {
1335
- const tId = await this.decodeThreadId(threadId);
1336
- await this.network.exchange( tId);
1337
- return null;
1338
- } catch (error) {
1339
- return error as Error;
1340
- }
1341
- }
1342
-
1343
-
1344
- async upgradeCollections(threadId: string, configs: ICollectionConfig[]): Promise<Error | null> {
1345
- try {
1346
- const tId = await this.decodeThreadId(threadId);
1347
- const db = this.dbs.get(tId.toString());
1348
- if (!db) {
1349
- return Errors.ErrDBNotFound;
1350
- }
1351
- await db.upgradeCollections(configs);
1352
- return null;
1353
- } catch (error) {
1354
- return error as Error;
1355
- }
1356
- }
1357
-
1358
-
1359
-
1360
-
1361
- private async decodeThreadId(threadid: string): Promise<ThreadID> {
1362
- if (!threadid) {
1363
- throw new Error('Thread ID is empty');
1364
- }
1365
-
1366
- try {
1367
- // 基本格式验证
1368
- if (!/^[a-zA-Z0-9]+$/.test(threadid)) {
1369
- throw Errors.ErrorThreadIDValidation;
1370
- }
1371
-
1372
- // 尝试解码
1373
- const threadID = ThreadID.fromString(threadid);
1374
-
1375
- // 验证长度
1376
- const bytes = threadID.toBytes();
1377
- if (bytes.length < 32) {
1378
- throw new Error('Thread ID too short');
1379
- }
1380
-
1381
- return threadID;
1382
- } catch (error) {
1383
- const errMsg = (error as any).message;
1384
- if (errMsg === Errors.ErrorThreadIDValidation.message) {
1385
- throw error;
1386
- }
1387
- throw new Error(`Failed to decode thread ID: $errMsg}`);
1388
- }
1389
- }
1390
-
1391
- // 为了方便使用,可以添加一个验证方法
1392
- async validateThreadId(threadid: string): Promise<boolean> {
1393
- try {
1394
- await this.decodeThreadId(threadid);
1395
- return true;
1396
- } catch {
1397
- return false;
1398
- }
1399
- }
1400
-
1401
- async close(): Promise<void> {
1402
- // await this.lock.acquire('dbs', async () => {
1403
- for (const db of this.dbs.values()) {
1404
- await db.close();
1405
- }
1406
- this.dbs.clear();
1407
- // });
1408
- }
1409
-
1410
- /**
1411
- * Gets a database by ID
1412
- * @param ctx The context for the operation
1413
- * @param id The thread ID of the database
1414
- * @param opts Optional managed options
1415
- * @returns Promise resolving to the database instance
1416
- * @throws Error if the database cannot be found
1417
- */
1418
- async getDB(id: ThreadID, opts?: ManagedOptions): Promise<ThreadDb> {
1419
- console.debug(`manager: getting db ${id}`);
1420
-
1421
-
1422
-
1423
- console.debug(`manager: getting thread ${id} from net`);
1424
- try {
1425
- // Get thread from the network
1426
- await this.network.getThread(id, { token: opts?.token });
1427
- console.debug(`manager: got thread ${id} from net`);
1428
- } catch (err) {
1429
- throw err;
1430
- }
1431
-
1432
- const db = this.dbs.get(id.toString());
1433
- if (!db) {
1434
- throw Errors.ErrDBNotFound;
1435
- }
1436
- return db;
1437
-
1438
- }
1439
-
1440
-
1441
- async getDBInfo(id: ThreadID, opts?: ManagedOptions): Promise<[IDBInfo|null, Error|null]> {
1442
- let dbInfo: IDBInfo|null = null;
1443
- try {
1444
- const db = this.dbs.get(id.toString());
1445
- if (!db) {
1446
- throw Errors.ErrDBNotFound;
1447
- }
1448
- dbInfo = await db.getDBInfo(opts);
1449
- if (!dbInfo || dbInfo === null) {
1450
- throw new Error(`No info available for db ${id}`);
1451
- }
1452
- }catch (err) {
1453
- console.error(`Error getting DB info for ${id}:`, err);
1454
- return [null, err as Error];
1455
- }
1456
- return [dbInfo,null];
1457
- }
1458
-
1459
-
1460
-
1461
-
1462
-
1463
- // DeleteDB deletes a db by id.
1464
- async deleteDB( id: ThreadID, deleteThreadFlag: boolean, opts?: ManagedOptions): Promise<void> {
1465
- console.debug(`manager: deleting db ${id}`);
1466
-
1467
- console.debug(`manager: getting thread ${id} from net`);
1468
- try {
1469
- await this.network.getThread(id, { token: opts?.token });
1470
- console.debug(`manager: got thread ${id} from net`);
1471
- } catch (err) {
1472
- throw err;
1473
- }
1474
-
1475
- const db = this.dbs.get(id.toString());
1476
- if (!db) {
1477
- throw Errors.ErrDBNotFound;
1478
- }
1479
-
1480
- try {
1481
- await db.close();
1482
- } catch (err) {
1483
- throw err;
1484
- }
1485
-
1486
- if (deleteThreadFlag) {
1487
- console.debug(`manager: deleting thread ${id} from net`);
1488
- try {
1489
- await this.network.deleteThread(id, { token: opts?.token, apiToken: db.connector?.token });
1490
- console.debug(`manager: deleted thread ${id} from net`);
1491
- } catch (err) {
1492
- throw err;
1493
- }
1494
- }
1495
-
1496
- try {
1497
- await this.deleteThreadNamespace(id);
1498
- } catch (err) {
1499
- throw err;
1500
- }
1501
- // this.lock.acquire('dbs', async () => {
1502
- this.dbs.delete(id.toString());
1503
- console.debug(`manager: deleted db ${id}`);
1504
- // });
1505
- }
1506
-
1507
- private async deleteThreadNamespace(id: ThreadID): Promise<void> {
1508
- const pre = dsManagerBaseKey.child(new Key(id.toString())).toString();
1509
- const q = { prefix: pre, keysOnly: true };
1510
- const results = this.store.query(q);
1511
- for await (const result of results) {
1512
- await this.store.delete(result.key);
1513
- }
1514
- }
1515
-
1516
-
1517
- /**********************数据库数据操作相关**********************/
1518
- /**
1519
- * Create creates new instances of objects in a collection
1520
- * @param threadId Thread ID string
1521
- * @param collectionName Collection name
1522
- * @param jsonInstance JSON string representing the instance
1523
- * @returns Promise resolving to the created instance ID
1524
- * @throws Error if creation fails
1525
- */
1526
- async create(threadId: string, collectionName: string, jsonInstance: string): Promise<string> {
1527
- // // 检查实例大小
1528
- // if (jsonInstance.length > 100 * 1024) { // 100 KB
1529
- // throw new Error("instance too big");
1530
- // }
1531
-
1532
- // 判断instance里面是否有_mod字段,存在则删除
1533
- try {
1534
- const instanceObj = JSON.parse(jsonInstance);
1535
- if (instanceObj && typeof instanceObj === 'object' && '_mod' in instanceObj) {
1536
- delete instanceObj._mod;
1537
- jsonInstance = JSON.stringify(instanceObj);
1538
- }
1539
- } catch (err) {
1540
- // JSON解析失败,保持原字符串不变
1541
- console.warn('Failed to parse instance JSON, keeping original:', err);
1542
- throw new Error("Invalid instance JSON format");
1543
- }
1544
- try {
1545
-
1546
- // 解码threaddbID
1547
- const tID = ThreadID.fromString(threadId);
1548
-
1549
- // 获取threaddb数据库
1550
- const threadDB = await this.getDB(tID);
1551
-
1552
- // 获取集合
1553
- const collection = threadDB.getCollection(collectionName);
1554
- if (!collection) {
1555
- throw new Error("Collection does not exist");
1556
- }
1557
-
1558
- // 创建实例
1559
- const instanceID = await collection.create(Buffer.from(jsonInstance));
1560
- // 返回实例ID
1561
- return instanceID ?instanceID.toString():"";
1562
- } catch (err) {
1563
- console.error(`Failed to create instance: ${err instanceof Error ? err.message : String(err)}`);
1564
- throw err;
1565
- }
1566
- }
1567
-
1568
-
1569
- /**
1570
- * Delete deletes an instance by ID
1571
- * @param threadId Thread ID string
1572
- * @param collectionName Collection name
1573
- * @param instanceID Instance ID to delete
1574
- * @throws Error if deletion fails
1575
- */
1576
- async delete(threadId: string, collectionName: string, instanceID: string): Promise<void> {
1577
- try {
1578
- // 解码线程ID
1579
- const tID = ThreadID.fromString(threadId);
1580
-
1581
-
1582
-
1583
- // 获取线程数据库
1584
- const threadDB = await this.getDB(tID);
1585
-
1586
- // 获取集合
1587
- const collection = threadDB.getCollection(collectionName);
1588
- if (!collection) {
1589
- throw new Error("Collection does not exist");
1590
- }
1591
- // 删除实例
1592
- await collection.delete(instanceID);
1593
- } catch (err) {
1594
- console.error(`Failed to delete instance: ${err instanceof Error ? err.message : String(err)}`);
1595
- throw err;
1596
- }
1597
- }
1598
-
1599
- /**
1600
- * Save updates an existing instance
1601
- * @param threadId Thread ID string
1602
- * @param collectionName Collection name
1603
- * @param instance JSON string representing the instance
1604
- * @throws Error if update fails
1605
- */
1606
- async save(threadId: string, collectionName: string, instance: string): Promise<void> {
1607
- // // 检查实例大小
1608
- // if (instance.length > 100 * 1024) { // 100 KB
1609
- // throw new Error("instance too big");
1610
- // }
1611
-
1612
- try {
1613
- // 解码线程ID
1614
- const tID = ThreadID.fromString(threadId);
1615
-
1616
- // 判断instance里面是否有_mod字段,存在则删除
1617
- try {
1618
- const instanceObj = JSON.parse(instance);
1619
- if (instanceObj && typeof instanceObj === 'object' && '_mod' in instanceObj) {
1620
- delete instanceObj._mod;
1621
- instance = JSON.stringify(instanceObj);
1622
- }
1623
- } catch (err) {
1624
- // JSON解析失败,保持原字符串不变
1625
- console.warn('Failed to parse instance JSON, keeping original:', err);
1626
- throw new Error("Invalid instance JSON format");
1627
- }
1628
-
1629
-
1630
-
1631
- // 获取线程数据库
1632
- const threadDB = await this.getDB(tID);
1633
-
1634
- // 获取集合
1635
- const collection = threadDB.getCollection(collectionName);
1636
- if (!collection) {
1637
- throw new Error("Collection does not exist");
1638
- }
1639
- // 保存实例
1640
- await collection.save(Buffer.from(instance));
1641
- } catch (err) {
1642
- console.error(`Failed to save instance: ${err instanceof Error ? err.message : String(err)}`);
1643
- throw err;
1644
- }
1645
- }
1646
-
1647
- /**
1648
- * DeleteMany deletes multiple instances by their IDs
1649
- * @param threadId Thread ID string
1650
- * @param collectionName Collection name
1651
- * @param instanceIDs Comma-separated or JSON array of instance IDs
1652
- * @throws Error if deletion fails
1653
- */
1654
- async deleteMany(threadId: string, collectionName: string, instanceIDs: string): Promise<void> {
1655
- try {
1656
- // 解码线程ID
1657
- const tID = ThreadID.fromString(threadId);
1658
-
1659
-
1660
- // 获取线程数据库
1661
- const threadDB = await this.getDB(tID);
1662
-
1663
- // 获取集合
1664
- const collection = threadDB.getCollection(collectionName);
1665
- if (!collection) {
1666
- throw new Error("Collection does not exist");
1667
- }
1668
-
1669
- // 解析实例ID列表
1670
- let IDs: string[] = [];
1671
- instanceIDs = instanceIDs.trim();
1672
- if (instanceIDs === "") {
1673
- return;
1674
- }
1675
-
1676
- if (instanceIDs[0] !== '[') {
1677
- // 逗号分隔的ID列表
1678
- IDs = instanceIDs.split(',');
1679
- } else {
1680
- // JSON数组
1681
- try {
1682
- IDs = JSON.parse(instanceIDs);
1683
- } catch (err) {
1684
- // 解析失败时,将整个字符串作为一个ID
1685
- IDs = [instanceIDs];
1686
- }
1687
- }
1688
-
1689
- // 批量处理,每次最多100个(避免事务过大)
1690
- const idsLen = IDs.length;
1691
- for (let i = 0; i < idsLen; i += 100) {
1692
- const batchIds = IDs.slice(i, Math.min(i + 100, idsLen));
1693
- await collection.deleteMany(batchIds);
1694
- }
1695
- } catch (err) {
1696
- console.error(`Failed to delete instances: ${err instanceof Error ? err.message : String(err)}`);
1697
- throw err;
1698
- }
1699
- }
1700
-
1701
- /**
1702
- * Has checks if the specified instance exists
1703
- * @param threadId Thread ID string
1704
- * @param collectionName Collection name
1705
- * @param instanceID Instance ID to check
1706
- * @returns Promise resolving to a boolean indicating if instance exists
1707
- */
1708
- async has(threadId: string, collectionName: string, instanceID: string): Promise<boolean> {
1709
- try {
1710
- // 解码线程ID
1711
- const tID = ThreadID.fromString(threadId);
1712
-
1713
-
1714
-
1715
- // 获取线程数据库
1716
- const threadDB = await this.getDB(tID);
1717
-
1718
- // 获取集合
1719
- const collection = threadDB.getCollection(collectionName);
1720
- if (!collection) {
1721
- return false;
1722
- }
1723
-
1724
- // 检查实例是否存在
1725
- return await collection.has(instanceID);
1726
- } catch (err) {
1727
- console.error(`Failed to check instance existence: ${err instanceof Error ? err.message : String(err)}`);
1728
- return false;
1729
- }
1730
- }
1731
-
1732
- /**
1733
- * Find finds instances by query
1734
- * @param threadId Thread ID string
1735
- * @param collectionName Collection name
1736
- * @param queryString JSON string representing the query
1737
- * @returns Promise resolving to a JSON string with found instances
1738
- * @throws Error if query fails
1739
- */
1740
- async find(threadId: string, collectionName: string, queryString?: string): Promise<string> {
1741
- try {
1742
- if (!queryString) {
1743
- queryString = "{}";
1744
- }
1745
-
1746
- // 解析查询字符串
1747
- const query = parseJsonToQuery(queryString);
1748
- // 解码线程ID
1749
- const tID = ThreadID.fromString(threadId);
1750
-
1751
-
1752
-
1753
- // 获取线程数据库
1754
- const threadDB = await this.getDB(tID);
1755
-
1756
- // 获取集合
1757
- const collection = threadDB.getCollection(collectionName);
1758
- if (!collection) {
1759
- throw new Error("Collection does not exist");
1760
- }
1761
-
1762
- // 执行查询
1763
- const results = await collection.find(query);
1764
-
1765
- // 合并结果并返回JSON字符串
1766
- if (Array.isArray(results)) {
1767
- return jsonStringify(results);
1768
- } else {
1769
- // 如果结果是字节数组,则需要连接它们
1770
- const resultArray = results as Buffer[];
1771
- const joinedResult = Buffer.concat([
1772
- Buffer.from('['),
1773
- Buffer.concat(
1774
- resultArray.map((buf, idx) =>
1775
- Buffer.concat([
1776
- buf,
1777
- idx < resultArray.length - 1 ? Buffer.from(',') : Buffer.from('')
1778
- ])
1779
- )
1780
- ),
1781
- Buffer.from(']')
1782
- ]);
1783
- return joinedResult.toString();
1784
- }
1785
- } catch (err) {
1786
- console.error(`Failed to find instances: ${err instanceof Error ? err.message : String(err)}`);
1787
- throw err;
1788
- }
1789
- }
1790
-
1791
- /**
1792
- * FindByID finds an instance by ID
1793
- * @param threadId Thread ID string
1794
- * @param collectionName Collection name
1795
- * @param instanceID Instance ID to find
1796
- * @returns Promise resolving to a JSON string with found instance
1797
- * @throws Error if query fails
1798
- */
1799
- async findByID(threadId: string, collectionName: string, instanceID: string): Promise<string> {
1800
- try {
1801
- // 解码线程ID
1802
- const tID = ThreadID.fromString(threadId);
1803
-
1804
- // 获取线程数据库
1805
- const threadDB = await this.getDB(tID);
1806
-
1807
- // 获取集合
1808
- const collection = threadDB.getCollection(collectionName);
1809
- if (!collection) {
1810
- throw new Error("Collection does not exist");
1811
- }
1812
-
1813
- // 根据ID查找实例
1814
- const result = await collection.findByID(instanceID);
1815
-
1816
- // 返回实例
1817
- return result instanceof Buffer ? result.toString() : jsonStringify(result);
1818
- } catch (err) {
1819
- console.error(`Failed to find instance by ID: ${err instanceof Error ? err.message : String(err)}`);
1820
- throw err;
1821
- }
1822
- }
1823
-
1824
- /**
1825
- * ModifiedSince returns instance IDs modified since the given time
1826
- * @param threadId Thread ID string
1827
- * @param collectionName Collection name
1828
- * @param time Unix timestamp in milliseconds
1829
- * @returns Promise resolving to a JSON string with instance IDs
1830
- * @throws Error if query fails
1831
- */
1832
- async modifiedSince(threadId: string, collectionName: string, time: number): Promise<string> {
1833
- try {
1834
- // 解码线程ID
1835
- const tID = ThreadID.fromString(threadId);
1836
-
1837
-
1838
-
1839
- // 获取线程数据库
1840
- const threadDB = await this.getDB(tID);
1841
-
1842
- // 获取集合
1843
- const collection = threadDB.getCollection(collectionName);
1844
- if (!collection) {
1845
- throw new Error("Collection does not exist");
1846
- }
1847
-
1848
- // 获取指定时间后修改的实例ID列表
1849
- const ids = await collection.modifiedSince(time);
1850
-
1851
- // 序列化并返回ID列表
1852
- return JSON.stringify(ids);
1853
- } catch (err) {
1854
- console.error(`Failed to get modified instances: ${err instanceof Error ? err.message : String(err)}`);
1855
- throw err;
1856
- }
1857
- }
1858
-
1859
-
1860
- }
1861
-
1862
-
1863
- class AsyncLock {
1864
- private locks: Map<string, Promise<void>>;
1865
- constructor() {
1866
- this.locks = new Map();
1867
- }
1868
- async acquire<T>(key: string, fn: () => Promise<T>): Promise<T> {
1869
- while (this.locks.has(key)) {
1870
- await this.locks.get(key);
1871
- }
1872
- let resolve: () => void;
1873
- const promise = new Promise<void>((r) => (resolve = r));
1874
- this.locks.set(key, promise);
1875
-
1876
- try {
1877
- const result = await fn();
1878
- return result;
1879
- } catch (err: any) {
1880
- // 重新抛出错误以保持类型一致性
1881
- throw err;
1882
- } finally {
1883
- this.locks.delete(key);
1884
- resolve!();
1885
- }
1886
- }
1887
- }
1888
-
1889
- function isValidName(name: string): boolean {
1890
- return /^[a-zA-Z0-9_-]+$/.test(name);
1891
- }
1892
-
1893
- export function createContext(timeout: number): Context {
1894
- const ctx : Context = {
1895
- deadline: new Date(Date.now() + timeout)
1896
- };
1897
- if (timeout === 0) {
1898
- ctx.deadline = undefined;
1899
- }
1900
- if (typeof AbortController !== 'undefined') {
1901
- ctx.signal = new AbortController().signal;
1902
- }
1903
- return ctx;
1904
- }
1905
-
1906
-