@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,87 @@
1
+ import * as plugins from './plugins.js';
2
+ import {
3
+ SmartdataCollection,
4
+ type INormalizedCollectionModelSchema,
5
+ } from './classes.collection.js';
6
+ import { SmartdataDb } from './classes.db.js';
7
+ import { SmartdataPersistenceError } from './classes.persistence.js';
8
+
9
+ /**
10
+ * Per-SmartdataDb collection cache.
11
+ *
12
+ * Historically this class keyed its cache by class name alone, which meant
13
+ * the first SmartdataDb to request a collection of a given class name
14
+ * "won" — every subsequent call from a different SmartdataDb instance
15
+ * received the cached collection bound to the first db. That silently
16
+ * broke multi-tenant SaaS apps (one db per tenant), tests instantiating
17
+ * multiple SmartdataDbs in sequence, and any in-process db cluster switch.
18
+ *
19
+ * The cache is now keyed by `(SmartdataDb instance, className)` using a
20
+ * WeakMap of db → Map<className, SmartdataCollection>. Entries are GC'd
21
+ * with their parent db automatically.
22
+ */
23
+ export class CollectionFactory {
24
+ private perDbCollections: WeakMap<
25
+ SmartdataDb,
26
+ Map<
27
+ string,
28
+ {
29
+ collection: SmartdataCollection<any>;
30
+ schema?: INormalizedCollectionModelSchema;
31
+ }
32
+ >
33
+ > = new WeakMap();
34
+
35
+ public getCollection = (
36
+ nameArg: string,
37
+ dbArg: SmartdataDb,
38
+ schemaArg?: INormalizedCollectionModelSchema,
39
+ ): SmartdataCollection<any> => {
40
+ if (!(dbArg instanceof SmartdataDb)) {
41
+ // Preserve the historical behavior of returning undefined-ish for
42
+ // non-db args. All in-repo callers already guard on instanceof
43
+ // before using the result (see classes.collection.ts).
44
+ return undefined as unknown as SmartdataCollection<any>;
45
+ }
46
+ let dbMap = this.perDbCollections.get(dbArg);
47
+ if (!dbMap) {
48
+ dbMap = new Map();
49
+ this.perDbCollections.set(dbArg, dbMap);
50
+ }
51
+ let entry = dbMap.get(nameArg);
52
+ if (!entry) {
53
+ const collection = new SmartdataCollection(nameArg, dbArg);
54
+ entry = { collection, schema: schemaArg };
55
+ if (schemaArg) {
56
+ collection.bindModelSchema(schemaArg);
57
+ }
58
+ dbMap.set(nameArg, entry);
59
+ } else if (schemaArg) {
60
+ if (
61
+ entry.schema &&
62
+ entry.schema.fingerprint !== schemaArg.fingerprint
63
+ ) {
64
+ throw new SmartdataPersistenceError(
65
+ 'invalid_configuration',
66
+ `Collection "${nameArg}" already has a divergent model schema in this SmartdataDb.`,
67
+ );
68
+ }
69
+ if (!entry.schema) {
70
+ entry.schema = schemaArg;
71
+ }
72
+ entry.collection.bindModelSchema(schemaArg);
73
+ }
74
+ return entry.collection;
75
+ };
76
+
77
+ /**
78
+ * @deprecated Internal back-compat shim. The previous field was a public
79
+ * Record<className, SmartdataCollection> but was not part of the
80
+ * documented public API. WeakMap is not iterable, so this getter returns
81
+ * an empty object — anyone actually relying on the old shape would get
82
+ * clean nothing rather than wrong-db data. Will be removed in 8.0.
83
+ */
84
+ public get collections(): { [key: string]: SmartdataCollection<any> } {
85
+ return {};
86
+ }
87
+ }
@@ -0,0 +1,24 @@
1
+ type TCollectionReconnectHandler = () => void;
2
+
3
+ const reconnectHandlersByCollection = new WeakMap<
4
+ object,
5
+ Set<TCollectionReconnectHandler>
6
+ >();
7
+
8
+ export const registerCollectionReconnectHandler = (
9
+ collectionArg: object,
10
+ handlerArg: TCollectionReconnectHandler,
11
+ ): void => {
12
+ let handlers = reconnectHandlersByCollection.get(collectionArg);
13
+ if (!handlers) {
14
+ handlers = new Set();
15
+ reconnectHandlersByCollection.set(collectionArg, handlers);
16
+ }
17
+ handlers.add(handlerArg);
18
+ };
19
+
20
+ export const notifyCollectionReconnect = (collectionArg: object): void => {
21
+ for (const handler of reconnectHandlersByCollection.get(collectionArg) || []) {
22
+ handler();
23
+ }
24
+ };
@@ -0,0 +1,164 @@
1
+ import type * as plugins from './plugins.js';
2
+ import type { INormalizedCollectionModelSchema } from './classes.collection.js';
3
+ import { SmartdataPersistenceError } from './classes.persistence.js';
4
+ import { readBoundedInspectionCursor } from './classes.namespaceinspection.js';
5
+ import {
6
+ getExpectedCollectionTopologyForSchema,
7
+ inspectCollectionTopologySnapshot,
8
+ } from './classes.collectiontopology.js';
9
+
10
+ export interface ISmartdataCollectionPreparationOptions {
11
+ /** Observe an exact topology, or install only missing declarations. */
12
+ mode: 'requireExact' | 'ensureCompatible';
13
+ /** Total budget including queueing and cursor cleanup: 1 through 120000 ms. */
14
+ timeoutMs: number;
15
+ signal?: AbortSignal;
16
+ }
17
+
18
+ /** @internal Validate before resolving a model manager or starting a deadline. */
19
+ export const validateCollectionPreparationOptions = (opts: ISmartdataCollectionPreparationOptions): void => {
20
+ if (!opts || Object.getPrototypeOf(opts) !== Object.prototype ||
21
+ Reflect.ownKeys(opts).some((keyArg) => typeof keyArg !== 'string' ||
22
+ !['mode', 'timeoutMs', 'signal'].includes(keyArg) ||
23
+ !('value' in Object.getOwnPropertyDescriptor(opts, keyArg)!)) ||
24
+ !['requireExact', 'ensureCompatible'].includes(opts.mode) ||
25
+ !Number.isSafeInteger(opts.timeoutMs) || opts.timeoutMs < 1 || opts.timeoutMs > 120_000 ||
26
+ (opts.signal !== undefined && !(opts.signal instanceof AbortSignal))) {
27
+ throw new SmartdataPersistenceError('invalid_argument', 'Invalid collection preparation options.');
28
+ }
29
+ opts.signal?.throwIfAborted();
30
+ };
31
+
32
+ /** @internal Owns the explicit preparation caller's deadline and queue wait. */
33
+ export class CollectionPreparationBudget {
34
+ public readonly signal: AbortSignal;
35
+ public readonly mode: ISmartdataCollectionPreparationOptions['mode'];
36
+ private readonly deadline = new AbortController();
37
+ private readonly expiresAt: number;
38
+ private readonly timer: ReturnType<typeof setTimeout>;
39
+
40
+ constructor(opts: ISmartdataCollectionPreparationOptions) {
41
+ validateCollectionPreparationOptions(opts);
42
+ this.mode = opts.mode;
43
+ this.expiresAt = performance.now() + opts.timeoutMs;
44
+ this.timer = setTimeout(() => this.expire(), opts.timeoutMs);
45
+ this.signal = opts.signal ? AbortSignal.any([opts.signal, this.deadline.signal]) : this.deadline.signal;
46
+ }
47
+
48
+ private expire(): void {
49
+ this.deadline.abort(new SmartdataPersistenceError(
50
+ 'unsupported_operation', 'Collection preparation deadline exceeded.',
51
+ ));
52
+ }
53
+
54
+ public check(): void {
55
+ if (performance.now() >= this.expiresAt) this.expire();
56
+ this.signal.throwIfAborted();
57
+ }
58
+
59
+ public operationOptions(): { timeoutMS: number } {
60
+ this.check();
61
+ return this.cleanupOptions();
62
+ }
63
+
64
+ /** Cleanup still runs after expiration; zero would disable the driver's timeout. */
65
+ public cleanupOptions(): { timeoutMS: number } {
66
+ return { timeoutMS: Math.max(1, Math.ceil(this.expiresAt - performance.now())) };
67
+ }
68
+
69
+ /** Cancellation detaches only this waiter, never the operation it is waiting for. */
70
+ public async waitFor(operationArg: Promise<void>): Promise<void> {
71
+ this.check();
72
+ await new Promise<void>((resolveArg, rejectArg) => {
73
+ const onAbort = () => { cleanup(); rejectArg(this.signal.reason); };
74
+ const cleanup = () => this.signal.removeEventListener('abort', onAbort);
75
+ this.signal.addEventListener('abort', onAbort, { once: true });
76
+ operationArg.then(
77
+ () => { cleanup(); resolveArg(); },
78
+ () => { cleanup(); resolveArg(); },
79
+ );
80
+ if (this.signal.aborted) onAbort();
81
+ });
82
+ this.check();
83
+ }
84
+
85
+ public close(): void { clearTimeout(this.timer); }
86
+ }
87
+
88
+ /** @internal No state is published until all commands and cleanup have settled. */
89
+ export const prepareBoundCollection = async (
90
+ dbArg: plugins.mongodb.Db,
91
+ schemaArg: INormalizedCollectionModelSchema,
92
+ budgetArg: CollectionPreparationBudget,
93
+ assertCurrentArg: () => void,
94
+ ): Promise<plugins.mongodb.Collection> => {
95
+ const expected = getExpectedCollectionTopologyForSchema(schemaArg);
96
+ const collection = dbArg.collection(schemaArg.collectionName);
97
+ const check = () => { budgetArg.check(); assertCurrentArg(); };
98
+ const inspect = async () => {
99
+ check();
100
+ const namespaces = await readBoundedInspectionCursor(dbArg.listCollections(
101
+ { name: schemaArg.collectionName },
102
+ { nameOnly: false, batchSize: 1, ...budgetArg.operationOptions(), signal: budgetArg.signal },
103
+ ), 1, budgetArg.signal, () => budgetArg.cleanupOptions());
104
+ check();
105
+ const namespace = namespaces[0] as unknown as Record<string, unknown> | undefined;
106
+ const indexes = namespace?.type === 'collection'
107
+ ? await readBoundedInspectionCursor(collection.listIndexes({
108
+ batchSize: 1, ...budgetArg.operationOptions(),
109
+ }), 64, budgetArg.signal, () => budgetArg.cleanupOptions())
110
+ : [];
111
+ check();
112
+ return inspectCollectionTopologySnapshot(expected, { namespace: namespace ?? null, indexes });
113
+ };
114
+ const requireAcceptable = (inspectionArg: Awaited<ReturnType<typeof inspect>>, exactArg: boolean) => {
115
+ if (inspectionArg.status === 'divergent' || (exactArg && inspectionArg.status !== 'exact')) {
116
+ throw new SmartdataPersistenceError('invalid_configuration',
117
+ `Collection "${schemaArg.collectionName}" requires ${exactArg ? 'exact' : 'compatible'} topology: ` +
118
+ `${inspectionArg.status}${inspectionArg.reasonCodes.length ? ` (${inspectionArg.reasonCodes.join(', ')})` : ''}.`);
119
+ }
120
+ };
121
+
122
+ let inspection = await inspect();
123
+ requireAcceptable(inspection, budgetArg.mode === 'requireExact');
124
+ if (budgetArg.mode === 'ensureCompatible' && inspection.status !== 'exact') {
125
+ if (inspection.status === 'absent') {
126
+ check();
127
+ try {
128
+ await dbArg.createCollection(schemaArg.collectionName, budgetArg.operationOptions());
129
+ } catch (errorArg) {
130
+ if ((errorArg as { code?: unknown })?.code !== 48) throw errorArg;
131
+ }
132
+ // A competing creator may have installed divergent options or indexes.
133
+ inspection = await inspect();
134
+ requireAcceptable(inspection, false);
135
+ if (inspection.status === 'absent') {
136
+ throw new SmartdataPersistenceError('unsupported_operation', 'Created collection is not observable.');
137
+ }
138
+ }
139
+ const existing = new Set(inspection.actualIndexes.map((indexArg) => indexArg.name));
140
+ for (const index of schemaArg.indexes) {
141
+ if (existing.has(index.name)) continue;
142
+ check();
143
+ try {
144
+ await collection.createIndex(Object.fromEntries(index.key), {
145
+ ...index.options, name: index.name, ...budgetArg.operationOptions(),
146
+ });
147
+ } catch (errorArg) {
148
+ // A rejected acknowledgement does not prove the index build failed.
149
+ // Continue only from compatible observed topology containing this exact
150
+ // declaration, within the same deadline and connection generation.
151
+ inspection = await inspect();
152
+ requireAcceptable(inspection, false);
153
+ if (!inspection.actualIndexes.some((actualArg) => actualArg.name === index.name)) {
154
+ throw errorArg;
155
+ }
156
+ for (const actual of inspection.actualIndexes) existing.add(actual.name);
157
+ }
158
+ }
159
+ inspection = await inspect();
160
+ requireAcceptable(inspection, true);
161
+ }
162
+ check();
163
+ return collection;
164
+ };