@opendaw/lib-box 0.0.69 → 0.0.70
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.
- package/README.md +154 -1
- package/dist/box.d.ts +9 -2
- package/dist/box.d.ts.map +1 -1
- package/dist/box.js +10 -1
- package/dist/editing.d.ts.map +1 -1
- package/dist/editing.js +39 -5
- package/dist/graph.d.ts +2 -0
- package/dist/graph.d.ts.map +1 -1
- package/dist/graph.js +33 -0
- package/package.json +32 -32
package/README.md
CHANGED
|
@@ -42,4 +42,157 @@ Graph-based object modeling system with serialization, transactions, and pointer
|
|
|
42
42
|
## Editing & Transactions
|
|
43
43
|
|
|
44
44
|
* **editing.ts** - Undo/redo system for graph modifications
|
|
45
|
-
* **indexed-box.ts** - Indexed box implementation for efficient lookups
|
|
45
|
+
* **indexed-box.ts** - Indexed box implementation for efficient lookups
|
|
46
|
+
|
|
47
|
+
## Transaction Mechanism
|
|
48
|
+
|
|
49
|
+
The box system uses transactions to batch changes and ensure consistency. Understanding the transaction lifecycle is crucial for working with the system.
|
|
50
|
+
|
|
51
|
+
### Transaction Lifecycle
|
|
52
|
+
|
|
53
|
+
1. **`beginTransaction()`** - Starts a transaction, sets `#inTransaction = true`
|
|
54
|
+
2. **Changes are made** - Field values updated, pointers modified
|
|
55
|
+
3. **`endTransaction()`** - Commits changes and fires deferred notifications
|
|
56
|
+
|
|
57
|
+
### Pointer Updates and Deferred Notifications
|
|
58
|
+
|
|
59
|
+
When a pointer is changed via `refer()`, two things happen:
|
|
60
|
+
|
|
61
|
+
1. **Immediate**: Graph edges are updated synchronously
|
|
62
|
+
- `graph.edges().connect(pointer, address)` is called
|
|
63
|
+
- The pointer hub's `incoming()` returns the new state immediately
|
|
64
|
+
|
|
65
|
+
2. **Deferred**: Notifications are batched until `endTransaction()`
|
|
66
|
+
- Pointer changes are recorded in `#pointerTransactionState`
|
|
67
|
+
- At `endTransaction()`, notifications fire in the order changes were made:
|
|
68
|
+
```typescript
|
|
69
|
+
initial.ifSome(address => vertex.pointerHub.onRemoved(pointer))
|
|
70
|
+
final.ifSome(address => vertex.pointerHub.onAdded(pointer))
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Implications for Adapters
|
|
74
|
+
|
|
75
|
+
When creating adapters during a transaction:
|
|
76
|
+
|
|
77
|
+
1. **`catchupAndSubscribe()`** on a pointer hub will:
|
|
78
|
+
- Catch up with current edges (immediately available)
|
|
79
|
+
- Subscribe to future `onAdded`/`onRemoved` notifications (deferred)
|
|
80
|
+
|
|
81
|
+
2. **Order of notifications** at `endTransaction()`:
|
|
82
|
+
- Determined by the `index` field in `#pointerTransactionState`
|
|
83
|
+
- Index is assigned when the pointer change is recorded
|
|
84
|
+
- Earlier changes fire before later changes
|
|
85
|
+
|
|
86
|
+
### Example: Creating a Track and Moving a Region
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
editing.modify(() => {
|
|
90
|
+
// 1. Create new track (deferred: track added notification)
|
|
91
|
+
const newTrack = projectApi.createNoteTrack(audioUnit, index)
|
|
92
|
+
|
|
93
|
+
// 2. Get adapter - this creates TrackRegions which subscribes to pointer hub
|
|
94
|
+
// At this point, no regions are on this track yet
|
|
95
|
+
const adapter = boxAdapters.adapterFor(newTrack, TrackBoxAdapter)
|
|
96
|
+
|
|
97
|
+
// 3. Move region to new track (deferred: region added notification)
|
|
98
|
+
regionBox.regions.refer(adapter.box.regions)
|
|
99
|
+
})
|
|
100
|
+
// After modify() returns, endTransaction() has been called:
|
|
101
|
+
// - "track added" notification fires -> TracksManager.onAdd() -> UI created
|
|
102
|
+
// - "region added" notification fires -> TrackRegions.onAdded() -> dispatchChange()
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Box Construction During Transaction
|
|
106
|
+
|
|
107
|
+
When creating a box inside a transaction (`TrackBox.create()`):
|
|
108
|
+
- The `#constructingBox` flag is set
|
|
109
|
+
- Pointer updates during construction are added to `#deferredPointerUpdates`
|
|
110
|
+
- These are processed at the start of `endTransaction()` before other notifications
|
|
111
|
+
|
|
112
|
+
### Key Points
|
|
113
|
+
|
|
114
|
+
- **Edges are synchronous**: `pointerHub.incoming()` reflects changes immediately
|
|
115
|
+
- **Notifications are deferred**: `onAdded`/`onRemoved` fire at `endTransaction()`
|
|
116
|
+
- **Order matters**: Notifications fire in the order changes were made
|
|
117
|
+
- **Adapter creation timing**: Creating an adapter during a transaction means its subscriptions won't receive notifications for changes made earlier in the same transaction (those are caught up via `catchupAndSubscribe`)
|
|
118
|
+
|
|
119
|
+
## Resource Boxes
|
|
120
|
+
|
|
121
|
+
Boxes can be marked as resources using the `resource` property. Resources act as endpoints in dependency collection, useful for copy/paste operations.
|
|
122
|
+
|
|
123
|
+
### Resource Types
|
|
124
|
+
|
|
125
|
+
| Type | Description | UUID on Copy |
|
|
126
|
+
|------|-------------|--------------|
|
|
127
|
+
| `"external"` | References something outside the project (files, etc.) | Keep original |
|
|
128
|
+
| `"internal"` | Project-level shared resource | Remap to target's existing |
|
|
129
|
+
|
|
130
|
+
### Usage in BoxSchema
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
export const AudioFileBox: BoxSchema<Pointers> = {
|
|
134
|
+
type: "box",
|
|
135
|
+
class: {...},
|
|
136
|
+
pointerRules: {...},
|
|
137
|
+
resource: "external" // Marks as external resource
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Accessing Resource Type
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
const box = AudioFileBox.create(graph, uuid)
|
|
145
|
+
console.log(box.resource) // "external"
|
|
146
|
+
console.log(AudioFileBox.Resource) // "external" (static property)
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Dependency Collection
|
|
150
|
+
|
|
151
|
+
The `dependenciesOf` method collects boxes that depend on a given box. This is used for operations like delete and copy.
|
|
152
|
+
|
|
153
|
+
### Options
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
dependenciesOf(box: Box, options: {
|
|
157
|
+
excludeBox?: Predicate<Box> // Filter out specific boxes
|
|
158
|
+
alwaysFollowMandatory?: boolean // Bypass incoming pointer check
|
|
159
|
+
stopAtResources?: boolean // Stop traversal at resource boxes
|
|
160
|
+
} = {}): Dependencies
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### stopAtResources Behavior
|
|
164
|
+
|
|
165
|
+
When `stopAtResources: true`:
|
|
166
|
+
|
|
167
|
+
1. **Resource boxes ARE added** to the dependency set (they need to be copied)
|
|
168
|
+
2. **Children are included**: Incoming edges to FIELDS (`!address.isBox()`) are followed
|
|
169
|
+
3. **Users are excluded**: Incoming edges to the BOX itself (`address.isBox()`) are NOT followed
|
|
170
|
+
4. **Outgoing edges are skipped**: Resources don't have dependencies we need to follow
|
|
171
|
+
|
|
172
|
+
### Example: AudioRegion → AudioFileBox ← TransientMarker
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
// Scenario:
|
|
176
|
+
// Region → AudioFileBox (external resource)
|
|
177
|
+
// TransientMarker → AudioFileBox.children (points to FIELD)
|
|
178
|
+
// OtherRegion → AudioFileBox (points to BOX)
|
|
179
|
+
|
|
180
|
+
const {boxes} = graph.dependenciesOf(region, {
|
|
181
|
+
stopAtResources: true,
|
|
182
|
+
alwaysFollowMandatory: true
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
// Result:
|
|
186
|
+
// - AudioFileBox: included (resource endpoint)
|
|
187
|
+
// - TransientMarker: included (child, points to field)
|
|
188
|
+
// - OtherRegion: excluded (user, points to box)
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Differentiating Children vs Users
|
|
192
|
+
|
|
193
|
+
The key distinction is the target address:
|
|
194
|
+
|
|
195
|
+
- **Child** (include): `pointer.targetAddress.isBox() === false` - points to a field within the resource
|
|
196
|
+
- **User** (exclude): `pointer.targetAddress.isBox() === true` - points to the resource box itself
|
|
197
|
+
|
|
198
|
+
This allows resource boxes to have "owned" children that are always included when copying, while preventing other boxes that merely reference the resource from being pulled in.
|
package/dist/box.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Address } from "./address";
|
|
2
|
-
import { Class, DataInput, DataOutput, Func, int, JSONValue, Maybe, Option, Optional, Procedure, Subscription, UUID } from "@opendaw/lib-std";
|
|
2
|
+
import { Class, DataInput, DataOutput, Exec, Func, int, JSONValue, Maybe, Option, Optional, Procedure, Subscription, UUID } from "@opendaw/lib-std";
|
|
3
3
|
import { PointerRules, Vertex, VertexVisitor } from "./vertex";
|
|
4
4
|
import { Field, FieldKey, FieldKeys, Fields } from "./field";
|
|
5
5
|
import { PointerField, PointerTypes } from "./pointer";
|
|
@@ -7,30 +7,37 @@ import { PointerHub } from "./pointer-hub";
|
|
|
7
7
|
import { BoxGraph } from "./graph";
|
|
8
8
|
import { Update } from "./updates";
|
|
9
9
|
import { Propagation } from "./dispatchers";
|
|
10
|
+
export type ResourceType = "external" | "internal";
|
|
10
11
|
export type BoxConstruct<T extends PointerTypes> = {
|
|
11
12
|
uuid: UUID.Bytes;
|
|
12
13
|
graph: BoxGraph;
|
|
13
14
|
name: string;
|
|
14
15
|
pointerRules: PointerRules<T>;
|
|
16
|
+
resource?: ResourceType;
|
|
17
|
+
ephemeral?: boolean;
|
|
15
18
|
};
|
|
16
19
|
export declare abstract class Box<P extends PointerTypes = PointerTypes, F extends Fields = any> implements Vertex<P, F> {
|
|
17
20
|
#private;
|
|
18
21
|
static readonly DEBUG_DELETION = false;
|
|
19
22
|
static Index: int;
|
|
20
|
-
protected constructor({ uuid, graph, name, pointerRules }: BoxConstruct<P>);
|
|
23
|
+
protected constructor({ uuid, graph, name, pointerRules, resource, ephemeral }: BoxConstruct<P>);
|
|
21
24
|
protected abstract initializeFields(): F;
|
|
25
|
+
abstract get tags(): Readonly<Record<string, string | number | boolean>>;
|
|
22
26
|
abstract accept<VISITOR extends VertexVisitor<any>>(visitor: VISITOR): VISITOR extends VertexVisitor<infer R> ? Maybe<R> : void;
|
|
23
27
|
fields(): ReadonlyArray<Field>;
|
|
24
28
|
record(): Readonly<Record<string, Field>>;
|
|
25
29
|
getField<K extends keyof F>(key: K): F[K];
|
|
26
30
|
optField<K extends keyof F>(key: K): Option<F[K]>;
|
|
27
31
|
subscribe(propagation: Propagation, procedure: Procedure<Update>): Subscription;
|
|
32
|
+
subscribeDeletion(listener: Exec): Subscription;
|
|
28
33
|
get box(): Box;
|
|
29
34
|
get name(): string;
|
|
30
35
|
get graph(): BoxGraph;
|
|
31
36
|
get parent(): Vertex;
|
|
32
37
|
get address(): Address;
|
|
33
38
|
get pointerRules(): PointerRules<P>;
|
|
39
|
+
get resource(): ResourceType | undefined;
|
|
40
|
+
get ephemeral(): boolean;
|
|
34
41
|
get creationIndex(): number;
|
|
35
42
|
get pointerHub(): PointerHub;
|
|
36
43
|
estimateMemory(): int;
|
package/dist/box.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"box.d.ts","sourceRoot":"","sources":["../src/box.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,OAAO,EAAC,MAAM,WAAW,CAAA;AACjC,OAAO,EAMH,KAAK,EACL,SAAS,EACT,UAAU,EACV,IAAI,EACJ,GAAG,EAGH,SAAS,EAET,KAAK,EACL,MAAM,EACN,QAAQ,EAER,SAAS,EACT,YAAY,EACZ,IAAI,EACP,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAC,YAAY,EAAE,MAAM,EAAE,aAAa,EAAC,MAAM,UAAU,CAAA;AAC5D,OAAO,EAAC,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAC,MAAM,SAAS,CAAA;AAC1D,OAAO,EAAC,YAAY,EAAE,YAAY,EAAC,MAAM,WAAW,CAAA;AACpD,OAAO,EAAC,UAAU,EAAC,MAAM,eAAe,CAAA;AAExC,OAAO,EAAC,QAAQ,EAAC,MAAM,SAAS,CAAA;AAChC,OAAO,EAAC,MAAM,EAAC,MAAM,WAAW,CAAA;AAChC,OAAO,EAAC,WAAW,EAAC,MAAM,eAAe,CAAA;AAKzC,MAAM,MAAM,YAAY,CAAC,CAAC,SAAS,YAAY,IAAI;IAC/C,IAAI,EAAE,IAAI,CAAC,KAAK,CAAA;IAChB,KAAK,EAAE,QAAQ,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,YAAY,EAAE,YAAY,CAAC,CAAC,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"box.d.ts","sourceRoot":"","sources":["../src/box.ts"],"names":[],"mappings":"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"}
|
package/dist/box.js
CHANGED
|
@@ -18,13 +18,17 @@ export class Box {
|
|
|
18
18
|
#graph;
|
|
19
19
|
#name;
|
|
20
20
|
#pointerRules;
|
|
21
|
+
#resource;
|
|
22
|
+
#ephemeral;
|
|
21
23
|
#fields;
|
|
22
24
|
#creationIndex = Box.Index++;
|
|
23
|
-
constructor({ uuid, graph, name, pointerRules }) {
|
|
25
|
+
constructor({ uuid, graph, name, pointerRules, resource, ephemeral }) {
|
|
24
26
|
this.#address = Address.compose(uuid);
|
|
25
27
|
this.#graph = graph;
|
|
26
28
|
this.#name = name;
|
|
27
29
|
this.#pointerRules = pointerRules;
|
|
30
|
+
this.#resource = resource;
|
|
31
|
+
this.#ephemeral = ephemeral === true;
|
|
28
32
|
this.#fields = this.initializeFields();
|
|
29
33
|
if (pointerRules.mandatory || pointerRules.exclusive) {
|
|
30
34
|
this.graph.edges().watchVertex(this);
|
|
@@ -39,12 +43,17 @@ export class Box {
|
|
|
39
43
|
subscribe(propagation, procedure) {
|
|
40
44
|
return this.graph.subscribeVertexUpdates(propagation, this.address, procedure);
|
|
41
45
|
}
|
|
46
|
+
subscribeDeletion(listener) {
|
|
47
|
+
return this.graph.subscribeDeletion(this.address.uuid, listener);
|
|
48
|
+
}
|
|
42
49
|
get box() { return this; }
|
|
43
50
|
get name() { return this.#name; }
|
|
44
51
|
get graph() { return this.#graph; }
|
|
45
52
|
get parent() { return this; }
|
|
46
53
|
get address() { return this.#address; }
|
|
47
54
|
get pointerRules() { return this.#pointerRules; }
|
|
55
|
+
get resource() { return this.#resource; }
|
|
56
|
+
get ephemeral() { return this.#ephemeral; }
|
|
48
57
|
get creationIndex() { return this.#creationIndex; }
|
|
49
58
|
get pointerHub() { return new PointerHub(this); }
|
|
50
59
|
estimateMemory() {
|
package/dist/editing.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"editing.d.ts","sourceRoot":"","sources":["../src/editing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,QAAQ,EAAC,MAAM,SAAS,CAAA;AAChC,OAAO,EAGH,OAAO,EAEP,KAAK,EAEL,QAAQ,EACR,MAAM,EACN,YAAY,EACZ,YAAY,
|
|
1
|
+
{"version":3,"file":"editing.d.ts","sourceRoot":"","sources":["../src/editing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,QAAQ,EAAC,MAAM,SAAS,CAAA;AAChC,OAAO,EAGH,OAAO,EAEP,KAAK,EAEL,QAAQ,EACR,MAAM,EACN,YAAY,EACZ,YAAY,EAEf,MAAM,kBAAkB,CAAA;AAiDzB,MAAM,WAAW,mBAAmB;IAChC,OAAO,IAAI,IAAI,CAAA;IACf,MAAM,IAAI,IAAI,CAAA;CACjB;AAED,qBAAa,UAAW,YAAW,OAAO;;gBAY1B,KAAK,EAAE,QAAQ;IAM3B,IAAI,KAAK,IAAI,QAAQ,CAAqB;IAE1C,SAAS,CAAC,QAAQ,EAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,YAAY;IAIjD,SAAS,IAAI,IAAI;IAKjB,iBAAiB,IAAI,OAAO;IAM5B,OAAO,IAAI,OAAO;IAElB,KAAK,IAAI,IAAI;IASb,IAAI,IAAI,OAAO;IAWf,IAAI,IAAI,OAAO;IASf,OAAO,IAAI,OAAO;IAKlB,OAAO,IAAI,OAAO;IAkBlB,UAAU,IAAI,OAAO;IAErB,MAAM,CAAC,CAAC,EAAE,QAAQ,EAAE,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,GAAE,OAAc,GAAG,MAAM,CAAC,CAAC,CAAC;IA4B5E,iBAAiB,IAAI,mBAAmB;IAoCxC,IAAI,IAAI,IAAI;IAYZ,YAAY,IAAI,IAAI;IAOpB,OAAO,IAAI,IAAI;CAGlB"}
|
package/dist/editing.js
CHANGED
|
@@ -1,4 +1,36 @@
|
|
|
1
|
-
import { Arrays, assert, Notifier, Option } from "@opendaw/lib-std";
|
|
1
|
+
import { Arrays, assert, Notifier, Option, UUID } from "@opendaw/lib-std";
|
|
2
|
+
import { DeleteUpdate, NewUpdate, PointerUpdate, PrimitiveUpdate } from "./updates";
|
|
3
|
+
// Removes updates for boxes that were created AND deleted in the same transaction.
|
|
4
|
+
const optimizeUpdates = (updates) => {
|
|
5
|
+
const createdUuids = UUID.newSet(uuid => uuid);
|
|
6
|
+
const deletedUuids = UUID.newSet(uuid => uuid);
|
|
7
|
+
for (const update of updates) {
|
|
8
|
+
if (update instanceof NewUpdate) {
|
|
9
|
+
createdUuids.add(update.uuid);
|
|
10
|
+
}
|
|
11
|
+
else if (update instanceof DeleteUpdate) {
|
|
12
|
+
deletedUuids.add(update.uuid);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
const phantomUuids = UUID.newSet(uuid => uuid);
|
|
16
|
+
for (const uuid of createdUuids.values()) {
|
|
17
|
+
if (deletedUuids.hasKey(uuid)) {
|
|
18
|
+
phantomUuids.add(uuid);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
if (phantomUuids.isEmpty()) {
|
|
22
|
+
return updates;
|
|
23
|
+
}
|
|
24
|
+
return updates.filter(update => {
|
|
25
|
+
if (update instanceof NewUpdate || update instanceof DeleteUpdate) {
|
|
26
|
+
return !phantomUuids.hasKey(update.uuid);
|
|
27
|
+
}
|
|
28
|
+
else if (update instanceof PointerUpdate || update instanceof PrimitiveUpdate) {
|
|
29
|
+
return !phantomUuids.hasKey(update.address.uuid);
|
|
30
|
+
}
|
|
31
|
+
return true;
|
|
32
|
+
});
|
|
33
|
+
};
|
|
2
34
|
class Modification {
|
|
3
35
|
#updates;
|
|
4
36
|
constructor(updates) { this.#updates = updates; }
|
|
@@ -126,8 +158,9 @@ export class BoxEditing {
|
|
|
126
158
|
const result = modifier();
|
|
127
159
|
this.#graph.endTransaction();
|
|
128
160
|
subscription.terminate();
|
|
129
|
-
|
|
130
|
-
|
|
161
|
+
const optimized = optimizeUpdates(updates);
|
|
162
|
+
if (optimized.length > 0) {
|
|
163
|
+
this.#pending.push(new Modification(optimized));
|
|
131
164
|
}
|
|
132
165
|
this.#modifying = false;
|
|
133
166
|
this.#graph.edges().validateRequirements();
|
|
@@ -150,8 +183,9 @@ export class BoxEditing {
|
|
|
150
183
|
approve: () => {
|
|
151
184
|
this.#graph.endTransaction();
|
|
152
185
|
subscription.terminate();
|
|
153
|
-
|
|
154
|
-
|
|
186
|
+
const optimized = optimizeUpdates(updates);
|
|
187
|
+
if (optimized.length > 0) {
|
|
188
|
+
this.#pending.push(new Modification(optimized));
|
|
155
189
|
}
|
|
156
190
|
this.#modifying = false;
|
|
157
191
|
this.#inProcess = false;
|
package/dist/graph.d.ts
CHANGED
|
@@ -33,6 +33,7 @@ export declare class BoxGraph<BoxMap = any> {
|
|
|
33
33
|
subscribeToAllUpdatesImmediate(listener: UpdateListener): Subscription;
|
|
34
34
|
subscribeVertexUpdates(propagation: Propagation, address: Address, procedure: Procedure<Update>): Subscription;
|
|
35
35
|
subscribeEndTransaction(observer: Exec): void;
|
|
36
|
+
subscribeDeletion(uuid: UUID.Bytes, listener: Exec): Subscription;
|
|
36
37
|
unstageBox(box: Box): void;
|
|
37
38
|
findBox<B extends Box = Box>(uuid: UUID.Bytes): Option<B>;
|
|
38
39
|
findVertex(address: Address): Option<Vertex>;
|
|
@@ -45,6 +46,7 @@ export declare class BoxGraph<BoxMap = any> {
|
|
|
45
46
|
dependenciesOf(box: Box, options?: {
|
|
46
47
|
excludeBox?: Predicate<Box>;
|
|
47
48
|
alwaysFollowMandatory?: boolean;
|
|
49
|
+
stopAtResources?: boolean;
|
|
48
50
|
}): Dependencies;
|
|
49
51
|
verifyPointers(): {
|
|
50
52
|
count: int;
|
package/dist/graph.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graph.d.ts","sourceRoot":"","sources":["../src/graph.ts"],"names":[],"mappings":"AAAA,OAAO,EAMH,IAAI,EACJ,GAAG,EAEH,SAAS,EAET,MAAM,EACN,QAAQ,EAER,SAAS,EAET,SAAS,EAET,YAAY,EACZ,IAAI,EACP,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAC,OAAO,EAAC,MAAM,WAAW,CAAA;AACjC,OAAO,EAAC,MAAM,EAAC,MAAM,UAAU,CAAA;AAC/B,OAAO,EAAC,YAAY,EAAC,MAAM,WAAW,CAAA;AACtC,OAAO,EAAC,cAAc,EAAE,eAAe,EAAC,MAAM,aAAa,CAAA;AAE3D,OAAO,EAAC,GAAG,EAAC,MAAM,OAAO,CAAA;AAGzB,OAAO,EAAuE,MAAM,EAAC,MAAM,WAAW,CAAA;AACtG,OAAO,EAAc,WAAW,EAAC,MAAM,eAAe,CAAA;AACtD,OAAO,EAAC,UAAU,EAAC,MAAM,eAAe,CAAA;AAExC,MAAM,MAAM,UAAU,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,MAAM,MAAM,EAClB,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,EACvB,IAAI,EAAE,IAAI,CAAC,KAAK,EAChB,WAAW,EAAE,SAAS,CAAC,GAAG,CAAC,KAAK,GAAG,CAAA;AAErE,MAAM,WAAW,mBAAmB;IAChC,kBAAkB,IAAI,IAAI,CAAA;IAC1B,gBAAgB,IAAI,IAAI,CAAA;CAC3B;AAED,MAAM,WAAW,cAAc;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;CACjC;AAED,MAAM,MAAM,YAAY,GAAG;IAAE,KAAK,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAC,YAAY,CAAC,CAAA;CAAE,CAAA;AAErF,qBAAa,QAAQ,CAAC,MAAM,GAAG,GAAG;;
|
|
1
|
+
{"version":3,"file":"graph.d.ts","sourceRoot":"","sources":["../src/graph.ts"],"names":[],"mappings":"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"}
|
package/dist/graph.js
CHANGED
|
@@ -14,6 +14,7 @@ export class BoxGraph {
|
|
|
14
14
|
#edges;
|
|
15
15
|
#pointerTransactionState;
|
|
16
16
|
#finalizeTransactionObservers;
|
|
17
|
+
#deletionListeners;
|
|
17
18
|
#inTransaction = false;
|
|
18
19
|
#constructingBox = false;
|
|
19
20
|
constructor(boxFactory = Option.None) {
|
|
@@ -27,6 +28,7 @@ export class BoxGraph {
|
|
|
27
28
|
this.#edges = new GraphEdges();
|
|
28
29
|
this.#pointerTransactionState = Address.newSet(({ pointer }) => pointer.address);
|
|
29
30
|
this.#finalizeTransactionObservers = [];
|
|
31
|
+
this.#deletionListeners = UUID.newSet(entry => entry.uuid);
|
|
30
32
|
}
|
|
31
33
|
beginTransaction() {
|
|
32
34
|
assert(!this.#inTransaction, "Transaction already in progress");
|
|
@@ -91,12 +93,25 @@ export class BoxGraph {
|
|
|
91
93
|
return this.#dispatchers.subscribe(propagation, address, procedure);
|
|
92
94
|
}
|
|
93
95
|
subscribeEndTransaction(observer) { this.#finalizeTransactionObservers.push(observer); }
|
|
96
|
+
subscribeDeletion(uuid, listener) {
|
|
97
|
+
const entry = this.#deletionListeners.getOrCreate(uuid, () => ({ uuid, listeners: new Set() }));
|
|
98
|
+
entry.listeners.add(listener);
|
|
99
|
+
return {
|
|
100
|
+
terminate: () => {
|
|
101
|
+
entry.listeners.delete(listener);
|
|
102
|
+
if (entry.listeners.size === 0) {
|
|
103
|
+
this.#deletionListeners.removeByKeyIfExist(uuid);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
}
|
|
94
108
|
unstageBox(box) {
|
|
95
109
|
this.#assertTransaction();
|
|
96
110
|
const deleted = this.#boxes.removeByKey(box.address.uuid);
|
|
97
111
|
assert(deleted === box, `${box} could not be found to unstage`);
|
|
98
112
|
this.#edges.unwatchVerticesOf(box);
|
|
99
113
|
const update = new DeleteUpdate(box.address.uuid, box.name, box.toArrayBuffer());
|
|
114
|
+
this.#deletionListeners.removeByKeyIfExist(box.address.uuid)?.listeners.forEach(listener => listener());
|
|
100
115
|
this.#updateListeners.proxy.onUpdate(update);
|
|
101
116
|
this.#immediateUpdateListeners.proxy.onUpdate(update);
|
|
102
117
|
}
|
|
@@ -202,6 +217,7 @@ export class BoxGraph {
|
|
|
202
217
|
dependenciesOf(box, options = {}) {
|
|
203
218
|
const excludeBox = isDefined(options.excludeBox) ? options.excludeBox : Predicates.alwaysFalse;
|
|
204
219
|
const alwaysFollowMandatory = isDefined(options.alwaysFollowMandatory) ? options.alwaysFollowMandatory : false;
|
|
220
|
+
const stopAtResources = isDefined(options.stopAtResources) ? options.stopAtResources : false;
|
|
205
221
|
const boxes = new Set();
|
|
206
222
|
const pointers = new Set();
|
|
207
223
|
const trace = (box) => {
|
|
@@ -209,6 +225,23 @@ export class BoxGraph {
|
|
|
209
225
|
return;
|
|
210
226
|
}
|
|
211
227
|
boxes.add(box);
|
|
228
|
+
// Handle resource boxes specially when stopAtResources is enabled
|
|
229
|
+
if (stopAtResources && isDefined(box.resource)) {
|
|
230
|
+
// Resource boxes are endpoints, but we still need their "children"
|
|
231
|
+
// Children = boxes that point to FIELDS within this box (not the box itself)
|
|
232
|
+
box.incomingEdges()
|
|
233
|
+
.forEach(pointer => {
|
|
234
|
+
pointers.add(pointer);
|
|
235
|
+
// Only follow if pointer targets a FIELD (child), not the BOX (user)
|
|
236
|
+
const targetsField = pointer.targetAddress.mapOr(address => !address.isBox(), false);
|
|
237
|
+
if (pointer.mandatory && targetsField) {
|
|
238
|
+
trace(pointer.box);
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
// Don't trace outgoing edges - resources are endpoints
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
// Normal box traversal
|
|
212
245
|
box.outgoingEdges()
|
|
213
246
|
.filter(([pointer]) => !pointers.has(pointer))
|
|
214
247
|
.forEach(([source, targetAddress]) => {
|
package/package.json
CHANGED
|
@@ -1,34 +1,34 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
2
|
+
"name": "@opendaw/lib-box",
|
|
3
|
+
"version": "0.0.70",
|
|
4
|
+
"sideEffects": false,
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"license": "LGPL-3.0-or-later",
|
|
8
|
+
"publishConfig": {
|
|
9
|
+
"access": "public"
|
|
10
|
+
},
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"default": "./dist/index.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist/**/*"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsc",
|
|
22
|
+
"lint": "eslint \"**/*.ts\"",
|
|
23
|
+
"test": "vitest run"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@opendaw/lib-runtime": "^0.0.67",
|
|
27
|
+
"@opendaw/lib-std": "^0.0.66"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@opendaw/eslint-config": "^0.0.27",
|
|
31
|
+
"@opendaw/typescript-config": "^0.0.28"
|
|
32
|
+
},
|
|
33
|
+
"gitHead": "a23a423a60a8b0cebfb973224ffb6eddd6fb6943"
|
|
34
34
|
}
|