Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/bindx-react/src/jsx/collectorProxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export function createCollectorProxy<T>(
$isDirty: false,
$isPersisting: false,
$persistedId: null,
$resolvedId: '__collector__',
$isNew: true,
__entityType: undefined as unknown as T,
__entityName: '__collector__',
Expand Down Expand Up @@ -235,6 +236,7 @@ function createCollectorFieldRef(
$isPersisting: false,
$isNew: false,
$persistedId: null,
$resolvedId: placeholderId,
__entityName: targetEntityName ?? '',

__schema: {} as Record<string, object>,
Expand Down
12 changes: 11 additions & 1 deletion packages/bindx/src/handles/EntityHandle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,12 +126,22 @@ export class EntityHandle<T extends object = object, TSelected = T> extends Enti
}

/**
* Gets the entity ID.
* Gets the entity ID (stable — always the original ID used to create the handle).
* For new entities this is the temp ID; for loaded entities it's the server ID.
* Use `$persistedId` to get the server-assigned ID after persist, or `$resolvedId` for best-known ID.
*/
get id(): string {
return this.entityId
}

/**
* Gets the best-known ID: the persisted (server-assigned) ID if available,
* otherwise the original ID (which may be a temp ID for new entities).
*/
get resolvedId(): string {
return this.store.getPersistedId(this.entityType, this.entityId) ?? this.entityId
}

/**
* Gets the current entity snapshot.
*/
Expand Down
3 changes: 3 additions & 0 deletions packages/bindx/src/handles/HasOneHandle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,9 @@ export class HasOneHandle<TEntity extends object = object, TSelected = TEntity>
/** Server-assigned ID after persistence - delegates to entityRaw */
get persistedId(): string | null { return this.entityRaw.persistedId }

/** Best-known ID: persisted if available, otherwise original - delegates to entityRaw */
get resolvedId(): string { return this.entityRaw.resolvedId }

/** Type brand for entity name */
get __entityName(): string { return this.targetType }

Expand Down
7 changes: 7 additions & 0 deletions packages/bindx/src/handles/PlaceholderHandle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,13 @@ export class PlaceholderHandle<TEntity extends object = object, TSelected = TEnt
return null
}

/**
* Placeholder entities have no persisted ID — returns placeholder ID.
*/
get resolvedId(): string {
return this.placeholderId
}

/**
* Placeholder entities are always new.
*/
Expand Down
2 changes: 2 additions & 0 deletions packages/bindx/src/handles/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ export interface HasOneRefInterface<
readonly $isNew: boolean
readonly $isPersisting: boolean
readonly $persistedId: string | null
readonly $resolvedId: string
readonly __entityName: TEntityName
readonly __schema?: TSchema & any
readonly __entityType: TEntity
Expand Down Expand Up @@ -295,6 +296,7 @@ export interface EntityRefInterface<
readonly $isDirty: boolean
readonly $isPersisting: boolean
readonly $persistedId: string | null
readonly $resolvedId: string
readonly $isNew: boolean
readonly __entityType: TEntity
readonly __selected?: TSelected
Expand Down
25 changes: 25 additions & 0 deletions tests/unit/handles/entityHandle.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,31 @@ describe('EntityHandle', () => {
expect(handle.id as string).toBe('a-1')
})

test('should return stable temp ID even after persisted ID mapping', () => {
const tempId = store.createEntity('Article', { title: 'New' })
store.mapTempIdToPersistedId('Article', tempId, 'real-id-456')

const handle = EntityHandle.create<TestArticle>(tempId, 'Article', store, dispatcher, schema)

// id is always the stable original ID (temp ID in this case)
expect(handle.id as string).toBe(tempId)
})

test('should return persisted ID via $resolvedId after temp ID mapping', () => {
const tempId = store.createEntity('Article', { title: 'New' })
store.mapTempIdToPersistedId('Article', tempId, 'real-id-456')

const handle = EntityHandle.create<TestArticle>(tempId, 'Article', store, dispatcher, schema)

expect(handle.$resolvedId as string).toBe('real-id-456')
})

test('should return original ID via $resolvedId when no persisted mapping', () => {
const handle = createEntityHandle('a-1')

expect(handle.$resolvedId as string).toBe('a-1')
})

test('should return entity type', () => {
const handle = createEntityHandleRaw('a-1')
expect(handle.__entityName).toBe('Article')
Expand Down