@contember/bindx 0.1.51 → 0.1.53

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 (54) hide show
  1. package/dist/core/ActionDispatcher.d.ts.map +1 -1
  2. package/dist/core/EntityLoader.d.ts +7 -0
  3. package/dist/core/EntityLoader.d.ts.map +1 -1
  4. package/dist/core/actions.d.ts +1 -2
  5. package/dist/core/actions.d.ts.map +1 -1
  6. package/dist/dataview/types.d.ts +6 -6
  7. package/dist/dataview/types.d.ts.map +1 -1
  8. package/dist/handles/EntityHandle.d.ts.map +1 -1
  9. package/dist/handles/HasManyListHandle.d.ts +8 -0
  10. package/dist/handles/HasManyListHandle.d.ts.map +1 -1
  11. package/dist/handles/HasOneHandle.d.ts.map +1 -1
  12. package/dist/index.d.ts +1 -1
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/persistence/BatchPersister.d.ts.map +1 -1
  15. package/dist/store/HasManyStore.d.ts +94 -78
  16. package/dist/store/HasManyStore.d.ts.map +1 -1
  17. package/dist/store/RelationStore.d.ts +21 -12
  18. package/dist/store/RelationStore.d.ts.map +1 -1
  19. package/dist/store/ResponseOccurrenceIndex.d.ts +65 -0
  20. package/dist/store/ResponseOccurrenceIndex.d.ts.map +1 -0
  21. package/dist/store/SnapshotStore.d.ts +53 -15
  22. package/dist/store/SnapshotStore.d.ts.map +1 -1
  23. package/dist/store/SubscriptionManager.d.ts +19 -4
  24. package/dist/store/SubscriptionManager.d.ts.map +1 -1
  25. package/dist/store/hasManyState.d.ts +164 -0
  26. package/dist/store/hasManyState.d.ts.map +1 -0
  27. package/dist/store/relationKey.d.ts +11 -0
  28. package/dist/store/relationKey.d.ts.map +1 -1
  29. package/dist/undo/UndoJournal.d.ts.map +1 -1
  30. package/dist/undo/rekeyJournalEntry.d.ts +4 -4
  31. package/dist/undo/rekeyJournalEntry.d.ts.map +1 -1
  32. package/dist/utils/aliasGenerator.d.ts +1 -1
  33. package/dist/utils/aliasGenerator.d.ts.map +1 -1
  34. package/package.json +2 -2
  35. package/src/core/ActionDispatcher.ts +0 -1
  36. package/src/core/EntityLoader.ts +13 -6
  37. package/src/core/actions.ts +1 -3
  38. package/src/dataview/types.ts +6 -6
  39. package/src/handles/EntityHandle.ts +11 -0
  40. package/src/handles/HasManyListHandle.ts +15 -17
  41. package/src/handles/HasOneHandle.ts +4 -2
  42. package/src/index.ts +6 -0
  43. package/src/persistence/BatchPersister.ts +15 -11
  44. package/src/store/HasManyStore.ts +312 -488
  45. package/src/store/RelationStore.ts +60 -24
  46. package/src/store/ResponseOccurrenceIndex.ts +224 -0
  47. package/src/store/SnapshotStore.ts +183 -109
  48. package/src/store/SubscriptionManager.ts +116 -58
  49. package/src/store/hasManyState.ts +408 -0
  50. package/src/store/relationKey.ts +17 -0
  51. package/src/undo/UndoJournal.ts +3 -1
  52. package/src/undo/UndoManager.ts +1 -1
  53. package/src/undo/rekeyJournalEntry.ts +31 -43
  54. package/src/utils/aliasGenerator.ts +1 -1
@@ -9,6 +9,23 @@ export function parentKeyFromRelationKey(relationKey: string): string {
9
9
  return relationKey.slice(0, lastSeparator)
10
10
  }
11
11
 
