@feltdb/core 0.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.
Files changed (65) hide show
  1. package/README.md +184 -0
  2. package/dist/agent-decision.d.ts +81 -0
  3. package/dist/agent-decision.d.ts.map +1 -0
  4. package/dist/agent-decision.js +18 -0
  5. package/dist/agent-memory.d.ts +91 -0
  6. package/dist/agent-memory.d.ts.map +1 -0
  7. package/dist/agent-memory.js +21 -0
  8. package/dist/agent-observation.d.ts +61 -0
  9. package/dist/agent-observation.d.ts.map +1 -0
  10. package/dist/agent-observation.js +12 -0
  11. package/dist/agent-registry.d.ts +92 -0
  12. package/dist/agent-registry.d.ts.map +1 -0
  13. package/dist/agent-registry.js +134 -0
  14. package/dist/agent-runtime.d.ts +147 -0
  15. package/dist/agent-runtime.d.ts.map +1 -0
  16. package/dist/agent-runtime.js +240 -0
  17. package/dist/agent.d.ts +132 -0
  18. package/dist/agent.d.ts.map +1 -0
  19. package/dist/agent.js +58 -0
  20. package/dist/capability.d.ts +21 -0
  21. package/dist/capability.d.ts.map +1 -0
  22. package/dist/capability.js +23 -0
  23. package/dist/collection.d.ts +95 -0
  24. package/dist/collection.d.ts.map +1 -0
  25. package/dist/collection.js +289 -0
  26. package/dist/db.d.ts +504 -0
  27. package/dist/db.d.ts.map +1 -0
  28. package/dist/db.js +700 -0
  29. package/dist/execution.d.ts +148 -0
  30. package/dist/execution.d.ts.map +1 -0
  31. package/dist/execution.js +18 -0
  32. package/dist/feltdb.d.ts +82 -0
  33. package/dist/feltdb.d.ts.map +1 -0
  34. package/dist/feltdb.js +6 -0
  35. package/dist/flowspec.d.ts +64 -0
  36. package/dist/flowspec.d.ts.map +1 -0
  37. package/dist/flowspec.js +272 -0
  38. package/dist/http-db.d.ts +50 -0
  39. package/dist/http-db.d.ts.map +1 -0
  40. package/dist/http-db.js +205 -0
  41. package/dist/index.d.ts +32 -0
  42. package/dist/index.d.ts.map +1 -0
  43. package/dist/index.js +27 -0
  44. package/dist/indexeddb-db.d.ts +54 -0
  45. package/dist/indexeddb-db.d.ts.map +1 -0
  46. package/dist/indexeddb-db.js +175 -0
  47. package/dist/memory-db.d.ts +49 -0
  48. package/dist/memory-db.d.ts.map +1 -0
  49. package/dist/memory-db.js +97 -0
  50. package/dist/operation.d.ts +25 -0
  51. package/dist/operation.d.ts.map +1 -0
  52. package/dist/operation.js +16 -0
  53. package/dist/reactive-graph.d.ts +67 -0
  54. package/dist/reactive-graph.d.ts.map +1 -0
  55. package/dist/reactive-graph.js +118 -0
  56. package/dist/recovery.d.ts +68 -0
  57. package/dist/recovery.d.ts.map +1 -0
  58. package/dist/recovery.js +104 -0
  59. package/dist/storage.d.ts +48 -0
  60. package/dist/storage.d.ts.map +1 -0
  61. package/dist/storage.js +6 -0
  62. package/dist/workflow.d.ts +20 -0
  63. package/dist/workflow.d.ts.map +1 -0
  64. package/dist/workflow.js +12 -0
  65. package/package.json +43 -0
