@ragestudio/scylla-odm 0.14.0 → 0.15.1

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 (186) hide show
  1. package/README.md +246 -0
  2. package/_virtual/_rolldown/runtime.js +1 -0
  3. package/client.d.ts +6 -5
  4. package/client.js +1 -117
  5. package/cql_gen/create_keyspace.d.ts +11 -0
  6. package/cql_gen/create_keyspace.js +1 -0
  7. package/cql_gen/create_table.d.ts +1 -2
  8. package/cql_gen/create_table.js +1 -38
  9. package/document/index.d.ts +3 -4
  10. package/document/index.js +1 -63
  11. package/driver/index.d.ts +304 -0
  12. package/driver/index.js +1 -0
  13. package/driver/lib/auth/base-dse-authenticator.js +1 -0
  14. package/driver/lib/auth/dse-gssapi-auth-provider.js +1 -0
  15. package/driver/lib/auth/dse-plain-text-auth-provider.js +1 -0
  16. package/driver/lib/auth/gssapi-client.js +1 -0
  17. package/driver/lib/auth/index.d.ts +46 -0
  18. package/driver/lib/auth/index.js +1 -0
  19. package/driver/lib/auth/no-auth-provider.js +1 -0
  20. package/driver/lib/auth/plain-text-auth-provider.js +1 -0
  21. package/driver/lib/auth/provider.js +1 -0
  22. package/driver/lib/client-options.js +1 -0
  23. package/driver/lib/client.js +1 -0
  24. package/driver/lib/concurrent/index.d.ts +25 -0
  25. package/driver/lib/concurrent/index.js +1 -0
  26. package/driver/lib/connection.js +1 -0
  27. package/driver/lib/control-connection.js +1 -0
  28. package/driver/lib/datastax/graph/complex-type-helper.js +1 -0
  29. package/driver/lib/datastax/graph/custom-type-serializers.js +1 -0
  30. package/driver/lib/datastax/graph/graph-executor.js +1 -0
  31. package/driver/lib/datastax/graph/graph-serializer.js +1 -0
  32. package/driver/lib/datastax/graph/index.d.ts +63 -0
  33. package/driver/lib/datastax/graph/index.js +1 -0
  34. package/driver/lib/datastax/graph/options.js +1 -0
  35. package/driver/lib/datastax/graph/result-set.js +1 -0
  36. package/driver/lib/datastax/graph/structure.js +1 -0
  37. package/driver/lib/datastax/graph/type-serializers.js +1 -0
  38. package/driver/lib/datastax/graph/wrappers.js +1 -0
  39. package/driver/lib/datastax/index.d.ts +7 -0
  40. package/driver/lib/datastax/search/date-range.js +1 -0
  41. package/driver/lib/datastax/search/index.d.ts +49 -0
  42. package/driver/lib/datastax/search/index.js +1 -0
  43. package/driver/lib/encoder.js +1 -0
  44. package/driver/lib/errors.js +1 -0
  45. package/driver/lib/execution-options.js +1 -0
  46. package/driver/lib/execution-profile.js +1 -0
  47. package/driver/lib/geometry/geometry.js +1 -0
  48. package/driver/lib/geometry/index.d.ts +49 -0
  49. package/driver/lib/geometry/index.js +1 -0
  50. package/driver/lib/geometry/line-string.js +1 -0
  51. package/driver/lib/geometry/point.js +1 -0
  52. package/driver/lib/geometry/polygon.js +1 -0
  53. package/driver/lib/host-connection-pool.js +1 -0
  54. package/driver/lib/host.js +1 -0
  55. package/driver/lib/insights-client.js +1 -0
  56. package/driver/lib/mapping/cache.js +1 -0
  57. package/driver/lib/mapping/doc-info-adapter.js +1 -0
  58. package/driver/lib/mapping/index.d.ts +142 -0
  59. package/driver/lib/mapping/index.js +1 -0
  60. package/driver/lib/mapping/mapper.js +1 -0
  61. package/driver/lib/mapping/mapping-handler.js +1 -0
  62. package/driver/lib/mapping/model-batch-item.js +1 -0
  63. package/driver/lib/mapping/model-batch-mapper.js +1 -0
  64. package/driver/lib/mapping/model-mapper.js +1 -0
  65. package/driver/lib/mapping/model-mapping-info.js +1 -0
  66. package/driver/lib/mapping/object-selector.js +1 -0
  67. package/driver/lib/mapping/q.js +1 -0
  68. package/driver/lib/mapping/query-generator.js +9 -0
  69. package/driver/lib/mapping/result-mapper.js +4 -0
  70. package/driver/lib/mapping/result.js +1 -0
  71. package/driver/lib/mapping/table-mappings.js +1 -0
  72. package/driver/lib/mapping/tree.js +1 -0
  73. package/driver/lib/metadata/aggregate.js +1 -0
  74. package/driver/lib/metadata/client-state.js +1 -0
  75. package/driver/lib/metadata/data-collection.js +1 -0
  76. package/driver/lib/metadata/event-debouncer.js +1 -0
  77. package/driver/lib/metadata/index.d.ts +184 -0
  78. package/driver/lib/metadata/index.js +1 -0
  79. package/driver/lib/metadata/materialized-view.js +1 -0
  80. package/driver/lib/metadata/schema-function.js +1 -0
  81. package/driver/lib/metadata/schema-index.js +1 -0
  82. package/driver/lib/metadata/schema-parser.js +1 -0
  83. package/driver/lib/metadata/table-metadata.js +1 -0
  84. package/driver/lib/metrics/client-metrics.js +1 -0
  85. package/driver/lib/metrics/default-metrics.js +1 -0
  86. package/driver/lib/metrics/index.d.ts +44 -0
  87. package/driver/lib/metrics/index.js +1 -0
  88. package/driver/lib/operation-state.js +1 -0
  89. package/driver/lib/policies/address-resolution.js +1 -0
  90. package/driver/lib/policies/index.d.ts +135 -0
  91. package/driver/lib/policies/index.js +1 -0
  92. package/driver/lib/policies/load-balancing.js +1 -0
  93. package/driver/lib/policies/reconnection.js +1 -0
  94. package/driver/lib/policies/retry.js +1 -0
  95. package/driver/lib/policies/speculative-execution.js +1 -0
  96. package/driver/lib/policies/timestamp-generation.js +1 -0
  97. package/driver/lib/prepare-handler.js +1 -0
  98. package/driver/lib/promise-utils.js +1 -0
  99. package/driver/lib/readers.js +1 -0
  100. package/driver/lib/request-execution.js +1 -0
  101. package/driver/lib/request-handler.js +1 -0
  102. package/driver/lib/requests.js +1 -0
  103. package/driver/lib/stream-id-stack.js +1 -0
  104. package/driver/lib/streams.js +1 -0
  105. package/driver/lib/token.js +1 -0
  106. package/driver/lib/tokenizer.js +1 -0
  107. package/driver/lib/tracker/index.d.ts +45 -0
  108. package/driver/lib/tracker/index.js +1 -0
  109. package/driver/lib/tracker/request-logger.js +1 -0
  110. package/driver/lib/tracker/request-tracker.js +1 -0
  111. package/driver/lib/types/big-decimal.js +1 -0
  112. package/driver/lib/types/duration.js +1 -0
  113. package/driver/lib/types/index.d.ts +320 -0
  114. package/driver/lib/types/index.js +1 -0
  115. package/driver/lib/types/inet-address.js +1 -0
  116. package/driver/lib/types/integer.js +1 -0
  117. package/driver/lib/types/local-date.js +1 -0
  118. package/driver/lib/types/local-time.js +1 -0
  119. package/driver/lib/types/mutable-long.js +1 -0
  120. package/driver/lib/types/protocol-version.js +1 -0
  121. package/driver/lib/types/result-set.js +1 -0
  122. package/driver/lib/types/result-stream.js +1 -0
  123. package/driver/lib/types/row.js +1 -0
  124. package/driver/lib/types/time-uuid.js +1 -0
  125. package/driver/lib/types/tuple.js +1 -0
  126. package/driver/lib/types/uuid.js +1 -0
  127. package/driver/lib/types/vector.js +1 -0
  128. package/driver/lib/types/version-number.js +1 -0
  129. package/driver/lib/utils.js +4 -0
  130. package/driver/lib/writers.js +1 -0
  131. package/index.js +1 -11
  132. package/logger/index.d.ts +9 -0
  133. package/logger/index.js +1 -0
  134. package/model/index.d.ts +2 -3
  135. package/model/index.js +1 -44
  136. package/operations/countAll.d.ts +1 -2
  137. package/operations/countAll.js +1 -16
  138. package/operations/delete.d.ts +3 -4
  139. package/operations/delete.js +1 -11
  140. package/operations/find.d.ts +1 -2
  141. package/operations/find.js +1 -22
  142. package/operations/findOne.d.ts +1 -2
  143. package/operations/findOne.js +1 -17
  144. package/operations/sync.d.ts +1 -2
  145. package/operations/sync.js +1 -16
  146. package/operations/tableExists.d.ts +1 -2
  147. package/operations/tableExists.js +2 -15
  148. package/operations/update.d.ts +1 -2
  149. package/operations/update.js +1 -16
  150. package/package.js +1 -0
  151. package/package.json +7 -4
  152. package/schema/index.d.ts +1 -2
  153. package/schema/index.js +1 -16
  154. package/types.d.ts +3 -3
  155. package/types.js +1 -34
  156. package/utils/buildMapper.d.ts +1 -2
  157. package/utils/buildMapper.js +1 -13
  158. package/utils/delay.d.ts +4 -0
  159. package/utils/delay.js +1 -0
  160. package/utils/fillDefaults.d.ts +1 -2
  161. package/utils/fillDefaults.js +1 -18
  162. package/utils/loadModels.d.ts +1 -2
  163. package/utils/loadModels.js +1 -30
  164. package/utils/queryParser.d.ts +1 -2
  165. package/utils/queryParser.js +1 -94
  166. package/utils/typeChecker.d.ts +1 -2
  167. package/utils/typeChecker.js +1 -64
  168. package/client.js.map +0 -1
  169. package/cql_gen/create_table.js.map +0 -1
  170. package/document/index.js.map +0 -1
  171. package/index.js.map +0 -1
  172. package/model/index.js.map +0 -1
  173. package/operations/countAll.js.map +0 -1
  174. package/operations/delete.js.map +0 -1
  175. package/operations/find.js.map +0 -1
  176. package/operations/findOne.js.map +0 -1
  177. package/operations/sync.js.map +0 -1
  178. package/operations/tableExists.js.map +0 -1
  179. package/operations/update.js.map +0 -1
  180. package/schema/index.js.map +0 -1
  181. package/types.js.map +0 -1
  182. package/utils/buildMapper.js.map +0 -1
  183. package/utils/fillDefaults.js.map +0 -1
  184. package/utils/loadModels.js.map +0 -1
  185. package/utils/queryParser.js.map +0 -1
  186. package/utils/typeChecker.js.map +0 -1
