@lossless.org/client 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (255) hide show
  1. package/.smartconfig.json +37 -0
  2. package/dist_ts/00_commitinfo_data.d.ts +8 -0
  3. package/dist_ts/00_commitinfo_data.js +9 -0
  4. package/dist_ts/classes.losslessorgclient.d.ts +32 -0
  5. package/dist_ts/classes.losslessorgclient.js +103 -0
  6. package/dist_ts/core/classes.error.d.ts +16 -0
  7. package/dist_ts/core/classes.error.js +29 -0
  8. package/dist_ts/core/classes.operation.d.ts +17 -0
  9. package/dist_ts/core/classes.operation.js +70 -0
  10. package/dist_ts/core/interfaces.d.ts +18 -0
  11. package/dist_ts/core/interfaces.js +2 -0
  12. package/dist_ts/index.d.ts +4 -0
  13. package/dist_ts/index.js +3 -0
  14. package/dist_ts/nosqldb/classes.atomicdelete.d.ts +26 -0
  15. package/dist_ts/nosqldb/classes.atomicdelete.js +44 -0
  16. package/dist_ts/nosqldb/classes.atomicfindoneandupdate.d.ts +27 -0
  17. package/dist_ts/nosqldb/classes.atomicfindoneandupdate.js +86 -0
  18. package/dist_ts/nosqldb/classes.atomicupdate.d.ts +26 -0
  19. package/dist_ts/nosqldb/classes.atomicupdate.js +55 -0
  20. package/dist_ts/nosqldb/classes.bootstraprecord.d.ts +14 -0
  21. package/dist_ts/nosqldb/classes.bootstraprecord.js +80 -0
  22. package/dist_ts/nosqldb/classes.bsonassertion.d.ts +2 -0
  23. package/dist_ts/nosqldb/classes.bsonassertion.js +29 -0
  24. package/dist_ts/nosqldb/classes.bsonobjectid.d.ts +2 -0
  25. package/dist_ts/nosqldb/classes.bsonobjectid.js +50 -0
  26. package/dist_ts/nosqldb/classes.bsonsnapshot.d.ts +3 -0
  27. package/dist_ts/nosqldb/classes.bsonsnapshot.js +126 -0
  28. package/dist_ts/nosqldb/classes.cached-document.d.ts +27 -0
  29. package/dist_ts/nosqldb/classes.cached-document.js +112 -0
  30. package/dist_ts/nosqldb/classes.collection.d.ts +302 -0
  31. package/dist_ts/nosqldb/classes.collection.js +1568 -0
  32. package/dist_ts/nosqldb/classes.collectionfactory.d.ts +30 -0
  33. package/dist_ts/nosqldb/classes.collectionfactory.js +65 -0
  34. package/dist_ts/nosqldb/classes.collectionlifecycle.d.ts +4 -0
  35. package/dist_ts/nosqldb/classes.collectionlifecycle.js +15 -0
  36. package/dist_ts/nosqldb/classes.collectionpreparation.d.ts +34 -0
  37. package/dist_ts/nosqldb/classes.collectionpreparation.js +134 -0
  38. package/dist_ts/nosqldb/classes.collectiontopology.d.ts +47 -0
  39. package/dist_ts/nosqldb/classes.collectiontopology.js +388 -0
  40. package/dist_ts/nosqldb/classes.connection.d.ts +18 -0
  41. package/dist_ts/nosqldb/classes.connection.js +23 -0
  42. package/dist_ts/nosqldb/classes.convenience.d.ts +1 -0
  43. package/dist_ts/nosqldb/classes.convenience.js +5 -0
  44. package/dist_ts/nosqldb/classes.cursor.d.ts +15 -0
  45. package/dist_ts/nosqldb/classes.cursor.js +82 -0
  46. package/dist_ts/nosqldb/classes.cursoroperation.d.ts +7 -0
  47. package/dist_ts/nosqldb/classes.cursoroperation.js +36 -0
  48. package/dist_ts/nosqldb/classes.db.d.ts +98 -0
  49. package/dist_ts/nosqldb/classes.db.js +434 -0
  50. package/dist_ts/nosqldb/classes.distributedcoordinator.d.ts +52 -0
  51. package/dist_ts/nosqldb/classes.distributedcoordinator.js +314 -0
  52. package/dist_ts/nosqldb/classes.doc.d.ts +722 -0
  53. package/dist_ts/nosqldb/classes.doc.js +2976 -0
  54. package/dist_ts/nosqldb/classes.easystore.d.ts +42 -0
  55. package/dist_ts/nosqldb/classes.easystore.js +199 -0
  56. package/dist_ts/nosqldb/classes.exactpersistence.d.ts +141 -0
  57. package/dist_ts/nosqldb/classes.exactpersistence.js +1110 -0
  58. package/dist_ts/nosqldb/classes.lucene.adapter.d.ts +173 -0
  59. package/dist_ts/nosqldb/classes.lucene.adapter.js +623 -0
  60. package/dist_ts/nosqldb/classes.mongoadministration.d.ts +73 -0
  61. package/dist_ts/nosqldb/classes.mongoadministration.js +497 -0
  62. package/dist_ts/nosqldb/classes.namespaceinspection.d.ts +42 -0
  63. package/dist_ts/nosqldb/classes.namespaceinspection.js +138 -0
  64. package/dist_ts/nosqldb/classes.operationbudget.d.ts +22 -0
  65. package/dist_ts/nosqldb/classes.operationbudget.js +65 -0
  66. package/dist_ts/nosqldb/classes.ordinarypersistence.d.ts +18 -0
  67. package/dist_ts/nosqldb/classes.ordinarypersistence.js +71 -0
  68. package/dist_ts/nosqldb/classes.persistence.d.ts +14 -0
  69. package/dist_ts/nosqldb/classes.persistence.js +34 -0
  70. package/dist_ts/nosqldb/classes.session.d.ts +28 -0
  71. package/dist_ts/nosqldb/classes.session.js +246 -0
  72. package/dist_ts/nosqldb/classes.storedinspection.d.ts +44 -0
  73. package/dist_ts/nosqldb/classes.storedinspection.js +100 -0
  74. package/dist_ts/nosqldb/classes.testdatabase.d.ts +56 -0
  75. package/dist_ts/nosqldb/classes.testdatabase.js +250 -0
  76. package/dist_ts/nosqldb/classes.watcher.d.ts +37 -0
  77. package/dist_ts/nosqldb/classes.watcher.js +98 -0
  78. package/dist_ts/nosqldb/index.d.ts +27 -0
  79. package/dist_ts/nosqldb/index.js +18 -0
  80. package/dist_ts/nosqldb/logging.d.ts +2 -0
  81. package/dist_ts/nosqldb/logging.js +3 -0
  82. package/dist_ts/nosqldb/plugins.d.ts +17 -0
  83. package/dist_ts/nosqldb/plugins.js +20 -0
  84. package/dist_ts/nosqldb/shim.d.ts +1 -0
  85. package/dist_ts/nosqldb/shim.js +8 -0
  86. package/dist_ts/nosqldb/testsupport.d.ts +3 -0
  87. package/dist_ts/nosqldb/testsupport.js +3 -0
  88. package/dist_ts/objectstorage/classes.bucket.d.ts +227 -0
  89. package/dist_ts/objectstorage/classes.bucket.js +939 -0
  90. package/dist_ts/objectstorage/classes.connection.d.ts +21 -0
  91. package/dist_ts/objectstorage/classes.connection.js +27 -0
  92. package/dist_ts/objectstorage/classes.directory.d.ts +124 -0
  93. package/dist_ts/objectstorage/classes.directory.js +335 -0
  94. package/dist_ts/objectstorage/classes.exactpathpurgeerror.d.ts +5 -0
  95. package/dist_ts/objectstorage/classes.exactpathpurgeerror.js +18 -0
  96. package/dist_ts/objectstorage/classes.exactuploaderror.d.ts +5 -0
  97. package/dist_ts/objectstorage/classes.exactuploaderror.js +19 -0
  98. package/dist_ts/objectstorage/classes.file.d.ts +91 -0
  99. package/dist_ts/objectstorage/classes.file.js +255 -0
  100. package/dist_ts/objectstorage/classes.listcursor.d.ts +41 -0
  101. package/dist_ts/objectstorage/classes.listcursor.js +67 -0
  102. package/dist_ts/objectstorage/classes.metadata.d.ts +57 -0
  103. package/dist_ts/objectstorage/classes.metadata.js +113 -0
  104. package/dist_ts/objectstorage/classes.smartbucket.d.ts +57 -0
  105. package/dist_ts/objectstorage/classes.smartbucket.js +357 -0
  106. package/dist_ts/objectstorage/classes.trash.d.ts +11 -0
  107. package/dist_ts/objectstorage/classes.trash.js +22 -0
  108. package/dist_ts/objectstorage/classes.watcher.d.ts +79 -0
  109. package/dist_ts/objectstorage/classes.watcher.js +249 -0
  110. package/dist_ts/objectstorage/helpers.d.ts +25 -0
  111. package/dist_ts/objectstorage/helpers.js +215 -0
  112. package/dist_ts/objectstorage/index.d.ts +13 -0
  113. package/dist_ts/objectstorage/index.js +13 -0
  114. package/dist_ts/objectstorage/interfaces.d.ts +276 -0
  115. package/dist_ts/objectstorage/interfaces.js +2 -0
  116. package/dist_ts/objectstorage/internal.exactpathpurge.operations.d.ts +11 -0
  117. package/dist_ts/objectstorage/internal.exactpathpurge.operations.js +1069 -0
  118. package/dist_ts/objectstorage/internal.exactupload.capability.d.ts +40 -0
  119. package/dist_ts/objectstorage/internal.exactupload.capability.js +382 -0
  120. package/dist_ts/objectstorage/internal.exactupload.operations.d.ts +11 -0
  121. package/dist_ts/objectstorage/internal.exactupload.operations.js +1104 -0
  122. package/dist_ts/objectstorage/internal.multipart.planner.d.ts +8 -0
  123. package/dist_ts/objectstorage/internal.multipart.planner.js +63 -0
  124. package/dist_ts/objectstorage/internal.multipart.stream.d.ts +17 -0
  125. package/dist_ts/objectstorage/internal.multipart.stream.js +440 -0
  126. package/dist_ts/objectstorage/internal.multipart.types.d.ts +82 -0
  127. package/dist_ts/objectstorage/internal.multipart.types.js +2 -0
  128. package/dist_ts/objectstorage/plugins.d.ts +17 -0
  129. package/dist_ts/objectstorage/plugins.js +23 -0
  130. package/dist_ts/plugins.d.ts +4 -0
  131. package/dist_ts/plugins.js +5 -0
  132. package/dist_ts/sqldb/classes.clickhouseconnection.d.ts +39 -0
  133. package/dist_ts/sqldb/classes.clickhouseconnection.js +225 -0
  134. package/dist_ts/sqldb/classes.sqlconnection.d.ts +50 -0
  135. package/dist_ts/sqldb/classes.sqlconnection.js +376 -0
  136. package/dist_ts/sqldb/classes.sqltable.d.ts +29 -0
  137. package/dist_ts/sqldb/classes.sqltable.js +70 -0
  138. package/dist_ts/sqldb/clickhouse/classes.metricwriter.d.ts +27 -0
  139. package/dist_ts/sqldb/clickhouse/classes.metricwriter.js +89 -0
  140. package/dist_ts/sqldb/clickhouse/index.d.ts +9 -0
  141. package/dist_ts/sqldb/clickhouse/index.js +13 -0
  142. package/dist_ts/sqldb/clickhouse/smartclickhouse.classes.clickhousetable.d.ts +88 -0
  143. package/dist_ts/sqldb/clickhouse/smartclickhouse.classes.clickhousetable.js +304 -0
  144. package/dist_ts/sqldb/clickhouse/smartclickhouse.classes.httpclient.d.ts +28 -0
  145. package/dist_ts/sqldb/clickhouse/smartclickhouse.classes.httpclient.js +46 -0
  146. package/dist_ts/sqldb/clickhouse/smartclickhouse.classes.querybuilder.d.ts +43 -0
  147. package/dist_ts/sqldb/clickhouse/smartclickhouse.classes.querybuilder.js +155 -0
  148. package/dist_ts/sqldb/clickhouse/smartclickhouse.classes.resultset.d.ts +13 -0
  149. package/dist_ts/sqldb/clickhouse/smartclickhouse.classes.resultset.js +36 -0
  150. package/dist_ts/sqldb/clickhouse/smartclickhouse.classes.smartclickhouse.d.ts +49 -0
  151. package/dist_ts/sqldb/clickhouse/smartclickhouse.classes.smartclickhouse.js +106 -0
  152. package/dist_ts/sqldb/clickhouse/smartclickhouse.classes.timedatatable.d.ts +51 -0
  153. package/dist_ts/sqldb/clickhouse/smartclickhouse.classes.timedatatable.js +96 -0
  154. package/dist_ts/sqldb/clickhouse/smartclickhouse.plugins.d.ts +5 -0
  155. package/dist_ts/sqldb/clickhouse/smartclickhouse.plugins.js +6 -0
  156. package/dist_ts/sqldb/clickhouse/smartclickhouse.types.d.ts +53 -0
  157. package/dist_ts/sqldb/clickhouse/smartclickhouse.types.js +41 -0
  158. package/dist_ts/sqldb/index.d.ts +6 -0
  159. package/dist_ts/sqldb/index.js +6 -0
  160. package/dist_ts/sqldb/interfaces.d.ts +53 -0
  161. package/dist_ts/sqldb/interfaces.js +19 -0
  162. package/dist_ts/sqldb/internal.sqlpool.d.ts +51 -0
  163. package/dist_ts/sqldb/internal.sqlpool.js +214 -0
  164. package/dist_ts/sqldb/plugins.clickhouse.d.ts +4 -0
  165. package/dist_ts/sqldb/plugins.clickhouse.js +5 -0
  166. package/dist_ts/sqldb/plugins.mariadb.d.ts +4 -0
  167. package/dist_ts/sqldb/plugins.mariadb.js +5 -0
  168. package/docs/source/smartbucket/license.md +19 -0
  169. package/docs/source/smartclickhouse/license.md +19 -0
  170. package/license.md +21 -0
  171. package/package.json +101 -0
  172. package/readme.md +157 -0
  173. package/ts/00_commitinfo_data.ts +8 -0
  174. package/ts/classes.losslessorgclient.ts +104 -0
  175. package/ts/core/classes.error.ts +31 -0
  176. package/ts/core/classes.operation.ts +60 -0
  177. package/ts/core/interfaces.ts +24 -0
  178. package/ts/index.ts +4 -0
  179. package/ts/nosqldb/classes.atomicdelete.ts +81 -0
  180. package/ts/nosqldb/classes.atomicfindoneandupdate.ts +152 -0
  181. package/ts/nosqldb/classes.atomicupdate.ts +99 -0
  182. package/ts/nosqldb/classes.bootstraprecord.ts +107 -0
  183. package/ts/nosqldb/classes.bsonassertion.ts +25 -0
  184. package/ts/nosqldb/classes.bsonobjectid.ts +45 -0
  185. package/ts/nosqldb/classes.bsonsnapshot.ts +105 -0
  186. package/ts/nosqldb/classes.cached-document.ts +67 -0
  187. package/ts/nosqldb/classes.collection.ts +2399 -0
  188. package/ts/nosqldb/classes.collectionfactory.ts +87 -0
  189. package/ts/nosqldb/classes.collectionlifecycle.ts +24 -0
  190. package/ts/nosqldb/classes.collectionpreparation.ts +164 -0
  191. package/ts/nosqldb/classes.collectiontopology.ts +645 -0
  192. package/ts/nosqldb/classes.connection.ts +31 -0
  193. package/ts/nosqldb/classes.convenience.ts +5 -0
  194. package/ts/nosqldb/classes.cursor.ts +88 -0
  195. package/ts/nosqldb/classes.cursoroperation.ts +38 -0
  196. package/ts/nosqldb/classes.db.ts +637 -0
  197. package/ts/nosqldb/classes.distributedcoordinator.ts +305 -0
  198. package/ts/nosqldb/classes.doc.ts +4928 -0
  199. package/ts/nosqldb/classes.easystore.ts +154 -0
  200. package/ts/nosqldb/classes.exactpersistence.ts +1974 -0
  201. package/ts/nosqldb/classes.lucene.adapter.ts +780 -0
  202. package/ts/nosqldb/classes.mongoadministration.ts +867 -0
  203. package/ts/nosqldb/classes.namespaceinspection.ts +199 -0
  204. package/ts/nosqldb/classes.operationbudget.ts +75 -0
  205. package/ts/nosqldb/classes.ordinarypersistence.ts +84 -0
  206. package/ts/nosqldb/classes.persistence.ts +62 -0
  207. package/ts/nosqldb/classes.session.ts +389 -0
  208. package/ts/nosqldb/classes.storedinspection.ts +151 -0
  209. package/ts/nosqldb/classes.testdatabase.ts +272 -0
  210. package/ts/nosqldb/classes.watcher.ts +96 -0
  211. package/ts/nosqldb/index.ts +51 -0
  212. package/ts/nosqldb/logging.ts +3 -0
  213. package/ts/nosqldb/plugins.ts +37 -0
  214. package/ts/nosqldb/shim.ts +6 -0
  215. package/ts/nosqldb/testsupport.ts +13 -0
  216. package/ts/objectstorage/classes.bucket.ts +1114 -0
  217. package/ts/objectstorage/classes.connection.ts +36 -0
  218. package/ts/objectstorage/classes.directory.ts +425 -0
  219. package/ts/objectstorage/classes.exactpathpurgeerror.ts +24 -0
  220. package/ts/objectstorage/classes.exactuploaderror.ts +25 -0
  221. package/ts/objectstorage/classes.file.ts +303 -0
  222. package/ts/objectstorage/classes.listcursor.ts +89 -0
  223. package/ts/objectstorage/classes.metadata.ts +134 -0
  224. package/ts/objectstorage/classes.smartbucket.ts +445 -0
  225. package/ts/objectstorage/classes.trash.ts +30 -0
  226. package/ts/objectstorage/classes.watcher.ts +289 -0
  227. package/ts/objectstorage/helpers.ts +254 -0
  228. package/ts/objectstorage/index.ts +13 -0
  229. package/ts/objectstorage/interfaces.ts +389 -0
  230. package/ts/objectstorage/internal.exactpathpurge.operations.ts +1936 -0
  231. package/ts/objectstorage/internal.exactupload.capability.ts +580 -0
  232. package/ts/objectstorage/internal.exactupload.operations.ts +1715 -0
  233. package/ts/objectstorage/internal.multipart.planner.ts +90 -0
  234. package/ts/objectstorage/internal.multipart.stream.ts +606 -0
  235. package/ts/objectstorage/internal.multipart.types.ts +106 -0
  236. package/ts/objectstorage/plugins.ts +35 -0
  237. package/ts/plugins.ts +4 -0
  238. package/ts/sqldb/classes.clickhouseconnection.ts +189 -0
  239. package/ts/sqldb/classes.sqlconnection.ts +350 -0
  240. package/ts/sqldb/classes.sqltable.ts +70 -0
  241. package/ts/sqldb/clickhouse/classes.metricwriter.ts +72 -0
  242. package/ts/sqldb/clickhouse/index.ts +16 -0
  243. package/ts/sqldb/clickhouse/smartclickhouse.classes.clickhousetable.ts +372 -0
  244. package/ts/sqldb/clickhouse/smartclickhouse.classes.httpclient.ts +52 -0
  245. package/ts/sqldb/clickhouse/smartclickhouse.classes.querybuilder.ts +120 -0
  246. package/ts/sqldb/clickhouse/smartclickhouse.classes.resultset.ts +44 -0
  247. package/ts/sqldb/clickhouse/smartclickhouse.classes.smartclickhouse.ts +123 -0
  248. package/ts/sqldb/clickhouse/smartclickhouse.classes.timedatatable.ts +114 -0
  249. package/ts/sqldb/clickhouse/smartclickhouse.plugins.ts +5 -0
  250. package/ts/sqldb/clickhouse/smartclickhouse.types.ts +134 -0
  251. package/ts/sqldb/index.ts +6 -0
  252. package/ts/sqldb/interfaces.ts +61 -0
  253. package/ts/sqldb/internal.sqlpool.ts +179 -0
  254. package/ts/sqldb/plugins.clickhouse.ts +4 -0
  255. package/ts/sqldb/plugins.mariadb.ts +4 -0
