@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,389 @@
1
+ import * as plugins from './plugins.js';
2
+
3
+ import type { SmartdataDb } from './classes.db.js';
4
+ import {
5
+ SmartdataPersistenceError,
6
+ normalizeOrdinaryPersistenceError,
7
+ } from './classes.persistence.js';
8
+
9
+ interface ISmartdataSessionBinding {
10
+ session: SmartdataSession;
11
+ contextToken: symbol;
12
+ owner: SmartdataDb;
13
+ client: plugins.mongodb.MongoClient;
14
+ rawSession: plugins.mongodb.ClientSession;
15
+ operationActive: boolean;
16
+ transactionActive: boolean;
17
+ transactionCallbackOpen: boolean;
18
+ transactionUsesOrdinaryWrite: boolean;
19
+ transactionAttempt?: symbol;
20
+ transactionFailure?: { error: unknown };
21
+ operationSettled?: Promise<void>;
22
+ resolveOperationSettled?: () => void;
23
+ invalidated: boolean;
24
+ closing?: Promise<void>;
25
+ }
26
+
27
+ const bindings = new WeakMap<SmartdataSession, ISmartdataSessionBinding>();
28
+ const bindingsByOwner = new WeakMap<SmartdataDb, Set<ISmartdataSessionBinding>>();
29
+ const smartdataSessionInvalidationAggregateBrand = Symbol(
30
+ 'smartdataSessionInvalidationAggregate',
31
+ );
32
+
33
+ class SmartdataSessionInvalidationAggregateError extends AggregateError {
34
+ public readonly [smartdataSessionInvalidationAggregateBrand] = true;
35
+ }
36
+ interface ITransactionContext {
37
+ contextToken: symbol;
38
+ attempt: symbol;
39
+ }
40
+
41
+ interface ISmartdataSessionAcquireOptions {
42
+ ordinaryWrite?: boolean;
43
+ }
44
+
45
+ interface IOrdinarySessionOperationOptions {
46
+ ordinaryWrite?: boolean;
47
+ prepared?: boolean;
48
+ preparationMessage?: string;
49
+ }
50
+
51
+ const transactionContext = new plugins.nodeAsyncHooks.AsyncLocalStorage<
52
+ ITransactionContext
53
+ >();
54
+
55
+ const resolveBinding = (
56
+ sessionArg: SmartdataSession,
57
+ ownerArg?: SmartdataDb,
58
+ ): ISmartdataSessionBinding => {
59
+ const binding = bindings.get(sessionArg);
60
+ if (!binding || binding.invalidated || binding.rawSession.hasEnded) {
61
+ throw new SmartdataPersistenceError(
62
+ 'unsupported_operation',
63
+ 'The SmartData session has ended or is no longer valid.',
64
+ );
65
+ }
66
+ if (ownerArg && binding.owner !== ownerArg) {
67
+ throw new SmartdataPersistenceError(
68
+ 'invalid_argument',
69
+ 'The SmartData session belongs to another database.',
70
+ );
71
+ }
72
+ if (
73
+ binding.owner.status !== 'connected'
74
+ || binding.owner.mongoDbClient !== binding.client
75
+ ) {
76
+ throw new SmartdataPersistenceError(
77
+ 'unsupported_operation',
78
+ 'The SmartData session belongs to a stale database lifecycle.',
79
+ );
80
+ }
81
+ return binding;
82
+ };
83
+
84
+ const assertTransactionSucceeded = (bindingArg: ISmartdataSessionBinding): void => {
85
+ if (bindingArg.transactionFailure) {
86
+ throw bindingArg.transactionFailure.error;
87
+ }
88
+ };
89
+
90
+ export class SmartdataSession {
91
+ public async withTransaction<TResult>(
92
+ callbackArg: (sessionArg: SmartdataSession) => Promise<TResult>,
93
+ ): Promise<TResult | undefined> {
94
+ if (typeof callbackArg !== 'function') {
95
+ throw new SmartdataPersistenceError(
96
+ 'invalid_argument',
97
+ 'SmartData transaction callback must be a function.',
98
+ );
99
+ }
100
+ const binding = resolveBinding(this);
101
+ if (binding.operationActive || binding.transactionActive) {
102
+ throw new SmartdataPersistenceError(
103
+ 'unsupported_operation',
104
+ 'Parallel or nested SmartData session operations are not supported.',
105
+ );
106
+ }
107
+ binding.transactionActive = true;
108
+ let callbackError: unknown;
109
+ try {
110
+ return await binding.rawSession.withTransaction(async () => {
111
+ const attempt = Symbol('smartdataTransactionAttempt');
112
+ binding.transactionAttempt = attempt;
113
+ binding.transactionCallbackOpen = true;
114
+ binding.transactionUsesOrdinaryWrite = false;
115
+ binding.transactionFailure = undefined;
116
+ callbackError = undefined;
117
+ try {
118
+ let callbackReturned = false;
119
+ try {
120
+ const result = await transactionContext.run(
121
+ { contextToken: binding.contextToken, attempt },
122
+ async () => callbackArg(this),
123
+ );
124
+ callbackReturned = true;
125
+ return result;
126
+ } finally {
127
+ const activeOperation = binding.operationActive
128
+ ? binding.operationSettled
129
+ : undefined;
130
+ binding.transactionCallbackOpen = false;
131
+ if (binding.transactionAttempt === attempt) {
132
+ binding.transactionAttempt = undefined;
133
+ }
134
+ if (activeOperation) {
135
+ await activeOperation;
136
+ }
137
+ assertTransactionSucceeded(binding);
138
+ if (callbackReturned && activeOperation) {
139
+ throw new SmartdataPersistenceError(
140
+ 'unsupported_operation',
141
+ 'A SmartData transaction callback returned with an operation still active.',
142
+ );
143
+ }
144
+ }
145
+ } catch (errorArg) {
146
+ callbackError = errorArg;
147
+ throw errorArg;
148
+ }
149
+ });
150
+ } catch (errorArg) {
151
+ if (errorArg === callbackError) {
152
+ throw errorArg;
153
+ }
154
+ if (binding.transactionUsesOrdinaryWrite) {
155
+ return normalizeOrdinaryPersistenceError(
156
+ errorArg,
157
+ 'SmartData transaction conflicts with a unique index.',
158
+ );
159
+ }
160
+ throw errorArg;
161
+ } finally {
162
+ binding.transactionActive = false;
163
+ binding.transactionUsesOrdinaryWrite = false;
164
+ binding.transactionFailure = undefined;
165
+ }
166
+ }
167
+
168
+ public async close(): Promise<void> {
169
+ const binding = bindings.get(this);
170
+ if (!binding) {
171
+ return;
172
+ }
173
+ if (binding.operationActive || binding.transactionActive) {
174
+ throw new SmartdataPersistenceError(
175
+ 'unsupported_operation',
176
+ 'Cannot close a SmartData session while an operation is active.',
177
+ );
178
+ }
179
+ await closeSessionBinding(binding);
180
+ }
181
+ }
182
+
183
+ export const createSmartdataSession = (ownerArg: SmartdataDb): SmartdataSession => {
184
+ if (ownerArg.status !== 'connected' || !ownerArg.mongoDbClient) {
185
+ throw new SmartdataPersistenceError(
186
+ 'unsupported_operation',
187
+ 'Cannot create a SmartData session before the database is connected.',
188
+ );
189
+ }
190
+ const session = new SmartdataSession();
191
+ const binding: ISmartdataSessionBinding = {
192
+ session,
193
+ contextToken: Symbol('smartdataSessionContext'),
194
+ owner: ownerArg,
195
+ client: ownerArg.mongoDbClient,
196
+ rawSession: ownerArg.mongoDbClient.startSession(),
197
+ operationActive: false,
198
+ transactionActive: false,
199
+ transactionCallbackOpen: false,
200
+ transactionUsesOrdinaryWrite: false,
201
+ invalidated: false,
202
+ };
203
+ bindings.set(session, binding);
204
+ let ownerBindings = bindingsByOwner.get(ownerArg);
205
+ if (!ownerBindings) {
206
+ ownerBindings = new Set();
207
+ bindingsByOwner.set(ownerArg, ownerBindings);
208
+ }
209
+ ownerBindings.add(binding);
210
+ return session;
211
+ };
212
+
213
+ export const acquireSmartdataSession = (
214
+ sessionArg: SmartdataSession,
215
+ ownerArg: SmartdataDb,
216
+ optionsArg: ISmartdataSessionAcquireOptions = {},
217
+ ): {
218
+ rawSession: plugins.mongodb.ClientSession;
219
+ release: () => void;
220
+ } => {
221
+ const binding = resolveBinding(sessionArg, ownerArg);
222
+ if (binding.transactionFailure) {
223
+ throw new SmartdataPersistenceError(
224
+ 'unsupported_operation',
225
+ 'The SmartData transaction attempt has aborted and cannot accept further operations.',
226
+ );
227
+ }
228
+ const currentContext = transactionContext.getStore();
229
+ const isOwnedTransactionContext =
230
+ currentContext?.contextToken === binding.contextToken;
231
+ if (
232
+ binding.operationActive
233
+ || (
234
+ binding.transactionActive
235
+ && (
236
+ !binding.transactionCallbackOpen
237
+ || !isOwnedTransactionContext
238
+ || currentContext.attempt !== binding.transactionAttempt
239
+ )
240
+ )
241
+ || (!binding.transactionActive && isOwnedTransactionContext)
242
+ ) {
243
+ throw new SmartdataPersistenceError(
244
+ 'unsupported_operation',
245
+ 'Parallel SmartData session operations are not supported.',
246
+ );
247
+ }
248
+ if (optionsArg.ordinaryWrite && binding.transactionActive) {
249
+ binding.transactionUsesOrdinaryWrite = true;
250
+ }
251
+ binding.operationActive = true;
252
+ binding.operationSettled = new Promise<void>((resolveArg) => {
253
+ binding.resolveOperationSettled = resolveArg;
254
+ });
255
+ let released = false;
256
+ return {
257
+ rawSession: binding.rawSession,
258
+ release: () => {
259
+ if (!released) {
260
+ released = true;
261
+ binding.operationActive = false;
262
+ binding.resolveOperationSettled?.();
263
+ binding.operationSettled = undefined;
264
+ binding.resolveOperationSettled = undefined;
265
+ }
266
+ },
267
+ };
268
+ };
269
+
270
+ /** Marks the current owned attempt for rollback by withTransaction. */
271
+ export const failSmartdataTransaction = (
272
+ sessionArg: SmartdataSession,
273
+ ownerArg: SmartdataDb,
274
+ errorArg: unknown,
275
+ ): never => {
276
+ const binding = resolveBinding(sessionArg, ownerArg);
277
+ const context = transactionContext.getStore();
278
+ if (
279
+ !binding.operationActive
280
+ || !binding.transactionActive
281
+ || context?.contextToken !== binding.contextToken
282
+ || context.attempt !== binding.transactionAttempt
283
+ ) {
284
+ throw new SmartdataPersistenceError(
285
+ 'unsupported_operation',
286
+ 'Only the active owned operation can abort a SmartData transaction attempt.',
287
+ );
288
+ }
289
+ binding.transactionFailure ??= { error: errorArg };
290
+ throw binding.transactionFailure.error;
291
+ };
292
+
293
+ export type TSmartdataOrdinarySession =
294
+ | plugins.mongodb.ClientSession
295
+ | SmartdataSession;
296
+
297
+ export const isSmartdataSession = (
298
+ sessionArg: TSmartdataOrdinarySession | undefined,
299
+ ): sessionArg is SmartdataSession => sessionArg instanceof SmartdataSession;
300
+
301
+ export const isOrdinarySessionInTransaction = (
302
+ sessionArg: TSmartdataOrdinarySession | undefined,
303
+ ): boolean => {
304
+ if (!sessionArg) {
305
+ return false;
306
+ }
307
+ if (isSmartdataSession(sessionArg)) {
308
+ return resolveBinding(sessionArg).transactionActive;
309
+ }
310
+ return sessionArg.inTransaction();
311
+ };
312
+
313
+ export const runWithOrdinarySmartdataSession = async <TResult>(
314
+ sessionArg: TSmartdataOrdinarySession | undefined,
315
+ ownerArg: SmartdataDb,
316
+ optionsArg: IOrdinarySessionOperationOptions,
317
+ operationArg: (
318
+ rawSessionArg: plugins.mongodb.ClientSession | undefined,
319
+ ) => Promise<TResult>,
320
+ ): Promise<TResult> => {
321
+ if (!isSmartdataSession(sessionArg)) {
322
+ return operationArg(sessionArg);
323
+ }
324
+ if (!optionsArg.prepared) {
325
+ throw new SmartdataPersistenceError(
326
+ 'unsupported_operation',
327
+ optionsArg.preparationMessage ||
328
+ 'Initialize every participating SmartData model before using an owned session.',
329
+ );
330
+ }
331
+ const lease = acquireSmartdataSession(sessionArg, ownerArg, {
332
+ ordinaryWrite: optionsArg.ordinaryWrite,
333
+ });
334
+ try {
335
+ return await operationArg(lease.rawSession);
336
+ } finally {
337
+ lease.release();
338
+ }
339
+ };
340
+
341
+ export const invalidateSmartdataSessions = async (
342
+ ownerArg: SmartdataDb,
343
+ ): Promise<void> => {
344
+ const ownerBindings = bindingsByOwner.get(ownerArg);
345
+ if (!ownerBindings) {
346
+ return;
347
+ }
348
+ const sessions = [...ownerBindings];
349
+ for (const bindingArg of sessions) {
350
+ bindingArg.invalidated = true;
351
+ }
352
+ const results = await Promise.allSettled(
353
+ sessions.map((bindingArg) =>
354
+ closeSessionBinding(bindingArg),
355
+ ),
356
+ );
357
+ const errors = results.flatMap((resultArg) =>
358
+ resultArg.status === 'rejected' ? [resultArg.reason] : [],
359
+ );
360
+ if (errors.length === 1) {
361
+ throw errors[0];
362
+ }
363
+ if (errors.length > 1) {
364
+ throw new SmartdataSessionInvalidationAggregateError(
365
+ errors,
366
+ 'Multiple SmartData sessions failed to close.',
367
+ );
368
+ }
369
+ };
370
+
371
+ /** Failed cleanup remains owned and invalidated until an explicit retry succeeds. */
372
+ const closeSessionBinding = async (bindingArg: ISmartdataSessionBinding): Promise<void> => {
373
+ if (bindingArg.closing) return bindingArg.closing;
374
+ bindingArg.invalidated = true;
375
+ const closing = Promise.resolve().then(async () => {
376
+ await bindingArg.rawSession.endSession();
377
+ bindingsByOwner.get(bindingArg.owner)?.delete(bindingArg);
378
+ bindings.delete(bindingArg.session);
379
+ });
380
+ bindingArg.closing = closing;
381
+ try { await closing; } finally { bindingArg.closing = undefined; }
382
+ };
383
+
384
+ export const unwrapSmartdataSessionInvalidationErrors = (
385
+ errorArg: unknown,
386
+ ): readonly unknown[] | undefined =>
387
+ errorArg instanceof SmartdataSessionInvalidationAggregateError
388
+ ? errorArg.errors
389
+ : undefined;
@@ -0,0 +1,151 @@
1
+ import * as plugins from './plugins.js';
2
+ import type { SmartdataDb } from './classes.db.js';
3
+ import { convertFilterForMongoDb } from './classes.doc.js';
4
+ import { SmartdataPersistenceError } from './classes.persistence.js';
5
+ import { runCursorOperation } from './classes.cursoroperation.js';
6
+ import { acquireSmartdataSession, isSmartdataSession, type SmartdataSession } from './classes.session.js';
7
+ import {
8
+ collectionTopologyDbInspectionSymbol, collectionModelSchemaResolverSymbol,
9
+ } from './classes.collectiontopology.js';
10
+ import type { INormalizedCollectionModelSchema } from './classes.collection.js';
11
+ import { validateOrdinaryStoredDocument } from './classes.ordinarypersistence.js';
12
+
13
+ export interface ISmartdataStoredInspectionOptions {
14
+ filter?: Record<string, unknown>;
15
+ /** Default 100, maximum 10000. Results are ordered by immutable _id. */
16
+ limit?: number;
17
+ skip?: number;
18
+ /** Default 4 MiB, maximum 64 MiB; overflow rejects the entire page. */
19
+ maxBytes?: number;
20
+ /** Default 5000ms, maximum 120000ms. */
21
+ timeoutMS?: number;
22
+ signal?: AbortSignal;
23
+ session?: SmartdataSession;
24
+ }
25
+
26
+ export interface ISmartdataStoredInspection {
27
+ readonly documents: readonly Readonly<Record<string, unknown>>[];
28
+ readonly hasMore: boolean;
29
+ }
30
+
31
+ export type TSmartdataOrdinaryDocumentEvidence =
32
+ | { readonly status: 'valid' }
33
+ | { readonly status: 'invalid'; readonly code: 'invalid_document' }
34
+ | { readonly status: 'not_configured' };
35
+
36
+ export interface ISmartdataStoredDocumentEvidence {
37
+ /** Untouched stored BSON for diagnosis, never an authorized application record. */
38
+ readonly document: Readonly<Record<string, unknown>>;
39
+ /** Reports only the model's ordinary persistence assertion, not exact/domain assertions. */
40
+ readonly ordinaryValidation: TSmartdataOrdinaryDocumentEvidence;
41
+ }
42
+
43
+ export interface ISmartdataStoredEvidenceInspection {
44
+ readonly evidence: readonly ISmartdataStoredDocumentEvidence[];
45
+ readonly hasMore: boolean;
46
+ }
47
+
48
+ /** No model binding, index initialization, hydration, or raw handle escapes. */
49
+ const readStoredDocuments = async (
50
+ modelArg: any,
51
+ dbArg: SmartdataDb,
52
+ opts: ISmartdataStoredInspectionOptions = {},
53
+ ): Promise<{
54
+ schema: INormalizedCollectionModelSchema;
55
+ page: { documents: plugins.mongodb.Document[]; hasMore: boolean };
56
+ }> => {
57
+ if (!opts || Object.getPrototypeOf(opts) !== Object.prototype ||
58
+ Reflect.ownKeys(opts).some((keyArg) => typeof keyArg !== 'string' ||
59
+ !['filter', 'limit', 'skip', 'maxBytes', 'timeoutMS', 'signal', 'session'].includes(keyArg) ||
60
+ !('value' in Object.getOwnPropertyDescriptor(opts, keyArg)!))) {
61
+ throw new SmartdataPersistenceError('invalid_argument', 'Invalid stored inspection options.');
62
+ }
63
+ const limit = opts.limit === undefined ? 100 : opts.limit;
64
+ const skip = opts.skip === undefined ? 0 : opts.skip;
65
+ const maxBytes = opts.maxBytes === undefined ? 4 * 1024 * 1024 : opts.maxBytes;
66
+ const timeoutMS = opts.timeoutMS === undefined ? 5_000 : opts.timeoutMS;
67
+ if (!Number.isSafeInteger(limit) || limit < 1 || limit > 10_000 ||
68
+ !Number.isSafeInteger(skip) || skip < 0 || skip > 2_147_483_647 ||
69
+ !Number.isSafeInteger(maxBytes) || maxBytes < 1 || maxBytes > 64 * 1024 * 1024 ||
70
+ !Number.isSafeInteger(timeoutMS) || timeoutMS < 1 || timeoutMS > 120_000 ||
71
+ (opts.signal !== undefined && !(opts.signal instanceof AbortSignal)) ||
72
+ (opts.session !== undefined && !isSmartdataSession(opts.session))) {
73
+ throw new SmartdataPersistenceError('invalid_argument', 'Invalid stored inspection bounds or context.');
74
+ }
75
+ opts.signal?.throwIfAborted();
76
+ const schema: INormalizedCollectionModelSchema = modelArg[collectionModelSchemaResolverSymbol]();
77
+ const filter = convertFilterForMongoDb(opts.filter || {});
78
+ const lease = opts.session ? acquireSmartdataSession(opts.session, dbArg) : undefined;
79
+ let page: { documents: plugins.mongodb.Document[]; hasMore: boolean };
80
+ try {
81
+ page = await dbArg[collectionTopologyDbInspectionSymbol](async (mongoDbArg) => {
82
+ const cursor = mongoDbArg.collection(schema.collectionName).find(filter, {
83
+ sort: { _id: 1 }, skip, limit: limit + 1, batchSize: 1,
84
+ timeoutMS, timeoutMode: 'cursorLifetime', signal: opts.signal, session: lease?.rawSession,
85
+ });
86
+ return runCursorOperation(cursor, async (cursorArg) => {
87
+ const documents: plugins.mongodb.Document[] = [];
88
+ let bytes = 0;
89
+ while (true) {
90
+ const next = await cursorArg.next();
91
+ if (next === null) return { documents, hasMore: false };
92
+ if (documents.length === limit) return { documents, hasMore: true };
93
+ bytes += plugins.mongodb.BSON.calculateObjectSize(next);
94
+ if (bytes > maxBytes) throw new SmartdataPersistenceError('unsupported_operation',
95
+ 'Stored inspection exceeds its byte budget.');
96
+ documents.push(next);
97
+ }
98
+ }, opts.signal);
99
+ });
100
+ } catch (errorArg) {
101
+ opts.signal?.throwIfAborted();
102
+ throw errorArg;
103
+ } finally { lease?.release(); }
104
+ return { schema, page };
105
+ };
106
+
107
+ /** @internal Strict inspection used by Model.inspectStoredDocuments. */
108
+ export const inspectStoredDocuments = async (
109
+ modelArg: any,
110
+ dbArg: SmartdataDb,
111
+ opts: ISmartdataStoredInspectionOptions = {},
112
+ ): Promise<ISmartdataStoredInspection> => {
113
+ const { schema, page } = await readStoredDocuments(modelArg, dbArg, opts);
114
+ // Full validation occurs on the untouched stored envelope, before any
115
+ // constructor/default/serializer can hide field-presence evidence.
116
+ const documents = schema.ordinaryPersistence
117
+ ? page.documents.map((documentArg) => validateOrdinaryStoredDocument(
118
+ documentArg, schema.ordinaryPersistence!, schema.persistedFields,
119
+ ))
120
+ : page.documents;
121
+ return Object.freeze({ documents: Object.freeze(documents), hasMore: page.hasMore });
122
+ };
123
+
124
+ /**
125
+ * @internal Diagnostic inspection preserves malformed evidence from the same
126
+ * model and bounds as strict inspection. Invalid records must never enter
127
+ * normal application flows. Exact and domain assertions remain caller-owned.
128
+ */
129
+ export const inspectStoredDocumentEvidence = async (
130
+ modelArg: any,
131
+ dbArg: SmartdataDb,
132
+ opts: ISmartdataStoredInspectionOptions = {},
133
+ ): Promise<ISmartdataStoredEvidenceInspection> => {
134
+ const { schema, page } = await readStoredDocuments(modelArg, dbArg, opts);
135
+ const evidence = page.documents.map((documentArg): ISmartdataStoredDocumentEvidence => {
136
+ let ordinaryValidation: TSmartdataOrdinaryDocumentEvidence = { status: 'not_configured' };
137
+ if (schema.ordinaryPersistence) {
138
+ try {
139
+ validateOrdinaryStoredDocument(documentArg, schema.ordinaryPersistence, schema.persistedFields);
140
+ ordinaryValidation = { status: 'valid' };
141
+ } catch (errorArg) {
142
+ if (!(errorArg instanceof SmartdataPersistenceError) || errorArg.code !== 'invalid_document') {
143
+ throw errorArg;
144
+ }
145
+ ordinaryValidation = { status: 'invalid', code: 'invalid_document' };
146
+ }
147
+ }
148
+ return Object.freeze({ document: documentArg, ordinaryValidation: Object.freeze(ordinaryValidation) });
149
+ });
150
+ return Object.freeze({ evidence: Object.freeze(evidence), hasMore: page.hasMore });
151
+ };