Facts are immutable.
So you don't want to put a property like name
in a project.
You would never be able to change it!
Instead, you can define a successor that represents changing the project name.
This is the pattern we use.
class ProjectName {
static Type = "Construction.Project.Name" as const ;
type = ProjectName. Type;
constructor (
public project: Project,
public value: string ,
public prior: ProjectName[ ]
) { }
}
That prior
array let's you overwrite a prior value.
The first value will have an empty array, which means there is no prior value.
const projectAName1 = await j. fact ( new ProjectName ( projectA, "Cheyenne Expansion" , [ ] ) ) ;
%0
Gs+Fo0xO04hUAteaPwrHZDmyovTwr7asnKsBrkRf3HE3M9nYIj4Sk7ZhR8YK5uMq1SMHPrQohtQNwo9B7whK0w==
Jinaga.User
publicKey
--- TEST USER ---
PYaXGGp+ksHg101LgyF/pB/OBQsixEhWZ9RDW9wxdwX/sVFWgyhpOZROgi4Gttdz1lWJ5Un0pJPJ5MvXEk1TCQ==
Construction.Project
id
52eb9df8-7b1c-43d4-9...
PYaXGGp+ksHg101LgyF/pB/OBQsixEhWZ9RDW9wxdwX/sVFWgyhpOZROgi4Gttdz1lWJ5Un0pJPJ5MvXEk1TCQ==->Gs+Fo0xO04hUAteaPwrHZDmyovTwr7asnKsBrkRf3HE3M9nYIj4Sk7ZhR8YK5uMq1SMHPrQohtQNwo9B7whK0w==
creator
RDT0fLC2Ht6/7ioD2jy9UkRQHU7YB8KjuhXMtW2p77stVVubhV6mw5rdytlr6xRuh2dxeaYviDEZYU4bXwcukg==
Construction.Project.Name
value
Cheyenne Expansion
RDT0fLC2Ht6/7ioD2jy9UkRQHU7YB8KjuhXMtW2p77stVVubhV6mw5rdytlr6xRuh2dxeaYviDEZYU4bXwcukg==->PYaXGGp+ksHg101LgyF/pB/OBQsixEhWZ9RDW9wxdwX/sVFWgyhpOZROgi4Gttdz1lWJ5Un0pJPJ5MvXEk1TCQ==
project
To change the name, you create a new fact that has the old name as its prior value.
const projectAName2 = await j. fact ( new ProjectName ( projectA, "Rivercrest Expansion" , [ projectAName1] ) ) ;
%0
Gs+Fo0xO04hUAteaPwrHZDmyovTwr7asnKsBrkRf3HE3M9nYIj4Sk7ZhR8YK5uMq1SMHPrQohtQNwo9B7whK0w==
Jinaga.User
publicKey
--- TEST USER ---
PYaXGGp+ksHg101LgyF/pB/OBQsixEhWZ9RDW9wxdwX/sVFWgyhpOZROgi4Gttdz1lWJ5Un0pJPJ5MvXEk1TCQ==
Construction.Project
id
52eb9df8-7b1c-43d4-9...
PYaXGGp+ksHg101LgyF/pB/OBQsixEhWZ9RDW9wxdwX/sVFWgyhpOZROgi4Gttdz1lWJ5Un0pJPJ5MvXEk1TCQ==->Gs+Fo0xO04hUAteaPwrHZDmyovTwr7asnKsBrkRf3HE3M9nYIj4Sk7ZhR8YK5uMq1SMHPrQohtQNwo9B7whK0w==
creator
RDT0fLC2Ht6/7ioD2jy9UkRQHU7YB8KjuhXMtW2p77stVVubhV6mw5rdytlr6xRuh2dxeaYviDEZYU4bXwcukg==
Construction.Project.Name
value
Cheyenne Expansion
RDT0fLC2Ht6/7ioD2jy9UkRQHU7YB8KjuhXMtW2p77stVVubhV6mw5rdytlr6xRuh2dxeaYviDEZYU4bXwcukg==->PYaXGGp+ksHg101LgyF/pB/OBQsixEhWZ9RDW9wxdwX/sVFWgyhpOZROgi4Gttdz1lWJ5Un0pJPJ5MvXEk1TCQ==
project
uECG/V1c641/SNfINkMRif0QPIQIoVcf0Qo7hWBpsG51BSX9SefJC1e7wbGg+/zcDQGxiw76bepnRcnL5TDifg==
Construction.Project.Name
value
Rivercrest Expansion
uECG/V1c641/SNfINkMRif0QPIQIoVcf0Qo7hWBpsG51BSX9SefJC1e7wbGg+/zcDQGxiw76bepnRcnL5TDifg==->PYaXGGp+ksHg101LgyF/pB/OBQsixEhWZ9RDW9wxdwX/sVFWgyhpOZROgi4Gttdz1lWJ5Un0pJPJ5MvXEk1TCQ==
project
uECG/V1c641/SNfINkMRif0QPIQIoVcf0Qo7hWBpsG51BSX9SefJC1e7wbGg+/zcDQGxiw76bepnRcnL5TDifg==->RDT0fLC2Ht6/7ioD2jy9UkRQHU7YB8KjuhXMtW2p77stVVubhV6mw5rdytlr6xRuh2dxeaYviDEZYU4bXwcukg==
prior
To query for the current name, you look for a successor that does not appear in any list of prior
values.
const namesOfProject = model. given ( Project) . match ( p =>
p. successors ( ProjectName, n => n. project)
. notExists ( n => n. successors ( ProjectName, next => next. prior) )
) ;
const names = await j. query ( namesOfProject, projectA) ;
names. map ( n => n. value)
[ "Rivercrest Expansion" ]
Concurrent Edits
The reason that we use the prior
array is to handle concurrent edits.
If a user on a different device changes the name of the project, you will get a fork in the graph.
const projectAName3 = await j. fact ( new ProjectName ( projectA, "Cheyenne Remodel" , [ projectAName1] ) ) ;
%0
Gs+Fo0xO04hUAteaPwrHZDmyovTwr7asnKsBrkRf3HE3M9nYIj4Sk7ZhR8YK5uMq1SMHPrQohtQNwo9B7whK0w==
Jinaga.User
publicKey
--- TEST USER ---
PYaXGGp+ksHg101LgyF/pB/OBQsixEhWZ9RDW9wxdwX/sVFWgyhpOZROgi4Gttdz1lWJ5Un0pJPJ5MvXEk1TCQ==
Construction.Project
id
52eb9df8-7b1c-43d4-9...
PYaXGGp+ksHg101LgyF/pB/OBQsixEhWZ9RDW9wxdwX/sVFWgyhpOZROgi4Gttdz1lWJ5Un0pJPJ5MvXEk1TCQ==->Gs+Fo0xO04hUAteaPwrHZDmyovTwr7asnKsBrkRf3HE3M9nYIj4Sk7ZhR8YK5uMq1SMHPrQohtQNwo9B7whK0w==
creator
RDT0fLC2Ht6/7ioD2jy9UkRQHU7YB8KjuhXMtW2p77stVVubhV6mw5rdytlr6xRuh2dxeaYviDEZYU4bXwcukg==
Construction.Project.Name
value
Cheyenne Expansion
RDT0fLC2Ht6/7ioD2jy9UkRQHU7YB8KjuhXMtW2p77stVVubhV6mw5rdytlr6xRuh2dxeaYviDEZYU4bXwcukg==->PYaXGGp+ksHg101LgyF/pB/OBQsixEhWZ9RDW9wxdwX/sVFWgyhpOZROgi4Gttdz1lWJ5Un0pJPJ5MvXEk1TCQ==
project
uECG/V1c641/SNfINkMRif0QPIQIoVcf0Qo7hWBpsG51BSX9SefJC1e7wbGg+/zcDQGxiw76bepnRcnL5TDifg==
Construction.Project.Name
value
Rivercrest Expansion
uECG/V1c641/SNfINkMRif0QPIQIoVcf0Qo7hWBpsG51BSX9SefJC1e7wbGg+/zcDQGxiw76bepnRcnL5TDifg==->PYaXGGp+ksHg101LgyF/pB/OBQsixEhWZ9RDW9wxdwX/sVFWgyhpOZROgi4Gttdz1lWJ5Un0pJPJ5MvXEk1TCQ==
project
uECG/V1c641/SNfINkMRif0QPIQIoVcf0Qo7hWBpsG51BSX9SefJC1e7wbGg+/zcDQGxiw76bepnRcnL5TDifg==->RDT0fLC2Ht6/7ioD2jy9UkRQHU7YB8KjuhXMtW2p77stVVubhV6mw5rdytlr6xRuh2dxeaYviDEZYU4bXwcukg==
prior
vGnXSLjOADIAr0eVBXktKVUElG/uFuumiUBtyRlJZO8ixenhcTySa9U+fJLMTkp3yFB2u28i6s0fA1yi2dAf5g==
Construction.Project.Name
value
Cheyenne Remodel
vGnXSLjOADIAr0eVBXktKVUElG/uFuumiUBtyRlJZO8ixenhcTySa9U+fJLMTkp3yFB2u28i6s0fA1yi2dAf5g==->PYaXGGp+ksHg101LgyF/pB/OBQsixEhWZ9RDW9wxdwX/sVFWgyhpOZROgi4Gttdz1lWJ5Un0pJPJ5MvXEk1TCQ==
project
vGnXSLjOADIAr0eVBXktKVUElG/uFuumiUBtyRlJZO8ixenhcTySa9U+fJLMTkp3yFB2u28i6s0fA1yi2dAf5g==->RDT0fLC2Ht6/7ioD2jy9UkRQHU7YB8KjuhXMtW2p77stVVubhV6mw5rdytlr6xRuh2dxeaYviDEZYU4bXwcukg==
prior
That will result in more than one successor matching the specification.
const names = await j. query ( namesOfProject, projectA) ;
names. map ( n => n. value)
[ "Rivercrest Expansion" , "Cheyenne Remodel" ]
This is how you can recognize that there have been concurrent edits.
It also shows you the candidate values for the project name.
To merge, identify the correct value.
Then create a new fact that sets the correct value and has all candidates in its prior
array.
const projectAName4 = await j. fact ( new ProjectName ( projectA, "Rivercrest Remodel" , names) ) ;
%0
Gs+Fo0xO04hUAteaPwrHZDmyovTwr7asnKsBrkRf3HE3M9nYIj4Sk7ZhR8YK5uMq1SMHPrQohtQNwo9B7whK0w==
Jinaga.User
publicKey
--- TEST USER ---
PYaXGGp+ksHg101LgyF/pB/OBQsixEhWZ9RDW9wxdwX/sVFWgyhpOZROgi4Gttdz1lWJ5Un0pJPJ5MvXEk1TCQ==
Construction.Project
id
52eb9df8-7b1c-43d4-9...
PYaXGGp+ksHg101LgyF/pB/OBQsixEhWZ9RDW9wxdwX/sVFWgyhpOZROgi4Gttdz1lWJ5Un0pJPJ5MvXEk1TCQ==->Gs+Fo0xO04hUAteaPwrHZDmyovTwr7asnKsBrkRf3HE3M9nYIj4Sk7ZhR8YK5uMq1SMHPrQohtQNwo9B7whK0w==
creator
RDT0fLC2Ht6/7ioD2jy9UkRQHU7YB8KjuhXMtW2p77stVVubhV6mw5rdytlr6xRuh2dxeaYviDEZYU4bXwcukg==
Construction.Project.Name
value
Cheyenne Expansion
RDT0fLC2Ht6/7ioD2jy9UkRQHU7YB8KjuhXMtW2p77stVVubhV6mw5rdytlr6xRuh2dxeaYviDEZYU4bXwcukg==->PYaXGGp+ksHg101LgyF/pB/OBQsixEhWZ9RDW9wxdwX/sVFWgyhpOZROgi4Gttdz1lWJ5Un0pJPJ5MvXEk1TCQ==
project
uECG/V1c641/SNfINkMRif0QPIQIoVcf0Qo7hWBpsG51BSX9SefJC1e7wbGg+/zcDQGxiw76bepnRcnL5TDifg==
Construction.Project.Name
value
Rivercrest Expansion
uECG/V1c641/SNfINkMRif0QPIQIoVcf0Qo7hWBpsG51BSX9SefJC1e7wbGg+/zcDQGxiw76bepnRcnL5TDifg==->PYaXGGp+ksHg101LgyF/pB/OBQsixEhWZ9RDW9wxdwX/sVFWgyhpOZROgi4Gttdz1lWJ5Un0pJPJ5MvXEk1TCQ==
project
uECG/V1c641/SNfINkMRif0QPIQIoVcf0Qo7hWBpsG51BSX9SefJC1e7wbGg+/zcDQGxiw76bepnRcnL5TDifg==->RDT0fLC2Ht6/7ioD2jy9UkRQHU7YB8KjuhXMtW2p77stVVubhV6mw5rdytlr6xRuh2dxeaYviDEZYU4bXwcukg==
prior
vGnXSLjOADIAr0eVBXktKVUElG/uFuumiUBtyRlJZO8ixenhcTySa9U+fJLMTkp3yFB2u28i6s0fA1yi2dAf5g==
Construction.Project.Name
value
Cheyenne Remodel
vGnXSLjOADIAr0eVBXktKVUElG/uFuumiUBtyRlJZO8ixenhcTySa9U+fJLMTkp3yFB2u28i6s0fA1yi2dAf5g==->PYaXGGp+ksHg101LgyF/pB/OBQsixEhWZ9RDW9wxdwX/sVFWgyhpOZROgi4Gttdz1lWJ5Un0pJPJ5MvXEk1TCQ==
project
vGnXSLjOADIAr0eVBXktKVUElG/uFuumiUBtyRlJZO8ixenhcTySa9U+fJLMTkp3yFB2u28i6s0fA1yi2dAf5g==->RDT0fLC2Ht6/7ioD2jy9UkRQHU7YB8KjuhXMtW2p77stVVubhV6mw5rdytlr6xRuh2dxeaYviDEZYU4bXwcukg==
prior
ML0Nc5M+KYIkky8t/IVjQ/BRaZm8IZGnkxQClMvsChIQsRgfHui79tE5a+fPnpX1ch8N0Yhd/6MW9eTZLfaFOg==
Construction.Project.Name
value
Rivercrest Remodel
ML0Nc5M+KYIkky8t/IVjQ/BRaZm8IZGnkxQClMvsChIQsRgfHui79tE5a+fPnpX1ch8N0Yhd/6MW9eTZLfaFOg==->PYaXGGp+ksHg101LgyF/pB/OBQsixEhWZ9RDW9wxdwX/sVFWgyhpOZROgi4Gttdz1lWJ5Un0pJPJ5MvXEk1TCQ==
project
ML0Nc5M+KYIkky8t/IVjQ/BRaZm8IZGnkxQClMvsChIQsRgfHui79tE5a+fPnpX1ch8N0Yhd/6MW9eTZLfaFOg==->uECG/V1c641/SNfINkMRif0QPIQIoVcf0Qo7hWBpsG51BSX9SefJC1e7wbGg+/zcDQGxiw76bepnRcnL5TDifg==
prior
ML0Nc5M+KYIkky8t/IVjQ/BRaZm8IZGnkxQClMvsChIQsRgfHui79tE5a+fPnpX1ch8N0Yhd/6MW9eTZLfaFOg==->vGnXSLjOADIAr0eVBXktKVUElG/uFuumiUBtyRlJZO8ixenhcTySa9U+fJLMTkp3yFB2u28i6s0fA1yi2dAf5g==
prior
And now only one successor matches the specification.
const names = await j. query ( namesOfProject, projectA) ;
names. map ( n => n. value)