@@ -0,0 +1,272 @@
1
+ import * as plugins from './plugins.js';
2
+ import { snapshotBoundedBsonDocument } from './classes.bsonsnapshot.js';
3
+ import { SmartdataDb } from './classes.db.js';
4
+ import { SmartDataDbDoc } from './classes.doc.js';
5
+ import { SmartdataPersistenceError } from './classes.persistence.js';
6
+ import { assertDataOptions, SmartdataOperationBudget } from './classes.operationbudget.js';
7
+
8
+ export interface ISmartdataTestConnection {
9
+ /** Literal loopback IPv4, ::1, or localhost (normalized to 127.0.0.1). */
10
+ host: string;
11
+ port: number;
12
+ /** Supply both credentials or neither. Authentication uses admin. */
13
+ username?: string;
14
+ password?: string;
15
+ }
16
+
17
+ export interface ISmartdataTestDatabaseOptions {
18
+ connection: ISmartdataTestConnection;
19
+ /** Default 5000; maximum 120000 ms, shared by connection and ownership checks. */
20
+ timeoutMs?: number;
21
+ signal?: AbortSignal;
22
+ }
23
+
24
+ export interface ISmartdataStoredDocumentFaultOptions {
25
+ /** Default 64 KiB, maximum 1 MiB. */
26
+ maxBytes?: number;
27
+ /** Default 5000; maximum 120000 ms, including collection preparation. */
28
+ timeoutMs?: number;
29
+ signal?: AbortSignal;
30
+ }
31
+
32
+ export interface ISmartdataIndexFaultOptions {
33
+ /** Default 5000; maximum 120000 ms, shared by topology verification and removal. */
34
+ timeoutMs?: number;
35
+ signal?: AbortSignal;
36
+ }
37
+
38
+ export type TSmartdataTestModel = Pick<typeof SmartDataDbDoc,
39
+ 'prototype' | 'collection' | 'getExpectedCollectionTopology' | 'prepareCollection'> &
40
+ (abstract new (...args: never[]) => object);
41
+
42
+ /** Construction failed and cleanup also failed. Explicit close retries the retained owner. */
43
+ export class SmartdataTestDatabaseCreationError extends SmartdataPersistenceError {
44
+ readonly #cleanup: () => Promise<void>;
45
+ constructor(cleanupArg: () => Promise<void>) {
46
+ super('unsupported_operation', 'Disposable database setup failed and cleanup must be retried with close().');
47
+ this.name = 'SmartdataTestDatabaseCreationError';
48
+ this.#cleanup = cleanupArg;
49
+ }
50
+ public close(): Promise<void> { return this.#cleanup(); }
51
+ }
52
+
53
+ const faultError = (): SmartdataPersistenceError => new SmartdataPersistenceError(
54
+ 'invalid_argument', 'Fault data must be bounded, inert, canonical BSON with an explicit scalar _id.',
55
+ );
56
+
57
+ const canonicalFaultDocument = (valueArg: unknown, maxBytesArg: number): plugins.mongodb.Document =>
58
+ snapshotBoundedBsonDocument(valueArg, maxBytesArg, faultError);
59
+
60
+ /** Disposable fault injection is available only through the testsupport package subpath. */
61
+ export class SmartdataTestDatabase {
62
+ public readonly db: SmartdataDb;
63
+ readonly #name = `smartdata_test_${plugins.nodeCrypto.randomBytes(24).toString('hex')}`;
64
+ readonly #descriptor: SmartdataDb['smartdataOptions'];
65
+ #owned = false;
66
+ #closed = false;
67
+ #stopping = false;
68
+ #reconnectForRemoval = false;
69
+ #active?: Promise<void>;
70
+ #closing?: Promise<void>;
71
+
72
+ private constructor(connectionArg: ISmartdataTestConnection) {
73
+ const host = connectionArg.host === 'localhost' ? '127.0.0.1' : connectionArg.host;
74
+ const authority = host === '::1' ? '[::1]' : host;
75
+ const credentials = connectionArg.username === undefined ? ''
76
+ : `${encodeURIComponent(connectionArg.username)}:${encodeURIComponent(connectionArg.password!)}@`;
77
+ this.#descriptor = Object.freeze({
78
+ mongoDbName: this.#name,
79
+ mongoDbUrl: `mongodb://${credentials}${authority}:${connectionArg.port}/?directConnection=true&authSource=admin`,
80
+ });
81
+ this.db = new SmartdataDb(this.#descriptor);
82
+ }
83
+
84
+ public static async create(optionsArg: ISmartdataTestDatabaseOptions): Promise<SmartdataTestDatabase> {
85
+ const options = assertDataOptions(optionsArg, ['connection', 'timeoutMs', 'signal']);
86
+ const connection = assertDataOptions(options.connection, ['host', 'port', 'username', 'password']);
87
+ if (typeof connection.host !== 'string' ||
88
+ !(connection.host === 'localhost' || connection.host === '::1' ||
89
+ (plugins.nodeNet.isIP(connection.host) === 4 && connection.host.startsWith('127.'))) ||
90
+ !Number.isSafeInteger(connection.port) || (connection.port as number) < 1 || (connection.port as number) > 65535 ||
91
+ ((connection.username === undefined) !== (connection.password === undefined)) ||
92
+ [connection.username, connection.password].some((valueArg) => valueArg !== undefined &&
93
+ (typeof valueArg !== 'string' || valueArg.length < 1 || valueArg.length > 1024))) {
94
+ throw new SmartdataPersistenceError('invalid_argument', 'An explicit loopback host, port and optional credential pair are required.');
95
+ }
96
+ const budget = new SmartdataOperationBudget(options.timeoutMs ?? 5000, options.signal);
97
+ let fixture: SmartdataTestDatabase | undefined;
98
+ try {
99
+ budget.check();
100
+ fixture = new SmartdataTestDatabase(connection as unknown as ISmartdataTestConnection);
101
+ await fixture.db.init({ timeoutMs: budget.remainingMs(), signal: budget.signal });
102
+ const databases = await fixture.db.mongoDb.admin().listDatabases({
103
+ filter: { name: fixture.#name }, nameOnly: true, authorizedDatabases: false,
104
+ ...budget.operationOptions(),
105
+ });
106
+ if (databases.ok !== 1 || !Array.isArray(databases.databases) || databases.databases.length !== 0) {
107
+ throw new SmartdataPersistenceError('invalid_configuration', 'The disposable namespace already exists or cannot be verified.');
108
+ }
109
+ const namespace = await fixture.db.inspectNamespace({ maxCollections: 1,
110
+ timeoutMs: budget.remainingMs(), signal: budget.signal });
111
+ if (namespace.collections.length) throw new SmartdataPersistenceError('invalid_configuration',
112
+ 'The disposable namespace is not blank.');
113
+ budget.check();
114
+ fixture.#owned = true;
115
+ return fixture;
116
+ } catch (errorArg) {
117
+ if (fixture) {
118
+ try { await fixture.close(); } catch {
119
+ const retained = fixture;
120
+ throw new SmartdataTestDatabaseCreationError(() => retained.close());
121
+ }
122
+ }
123
+ throw new SmartdataPersistenceError(errorArg instanceof SmartdataPersistenceError ? errorArg.code : 'unsupported_operation',
124
+ 'Disposable database setup failed or was cancelled.');
125
+ } finally { budget.close(); }
126
+ }
127
+
128
+ public insertStoredDocumentFault(modelArg: TSmartdataTestModel, documentArg: unknown,
129
+ optionsArg: ISmartdataStoredDocumentFaultOptions = {}): Promise<void> {
130
+ return this.inject(modelArg, documentArg, undefined, optionsArg);
131
+ }
132
+
133
+ public replaceStoredDocumentFault(modelArg: TSmartdataTestModel, expectedIdArg: unknown,
134
+ documentArg: unknown, optionsArg: ISmartdataStoredDocumentFaultOptions = {}): Promise<void> {
135
+ return this.inject(modelArg, documentArg, { expectedId: expectedIdArg }, optionsArg);
136
+ }
137
+
138
+ /** Remove one declared secondary index from an exact, owned test collection. */
139
+ public async dropDeclaredIndexFault(modelArg: TSmartdataTestModel, indexNameArg: string,
140
+ optionsArg: ISmartdataIndexFaultOptions = {}): Promise<void> {
141
+ const options = assertDataOptions(optionsArg, ['timeoutMs', 'signal']);
142
+ const budget = new SmartdataOperationBudget(options.timeoutMs ?? 5000, options.signal);
143
+ try {
144
+ budget.check();
145
+ this.assertFaultAvailable();
146
+ const expected = this.getFaultModelTopology(modelArg);
147
+ if (typeof indexNameArg !== 'string' || indexNameArg === '_id_' || indexNameArg === '*' ||
148
+ !expected.indexes.some((indexArg) => indexArg.name === indexNameArg)) {
149
+ throw new SmartdataPersistenceError('invalid_argument',
150
+ 'Index fault injection requires one declared secondary index name.');
151
+ }
152
+ const operation = (async () => {
153
+ this.assertOwnedBinding();
154
+ const targetDb = this.db.mongoDb;
155
+ await modelArg.prepareCollection({ mode: 'requireExact', timeoutMs: budget.remainingMs(), signal: budget.signal });
156
+ this.assertOwnedBinding();
157
+ if (this.db.mongoDb !== targetDb) throw new SmartdataPersistenceError(
158
+ 'unsupported_operation', 'The disposable database connection changed.');
159
+ await targetDb.collection(expected.collectionName).dropIndex(indexNameArg, budget.operationOptions());
160
+ budget.check();
161
+ this.assertOwnedBinding();
162
+ if (this.db.mongoDb !== targetDb) throw new SmartdataPersistenceError(
163
+ 'unsupported_operation', 'The disposable database connection changed.');
164
+ })();
165
+ this.#active = operation;
166
+ try { await operation; } finally { this.#active = undefined; }
167
+ } catch (errorArg) {
168
+ throw new SmartdataPersistenceError(errorArg instanceof SmartdataPersistenceError ? errorArg.code : 'unsupported_operation',
169
+ 'Declared-index fault injection failed or was cancelled.');
170
+ } finally { budget.close(); }
171
+ }
172
+
173
+ private assertFaultAvailable(): void {
174
+ if (this.#stopping || this.#closed || !this.#owned || this.#active) throw new SmartdataPersistenceError(
175
+ 'unsupported_operation', 'The disposable database is closed, closing, or already injecting a fault.');
176
+ }
177
+
178
+ private getFaultModelTopology(modelArg: TSmartdataTestModel) {
179
+ if (typeof modelArg !== 'function' || plugins.nodeUtil.types.isProxy(modelArg) ||
180
+ !(modelArg.prototype instanceof SmartDataDbDoc)) throw new SmartdataPersistenceError(
181
+ 'invalid_argument', 'Fault injection requires the declared SmartData model constructor.');
182
+ const collection = modelArg.collection;
183
+ const expected = modelArg.getExpectedCollectionTopology();
184
+ if (collection.smartdataDb !== this.db || collection.collectionName !== expected.collectionName) {
185
+ throw new SmartdataPersistenceError('invalid_configuration', 'The fault model must belong to this disposable database.');
186
+ }
187
+ return expected;
188
+ }
189
+
190
+ private assertOwnedBinding(): void {
191
+ if (this.db.smartdataOptions !== this.#descriptor || this.db.mongoDb.databaseName !== this.#name ||
192
+ this.db.mongoDb.client !== this.db.mongoDbClient || this.db.status !== 'connected') {
193
+ throw new SmartdataPersistenceError('invalid_configuration', 'The disposable database binding has changed.');
194
+ }
195
+ }
196
+
197
+ private async inject(modelArg: TSmartdataTestModel, documentArg: unknown,
198
+ replacementArg: { expectedId: unknown } | undefined, optionsArg: ISmartdataStoredDocumentFaultOptions): Promise<void> {
199
+ const options = assertDataOptions(optionsArg, ['maxBytes', 'timeoutMs', 'signal']);
200
+ const maxBytes = options.maxBytes ?? 65536;
201
+ if (!Number.isSafeInteger(maxBytes) || (maxBytes as number) < 1 || (maxBytes as number) > 1024 * 1024) throw faultError();
202
+ const budget = new SmartdataOperationBudget(options.timeoutMs ?? 5000, options.signal);
203
+ try {
204
+ budget.check();
205
+ this.assertFaultAvailable();
206
+ const document = canonicalFaultDocument(documentArg, maxBytes as number);
207
+ if (replacementArg) {
208
+ const expected = canonicalFaultDocument({ _id: replacementArg.expectedId }, maxBytes as number);
209
+ if (!Buffer.from(plugins.mongodb.BSON.serialize(expected)).equals(
210
+ Buffer.from(plugins.mongodb.BSON.serialize({ _id: document._id })))) throw faultError();
211
+ }
212
+ const expected = this.getFaultModelTopology(modelArg);
213
+ const operation = (async () => {
214
+ this.assertOwnedBinding();
215
+ await modelArg.prepareCollection({ mode: 'ensureCompatible', timeoutMs: budget.remainingMs(), signal: budget.signal });
216
+ this.assertOwnedBinding();
217
+ const raw = this.db.mongoDb.collection(expected.collectionName);
218
+ if (replacementArg) {
219
+ const result = await raw.replaceOne({ _id: document._id }, document,
220
+ { upsert: false, ...budget.operationOptions() });
221
+ if (!result.acknowledged || result.matchedCount !== 1) throw new SmartdataPersistenceError(
222
+ 'unsupported_operation', 'Exact fault replacement requires one existing document.');
223
+ } else {
224
+ const result = await raw.insertOne(document, budget.operationOptions());
225
+ if (!result.acknowledged) throw new SmartdataPersistenceError('unsupported_operation', 'Fault insertion was not acknowledged.');
226
+ }
227
+ budget.check();
228
+ })();
229
+ this.#active = operation;
230
+ try { await operation; } finally { this.#active = undefined; }
231
+ } catch (errorArg) {
232
+ throw new SmartdataPersistenceError(errorArg instanceof SmartdataPersistenceError ? errorArg.code : 'unsupported_operation',
233
+ 'Stored-document fault injection failed or was cancelled.');
234
+ } finally { budget.close(); }
235
+ }
236
+
237
+ /** Idempotent after success. A failed close retains ownership for an explicit retry. */
238
+ public async close(optionsArg: { timeoutMs?: number } = {}): Promise<void> {
239
+ const options = assertDataOptions(optionsArg, ['timeoutMs']);
240
+ const budget = new SmartdataOperationBudget(options.timeoutMs ?? 5000);
241
+ try {
242
+ if (this.#closed) return;
243
+ this.#stopping = true;
244
+ if (this.#closing) {
245
+ const closing = this.#closing;
246
+ await budget.waitFor(closing);
247
+ await closing;
248
+ return;
249
+ }
250
+ const closing = this.dispose(budget);
251
+ this.#closing = closing;
252
+ try { await closing; } finally { this.#closing = undefined; }
253
+ } catch {
254
+ throw new SmartdataPersistenceError('unsupported_operation',
255
+ 'Disposable database cleanup failed; close must be retried.');
256
+ } finally { budget.close(); }
257
+ }
258
+
259
+ private async dispose(budgetArg: SmartdataOperationBudget): Promise<void> {
260
+ if (this.#active) await budgetArg.waitFor(this.#active);
261
+ if (this.#owned && this.#reconnectForRemoval) {
262
+ await this.db.close({ timeoutMs: budgetArg.remainingMs() });
263
+ this.db.smartdataOptions = this.#descriptor;
264
+ await this.db.init({ timeoutMs: budgetArg.remainingMs(), signal: budgetArg.signal });
265
+ }
266
+ this.#reconnectForRemoval = this.#owned;
267
+ await this.db.close({ timeoutMs: budgetArg.remainingMs(),
268
+ ...(this.#owned ? { dropDatabase: { expectedDatabaseName: this.#name } } : {}) });
269
+ this.#closed = true;
270
+ this.#owned = false;
271
+ }
272
+ }
@@ -0,0 +1,96 @@
1
+ import { SmartDataDbDoc } from './classes.doc.js';
2
+ import * as plugins from './plugins.js';
3
+ import { EventEmitter } from 'node:events';
4
+
5
+ /**
6
+ * a wrapper for the native mongodb cursor. Exposes better
7
+ */
8
+ /**
9
+ * Wraps a MongoDB ChangeStream with RxJS and EventEmitter support.
10
+ */
11
+ export class SmartdataDbWatcher<T = any> extends EventEmitter {
12
+ // STATIC
13
+ public readyDeferred = plugins.smartpromise.defer();
14
+
15
+ // INSTANCE
16
+ private changeStream: plugins.mongodb.ChangeStream<any>;
17
+ private rawSubject: plugins.smartrx.rxjs.Subject<T>;
18
+ private closePromise?: Promise<void>;
19
+ private onChange: (itemArg: any) => void;
20
+ private onStreamError: (errorArg: unknown) => void;
21
+ /** Terminal read/hydration failure, also delivered to subscribed error handlers. */
22
+ public failure?: unknown;
23
+ /** Emits change documents (or arrays of documents if buffered) */
24
+ public changeSubject: any;
25
+ /**
26
+ * @param changeStreamArg native MongoDB ChangeStream
27
+ * @param smartdataDbDocArg document class for instance creation
28
+ * @param opts.bufferTimeMs optional milliseconds to buffer events via RxJS
29
+ */
30
+ constructor(
31
+ changeStreamArg: plugins.mongodb.ChangeStream<any>,
32
+ smartdataDbDocArg: typeof SmartDataDbDoc,
33
+ opts?: { bufferTimeMs?: number },
34
+ ) {
35
+ super();
36
+ this.rawSubject = new plugins.smartrx.rxjs.Subject<T>();
37
+ // Apply buffering if requested
38
+ if (opts && opts.bufferTimeMs) {
39
+ this.changeSubject = this.rawSubject.pipe(plugins.smartrx.rxjs.ops.bufferTime(opts.bufferTimeMs));
40
+ } else {
41
+ this.changeSubject = this.rawSubject;
42
+ }
43
+ this.changeStream = changeStreamArg;
44
+ this.onStreamError = (errorArg) => {
45
+ if (this.closePromise) return;
46
+ this.failure = errorArg;
47
+ void this.close().catch((closeErrorArg) => {
48
+ this.failure = new AggregateError([errorArg, closeErrorArg], 'Watcher failed and cleanup failed.');
49
+ });
50
+ this.rawSubject.error(errorArg);
51
+ if (this.listenerCount('error') > 0) this.emit('error', errorArg);
52
+ };
53
+ this.onChange = (item: any) => {
54
+ if (this.closePromise) return;
55
+ let docInstance: T | null = null;
56
+ try {
57
+ if (item.fullDocument) {
58
+ docInstance = smartdataDbDocArg.createInstanceFromMongoDbNativeDoc(
59
+ item.fullDocument
60
+ ) as any as T;
61
+ }
62
+ } catch (errorArg) { this.onStreamError(errorArg); return; }
63
+ // Notify subscribers
64
+ this.rawSubject.next(docInstance as T);
65
+ this.emit('change', docInstance);
66
+ };
67
+ this.changeStream.on('change', this.onChange);
68
+ this.changeStream.on('error', this.onStreamError);
69
+ // Signal readiness after one tick
70
+ plugins.smartdelay.delayFor(0).then(() => {
71
+ this.readyDeferred.resolve();
72
+ });
73
+ }
74
+
75
+ /**
76
+ * Close the change stream, complete the RxJS subject, and remove listeners.
77
+ */
78
+ public async close(): Promise<void> {
79
+ this.closePromise ??= (async () => {
80
+ try { await this.changeStream.close(); }
81
+ finally {
82
+ this.changeStream.off('change', this.onChange);
83
+ this.changeStream.off('error', this.onStreamError);
84
+ this.rawSubject.complete();
85
+ this.removeAllListeners();
86
+ }
87
+ })();
88
+ return this.closePromise;
89
+ }
90
+ /**
91
+ * Alias for close(), matching README usage
92
+ */
93
+ public async stop(): Promise<void> {
94
+ return this.close();
95
+ }
96
+ }
@@ -0,0 +1,51 @@
1
+ // Polyfill must be imported first - ES modules hoist exports before code runs
2
+ import './shim.js';
3
+
4
+ export { NoSqlConnection } from './classes.connection.js';
5
+ export type { INoSqlConnectionOptions } from './classes.connection.js';
6
+
7
+ export * from './classes.db.js';
8
+ export * from './classes.mongoadministration.js';
9
+ export type {
10
+ ISmartdataNamespaceInspectionOptions,
11
+ ISmartdataNamespaceInspection,
12
+ ISmartdataObservedCollection,
13
+ TSmartdataObservedDocumentCount,
14
+ } from './classes.namespaceinspection.js';
15
+ export * from './classes.collection.js';
16
+ export type { ISmartdataCollectionPreparationOptions } from './classes.collectionpreparation.js';
17
+ export type { ISmartdataBootstrapRecordOptions, ISmartdataBootstrapRecordResult } from './classes.bootstraprecord.js';
18
+ export type {
19
+ ISmartdataCollectionTopologyIndex,
20
+ ISmartdataCollectionTopologyIndexKey,
21
+ ISmartdataCollectionTopologyInspection,
22
+ ISmartdataExpectedCollectionTopology,
23
+ TSmartdataCollectionTopologyIndexDirection,
24
+ TSmartdataCollectionTopologyReasonCode,
25
+ TSmartdataCollectionTopologyStatus,
26
+ } from './classes.collectiontopology.js';
27
+ export * from './classes.doc.js';
28
+ export { ordinaryPersistence } from './classes.ordinarypersistence.js';
29
+ export type { IOrdinaryPersistenceOptions } from './classes.ordinarypersistence.js';
30
+ export type {
31
+ ISmartdataStoredInspectionOptions, ISmartdataStoredInspection,
32
+ ISmartdataStoredDocumentEvidence, ISmartdataStoredEvidenceInspection,
33
+ TSmartdataOrdinaryDocumentEvidence,
34
+ } from './classes.storedinspection.js';
35
+ export { SmartdataPersistenceError } from './classes.persistence.js';
36
+ export type { TSmartdataPersistenceErrorCode } from './classes.persistence.js';
37
+ export * from './classes.exactpersistence.js';
38
+ export * from './classes.easystore.js';
39
+ export * from './classes.cursor.js';
40
+ export * from './classes.watcher.js';
41
+ export * from './classes.distributedcoordinator.js';
42
+ export * from './classes.cached-document.js';
43
+
44
+ import * as convenience from './classes.convenience.js';
45
+
46
+ export { convenience };
47
+
48
+ // to be removed with the next breaking update
49
+ import type * as plugins from './plugins.js';
50
+ type IMongoDescriptor = plugins.tsclass.database.IMongoDescriptor;
51
+ export type { IMongoDescriptor };
@@ -0,0 +1,3 @@
1
+ import * as plugins from './plugins.js';
2
+
3
+ export const logger = new plugins.smartlog.ConsoleLog();
@@ -0,0 +1,37 @@
1
+ // tsclass scope
2
+ import * as tsclass from '@tsclass/tsclass';
3
+
4
+ export { tsclass };
5
+
6
+ // @pushrocks scope
7
+ import * as lik from '@push.rocks/lik';
8
+ import * as smartdelay from '@push.rocks/smartdelay';
9
+ import * as smartlog from '@push.rocks/smartlog';
10
+ import * as smartpromise from '@push.rocks/smartpromise';
11
+ import * as smartrx from '@push.rocks/smartrx';
12
+ import * as smartstring from '@push.rocks/smartstring';
13
+ import * as smarttime from '@push.rocks/smarttime';
14
+ import * as smartunique from '@push.rocks/smartunique';
15
+ import * as taskbuffer from '@push.rocks/taskbuffer';
16
+ import * as mongodb from 'mongodb';
17
+ import * as nodeUtil from 'node:util';
18
+ import * as nodeAsyncHooks from 'node:async_hooks';
19
+ import * as nodeCrypto from 'node:crypto';
20
+ import * as nodeNet from 'node:net';
21
+
22
+ export {
23
+ lik,
24
+ smartdelay,
25
+ smartpromise,
26
+ smartlog,
27
+ smartrx,
28
+ mongodb,
29
+ nodeUtil,
30
+ nodeAsyncHooks,
31
+ nodeCrypto,
32
+ nodeNet,
33
+ smartstring,
34
+ smarttime,
35
+ smartunique,
36
+ taskbuffer,
37
+ };
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Polyfill for Symbol.metadata required by TC39 Stage 3 decorators.
3
+ * Must be imported before any decorator code loads.
4
+ * @see https://github.com/tc39/proposal-decorator-metadata
5
+ */
6
+ (Symbol as any).metadata ??= Symbol.for('Symbol.metadata');
@@ -0,0 +1,13 @@
1
+ import './shim.js';
2
+
3
+ export {
4
+ SmartdataTestDatabase,
5
+ SmartdataTestDatabaseCreationError,
6
+ } from './classes.testdatabase.js';
7
+ export type {
8
+ ISmartdataTestConnection,
9
+ ISmartdataTestDatabaseOptions,
10
+ ISmartdataStoredDocumentFaultOptions,
11
+ ISmartdataIndexFaultOptions,
12
+ TSmartdataTestModel,
13
+ } from './classes.testdatabase.js';