@pdtf/schemas 3.6.0-dev.21 → 3.6.0-dev.23
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/docs/v4-mapping-and-recomposition.md +5 -2
- package/package.json +1 -1
- package/src/schemas/v3/combined.json +20 -1
- package/src/schemas/v3/compactSkeleton.txt +3 -0
- package/src/schemas/v3/pdtf-transaction.json +20 -1
- package/src/schemas/v3/skeleton.json +3 -0
- package/src/schemas/v4/Gift.json +2 -2
- package/src/schemas/v4/Offer.json +22 -3
- package/src/schemas/v4/Organisation.json +2 -2
- package/src/schemas/v4/Person.json +2 -2
- package/src/schemas/v4/Property.json +2 -2
- package/src/schemas/v4/Representation.json +2 -2
- package/src/schemas/v4/SellerCapacity.json +2 -2
- package/src/schemas/v4/Title.json +2 -2
- package/src/schemas/v4/Transaction.json +23 -4
- package/src/schemas/v4/TransactionRole.json +2 -2
- package/src/schemas/v4/mapping.json +22 -3
- package/src/utils/generateV4Schemas.js +19 -2
- package/src/utils/v4.js +95 -22
|
@@ -139,8 +139,11 @@ position that could diverge from another producer's.
|
|
|
139
139
|
|
|
140
140
|
1. **Overlay metadata** (`*Ref`, `*Required` keys) is stripped from V4. It is
|
|
141
141
|
schema-level only and never appears in instance data.
|
|
142
|
-
2.
|
|
143
|
-
`titlesToBeSold
|
|
142
|
+
2. `Transaction.titlesToBeSold` lists the titles V3 listed under
|
|
143
|
+
`propertyPack.titlesToBeSold`. A title known only from
|
|
144
|
+
`ownershipsToBeTransferred` is a `Title` entity but is not listed there, which
|
|
145
|
+
is how recomposition tells the two apart — and why a title whose only listed
|
|
146
|
+
key is `titleNumber` still gets its entry back.
|
|
144
147
|
3. **`ownershipsToBeTransferred` order is canonicalised** to `titlesToBeSold`
|
|
145
148
|
order. The array is correlated by `titleNumber` and its order carries no
|
|
146
149
|
meaning in V3, so this is a deliberate normalisation that removes the
|
package/package.json
CHANGED
|
@@ -49618,11 +49618,15 @@
|
|
|
49618
49618
|
"intendedUse": {
|
|
49619
49619
|
"type": "string",
|
|
49620
49620
|
"title": "Intended use of property",
|
|
49621
|
+
"description": "The primary purpose of the purchase: who will occupy the property, and on what basis. Secondary activities (a home office, a lodger, pets, alterations) are recorded in the separate fields below and are never values of this field, so a home-worker is \"Owner occupied\" with intendsBusiness true.",
|
|
49621
49622
|
"enum": [
|
|
49622
49623
|
"Owner occupied",
|
|
49623
49624
|
"Buy-to-let",
|
|
49624
49625
|
"Holiday let",
|
|
49625
|
-
"Second home"
|
|
49626
|
+
"Second home",
|
|
49627
|
+
"Family member occupied",
|
|
49628
|
+
"Development or renovation",
|
|
49629
|
+
"Business or mixed-use premises"
|
|
49626
49630
|
]
|
|
49627
49631
|
},
|
|
49628
49632
|
"firstTimeBuyer": {
|
|
@@ -49671,6 +49675,11 @@
|
|
|
49671
49675
|
"title": "Buyer has pets",
|
|
49672
49676
|
"description": "Relevant to lease and covenant pet restriction assessment"
|
|
49673
49677
|
},
|
|
49678
|
+
"petDetails": {
|
|
49679
|
+
"type": "string",
|
|
49680
|
+
"title": "Pets or other animals",
|
|
49681
|
+
"description": "The pets, horses or other animals the buyer intends to keep at the property, and how many (e.g. \"2 dogs, 1 cat\")"
|
|
49682
|
+
},
|
|
49674
49683
|
"intendsSublet": {
|
|
49675
49684
|
"type": "boolean",
|
|
49676
49685
|
"title": "Buyer intends to sublet",
|
|
@@ -49695,6 +49704,16 @@
|
|
|
49695
49704
|
"title": "Buyer intends significant alterations",
|
|
49696
49705
|
"description": "Relevant to covenant, planning and building regulations assessment"
|
|
49697
49706
|
},
|
|
49707
|
+
"alterationDescription": {
|
|
49708
|
+
"type": "string",
|
|
49709
|
+
"title": "Planned alterations",
|
|
49710
|
+
"description": "What the buyer intends to alter, extend or develop"
|
|
49711
|
+
},
|
|
49712
|
+
"vehicleRequirements": {
|
|
49713
|
+
"type": "string",
|
|
49714
|
+
"title": "Vehicles to keep at the property",
|
|
49715
|
+
"description": "Any caravan, motorhome, commercial vehicle or boat the buyer intends to keep at the property. Relevant to parking and use covenants"
|
|
49716
|
+
},
|
|
49698
49717
|
"financingMethod": {
|
|
49699
49718
|
"type": "string",
|
|
49700
49719
|
"title": "Primary financing method",
|
|
@@ -4518,11 +4518,14 @@
|
|
|
4518
4518
|
requiresMortgage
|
|
4519
4519
|
isAdditionalProperty
|
|
4520
4520
|
hasPets
|
|
4521
|
+
petDetails
|
|
4521
4522
|
intendsSublet
|
|
4522
4523
|
subletType
|
|
4523
4524
|
intendsBusiness
|
|
4524
4525
|
businessType
|
|
4525
4526
|
intendsAlterations
|
|
4527
|
+
alterationDescription
|
|
4528
|
+
vehicleRequirements
|
|
4526
4529
|
financingMethod
|
|
4527
4530
|
helpToBuyEquityLoan
|
|
4528
4531
|
armedForcesHelpToBuy
|
|
@@ -41645,11 +41645,15 @@
|
|
|
41645
41645
|
"intendedUse": {
|
|
41646
41646
|
"type": "string",
|
|
41647
41647
|
"title": "Intended use of property",
|
|
41648
|
+
"description": "The primary purpose of the purchase: who will occupy the property, and on what basis. Secondary activities (a home office, a lodger, pets, alterations) are recorded in the separate fields below and are never values of this field, so a home-worker is \"Owner occupied\" with intendsBusiness true.",
|
|
41648
41649
|
"enum": [
|
|
41649
41650
|
"Owner occupied",
|
|
41650
41651
|
"Buy-to-let",
|
|
41651
41652
|
"Holiday let",
|
|
41652
|
-
"Second home"
|
|
41653
|
+
"Second home",
|
|
41654
|
+
"Family member occupied",
|
|
41655
|
+
"Development or renovation",
|
|
41656
|
+
"Business or mixed-use premises"
|
|
41653
41657
|
]
|
|
41654
41658
|
},
|
|
41655
41659
|
"firstTimeBuyer": {
|
|
@@ -41701,6 +41705,11 @@
|
|
|
41701
41705
|
"title": "Buyer has pets",
|
|
41702
41706
|
"description": "Relevant to lease and covenant pet restriction assessment"
|
|
41703
41707
|
},
|
|
41708
|
+
"petDetails": {
|
|
41709
|
+
"type": "string",
|
|
41710
|
+
"title": "Pets or other animals",
|
|
41711
|
+
"description": "The pets, horses or other animals the buyer intends to keep at the property, and how many (e.g. \"2 dogs, 1 cat\")"
|
|
41712
|
+
},
|
|
41704
41713
|
"intendsSublet": {
|
|
41705
41714
|
"type": "boolean",
|
|
41706
41715
|
"title": "Buyer intends to sublet",
|
|
@@ -41732,6 +41741,16 @@
|
|
|
41732
41741
|
"title": "Buyer intends significant alterations",
|
|
41733
41742
|
"description": "Relevant to covenant, planning and building regulations assessment"
|
|
41734
41743
|
},
|
|
41744
|
+
"alterationDescription": {
|
|
41745
|
+
"type": "string",
|
|
41746
|
+
"title": "Planned alterations",
|
|
41747
|
+
"description": "What the buyer intends to alter, extend or develop"
|
|
41748
|
+
},
|
|
41749
|
+
"vehicleRequirements": {
|
|
41750
|
+
"type": "string",
|
|
41751
|
+
"title": "Vehicles to keep at the property",
|
|
41752
|
+
"description": "Any caravan, motorhome, commercial vehicle or boat the buyer intends to keep at the property. Relevant to parking and use covenants"
|
|
41753
|
+
},
|
|
41735
41754
|
"financingMethod": {
|
|
41736
41755
|
"type": "string",
|
|
41737
41756
|
"title": "Primary financing method",
|
|
@@ -5780,11 +5780,14 @@
|
|
|
5780
5780
|
"requiresMortgage": "variant",
|
|
5781
5781
|
"isAdditionalProperty": "boolean",
|
|
5782
5782
|
"hasPets": "boolean",
|
|
5783
|
+
"petDetails": "string",
|
|
5783
5784
|
"intendsSublet": "boolean",
|
|
5784
5785
|
"subletType": "string",
|
|
5785
5786
|
"intendsBusiness": "boolean",
|
|
5786
5787
|
"businessType": "string",
|
|
5787
5788
|
"intendsAlterations": "boolean",
|
|
5789
|
+
"alterationDescription": "string",
|
|
5790
|
+
"vehicleRequirements": "string",
|
|
5788
5791
|
"financingMethod": "string",
|
|
5789
5792
|
"helpToBuyEquityLoan": "boolean",
|
|
5790
5793
|
"armedForcesHelpToBuy": "boolean",
|
package/src/schemas/v4/Gift.json
CHANGED
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
"description": "A gift of funds towards a purchase, made by a gift donor. Implies the Gift Donor role.",
|
|
7
7
|
"x-pdtf-source": {
|
|
8
8
|
"packageName": "@pdtf/schemas",
|
|
9
|
-
"packageVersion": "3.6.0-dev.
|
|
9
|
+
"packageVersion": "3.6.0-dev.23",
|
|
10
10
|
"v3SchemaId": "https://trust.propdata.org.uk/schemas/v3/pdtf-transaction.json",
|
|
11
11
|
"combinedPath": "src/schemas/v3/combined.json",
|
|
12
|
-
"combinedSha256": "
|
|
12
|
+
"combinedSha256": "21df4c6fb2117e9913eb9b16e48c9e6fe5f9ae9988af99e22bfcd4ef4bc6ceab"
|
|
13
13
|
},
|
|
14
14
|
"properties": {
|
|
15
15
|
"id": {
|
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
"description": "An offer made by a prospective buyer in a property transaction.",
|
|
7
7
|
"x-pdtf-source": {
|
|
8
8
|
"packageName": "@pdtf/schemas",
|
|
9
|
-
"packageVersion": "3.6.0-dev.
|
|
9
|
+
"packageVersion": "3.6.0-dev.23",
|
|
10
10
|
"v3SchemaId": "https://trust.propdata.org.uk/schemas/v3/pdtf-transaction.json",
|
|
11
11
|
"combinedPath": "src/schemas/v3/combined.json",
|
|
12
|
-
"combinedSha256": "
|
|
12
|
+
"combinedSha256": "21df4c6fb2117e9913eb9b16e48c9e6fe5f9ae9988af99e22bfcd4ef4bc6ceab"
|
|
13
13
|
},
|
|
14
14
|
"properties": {
|
|
15
15
|
"id": {
|
|
@@ -130,11 +130,15 @@
|
|
|
130
130
|
"intendedUse": {
|
|
131
131
|
"type": "string",
|
|
132
132
|
"title": "Intended use of property",
|
|
133
|
+
"description": "The primary purpose of the purchase: who will occupy the property, and on what basis. Secondary activities (a home office, a lodger, pets, alterations) are recorded in the separate fields below and are never values of this field, so a home-worker is \"Owner occupied\" with intendsBusiness true.",
|
|
133
134
|
"enum": [
|
|
134
135
|
"Owner occupied",
|
|
135
136
|
"Buy-to-let",
|
|
136
137
|
"Holiday let",
|
|
137
|
-
"Second home"
|
|
138
|
+
"Second home",
|
|
139
|
+
"Family member occupied",
|
|
140
|
+
"Development or renovation",
|
|
141
|
+
"Business or mixed-use premises"
|
|
138
142
|
]
|
|
139
143
|
},
|
|
140
144
|
"firstTimeBuyer": {
|
|
@@ -186,6 +190,11 @@
|
|
|
186
190
|
"title": "Buyer has pets",
|
|
187
191
|
"description": "Relevant to lease and covenant pet restriction assessment"
|
|
188
192
|
},
|
|
193
|
+
"petDetails": {
|
|
194
|
+
"type": "string",
|
|
195
|
+
"title": "Pets or other animals",
|
|
196
|
+
"description": "The pets, horses or other animals the buyer intends to keep at the property, and how many (e.g. \"2 dogs, 1 cat\")"
|
|
197
|
+
},
|
|
189
198
|
"intendsSublet": {
|
|
190
199
|
"type": "boolean",
|
|
191
200
|
"title": "Buyer intends to sublet",
|
|
@@ -217,6 +226,16 @@
|
|
|
217
226
|
"title": "Buyer intends significant alterations",
|
|
218
227
|
"description": "Relevant to covenant, planning and building regulations assessment"
|
|
219
228
|
},
|
|
229
|
+
"alterationDescription": {
|
|
230
|
+
"type": "string",
|
|
231
|
+
"title": "Planned alterations",
|
|
232
|
+
"description": "What the buyer intends to alter, extend or develop"
|
|
233
|
+
},
|
|
234
|
+
"vehicleRequirements": {
|
|
235
|
+
"type": "string",
|
|
236
|
+
"title": "Vehicles to keep at the property",
|
|
237
|
+
"description": "Any caravan, motorhome, commercial vehicle or boat the buyer intends to keep at the property. Relevant to parking and use covenants"
|
|
238
|
+
},
|
|
220
239
|
"financingMethod": {
|
|
221
240
|
"type": "string",
|
|
222
241
|
"title": "Primary financing method",
|
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
"description": "A legal entity (company, firm, partnership) participating in a property transaction.",
|
|
7
7
|
"x-pdtf-source": {
|
|
8
8
|
"packageName": "@pdtf/schemas",
|
|
9
|
-
"packageVersion": "3.6.0-dev.
|
|
9
|
+
"packageVersion": "3.6.0-dev.23",
|
|
10
10
|
"v3SchemaId": "https://trust.propdata.org.uk/schemas/v3/pdtf-transaction.json",
|
|
11
11
|
"combinedPath": "src/schemas/v3/combined.json",
|
|
12
|
-
"combinedSha256": "
|
|
12
|
+
"combinedSha256": "21df4c6fb2117e9913eb9b16e48c9e6fe5f9ae9988af99e22bfcd4ef4bc6ceab"
|
|
13
13
|
},
|
|
14
14
|
"properties": {
|
|
15
15
|
"id": {
|
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
"description": "A natural person participating in a property transaction — name, contact details, address, verification status.",
|
|
7
7
|
"x-pdtf-source": {
|
|
8
8
|
"packageName": "@pdtf/schemas",
|
|
9
|
-
"packageVersion": "3.6.0-dev.
|
|
9
|
+
"packageVersion": "3.6.0-dev.23",
|
|
10
10
|
"v3SchemaId": "https://trust.propdata.org.uk/schemas/v3/pdtf-transaction.json",
|
|
11
11
|
"combinedPath": "src/schemas/v3/combined.json",
|
|
12
|
-
"combinedSha256": "
|
|
12
|
+
"combinedSha256": "21df4c6fb2117e9913eb9b16e48c9e6fe5f9ae9988af99e22bfcd4ef4bc6ceab"
|
|
13
13
|
},
|
|
14
14
|
"properties": {
|
|
15
15
|
"name": {
|
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
"description": "Physical and environmental facts about a property — address, construction, fixtures, services, searches, documents. Excludes ownership, titles, and participants.",
|
|
7
7
|
"x-pdtf-source": {
|
|
8
8
|
"packageName": "@pdtf/schemas",
|
|
9
|
-
"packageVersion": "3.6.0-dev.
|
|
9
|
+
"packageVersion": "3.6.0-dev.23",
|
|
10
10
|
"v3SchemaId": "https://trust.propdata.org.uk/schemas/v3/pdtf-transaction.json",
|
|
11
11
|
"combinedPath": "src/schemas/v3/combined.json",
|
|
12
|
-
"combinedSha256": "
|
|
12
|
+
"combinedSha256": "21df4c6fb2117e9913eb9b16e48c9e6fe5f9ae9988af99e22bfcd4ef4bc6ceab"
|
|
13
13
|
},
|
|
14
14
|
"properties": {
|
|
15
15
|
"address": {
|
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
"description": "A professional representation relationship — one party instructed by another, e.g. a conveyancer acting for a seller. One entity per (representative, represented party) pair: a conveyancer instructed jointly by two sellers yields two, and sellers who instruct separate conveyancers yield one each.",
|
|
7
7
|
"x-pdtf-source": {
|
|
8
8
|
"packageName": "@pdtf/schemas",
|
|
9
|
-
"packageVersion": "3.6.0-dev.
|
|
9
|
+
"packageVersion": "3.6.0-dev.23",
|
|
10
10
|
"v3SchemaId": "https://trust.propdata.org.uk/schemas/v3/pdtf-transaction.json",
|
|
11
11
|
"combinedPath": "src/schemas/v3/combined.json",
|
|
12
|
-
"combinedSha256": "
|
|
12
|
+
"combinedSha256": "21df4c6fb2117e9913eb9b16e48c9e6fe5f9ae9988af99e22bfcd4ef4bc6ceab"
|
|
13
13
|
},
|
|
14
14
|
"properties": {
|
|
15
15
|
"id": {
|
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
"description": "The legal capacity in which a seller is selling a given title. Derived from the V3 participants 'Seller' branch (sellersCapacity, dateBecameOwnerOrAuthority).",
|
|
7
7
|
"x-pdtf-source": {
|
|
8
8
|
"packageName": "@pdtf/schemas",
|
|
9
|
-
"packageVersion": "3.6.0-dev.
|
|
9
|
+
"packageVersion": "3.6.0-dev.23",
|
|
10
10
|
"v3SchemaId": "https://trust.propdata.org.uk/schemas/v3/pdtf-transaction.json",
|
|
11
11
|
"combinedPath": "src/schemas/v3/combined.json",
|
|
12
|
-
"combinedSha256": "
|
|
12
|
+
"combinedSha256": "21df4c6fb2117e9913eb9b16e48c9e6fe5f9ae9988af99e22bfcd4ef4bc6ceab"
|
|
13
13
|
},
|
|
14
14
|
"properties": {
|
|
15
15
|
"id": {
|
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
"description": "HM Land Registry title — register extract, tenure type, and legal interest details for a single title number.",
|
|
7
7
|
"x-pdtf-source": {
|
|
8
8
|
"packageName": "@pdtf/schemas",
|
|
9
|
-
"packageVersion": "3.6.0-dev.
|
|
9
|
+
"packageVersion": "3.6.0-dev.23",
|
|
10
10
|
"v3SchemaId": "https://trust.propdata.org.uk/schemas/v3/pdtf-transaction.json",
|
|
11
11
|
"combinedPath": "src/schemas/v3/combined.json",
|
|
12
|
-
"combinedSha256": "
|
|
12
|
+
"combinedSha256": "21df4c6fb2117e9913eb9b16e48c9e6fe5f9ae9988af99e22bfcd4ef4bc6ceab"
|
|
13
13
|
},
|
|
14
14
|
"properties": {
|
|
15
15
|
"titleNumber": {
|
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
"description": "Transaction-level metadata — status, milestones, chain, contracts, offers, enquiries, and sale context.",
|
|
7
7
|
"x-pdtf-source": {
|
|
8
8
|
"packageName": "@pdtf/schemas",
|
|
9
|
-
"packageVersion": "3.6.0-dev.
|
|
9
|
+
"packageVersion": "3.6.0-dev.23",
|
|
10
10
|
"v3SchemaId": "https://trust.propdata.org.uk/schemas/v3/pdtf-transaction.json",
|
|
11
11
|
"combinedPath": "src/schemas/v3/combined.json",
|
|
12
|
-
"combinedSha256": "
|
|
12
|
+
"combinedSha256": "21df4c6fb2117e9913eb9b16e48c9e6fe5f9ae9988af99e22bfcd4ef4bc6ceab"
|
|
13
13
|
},
|
|
14
14
|
"properties": {
|
|
15
15
|
"transactionId": {
|
|
@@ -984,11 +984,15 @@
|
|
|
984
984
|
"intendedUse": {
|
|
985
985
|
"type": "string",
|
|
986
986
|
"title": "Intended use of property",
|
|
987
|
+
"description": "The primary purpose of the purchase: who will occupy the property, and on what basis. Secondary activities (a home office, a lodger, pets, alterations) are recorded in the separate fields below and are never values of this field, so a home-worker is \"Owner occupied\" with intendsBusiness true.",
|
|
987
988
|
"enum": [
|
|
988
989
|
"Owner occupied",
|
|
989
990
|
"Buy-to-let",
|
|
990
991
|
"Holiday let",
|
|
991
|
-
"Second home"
|
|
992
|
+
"Second home",
|
|
993
|
+
"Family member occupied",
|
|
994
|
+
"Development or renovation",
|
|
995
|
+
"Business or mixed-use premises"
|
|
992
996
|
]
|
|
993
997
|
},
|
|
994
998
|
"firstTimeBuyer": {
|
|
@@ -1040,6 +1044,11 @@
|
|
|
1040
1044
|
"title": "Buyer has pets",
|
|
1041
1045
|
"description": "Relevant to lease and covenant pet restriction assessment"
|
|
1042
1046
|
},
|
|
1047
|
+
"petDetails": {
|
|
1048
|
+
"type": "string",
|
|
1049
|
+
"title": "Pets or other animals",
|
|
1050
|
+
"description": "The pets, horses or other animals the buyer intends to keep at the property, and how many (e.g. \"2 dogs, 1 cat\")"
|
|
1051
|
+
},
|
|
1043
1052
|
"intendsSublet": {
|
|
1044
1053
|
"type": "boolean",
|
|
1045
1054
|
"title": "Buyer intends to sublet",
|
|
@@ -1071,6 +1080,16 @@
|
|
|
1071
1080
|
"title": "Buyer intends significant alterations",
|
|
1072
1081
|
"description": "Relevant to covenant, planning and building regulations assessment"
|
|
1073
1082
|
},
|
|
1083
|
+
"alterationDescription": {
|
|
1084
|
+
"type": "string",
|
|
1085
|
+
"title": "Planned alterations",
|
|
1086
|
+
"description": "What the buyer intends to alter, extend or develop"
|
|
1087
|
+
},
|
|
1088
|
+
"vehicleRequirements": {
|
|
1089
|
+
"type": "string",
|
|
1090
|
+
"title": "Vehicles to keep at the property",
|
|
1091
|
+
"description": "Any caravan, motorhome, commercial vehicle or boat the buyer intends to keep at the property. Relevant to parking and use covenants"
|
|
1092
|
+
},
|
|
1074
1093
|
"financingMethod": {
|
|
1075
1094
|
"type": "string",
|
|
1076
1095
|
"title": "Primary financing method",
|
|
@@ -1396,7 +1415,7 @@
|
|
|
1396
1415
|
},
|
|
1397
1416
|
"titlesToBeSold": {
|
|
1398
1417
|
"title": "Titles to be Sold",
|
|
1399
|
-
"description": "
|
|
1418
|
+
"description": "Ordered URNs of the Title entities that V3 listed in propertyPack.titlesToBeSold. A title known only from propertyPack.ownership.ownershipsToBeTransferred is a Title entity but is NOT listed here, so recomposition can tell the two apart and rebuild each source array faithfully.",
|
|
1400
1419
|
"type": "array",
|
|
1401
1420
|
"minItems": 1,
|
|
1402
1421
|
"items": {
|
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
"description": "A party's role in a transaction, where no more specific relationship credential applies. Asserts participation in a role, not a relationship to another named party.",
|
|
7
7
|
"x-pdtf-source": {
|
|
8
8
|
"packageName": "@pdtf/schemas",
|
|
9
|
-
"packageVersion": "3.6.0-dev.
|
|
9
|
+
"packageVersion": "3.6.0-dev.23",
|
|
10
10
|
"v3SchemaId": "https://trust.propdata.org.uk/schemas/v3/pdtf-transaction.json",
|
|
11
11
|
"combinedPath": "src/schemas/v3/combined.json",
|
|
12
|
-
"combinedSha256": "
|
|
12
|
+
"combinedSha256": "21df4c6fb2117e9913eb9b16e48c9e6fe5f9ae9988af99e22bfcd4ef4bc6ceab"
|
|
13
13
|
},
|
|
14
14
|
"properties": {
|
|
15
15
|
"id": {
|
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
"generator": "src/utils/generateV4Schemas.js",
|
|
6
6
|
"source": {
|
|
7
7
|
"packageName": "@pdtf/schemas",
|
|
8
|
-
"packageVersion": "3.6.0-dev.
|
|
8
|
+
"packageVersion": "3.6.0-dev.23",
|
|
9
9
|
"v3SchemaId": "https://trust.propdata.org.uk/schemas/v3/pdtf-transaction.json",
|
|
10
10
|
"combinedPath": "src/schemas/v3/combined.json",
|
|
11
|
-
"combinedSha256": "
|
|
11
|
+
"combinedSha256": "21df4c6fb2117e9913eb9b16e48c9e6fe5f9ae9988af99e22bfcd4ef4bc6ceab"
|
|
12
12
|
},
|
|
13
13
|
"entities": {
|
|
14
14
|
"Property": {
|
|
@@ -93,6 +93,13 @@
|
|
|
93
93
|
"The round trip restores deep equality, NOT byte equality: object key order is not preserved, because keys are regrouped by entity and reassembled. JSON attaches no meaning to key order, but do not compare a recomposed instance to the original by hashing or string equality — compare structurally, or canonicalise first (e.g. JCS) if you need a stable digest."
|
|
94
94
|
]
|
|
95
95
|
},
|
|
96
|
+
"extensionPolicy": {
|
|
97
|
+
"description": "What each rule does with instance keys the V3 schema does not define — platform extensions such as an externalIds namespace or a vendor's own block. `passthrough` carries them into the entity and back out again on recomposition; `drop` discards them. Exactly one rule per V3 node is the sink, so an unknown key has a single, predictable home. verifyCoverage() guarantees every V3 SCHEMA key has a V4 home; this is the answer for everything outside the schema.",
|
|
98
|
+
"values": [
|
|
99
|
+
"passthrough",
|
|
100
|
+
"drop"
|
|
101
|
+
]
|
|
102
|
+
},
|
|
96
103
|
"arrayKeys": [
|
|
97
104
|
{
|
|
98
105
|
"pointer": "/participants",
|
|
@@ -883,6 +890,7 @@
|
|
|
883
890
|
"covenantAnalysis"
|
|
884
891
|
]
|
|
885
892
|
},
|
|
893
|
+
"extensions": "passthrough",
|
|
886
894
|
"instance": {
|
|
887
895
|
"cardinality": "many",
|
|
888
896
|
"orderedBy": {
|
|
@@ -892,7 +900,7 @@
|
|
|
892
900
|
"correlateBy": "titleNumber",
|
|
893
901
|
"primarySource": true
|
|
894
902
|
},
|
|
895
|
-
"notes": "Primary source:
|
|
903
|
+
"notes": "Primary source: it takes any key the V3 schema does not define, and recomposition emits one entry per id in Transaction.titlesToBeSold."
|
|
896
904
|
},
|
|
897
905
|
{
|
|
898
906
|
"id": "title.ownershipsToBeTransferred",
|
|
@@ -913,6 +921,7 @@
|
|
|
913
921
|
"otherOwnershipDetails"
|
|
914
922
|
]
|
|
915
923
|
},
|
|
924
|
+
"extensions": "drop",
|
|
916
925
|
"instance": {
|
|
917
926
|
"cardinality": "many",
|
|
918
927
|
"orderedBy": {
|
|
@@ -944,6 +953,7 @@
|
|
|
944
953
|
"ownershipsToBeTransferred"
|
|
945
954
|
]
|
|
946
955
|
},
|
|
956
|
+
"extensions": "passthrough",
|
|
947
957
|
"instance": {
|
|
948
958
|
"cardinality": "single"
|
|
949
959
|
}
|
|
@@ -961,6 +971,7 @@
|
|
|
961
971
|
"legalOwners"
|
|
962
972
|
]
|
|
963
973
|
},
|
|
974
|
+
"extensions": "passthrough",
|
|
964
975
|
"instance": {
|
|
965
976
|
"cardinality": "single"
|
|
966
977
|
}
|
|
@@ -979,6 +990,7 @@
|
|
|
979
990
|
"organisationReference"
|
|
980
991
|
]
|
|
981
992
|
},
|
|
993
|
+
"extensions": "drop",
|
|
982
994
|
"instance": {
|
|
983
995
|
"cardinality": "many",
|
|
984
996
|
"orderedBy": {
|
|
@@ -990,6 +1002,7 @@
|
|
|
990
1002
|
},
|
|
991
1003
|
{
|
|
992
1004
|
"id": "credential.sellerCapacity",
|
|
1005
|
+
"extensions": "drop",
|
|
993
1006
|
"v3Pointer": "/participants/{index}",
|
|
994
1007
|
"entity": "SellerCapacity",
|
|
995
1008
|
"entityPointer": "",
|
|
@@ -1008,6 +1021,7 @@
|
|
|
1008
1021
|
},
|
|
1009
1022
|
{
|
|
1010
1023
|
"id": "credential.offer",
|
|
1024
|
+
"extensions": "drop",
|
|
1011
1025
|
"v3Pointer": "/participants/{index}",
|
|
1012
1026
|
"entity": "Offer",
|
|
1013
1027
|
"entityPointer": "",
|
|
@@ -1025,6 +1039,7 @@
|
|
|
1025
1039
|
},
|
|
1026
1040
|
{
|
|
1027
1041
|
"id": "credential.gift",
|
|
1042
|
+
"extensions": "drop",
|
|
1028
1043
|
"v3Pointer": "/participants/{index}",
|
|
1029
1044
|
"entity": "Gift",
|
|
1030
1045
|
"entityPointer": "",
|
|
@@ -1043,6 +1058,7 @@
|
|
|
1043
1058
|
},
|
|
1044
1059
|
{
|
|
1045
1060
|
"id": "credential.representation",
|
|
1061
|
+
"extensions": "drop",
|
|
1046
1062
|
"v3Pointer": "/participants/{index}",
|
|
1047
1063
|
"entity": "Representation",
|
|
1048
1064
|
"entityPointer": "",
|
|
@@ -1065,6 +1081,7 @@
|
|
|
1065
1081
|
},
|
|
1066
1082
|
{
|
|
1067
1083
|
"id": "credential.transactionRole",
|
|
1084
|
+
"extensions": "drop",
|
|
1068
1085
|
"v3Pointer": "/participants/{index}",
|
|
1069
1086
|
"entity": "TransactionRole",
|
|
1070
1087
|
"entityPointer": "",
|
|
@@ -1099,6 +1116,7 @@
|
|
|
1099
1116
|
"verification"
|
|
1100
1117
|
]
|
|
1101
1118
|
},
|
|
1119
|
+
"extensions": "passthrough",
|
|
1102
1120
|
"instance": {
|
|
1103
1121
|
"cardinality": "many",
|
|
1104
1122
|
"orderedBy": {
|
|
@@ -1121,6 +1139,7 @@
|
|
|
1121
1139
|
"$schema"
|
|
1122
1140
|
]
|
|
1123
1141
|
},
|
|
1142
|
+
"extensions": "passthrough",
|
|
1124
1143
|
"instance": {
|
|
1125
1144
|
"cardinality": "single"
|
|
1126
1145
|
}
|
|
@@ -398,7 +398,7 @@ const generateTransaction = () => {
|
|
|
398
398
|
topLevel.titlesToBeSold = {
|
|
399
399
|
title: "Titles to be Sold",
|
|
400
400
|
description:
|
|
401
|
-
"
|
|
401
|
+
"Ordered URNs of the Title entities that V3 listed in propertyPack.titlesToBeSold. A title known only from propertyPack.ownership.ownershipsToBeTransferred is a Title entity but is NOT listed here, so recomposition can tell the two apart and rebuild each source array faithfully.",
|
|
402
402
|
type: "array",
|
|
403
403
|
minItems: 1,
|
|
404
404
|
items: urnRef("Title URN", "URN reference to a Title entity"),
|
|
@@ -973,6 +973,7 @@ const buildRules = () => [
|
|
|
973
973
|
entity: "Title",
|
|
974
974
|
entityPointer: "",
|
|
975
975
|
keys: { mode: "include", values: titlesKeys },
|
|
976
|
+
extensions: "passthrough",
|
|
976
977
|
instance: {
|
|
977
978
|
cardinality: "many",
|
|
978
979
|
orderedBy: { entity: "Transaction", pointer: "/titlesToBeSold" },
|
|
@@ -980,7 +981,7 @@ const buildRules = () => [
|
|
|
980
981
|
primarySource: true,
|
|
981
982
|
},
|
|
982
983
|
notes:
|
|
983
|
-
"Primary source:
|
|
984
|
+
"Primary source: it takes any key the V3 schema does not define, and recomposition emits one entry per id in Transaction.titlesToBeSold.",
|
|
984
985
|
},
|
|
985
986
|
{
|
|
986
987
|
id: "title.ownershipsToBeTransferred",
|
|
@@ -988,6 +989,7 @@ const buildRules = () => [
|
|
|
988
989
|
entity: "Title",
|
|
989
990
|
entityPointer: "",
|
|
990
991
|
keys: { mode: "include", values: ownershipItemKeys },
|
|
992
|
+
extensions: "drop",
|
|
991
993
|
instance: {
|
|
992
994
|
cardinality: "many",
|
|
993
995
|
orderedBy: { entity: "Transaction", pointer: "/titlesToBeSold" },
|
|
@@ -1012,6 +1014,7 @@ const buildRules = () => [
|
|
|
1012
1014
|
entity: "Transaction",
|
|
1013
1015
|
entityPointer: "/saleContext",
|
|
1014
1016
|
keys: { mode: "exclude", values: ["ownershipsToBeTransferred"] },
|
|
1017
|
+
extensions: "passthrough",
|
|
1015
1018
|
instance: { cardinality: "single" },
|
|
1016
1019
|
},
|
|
1017
1020
|
|
|
@@ -1022,6 +1025,7 @@ const buildRules = () => [
|
|
|
1022
1025
|
entity: "Property",
|
|
1023
1026
|
entityPointer: "",
|
|
1024
1027
|
keys: { mode: "exclude", values: PROPERTY_EXCLUDE },
|
|
1028
|
+
extensions: "passthrough",
|
|
1025
1029
|
instance: { cardinality: "single" },
|
|
1026
1030
|
},
|
|
1027
1031
|
|
|
@@ -1032,6 +1036,7 @@ const buildRules = () => [
|
|
|
1032
1036
|
entity: "Transaction",
|
|
1033
1037
|
entityPointer: "/participants/{index}",
|
|
1034
1038
|
keys: { mode: "include", values: PARTICIPANT_ROSTER_FIELDS },
|
|
1039
|
+
extensions: "drop",
|
|
1035
1040
|
instance: {
|
|
1036
1041
|
cardinality: "many",
|
|
1037
1042
|
orderedBy: { entity: "Transaction", pointer: "/participants" },
|
|
@@ -1046,6 +1051,7 @@ const buildRules = () => [
|
|
|
1046
1051
|
// returns the candidates; pass the decomposed entities to narrow it.
|
|
1047
1052
|
{
|
|
1048
1053
|
id: "credential.sellerCapacity",
|
|
1054
|
+
extensions: "drop",
|
|
1049
1055
|
v3Pointer: "/participants/{index}",
|
|
1050
1056
|
entity: "SellerCapacity",
|
|
1051
1057
|
entityPointer: "",
|
|
@@ -1055,6 +1061,7 @@ const buildRules = () => [
|
|
|
1055
1061
|
},
|
|
1056
1062
|
{
|
|
1057
1063
|
id: "credential.offer",
|
|
1064
|
+
extensions: "drop",
|
|
1058
1065
|
v3Pointer: "/participants/{index}",
|
|
1059
1066
|
entity: "Offer",
|
|
1060
1067
|
entityPointer: "",
|
|
@@ -1064,6 +1071,7 @@ const buildRules = () => [
|
|
|
1064
1071
|
},
|
|
1065
1072
|
{
|
|
1066
1073
|
id: "credential.gift",
|
|
1074
|
+
extensions: "drop",
|
|
1067
1075
|
v3Pointer: "/participants/{index}",
|
|
1068
1076
|
entity: "Gift",
|
|
1069
1077
|
entityPointer: "",
|
|
@@ -1073,6 +1081,7 @@ const buildRules = () => [
|
|
|
1073
1081
|
},
|
|
1074
1082
|
{
|
|
1075
1083
|
id: "credential.representation",
|
|
1084
|
+
extensions: "drop",
|
|
1076
1085
|
v3Pointer: "/participants/{index}",
|
|
1077
1086
|
entity: "Representation",
|
|
1078
1087
|
entityPointer: "",
|
|
@@ -1084,6 +1093,7 @@ const buildRules = () => [
|
|
|
1084
1093
|
},
|
|
1085
1094
|
{
|
|
1086
1095
|
id: "credential.transactionRole",
|
|
1096
|
+
extensions: "drop",
|
|
1087
1097
|
v3Pointer: "/participants/{index}",
|
|
1088
1098
|
entity: "TransactionRole",
|
|
1089
1099
|
entityPointer: "",
|
|
@@ -1098,6 +1108,7 @@ const buildRules = () => [
|
|
|
1098
1108
|
entity: "Person",
|
|
1099
1109
|
entityPointer: "",
|
|
1100
1110
|
keys: { mode: "include", values: personKeys },
|
|
1111
|
+
extensions: "passthrough",
|
|
1101
1112
|
instance: {
|
|
1102
1113
|
cardinality: "many",
|
|
1103
1114
|
orderedBy: {
|
|
@@ -1115,6 +1126,7 @@ const buildRules = () => [
|
|
|
1115
1126
|
entity: "Transaction",
|
|
1116
1127
|
entityPointer: "",
|
|
1117
1128
|
keys: { mode: "exclude", values: [...TRANSACTION_EXCLUDE, "$schema"] },
|
|
1129
|
+
extensions: "passthrough",
|
|
1118
1130
|
instance: { cardinality: "single" },
|
|
1119
1131
|
},
|
|
1120
1132
|
];
|
|
@@ -1236,6 +1248,11 @@ const mapping = {
|
|
|
1236
1248
|
"The round trip restores deep equality, NOT byte equality: object key order is not preserved, because keys are regrouped by entity and reassembled. JSON attaches no meaning to key order, but do not compare a recomposed instance to the original by hashing or string equality — compare structurally, or canonicalise first (e.g. JCS) if you need a stable digest.",
|
|
1237
1249
|
],
|
|
1238
1250
|
},
|
|
1251
|
+
extensionPolicy: {
|
|
1252
|
+
description:
|
|
1253
|
+
"What each rule does with instance keys the V3 schema does not define — platform extensions such as an externalIds namespace or a vendor's own block. `passthrough` carries them into the entity and back out again on recomposition; `drop` discards them. Exactly one rule per V3 node is the sink, so an unknown key has a single, predictable home. verifyCoverage() guarantees every V3 SCHEMA key has a V4 home; this is the answer for everything outside the schema.",
|
|
1254
|
+
values: ["passthrough", "drop"],
|
|
1255
|
+
},
|
|
1239
1256
|
arrayKeys: arrayKeys.keyed,
|
|
1240
1257
|
arrayKeyScopes: {
|
|
1241
1258
|
canonical:
|
package/src/utils/v4.js
CHANGED
|
@@ -46,12 +46,69 @@ const omit = (obj, keys) =>
|
|
|
46
46
|
Object.entries(obj || {}).filter(([k]) => !keys.includes(k))
|
|
47
47
|
);
|
|
48
48
|
|
|
49
|
-
/**
|
|
49
|
+
/**
|
|
50
|
+
* Every key any rule at this V3 pointer claims by name. Anything outside it is
|
|
51
|
+
* an extension: a platform's own block, or an externalIds namespace the schema
|
|
52
|
+
* does not define.
|
|
53
|
+
*/
|
|
54
|
+
const claimedKeysAt = (v3Pointer) => {
|
|
55
|
+
const claimed = new Set();
|
|
56
|
+
for (const rule of mapping.rules) {
|
|
57
|
+
if (rule.v3Pointer !== v3Pointer || rule.keys?.mode !== "include") continue;
|
|
58
|
+
for (const key of rule.keys.values) claimed.add(key);
|
|
59
|
+
}
|
|
60
|
+
return claimed;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const CLAIMED_BY_POINTER = new Map();
|
|
64
|
+
const claimedFor = (v3Pointer) => {
|
|
65
|
+
if (!CLAIMED_BY_POINTER.has(v3Pointer)) {
|
|
66
|
+
CLAIMED_BY_POINTER.set(v3Pointer, claimedKeysAt(v3Pointer));
|
|
67
|
+
}
|
|
68
|
+
return CLAIMED_BY_POINTER.get(v3Pointer);
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Apply a rule's `keys` selector to a V3 node.
|
|
73
|
+
*
|
|
74
|
+
* An exclude-mode rule takes everything it does not exclude, extensions
|
|
75
|
+
* included. An include-mode rule names its keys, so extensions would fall
|
|
76
|
+
* through the gap — which is why each V3 node nominates exactly one rule as its
|
|
77
|
+
* extension sink (`extensions: "passthrough"` in the manifest). Without that,
|
|
78
|
+
* whether a platform's own keys survived depended on which mode happened to
|
|
79
|
+
* claim their parent.
|
|
80
|
+
*/
|
|
50
81
|
const selectKeys = (node, rule) => {
|
|
51
82
|
if (!rule.keys) return { ...(node || {}) };
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
83
|
+
if (rule.keys.mode === "exclude") return omit(node, rule.keys.values);
|
|
84
|
+
|
|
85
|
+
const selected = pick(node, rule.keys.values);
|
|
86
|
+
if (rule.extensions === "passthrough") {
|
|
87
|
+
const claimed = claimedFor(rule.v3Pointer);
|
|
88
|
+
for (const [key, value] of Object.entries(node || {})) {
|
|
89
|
+
if (!claimed.has(key)) selected[key] = value;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return selected;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* A hole in a V3 array — an index written before an earlier one exists — is
|
|
97
|
+
* skipped by forEach, which would silently shift every later item and lose its
|
|
98
|
+
* position. Refuse instead: the position is data here, and a quiet shift is
|
|
99
|
+
* worse than a stop.
|
|
100
|
+
*/
|
|
101
|
+
const assertDense = (array, label) => {
|
|
102
|
+
if (!Array.isArray(array)) return array;
|
|
103
|
+
for (let i = 0; i < array.length; i += 1) {
|
|
104
|
+
if (!(i in array)) {
|
|
105
|
+
throw new Error(
|
|
106
|
+
`v4 decompose: ${label} has a hole at index ${i} (length ${array.length}). ` +
|
|
107
|
+
"Array position carries meaning here, so the gap must be filled or the entry removed before decomposing."
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return array;
|
|
55
112
|
};
|
|
56
113
|
|
|
57
114
|
const isEmpty = (v) =>
|
|
@@ -153,17 +210,19 @@ const decompose = (v3, { idFactory } = {}) => {
|
|
|
153
210
|
* malformed data. Failing loudly beats losing an entry.
|
|
154
211
|
*/
|
|
155
212
|
const mergeSource = (items, rule, sourceName) => {
|
|
156
|
-
const seen = new
|
|
213
|
+
const seen = new Map();
|
|
214
|
+
assertDense(items, sourceName);
|
|
157
215
|
items.forEach((item, index) => {
|
|
158
216
|
const correlationValue = item[correlateBy];
|
|
159
217
|
if (correlationValue !== undefined) {
|
|
160
218
|
if (seen.has(correlationValue)) {
|
|
161
219
|
throw new Error(
|
|
162
|
-
`v4 decompose: ${sourceName}[${index}] repeats ${correlateBy} "${correlationValue}"
|
|
220
|
+
`v4 decompose: ${sourceName}[${index}] repeats ${correlateBy} "${correlationValue}", ` +
|
|
221
|
+
`already used by ${sourceName}[${seen.get(correlationValue)}]. ` +
|
|
163
222
|
`${correlateBy} must be unique within an array — it is what correlates the two Title sources.`
|
|
164
223
|
);
|
|
165
224
|
}
|
|
166
|
-
seen.
|
|
225
|
+
seen.set(correlationValue, index);
|
|
167
226
|
}
|
|
168
227
|
Object.assign(upsert(correlationValue), selectKeys(item, rule));
|
|
169
228
|
});
|
|
@@ -171,6 +230,10 @@ const decompose = (v3, { idFactory } = {}) => {
|
|
|
171
230
|
|
|
172
231
|
// titlesToBeSold order leads; ownership-only titles are appended after.
|
|
173
232
|
mergeSource(titleItems, titlesRule, "propertyPack.titlesToBeSold");
|
|
233
|
+
// Which titles V3 actually listed under titlesToBeSold, as opposed to knowing
|
|
234
|
+
// only from ownership. Without this, a title whose sole titlesToBeSold key is
|
|
235
|
+
// titleNumber is indistinguishable from one that was never listed there.
|
|
236
|
+
const listedUnderTitles = new Set(titles);
|
|
174
237
|
mergeSource(
|
|
175
238
|
ownershipItems,
|
|
176
239
|
ownershipsRule,
|
|
@@ -182,7 +245,7 @@ const decompose = (v3, { idFactory } = {}) => {
|
|
|
182
245
|
});
|
|
183
246
|
|
|
184
247
|
// --- Person + participant context ---------------------------------------
|
|
185
|
-
const participants = v3.participants || [];
|
|
248
|
+
const participants = assertDense(v3.participants || [], "participants");
|
|
186
249
|
const persons = [];
|
|
187
250
|
const personIndexById = new Map();
|
|
188
251
|
const participantContext = [];
|
|
@@ -225,7 +288,7 @@ const decompose = (v3, { idFactory } = {}) => {
|
|
|
225
288
|
assignIfPresent(
|
|
226
289
|
transaction,
|
|
227
290
|
"titlesToBeSold",
|
|
228
|
-
titles.map((t) => t.id)
|
|
291
|
+
titles.filter((t) => listedUnderTitles.has(t)).map((t) => t.id)
|
|
229
292
|
);
|
|
230
293
|
assignIfPresent(transaction, "participants", participantContext);
|
|
231
294
|
|
|
@@ -392,31 +455,41 @@ const recompose = ({
|
|
|
392
455
|
const propertyPack = omit(Property || {}, ["id"]);
|
|
393
456
|
|
|
394
457
|
// Split each Title back into the two source arrays it was merged from.
|
|
395
|
-
//
|
|
396
|
-
//
|
|
458
|
+
// Transaction.titlesToBeSold says which titles V3 listed under titlesToBeSold,
|
|
459
|
+
// so an entry is emitted for every one of them — including a title whose only
|
|
460
|
+
// key there was titleNumber, which is every transaction between the seller
|
|
461
|
+
// naming the title and the deeds arriving.
|
|
397
462
|
const titlesOwned = titlesRule.keys.values;
|
|
398
463
|
const ownershipsOwned = ownershipsRule.keys.values;
|
|
464
|
+
const schemaKeys = new Set([...titlesOwned, ...ownershipsOwned, "id"]);
|
|
399
465
|
|
|
400
466
|
const titlesToBeSold = [];
|
|
401
467
|
const ownershipsToBeTransferred = [];
|
|
402
468
|
|
|
403
|
-
// Transaction.titlesToBeSold is the authoritative order for Title entities.
|
|
404
469
|
const titleById = new Map(titles.map((t) => [t.id, t]));
|
|
405
|
-
const
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
const titleList =
|
|
470
|
+
const listedIds = transaction.titlesToBeSold || [];
|
|
471
|
+
const listed = listedIds.map((id) => titleById.get(id)).filter(Boolean);
|
|
472
|
+
// Ordered by the listed titles first, then any known only from ownership.
|
|
473
|
+
const titleList = [...listed, ...titles.filter((t) => !listedIds.includes(t.id))];
|
|
409
474
|
|
|
410
475
|
for (const title of titleList) {
|
|
411
|
-
const titlesPart = pick(title, titlesOwned);
|
|
412
476
|
const ownershipPart = pick(title, ownershipsOwned);
|
|
413
|
-
|
|
414
|
-
const hasTitlesOnly = Object.keys(omit(titlesPart, SHARED_TITLE_KEYS)).length > 0;
|
|
415
|
-
const hasOwnershipOnly =
|
|
477
|
+
const hasOwnership =
|
|
416
478
|
Object.keys(omit(ownershipPart, SHARED_TITLE_KEYS)).length > 0;
|
|
417
479
|
|
|
418
|
-
if (
|
|
419
|
-
|
|
480
|
+
if (listed.includes(title)) {
|
|
481
|
+
const titlesPart = pick(title, titlesOwned);
|
|
482
|
+
// Extension keys go back to the rule that took them (titlesToBeSold is
|
|
483
|
+
// the declared sink); see mapping.extensionPolicy.
|
|
484
|
+
if (titlesRule.extensions === "passthrough") {
|
|
485
|
+
for (const [key, value] of Object.entries(title)) {
|
|
486
|
+
if (!schemaKeys.has(key)) titlesPart[key] = value;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
titlesToBeSold.push(titlesPart);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
if (hasOwnership) ownershipsToBeTransferred.push(ownershipPart);
|
|
420
493
|
}
|
|
421
494
|
|
|
422
495
|
assignIfPresent(propertyPack, "titlesToBeSold", titlesToBeSold);
|