@stream44.studio/encapsulate 0.4.0-rc.19 → 0.4.0-rc.21
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/.o/stream44.studio/assets/Icon-v1.svg +1170 -0
- package/LICENSE.txt +1 -1
- package/README.md +23 -12
- package/package.json +1 -1
- package/src/capsule-projectors/CapsuleModuleProjector.v0.ts +19 -19
- package/src/encapsulate.ts +56 -25
- package/src/spine-contracts/CapsuleSpineContract.v0/Membrane.v0.ts +6 -4
- package/src/spine-contracts/CapsuleSpineContract.v0/Overview.drawio +261 -0
- package/src/spine-contracts/CapsuleSpineContract.v0/Overview.svg +1 -0
- package/src/spine-contracts/CapsuleSpineContract.v0/README.md +437 -1
- package/src/spine-contracts/CapsuleSpineContract.v0/Static.v0.ts +41 -3
- package/src/spine-factories/CapsuleSpineFactory.v0.ts +23 -5
- package/src/static-analyzer.v0.ts +129 -17
package/LICENSE.txt
CHANGED
package/README.md
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
<table>
|
|
2
|
+
<tr>
|
|
3
|
+
<td><a href="https://Stream44.Studio"><img src=".o/stream44.studio/assets/Icon-v1.svg" width="42" height="42"></a></td>
|
|
4
|
+
<td><strong><a href="https://Stream44.Studio">Stream44 Studio</a></strong><br/>Open Development Project</td>
|
|
5
|
+
<td>Preview release for community feedback.<br/>Get in touch on <a href="https://discord.gg/9eBcQXEJAN">discord</a>.</td>
|
|
6
|
+
</tr>
|
|
7
|
+
</table>
|
|
4
8
|
|
|
5
|
-
⚠️ **Disclaimer:** Under active development. Code has not been audited
|
|
9
|
+
⚠️ **Disclaimer:** Under active development. Code has not been audited. APIs and interfaces are subject to change!
|
|
6
10
|
|
|
7
11
|
encapsulate [](https://github.com/Stream44/encapsulate/actions/workflows/test.yaml?query=branch%3Amain)
|
|
8
12
|
===
|
|
@@ -21,17 +25,24 @@ It is being used to underpin:
|
|
|
21
25
|
<br/><br/>
|
|
22
26
|
</p>
|
|
23
27
|
|
|
24
|
-
|
|
28
|
+
The CAPSULE Spine Contract
|
|
25
29
|
---
|
|
26
30
|
|
|
27
|
-
|
|
28
|
-
- Projector reference implementations are here: [github.com/Stream44/ink-component-projector](https://github.com/Stream44/ink-component-projector)
|
|
31
|
+
The `encapsulate` library wraps TypeScript objects and binds reference trees for constructing executable component graphs.
|
|
29
32
|
|
|
30
|
-
|
|
31
|
-
|
|
33
|
+
The binding rules are defined by **Spine Contracts**. The first *experimental* spine contract is the **Capsule Spine Contract**. It builds a model
|
|
34
|
+
around **Capsules** which have certain properties.
|
|
35
|
+
|
|
36
|
+
The capsule spine contract is implemented here: [src/spine-contracts/CapsuleSpineContract.v0/](src/spine-contracts/CapsuleSpineContract.v0/)
|
|
37
|
+
|
|
38
|
+
### Roadmap
|
|
39
|
+
|
|
40
|
+
- [ ] Private/Projected properties
|
|
41
|
+
- [ ] Property annotations
|
|
42
|
+
- [ ] Capsule Projectors
|
|
43
|
+
- [ ] Load capsules from packs
|
|
32
44
|
|
|
33
|
-
|
|
34
|
-
- [ ] Private properties
|
|
45
|
+

|
|
35
46
|
|
|
36
47
|
|
|
37
48
|
Provenance
|
|
@@ -51,4 +62,4 @@ Repository DID: `did:repo:65bf6c297919ca938c513cdb7517605d0d62cdbf`
|
|
|
51
62
|
</tr>
|
|
52
63
|
</table>
|
|
53
64
|
|
|
54
|
-
(c) 2026 [Christoph.diy](https://christoph.diy) • Code:
|
|
65
|
+
(c) 2026 [Christoph.diy](https://christoph.diy) • Code: [MIT](./LICENSE.txt) • Text: [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/) • Created with [Stream44.Studio](https://Stream44.Studio)
|
package/package.json
CHANGED
|
@@ -116,8 +116,8 @@ export function CapsuleModuleProjector({
|
|
|
116
116
|
// Helper: Find custom projection path from property names starting with '/'
|
|
117
117
|
function findCustomProjectionPath(capsule: any, spineContractUri: string): string | null {
|
|
118
118
|
const spineContract = capsule.cst.spineContracts[spineContractUri]
|
|
119
|
-
if (spineContract?.
|
|
120
|
-
for (const propName in spineContract.
|
|
119
|
+
if (spineContract?.propertyContracts) {
|
|
120
|
+
for (const propName in spineContract.propertyContracts) {
|
|
121
121
|
if (propName.startsWith('/')) {
|
|
122
122
|
return propName.substring(1) // Remove leading '/'
|
|
123
123
|
}
|
|
@@ -131,10 +131,10 @@ export function CapsuleModuleProjector({
|
|
|
131
131
|
const mapped: Array<{ capsuleHash: string, projectionPath: string, capsule: any }> = []
|
|
132
132
|
const spineContract = capsule.cst.spineContracts[spineContractUri]
|
|
133
133
|
|
|
134
|
-
if (spineContract?.
|
|
135
|
-
for (const propName in spineContract.
|
|
134
|
+
if (spineContract?.propertyContracts) {
|
|
135
|
+
for (const propName in spineContract.propertyContracts) {
|
|
136
136
|
if (propName.startsWith('/')) {
|
|
137
|
-
const prop = spineContract.
|
|
137
|
+
const prop = spineContract.propertyContracts[propName]
|
|
138
138
|
// Check if this is a Mapping type property
|
|
139
139
|
if (prop.type === 'CapsulePropertyTypes.Mapping') {
|
|
140
140
|
// First try to find the mapped capsule in ambient references
|
|
@@ -178,7 +178,7 @@ export function CapsuleModuleProjector({
|
|
|
178
178
|
}
|
|
179
179
|
// Also check nested property contracts that start with '#'
|
|
180
180
|
if (propName.startsWith('#')) {
|
|
181
|
-
const propContract = spineContract.
|
|
181
|
+
const propContract = spineContract.propertyContracts[propName] as any
|
|
182
182
|
if (propContract?.properties) {
|
|
183
183
|
for (const nestedPropName in propContract.properties) {
|
|
184
184
|
if (nestedPropName.startsWith('/')) {
|
|
@@ -264,8 +264,8 @@ export function CapsuleModuleProjector({
|
|
|
264
264
|
}
|
|
265
265
|
|
|
266
266
|
const spineContract = capsule.cst.spineContracts[spineContractUri]
|
|
267
|
-
if (spineContract?.
|
|
268
|
-
traverseProperties(spineContract.
|
|
267
|
+
if (spineContract?.propertyContracts) {
|
|
268
|
+
traverseProperties(spineContract.propertyContracts)
|
|
269
269
|
}
|
|
270
270
|
|
|
271
271
|
return uris
|
|
@@ -275,8 +275,8 @@ export function CapsuleModuleProjector({
|
|
|
275
275
|
function hasSolidJsProperty(capsule: any, spineContractUri: string): boolean {
|
|
276
276
|
const spineContract = capsule.cst.spineContracts[spineContractUri]
|
|
277
277
|
// Check both top-level and nested under '#' property contract
|
|
278
|
-
const topLevelProps = spineContract?.
|
|
279
|
-
const nestedProps = spineContract?.
|
|
278
|
+
const topLevelProps = spineContract?.propertyContracts || {}
|
|
279
|
+
const nestedProps = spineContract?.propertyContracts?.['#']?.properties || {}
|
|
280
280
|
|
|
281
281
|
// Check for solidjs.com/standalone specifically
|
|
282
282
|
for (const key of Object.keys(topLevelProps)) {
|
|
@@ -292,8 +292,8 @@ export function CapsuleModuleProjector({
|
|
|
292
292
|
function hasStandaloneProperty(capsule: any, spineContractUri: string): boolean {
|
|
293
293
|
const spineContract = capsule.cst.spineContracts[spineContractUri]
|
|
294
294
|
// Check both top-level and nested under '#' property contract
|
|
295
|
-
const topLevelProps = spineContract?.
|
|
296
|
-
const nestedProps = spineContract?.
|
|
295
|
+
const topLevelProps = spineContract?.propertyContracts || {}
|
|
296
|
+
const nestedProps = spineContract?.propertyContracts?.['#']?.properties || {}
|
|
297
297
|
|
|
298
298
|
// Check for exact match or with suffix
|
|
299
299
|
for (const key of Object.keys(topLevelProps)) {
|
|
@@ -310,8 +310,8 @@ export function CapsuleModuleProjector({
|
|
|
310
310
|
const spineContract = capsule.cst.spineContracts[spineContractUri]
|
|
311
311
|
|
|
312
312
|
// Check nested under '#' property contract first, looking for solidjs.com/standalone
|
|
313
|
-
const nestedProps = spineContract?.
|
|
314
|
-
const topLevelProps = spineContract?.
|
|
313
|
+
const nestedProps = spineContract?.propertyContracts?.['#']?.properties || {}
|
|
314
|
+
const topLevelProps = spineContract?.propertyContracts || {}
|
|
315
315
|
|
|
316
316
|
let solidjsProp = null
|
|
317
317
|
for (const key of Object.keys(nestedProps)) {
|
|
@@ -370,8 +370,8 @@ export function CapsuleModuleProjector({
|
|
|
370
370
|
const spineContract = capsule.cst.spineContracts[spineContractUri]
|
|
371
371
|
|
|
372
372
|
// Check nested under '#' property contract first, looking for encapsulate.dev/standalone or encapsulate.dev/standalone/*
|
|
373
|
-
const nestedProps = spineContract?.
|
|
374
|
-
const topLevelProps = spineContract?.
|
|
373
|
+
const nestedProps = spineContract?.propertyContracts?.['#']?.properties || {}
|
|
374
|
+
const topLevelProps = spineContract?.propertyContracts || {}
|
|
375
375
|
|
|
376
376
|
let standaloneProp = null
|
|
377
377
|
for (const key of Object.keys(nestedProps)) {
|
|
@@ -503,7 +503,7 @@ export function CapsuleModuleProjector({
|
|
|
503
503
|
|
|
504
504
|
// Only project capsules that have the Capsule struct property
|
|
505
505
|
const spineContract = capsule.cst.spineContracts[spineContractUri]
|
|
506
|
-
if (!spineContract?.
|
|
506
|
+
if (!spineContract?.propertyContracts?.['#@stream44.studio/encapsulate/structs/Capsule']) {
|
|
507
507
|
return false
|
|
508
508
|
}
|
|
509
509
|
|
|
@@ -571,11 +571,11 @@ export function CapsuleModuleProjector({
|
|
|
571
571
|
if (potentialMappedCapsule === capsule) continue
|
|
572
572
|
|
|
573
573
|
// Check if this capsule has the Capsule struct (meaning it should be projected)
|
|
574
|
-
if (potentialMappedCapsule.cst?.spineContracts?.[spineContractUri]?.
|
|
574
|
+
if (potentialMappedCapsule.cst?.spineContracts?.[spineContractUri]?.propertyContracts?.['#@stream44.studio/encapsulate/structs/Capsule']) {
|
|
575
575
|
// Check if this capsule's moduleFilepath is referenced in any mapping property
|
|
576
576
|
const mappedModulePath = potentialMappedCapsule.cst.source.moduleFilepath
|
|
577
577
|
|
|
578
|
-
for (const [propContractKey, propContract] of Object.entries(spineContract.
|
|
578
|
+
for (const [propContractKey, propContract] of Object.entries(spineContract.propertyContracts)) {
|
|
579
579
|
if (propContractKey.startsWith('#') && (propContract as any).properties) {
|
|
580
580
|
for (const [propName, propDef] of Object.entries((propContract as any).properties)) {
|
|
581
581
|
const prop = propDef as any
|
package/src/encapsulate.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
// CACHE_BUST_VERSION: Increment this whenever CST cache must be invalidated due to structural changes
|
|
3
3
|
// This ensures projected capsules are regenerated when the CST format changes
|
|
4
|
-
const CACHE_BUST_VERSION =
|
|
4
|
+
const CACHE_BUST_VERSION = 18
|
|
5
5
|
|
|
6
6
|
type TSpineOptions = {
|
|
7
7
|
spineFilesystemRoot?: string,
|
|
@@ -522,7 +522,8 @@ async function encapsulate(definition: TCapsuleDefinition, options: TCapsuleOpti
|
|
|
522
522
|
}
|
|
523
523
|
|
|
524
524
|
// Get capsuleName from options first, then fall back to CST if available
|
|
525
|
-
|
|
525
|
+
// When parseModule returns CSTs without this capsule (e.g. projected files), fall back to options.cst
|
|
526
|
+
const cst = csts?.[capsuleSourceLineRef] || options.cst
|
|
526
527
|
const capsuleName = options.capsuleName || cst?.source?.capsuleName
|
|
527
528
|
|
|
528
529
|
const encapsulateOptions: TEncapsulateOptions = {
|
|
@@ -613,9 +614,13 @@ async function encapsulate(definition: TCapsuleDefinition, options: TCapsuleOpti
|
|
|
613
614
|
propertyContractDefinitions[spineContractUri]['#'] = {}
|
|
614
615
|
}
|
|
615
616
|
|
|
617
|
+
// Look up capsule object from spine registry if available (for inline capsule refs)
|
|
618
|
+
const delegateUri = propContractUri.substring(1)
|
|
619
|
+
const delegateCapsuleObj = spine.capsules[delegateUri]
|
|
620
|
+
|
|
616
621
|
propertyContractDefinitions[spineContractUri]['#'][contractKey] = {
|
|
617
622
|
type: CapsulePropertyTypes.Mapping,
|
|
618
|
-
value:
|
|
623
|
+
value: delegateCapsuleObj || delegateUri,
|
|
619
624
|
propertyContractDelegate: propContractUri,
|
|
620
625
|
as: aliasName,
|
|
621
626
|
// Pass options from the property contract delegate to the mapped capsule
|
|
@@ -711,13 +716,12 @@ async function encapsulate(definition: TCapsuleDefinition, options: TCapsuleOpti
|
|
|
711
716
|
// The selfProxy in spine contracts will expose this as 'self' property
|
|
712
717
|
const ownSelf = merge({}, defaultInstance, defaultPropertyValues, ...Object.values(mergedValuesByContract))
|
|
713
718
|
|
|
714
|
-
// Capsule metadata struct will be set on self/ownSelf AFTER spine contract processing
|
|
715
|
-
// to avoid being overwritten by the empty struct marker in the definition
|
|
716
719
|
// Convert relative paths to absolute for metadata exposure
|
|
717
720
|
const absoluteCapsuleSourceLineRef = `${absoluteModuleFilepath}:${importStackLine}`
|
|
718
|
-
const capsuleMetadataStruct = {
|
|
721
|
+
const capsuleMetadataStruct: Record<string, any> = {
|
|
719
722
|
capsuleName: encapsulateOptions.capsuleName,
|
|
720
723
|
capsuleSourceLineRef: absoluteCapsuleSourceLineRef,
|
|
724
|
+
capsuleSourceNameRefHash: cst?.capsuleSourceNameRefHash,
|
|
721
725
|
moduleFilepath: absoluteModuleFilepath,
|
|
722
726
|
// Root capsule metadata will be populated after extends chain is resolved
|
|
723
727
|
rootCapsule: {
|
|
@@ -734,23 +738,34 @@ async function encapsulate(definition: TCapsuleDefinition, options: TCapsuleOpti
|
|
|
734
738
|
// Check CST first, then fall back to encapsulateOptions for direct capsule references
|
|
735
739
|
let extendsCapsuleValue = capsule.cst?.source?.extendsCapsule || encapsulateOptions.extendsCapsule
|
|
736
740
|
|
|
737
|
-
// If extendsCapsule is a string identifier,
|
|
741
|
+
// If extendsCapsule is a string identifier, resolve from ambient references
|
|
738
742
|
if (typeof extendsCapsuleValue === 'string') {
|
|
739
743
|
const cstAmbientRefs = capsule.cst?.source?.ambientReferences || {}
|
|
740
744
|
const runtimeAmbientRefs = encapsulateOptions.ambientReferences || {}
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
745
|
+
|
|
746
|
+
// First: try runtime ambient refs directly (capsule objects with .makeInstance)
|
|
747
|
+
if (runtimeAmbientRefs[extendsCapsuleValue]) {
|
|
748
|
+
const runtimeRef = runtimeAmbientRefs[extendsCapsuleValue]
|
|
749
|
+
if (runtimeRef && typeof runtimeRef === 'object' && typeof runtimeRef.makeInstance === 'function') {
|
|
750
|
+
extendsCapsuleValue = runtimeRef
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
// Second: try CST ambient refs if still a string
|
|
755
|
+
if (typeof extendsCapsuleValue === 'string') {
|
|
756
|
+
for (const [refName, ref] of Object.entries(cstAmbientRefs)) {
|
|
757
|
+
const refTyped = ref as any
|
|
758
|
+
if (refName === extendsCapsuleValue) {
|
|
759
|
+
if (refTyped.type === 'capsule' && refTyped.value) {
|
|
760
|
+
extendsCapsuleValue = refTyped.value
|
|
761
|
+
} else if (refTyped.type === 'instance' && runtimeAmbientRefs[refName]) {
|
|
762
|
+
const runtimeRef = runtimeAmbientRefs[refName]
|
|
763
|
+
if (runtimeRef && typeof runtimeRef === 'object' && typeof runtimeRef.makeInstance === 'function') {
|
|
764
|
+
extendsCapsuleValue = runtimeRef
|
|
765
|
+
}
|
|
751
766
|
}
|
|
767
|
+
break
|
|
752
768
|
}
|
|
753
|
-
break
|
|
754
769
|
}
|
|
755
770
|
}
|
|
756
771
|
}
|
|
@@ -824,6 +839,14 @@ async function encapsulate(definition: TCapsuleDefinition, options: TCapsuleOpti
|
|
|
824
839
|
rootCapsule: resolvedRootCapsule
|
|
825
840
|
}
|
|
826
841
|
|
|
842
|
+
// Set capsule metadata struct on self early so it's available in options() callbacks during mapping
|
|
843
|
+
if (!self['#@stream44.studio/encapsulate/structs/Capsule'] ||
|
|
844
|
+
typeof self['#@stream44.studio/encapsulate/structs/Capsule'] !== 'object' ||
|
|
845
|
+
!self['#@stream44.studio/encapsulate/structs/Capsule'].capsuleName) {
|
|
846
|
+
self['#@stream44.studio/encapsulate/structs/Capsule'] = capsuleMetadataStruct
|
|
847
|
+
}
|
|
848
|
+
ownSelf['#@stream44.studio/encapsulate/structs/Capsule'] = capsuleMetadataStruct
|
|
849
|
+
|
|
827
850
|
// Use runtime spine contracts if provided, otherwise fall back to encapsulation spine contracts
|
|
828
851
|
const activeSpineContracts = runtimeSpineContracts || spine.spineContracts
|
|
829
852
|
|
|
@@ -853,10 +876,18 @@ async function encapsulate(definition: TCapsuleDefinition, options: TCapsuleOpti
|
|
|
853
876
|
if (propertyContractUri !== '#') {
|
|
854
877
|
continue
|
|
855
878
|
}
|
|
879
|
+
// Get CST property definitions for this spine contract to merge depends
|
|
880
|
+
const cstProperties = cst?.spineContracts?.[spineContractUri]?.propertyContracts?.[propertyContractUri]?.properties
|
|
881
|
+
|
|
856
882
|
for (const [propertyName, propertyDefinition] of Object.entries(properties)) {
|
|
857
883
|
|
|
858
884
|
if (!propertyDefinition.type || !(propertyDefinition.type in CapsulePropertyTypes)) throw new Error(`Type '${propertyDefinition.type}' for property '${propertyName}' on spineContract '${spineContractUri}' not set or supported!`)
|
|
859
885
|
|
|
886
|
+
// Merge CST depends into property definition (CST is authoritative)
|
|
887
|
+
const cstDepends = cstProperties?.[propertyName]?.depends
|
|
888
|
+
if (cstDepends && !propertyDefinition.depends) {
|
|
889
|
+
propertyDefinition.depends = cstDepends
|
|
890
|
+
}
|
|
860
891
|
await spineContractCapsuleInstance.mapProperty({
|
|
861
892
|
overrides,
|
|
862
893
|
options,
|
|
@@ -866,17 +897,17 @@ async function encapsulate(definition: TCapsuleDefinition, options: TCapsuleOpti
|
|
|
866
897
|
propertyContractUri
|
|
867
898
|
}
|
|
868
899
|
})
|
|
900
|
+
|
|
901
|
+
// Re-set capsule metadata after the Capsule struct delegate mapping overwrites self
|
|
902
|
+
if (propertyDefinition.propertyContractDelegate === '#@stream44.studio/encapsulate/structs/Capsule') {
|
|
903
|
+
self[propertyName] = capsuleMetadataStruct
|
|
904
|
+
}
|
|
869
905
|
}
|
|
870
906
|
}
|
|
871
907
|
}
|
|
872
908
|
|
|
873
|
-
//
|
|
874
|
-
//
|
|
875
|
-
if (!self['#@stream44.studio/encapsulate/structs/Capsule'] ||
|
|
876
|
-
typeof self['#@stream44.studio/encapsulate/structs/Capsule'] !== 'object' ||
|
|
877
|
-
!self['#@stream44.studio/encapsulate/structs/Capsule'].capsuleName) {
|
|
878
|
-
self['#@stream44.studio/encapsulate/structs/Capsule'] = capsuleMetadataStruct
|
|
879
|
-
}
|
|
909
|
+
// Ensure capsule metadata struct is set on ownSelf after spine contract processing
|
|
910
|
+
// (self may have been updated by the extends chain; ownSelf always reflects this capsule)
|
|
880
911
|
ownSelf['#@stream44.studio/encapsulate/structs/Capsule'] = capsuleMetadataStruct
|
|
881
912
|
|
|
882
913
|
// Collect lifecycle functions and mapped capsule instances from all spine contract capsule instances
|
|
@@ -127,10 +127,12 @@ class MembraneContractCapsuleInstanceFactory extends ContractCapsuleInstanceFact
|
|
|
127
127
|
|
|
128
128
|
// delegateOptions is set by encapsulate.ts for property contract delegates
|
|
129
129
|
// options can be a function or an object for regular mappings
|
|
130
|
+
// Always pass { self, constants } - self is populated when depends is specified, empty otherwise
|
|
131
|
+
const optionsFn = property.definition.options
|
|
130
132
|
const mappingOptions = property.definition.delegateOptions
|
|
131
|
-
|| (typeof
|
|
132
|
-
? await property.definition.
|
|
133
|
-
:
|
|
133
|
+
|| (typeof optionsFn === 'function'
|
|
134
|
+
? await optionsFn({ self: property.definition.depends ? this.self : {}, constants })
|
|
135
|
+
: optionsFn)
|
|
134
136
|
|
|
135
137
|
// Check for existing instance in registry - reuse if available (regardless of options)
|
|
136
138
|
// Pre-registration with null allows parent capsules to "claim" a slot before child capsules process
|
|
@@ -333,7 +335,7 @@ class MembraneContractCapsuleInstanceFactory extends ContractCapsuleInstanceFact
|
|
|
333
335
|
// This avoids triggering the proxy and firing unwanted membrane events
|
|
334
336
|
const delegateTarget = this.encapsulatedApi[property.definition.propertyContractDelegate]
|
|
335
337
|
const mappedCapsuleCst = mappedCapsule.cst
|
|
336
|
-
const spineContractProperties = mappedCapsuleCst?.spineContracts?.[this.spineContractUri]?.
|
|
338
|
+
const spineContractProperties = mappedCapsuleCst?.spineContracts?.[this.spineContractUri]?.propertyContracts
|
|
337
339
|
|
|
338
340
|
if (spineContractProperties) {
|
|
339
341
|
for (const [key, propDef] of Object.entries(spineContractProperties)) {
|