@stream44.studio/encapsulate 0.4.0-rc.25 → 0.4.0-rc.26
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/LICENSE.txt
CHANGED
package/package.json
CHANGED
package/src/encapsulate.ts
CHANGED
|
@@ -55,7 +55,8 @@ type TCapsuleMakeInstanceOptions = {
|
|
|
55
55
|
moduleFilepath: string
|
|
56
56
|
},
|
|
57
57
|
parentCapsuleSourceUriLineRefInstanceId?: string,
|
|
58
|
-
sit?: { capsuleInstances: Record<string, { capsuleName: string, capsuleSourceUriLineRef: string, parentCapsuleSourceUriLineRefInstanceId: string }> }
|
|
58
|
+
sit?: { capsuleInstances: Record<string, { capsuleName: string, capsuleSourceUriLineRef: string, parentCapsuleSourceUriLineRefInstanceId: string }> },
|
|
59
|
+
skipCache?: boolean
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
type TCapsule = {
|
|
@@ -554,13 +555,15 @@ async function encapsulate(definition: TCapsuleDefinition, options: TCapsuleOpti
|
|
|
554
555
|
encapsulateOptions,
|
|
555
556
|
cst,
|
|
556
557
|
crt: crts?.[capsuleSourceLineRef],
|
|
557
|
-
makeInstance: async ({ overrides = {}, options = {}, runtimeSpineContracts, sharedSelf, rootCapsule, parentCapsuleSourceUriLineRefInstanceId, sit }: TCapsuleMakeInstanceOptions = {}) => {
|
|
558
|
+
makeInstance: async ({ overrides = {}, options = {}, runtimeSpineContracts, sharedSelf, rootCapsule, parentCapsuleSourceUriLineRefInstanceId, sit, skipCache }: TCapsuleMakeInstanceOptions = {}) => {
|
|
558
559
|
|
|
559
560
|
// Create cache key based on parameters
|
|
560
561
|
// When sharedSelf is provided, we must NOT cache because each extending capsule
|
|
561
562
|
// needs its own instance with its own 'this' context (sharedSelf).
|
|
562
563
|
// This is critical for the pattern where multiple structs extend the same parent.
|
|
563
|
-
|
|
564
|
+
// When skipCache is true (property contract delegates like structs/Capsule),
|
|
565
|
+
// each parent capsule must get its own unique instance.
|
|
566
|
+
const cacheKey = (sharedSelf || skipCache) ? null : JSON.stringify({
|
|
564
567
|
overrides,
|
|
565
568
|
options,
|
|
566
569
|
hasRuntimeContracts: !!runtimeSpineContracts
|
|
@@ -149,9 +149,11 @@ class MembraneContractCapsuleInstanceFactory extends ContractCapsuleInstanceFact
|
|
|
149
149
|
|
|
150
150
|
// Check for existing instance in registry - reuse if available (regardless of options)
|
|
151
151
|
// Pre-registration with null allows parent capsules to "claim" a slot before child capsules process
|
|
152
|
+
// Property contract delegates (structs) always get a fresh instance per parent capsule
|
|
152
153
|
const capsuleName = mappedCapsule.encapsulateOptions?.capsuleName
|
|
154
|
+
const isCapsuleStruct = property.definition.propertyContractDelegate === '#@stream44.studio/encapsulate/structs/Capsule'
|
|
153
155
|
|
|
154
|
-
if (capsuleName && this.instanceRegistry) {
|
|
156
|
+
if (capsuleName && this.instanceRegistry && !isCapsuleStruct) {
|
|
155
157
|
if (this.instanceRegistry.has(capsuleName)) {
|
|
156
158
|
const existingEntry = this.instanceRegistry.get(capsuleName)
|
|
157
159
|
|
|
@@ -275,12 +277,16 @@ class MembraneContractCapsuleInstanceFactory extends ContractCapsuleInstanceFact
|
|
|
275
277
|
overrides: mappedOverrides,
|
|
276
278
|
options: ownMappingOptions,
|
|
277
279
|
runtimeSpineContracts: this.runtimeSpineContracts,
|
|
278
|
-
rootCapsule: this.capsuleInstance?.rootCapsule
|
|
280
|
+
rootCapsule: this.capsuleInstance?.rootCapsule,
|
|
281
|
+
parentCapsuleSourceUriLineRefInstanceId: this.capsuleInstance?.capsuleSourceUriLineRefInstanceId,
|
|
282
|
+
sit: this.capsuleInstance?.sit,
|
|
283
|
+
skipCache: isCapsuleStruct
|
|
279
284
|
})
|
|
280
285
|
|
|
281
286
|
// Register the instance (replaces null pre-registration marker)
|
|
282
287
|
// Always register to make instance available for child capsules with deferred proxies
|
|
283
|
-
|
|
288
|
+
// Property contract delegates skip registry (each parent gets its own instance)
|
|
289
|
+
if (capsuleName && this.instanceRegistry && !isCapsuleStruct) {
|
|
284
290
|
this.instanceRegistry.set(capsuleName, mappedCapsuleInstance)
|
|
285
291
|
}
|
|
286
292
|
|
|
@@ -206,9 +206,11 @@ export class ContractCapsuleInstanceFactory {
|
|
|
206
206
|
|
|
207
207
|
// Check for existing instance in registry - reuse if available when no options
|
|
208
208
|
// Pre-registration with null allows parent capsules to "claim" a slot before child capsules process
|
|
209
|
+
// Property contract delegates (structs) always get a fresh instance per parent capsule
|
|
209
210
|
const capsuleName = mappedCapsule.encapsulateOptions?.capsuleName
|
|
211
|
+
const isCapsuleStruct = property.definition.propertyContractDelegate === '#@stream44.studio/encapsulate/structs/Capsule'
|
|
210
212
|
|
|
211
|
-
if (capsuleName && this.instanceRegistry) {
|
|
213
|
+
if (capsuleName && this.instanceRegistry && !isCapsuleStruct) {
|
|
212
214
|
if (this.instanceRegistry.has(capsuleName)) {
|
|
213
215
|
const existingEntry = this.instanceRegistry.get(capsuleName)
|
|
214
216
|
|
|
@@ -315,12 +317,14 @@ export class ContractCapsuleInstanceFactory {
|
|
|
315
317
|
runtimeSpineContracts: this.runtimeSpineContracts,
|
|
316
318
|
rootCapsule: this.capsuleInstance?.rootCapsule,
|
|
317
319
|
parentCapsuleSourceUriLineRefInstanceId: this.capsuleInstance?.capsuleSourceUriLineRefInstanceId,
|
|
318
|
-
sit: this.capsuleInstance?.sit
|
|
320
|
+
sit: this.capsuleInstance?.sit,
|
|
321
|
+
skipCache: isCapsuleStruct
|
|
319
322
|
})
|
|
320
323
|
|
|
321
324
|
// Register the instance (replaces null pre-registration marker)
|
|
322
325
|
// Always register to make instance available for child capsules with deferred proxies
|
|
323
|
-
|
|
326
|
+
// Property contract delegates skip registry (each parent gets its own instance)
|
|
327
|
+
if (capsuleName && this.instanceRegistry && !isCapsuleStruct) {
|
|
324
328
|
this.instanceRegistry.set(capsuleName, mappedInstance)
|
|
325
329
|
}
|
|
326
330
|
|
|
@@ -642,33 +642,28 @@ export async function CapsuleSpineFactory({
|
|
|
642
642
|
const rootInstance = await capsule.makeInstance()
|
|
643
643
|
const capsuleInstances: Record<string, { capsuleName: string, capsuleSourceUriLineRef: string, parentCapsuleSourceUriLineRefInstanceId: string }> = {}
|
|
644
644
|
|
|
645
|
-
//
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
const
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
capsuleName: instance.capsuleName || '',
|
|
661
|
-
capsuleSourceUriLineRef: instance.capsuleSourceUriLineRef || '',
|
|
662
|
-
parentCapsuleSourceUriLineRefInstanceId: parentId
|
|
663
|
-
}
|
|
645
|
+
// Iterative stack-based collection from instance tree
|
|
646
|
+
const stack: Array<{ instance: any, parentId: string }> = [{ instance: rootInstance, parentId: '' }]
|
|
647
|
+
const visited = new Set<string>()
|
|
648
|
+
while (stack.length > 0) {
|
|
649
|
+
const { instance, parentId } = stack.pop()!
|
|
650
|
+
if (!instance?.capsuleSourceUriLineRefInstanceId) continue
|
|
651
|
+
const id = instance.capsuleSourceUriLineRefInstanceId
|
|
652
|
+
if (visited.has(id)) continue
|
|
653
|
+
visited.add(id)
|
|
654
|
+
|
|
655
|
+
capsuleInstances[id] = {
|
|
656
|
+
capsuleName: instance.capsuleName || '',
|
|
657
|
+
capsuleSourceUriLineRef: instance.capsuleSourceUriLineRef || '',
|
|
658
|
+
parentCapsuleSourceUriLineRefInstanceId: parentId
|
|
659
|
+
}
|
|
664
660
|
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
}
|
|
661
|
+
if (instance.extendedCapsuleInstance) {
|
|
662
|
+
stack.push({ instance: instance.extendedCapsuleInstance, parentId: id })
|
|
663
|
+
}
|
|
664
|
+
if (instance.mappedCapsuleInstances) {
|
|
665
|
+
for (const mapped of instance.mappedCapsuleInstances) {
|
|
666
|
+
stack.push({ instance: mapped, parentId: id })
|
|
672
667
|
}
|
|
673
668
|
}
|
|
674
669
|
}
|