Collection Instance Filtering and SurrealDB Graph Traversal Improvements#650
Collection Instance Filtering and SurrealDB Graph Traversal Improvements#650
Conversation
Implement filtering of collections by class instance type using the where.isInstance
option in @Collection decorators. This allows collections to automatically filter
items to only include instances of a specific class.
Changes:
- Add isInstance filtering in getData() for single instance loading
- Add isInstance filtering in instancesFromSurrealResult() for query results
- Support both class reference (isInstance: App) and string name (isInstance: 'Message') syntaxes
- Make PerspectiveProxy.getSubjectClassMetadataFromSDNA() and batchCheckSubjectInstances() public
- Use batch checking via batchCheckSubjectInstances() to efficiently filter collections
This enables cleaner collection definitions without custom Prolog conditions:
@Collection({ through: 'ad4m://has_child', where: { isInstance: App } })
…Ad4mModel Add surrealGetter option to PropertyOptions and CollectionOptions decorators, enabling custom SurrealQL expressions for reverse link lookups and complex graph queries. This allows properties and collections to use graph traversal syntax (e.g., <-link for incoming links) without breaking Prolog compatibility. Key changes: - Add surrealGetter field to PropertyMetadata and CollectionMetadata interfaces - Implement evaluateSurrealGettersForInstance() method to execute custom queries - Skip properties/collections with surrealGetter during normal link processing - Evaluate surrealGetter expressions post-query using node graph context - Filter out 'None' and empty string values from results - Add comprehensive documentation for surrealGetter usage This enables bi-directional relationship traversals (e.g., finding parent from child in reply chains) while maintaining separation between Prolog and SurrealQL query paths, preventing engine crashes from mixed syntax. Example usage: ```typescript @optional({ through: HAS_REPLY, surrealGetter: "(<-link[WHERE predicate = 'flux://has_reply'].in.uri)[0]" }) replyingTo: string | undefined;
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds optional Changes
Sequence DiagramsequenceDiagram
autonumber
participant Client as Client
participant Meta as ModelMetadata
participant Loader as DataLoader
participant Perspective as PerspectiveProxy
participant Eval as SurrealQL_Evaluator
participant Instance as ModelInstance
Client->>Meta: getModelMetadata()
Meta-->>Client: metadata (may include surrealGetter)
Client->>Loader: request load instance(s)
Loader->>Perspective: fetch SDNA / SurrealDB data
Perspective-->>Loader: raw triples / links / timestamps
Loader->>Loader: populate fields (skip surrealGetter fields)
Loader->>Eval: evaluateSurrealGettersForInstance(instance, perspective, metadata)
Eval->>Perspective: execute SurrealQL getters
Perspective-->>Eval: getter results
Eval->>Instance: assign evaluated property/collection values
Instance-->>Client: fully populated instance(s) with createdAt/updatedAt
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
core/src/model/Ad4mModel.ts (1)
781-867: Use@ModelOptionsclassName when resolvingwhere.isInstance.Line 841 uses the constructor
.name, which can diverge from the SDNA class name (minified builds or custom@ModelOptions). This can lead to false negatives inwhere.isInstancefiltering. Prefer the prototype/className fallback used elsewhere.🛠️ Suggested fix
- const className = typeof collMeta.where.isInstance === 'string' - ? collMeta.where.isInstance - : collMeta.where.isInstance.name; + const className = typeof collMeta.where.isInstance === 'string' + ? collMeta.where.isInstance + : (collMeta.where.isInstance.prototype?.className || + collMeta.where.isInstance.className || + collMeta.where.isInstance.name);
🤖 Fix all issues with AI agents
In `@core/src/model/Ad4mModel.ts`:
- Around line 1718-1756: The code currently calls
evaluateSurrealGettersForInstance twice for each instance which causes SurrealDB
re-queries and overwrites collections filtered by where.isInstance; keep a
single evaluation pass: run evaluateSurrealGettersForInstance once (so
collection values exist for filtering), perform the where.isInstance filtering
using perspective.getSubjectClassMetadataFromSDNA and
perspective.batchCheckSubjectInstances on metadata.collections entries, and
remove the second loop that re-invokes evaluateSurrealGettersForInstance to
avoid doubling queries and overwriting filtered instance[collName] values.
Add comprehensive documentation explaining why ensureSDNASubjectClass() cannot be called in parallel due to sdna_change_mutex in Rust executor. The document covers: - Root cause: Mutex serialization causing deadlocks with concurrent calls - Historical context: Limitation present since March 2024 (c6f1f96) - Current workaround: Sequential execution instead of Promise.all() - Four solution options with trade-offs analysis - Recommended hybrid approach with phased implementation - Migration guide for downstream consumers (Flux, we-*) This addresses hangs/timeouts when 12+ SDNA registrations are made concurrently, particularly during community/space initialization.
Changed timestamp tracking from single MAX to MIN/MAX (createdAt/updatedAt): - createdAt: Earliest link timestamp (MIN) - when instance was first created - updatedAt: Most recent link timestamp (MAX) - when last modified - author: Changed from latest author to original author (based on createdAt) - Added backwards-compatible timestamp getter that returns createdAt Implementation details: - Both fields computed in-memory during getData() and instancesFromSurrealResult() - NOT persisted as @Property decorators - derived from link timestamps - Updated SurrealDB queries to track both MIN and MAX timestamps - Prolog queries still return earliest timestamp (backwards compatible) This enables proper temporal tracking while maintaining API compatibility.
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
core/src/model/Ad4mModel.ts (2)
786-845: createdAt/updatedAt ignore collection-only updates.
Line 802 updates timestamps only while iterating property links, so a collection update won’t advanceupdatedAt, and instances with only collection changes may never setcreatedAt. Consider computing min/max across all links once, then use that for author/timestamps.🛠️ Proposed fix (compute timestamps once for all links)
- let minTimestamp = null; - let maxTimestamp = null; - let latestAuthor = null; - let originalAuthor = null; + let minTimestamp = null; + let maxTimestamp = null; + let latestAuthor = null; + let originalAuthor = null; + + // Track timestamps/authors across ALL links + for (const link of links) { + if (!link.timestamp) continue; + if (minTimestamp === null || link.timestamp < minTimestamp) { + minTimestamp = link.timestamp; + originalAuthor = link.author; + } + if (maxTimestamp === null || link.timestamp > maxTimestamp) { + maxTimestamp = link.timestamp; + latestAuthor = link.author; + } + } ... - // Track timestamps/authors for createdAt and updatedAt - if (link.timestamp) { - if (!minTimestamp || link.timestamp < minTimestamp) { - minTimestamp = link.timestamp; - originalAuthor = link.author; - } - if (!maxTimestamp || link.timestamp > maxTimestamp) { - maxTimestamp = link.timestamp; - latestAuthor = link.author; - } - }
1745-1753: Selective attribute filtering dropscreatedAt/updatedAtand breakstimestamp.
Whenproperties/collectionsare specified,createdAt/updatedAtget deleted (Line 1750), which makes thetimestampgetter returnundefined. Consider keeping these fields alongsideauthor.🛠️ Proposed fix (preserve createdAt/updatedAt)
- if (!requestedAttributes.includes(key) && key !== 'timestamp' && key !== 'author' && key !== 'baseExpression') { + if ( + !requestedAttributes.includes(key) && + key !== 'timestamp' && + key !== 'author' && + key !== 'baseExpression' && + key !== 'createdAt' && + key !== 'updatedAt' + ) { delete instance[key]; }
🤖 Fix all issues with AI agents
In `@core/src/model/Ad4mModel.ts`:
- Around line 847-888: The loop in getData() skips collections with
collMeta.surrealGetter so their values (populated later by
ctor.evaluateSurrealGettersForInstance) never get the where.isInstance filter;
fix by applying the same isInstance filtering to surrealGetter collections after
evaluateSurrealGettersForInstance runs: after calling
ctor.evaluateSurrealGettersForInstance(this, this.#perspective, metadata)
iterate the metadata.collections entries where collMeta.surrealGetter is true,
read the populated values from (this as any)[collName], determine className the
same way (collMeta.where?.isInstance), call
this.#perspective.getSubjectClassMetadataFromSDNA and batchCheckSubjectInstances
as done for normal collections, and replace (this as any)[collName] with the
filtered result; preserve the existing try/catch behavior for errors.
…sals Add three detailed technical documents analyzing and proposing improvements to the AD4M model system: - AD4M-MODEL-ARCHITECTURE-ANALYSIS.md: Complete assessment of the current 3,320-line monolithic model architecture, identifying critical issues including God Object anti-pattern, dual query engine maintenance burden, contradictory decorator semantics, and excessive documentation. Proposes phased refactor strategy with estimated 8-12 week timeline. - CRDT-ORDERING-STRATEGY.md: Design proposal for abstracting P2P-safe ordering strategies into @Collection decorator. Introduces LinkedList, FractionalIndex, and Timestamp strategies with automatic CRDT link generation for conflict-free list ordering in distributed environments. - DECORATOR-SYSTEM-REDESIGN.md: Comprehensive redesign of the decorator API to eliminate semantic contradictions (@optional with required: true), introduce relationship-based decorators (@hasmany, @BelongsTo), add eager loading support, and align with modern ORM patterns while maintaining P2P/graph capabilities. These documents serve as technical planning artifacts for future model system improvements and refactoring efforts.
…mbedded auth handling Core Changes (Ad4mModel): - Fix "Cannot set property timestamp which has only a getter" error by filtering read-only properties before Object.assign() - Change Prolog result mapping from "timestamp" to "createdAt" since Prolog returns earliest timestamp (creation time) - Update SurrealDB attribute filtering to preserve "createdAt" and "updatedAt" instead of "timestamp" getter - The "timestamp" property remains as a backwards-compatible getter that returns createdAt Connect Changes (Ad4mConnect): - Improve embedded mode auth state handling with clearer conditional logic - Clean up comment formatting for timeout configuration Technical Details: - Add filtering in assignValuesToInstance() to skip properties with getters but no setters - Update instancesFromPrologResult() to map Timestamp → createdAt (line 1502) - Update instancesFromSurrealResult() to preserve createdAt/updatedAt during attribute filtering (line 1754) - Maintain backwards compatibility via timestamp getter alias Fixes CI test failures in Ad4mModel.test.ts: - "should filter properties when query specifies properties" - "should use Prolog when useSurrealDB is false in findAll()" - "should use Prolog when useSurrealDB(false) in ModelQueryBuilder.get()"
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
connect/src/core.ts (1)
50-77:⚠️ Potential issue | 🟠 MajorPrevent premature rejection while embedded auth check is still pending.
If
AD4M_CONFIGarrives beforeconnect()is called,this.ad4mClientcan be set whilecheckAuth()is still running andauthStateis still the default"unauthenticated". The new branch rejects immediately and can fail even when auth later succeeds. Gate the rejection on an explicit “auth check finished” flag (or a transient auth state) to avoid this race.🔧 Suggested fix (track auth completion)
@@ authState: AuthStates = "unauthenticated"; + private embeddedAuthChecked: boolean = false; @@ async checkAuth(): Promise<boolean> { try { @@ return true; } catch (error) { @@ return false; } + } finally { + this.embeddedAuthChecked = true; } } @@ if (this.ad4mClient) { if (this.authState === 'authenticated') { clearTimeout(timeout); console.log('[Ad4m Connect] Client already initialized in embedded mode'); resolve(this.ad4mClient); - } else { + } else if (this.embeddedAuthChecked) { // Auth already failed before connect() was called clearTimeout(timeout); reject(new Error(`Embedded auth state: ${this.authState}`)); } }
🤖 Fix all issues with AI agents
In `@core/src/model/Ad4mModel.ts`:
- Around line 761-770: The filter for writableProps allows accessor descriptors
without setters because descriptor.writable is undefined for accessors; change
the predicate in the Object.entries(propsObject).filter to explicitly detect
accessor descriptors via
Object.getOwnPropertyDescriptor(Object.getPrototypeOf(instance), key) and only
allow accessors when descriptor.set is defined, otherwise for data descriptors
require descriptor.writable !== false (or no descriptor). Update the logic
around writableProps/Object.assign to use this explicit accessor vs data
descriptor check so getter-only properties are excluded before calling
Object.assign(instance, writableProps).
- Around line 970-975: The collection filtering uses a truthiness check that
incorrectly drops valid falsy values (e.g., 0, false); update the filter in
Ad4mModel (where you assign instance[collName] from result[0].value) to use the
same explicit checks as the property filter: replace the predicate "v && v !==
'' && v !== 'None'" with "v !== undefined && v !== null && v !== '' && v !==
'None'"; also make the identical change to the other collection filter instance
(the one around the second occurrence) so both collection filters match the
explicit null/undefined/empty/'None' checks used by the property handling.
🧹 Nitpick comments (1)
core/src/model/Ad4mModel.ts (1)
946-947: Use a word-boundary placeholder when substitutingBase.
/Base/gcan accidentally replace identifiers likeDatabaseor string literals. Replacing only whole-wordBaseavoids unintended SurrealQL mutations.♻️ Suggested tweak
- const query = (propMeta as any).surrealGetter.replace(/Base/g, safeBaseExpression); + const query = (propMeta as any).surrealGetter.replace(/\bBase\b/g, safeBaseExpression); @@ - const query = (collMeta as any).surrealGetter.replace(/Base/g, safeBaseExpression); + const query = (collMeta as any).surrealGetter.replace(/\bBase\b/g, safeBaseExpression);Also applies to: 965-966
- Move z-index to positioned elements (.wrapper and .settings-button) - Change settings button from absolute to fixed positioning with z-index - Add mobile detection to ConnectionOptions component - Hide local node option on mobile devices (width < 800px) - Add resize listener for responsive mobile detection
- Remove early return in notifyAuthChange to ensure authstatechange event always fires - Automatically clear invalid tokens when InvalidSignature error is detected - Improve UX by preventing retry attempts with invalid credentials on page refresh - Reduces console error spam from expired/invalid tokens
…nd falsy value handling - Fix getter-only property assignment: Explicitly distinguish between accessor and data descriptors to prevent getter-only properties from being incorrectly assigned via Object.assign (descriptor.writable is undefined for accessors) - Apply where.isInstance filtering to surrealGetter collections in getData(): Collections with surrealGetter were previously skipped during the filtering loop and never got instance-checked after evaluation - Fix collection filters to preserve valid falsy values: Replace truthiness checks with explicit null/undefined/empty/'None' checks to prevent dropping valid values like 0 or false - Remove duplicate evaluateSurrealGettersForInstance in findAll(): Consolidate to single evaluation pass to avoid redundant SurrealDB queries and prevent overwriting filtered collection values
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
core/src/model/Ad4mModel.ts (1)
2350-2358:⚠️ Potential issue | 🟠 Major
cleanCopy()does not excludecreatedAt/updatedAt, leaking metadata into subject-class resolution.
createdAtandupdatedAtare now own instance properties (class fields at lines 416-417), soObject.entries(this)will include them. The existingkey !== "timestamp"guard is a dead branch sincetimestampis a prototype getter.This copy feeds into
stringOrTemplateObjectToSubjectClassName()viainnerUpdate()andget(). Including timestamp metadata in the class-name lookup may cause misidentification.Proposed fix
private cleanCopy() { const cleanCopy = {}; const props = Object.entries(this); for (const [key, value] of props) { - if (value !== undefined && value !== null && key !== "author" && key !== "timestamp") { + if (value !== undefined && value !== null && key !== "author" && key !== "timestamp" && key !== "createdAt" && key !== "updatedAt") { cleanCopy[key] = value; } } return cleanCopy; }
🤖 Fix all issues with AI agents
In `@core/src/model/Ad4mModel.ts`:
- Around line 806-831: getData() updates minTimestamp/maxTimestamp and
originalAuthor/latestAuthor only while iterating properties, ignoring collection
links and causing different createdAt/updatedAt compared to
instancesFromSurrealResult(); modify getData() so timestamp/author tracking runs
over all links (both property and collection links) before computing
createdAt/updatedAt — either move the timestamp logic into a single preliminary
loop over links or add the same timestamp checks into the collection-processing
loop (the code around minTimestamp/maxTimestamp updates and the collection
handling block) so that timestamps are derived from every link just like
instancesFromSurrealResult().
- Around line 973-999: The current replacement of the placeholder in
surrealGetter uses a greedy regex (/Base/g) which will alter any identifier
containing "Base" (affecting code paths in Ad4mModel where surrealGetter is used
for metadata.properties and metadata.collections); update the replacement in
both loops that build `query` (the properties loop iterating over
`propName`/`propMeta` and the collections loop iterating over
`collName`/`collMeta`) to target only the standalone placeholder (e.g., use a
word-boundary regex like `\bBase\b` or an exact token replace) when substituting
`safeBaseExpression`, leaving other identifiers intact, and keep the existing
error handling and use of `perspective.querySurrealDB` unchanged.
- Around line 1808-1837: The code calls
perspective.getSubjectClassMetadataFromSDNA repeatedly inside the per-instance
loop causing redundant SDNA fetches; hoist and cache class metadata per target
class before iterating instances by scanning metadata.collections for
collMeta.where.isInstance, computing the targetClassName (same logic as now),
calling perspective.getSubjectClassMetadataFromSDNA once per unique
targetClassName, storing results in a map, and then inside the existing
instances loop use the cached classMetadata and call
perspective.batchCheckSubjectInstances(subjects, classMetadata); preserve
existing try/catch semantics and skip filtering when classMetadata is missing or
failed.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@connect/src/web.ts`:
- Around line 93-99: The settings button CSS currently uses width: 34px; height:
34px; bottom: 10px; right: 10px which yields a touch target smaller than the
44×44px recommendation and may collide with OS safe areas; update the settings
button CSS block (the rule setting width/height/bottom/right) to preserve the
34px visual size but expand the interactive area to 44px by using padding or
min-width/min-height and box-sizing (e.g., add padding: 5px; box-sizing:
content-box or set min-width:44px; min-height:44px), and adjust the bottom/right
offsets to respect safe areas using env(safe-area-inset-bottom) /
env(safe-area-inset-right) (e.g., bottom: calc(10px +
env(safe-area-inset-bottom)); right: calc(10px + env(safe-area-inset-right)));
ensure pointer-events remain enabled for the larger hit area.
🧹 Nitpick comments (1)
connect/src/web.ts (1)
42-42: Consider using a CSS custom property for the z-index magic number.
99999is repeated in both.wrapperand.settings-button. Extracting it into a CSS custom property (e.g.,--ac-z-index: 99999) on:hostwould make it easier for consumers to override if it conflicts with their app's stacking context, and avoids the duplication.Note that even inside Shadow DOM,
position: fixedelements participate in the host document's stacking context, so a z-index this high could still clash with other overlays (e.g., third-party chat widgets, toast libraries). A configurable property would give integrators a relief valve.Proposed refactor
:host { --ac-primary-color: `#91e3fd`; + --ac-z-index: 99999; ... } .wrapper { - z-index: 99999; + z-index: var(--ac-z-index); } .settings-button { - z-index: 99999; + z-index: var(--ac-z-index); }Also applies to: 97-99
lucksus
left a comment
There was a problem hiding this comment.
Code looks fine, but there are not tests for surrealGetters, and the isInstance tests that I deactivated would need to be unskipped to show this new code is working.
…es + fix critical bugs ## New Tests - Added 3 tests for surrealGetter feature: * Property evaluation with custom SurrealQL expressions * Collection evaluation with graph traversal queries * 'None'/empty value filtering - Added 3 tests for isInstance filtering: * Collection filtering with class reference * Collection filtering with string class name * findAll() integration with isInstance constraints ## Critical Bug Fixes ### 1. Fixed GROUP BY bug in batchCheckSubjectInstances (PerspectiveProxy.ts) - **Problem**: SurrealDB queries with `GROUP BY in.uri` only returned one result per expression set instead of all matching records - **Impact**: isInstance filtering was only finding 1 of N valid instances - **Solution**: Removed `GROUP BY in.uri` clause from both flag and property check queries - **Evidence**: Diagnostic tests showed WITH GROUP BY = 1 result, WITHOUT = all results ### 2. Fixed @Flag property persistence in save() (Ad4mModel.ts) - **Problem**: Flag properties and other metadata-defined initial values weren't being saved - **Cause**: save() only iterated Object.entries(this), missing decorator-set prototype values - **Solution**: Added metadata iteration to include properties with initial values from decorators - **Impact**: Ensures @Flag properties are now properly persisted on first save ## Files Changed - core/src/perspectives/PerspectiveProxy.ts: Removed GROUP BY from SurrealDB queries (lines 1503-1520) - core/src/model/Ad4mModel.ts: Enhanced save() to include metadata initial values (lines 2320-2350) - tests/js/tests/prolog-and-literals.test.ts: Added 6 comprehensive integration tests (~350 lines) All tests passing: 85 passing, 6 pending, 0 failing ✅
Adds surrealCondition support to collection metadata/SDNA handling and applies SurrealDB-side filtering in Ad4mModel and PerspectiveProxy, with updated docs and tests for surrealCondition behavior.
Renamed model decorator properties to clarify query engine usage and establish SurrealDB as the default/primary query system: **Prolog-specific (explicitly prefixed):** - `getter` → `prologGetter` - `setter` → `prologSetter` - `condition` → `prologCondition` **SurrealDB (now default, no prefix):** - `surrealGetter` → `getter` - `surrealCondition` → `condition` This naming convention makes SurrealDB the intuitive default while explicitly marking legacy Prolog features, supporting the strategic direction to deprecate Prolog in favor of SurrealDB's superior performance (10-100x faster graph queries). **Changed files:** - core/src/model/Ad4mModel.ts: Updated interfaces, metadata, and evaluation methods - core/src/model/decorators.ts: Updated PropertyOptions, CollectionOptions, SDNA generation - core/src/perspectives/PerspectiveProxy.ts: Updated collection filtering and metadata types - core/src/model/Ad4mModel.test.ts: Updated test decorators and assertions - tests/js/tests/prolog-and-literals.test.ts: Updated 90+ test usages - PR.md: Updated documentation and examples All tests passing. No breaking changes to external APIs - decorators maintain the same structure, just with clearer naming.
Collection Instance Filtering and SurrealDB Graph Traversal Improvements
Overview
This PR enhances the AD4M model system with custom graph traversal capabilities, improved timestamp tracking, and critical bug fixes for property assignment and collection filtering. The changes enable bi-directional relationship traversals while maintaining separation between Prolog and SurrealQL query paths.
Key Features
1. Custom Graph Traversal with
getter(Commit: 1256af3)Added
getteroption to@Propertyand@Collectiondecorators, enabling custom SurrealQL expressions for reverse link lookups and complex graph queries.Key Changes:
getterfield toPropertyMetadataandCollectionMetadatainterfacesevaluateCustomGettersForInstance()method to execute custom queriesgetterare skipped during normal link processing'None'and empty string values from resultsBenefits:
Example Usage:
2. Enhanced Timestamp Tracking:
createdAtandupdatedAt(Commit: 1905ff1)Changed from single timestamp tracking to dual timestamp system:
Changes:
createdAt: Earliest link timestamp (MIN) - when instance was first createdupdatedAt: Most recent link timestamp (MAX) - when last modifiedauthor: Changed from latest author to original author (based oncreatedAt)timestamp: Now a backwards-compatible getter that returnscreatedAtImplementation Details:
getData()andinstancesFromSurrealResult()@Propertydecorators - derived from link timestampsBenefits:
3. SurrealDB Collection Filtering with
conditionAdded
conditionoption to@Collectiondecorator'swhereclause, enabling SurrealDB-native graph traversal filtering for collection items.Key Changes:
conditionfield toCollectionMetadatainterface andWhereOptionscondition(no Prolog conditions required)Ad4mModel.evaluateCustomGettersForInstance()to apply conditions per-itemPerspectiveProxy.getCollectionValuesViaSurreal()with same filtering logicprolog-and-literals.test.tsBenefits:
Example Usage:
Implementation Details:
$perspective,$base, andTargetWHERE, automatically wrapped inarray::len(SELECT * FROM link ...) > 0pattern4. Documentation Improvements
SDNA Parallel Calls Limitation (Commit: e2c510b)
Added comprehensive documentation explaining why
ensureSDNASubjectClass()cannot be called in parallel due tosdna_change_mutexin Rust executor.Documentation Covers:
Promise.all()Model System Architecture Analysis (Commit: 3c14940)
Added three detailed technical documents:
AD4M-MODEL-ARCHITECTURE-ANALYSIS.md: Complete assessment of the 3,320-line monolithic model architecture
CRDT-ORDERING-STRATEGY.md: Design proposal for abstracting P2P-safe ordering strategies
DECORATOR-SYSTEM-REDESIGN.md: Comprehensive decorator API redesign
@Optionalwithrequired: true)@HasMany,@BelongsTo)Critical Bug Fixes
5. Property Assignment and Timestamp Handling (Commit: 0b5c094)
Fixed Issues:
Solutions:
Object.assign()"timestamp"to"createdAt"(Prolog returns earliest timestamp)"createdAt"and"updatedAt"timestampproperty as backwards-compatible getterTechnical Details:
assignValuesToInstance()to skip properties with getters but no settersinstancesFromPrologResult()to mapTimestamp → createdAt(line 1502)instancesFromSurrealResult()to preservecreatedAt/updatedAtduring attribute filtering (line 1754)Tests Fixed:
6. Collection Instance Filtering and Falsy Value Handling (Commit: 1389a97)
Fixed Issues:
where.isInstancefiltering not applied togettercollectionsevaluateCustomGettersForInstancecalls causing redundant queriesSolutions:
Fix 1: Explicitly distinguish accessor vs data descriptors
Fix 2: Apply
isInstancefiltering to getter collectionsgetterwere previously skipped during filtering loopgetData()Fix 3: Preserve valid falsy values
Fix 4: Remove duplicate evaluation
evaluateCustomGettersForInstancecall infindAll()7. Ad4m Connect Improvements
Z-Index and Mobile Detection (Commit: 66f0bec)
.wrapperand.settings-button)ConnectionOptionscomponentInvalid Token Handling (Commit: 158a6cd)
notifyAuthChangeto ensureauthstatechangeevent always firesInvalidSignatureerror is detectedZ-Index Increase (Commit: c1026de)
Version Bumps
Breaking Changes
Query Engine Property Naming Refactor
To establish SurrealDB as the default query engine and clarify which properties target which engine, decorator property names have been standardized:
Renamed to explicitly mark Prolog usage:
getter→prologGetter(for Prolog-based property getters)setter→prologSetter(for Prolog-based property setters)condition→prologCondition(for Prolog-based collection filtering)Renamed to become the default (SurrealDB):
surrealGetter→getter(SurrealDB is now the default for custom getters)surrealCondition→condition(SurrealDB is now the default for collection filtering)This makes SurrealDB features the intuitive default while explicitly marking legacy Prolog features.
Migration required: Update any decorators using the old
getter,setter, orconditionproperties to use theprologprefixed versions if they contain Prolog code.Non-Breaking Changes
timestampproperty remains as getter returningcreatedAtauthorsemantics changed but field name unchangedMigration Guide
For Developers Using
timestamp:For Developers Adding Custom Graph Traversals:
For Downstream Consumers (Flux, we-*):
ensureSDNASubjectClass()calls if experiencing hangsauthorfield semantics (now returns original author, not latest)Related Issues
where.isInstanceDocumentation
getterusageFuture Work
Based on architectural analysis documents:
Summary by CodeRabbit
Release Notes
New Features
Bug Fixes
Documentation