package/README.md ADDED
@@ -0,0 +1,246 @@
1
+ # @ragestudio/scylla-odm
2
+
3
+ > A lightweight ODM for ScyllaDB and Cassandra.
4
+
5
+ > **Warning:** under active development, not production-ready.
6
+
7
+ ---
8
+
9
+ ## why this exists
10
+
11
+ Most of (not existent) Node.js ODMs for ScyllaDB/Cassandra rely on the official `cassandra-driver`.
12
+
13
+ The official `cassandra-driver` ships a built-in mapper, but it has no
14
+ TypeScript support or a friendly interface.
15
+ You define your columns with plain strings and get
16
+ plain objects back — no autocomplete, no type checking, no safety net.
17
+
18
+ This ODM is built on top of a modified `cassandra-driver` and adds:
19
+
20
+ - **Type-safe schemas** — your column types are inferred, so `find`,
21
+ `create`, and `save` all know the shape of your data
22
+ - **ODM Methods** — easier & friendly API for interacting with your data
23
+ - **Full TypeScript support** — autocomplete, type checking, and inference
24
+ from schema to model to document
25
+ - **MongoDB-style query operators** — `$gt`, `$in`, `$lte`, etc., all typed
26
+
27
+ We also stripped out everything you probably never use (DataStax Insights,
28
+ metrics, heavy dependencies, and other services) and applied a bunch of performance patches on
29
+ top of the driver itself.
30
+
31
+ The result is a leaner driver with less overhead, fewer timers, and better
32
+ memory usage.
33
+
34
+ ---
35
+
36
+ ## install
37
+
38
+ ```bash
39
+ npm install @ragestudio/scylla-odm
40
+ ```
41
+
42
+ ---
43
+
44
+ ## quick start
45
+
46
+ ```ts
47
+ import Client, { Schema, Model, ColumnTypes } from "@ragestudio/scylla-odm";
48
+ import type { Column } from "@ragestudio/scylla-odm/types";
49
+
50
+ // 1. define a schema
51
+ const userSchema = new Schema(
52
+ {
53
+ table_name: "users",
54
+ keys: ["id"], // partition key and clustering keys
55
+ },
56
+ {
57
+ id: { type: ColumnTypes.Int, required: true } as Column<number>,
58
+ name: { type: ColumnTypes.Text, required: true } as Column<string>,
59
+ email: { type: ColumnTypes.Text, required: true } as Column<string>,
60
+ age: { type: ColumnTypes.Int } as Column<number>,
61
+ },
62
+ );
63
+
64
+ // 2. create a model
65
+ const User = new Model("User", userSchema);
66
+
67
+ // 3. create a client
68
+ const client = new Client({
69
+ contactPoints: ["127.0.0.1"],
70
+ localDataCenter: "datacenter1",
71
+ keyspace: "myapp",
72
+ });
73
+
74
+ // 4. initialize and connect
75
+ await client.initialize();
76
+
77
+ // 4. create and save documents
78
+ const user = User.create({
79
+ id: 1234,
80
+ name: "Alice",
81
+ email: "alice@example.com",
82
+ age: 30,
83
+ });
84
+
85
+ await user.save();
86
+
87
+ // 5. query
88
+ const found = await User.findOne({ id: 1234 });
89
+ console.log(found?.name); // "Alice"
90
+
91
+ // 6. shutdown
92
+ await client.shutdown();
93
+ ```
94
+
95
+ ---
96
+
97
+ ## schema
98
+
99
+ A schema describes a table: its name, primary keys, and columns.
100
+
101
+ ```ts
102
+ const productSchema = new Schema(
103
+ {
104
+ table_name: "products",
105
+ keys: [["category"], "id"], // compound partition key
106
+ clustering_order: { created_at: "desc" }, // optional
107
+ },
108
+ {
109
+ category: { type: ColumnTypes.Text, required: true } as Column<string>,
110
+ id: { type: ColumnTypes.Uuid, required: true } as Column<string>,
111
+ title: { type: ColumnTypes.Text, required: true } as Column<string>,
112
+ price: { type: ColumnTypes.Double, required: true } as Column<number>,
113
+ created_at: { type: ColumnTypes.Timestamp } as Column<Date>,
114
+ },
115
+ );
116
+ ```
117
+
118
+ ---
119
+
120
+ ## model
121
+
122
+ A model wraps a schema and provides the query interface.
123
+
124
+ ```ts
125
+ const Product = new Model("Product", productSchema);
126
+ ```
127
+
128
+ ### methods
129
+
130
+ | method | description |
131
+ | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
132
+ | `create(data)` / `obj()` | create a new document (not persisted yet) |
133
+ | `find(query, options?)` | find documents matching the query, returns an array of documents |
134
+ | `findOne(query, options?)` | find a single document, if no document is found, returns `null`. Due the mapper limitation, note that all partition and clustering keys must be defined in order to use this method. |
135
+ | |
136
+ | `update(query)` | update documents matching the query |
137
+ | `delete(query)` | delete documents matching the query |
138
+ | `countAll()` | count all documents in the table |
139
+
140
+ ---
141
+
142
+ ## document
143
+
144
+ Documents are the objects you work with. They carry the data and a few helpers.
145
+
146
+ ```ts
147
+ const product = Product.create({
148
+ category: "books",
149
+ id: uuid(),
150
+ title: "ODM 101",
151
+ price: 29.99,
152
+ });
153
+
154
+ await product.save(); // persist to ScyllaDB
155
+ await product.delete(); // delete from ScyllaDB
156
+ product.toRaw(); // plain object without internal fields
157
+ product.getChangedFields(original); // diff between current and original data
158
+ ```
159
+
160
+ ---
161
+
162
+ ## query operators
163
+
164
+ Use MongoDB-style operators in your queries:
165
+
166
+ ```ts
167
+ // exact match
168
+ await User.find({ name: "Alice" });
169
+
170
+ // comparison operators
171
+ await Product.find({ price: { $gt: 10, $lte: 50 } });
172
+
173
+ // list matching
174
+ await User.find({ age: { $in: [25, 30, 35] } });
175
+
176
+ // sorting
177
+ await Product.find({ category: "books" }, { $orderby: { created_at: "desc" } });
178
+
179
+ // raw results (skip document wrapping)
180
+ const raw = await Product.find({ category: "books" }, { raw: true });
181
+ ```
182
+
183
+ ---
184
+
185
+ ## configuration
186
+
187
+ ```ts
188
+ const client = new Client({
189
+ modelsPath: "./db",
190
+ contactPoints: ["127.0.0.1"],
191
+ localDataCenter: "datacenter1",
192
+ keyspace: "myapp",
193
+ port: 9042,
194
+ maxRetries: 3,
195
+ retryDelay: 1000,
196
+ pooling: {
197
+ coreConnectionsPerHost: {
198
+ local: 2,
199
+ },
200
+ maxRequestsPerConnection: 1024,
201
+ },
202
+ });
203
+ ```
204
+
205
+ You can also configure via environment variables:
206
+
207
+ | variable | default |
208
+ | -------------------------- | ------------------ |
209
+ | `SCYLLA_CONTACT_POINTS` | `127.0.0.1` |
210
+ | `SCYLLA_LOCAL_DATA_CENTER` | `datacenter1` |
211
+ | `SCYLLA_KEYSPACE` | `default_keyspace` |
212
+
213
+ ---
214
+
215
+ ## about the driver
216
+
217
+ This package ships a modified version of the `cassandra-driver`
218
+
219
+ ---
220
+
221
+ ## license
222
+
223
+ This project is licensed under the [MIT license](https://opensource.org/licenses/MIT).
224
+
225
+ The bundled `cassandra-driver` is licensed under the
226
+ [Apache License, Version 2.0](src/driver/LICENSE.txt).
227
+
228
+ ```
229
+ Apache Cassandra NodeJS Driver
230
+ Copyright 2013 The Apache Software Foundation
231
+
232
+ Licensed under the Apache License, Version 2.0 (the "License");
233
+ you may not use this file except in compliance with the License.
234
+ You may obtain a copy of the License at
235
+
236
+ http://www.apache.org/licenses/LICENSE-2.0
237
+
238
+ Unless required by applicable law or agreed to in writing, software
239
+ distributed under the License is distributed on an "AS IS" BASIS,
240
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
241
+ See the License for the specific language governing permissions and
242
+ limitations under the License.
243
+ ```
244
+
245
+ See [`src/driver/LICENSE.txt`](src/driver/LICENSE.txt) and
246
+ [`src/driver/NOTICE.txt`](src/driver/NOTICE.txt) for the full legal text.
@@ -0,0 +1 @@
1
+ import{createRequire as e}from"node:module";var t=Object.create,n=Object.defineProperty,r=Object.getOwnPropertyDescriptor,i=Object.getOwnPropertyNames,a=Object.getPrototypeOf,o=Object.prototype.hasOwnProperty,s=(e,t)=>()=>(t||(e((t={exports:{}}).exports,t),e=null),t.exports),c=(e,t,a,s)=>{if(t&&typeof t==`object`||typeof t==`function`)for(var c=i(t),l=0,u=c.length,d;l<u;l++)d=c[l],!o.call(e,d)&&d!==a&&n(e,d,{get:(e=>t[e]).bind(null,d),enumerable:!(s=r(t,d))||s.enumerable});return e},l=(e,r,i)=>(i=e==null?{}:t(a(e)),c(r||!e||!e.__esModule?n(i,`default`,{value:e,enumerable:!0}):i,e)),u=e(import.meta.url);export{s as __commonJSMin,u as __require,l as __toESM};
package/client.d.ts CHANGED
@@ -1,6 +1,8 @@
1
+ import { mapping } from "./driver/lib/mapping/index.js";
2
+ import { Client as Client$1 } from "./driver/index.js";
1
3
  import { Model } from "./model/index.js";
2
4
  import { ClientConfig } from "./types.js";
3
- import { Client as Client$1, mapping } from "cassandra-driver";
5
+ import { Logger } from "./logger/index.js";
4
6
 
5
7
  //#region src/client.d.ts
6
8
  declare class Client {
@@ -10,15 +12,14 @@ declare class Client {
10
12
  mapper: mapping.Mapper;
11
13
  models: Map<string, Model<any>>;
12
14
  model(name: string): Model<any> | undefined;
15
+ logger: Logger;
13
16
  initialize(options?: {
14
17
  sync?: boolean;
15
18
  }): Promise<void>;
16
- private connectWithRetry;
17
- private delay;
18
19
  shutdown(): Promise<void>;
20
+ private connectWithRetry;
19
21
  executeWithRetry<T>(operation: () => Promise<T>, operationName?: string): Promise<T>;
20
22
  private isRetryableError;
21
23
  }
22
24
  //#endregion
23
- export { Client, Client as default };
24
- //# sourceMappingURL=client.d.ts.map
25
+ export { Client, Client as default };
package/client.js CHANGED
@@ -1,117 +1 @@
1
- import loadModels_default from "./utils/loadModels.js";
2
- import buildMapper_default from "./utils/buildMapper.js";
3
- import { Model } from "./model/index.js";
4
- import path from "node:path";
5
- import Cassandra from "cassandra-driver";
6
- //#region src/client.ts
7
- const DEFAULT_MAX_RETRIES = 3;
8
- const DEFAULT_RETRY_DELAY = 1e3;
9
- const { SCYLLA_CONTACT_POINTS, SCYLLA_LOCAL_DATA_CENTER, SCYLLA_KEYSPACE } = process.env;
10
- var Client = class {
11
- constructor(config = {}) {
12
- if (globalThis.__scylla_client) throw new Error("An instance of Scylla Client is already initialized");
13
- this.config = {
14
- modelsPath: path.resolve(process.cwd(), "db"),
15
- contactPoints: config.contactPoints ?? SCYLLA_CONTACT_POINTS?.split(",") ?? ["127.0.0.1"],
16
- localDataCenter: config.localDataCenter ?? SCYLLA_LOCAL_DATA_CENTER ?? "datacenter1",
17
- keyspace: config.keyspace ?? SCYLLA_KEYSPACE ?? "default",
18
- port: 9042,
19
- maxRetries: DEFAULT_MAX_RETRIES,
20
- retryDelay: DEFAULT_RETRY_DELAY,
21
- ...config
22
- };
23
- const clientOptions = {
24
- contactPoints: this.config.contactPoints,
25
- localDataCenter: this.config.localDataCenter,
26
- keyspace: this.config.keyspace,
27
- protocolOptions: { port: this.config.port }
28
- };
29
- if (this.config.pooling) clientOptions.pooling = this.config.pooling;
30
- this.driver = new Cassandra.Client(clientOptions);
31
- }
32
- config;
33
- driver;
34
- mapper;
35
- models = /* @__PURE__ */ new Map();
36
- model(name) {
37
- return this.models.get(name);
38
- }
39
- async initialize(options = {}) {
40
- let models;
41
- try {
42
- models = await loadModels_default(this.config.modelsPath);
43
- } catch (error) {
44
- throw new Error(`Failed to load models: ${error.message}`);
45
- }
46
- models = models.filter((schema) => schema instanceof Model);
47
- this.mapper = new Cassandra.mapping.Mapper(this.driver, { models: buildMapper_default(models) });
48
- globalThis.__scylla_client = this;
49
- for (let model of models) {
50
- this.models.set(model.name, model);
51
- if (options?.sync === true) await model._sync();
52
- }
53
- console.log("Connecting to ScyllaDB");
54
- await this.connectWithRetry();
55
- console.log("ScyllaDB Connected");
56
- }
57
- async connectWithRetry() {
58
- let lastError = null;
59
- for (let attempt = 1; attempt <= this.config.maxRetries; attempt++) try {
60
- await this.driver.connect();
61
- return;
62
- } catch (error) {
63
- lastError = error;
64
- console.warn(`Connection attempt ${attempt} failed: ${error.message}`);
65
- if (attempt < this.config.maxRetries) {
66
- console.log(`Retrying in ${this.config.retryDelay}ms...`);
67
- await this.delay(this.config.retryDelay);
68
- }
69
- }
70
- throw new Error(`Failed to connect to ScyllaDB after ${this.config.maxRetries} attempts: ${lastError?.message}`);
71
- }
72
- delay(ms) {
73
- return new Promise((resolve) => setTimeout(resolve, ms));
74
- }
75
- async shutdown() {
76
- try {
77
- await this.driver.shutdown();
78
- console.log("ScyllaDB connection closed");
79
- delete globalThis.__scylla_client;
80
- } catch (error) {
81
- console.error("Error shutting down ScyllaDB connection:", error);
82
- throw error;
83
- }
84
- }
85
- async executeWithRetry(operation, operationName = "operation") {
86
- let lastError = null;
87
- for (let attempt = 1; attempt <= this.config.maxRetries; attempt++) try {
88
- return await operation();
89
- } catch (error) {
90
- lastError = error;
91
- if (this.isRetryableError(error) && attempt < this.config.maxRetries) {
92
- console.warn(`Operation ${operationName} attempt ${attempt} failed: ${error.message}`);
93
- console.log(`Retrying in ${this.config.retryDelay}ms...`);
94
- await this.delay(this.config.retryDelay);
95
- continue;
96
- }
97
- throw error;
98
- }
99
- throw new Error(`Operation ${operationName} failed after ${this.config.maxRetries} attempts: ${lastError?.message}`);
100
- }
101
- isRetryableError(error) {
102
- const retryableMessages = [
103
- "timeout",
104
- "connection",
105
- "network",
106
- "unavailable",
107
- "overloaded",
108
- "no hosts available"
109
- ];
110
- const errorMessage = error.message?.toLowerCase() || "";
111
- return retryableMessages.some((msg) => errorMessage.includes(msg));
112
- }
113
- };
114
- //#endregion
115
- export { Client, Client as default };
116
-
117
- //# sourceMappingURL=client.js.map
1
+ import{__toESM as e}from"./_virtual/_rolldown/runtime.js";import{require_driver as t}from"./driver/index.js";import{Model as n}from"./model/index.js";import{Logger as r}from"./logger/index.js";import i from"./utils/loadModels.js";import a from"./utils/buildMapper.js";import o from"./utils/delay.js";import s from"node:path";var c=e(t(),1);const{SCYLLA_CONTACT_POINTS:l,SCYLLA_LOCAL_DATA_CENTER:u,SCYLLA_KEYSPACE:d}=process.env;var f=class{constructor(e={}){if(globalThis.__scylla_client)throw Error(`An instance of Scylla Client is already initialized`);this.config={modelsPath:s.resolve(process.cwd(),`db`),contactPoints:e.contactPoints??l?.split(`,`)??[`127.0.0.1`],localDataCenter:e.localDataCenter??u??`datacenter1`,keyspace:e.keyspace??d??`default_keyspace`,port:9042,maxRetries:3,retryDelay:1e3,...e};let t={contactPoints:this.config.contactPoints,localDataCenter:this.config.localDataCenter,keyspace:this.config.keyspace,protocolOptions:{port:this.config.port}};this.config.pooling&&(t.pooling=this.config.pooling),this.driver=new c.Client(t)}config;driver;mapper;models=new Map;model(e){return this.models.get(e)}logger=new r;async initialize(e={}){let t;try{t=await i(this.config.modelsPath)}catch(e){throw Error(`Failed to load models: ${e.message}`)}t=t.filter(e=>e instanceof n),this.mapper=new c.mapping.Mapper(this.driver,{models:a(t)}),globalThis.__scylla_client=this,this.logger.log(`Connecting...`),await this.connectWithRetry(),this.logger.log(`Connected`);for(let n of t)this.models.set(n.name,n),e?.sync===!0&&await n._sync()}async shutdown(){try{await this.driver.shutdown(),this.logger.log(`connection closed`),delete globalThis.__scylla_client}catch(e){throw this.logger.error(`error shutting down connection:`,e),e}}async connectWithRetry(){let e=null;for(let t=1;t<=this.config.maxRetries;t++)try{await this.driver.connect();return}catch(n){e=n,this.logger.warn(`Connection attempt ${t} failed: ${n.message}`),t<this.config.maxRetries&&(this.logger.log(`Retrying in ${this.config.retryDelay}ms...`),await o(this.config.retryDelay))}throw Error(`Failed to connect to ScyllaDB after ${this.config.maxRetries} attempts: ${e?.message}`)}async executeWithRetry(e,t=`operation`){let n=null;for(let r=1;r<=this.config.maxRetries;r++)try{return await e()}catch(e){if(n=e,this.isRetryableError(e)&&r<this.config.maxRetries){this.logger.warn(`Operation ${t} attempt ${r} failed: ${e.message}`),this.logger.log(`Retrying in ${this.config.retryDelay}ms...`),await o(this.config.retryDelay);continue}throw e}throw Error(`Operation ${t} failed after ${this.config.maxRetries} attempts: ${n?.message}`)}isRetryableError(e){let t=[`timeout`,`connection`,`network`,`unavailable`,`overloaded`,`no hosts available`],n=e.message?.toLowerCase()||``;return t.some(e=>n.includes(e))}};export{f as Client,f as default};
@@ -0,0 +1,11 @@
1
+ //#region src/cql_gen/create_keyspace.d.ts
2
+ type KeyspaceCreateOptions = {
3
+ keyspace: string;
4
+ replication?: {
5
+ strategy: string;
6
+ options: Record<string, string>;
7
+ };
8
+ };
9
+ declare function createKeyspace(options: KeyspaceCreateOptions): string;
10
+ //#endregion
11
+ export { createKeyspace as default };
@@ -0,0 +1 @@
1
+ function e(e){let t={};return t=e.replication?e.replication:{strategy:`SimpleStrategy`,options:{replication_factor:`1`}},`CREATE KEYSPACE IF NOT EXISTS ${e.keyspace} WITH REPLICATION = ${JSON.stringify(t)};`}export{e as default};
@@ -3,5 +3,4 @@ import { Model } from "../model/index.js";
3
3
  //#region src/cql_gen/create_table.d.ts
4
4
  declare function export_default(model: Model): string;
5
5
  //#endregion
6
- export { export_default as default };
7
- //# sourceMappingURL=create_table.d.ts.map
6
+ export { export_default as default };
@@ -1,38 +1 @@
1
- //#region src/cql_gen/create_table.ts
2
- function create_table_default(model) {
3
- const desc = model.schema;
4
- const tableName = desc.table_name;
5
- const keyspace = model.client.config.keyspace;
6
- const fields = desc.fields;
7
- const key = desc.keys;
8
- const clusteringOrder = desc.clustering_order;
9
- let columnsDef = "";
10
- for (const fieldName in fields) {
11
- const field = fields[fieldName];
12
- const typeStr = typeof field === "string" ? field : field?.type;
13
- if (!typeStr) throw new Error(`Invalid field type for "${fieldName}" in model "${tableName}"`);
14
- columnsDef += `"${fieldName}" ${typeStr.toUpperCase()}, `;
15
- }
16
- let pkDef = "";
17
- if (typeof key === "string") pkDef = `"${key}"`;
18
- else if (Array.isArray(key) && key.length > 0) {
19
- const first = key[0];
20
- if (Array.isArray(first)) pkDef = `(${first.map((k) => `"${k}"`).join(", ")})`;
21
- else pkDef = `"${first}"`;
22
- for (let i = 1; i < key.length; i++) pkDef += `, "${key[i]}"`;
23
- } else throw new Error(`Missing or invalid primary key in model "${tableName}"`);
24
- let clusterClause = "";
25
- if (clusteringOrder) {
26
- let orderDef = "";
27
- for (const col in clusteringOrder) {
28
- if (orderDef !== "") orderDef += ", ";
29
- orderDef += `"${col}" ${clusteringOrder[col].toUpperCase()}`;
30
- }
31
- if (orderDef !== "") clusterClause = ` WITH CLUSTERING ORDER BY (${orderDef})`;
32
- }
33
- return `CREATE TABLE IF NOT EXISTS ${keyspace}.${tableName} (${columnsDef}PRIMARY KEY (${pkDef}))${clusterClause}`;
34
- }
35
- //#endregion
36
- export { create_table_default as default };
37
-
38
- //# sourceMappingURL=create_table.js.map
1
+ function e(e){let t=e.schema,n=t.table_name,r=e.client.config.keyspace,i=t.fields,a=t.keys,o=t.clustering_order,s=``;for(let e in i){let t=i[e],r=typeof t==`string`?t:t?.type;if(!r)throw Error(`Invalid field type for "${e}" in model "${n}"`);s+=`"${e}" ${r.toUpperCase()}, `}let c=``;if(typeof a==`string`)c=`"${a}"`;else if(Array.isArray(a)&&a.length>0){let e=a[0];c=Array.isArray(e)?`(${e.map(e=>`"${e}"`).join(`, `)})`:`"${e}"`;for(let e=1;e<a.length;e++)c+=`, "${a[e]}"`}else throw Error(`Missing or invalid primary key in model "${n}"`);let l=``;if(o){let e=``;for(let t in o)e!==``&&(e+=`, `),e+=`"${t}" ${o[t].toUpperCase()}`;e!==``&&(l=` WITH CLUSTERING ORDER BY (${e})`)}return`CREATE TABLE IF NOT EXISTS ${r}.${n} (${s}PRIMARY KEY (${c}))${l}`}export{e as default};
@@ -1,17 +1,16 @@
1
+ import { mapping } from "../driver/lib/mapping/index.js";
1
2
  import { Model } from "../model/index.js";
2
3
  import { Doc } from "../types.js";
3
- import * as _$cassandra_driver0 from "cassandra-driver";
4
4
 
5
5
  //#region src/document/index.d.ts
6
6
  declare class Document<TDoc = any> {
7
7
  constructor(data: TDoc, model: Model<any, TDoc>);
8
8
  _model: Model<any, TDoc>;
9
9
  save(): Promise<Doc<TDoc>>;
10
- delete(): Promise<_$cassandra_driver0.mapping.Result<any>>;
10
+ delete(): Promise<mapping.Result<any>>;
11
11
  toRaw(): TDoc;
12
12
  isValid(): boolean;
13
13
  getChangedFields(original: Partial<TDoc>): (keyof TDoc)[];
14
14
  }
15
15
  //#endregion
16
- export { Document, Document as default };
17
- //# sourceMappingURL=index.d.ts.map
16
+ export { Document, Document as default };
package/document/index.js CHANGED
@@ -1,63 +1 @@
1
- //#region src/document/index.ts
2
- var Document = class {
3
- constructor(data, model) {
4
- if (data == null) throw new Error("Cannot create Document with null or undefined data");
5
- if (typeof data !== "object" || Array.isArray(data)) throw new Error("Document data must be an object");
6
- Object.assign(this, data);
7
- Object.defineProperty(this, "_model", {
8
- value: model,
9
- enumerable: false,
10
- writable: false,
11
- configurable: false
12
- });
13
- }
14
- _model;
15
- async save() {
16
- try {
17
- const data = this.toRaw();
18
- return await this._model.update(data);
19
- } catch (error) {
20
- throw new Error(`Failed to save document: ${error.message}`);
21
- }
22
- }
23
- async delete() {
24
- try {
25
- return await this._model.delete(this.toRaw());
26
- } catch (error) {
27
- throw new Error(`Failed to delete document: ${error.message}`);
28
- }
29
- }
30
- toRaw() {
31
- const raw = {};
32
- for (const key in this) {
33
- if (key === "_model") continue;
34
- if (this.propertyIsEnumerable(key)) {
35
- const value = this[key];
36
- try {
37
- JSON.stringify(value);
38
- raw[key] = value;
39
- } catch (error) {
40
- raw[key] = String(value);
41
- }
42
- }
43
- }
44
- return raw;
45
- }
46
- isValid() {
47
- try {
48
- return true;
49
- } catch {
50
- return false;
51
- }
52
- }
53
- getChangedFields(original) {
54
- const current = this.toRaw();
55
- const changed = [];
56
- for (const key in current) if (!(key in original) || current[key] !== original[key]) changed.push(key);
57
- return changed;
58
- }
59
- };
60
- //#endregion
61
- export { Document, Document as default };
62
-
63
- //# sourceMappingURL=index.js.map
1
+ var e=class{constructor(e,t){if(e==null)throw Error(`Cannot create Document with null or undefined data`);if(typeof e!=`object`||Array.isArray(e))throw Error(`Document data must be an object`);Object.assign(this,e),Object.defineProperty(this,`_model`,{value:t,enumerable:!1,writable:!1,configurable:!1})}_model;async save(){try{let e=this.toRaw();return await this._model.update(e)}catch(e){throw Error(`Failed to save document: ${e.message}`)}}async delete(){try{return await this._model.delete(this.toRaw())}catch(e){throw Error(`Failed to delete document: ${e.message}`)}}toRaw(){let e={};for(let t in this)if(t!==`_model`&&this.propertyIsEnumerable(t)){let n=this[t];try{JSON.stringify(n),e[t]=n}catch{e[t]=String(n)}}return e}isValid(){try{return!0}catch{return!1}}getChangedFields(e){let t=this.toRaw(),n=[];for(let r in t)(!(r in e)||t[r]!==e[r])&&n.push(r);return n}};export{e as Document,e as default};