@objectql/core 1.0.0 → 1.2.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.
@@ -1,86 +0,0 @@
1
- import { Driver } from '../src/driver';
2
-
3
- export class MockDriver implements Driver {
4
- private data: Record<string, any[]> = {};
5
- private transactions: Set<any> = new Set();
6
-
7
- constructor() {}
8
-
9
- private getData(objectName: string) {
10
- if (!this.data[objectName]) {
11
- this.data[objectName] = [];
12
- }
13
- return this.data[objectName];
14
- }
15
-
16
- async find(objectName: string, query: any, options?: any): Promise<any[]> {
17
- const items = this.getData(objectName);
18
- // Very basic filter implementation for testing
19
- if (query.filters) {
20
- return items.filter(item => {
21
- // Assuming simple filter: [['field', '=', 'value']]
22
- const filter = query.filters[0];
23
- if (filter && Array.isArray(filter) && filter[1] === '=') {
24
- return item[filter[0]] === filter[2];
25
- }
26
- return true;
27
- });
28
- }
29
- return items;
30
- }
31
-
32
- async findOne(objectName: string, id: string | number, query?: any, options?: any): Promise<any> {
33
- const items = this.getData(objectName);
34
- return items.find((item: any) => item._id === id);
35
- }
36
-
37
- async create(objectName: string, data: any, options?: any): Promise<any> {
38
- const items = this.getData(objectName);
39
- const newItem = {
40
- ...data,
41
- _id: data._id || `id-${Date.now()}-${Math.random()}`
42
- };
43
- items.push(newItem);
44
- return newItem;
45
- }
46
-
47
- async update(objectName: string, id: string | number, data: any, options?: any): Promise<any> {
48
- const items = this.getData(objectName);
49
- const index = items.findIndex((item: any) => item._id === id);
50
- if (index > -1) {
51
- items[index] = { ...items[index], ...data };
52
- return items[index];
53
- }
54
- throw new Error('Not found');
55
- }
56
-
57
- async delete(objectName: string, id: string | number, options?: any): Promise<any> {
58
- const items = this.getData(objectName);
59
- const index = items.findIndex((item: any) => item._id === id);
60
- if (index > -1) {
61
- items.splice(index, 1);
62
- return true;
63
- }
64
- return false;
65
- }
66
-
67
- async count(objectName: string, filters: any, options?: any): Promise<number> {
68
- return (await this.find(objectName, { filters }, options)).length;
69
- }
70
-
71
- async beginTransaction(): Promise<any> {
72
- const trx = { id: Date.now() };
73
- this.transactions.add(trx);
74
- return trx;
75
- }
76
-
77
- async commitTransaction(trx: any): Promise<void> {
78
- if (!this.transactions.has(trx)) throw new Error('Invalid transaction');
79
- this.transactions.delete(trx);
80
- }
81
-
82
- async rollbackTransaction(trx: any): Promise<void> {
83
- if (!this.transactions.has(trx)) throw new Error('Invalid transaction');
84
- this.transactions.delete(trx);
85
- }
86
- }
@@ -1,150 +0,0 @@
1
- import { ObjectQL } from '../src/index';
2
- import { MockDriver } from './mock-driver';
3
- import { ObjectConfig } from '../src/metadata';
4
-
5
- const todoObject: ObjectConfig = {
6
- name: 'todo',
7
- fields: {
8
- title: { type: 'text' },
9
- completed: { type: 'boolean' },
10
- owner: { type: 'text' }
11
- },
12
- listeners: {}
13
- };
14
-
15
- describe('ObjectQL Repository', () => {
16
- let app: ObjectQL;
17
- let driver: MockDriver;
18
-
19
- beforeEach(() => {
20
- driver = new MockDriver();
21
- app = new ObjectQL({
22
- datasources: {
23
- default: driver
24
- },
25
- objects: {
26
- todo: todoObject
27
- }
28
- });
29
- // Reset listeners
30
- if (todoObject.listeners) {
31
- todoObject.listeners.beforeCreate = undefined;
32
- todoObject.listeners.afterCreate = undefined;
33
- }
34
- });
35
-
36
- it('should create and retrieve a record', async () => {
37
- const ctx = app.createContext({ userId: 'u1' });
38
- const repo = ctx.object('todo');
39
-
40
- const created = await repo.create({ title: 'Buy milk' });
41
- expect(created.title).toBe('Buy milk');
42
- expect(created.created_by).toBe('u1');
43
- expect(created._id).toBeDefined();
44
-
45
- const found = await repo.findOne(created._id);
46
- expect(found).toMatchObject(created);
47
- });
48
-
49
- it('should update a record', async () => {
50
- const ctx = app.createContext({ userId: 'u1' });
51
- const repo = ctx.object('todo');
52
- const created = await repo.create({ title: 'Buy milk', completed: false });
53
-
54
- const updated = await repo.update(created._id, { completed: true });
55
- expect(updated.completed).toBe(true);
56
-
57
- const found = await repo.findOne(created._id);
58
- expect(found.completed).toBe(true);
59
- });
60
-
61
- it('should delete a record', async () => {
62
- const ctx = app.createContext({ userId: 'u1' });
63
- const repo = ctx.object('todo');
64
- const created = await repo.create({ title: 'Delete me' });
65
-
66
- await repo.delete(created._id);
67
- const found = await repo.findOne(created._id);
68
- expect(found).toBeUndefined();
69
- });
70
-
71
- it('should support listeners (triggers)', async () => {
72
- const ctx = app.createContext({ userId: 'u1' });
73
- const repo = ctx.object('todo');
74
-
75
- let beforeCalled = false;
76
- let afterCalled = false;
77
-
78
- // Register listeners
79
- todoObject.listeners = {
80
- beforeCreate: async (context) => {
81
- beforeCalled = true;
82
- if (context.doc) {
83
- context.doc.title = context.doc.title + ' (checked)';
84
- }
85
- },
86
- afterCreate: async (context) => {
87
- afterCalled = true;
88
- }
89
- };
90
-
91
- const created = await repo.create({ title: 'Test hooks' });
92
-
93
- expect(beforeCalled).toBe(true);
94
- expect(afterCalled).toBe(true);
95
- expect(created.title).toBe('Test hooks (checked)');
96
- });
97
-
98
- it('should support beforeFind hook for Row Level Security', async () => {
99
- // 1. Setup data
100
- const adminCtx = app.createContext({ isSystem: true });
101
- await adminCtx.object('todo').create({ title: 'My Task', owner: 'u1' });
102
- await adminCtx.object('todo').create({ title: 'Other Task', owner: 'u2' });
103
-
104
- // 2. Setup Hook to filter by owner
105
- todoObject.listeners = {
106
- beforeFind: async (context) => {
107
- // Ignore for admin/system
108
- if (context.ctx.isSystem) return;
109
-
110
- // RLS: Only see own tasks
111
- context.utils.restrict(['owner', '=', context.ctx.userId]);
112
- }
113
- };
114
-
115
- // 3. User u1 Query
116
- const userCtx = app.createContext({ userId: 'u1' });
117
- const userResults = await userCtx.object('todo').find();
118
-
119
- expect(userResults).toHaveLength(1);
120
- expect(userResults[0].title).toBe('My Task');
121
-
122
- // 4. System Query (Bypass)
123
- const sysResults = await adminCtx.object('todo').find();
124
- expect(sysResults).toHaveLength(2);
125
- });
126
-
127
- it('should support transactions', async () => {
128
- const ctx = app.createContext({});
129
-
130
- await ctx.transaction(async (trxCtx) => {
131
- // In a real driver we would check isolation,
132
- // here we just check that the context has a transaction handle
133
- expect((trxCtx as any).transactionHandle).toBeDefined();
134
- const repo = trxCtx.object('todo');
135
- await repo.create({ title: 'Inside Trx' });
136
- });
137
-
138
- // Data should be persisted (mock driver auto-commits efficiently in memory)
139
- const found = await ctx.object('todo').find({ filters: [['title', '=', 'Inside Trx']]});
140
- expect(found).toHaveLength(1);
141
- });
142
-
143
- it('should auto-populate spaceId', async () => {
144
- const ctx = app.createContext({ spaceId: 'space-A' });
145
- const repo = ctx.object('todo');
146
-
147
- const created = await repo.create({ title: 'Space test' });
148
- expect(created.space_id).toBe('space-A');
149
- });
150
- });