Skip to content

Commit bc7b356

Browse files
committed
7.0.0-alpha.5: graph navigation and events improvements
- Add go() method for graph navigation through contains - Improve value unpacking with proper type handling - Return unsubscribe function from on() - Fix file sync tests cleanup - Enhance event handling in syncJSONFile - Bump version to 7.0.0-alpha.5
1 parent df8dde5 commit bc7b356

File tree

9 files changed

+356
-156
lines changed

9 files changed

+356
-156
lines changed

package.json

Lines changed: 1 addition & 1 deletion
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.4",
6+
"version": "7.0.0-alpha.5",
77
"description": "Universal solution for working with any meaning",
88
"license": "Unlicense",
99
"main": "dist/cli.js",

src/deep.ts

Lines changed: 35 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -254,20 +254,20 @@ export class Deep {
254254
deep.contains.from = deep.__from = deep.contains.One.new();
255255
deep.contains.out = deep.__out = deep.contains.Many.new();
256256
deep.contains.to = deep.__to = deep.contains.One.new();
257-
deep.contains.out = deep.__out = deep.contains.Many.new();
257+
deep.contains.in = deep.__in = deep.contains.Many.new();
258258
deep.contains.value = deep.__value = deep.contains.One.new();
259259
deep.contains.valued = deep.__valued = deep.contains.Many.new();
260260

261261
const Condition = deep.Condition = deep.contains.Condition = Relation.new();
262262

263-
deep.contains.eq = Condition.new();
264-
deep.contains.neq = Condition.new();
265-
deep.contains.gt = Condition.new();
266-
deep.contains.lt = Condition.new();
267-
deep.contains.gte = Condition.new();
268-
deep.contains.lte = Condition.new();
269-
deep.contains.in = Condition.new();
270-
deep.contains.nin = Condition.new();
263+
// deep.contains.eq = Condition.new();
264+
// deep.contains.neq = Condition.new();
265+
// deep.contains.gt = Condition.new();
266+
// deep.contains.lt = Condition.new();
267+
// deep.contains.gte = Condition.new();
268+
// deep.contains.lte = Condition.new();
269+
// deep.contains.in = Condition.new();
270+
// deep.contains.nin = Condition.new();
271271

272272
const Logic = deep.Logic = deep.contains.Logic = Relation.new();
273273

@@ -1499,9 +1499,7 @@ export class Deep {
14991499
for (let key in this.deep.contains.relations.call) {
15001500
if (input.hasOwnProperty(key)) {
15011501
const relation = this.deep.contains[key].new();
1502-
if (isDeep(input[key])) {
1503-
exp.call[key] = input[key];
1504-
} else if (isPlainObject(input[key])) {
1502+
if (isDeep(input[key]) || isPlainObject(input[key])) {
15051503
const nestedSelection = this.selection();
15061504
this.exp(input[key], nestedSelection);
15071505
exp.call[key] = nestedSelection;
@@ -1546,7 +1544,7 @@ export class Deep {
15461544
throw new Error(' Sorry not relized yet.');
15471545
} else throw new Error(' Only Deep and string can be .id');
15481546
} else if (relation.typeof(this.deep.Many)) {
1549-
const nextSet = new Set([relation.to[rels[relation.type.name].invert]]);
1547+
const nextSet = relation.to.call()[`${rels[relation.type.name].invert}s`].call;
15501548
set = set ? set.intersection(nextSet) : nextSet;
15511549
} else if (relation.typeof(this.deep.One)) {
15521550
const nextSet = relation.to.type === this.deep.Selection ?
@@ -1769,21 +1767,17 @@ export class Deep {
17691767
* @returns Deep instance with unpacked value
17701768
*/
17711769
valueUnpack(packedValue: Pack['values'][0]): Deep {
1772-
try {
1773-
let typeDeep;
1774-
for (const type of [this.Symbol, this.Undefined, this.Promise, this.Boolean,
1775-
this.String, this.Number, this.BigInt, this.Set,
1776-
this.WeakSet, this.Map, this.WeakMap, this.Array,
1777-
this.Object, this.Function]) {
1778-
if (type.name === packedValue.type) {
1779-
typeDeep = type;
1780-
break;
1781-
}
1782-
}
1783-
if (!typeDeep) {
1784-
throw new Error(` Unknown type ${packedValue.type} for value`);
1770+
let typeDeep;
1771+
for (const type of this.deep.contains.Value.typed) {
1772+
if (type.name === packedValue.type) {
1773+
typeDeep = type;
1774+
break;
17851775
}
1786-
1776+
}
1777+
if (!typeDeep) {
1778+
throw new Error(` Unknown type ${packedValue.type} for value`);
1779+
}
1780+
try {
17871781
switch(packedValue.type) {
17881782
case 'Symbol':
17891783
return this.wrap(Symbol());
@@ -1929,6 +1923,20 @@ export class Deep {
19291923

19301924
return this.select({ or });
19311925
}
1926+
1927+
/**
1928+
* Проходит по пути из строк через contains и возвращает найденный deep или undefined
1929+
* @param paths массив строк, представляющих путь через contains
1930+
* @returns найденный deep или undefined
1931+
*/
1932+
go(...paths: string[]): Deep | undefined {
1933+
let current: Deep = this;
1934+
for (const p of paths) {
1935+
if (!current?.contains?.[p]) return undefined;
1936+
current = current.contains[p];
1937+
}
1938+
return current;
1939+
}
19321940
}
19331941

19341942
/**

src/main.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
export * from './deep.js';
55
export * from './on.js';
66
export * from './benchmark.js';
7+
export * from './pckg.js';
78

89
const { argv } = process;
910
const args = argv.slice(2);

src/on.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export function On(customOn?: any): OnI {
99
let callbacks: any[] = []; // Deeps in future
1010
const on = function (callback) {
1111
callbacks.push(customOn ? customOn(...arguments) : callback);
12+
return () => on.off(callback);
1213
};
1314
on.off = (callback) => {
1415
callbacks = callbacks.filter(c => c != callback);

src/pckg.ts

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import { Deep } from './deep.js';
2+
import chokidar from 'chokidar';
3+
import fs from 'fs/promises';
4+
5+
/**
6+
* Synchronizes a JSON file with a Deep selection.
7+
* When file changes, creates a new selection from the file content.
8+
* When input selection changes, saves it to the file.
9+
*
10+
* @param inputSelection - The Deep selection to sync with file
11+
* @param path - Path to the JSON file
12+
* @returns The selection created from file content
13+
*/
14+
export async function syncJSONFile(inputSelection: Deep, path: string): Promise<Deep> {
15+
const deep = inputSelection.deep;
16+
let SyncJSONFile = deep.contains.SyncJSONFile;
17+
if (!SyncJSONFile) {
18+
SyncJSONFile = deep.contains.SyncJSONFile = deep.new();
19+
const allJSONFiles = deep.select({ type: SyncJSONFile });
20+
allJSONFiles.on((event) => {
21+
if (event.name === 'kill') {
22+
23+
}
24+
});
25+
}
26+
const syncJSONFile = SyncJSONFile.new();
27+
28+
// Create initial selection from file
29+
try {
30+
const fileContent = await fs.readFile(path, 'utf-8');
31+
const data = JSON.parse(fileContent);
32+
const fileSelection = inputSelection.unpack(data);
33+
} catch (error) {
34+
if (error.code === 'ENOENT') { // File doesn't exist
35+
// Save initial selection to file
36+
const data = await inputSelection.pack;
37+
await fs.writeFile(path, JSON.stringify(data, null, 2));
38+
} else {
39+
console.error(`Error loading initial data from ${path}:`, error);
40+
throw error;
41+
}
42+
}
43+
44+
// Watch for file changes
45+
const watcher = chokidar.watch(path, {
46+
persistent: true,
47+
ignoreInitial: true,
48+
});
49+
syncJSONFile.from = deep.wrap(() => {
50+
console.log('closing watcher');
51+
watcher.close();
52+
});
53+
54+
let isUpdatingFile = false; // Flag to prevent recursive updates
55+
56+
watcher.on('change', async () => {
57+
if (isUpdatingFile) return; // Skip if we're currently updating the file
58+
59+
try {
60+
const fileContent = await fs.readFile(path, 'utf-8');
61+
const data = JSON.parse(fileContent);
62+
const fileSelection = await inputSelection.unpack(data);
63+
} catch (error) {
64+
console.error(`Error processing file change for ${path}:`, error);
65+
}
66+
});
67+
68+
// Watch for input selection changes
69+
const unsubscribe = inputSelection.on(async (event) => {
70+
if (isUpdatingFile) return; // Skip if we're currently updating the file
71+
72+
try {
73+
isUpdatingFile = true;
74+
const data = await inputSelection.pack;
75+
await fs.writeFile(path, JSON.stringify(data, null, 2));
76+
} catch (error) {
77+
console.error(`Error saving changes to ${path}:`, error);
78+
} finally {
79+
isUpdatingFile = false;
80+
}
81+
});
82+
syncJSONFile.to = deep.wrap(unsubscribe);
83+
84+
// Add cleanup method to the returned selection
85+
syncJSONFile.kill = () => {
86+
syncJSONFile.from.call();
87+
syncJSONFile.to.call();
88+
};
89+
90+
return syncJSONFile;
91+
}
92+
93+
export default syncJSONFile;

src/test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
import './tests/core.js';
22
import './tests/sync.js';
3+
import './tests/select.js';
34
// import './tests/langchain.js';

src/tests/core.ts

Lines changed: 22 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -547,97 +547,6 @@ test('getById', () => {
547547
assert.equal(deep.getById('entity-a', agent), undefined);
548548
});
549549

550-
test('select type, type.type', () => {
551-
const deep = new Deep();
552-
const prevAllSize = deep.memory.all.size;
553-
const A = deep.new();
554-
const B = deep.new();
555-
B.from = A; B.to = A;
556-
const a = deep.new(); a.type = A;
557-
const b = deep.new(); b.type = B;
558-
b.from = a; b.to = a;
559-
assert.equal(A.type, deep);
560-
assert.equal(A.from, undefined);
561-
assert.equal(A.to, undefined);
562-
assert.equal(A.value, undefined);
563-
assert.equal(B.type, deep);
564-
assert.equal(B.from, A);
565-
assert.equal(B.to, A);
566-
assert.equal(B.value, undefined);
567-
assert.equal(a.type, A);
568-
assert.equal(a.from, undefined);
569-
assert.equal(a.to, undefined);
570-
assert.equal(a.value, undefined);
571-
assert.equal(b.type, B);
572-
assert.equal(b.from, a);
573-
assert.equal(b.to, a);
574-
assert.equal(b.value, undefined);
575-
});
576-
577-
test('select from.type to.type', () => {
578-
const deep = new Deep();
579-
const A = deep.new();
580-
const B = deep.new();
581-
B.from = A; B.to = A;
582-
const C = deep.new();
583-
C.from = A; C.to = B;
584-
assert.equal(deep.contains.type.typed.size, 0);
585-
assert.equal(deep.contains.from.typed.size, 0);
586-
assert.equal(deep.contains.to.typed.size, 0);
587-
const selection = deep.select({ from: { type: A }, to: { type: B } });
588-
assert.equal(deep.contains.type.typed.size, 2);
589-
assert.equal(deep.contains.from.typed.size, 1);
590-
assert.equal(deep.contains.to.typed.size, 1);
591-
let result = selection.call();
592-
let outerCounter = 0;
593-
selection.on(() => {
594-
outerCounter++;
595-
result = selection.call();
596-
});
597-
const relations1 = selection.out;
598-
assert.equal(relations1.size, 2);
599-
let innerRelationsCounter = 0;
600-
relations1.each(r => r.on(() => {
601-
innerRelationsCounter++;
602-
}))
603-
assert.equal(outerCounter, 0);
604-
assert.equal(innerRelationsCounter, 0);
605-
assert.equal(result.size, 0);
606-
const a = A.new();
607-
assert.equal(innerRelationsCounter, 2);
608-
assert.equal(outerCounter, 2);
609-
assert.equal(result.size, 0);
610-
const b = B.new();
611-
assert.equal(innerRelationsCounter, 4);
612-
assert.equal(outerCounter, 4);
613-
assert.equal(result.size, 0);
614-
const c = C.new();
615-
assert.equal(innerRelationsCounter, 4);
616-
assert.equal(result.size, 0);
617-
c.from = a;
618-
assert.equal(innerRelationsCounter, 5);
619-
assert.equal(result.size, 0);
620-
c.to = b;
621-
assert.equal(innerRelationsCounter, 6);
622-
assert.equal(result.size, 1);
623-
assert.equal(outerCounter, 7);
624-
});
625-
626-
test('select result changes', () => {
627-
const deep = new Deep();
628-
const A = deep.new();
629-
const B = deep.new();
630-
const b = B.new();
631-
const selection = deep.select({ type: B });
632-
assert.equal(selection.to.size, 1);
633-
let outerCounter = 0;
634-
selection.on(() => {
635-
outerCounter++;
636-
});
637-
b.from = A; b.to = A;
638-
assert.equal(outerCounter, 2);
639-
});
640-
641550
test('not operator', () => {
642551
const deep = new Deep();
643552
const prevAllSize = deep.memory.all.size;
@@ -820,41 +729,6 @@ test('association events order', () => {
820729
}
821730
});
822731

823-
test.skip('benchmark', async () => {
824-
const deep = new Deep();
825-
const { Benchmark, Benchmarked } = benchmarks(deep);
826-
827-
// Function that creates new Deep instances
828-
const testFn = () => {
829-
const d = deep.new();
830-
const d2 = deep.new();
831-
d.from = d2;
832-
return d;
833-
};
834-
835-
// Run benchmark
836-
const benchmarked = await Benchmark.call(testFn);
837-
838-
// Check that result is a Deep instance
839-
assert(benchmarked.typeof(Benchmarked));
840-
841-
// Check that from contains the test function
842-
assert(deep.isDeep(benchmarked.from));
843-
assert.equal(benchmarked.from.value, testFn);
844-
845-
// Check that to contains benchmark results
846-
assert(deep.isDeep(benchmarked.to));
847-
const result = benchmarked.to.value;
848-
assert(result.target);
849-
assert(typeof result.hz === 'number');
850-
assert(result.stats);
851-
assert(result.times);
852-
853-
// Check that value contains hz
854-
assert(typeof benchmarked.call, 'number');
855-
assert.equal(benchmarked.value, result.hz);
856-
});
857-
858732
test('inof and outof with multiple types', () => {
859733
const deep = new Deep();
860734

@@ -985,3 +859,25 @@ test('collection getters (types, froms, tos, typeds, outs, ins)', () => {
985859
}
986860
}
987861
});
862+
863+
test('go method', () => {
864+
const deep = new Deep();
865+
866+
// Создаем тестовую структуру
867+
const a = deep.new();
868+
deep.contains.a = a;
869+
const b = deep.new();
870+
a.contains.b = b;
871+
const c = deep.new();
872+
b.contains.c = c;
873+
874+
// Проверяем успешный поиск
875+
assert.equal(deep.go('a', 'b', 'c'), c);
876+
assert.equal(deep.go('a', 'b'), b);
877+
assert.equal(deep.go('a'), a);
878+
879+
// Проверяем неуспешный поиск
880+
assert.equal(deep.go('x'), undefined);
881+
assert.equal(deep.go('a', 'x'), undefined);
882+
assert.equal(deep.go('a', 'b', 'x'), undefined);
883+
});

0 commit comments

Comments
 (0)