@syntropix/database 0.0.4 → 0.0.6

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 (49) hide show
  1. package/dist/core/dataClient.js +3 -0
  2. package/dist/core/syntropix.d.ts +1 -1
  3. package/dist/core/syntropix.js +2 -1
  4. package/dist/index.d.ts +1 -1
  5. package/dist/index.js +1 -1
  6. package/dist/types/basemodel.d.ts +4 -3
  7. package/dist/types/basemodel.js +36 -35
  8. package/dist/types/common.d.ts +32 -28
  9. package/dist/types/common.js +11 -12
  10. package/dist/types/data-type.d.ts +48 -38
  11. package/dist/types/data-type.js +17 -18
  12. package/dist/types/dto/table.d.ts +6 -6
  13. package/dist/types/field.d.ts +60 -41
  14. package/dist/types/field.js +35 -32
  15. package/dist/types/filter.d.ts +43 -44
  16. package/dist/types/filter.js +56 -41
  17. package/dist/types/requests.d.ts +25 -14
  18. package/dist/types/requests.js +6 -1
  19. package/package.json +5 -2
  20. package/.editorconfig +0 -8
  21. package/.env +0 -2
  22. package/.gitattributes +0 -4
  23. package/.husky/pre-commit +0 -1
  24. package/.prettierrc +0 -8
  25. package/.vscode/settings.json +0 -1
  26. package/.yarnrc.yml +0 -1
  27. package/eslint.config.mjs +0 -23
  28. package/examples/advanced-usage.ts +0 -214
  29. package/examples/tsconfig.json +0 -13
  30. package/examples/usage.ts +0 -94
  31. package/jest.config.ts +0 -9
  32. package/src/core/client.ts +0 -41
  33. package/src/core/config.ts +0 -29
  34. package/src/core/dataClient.ts +0 -78
  35. package/src/core/syntropix.ts +0 -27
  36. package/src/core/tableClient.ts +0 -86
  37. package/src/index.ts +0 -10
  38. package/src/types/basemodel.ts +0 -558
  39. package/src/types/common.ts +0 -83
  40. package/src/types/data-type.ts +0 -23
  41. package/src/types/dto/base.ts +0 -4
  42. package/src/types/dto/table.ts +0 -21
  43. package/src/types/field.ts +0 -277
  44. package/src/types/filter.ts +0 -281
  45. package/src/types/requests.ts +0 -91
  46. package/test.json +0 -48
  47. package/tests/basic.test.ts +0 -141
  48. package/tests/tsconfig.json +0 -8
  49. package/tsconfig.json +0 -18