package/dist/agent.js ADDED
@@ -0,0 +1,58 @@
1
+ /**
2
+ * FeltDB Agent Runtime
3
+ *
4
+ * Agents are durable, addressable participants in the FeltDB fabric.
5
+ * An agent's observations, decisions, actions, and results are all part
6
+ * of the same causal graph.
7
+ */
8
+ /**
9
+ * Lifecycle states for agent execution
10
+ */
11
+ export var AgentExecutionStatus;
12
+ (function (AgentExecutionStatus) {
13
+ AgentExecutionStatus["Created"] = "Created";
14
+ AgentExecutionStatus["Planning"] = "Planning";
15
+ AgentExecutionStatus["Observing"] = "Observing";
16
+ AgentExecutionStatus["Deciding"] = "Deciding";
17
+ AgentExecutionStatus["Acting"] = "Acting";
18
+ AgentExecutionStatus["Waiting"] = "Waiting";
19
+ AgentExecutionStatus["Completed"] = "Completed";
20
+ AgentExecutionStatus["Failed"] = "Failed";
21
+ AgentExecutionStatus["Cancelled"] = "Cancelled";
22
+ AgentExecutionStatus["Blocked"] = "Blocked";
23
+ })(AgentExecutionStatus || (AgentExecutionStatus = {}));
24
+ /**
25
+ * Parse an agent reference from its canonical form
26
+ * @example parseAgentRef("flow://agent/researcher@1")
27
+ */
28
+ export function parseAgentRef(ref) {
29
+ const match = ref.match(/^flow:\/\/agent\/([^@]+)@(\d+)$/);
30
+ if (!match) {
31
+ throw new Error(`Invalid agent reference format: ${ref}`);
32
+ }
33
+ return {
34
+ name: match[1],
35
+ version: parseInt(match[2], 10),
36
+ toString() {
37
+ return `flow://agent/${this.name}@${this.version}`;
38
+ },
39
+ };
40
+ }
41
+ /**
42
+ * Create an agent reference
43
+ */
44
+ export function createAgentRef(name, version) {
45
+ return {
46
+ name,
47
+ version,
48
+ toString() {
49
+ return `flow://agent/${this.name}@${this.version}`;
50
+ },
51
+ };
52
+ }
53
+ /**
54
+ * Generate a unique execution ID
55
+ */
56
+ export function generateExecutionId(agentName) {
57
+ return `exec-${agentName}-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
58
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Capability registration and execution
3
+ *
4
+ * Capabilities are distributed, pluggable features like search, vector embeddings, etc.
5
+ */
6
+ export interface Capability {
7
+ id: string;
8
+ name: string;
9
+ execute(...args: any[]): Promise<any>;
10
+ }
11
+ export interface CapabilityDefinition {
12
+ name: string;
13
+ handler: (...args: any[]) => Promise<any>;
14
+ }
15
+ export declare class CapabilityRegistry {
16
+ private capabilities;
17
+ register(capability: Capability): void;
18
+ get(id: string): Capability | undefined;
19
+ execute(capabilityId: string, ...args: any[]): Promise<any>;
20
+ }
21
+ //# sourceMappingURL=capability.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capability.d.ts","sourceRoot":"","sources":["../src/capability.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;CACvC;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;CAC3C;AAED,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,YAAY,CAAiC;IAErD,QAAQ,CAAC,UAAU,EAAE,UAAU,GAAG,IAAI;IAItC,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;IAIjC,OAAO,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC;CAOlE"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Capability registration and execution
3
+ *
4
+ * Capabilities are distributed, pluggable features like search, vector embeddings, etc.
5
+ */
6
+ export class CapabilityRegistry {
7
+ constructor() {
8
+ this.capabilities = new Map();
9
+ }
10
+ register(capability) {
11
+ this.capabilities.set(capability.id, capability);
12
+ }
13
+ get(id) {
14
+ return this.capabilities.get(id);
15
+ }
16
+ async execute(capabilityId, ...args) {
17
+ const capability = this.get(capabilityId);
18
+ if (!capability) {
19
+ throw new Error(`Capability not found: ${capabilityId}`);
20
+ }
21
+ return capability.execute(...args);
22
+ }
23
+ }
@@ -0,0 +1,95 @@
1
+ /**
2
+ * FeltDB State-First Collections API
3
+ *
4
+ * Provides a high-level, state-oriented collection abstraction
5
+ * that makes the database disappear from the programming model.
6
+ *
7
+ * Collections now use reactive dependency graphs instead of polling
8
+ * for efficient, real-time state propagation.
9
+ */
10
+ import type { JsDb } from './feltdb.js';
11
+ export type Predicate<T> = (item: T) => boolean;
12
+ export type Subscriber<T> = (items: T[]) => void;
13
+ /**
14
+ * A live collection that automatically updates when underlying data changes.
15
+ * Represents application state, not a one-time query result.
16
+ *
17
+ * Uses reactive dependency graph instead of polling for efficient updates.
18
+ */
19
+ export declare class Collection<T> {
20
+ private db;
21
+ private name;
22
+ private collectionId;
23
+ private predicate;
24
+ private cache;
25
+ private subscribers;
26
+ private parent;
27
+ private parentId;
28
+ private isInitialized;
29
+ private unsubscribeFunctions;
30
+ private graphUnsubscribe;
31
+ private runtimeUnsubscribe;
32
+ constructor(db: JsDb, name: string, predicate?: Predicate<T>, parent?: Collection<T>);
33
+ /**
34
+ * Get all records in this collection.
35
+ * Returns cached results (live-updated).
36
+ */
37
+ all(): Promise<T[]>;
38
+ /** Find records whose fields match the supplied query. */
39
+ find(query?: Partial<T>): Promise<T[]>;
40
+ /**
41
+ * Get a single record by ID.
42
+ */
43
+ get(id: string | number): Promise<T | null>;
44
+ /**
45
+ * Find records matching a predicate.
46
+ * Returns a derived collection that automatically updates.
47
+ */
48
+ where(predicate: Predicate<T>): Collection<T>;
49
+ /**
50
+ * Insert a new record into this collection.
51
+ */
52
+ insert(data: Partial<T>, id?: string | number): Promise<string>;
53
+ /**
54
+ * Update a record in this collection.
55
+ */
56
+ update(id: string | number, changes: Partial<T>): Promise<void>;
57
+ /**
58
+ * Delete a record from this collection.
59
+ */
60
+ delete(id: string | number): Promise<void>;
61
+ /**
62
+ * Count records in this collection.
63
+ */
64
+ count(): Promise<number>;
65
+ /**
66
+ * Check if a record exists.
67
+ */
68
+ exists(id: string | number): Promise<boolean>;
69
+ /**
70
+ * Subscribe to changes in this collection using reactive dependency graph.
71
+ * Returns an unsubscribe function.
72
+ */
73
+ subscribe(subscriber: Subscriber<T>, _pollInterval?: number): () => void;
74
+ /** Release runtime subscriptions owned by this live collection. */
75
+ close(): void;
76
+ /**
77
+ * Refresh data from the database and notify subscribers.
78
+ */
79
+ refresh(): Promise<void>;
80
+ }
81
+ /**
82
+ * Relationship helper for loading related data.
83
+ */
84
+ export declare class Relationship<Parent, Child> {
85
+ private parentDb;
86
+ private childDb;
87
+ private childCollection;
88
+ private foreignKey;
89
+ constructor(parentDb: JsDb, childDb: JsDb, childCollection: string, foreignKey: (child: Child) => string | number);
90
+ /**
91
+ * Load related children for a parent item.
92
+ */
93
+ load(parentId: string | number): Promise<Child[]>;
94
+ }
95
+ //# sourceMappingURL=collection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collection.d.ts","sourceRoot":"","sources":["../src/collection.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAGxC,MAAM,MAAM,SAAS,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC;AAChD,MAAM,MAAM,UAAU,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,IAAI,CAAC;AAEjD;;;;;GAKG;AACH,qBAAa,UAAU,CAAC,CAAC;IACvB,OAAO,CAAC,EAAE,CAAO;IACjB,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,SAAS,CAA6B;IAC9C,OAAO,CAAC,KAAK,CAAW;IACxB,OAAO,CAAC,WAAW,CAAiC;IACpD,OAAO,CAAC,MAAM,CAA8B;IAC5C,OAAO,CAAC,QAAQ,CAAuB;IACvC,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,oBAAoB,CAA8B;IAC1D,OAAO,CAAC,gBAAgB,CAA6B;IACrD,OAAO,CAAC,kBAAkB,CAA6B;gBAE3C,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC;IAepF;;;OAGG;IACG,GAAG,IAAI,OAAO,CAAC,CAAC,EAAE,CAAC;IAKzB,0DAA0D;IACpD,IAAI,CAAC,KAAK,GAAE,OAAO,CAAC,CAAC,CAAM,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC;IAQhD;;OAEG;IACG,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IAajD;;;OAGG;IACH,KAAK,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC;IAO7C;;OAEG;IACG,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAyBrE;;OAEG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IA2BrE;;OAEG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAqBhD;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IAK9B;;OAEG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKnD;;;OAGG;IACH,SAAS,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,IAAI;IAwCxE,mEAAmE;IACnE,KAAK,IAAI,IAAI;IASb;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAgC/B;AAED;;GAEG;AACH,qBAAa,YAAY,CAAC,MAAM,EAAE,KAAK;IACrC,OAAO,CAAC,QAAQ,CAAO;IACvB,OAAO,CAAC,OAAO,CAAO;IACtB,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,UAAU,CAAoC;gBAGpD,QAAQ,EAAE,IAAI,EACd,OAAO,EAAE,IAAI,EACb,eAAe,EAAE,MAAM,EACvB,UAAU,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,MAAM,GAAG,MAAM;IAQ/C;;OAEG;IACG,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;CAoBxD"}
@@ -0,0 +1,289 @@
1
+ /**
2
+ * FeltDB State-First Collections API
3
+ *
4
+ * Provides a high-level, state-oriented collection abstraction
5
+ * that makes the database disappear from the programming model.
6
+ *
7
+ * Collections now use reactive dependency graphs instead of polling
8
+ * for efficient, real-time state propagation.
9
+ */
10
+ import { getReactiveDependencyGraph } from './reactive-graph.js';
11
+ /**
12
+ * A live collection that automatically updates when underlying data changes.
13
+ * Represents application state, not a one-time query result.
14
+ *
15
+ * Uses reactive dependency graph instead of polling for efficient updates.
16
+ */
17
+ export class Collection {
18
+ constructor(db, name, predicate, parent) {
19
+ this.predicate = null;
20
+ this.cache = [];
21
+ this.subscribers = new Set();
22
+ this.parent = null;
23
+ this.parentId = null;
24
+ this.isInitialized = false;
25
+ this.unsubscribeFunctions = new Set();
26
+ this.graphUnsubscribe = null;
27
+ this.runtimeUnsubscribe = null;
28
+ this.db = db;
29
+ this.name = name;
30
+ this.collectionId = `${name}-${Math.random().toString(36).substr(2, 9)}`;
31
+ this.predicate = predicate || null;
32
+ this.parent = parent || null;
33
+ this.parentId = parent ? parent.collectionId : null;
34
+ // Register dependency if this is a derived collection
35
+ if (parent) {
36
+ const graph = getReactiveDependencyGraph();
37
+ graph.registerDependency(parent.collectionId, this.collectionId);
38
+ }
39
+ }
40
+ /**
41
+ * Get all records in this collection.
42
+ * Returns cached results (live-updated).
43
+ */
44
+ async all() {
45
+ await this.refresh();
46
+ return [...this.cache];
47
+ }
48
+ /** Find records whose fields match the supplied query. */
49
+ async find(query = {}) {
50
+ const items = await this.all();
51
+ const entries = Object.entries(query);
52
+ return items.filter((item) => entries.every(([field, expected]) => item[field] === expected));
53
+ }
54
+ /**
55
+ * Get a single record by ID.
56
+ */
57
+ async get(id) {
58
+ const key = `${this.name}:${id}`;
59
+ const result = await this.db.get(key);
60
+ if (result.success && result.data) {
61
+ try {
62
+ return JSON.parse(result.data);
63
+ }
64
+ catch {
65
+ return null;
66
+ }
67
+ }
68
+ return null;
69
+ }
70
+ /**
71
+ * Find records matching a predicate.
72
+ * Returns a derived collection that automatically updates.
73
+ */
74
+ where(predicate) {
75
+ return new Collection(this.db, this.name, (item) => {
76
+ const parentMatch = this.predicate ? this.predicate(item) : true;
77
+ return parentMatch && predicate(item);
78
+ }, this.parent || this);
79
+ }
80
+ /**
81
+ * Insert a new record into this collection.
82
+ */
83
+ async insert(data, id) {
84
+ const recordId = id ?? `${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
85
+ const key = `${this.name}:${recordId}`;
86
+ const stored = typeof data === 'object' && data !== null
87
+ ? { ...data, id: recordId }
88
+ : data;
89
+ const result = await this.db.insert(key, JSON.stringify(stored));
90
+ if (!result.success) {
91
+ throw new Error(`Insert failed: ${result.error}`);
92
+ }
93
+ // Emit change through reactive dependency graph
94
+ const change = {
95
+ type: 'insert',
96
+ key,
97
+ value: stored,
98
+ timestamp: Date.now(),
99
+ };
100
+ const graph = getReactiveDependencyGraph();
101
+ await graph.emitChange(this.name, change);
102
+ return String(recordId);
103
+ }
104
+ /**
105
+ * Update a record in this collection.
106
+ */
107
+ async update(id, changes) {
108
+ const current = await this.get(id);
109
+ if (!current) {
110
+ throw new Error(`Record ${id} not found`);
111
+ }
112
+ const updated = { ...current, ...changes };
113
+ const key = `${this.name}:${id}`;
114
+ const result = await (this.db.update
115
+ ? this.db.update(key, JSON.stringify(updated))
116
+ : this.db.insert(key, JSON.stringify(updated)));
117
+ if (!result.success) {
118
+ throw new Error(`Update failed: ${result.error}`);
119
+ }
120
+ // Emit change through reactive dependency graph
121
+ const change = {
122
+ type: 'update',
123
+ key,
124
+ value: updated,
125
+ timestamp: Date.now(),
126
+ };
127
+ const graph = getReactiveDependencyGraph();
128
+ await graph.emitChange(this.name, change);
129
+ }
130
+ /**
131
+ * Delete a record from this collection.
132
+ */
133
+ async delete(id) {
134
+ const key = `${this.name}:${id}`;
135
+ if (!this.db.delete) {
136
+ throw new Error('Delete is not supported by this FeltDB runtime');
137
+ }
138
+ const result = await this.db.delete(key);
139
+ if (!result.success) {
140
+ throw new Error(`Delete failed: ${result.error}`);
141
+ }
142
+ // Emit delete change through reactive dependency graph
143
+ const change = {
144
+ type: 'delete',
145
+ key,
146
+ value: null,
147
+ timestamp: Date.now(),
148
+ };
149
+ const graph = getReactiveDependencyGraph();
150
+ await graph.emitChange(this.name, change);
151
+ }
152
+ /**
153
+ * Count records in this collection.
154
+ */
155
+ async count() {
156
+ const items = await this.all();
157
+ return items.length;
158
+ }
159
+ /**
160
+ * Check if a record exists.
161
+ */
162
+ async exists(id) {
163
+ const item = await this.get(id);
164
+ return item !== null;
165
+ }
166
+ /**
167
+ * Subscribe to changes in this collection using reactive dependency graph.
168
+ * Returns an unsubscribe function.
169
+ */
170
+ subscribe(subscriber, _pollInterval) {
171
+ this.subscribers.add(subscriber);
172
+ // Initialize cache if needed
173
+ if (!this.isInitialized) {
174
+ this.refresh().catch(err => console.error('Failed to initialize collection:', err));
175
+ }
176
+ // Subscribe to changes via reactive dependency graph
177
+ if (!this.graphUnsubscribe) {
178
+ const notify = async () => {
179
+ await this.refresh();
180
+ for (const sub of this.subscribers)
181
+ sub([...this.cache]);
182
+ };
183
+ const graph = getReactiveDependencyGraph();
184
+ this.graphUnsubscribe = graph.subscribe(this.name, notify);
185
+ if (this.db.subscribe_changes) {
186
+ this.runtimeUnsubscribe = this.db.subscribe_changes(collection => {
187
+ if (collection === this.name)
188
+ void notify();
189
+ });
190
+ }
191
+ }
192
+ // Return unsubscribe function
193
+ return () => {
194
+ this.subscribers.delete(subscriber);
195
+ // If no more subscribers, cleanup
196
+ if (this.subscribers.size === 0) {
197
+ // Unsubscribe from graph
198
+ this.graphUnsubscribe?.();
199
+ this.runtimeUnsubscribe?.();
200
+ this.graphUnsubscribe = null;
201
+ this.runtimeUnsubscribe = null;
202
+ for (const unsub of this.unsubscribeFunctions)
203
+ unsub();
204
+ this.unsubscribeFunctions.clear();
205
+ }
206
+ };
207
+ }
208
+ /** Release runtime subscriptions owned by this live collection. */
209
+ close() {
210
+ this.graphUnsubscribe?.();
211
+ this.runtimeUnsubscribe?.();
212
+ this.graphUnsubscribe = null;
213
+ this.runtimeUnsubscribe = null;
214
+ this.subscribers.clear();
215
+ for (const unsubscribe of this.unsubscribeFunctions)
216
+ unsubscribe();
217
+ this.unsubscribeFunctions.clear();
218
+ }
219
+ /**
220
+ * Refresh data from the database and notify subscribers.
221
+ */
222
+ async refresh() {
223
+ try {
224
+ const result = await this.db.query(this.name);
225
+ if (result.success && result.data) {
226
+ try {
227
+ let items = JSON.parse(result.data);
228
+ // Apply predicate if this is a derived collection
229
+ if (this.predicate) {
230
+ items = items.filter((item) => {
231
+ try {
232
+ return this.predicate(item);
233
+ }
234
+ catch {
235
+ return false;
236
+ }
237
+ });
238
+ }
239
+ // Update cache with all results
240
+ this.cache = items;
241
+ this.isInitialized = true;
242
+ }
243
+ catch (err) {
244
+ console.error('Failed to parse query results:', err);
245
+ }
246
+ }
247
+ else if (!result.success) {
248
+ console.error('Query failed:', result.error);
249
+ }
250
+ }
251
+ catch (err) {
252
+ console.error('Refresh error:', err);
253
+ }
254
+ }
255
+ }
256
+ /**
257
+ * Relationship helper for loading related data.
258
+ */
259
+ export class Relationship {
260
+ constructor(parentDb, childDb, childCollection, foreignKey) {
261
+ this.parentDb = parentDb;
262
+ this.childDb = childDb;
263
+ this.childCollection = childCollection;
264
+ this.foreignKey = foreignKey;
265
+ }
266
+ /**
267
+ * Load related children for a parent item.
268
+ */
269
+ async load(parentId) {
270
+ const result = await this.childDb.query(this.childCollection);
271
+ if (!result.success || !result.data) {
272
+ return [];
273
+ }
274
+ try {
275
+ const children = JSON.parse(result.data);
276
+ return children.filter((child) => {
277
+ try {
278
+ return this.foreignKey(child) === parentId;
279
+ }
280
+ catch {
281
+ return false;
282
+ }
283
+ });
284
+ }
285
+ catch {
286
+ return [];
287
+ }
288
+ }
289
+ }