12
+ /**
13
+ * Derives the parent entity type from a relation key ("parentType:parentId:fieldName").
14
+ * Entity type names never contain ':', so the type is everything before the first separator.
15
+ */
16
+ export function entityTypeFromRelationKey(relationKey: string): string {
17
+ return relationKey.slice(0, relationKey.indexOf(':'))
18
+ }
19
+
20
+ /**
21
+ * Derives the field name from a relation key ("parentType:parentId:fieldName").
22
+ * The counterpart of {@link parentKeyFromRelationKey}: entity ids and field names
23
+ * never contain ':', so the field is everything after the last separator.
24
+ */
25
+ export function fieldFromRelationKey(relationKey: string): string {
26
+ return relationKey.slice(relationKey.lastIndexOf(':') + 1)
27
+ }
28
+
12
29
  /**
13
30
  * Derives the parent composite key ("parentType:parentId") from an owner key
14
31
  * prefix ("parentType:parentId:") by dropping the trailing separator. Callers
@@ -113,7 +113,9 @@ function collectDetachedSeedIds(active: Map<string, JournalCellImage>): Set<stri
113
113
  if (image.kind === 'relation') {
114
114
  if (image.state?.currentId) ids.add(image.state.currentId)
115
115
  } else if (image.kind === 'hasMany' && image.state) {
116
- if (image.state.orderedIds) for (const id of image.state.orderedIds) ids.add(id)
116
+ for (const view of image.state.views.values()) {
117
+ if (view.orderedIds) for (const id of view.orderedIds) ids.add(id)
118
+ }
117
119
  for (const id of image.state.plannedAdditions.keys()) ids.add(id)
118
120
  for (const id of image.state.plannedRemovals.keys()) ids.add(id)
119
121
  }
@@ -214,7 +214,7 @@ export class UndoManager {
214
214
  * so stored cells keep valid keys / id references (and sealed creates drop out).
215
215
  */