@@ -1,558 +0,0 @@
1
- // BaseModel implementation with decorator support
2
- import 'reflect-metadata';
3
- import { ClientConfig } from '../core/config';
4
- import { SyntropixClient } from '../core/syntropix';
5
- import { Aggregate, AggregateFunction, ForeignKey, GroupBy, Index, Join, Sort } from './common';
6
- import { TableCreateResponse } from './dto/table';
7
- import { Field, ForeignKeyField, IntegerField } from './field';
8
- import { AND, EQ, Filter, GTE, OR, Value } from './filter';
9
-
10
- // Metadata keys
11
- const FIELDS_KEY = Symbol('fields');
12
- const TABLE_NAME_KEY = Symbol('tableName');
13
- const DESCRIPTION_KEY = Symbol('description');
14
- const INDEXES_KEY = Symbol('indexes');
15
-
16
- // Column decorator
17
- export function Column(
18
- options: {
19
- type?: string | Record<string, any>;
20
- name?: string;
21
- description?: string;
22
- primary?: boolean;
23
- nullable?: boolean;
24
- auto_increment?: boolean;
25
- default?: any;
26
- } = {},
27
- ) {
28
- return function (target: any, propertyKey: string) {
29
- const fields = Reflect.getMetadata(FIELDS_KEY, target.constructor) || {};
30
-
31
- const fieldName = options.name || propertyKey.toLowerCase();
32
- const column_type = options.type || 'Text';
33
-
34
- const field = new (class extends Field {
35
- constructor() {
36
- super(column_type, {
37
- name: fieldName,
38
- description: options.description,
39
- is_primary_key: options.primary,
40
- is_nullable: options.nullable,
41
- auto_increment: options.auto_increment,
42
- default: options.default,
43
- });
44
- }
45
- })();
46
-
47
- fields[propertyKey] = field;
48
- Reflect.defineMetadata(FIELDS_KEY, fields, target.constructor);
49
- };
50
- }
51
-
52
- // Description decorator
53
- export function Description(description: string) {
54
- return function (target: any) {
55
- Reflect.defineMetadata(DESCRIPTION_KEY, description, target);
56
- };
57
- }
58
-
59
- // ForeignKey decorator
60
- export function ForeignKey(
61
- tableName: string,
62
- columnName: string,
63
- options: {
64
- type?: string | Record<string, any>;
65
- name?: string;
66
- onDelete?: string;
67
- onUpdate?: string;
68
- description?: string;
69
- nullable?: boolean;
70
- default?: any;
71
- } = {},
72
- ) {
73
- return function (target: any, propertyKey: string) {
74
- const fields = Reflect.getMetadata(FIELDS_KEY, target.constructor) || {};
75
-
76
- const field = new ForeignKeyField(options.type || 'Integer', tableName, columnName, {
77
- onDelete: options.onDelete as any,
78
- onUpdate: options.onUpdate as any,
79
- description: options.description,
80
- is_nullable: options.nullable,
81
- default: options.default,
82
- });
83
- field.name = options.name || propertyKey.toLowerCase();
84
-
85
- fields[propertyKey] = field;
86
- Reflect.defineMetadata(FIELDS_KEY, fields, target.constructor);
87
- };
88
- }
89
-
90
- export class BaseModel {
91
- private _client?: SyntropixClient;
92
- private _extra: Record<string, any> = {};
93
-
94
- constructor(data: Record<string, any> = {}) {
95
- const fields = this.getFields();
96
-
97
- // Create a mapping from column names to property names
98
- const columnToPropertyMap: Record<string, string> = {};
99
- for (const [propertyName, field] of Object.entries(fields)) {
100
- columnToPropertyMap[field.name] = propertyName;
101
- }
102
-
103
- for (const [key, value] of Object.entries(data)) {
104
- // Check if key is a property name
105
- if (key in fields) {
106
- (this as any)[key] = value;
107
- }
108
- // Check if key is a column name
109
- else if (key in columnToPropertyMap) {
110
- (this as any)[columnToPropertyMap[key]] = value;
111
- }
112
- // Otherwise, store in _extra
113
- else {
114
- this._extra[key] = value;
115
- }
116
- }
117
- }
118
-
119
- // Static metadata getters
120
- protected static getTableName(): string {
121
- const tableName = Reflect.getMetadata(TABLE_NAME_KEY, this);
122
- if (tableName) return tableName;
123
-
124
- // Check for static tableName property
125
- if ('tableName' in this && typeof (this as any).tableName === 'string') {
126
- return (this as any).tableName;
127
- }
128
-
129
- return this.name.toLowerCase();
130
- }
131
-
132
- public static getDescription(): string {
133
- const metadataDescription = Reflect.getMetadata(DESCRIPTION_KEY, this);
134
- if (metadataDescription) return metadataDescription;
135
-
136
- // Check for static description property as fallback
137
- if ('description' in this && typeof (this as any).description === 'string') {
138
- return (this as any).description;
139
- }
140
-
141
- return '';
142
- }
143
-
144
- protected static getIndexes(): Index[] {
145
- return Reflect.getMetadata(INDEXES_KEY, this) || [];
146
- }
147
-
148
- protected static getFields(): Record<string, Field> {
149
- const fields = Reflect.getMetadata(FIELDS_KEY, this) || {};
150
-
151
- // Check if there's a primary key
152
- let hasPrimaryKey = false;
153
- for (const field of Object.values(fields) as Field[]) {
154
- if (field.is_primary_key) {
155
- hasPrimaryKey = true;
156
- field.is_nullable = false;
157
- break;
158
- }
159
- }
160
-
161
- // If no primary key, add default 'id' field
162
- if (!hasPrimaryKey && !('id' in fields)) {
163
- fields['id'] = new IntegerField({
164
- is_primary_key: true,
165
- is_nullable: false,
166
- auto_increment: true,
167
- description: 'Primary key',
168
- });
169
- fields['id'].name = 'id';
170
- }
171
-
172
- return fields;
173
- }
174
-
175
- protected static getPrimaryKeyName(): string {
176
- const fields = this.getFields();
177
- for (const [key, field] of Object.entries(fields)) {
178
- if (field.is_primary_key) {
179
- return field.name;
180
- }
181
- }
182
- return 'id';
183
- }
184
-
185
- protected static getAssociations(): ForeignKeyField[] {
186
- const fields = this.getFields();
187
- return Object.values(fields).filter((f) => f instanceof ForeignKeyField) as ForeignKeyField[];
188
- }
189
-
190
- // Instance metadata getters
191
- protected getFields(): Record<string, Field> {
192
- return (this.constructor as typeof BaseModel).getFields();
193
- }
194
-
195
- protected getTableName(): string {
196
- return (this.constructor as typeof BaseModel).getTableName();
197
- }
198
-
199
- protected getPrimaryKeyName(): string {
200
- return (this.constructor as typeof BaseModel).getPrimaryKeyName();
201
- }
202
-
203
- protected getPrimaryKey(): Field | undefined {
204
- const fields = this.getFields();
205
- for (const field of Object.values(fields)) {
206
- if (field.is_primary_key) {
207
- return field;
208
- }
209
- }
210
- return undefined;
211
- }
212
-
213
- // Client getter
214
- protected get client(): SyntropixClient {
215
- if (!this._client) {
216
- this._client = new SyntropixClient(new ClientConfig());
217
- }
218
- return this._client;
219
- }
220
-
221
- // Table operations
222
- static async createTable(_client?: SyntropixClient | null): Promise<TableCreateResponse> {
223
- const fields = this.getFields();
224
- const columns = Object.values(fields).map((f) => f.into());
225
- const foreignKeys: ForeignKey[] = this.getAssociations().map((f) => ({
226
- from_table: this.getTableName(),
227
- from_column: f.name,
228
- to_table: f.tableName,
229
- to_column: f.columnName,
230
- on_delete: f.onDelete,
231
- on_update: f.onUpdate,
232
- }));
233
- const indexes = this.getIndexes();
234
-
235
- const client = _client || new SyntropixClient(new ClientConfig());
236
- return client.table.createTable({
237
- name: this.getTableName(),
238
- description: this.getDescription() || 'No description',
239
- columns,
240
- foreign_keys: foreignKeys,
241
- indexes,
242
- });
243
- }
244
-
245
- static async dropTable(_client?: SyntropixClient | null): Promise<any> {
246
- const client = _client || new SyntropixClient(new ClientConfig());
247
- return client.table.dropTable({ name: this.getTableName() });
248
- }
249
-
250
- static async renameTable(newName: string, _client?: SyntropixClient | null): Promise<any> {
251
- const client = _client || new SyntropixClient(new ClientConfig());
252
- return client.table.renameTable({
253
- name: this.getTableName(),
254
- new_name: newName,
255
- });
256
- }
257
-
258
- static async truncateTable(_client?: SyntropixClient | null): Promise<any> {
259
- const client = _client || new SyntropixClient(new ClientConfig());
260
- return client.table.truncateTable({ name: this.getTableName() });
261
- }
262
-
263
- // Data operations
264
- static async create<T extends BaseModel>(
265
- this: new (data?: any) => T,
266
- data: Record<string, any>,
267
- _client?: SyntropixClient | null,
268
- ): Promise<any> {
269
- const model = this as any as typeof BaseModel;
270
- const fields = model.getFields();
271
- const columns: string[] = [];
272
- const values: any[] = [];
273
-
274
- for (const [key, value] of Object.entries(data)) {
275
- if (key in fields) {
276
- columns.push(fields[key].name);
277
- values.push(value);
278
- } else {
279
- throw new Error(`Invalid field: ${key}`);
280
- }
281
- }
282
-
283
- const client = _client || new SyntropixClient(new ClientConfig());
284
- return client.data.insertOne({
285
- table_name: model.getTableName(),
286
- data: {
287
- columns,
288
- values: [values],
289
- },
290
- });
291
- }
292
-
293
- async save(_client?: SyntropixClient | null): Promise<any> {
294
- const pk = this.getPrimaryKey();
295
- const pkValue = (this as any)[this.getPrimaryKeyName()];
296
- const fields = this.getFields();
297
-
298
- if (pkValue === null || pkValue === undefined) {
299
- const data: Record<string, any> = {};
300
- for (const [key, field] of Object.entries(fields)) {
301
- data[key] = (this as any)[key];
302
- }
303
- return (this.constructor as typeof BaseModel).create(data, _client);
304
- }
305
-
306
- const columns: string[] = [];
307
- const values: any[] = [];
308
- for (const [key, field] of Object.entries(fields)) {
309
- if (field.name !== pk?.name) {
310
- columns.push(field.name);
311
- values.push((this as any)[key]);
312
- }
313
- }
314
-
315
- return (
316
- _client ||
317
- this.client.data.updateByPrimaryKey(pkValue, {
318
- table_name: this.getTableName(),
319
- payload: {
320
- filter: [[]],
321
- columns,
322
- values,
323
- },
324
- })
325
- );
326
- }
327
-
328
- async remove(filter?: Filter, _client?: SyntropixClient | null): Promise<number> {
329
- const pk = this.getPrimaryKey();
330
- const pkValue = (this as any)[this.getPrimaryKeyName()];
331
- const finalFilter = filter || OR(AND(EQ(pk?.name || 'id', pkValue)));
332
-
333
- return (
334
- _client ||
335
- this.client.data.deleteData({
336
- table_name: this.getTableName(),
337
- payload: {
338
- filter: finalFilter,
339
- },
340
- })
341
- );
342
- }
343
-
344
- static async bulkCreate<T extends BaseModel>(
345
- this: new (data?: any) => T,
346
- datas: Record<string, any>[],
347
- batchSize: number = 32,
348
- _client?: SyntropixClient | null,
349
- ): Promise<number> {
350
- if (!datas.length) return 0;
351
- if (batchSize <= 0) throw new Error('Batch size must be greater than 0');
352
-
353
- const model = this as any as typeof BaseModel;
354
- const fields = model.getFields();
355
- let cnt = 0;
356
- batchSize = Math.min(batchSize, 128);
357
-
358
- const columns = Object.keys(datas[0]);
359
- const columnsSet = new Set(columns);
360
-
361
- for (const col of columns) {
362
- if (!(col in fields)) {
363
- throw new Error(`Invalid field: ${col}`);
364
- }
365
- }
366
-
367
- let values: any[][] = [];
368
- const client = _client || new SyntropixClient(new ClientConfig());
369
-
370
- for (const data of datas) {
371
- if (columnsSet.size !== new Set(Object.keys(data)).size || !columns.every((c) => c in data)) {
372
- throw new Error('All data must have the same columns');
373
- }
374
-
375
- values.push(columns.map((c) => data[c]));
376
-
377
- if (values.length === batchSize) {
378
- cnt += await client.data.insertData({
379
- table_name: model.getTableName(),
380
- data: { columns: columns.map((c) => fields[c].name), values },
381
- });
382
- values = [];
383
- }
384
- }
385
-
386
- if (values.length > 0) {
387
- cnt += await client.data.insertData({
388
- table_name: model.getTableName(),
389
- data: { columns: columns.map((c) => fields[c].name), values },
390
- });
391
- }
392
-
393
- return cnt;
394
- }
395
-
396
- static async update<T extends BaseModel>(
397
- this: new (data?: any) => T,
398
- filter: Filter,
399
- data: Record<string, any>,
400
- _client?: SyntropixClient | null,
401
- ): Promise<number> {
402
- const model = this as any as typeof BaseModel;
403
- const fields = model.getFields();
404
- const columns = Object.keys(data);
405
-
406
- if (!columns.length) {
407
- throw new Error('No columns to update');
408
- }
409
-
410
- for (const col of columns) {
411
- if (!(col in fields)) {
412
- throw new Error(`Invalid field: ${col}`);
413
- }
414
- }
415
-
416
- const values = columns.map((c) => data[c]);
417
- const client = _client || new SyntropixClient(new ClientConfig());
418
-
419
- return client.data.updateData({
420
- table_name: model.getTableName(),
421
- payload: {
422
- filter,
423
- columns: columns.map((c) => fields[c].name),
424
- values,
425
- },
426
- });
427
- }
428
-
429
- static async delete<T extends BaseModel>(
430
- this: new (data?: any) => T,
431
- filter: Filter,
432
- _client?: SyntropixClient | null,
433
- ): Promise<number> {
434
- const model = this as any as typeof BaseModel;
435
- const client = _client || new SyntropixClient(new ClientConfig());
436
-
437
- return client.data.deleteData({
438
- table_name: model.getTableName(),
439
- payload: { filter },
440
- });
441
- }
442
-
443
- static async get<T extends BaseModel>(
444
- this: new (data?: any) => T,
445
- filter: Filter,
446
- select?: string[],
447
- _client?: SyntropixClient | null,
448
- ): Promise<T> {
449
- const model = this as any as typeof BaseModel;
450
- const fields = model.getFields();
451
- const client = _client || new SyntropixClient(new ClientConfig());
452
-
453
- const data = await client.data.queryOne({
454
- table_name: model.getTableName(),
455
- query: {
456
- filter,
457
- select: select || Object.values(fields).map((f) => f.name),
458
- limit: 1,
459
- },
460
- });
461
-
462
- return new this(data);
463
- }
464
-
465
- static async filter<T extends BaseModel>(
466
- this: new (data?: any) => T,
467
- options: {
468
- filter: Filter;
469
- sort?: Sort[];
470
- aggregate?: Aggregate[];
471
- join?: Join;
472
- limit?: number;
473
- offset?: number;
474
- groupBy?: GroupBy;
475
- select?: string[];
476
- _client?: SyntropixClient | null;
477
- },
478
- ): Promise<T[]> {
479
- const model = this as any as typeof BaseModel;
480
- const client = options._client || new SyntropixClient(new ClientConfig());
481
-
482
- const data = await client.data.queryMany({
483
- table_name: model.getTableName(),
484
- query: {
485
- filter: options.filter,
486
- sort: options.sort,
487
- aggregate: options.aggregate,
488
- join: options.join ? [options.join] : undefined,
489
- limit: options.limit,
490
- offset: options.offset,
491
- group_by: options.groupBy,
492
- select: options.select,
493
- },
494
- });
495
-
496
- return data.map((item: any) => new this(item));
497
- }
498
-
499
- static async count<T extends BaseModel>(
500
- this: new (data?: any) => T,
501
- options: {
502
- filter?: Filter;
503
- join?: Join;
504
- groupBy?: GroupBy;
505
- _client?: SyntropixClient | null;
506
- } = {},
507
- ): Promise<number> {
508
- const model = this as any as typeof BaseModel;
509
- const client = options._client || new SyntropixClient(new ClientConfig());
510
-
511
- const data = await client.data.queryMany({
512
- table_name: model.getTableName(),
513
- query: {
514
- filter: options.filter || [[GTE('id', Value(0))]],
515
- aggregate: [
516
- {
517
- column: 'id',
518
- function: AggregateFunction.COUNT,
519
- alias: 'count',
520
- },
521
- ],
522
- join: options.join ? [options.join] : undefined,
523
- limit: 1,
524
- offset: 0,
525
- group_by: options.groupBy,
526
- select: ['count'],
527
- },
528
- });
529
-
530
- return data.length > 0 ? data[0].count : 0;
531
- }
532
-
533
- toString(): string {
534
- const fields = this.getFields();
535
- const data: Record<string, any> = {};
536
-
537
- for (const key of Object.keys(fields)) {
538
- const value = (this as any)[key];
539
- if (!(value instanceof Field)) {
540
- data[key] = value;
541
- }
542
- }
543
-
544
- data._extra = this._extra;
545
- return `${this.constructor.name}(${JSON.stringify(data).slice(1, -1)})`;
546
- }
547
-
548
- toJSON(): Record<string, any> {
549
- const fields = this.getFields();
550
- const data: Record<string, any> = {};
551
-
552
- for (const key of Object.keys(fields)) {
553
- data[key] = (this as any)[key];
554
- }
555
-
556
- return { ...data, ...this._extra };
557
- }
558
- }
@@ -1,83 +0,0 @@
1
- // Common types used across the SDK
2
-
3
- export enum ForeignKeyAction {
4
- CASCADE = 'Cascade',
5
- RESTRICT = 'Restrict',
6
- SET_NULL = 'SetNull',
7
- NO_ACTION = 'NoAction',
8
- SET_DEFAULT = 'SetDefault',
9
- }
10
-
11
- export enum AggregateFunction {
12
- COUNT = 'Count',
13
- SUM = 'Sum',
14
- AVG = 'AVG',
15
- MIN = 'Min',
16
- MAX = 'Max',
17
- COUNT_DISTINCT = 'CountDistinct',
18
- }
19
-
20
- export interface Sort {
21
- column: string;
22
- direction: 'ASCENDING' | 'DESCENDING';
23
- }
24
-
25
- export interface Join {
26
- type: 'INNER' | 'LEFT' | 'RIGHT' | 'FULL';
27
- table: string;
28
- on: any;
29
- }
30
-
31
- export interface Aggregate {
32
- column: string;
33
- function: AggregateFunction;
34
- alias: string;
35
- }
36
-
37
- export interface GroupBy {
38
- columns: string[];
39
- }
40
-
41
- export interface ForeignKey {
42
- from_table: string;
43
- from_column: string;
44
- to_table: string;
45
- to_column: string;
46
- on_delete?: ForeignKeyAction;
47
- on_update?: ForeignKeyAction;
48
- }
49
-
50
- export interface Column {
51
- name: string;
52
- column_type: string | Record<string, any>;
53
- description?: string;
54
- is_primary_key?: boolean;
55
- is_nullable?: boolean;
56
- auto_increment?: boolean;
57
- default?: any;
58
- }
59
-
60
- export interface Index {
61
- name: string;
62
- columns: string[];
63
- unique?: boolean;
64
- }
65
-
66
- export interface SyntropixDBColumn {
67
- id: string; // UUID as string in TypeScript
68
- name: string;
69
- description: string;
70
- column_type: string | Record<string, any>;
71
- is_nullable: boolean;
72
- is_primary_key: boolean;
73
- auto_increment: boolean;
74
- default?: any; // Optional field
75
- }
76
-
77
- export interface SyntropixDBTable {
78
- id: string; // UUID as string in TypeScript
79
- name: string;
80
- description: string;
81
- created_at: Date;
82
- columns: SyntropixDBColumn[];
83
- }
@@ -1,23 +0,0 @@
1
- // Data type definitions for database columns
2
- export class DataType {
3
- // Basic types
4
- static readonly Integer = 'Integer';
5
- static readonly String = (maxLength: number) => ({ type: 'String', maxLength });
6
- static readonly Text = 'Text';
7
- static readonly Boolean = 'Boolean';
8
- static readonly DateTime = 'DateTime';
9
- static readonly Timestamp = 'Timestamp';
10
- static readonly Date = 'Date';
11
- static readonly Json = 'Json';
12
- static readonly Uuid = 'Uuid';
13
- static readonly Double = 'Double';
14
-
15
- // Complex types
16
- static readonly Vector = (dimensions: number) => ({ type: 'Vector', dimensions });
17
- static readonly Array = (dataType: string | Record<string, any>) => ({ type: 'Array', dataType });
18
- static readonly Enum = (name: string, variants: string[]) => ({ type: 'Enum', name, variants });
19
- static readonly Money = (precision: number, scale: number) => ({ type: 'Money', precision, scale });
20
- static readonly Decimal = (precision: number, scale: number) => ({ type: 'Decimal', precision, scale });
21
- }
22
-
23
- export type ColumnType = string | Record<string, any>;
@@ -1,4 +0,0 @@
1
- export interface ApiResponse<T> {
2
- status: string;
3
- data: T;
4
- }
@@ -1,21 +0,0 @@
1
- import { Column, ForeignKey, Index } from '../common';
2
-
3
- export interface TableCreateResponse {
4
- _id: { $oid: string };
5
- table: {
6
- id: string;
7
- name: string;
8
- description: string;
9
- created_at: any;
10
- columns: Column[];
11
- foreign_keys: ForeignKey[];
12
- indexes: Index[];
13
- schema: string;
14
- default_access: string | null;
15
- };
16
- created_at: any;
17
- updated_at: any;
18
- created_by: string;
19
- metadata: Record<string, any>;
20
- triggers: any[];
21
- }