@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,637 @@
1
+ import * as plugins from './plugins.js';
2
+
3
+ import { SmartdataCollection } from './classes.collection.js';
4
+ import { EasyStore } from './classes.easystore.js';
5
+ import { SmartdataPersistenceError } from './classes.persistence.js';
6
+ import { SmartdataMongoAdministration } from './classes.mongoadministration.js';
7
+ import {
8
+ createSmartdataSession,
9
+ invalidateSmartdataSessions,
10
+ SmartdataSession,
11
+ unwrapSmartdataSessionInvalidationErrors,
12
+ } from './classes.session.js';
13
+ import { collectionTopologyDbInspectionSymbol } from './classes.collectiontopology.js';
14
+ import {
15
+ inspectSmartdataNamespace,
16
+ type ISmartdataNamespaceInspectionOptions,
17
+ type ISmartdataNamespaceInspection,
18
+ } from './classes.namespaceinspection.js';
19
+
20
+ export { SmartdataSession } from './classes.session.js';
21
+ export type { TSmartdataOrdinarySession } from './classes.session.js';
22
+
23
+ import { logger } from './logging.js';
24
+ import { assertDataOptions, SmartdataOperationBudget } from './classes.operationbudget.js';
25
+
26
+ /**
27
+ * interface - indicates the connection status of the db
28
+ */
29
+ export type TConnectionStatus = 'initial' | 'disconnected' | 'connected' | 'failed';
30
+
31
+ export interface ISmartdataReadinessOptions {
32
+ timeoutMs?: number;
33
+ signal?: AbortSignal;
34
+ }
35
+
36
+ export interface ISmartdataInitOptions {
37
+ /** Bounds connection setup; cleanup is always joined before the call settles. */
38
+ timeoutMs?: number;
39
+ signal?: AbortSignal;
40
+ }
41
+
42
+ export interface ISmartdataCloseOptions {
43
+ /** Total deadline for removal, including preceding session cleanup (1..120000 ms). */
44
+ timeoutMs?: number;
45
+ dropDatabase?: {
46
+ expectedDatabaseName: string;
47
+ };
48
+ }
49
+
50
+ export type TSmartdataReadinessFailureReason =
51
+ | 'not_initialized'
52
+ | 'connecting'
53
+ | 'connection_failed'
54
+ | 'closing'
55
+ | 'closed'
56
+ | 'close_failed'
57
+ | 'aborted'
58
+ | 'timeout'
59
+ | 'unavailable';
60
+
61
+ export type TSmartdataReadinessResult =
62
+ | {
63
+ ready: true;
64
+ status: 'ready';
65
+ durationMs: number;
66
+ }
67
+ | {
68
+ ready: false;
69
+ status: 'unavailable';
70
+ reason: TSmartdataReadinessFailureReason;
71
+ durationMs: number;
72
+ };
73
+
74
+ type TSmartdataDbLifecycleState =
75
+ | 'initial'
76
+ | 'connecting'
77
+ | 'connected'
78
+ | 'connection_failed'
79
+ | 'closing'
80
+ | 'closed'
81
+ | 'close_failed';
82
+
83
+ type TSmartdataDbLifecycleOperation = 'init' | 'close';
84
+
85
+ const readinessDefaultTimeoutMs = 2_000;
86
+ const readinessMaximumTimeoutMs = 30_000;
87
+
88
+ const assertExactDataObject = (
89
+ valueArg: unknown,
90
+ expectedKeysArg: readonly string[],
91
+ descriptionArg: string,
92
+ allowEmptyArg = false,
93
+ ): Record<string, unknown> => {
94
+ if (
95
+ valueArg === null ||
96
+ typeof valueArg !== 'object' ||
97
+ Array.isArray(valueArg) ||
98
+ Object.getPrototypeOf(valueArg) !== Object.prototype
99
+ ) {
100
+ throw new SmartdataPersistenceError(
101
+ 'invalid_argument',
102
+ `${descriptionArg} must be a plain object.`,
103
+ );
104
+ }
105
+ const keys = Reflect.ownKeys(valueArg);
106
+ if (
107
+ (!allowEmptyArg && keys.length !== expectedKeysArg.length) ||
108
+ (allowEmptyArg && keys.length !== 0 && keys.length !== expectedKeysArg.length) ||
109
+ keys.some(
110
+ (keyArg) =>
111
+ typeof keyArg !== 'string' || !expectedKeysArg.includes(keyArg),
112
+ )
113
+ ) {
114
+ throw new SmartdataPersistenceError(
115
+ 'invalid_argument',
116
+ `${descriptionArg} has an invalid shape.`,
117
+ );
118
+ }
119
+ for (const key of keys as string[]) {
120
+ const descriptor = Object.getOwnPropertyDescriptor(valueArg, key);
121
+ if (!descriptor || !('value' in descriptor)) {
122
+ throw new SmartdataPersistenceError(
123
+ 'invalid_argument',
124
+ `${descriptionArg} must contain data properties only.`,
125
+ );
126
+ }
127
+ }
128
+ return valueArg as Record<string, unknown>;
129
+ };
130
+
131
+ export class SmartdataDb {
132
+ smartdataOptions: plugins.tsclass.database.IMongoDescriptor;
133
+ mongoDbClient!: plugins.mongodb.MongoClient;
134
+ mongoDb!: plugins.mongodb.Db;
135
+ status: TConnectionStatus;
136
+ statusConnectedDeferred = plugins.smartpromise.defer();
137
+ smartdataCollectionMap = new plugins.lik.ObjectMap<SmartdataCollection<any>>();
138
+ private lifecycleState: TSmartdataDbLifecycleState = 'initial';
139
+ private lifecycleEpoch = 0;
140
+ private activeLifecycleOperation: TSmartdataDbLifecycleOperation | null = null;
141
+ private pendingClient?: plugins.mongodb.MongoClient;
142
+
143
+ constructor(smartdataOptions: plugins.tsclass.database.IMongoDescriptor) {
144
+ this.smartdataOptions = smartdataOptions;
145
+ this.status = 'initial';
146
+ void this.statusConnectedDeferred.promise.catch(() => {});
147
+ }
148
+
149
+ // easystore
150
+ public async createEasyStore<T = unknown>(nameIdArg: string): Promise<EasyStore<T>> {
151
+ const easyStore = new EasyStore<T>(nameIdArg, this);
152
+ return easyStore;
153
+ }
154
+
155
+ // basic connection stuff ----------------------------------------------
156
+
157
+ /**
158
+ * connects to the database that was specified during instance creation
159
+ */
160
+ public async init(optionsArg: ISmartdataInitOptions = {}): Promise<void> {
161
+ this.assertLifecycleOperationCanBegin('init');
162
+ const options = assertDataOptions(optionsArg, ['timeoutMs', 'signal']);
163
+ const budget = Object.keys(options).length
164
+ ? new SmartdataOperationBudget(options.timeoutMs ?? 30_000, options.signal) : undefined;
165
+ try { budget?.check(); } catch (errorArg) { budget?.close(); throw errorArg; }
166
+ this.beginLifecycleOperation('init');
167
+ this.transitionLifecycle('connecting');
168
+ let candidateClient: plugins.mongodb.MongoClient | undefined;
169
+ try {
170
+ try {
171
+ // Safely encode credentials to handle special characters
172
+ const encodedUser = this.smartdataOptions.mongoDbUser
173
+ ? encodeURIComponent(this.smartdataOptions.mongoDbUser)
174
+ : '';
175
+ const encodedPass = this.smartdataOptions.mongoDbPass
176
+ ? encodeURIComponent(this.smartdataOptions.mongoDbPass)
177
+ : '';
178
+
179
+ const finalConnectionUrl = this.smartdataOptions.mongoDbUrl
180
+ .replace('<USERNAME>', encodedUser)
181
+ .replace('<username>', encodedUser)
182
+ .replace('<USER>', encodedUser)
183
+ .replace('<user>', encodedUser)
184
+ .replace('<PASSWORD>', encodedPass)
185
+ .replace('<password>', encodedPass)
186
+ .replace('<DBNAME>', this.smartdataOptions.mongoDbName || '')
187
+ .replace('<dbname>', this.smartdataOptions.mongoDbName || '');
188
+
189
+ const descriptor = this.smartdataOptions as plugins.tsclass.database.IMongoDescriptor &
190
+ Pick<plugins.mongodb.MongoClientOptions, 'maxPoolSize' | 'maxIdleTimeMS' |
191
+ 'serverSelectionTimeoutMS' | 'socketTimeoutMS'>;
192
+ const clientOptions: plugins.mongodb.MongoClientOptions = {
193
+ maxPoolSize: descriptor.maxPoolSize ?? 100,
194
+ maxIdleTimeMS: descriptor.maxIdleTimeMS ?? 300000,
195
+ serverSelectionTimeoutMS:
196
+ budget?.remainingMs() ?? descriptor.serverSelectionTimeoutMS ?? 30000,
197
+ ...(budget ? { connectTimeoutMS: budget.remainingMs() } : {}),
198
+ socketTimeoutMS: descriptor.socketTimeoutMS ?? 30000,
199
+ retryWrites: true,
200
+ };
201
+
202
+ candidateClient = new plugins.mongodb.MongoClient(finalConnectionUrl, clientOptions);
203
+ this.pendingClient = candidateClient;
204
+ const connectingClient = candidateClient;
205
+ let cancellationCleanup: Promise<void> | undefined;
206
+ const onAbort = () => {
207
+ cancellationCleanup ??= connectingClient.close();
208
+ // The connection and this cleanup are both joined below.
209
+ void cancellationCleanup.catch(() => {});
210
+ };
211
+ budget?.signal.addEventListener('abort', onAbort, { once: true });
212
+ try {
213
+ budget?.check();
214
+ await connectingClient.connect();
215
+ budget?.check();
216
+ } finally {
217
+ budget?.signal.removeEventListener('abort', onAbort);
218
+ if (cancellationCleanup) await cancellationCleanup;
219
+ }
220
+ const candidateDb = candidateClient.db(
221
+ this.smartdataOptions.mongoDbName,
222
+ );
223
+ for (const collection of this.smartdataCollectionMap.getArray()) {
224
+ collection.resetForDatabaseReconnect();
225
+ }
226
+ budget?.check();
227
+ this.mongoDbClient = candidateClient;
228
+ this.mongoDb = candidateDb;
229
+ candidateClient = undefined;
230
+ this.pendingClient = undefined;
231
+ this.status = 'connected';
232
+ this.transitionLifecycle('connected');
233
+ this.statusConnectedDeferred.resolve();
234
+ logger.log('info', `Connected to database ${this.smartdataOptions.mongoDbName}`);
235
+ } catch (error) {
236
+ let cleanupError: unknown;
237
+ if (candidateClient) {
238
+ try {
239
+ // A cancelled connect may establish resources after its first close.
240
+ await candidateClient.close();
241
+ this.pendingClient = undefined;
242
+ } catch (errorArg) {
243
+ cleanupError = errorArg;
244
+ }
245
+ }
246
+ this.status = 'disconnected';
247
+ this.transitionLifecycle(this.pendingClient ? 'close_failed' : 'connection_failed');
248
+ logger.log('error', 'Failed to connect to the SmartData database.');
249
+ if (budget) {
250
+ throw new SmartdataPersistenceError('unsupported_operation', this.pendingClient
251
+ ? 'SmartData connection setup and cleanup failed; close must be retried.'
252
+ : 'SmartData connection setup failed or was cancelled.');
253
+ }
254
+ if (cleanupError) throw new AggregateError([error, cleanupError],
255
+ 'SmartData connection setup and cleanup failed; close must be retried.');
256
+ throw error;
257
+ }
258
+ } finally {
259
+ this.endLifecycleOperation('init');
260
+ budget?.close();
261
+ }
262
+ }
263
+
264
+ /**
265
+ * closes the connection to the databse
266
+ */
267
+ public async close(optionsArg: ISmartdataCloseOptions = {}): Promise<void> {
268
+ this.assertLifecycleOperationCanBegin('close');
269
+ const databaseToDrop = this.resolveDatabaseToDrop(optionsArg);
270
+ const timeoutMs = Object.hasOwn(optionsArg, 'timeoutMs') ? optionsArg.timeoutMs : undefined;
271
+ const budget = timeoutMs === undefined ? undefined : new SmartdataOperationBudget(timeoutMs);
272
+ this.beginLifecycleOperation('close');
273
+ this.transitionLifecycle('closing');
274
+ this.status = 'disconnected';
275
+ const mongoDbClient = this.pendingClient ?? this.mongoDbClient;
276
+ try {
277
+ const closeErrors: unknown[] = [];
278
+ try {
279
+ await invalidateSmartdataSessions(this);
280
+ } catch (errorArg) {
281
+ closeErrors.push(
282
+ ...(unwrapSmartdataSessionInvalidationErrors(errorArg) ?? [errorArg]),
283
+ );
284
+ }
285
+ if (databaseToDrop) {
286
+ try {
287
+ const databaseDropped = await databaseToDrop.dropDatabase(budget?.operationOptions());
288
+ if (!databaseDropped) {
289
+ throw new SmartdataPersistenceError(
290
+ 'unsupported_operation',
291
+ 'MongoDB did not acknowledge the requested database removal.',
292
+ );
293
+ }
294
+ } catch (errorArg) {
295
+ closeErrors.push(errorArg);
296
+ }
297
+ }
298
+ try {
299
+ await mongoDbClient?.close();
300
+ this.pendingClient = undefined;
301
+ } catch (errorArg) {
302
+ closeErrors.push(errorArg);
303
+ }
304
+ if (closeErrors.length > 0) {
305
+ this.transitionLifecycle('close_failed');
306
+ if (budget) throw new SmartdataPersistenceError('unsupported_operation',
307
+ 'SmartData database cleanup failed; close must be retried.');
308
+ if (closeErrors.length === 1) {
309
+ throw closeErrors[0];
310
+ }
311
+ throw new AggregateError(
312
+ closeErrors,
313
+ 'Multiple SmartData database cleanup operations failed.',
314
+ );
315
+ }
316
+ this.transitionLifecycle('closed');
317
+ logger.log('info', `disconnected from database ${this.smartdataOptions.mongoDbName}`);
318
+ } finally {
319
+ this.endLifecycleOperation('close');
320
+ budget?.close();
321
+ }
322
+ }
323
+
324
+ /**
325
+ * Performs a bounded, read-only round trip to verify database readiness.
326
+ */
327
+ public async checkReadiness(
328
+ optionsArg: ISmartdataReadinessOptions = {},
329
+ ): Promise<TSmartdataReadinessResult> {
330
+ const startedAt = performance.now();
331
+ const timeoutMs = optionsArg.timeoutMs ?? readinessDefaultTimeoutMs;
332
+ if (
333
+ !Number.isSafeInteger(timeoutMs) ||
334
+ timeoutMs <= 0 ||
335
+ timeoutMs > readinessMaximumTimeoutMs
336
+ ) {
337
+ throw new SmartdataPersistenceError(
338
+ 'invalid_argument',
339
+ `Readiness timeoutMs must be a positive safe integer no greater than ${readinessMaximumTimeoutMs}.`,
340
+ );
341
+ }
342
+
343
+ const lifecycleReason = this.getReadinessLifecycleReason();
344
+ if (lifecycleReason) {
345
+ return this.createReadinessFailure(lifecycleReason, startedAt);
346
+ }
347
+
348
+ const signal = optionsArg.signal;
349
+ if (signal?.aborted) {
350
+ return this.createReadinessFailure('aborted', startedAt);
351
+ }
352
+
353
+ const lifecycleEpoch = this.lifecycleEpoch;
354
+ const mongoDb = this.mongoDb;
355
+ const mongoDbClient = this.mongoDbClient;
356
+ let response: plugins.mongodb.BSON.Document;
357
+ try {
358
+ response = await mongoDb.command(
359
+ { ping: 1 },
360
+ {
361
+ timeoutMS: timeoutMs,
362
+ signal,
363
+ },
364
+ );
365
+ } catch (error) {
366
+ if (signal?.aborted) {
367
+ return this.createReadinessFailure('aborted', startedAt);
368
+ }
369
+ if (
370
+ this.lifecycleEpoch !== lifecycleEpoch ||
371
+ this.mongoDb !== mongoDb ||
372
+ this.mongoDbClient !== mongoDbClient
373
+ ) {
374
+ return this.createReadinessFailure(
375
+ this.getReadinessLifecycleReason() ?? 'unavailable',
376
+ startedAt,
377
+ );
378
+ }
379
+ if (error instanceof plugins.mongodb.MongoOperationTimeoutError) {
380
+ return this.createReadinessFailure('timeout', startedAt);
381
+ }
382
+ return this.createReadinessFailure('unavailable', startedAt);
383
+ }
384
+
385
+ if (
386
+ this.lifecycleEpoch !== lifecycleEpoch ||
387
+ this.mongoDb !== mongoDb ||
388
+ this.mongoDbClient !== mongoDbClient
389
+ ) {
390
+ return this.createReadinessFailure(
391
+ this.getReadinessLifecycleReason() ?? 'unavailable',
392
+ startedAt,
393
+ );
394
+ }
395
+
396
+ if (response.ok !== 1) {
397
+ return this.createReadinessFailure('unavailable', startedAt);
398
+ }
399
+
400
+ return {
401
+ ready: true,
402
+ status: 'ready',
403
+ durationMs: performance.now() - startedAt,
404
+ };
405
+ }
406
+
407
+ /**
408
+ * Starts a raw MongoDB session. Use createSession() when every transaction
409
+ * operation goes through SmartData and stable persistence errors are needed.
410
+ */
411
+ public startSession(): plugins.mongodb.ClientSession {
412
+ return this.mongoDbClient.startSession();
413
+ }
414
+
415
+ /**
416
+ * Creates a SmartData-owned session for model transactions.
417
+ */
418
+ public createSession(): SmartdataSession {
419
+ return createSmartdataSession(this);
420
+ }
421
+
422
+ /**
423
+ * Creates a narrowly scoped MongoDB administration facade bound to this
424
+ * SmartData lifecycle. Applications never need direct driver access.
425
+ */
426
+ public createMongoAdministration(): SmartdataMongoAdministration {
427
+ return new SmartdataMongoAdministration(this);
428
+ }
429
+
430
+ /** Observe all namespaces, indexes and bounded counts without initializing models. */
431
+ public async inspectNamespace(
432
+ optionsArg?: ISmartdataNamespaceInspectionOptions,
433
+ ): Promise<ISmartdataNamespaceInspection> {
434
+ return inspectSmartdataNamespace(this, optionsArg);
435
+ }
436
+
437
+ /** @internal Runs inspection or explicit model preparation against one connection generation. */
438
+ public async [collectionTopologyDbInspectionSymbol]<TResult>(
439
+ operationArg: (mongoDbArg: plugins.mongodb.Db, assertCurrentArg: () => void) => Promise<TResult>,
440
+ ): Promise<TResult> {
441
+ if (
442
+ this.lifecycleState !== 'connected' ||
443
+ this.status !== 'connected' ||
444
+ !this.mongoDb ||
445
+ !this.mongoDbClient ||
446
+ this.mongoDb.client !== this.mongoDbClient
447
+ ) {
448
+ throw new SmartdataPersistenceError(
449
+ 'unsupported_operation',
450
+ 'Collection topology inspection requires a connected SmartdataDb.',
451
+ );
452
+ }
453
+ const lifecycleEpoch = this.lifecycleEpoch;
454
+ const mongoDb = this.mongoDb;
455
+ const mongoDbClient = this.mongoDbClient;
456
+ const connectionIsCurrent = () =>
457
+ this.lifecycleState === 'connected' &&
458
+ this.status === 'connected' &&
459
+ this.lifecycleEpoch === lifecycleEpoch &&
460
+ this.mongoDb === mongoDb &&
461
+ this.mongoDbClient === mongoDbClient &&
462
+ mongoDb.client === mongoDbClient;
463
+ let result: TResult;
464
+ try {
465
+ result = await operationArg(mongoDb, () => {
466
+ if (!connectionIsCurrent()) {
467
+ throw new SmartdataPersistenceError('unsupported_operation', 'SmartData connection changed during operation.');
468
+ }
469
+ });
470
+ } catch {
471
+ if (!connectionIsCurrent()) {
472
+ throw new SmartdataPersistenceError(
473
+ 'unsupported_operation',
474
+ 'SmartData connection changed during collection topology inspection.',
475
+ );
476
+ }
477
+ throw new SmartdataPersistenceError(
478
+ 'unsupported_operation',
479
+ 'Collection topology inspection failed.',
480
+ );
481
+ }
482
+ if (!connectionIsCurrent()) {
483
+ throw new SmartdataPersistenceError(
484
+ 'unsupported_operation',
485
+ 'SmartData connection changed during collection topology inspection.',
486
+ );
487
+ }
488
+ return result;
489
+ }
490
+
491
+ // handle table to class distribution
492
+
493
+ public addCollection(SmartdataCollectionArg: SmartdataCollection<any>) {
494
+ this.smartdataCollectionMap.add(SmartdataCollectionArg);
495
+ }
496
+
497
+ /**
498
+ * Gets a collection's name and returns a SmartdataCollection instance
499
+ * @param nameArg
500
+ * @returns DbTable
501
+ */
502
+ public async getSmartdataCollectionByName<T>(nameArg: string): Promise<SmartdataCollection<T>> {
503
+ const resultCollection = await this.smartdataCollectionMap.find(async (dbTableArg) => {
504
+ return dbTableArg.collectionName === nameArg;
505
+ });
506
+ return resultCollection;
507
+ }
508
+
509
+ private beginLifecycleOperation(operationArg: TSmartdataDbLifecycleOperation): void {
510
+ this.assertLifecycleOperationCanBegin(operationArg);
511
+ this.activeLifecycleOperation = operationArg;
512
+ }
513
+
514
+ private assertLifecycleOperationCanBegin(
515
+ operationArg: TSmartdataDbLifecycleOperation,
516
+ ): void {
517
+ if (this.activeLifecycleOperation) {
518
+ throw new SmartdataPersistenceError(
519
+ 'unsupported_operation',
520
+ `Cannot start SmartData database ${operationArg} while ${this.activeLifecycleOperation} is still running.`,
521
+ );
522
+ }
523
+ if (
524
+ operationArg === 'init' &&
525
+ (this.lifecycleState === 'connected' ||
526
+ this.lifecycleState === 'close_failed')
527
+ ) {
528
+ throw new SmartdataPersistenceError(
529
+ 'unsupported_operation',
530
+ `Cannot start SmartData database init while the existing client is ${this.lifecycleState}.`,
531
+ );
532
+ }
533
+ }
534
+
535
+ private resolveDatabaseToDrop(
536
+ optionsArg: ISmartdataCloseOptions,
537
+ ): plugins.mongodb.Db | undefined {
538
+ const options = assertDataOptions(optionsArg, ['dropDatabase', 'timeoutMs']);
539
+ if (!Object.hasOwn(options, 'dropDatabase')) {
540
+ return undefined;
541
+ }
542
+ const dropDatabaseOptions = assertExactDataObject(
543
+ options.dropDatabase,
544
+ ['expectedDatabaseName'],
545
+ 'SmartData dropDatabase options',
546
+ );
547
+ const expectedDatabaseName = dropDatabaseOptions.expectedDatabaseName;
548
+ if (
549
+ typeof expectedDatabaseName !== 'string' ||
550
+ expectedDatabaseName.length === 0 ||
551
+ expectedDatabaseName.trim() !== expectedDatabaseName
552
+ ) {
553
+ throw new SmartdataPersistenceError(
554
+ 'invalid_argument',
555
+ 'SmartData expectedDatabaseName must be a non-empty string without surrounding whitespace.',
556
+ );
557
+ }
558
+ if (
559
+ this.lifecycleState !== 'connected' ||
560
+ this.status !== 'connected' ||
561
+ !this.mongoDbClient ||
562
+ !this.mongoDb
563
+ ) {
564
+ throw new SmartdataPersistenceError(
565
+ 'unsupported_operation',
566
+ 'SmartData can remove a database only from a connected lifecycle.',
567
+ );
568
+ }
569
+ if (this.mongoDb.client !== this.mongoDbClient) {
570
+ throw new SmartdataPersistenceError(
571
+ 'invalid_configuration',
572
+ 'SmartData database and client bindings are inconsistent.',
573
+ );
574
+ }
575
+ const boundDatabaseName = this.mongoDb.databaseName;
576
+ if (expectedDatabaseName !== boundDatabaseName) {
577
+ throw new SmartdataPersistenceError(
578
+ 'invalid_argument',
579
+ 'SmartData expectedDatabaseName does not match the connected database.',
580
+ );
581
+ }
582
+ if (this.smartdataOptions.mongoDbName !== boundDatabaseName) {
583
+ throw new SmartdataPersistenceError(
584
+ 'invalid_configuration',
585
+ 'SmartData database descriptor no longer matches the connected database.',
586
+ );
587
+ }
588
+ return this.mongoDb;
589
+ }
590
+
591
+ private endLifecycleOperation(operationArg: TSmartdataDbLifecycleOperation): void {
592
+ if (this.activeLifecycleOperation === operationArg) {
593
+ this.activeLifecycleOperation = null;
594
+ }
595
+ }
596
+
597
+ private transitionLifecycle(stateArg: TSmartdataDbLifecycleState): void {
598
+ this.lifecycleState = stateArg;
599
+ this.lifecycleEpoch++;
600
+ }
601
+
602
+ private getReadinessLifecycleReason():
603
+ | Exclude<
604
+ TSmartdataReadinessFailureReason,
605
+ 'aborted' | 'timeout' | 'unavailable'
606
+ >
607
+ | undefined {
608
+ switch (this.lifecycleState) {
609
+ case 'initial':
610
+ return 'not_initialized';
611
+ case 'connecting':
612
+ return 'connecting';
613
+ case 'connection_failed':
614
+ return 'connection_failed';
615
+ case 'closing':
616
+ return 'closing';
617
+ case 'closed':
618
+ return 'closed';
619
+ case 'close_failed':
620
+ return 'close_failed';
621
+ case 'connected':
622
+ return undefined;
623
+ }
624
+ }
625
+
626
+ private createReadinessFailure(
627
+ reasonArg: TSmartdataReadinessFailureReason,
628
+ startedAtArg: number,
629
+ ): TSmartdataReadinessResult {
630
+ return {
631
+ ready: false,
632
+ status: 'unavailable',
633
+ reason: reasonArg,
634
+ durationMs: performance.now() - startedAtArg,
635
+ };
636
+ }
637
+ }