216
216
  private rekeyStacks(ctx: RekeyContext): void {
217
- const liveServerIds = (key: string): Set<string> => this.store.getLiveHasManyServerIds(key)
217
+ const liveServerIds = (key: string): ReadonlyMap<string, ReadonlySet<string>> => this.store.getLiveHasManyServerIds(key)
218
218
  const undoCount = this.undoStack.length
219
219
  const redoCount = this.redoStack.length
220
220
  const hadPending = this.pending !== null && this.pending.size > 0
@@ -1,7 +1,7 @@
1
1
  import type { RekeyContext } from '../store/RekeyOrchestrator.js'
2
2
  import { createEntitySnapshot } from '../store/snapshots.js'
3
3
  import type { EntitySnapshot } from '../store/snapshots.js'
4
- import type { StoredHasManyState } from '../store/RelationStore.js'
4
+ import { cloneHasManyState, type StoredHasManyState } from '../store/hasManyState.js'
5
5
  import type { JournalEntry, JournalCellImage } from './UndoJournal.js'
6
6
 
7
7
  /**
@@ -15,11 +15,11 @@ import type { JournalEntry, JournalCellImage } from './UndoJournal.js'
15
15
  * server-backed row — only later edits to it remain undoable.
16
16
  */
17
17
  /**
18
- * Looks up the live server-member ids of a has-many list by its key. Lets the
19
- * rekey rebase a pre-image when the just-persisted create has become a permanent
20
- * member of that list.
18
+ * Looks up the live server-member ids of a has-many relation by its key, per
19
+ * args-view. Lets the rekey rebase a pre-image when the just-persisted create has
20
+ * become a permanent member of that list.
21
21
  */
22
- export type LiveServerIdsLookup = (relationKey: string) => Set<string>
22
+ export type LiveServerIdsLookup = (relationKey: string) => ReadonlyMap<string, ReadonlySet<string>>
23
23
 
24
24
  export function rekeyJournalEntry(
25
25
  entry: JournalEntry,
@@ -77,19 +77,19 @@ function rekeyCell(
77
77
 
78
78
  const key = rekeyKey(cell.key, ctx)
79
79
  if (!cell.state) return { ...cell, key }
80
- let state = rekeyHasManyState(cell.state, ctx)
80
+ const state = rekeyHasManyState(cell.state, ctx)
81
81
  // Membership rebase: when the just-persisted create became a permanent member of
82
- // this live list, fold it into the (older) pre-image so undo keeps it instead of
83
- // dropping it. Default order picks it up automatically; an explicit order needs
84
- // the id appended.
85
- if (liveServerIds(key).has(ctx.newId) && !state.serverIds.has(ctx.newId)) {
86
- const serverIds = new Set(state.serverIds)
87
- serverIds.add(ctx.newId)
88
- let orderedIds = state.orderedIds
89
- if (orderedIds && !orderedIds.includes(ctx.newId)) {
90
- orderedIds = [...orderedIds, ctx.newId]
82
+ // a live view, fold it into the (older) pre-image of that view so undo keeps it
83
+ // instead of dropping it. Default order picks it up automatically; an explicit
84
+ // order needs the id appended. Views born after the gesture are left alone —
85
+ // the pre-image says nothing about them.
86
+ const live = liveServerIds(key)
87
+ for (const [alias, view] of state.views) {
88
+ if (!live.get(alias)?.has(ctx.newId) || view.serverIds.has(ctx.newId)) continue
89
+ view.serverIds.add(ctx.newId)
90
+ if (view.orderedIds && !view.orderedIds.includes(ctx.newId)) {
91
+ view.orderedIds = [...view.orderedIds, ctx.newId]
91
92
  }
92
- state = { ...state, serverIds, orderedIds }
93
93
  }
94
94
  return { ...cell, key, state }
95
95
  }
@@ -105,41 +105,29 @@ function rekeySnapshotId(snapshot: EntitySnapshot, ctx: RekeyContext): EntitySna
105
105
  )
106
106
  }
107
107
 
108
- function swapInSet(set: Set<string>, oldId: string, newId: string): Set<string> {
109
- if (!set.has(oldId)) return set
110
- const next = new Set(set)
111
- next.delete(oldId)
112
- next.add(newId)
113
- return next
114
- }
115
-
108
+ /** Returns a deep copy with oldId swapped for newId everywhere it appears. */
116
109
  function rekeyHasManyState(state: StoredHasManyState, ctx: RekeyContext): StoredHasManyState {
117
- const serverIds = swapInSet(state.serverIds, ctx.oldId, ctx.newId)
110
+ const next = cloneHasManyState(state)
118
111
 
119
- let orderedIds = state.orderedIds
120
- if (orderedIds) {
121
- const idx = orderedIds.indexOf(ctx.oldId)
122
- if (idx !== -1) {
123
- orderedIds = [...orderedIds]
124
- orderedIds[idx] = ctx.newId
112
+ for (const view of next.views.values()) {
113
+ if (view.serverIds.delete(ctx.oldId)) view.serverIds.add(ctx.newId)
114
+ if (view.orderedIds) {
115
+ const idx = view.orderedIds.indexOf(ctx.oldId)
116
+ if (idx !== -1) view.orderedIds[idx] = ctx.newId
125
117
  }
126
118
  }
127
119
 
128
- let plannedAdditions = state.plannedAdditions
129
- const additionKind = plannedAdditions.get(ctx.oldId)
130
- if (additionKind !== undefined) {
131
- plannedAdditions = new Map(plannedAdditions)
132
- plannedAdditions.delete(ctx.oldId)
133
- plannedAdditions.set(ctx.newId, additionKind)
120
+ const addition = next.plannedAdditions.get(ctx.oldId)
121
+ if (addition) {
122
+ next.plannedAdditions.delete(ctx.oldId)
123
+ next.plannedAdditions.set(ctx.newId, addition)
134
124
  }
135
125
 
136
- let plannedRemovals = state.plannedRemovals
137
- const removalType = plannedRemovals.get(ctx.oldId)
126
+ const removalType = next.plannedRemovals.get(ctx.oldId)
138
127
  if (removalType !== undefined) {
139
- plannedRemovals = new Map(plannedRemovals)
140
- plannedRemovals.delete(ctx.oldId)
141
- plannedRemovals.set(ctx.newId, removalType)
128
+ next.plannedRemovals.delete(ctx.oldId)
129
+ next.plannedRemovals.set(ctx.newId, removalType)
142
130
  }
143
131
 
144
- return { serverIds, orderedIds, plannedRemovals, plannedAdditions, version: state.version }
132
+ return next
145
133
  }
@@ -1 +1 @@
1
- export { generateHasManyAlias } from '@contember/bindx-client'
1
+ export { canHasManyParamsExcludeMembers, generateHasManyAlias } from '@contember/bindx-client'