Skip to content

Commit 709ed13

Browse files
committed
7.0.0-alpha.7: feat(events): add new event types and improve selection
- Add 'add' and 'remove' event types to Event interface - Update emitDifference to use new event types - Improve selection documentation with clear expression object structure - Add detailed examples of selection usage - Translate all comments to English - Update tests to use new event types
1 parent 7ae299e commit 709ed13

File tree

8 files changed

+670
-139
lines changed

8 files changed

+670
-139
lines changed

CONTRIBUTING.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,8 @@ yarn build
8383
For publishing, it is necessary to:
8484
1. Increase the version level, usually patch
8585
2. Extract changes description from `git status` and `git diff` for all modified files
86-
3. Format the message according to the following template:
86+
3. Remove all NOT ENGLISH words from the all src/* files
87+
4. Format the message according to the following template:
8788
```
8889
Version: type(scope): summary
8990

README.md

+63-16
Original file line numberDiff line numberDiff line change
@@ -182,23 +182,62 @@ All methods work uniformly across different data types, treating single items as
182182
### Operations
183183

184184
#### Select
185+
186+
<details>
187+
<summary>Examples</summary>
188+
189+
```typescript
190+
const A = deep.new();
191+
const B = deep.new();
192+
const C = deep.new();
193+
const X = deep.new();
194+
195+
const a = A.new();
196+
const b1 = B.new();
197+
b1.from = a;
198+
const c = C.new();
199+
c.from = a;
200+
201+
const x = X.new();
202+
const b2 = B.new();
203+
b2.from = x;
204+
205+
// Search by specific relations
206+
deep.select({ type: C }).to; // Deep<Set<[c]>>
207+
208+
// Search for links that referenced from B
209+
deep.select({
210+
out: { type: B }
211+
}).to; // Deep<Set<[a,x]>>
212+
213+
// Get only those links from which both B and C instances originate at least one
214+
deep.select({
215+
and: [
216+
{ out: { type: B } },
217+
{ out: { type: C } },
218+
]
219+
}).to; // Deep<Set<[a]>>
220+
```
221+
</details>
222+
185223
- `select(expression)` → Selection - Creates a reactive selection of links based on expression
186-
- Expression can be:
187-
- Direct (One to one) relations:
188-
- [x] `type` - Get or set link type (returns Deep instance or undefined)
189-
- [x] `from` - Get or set source node (returns Deep instance or undefined)
190-
- [x] `to` - Get or set target node (returns Deep instance or undefined)
191-
- [x] `value` - Get or set link value (returns Deep instance or JS primitive)
192-
- Reverse (One to many) relations:
193-
- [x] `typed` - Get links that have this as their type
194-
- [x] `out` - Get links that have this as their from
195-
- [x] `in` - Get links that have this as their to
196-
- [x] `valued` - Get links that have this as their value
197-
- Logical operators for filtering:
198-
- [x] `and` - Array of expressions that all must match
199-
- [x] `not` - Expression that must not match
200-
- [x] `or` - Array of expressions where at least one must match
201-
- Condition types (comparison operators):
224+
- Expression is an object that can contain the following keys, where each key's value can be either another expression object or a Deep instance:
225+
- Direct selectors:
226+
- [x] `type` - Filter links by type
227+
- [x] `from` - Filter links by source node
228+
- [x] `to` - Filter links by target node
229+
- [x] `id` - Filter links by id
230+
- [x] `value` - Filter links by value
231+
- Reverse selectors:
232+
- [x] `typed` - Filter links where they have this as their type
233+
- [x] `out` - Filter links where they have this as their from
234+
- [x] `in` - Filter links where they have this as their to
235+
- [x] `valued` - Filter links where they have this as their value
236+
- Logic operators:
237+
- [x] `not` - Exclude links matching the expression
238+
- [x] `and` - Include links matching all expressions
239+
- [x] `or` - Include links matching any expression (must be an array where each item can be an expression object or a Deep instance)
240+
- Conditions (comparison operators):
202241
- [ ] `eq` - Equal to
203242
- [ ] `neq` - Not equal to
204243
- [ ] `gt` - Greater than
@@ -234,6 +273,14 @@ All methods work uniformly across different data types, treating single items as
234273
complexQuery.call() // returns Deep instance with multiple results
235274
```
236275

276+
#### Selection
277+
278+
Selection is a special type of association that represents a dynamic query result. When created:
279+
- It executes immediately once and stores the result in `selection.to`
280+
- `selection.to` always contains the latest query result
281+
- Calling `selection.call()` re-executes the query and updates the results
282+
- The selection automatically updates when the underlying data changes
283+
237284
#### Modify (Coming Soon)
238285
- `insert({ type, from, to, value })`Deep - Creates new link
239286
- `update({ type?, from?, to?, value? })`Deep - Updates existing link

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"title": "deep",
44
"appId": "com.deep_foundation.deep.app",
55
"macCategory": "public.app-category.developer-tools",
6-
"version": "7.0.0-alpha.6",
6+
"version": "7.0.0-alpha.7",
77
"description": "Universal solution for working with any meaning",
88
"license": "Unlicense",
99
"main": "dist/cli.js",

src/deep.ts

+108-60
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,8 @@ export class Memory {
133133
}
134134
}
135135

136-
export interface DeepEvent {
137-
name: 'change';
136+
export interface Event {
137+
name: 'change' | 'new' | 'kill' | 'add' | 'remove';
138138
deep: Deep;
139139
prev: {
140140
id?: string;
@@ -890,8 +890,8 @@ export class Deep {
890890
field?: 'id' | 'type' | 'from' | 'to' | 'value',
891891
previousValue?: any,
892892
currentValue?: any
893-
): DeepEvent {
894-
const event: DeepEvent = {
893+
): Event {
894+
const event: Event = {
895895
name: 'change',
896896
deep: this,
897897
prev: {},
@@ -909,7 +909,7 @@ export class Deep {
909909
/**
910910
* Gets the deep.Id instance .to this Deep instance .from current this.deep agent. If no ID is set, one will be created.
911911
* @param value - Optional ID value
912-
* @param agent - Optional agent Deep instance
912+
* @param agent - Optional agent Deep instance, default this.deep
913913
* @returns ID value
914914
*/
915915
id(value?: string, agent: Deep = this.deep): string {
@@ -1483,7 +1483,7 @@ export class Deep {
14831483
relation.from = selection;
14841484
relation.to = nestedSelection;
14851485
relation.value = i;
1486-
relation.on((e) => selection.emit(e));
1486+
relation.on((e: Event) => selection.emit(e));
14871487
}
14881488
} else {
14891489
exp = this.deep.Exp.new({});
@@ -1493,7 +1493,7 @@ export class Deep {
14931493
const relation = this.deep.__id.new();
14941494
relation.from = selection;
14951495
relation.to = input.id;
1496-
relation.on((e) => selection.emit(e));
1496+
relation.on((e: Event) => selection.emit(e));
14971497
} else throw new Error(` Only Deep or string can be value in exp (id)!`);
14981498
}
14991499
for (let key in this.deep.contains.relations.call) {
@@ -1503,11 +1503,11 @@ export class Deep {
15031503
const nestedSelection = this.selection();
15041504
this.exp(input[key], nestedSelection);
15051505
exp.call[key] = nestedSelection;
1506-
nestedSelection.on((e) => relation.emit(e));
1506+
nestedSelection.on((e: Event) => relation.emit(e));
15071507
} else throw new Error(` Only Deep or plain objects Exp can be value in exp (${key})!`);
15081508
relation.from = selection;
15091509
relation.to = exp.call[key]; // nestedSelection
1510-
relation.on((e) => selection.emit(e));
1510+
relation.on((e: Event) => selection.emit(e));
15111511
}
15121512
}
15131513
for (let logic of this.deep.Logic.typed) {
@@ -1519,7 +1519,7 @@ export class Deep {
15191519
exp.call[logic.name] = nestedSelection;
15201520
relation.from = selection;
15211521
relation.to = exp.call[logic.name];
1522-
relation.on((e) => selection.emit(e));
1522+
relation.on((e: Event) => selection.emit(e));
15231523
}
15241524
}
15251525
}
@@ -1533,52 +1533,56 @@ export class Deep {
15331533
selection() {
15341534
const rels = this.deep.contains.relations.call;
15351535
const selection = this.deep.Selection.new(() => {
1536-
const relations = selection.out;
1536+
const inRelations = selection.inof(this.deep.Relation);
1537+
const outRelations = selection.outof(this.deep.Relation);
15371538
let set;
1538-
for (let relation of relations) {
1539-
if (relation.typeof(this.deep.Relation)) {
1540-
if (relation.typeof(this.deep.__id)) {
1541-
if (isDeep(relation.to)) {
1542-
set = set ? set.intersection(new Set([relation.to])) : new Set([relation.to]);
1543-
} else if (isString(relation.to.call)) {
1544-
throw new Error(' Sorry not relized yet.');
1545-
} else throw new Error(' Only Deep and string can be .id');
1546-
} else if (relation.typeof(this.deep.Many)) {
1547-
const nextSet = relation.to.call()[`${rels[relation.type.name].invert}s`].call;
1548-
set = set ? set.intersection(nextSet) : nextSet;
1549-
} else if (relation.typeof(this.deep.One)) {
1550-
const nextSet = relation.to.type === this.deep.Selection ?
1551-
relation.to.call().reduce((result, d) => result.union(d[rels[relation.type.name].invert].call), new Set()) :
1552-
relation.to[rels[relation.type.name].invert].call;
1553-
set = set ? set.intersection(nextSet) : nextSet;
1554-
} else if (relation.typeof(this.deep.Condition)) {
1555-
1556-
} else if (relation.typeof(this.deep.Logic)) {
1557-
if (relation.typeof(this.deep.contains.not)) {
1558-
const currentSet = set || this.deep.Everything.call;
1559-
const notSet = relation.to.call().call;
1560-
set = currentSet.difference(notSet);
1561-
} else if (relation.typeof(this.deep.contains.and)) {
1562-
const currentSet = set || this.deep.Everything.call;
1563-
const arrayOfSets = relation.to.call();
1564-
set = arrayOfSets.reduce((result, set) => {
1565-
return result.intersection(set.call);
1566-
}, currentSet);
1567-
} else if (relation.typeof(this.deep.contains.or)) {
1568-
const arrayOfSets = relation.to.call();
1569-
set = arrayOfSets.reduce((result, item) => {
1570-
const itemSet = item.call;
1571-
return result ? new Set([...result, ...itemSet]) : itemSet;
1572-
}, set);
1573-
}
1574-
} else if (relation.typeof(this.deep.Order)) {
1575-
const nextSet = relation.to.call();
1576-
set = set ? (set.push(nextSet), set) : [nextSet];
1539+
for (let relation of outRelations) {
1540+
if (relation.typeof(this.deep.__id)) {
1541+
if (isDeep(relation.to)) {
1542+
set = set ? set.intersection(new Set([relation.to])) : new Set([relation.to]);
1543+
} else if (isString(relation.to.call)) {
1544+
throw new Error(' Sorry not relized yet.');
1545+
} else throw new Error(' Only Deep and string can be .id');
1546+
} else if (relation.typeof(this.deep.Many)) {
1547+
const nextSet = relation.to.call()[`${rels[relation.type.name].invert}s`].call;
1548+
set = set ? set.intersection(nextSet) : nextSet;
1549+
} else if (relation.typeof(this.deep.One)) {
1550+
const nextSet = relation.to.type === this.deep.Selection ?
1551+
relation.to.call().reduce((result, d) => result.union(d[rels[relation.type.name].invert].call), new Set()) :
1552+
relation.to[rels[relation.type.name].invert].call;
1553+
set = set ? set.intersection(nextSet) : nextSet;
1554+
} else if (relation.typeof(this.deep.Condition)) {
1555+
1556+
} else if (relation.typeof(this.deep.Logic)) {
1557+
if (relation.typeof(this.deep.contains.not)) {
1558+
const currentSet = set || this.deep.Everything.call;
1559+
const notSet = relation.to.call().call;
1560+
set = currentSet.difference(notSet);
1561+
} else if (relation.typeof(this.deep.contains.and)) {
1562+
const currentSet = set || this.deep.Everything.call;
1563+
const arrayOfSets = relation.to.call();
1564+
set = arrayOfSets.reduce((result, set) => {
1565+
return result.intersection(set.call);
1566+
}, currentSet);
1567+
} else if (relation.typeof(this.deep.contains.or)) {
1568+
const arrayOfSets = relation.to.call();
1569+
set = arrayOfSets.reduce((result, item) => {
1570+
const itemSet = item.call;
1571+
return result ? new Set([...result, ...itemSet]) : itemSet;
1572+
}, set);
15771573
}
1574+
} else if (relation.typeof(this.deep.Order)) {
1575+
const nextSet = relation.to.call();
1576+
set = set ? (set.push(nextSet), set) : [nextSet];
15781577
}
15791578
}
15801579
if (!set) set = this.deep.Everything.call;
15811580
const result = this.wrap(set);
1581+
if (!inRelations.size) {
1582+
const oldSet = selection.to?.call || new Set();
1583+
const newSet = result.call;
1584+
this.emitDifference(oldSet, newSet, selection);
1585+
}
15821586
selection.to = result;
15831587
return selection.to;
15841588
});
@@ -1603,6 +1607,40 @@ export class Deep {
16031607
return selection;
16041608
}
16051609

1610+
/**
1611+
* Emits difference events between two sets
1612+
* @param before - Set of items before change
1613+
* @param after - Set of items after change
1614+
* @param target - Deep instance to emit events on
1615+
*/
1616+
public emitDifference(before: Set<Deep>, after: Set<Deep>, target: Deep): void {
1617+
// Find added elements (present in after, not in before)
1618+
for (const item of after) {
1619+
if (!before.has(item)) {
1620+
const event: Event = {
1621+
name: 'add',
1622+
deep: item,
1623+
prev: { value: null },
1624+
next: { value: item }
1625+
};
1626+
target.emit(event);
1627+
}
1628+
}
1629+
1630+
// Find removed elements (present in before, not in after)
1631+
for (const item of before) {
1632+
if (!after.has(item)) {
1633+
const event: Event = {
1634+
name: 'remove',
1635+
deep: item,
1636+
prev: { value: item },
1637+
next: { value: null }
1638+
};
1639+
target.emit(event);
1640+
}
1641+
}
1642+
}
1643+
16061644
/**
16071645
* Gets, or creates if not exists the event emitter for this Deep instance
16081646
* @returns Event emitter instance
@@ -1616,8 +1654,14 @@ export class Deep {
16161654
* Emits an event from this Deep instance
16171655
* @param args - Event arguments
16181656
*/
1619-
emit(...args) {
1620-
if (this._on) this._on.emit(...args);
1657+
emit(...args: any[]): void {
1658+
if (this._on) {
1659+
const event = args[0];
1660+
if (typeof event === 'object' && !event.deep) {
1661+
event.deep = this;
1662+
}
1663+
this._on.emit(...args);
1664+
}
16211665
}
16221666

16231667
/**
@@ -1628,7 +1672,7 @@ export class Deep {
16281672
inof(type: Deep): Deep {
16291673
const result = new Set<Deep>();
16301674
for (const link of this.in.call) {
1631-
if (link.type === type) {
1675+
if (link.typeof(type)) {
16321676
result.add(link);
16331677
}
16341678
}
@@ -1643,7 +1687,7 @@ export class Deep {
16431687
outof(type: Deep): Deep {
16441688
const result = new Set<Deep>();
16451689
for (const link of this.out.call) {
1646-
if (link.type === type) {
1690+
if (link.typeof(type)) {
16471691
result.add(link);
16481692
}
16491693
}
@@ -1925,9 +1969,9 @@ export class Deep {
19251969
}
19261970

19271971
/**
1928-
* Проходит по пути из строк через contains и возвращает найденный deep или undefined
1929-
* @param paths массив строк, представляющих путь через contains
1930-
* @returns найденный deep или undefined
1972+
* Proceeds along the path through strings through contains and returns the found deep or undefined
1973+
* @param paths array of strings representing the path through contains
1974+
* @returns found deep or undefined
19311975
*/
19321976
go(...paths: string[]): Deep | undefined {
19331977
let current: Deep = this;
@@ -1939,8 +1983,8 @@ export class Deep {
19391983
}
19401984

19411985
/**
1942-
* Возвращает путь к текущему deep через входящие Contain связи
1943-
* @returns массив строк - путь через contains
1986+
* Returns the path to the current deep through incoming Contain connections
1987+
* @returns array of strings - path through contains
19441988
*/
19451989
path(): string[] {
19461990
const result: string[] = [];
@@ -1954,7 +1998,7 @@ export class Deep {
19541998
const contain = contains.first;
19551999
if (!contain) break;
19562000

1957-
// Находим ключ в contains, по которому хранится текущий deep
2001+
// Find the key in contains where the current deep is stored
19582002
const from = contain.from;
19592003
if (!from) break;
19602004

@@ -2013,3 +2057,7 @@ export class Contains {
20132057
this.deep = deep;
20142058
}
20152059
}
2060+
2061+
// Global instance of Deep
2062+
export const deep = new Deep();
2063+
export default deep;

0 commit comments

Comments
 (0)