@pdtf/schemas 3.6.0-dev.30 → 3.6.0-dev.32
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 +57 -0
- package/index.js +2 -0
- package/package.json +1 -1
- package/src/schemas/v3/combined.json +6 -1
- package/src/schemas/v3/overlays/ta7ed5.json +9 -0
- package/src/schemas/v4/Gift.json +2 -2
- package/src/schemas/v4/Offer.json +2 -2
- 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/PropertyTitleLink.json +183 -0
- package/src/schemas/v4/Representation.json +2 -2
- package/src/schemas/v4/SellerCapacity.json +2 -2
- package/src/schemas/v4/Title.json +18 -3
- package/src/schemas/v4/Transaction.json +2 -2
- package/src/schemas/v4/TransactionRole.json +2 -2
- package/src/schemas/v4/mapping.json +35 -8
- package/src/utils/generateV4Schemas.js +206 -3
- package/src/utils/v4.js +118 -0
|
@@ -343,6 +343,63 @@ transaction already carries the titles to be sold. It is emitted for **every**
|
|
|
343
343
|
seller, whether or not a capacity has been declared, so the credential embodying
|
|
344
344
|
the Seller role is never missing while a form is still being filled in.
|
|
345
345
|
|
|
346
|
+
## Property–Title links
|
|
347
|
+
|
|
348
|
+
`PropertyTitleLink` is the one relationship credential that references no
|
|
349
|
+
Transaction (spec 01 D34). It says a UPRN is related to a title, as at a date,
|
|
350
|
+
on a stated basis:
|
|
351
|
+
|
|
352
|
+
```jsonc
|
|
353
|
+
{ "id": "urn:pdtf:propertyTitleLink:ptl-1",
|
|
354
|
+
"property": "urn:pdtf:uprn:100023456789",
|
|
355
|
+
"title": "urn:pdtf:titleNumber:AB10000",
|
|
356
|
+
"basis": { "type": "Register Dataset",
|
|
357
|
+
"dataset": "HMLR Title Number and UPRN Look Up",
|
|
358
|
+
"asOf": "2026-10-01" } }
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
The relationship is many-to-many. A flat's UPRN can sit on its lease and on the
|
|
362
|
+
block freehold, and the block freehold covers every flat in the block. The link
|
|
363
|
+
is standing, so it is issued once for the UPRN and title and reused by every
|
|
364
|
+
sale, not reissued for each one.
|
|
365
|
+
|
|
366
|
+
**HMLR asserts, the seller selects.** The links say which titles a UPRN sits
|
|
367
|
+
on. `Transaction.titlesToBeSold` says which of those are being conveyed. A
|
|
368
|
+
linked title the transaction does not select is related but not sold, so no
|
|
369
|
+
extra field is needed for it. `linkedTitles()` (exported as `v4LinkedTitles`)
|
|
370
|
+
computes this:
|
|
371
|
+
|
|
372
|
+
```js
|
|
373
|
+
const { v4LinkedTitles } = require("@pdtf/schemas");
|
|
374
|
+
|
|
375
|
+
v4LinkedTitles({ property, links, transaction, titles });
|
|
376
|
+
// { linked, sold, relatedNotSold, unlinkedSold, superseded, awaitingRegisterLink }
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
`unlinkedSold` lists titles being sold with no link behind them. That is how a
|
|
380
|
+
missing link shows up, and the conveyancer reports it to HMLR. A wrong link
|
|
381
|
+
needs no handling: the seller does not select it.
|
|
382
|
+
|
|
383
|
+
**One issuer per fact.** Registration status decides who issues a link.
|
|
384
|
+
HM Land Registry issues it for a registered title (`urn:pdtf:titleNumber:*`, on
|
|
385
|
+
`Register Dataset`). The seller's conveyancer issues it for unregistered land
|
|
386
|
+
(`urn:pdtf:unregisteredTitle:*`, on `Epitome of Title`), which HMLR holds
|
|
387
|
+
nothing about. The schema ties `basis` to the title's URN scheme. The trust
|
|
388
|
+
framework checks who the issuer is, because a credential subject cannot vouch
|
|
389
|
+
for its own signer.
|
|
390
|
+
|
|
391
|
+
**First registration.** The Title issued on first registration lists the
|
|
392
|
+
unregistered URN in `alsoKnownAs`. Links and selections that name the old URN
|
|
393
|
+
resolve to the new title number. Once HMLR issues a link from the same UPRN to
|
|
394
|
+
that number, it supersedes the conveyancer's link. Until then the conveyancer's
|
|
395
|
+
link stays in force and the title is listed in `awaitingRegisterLink`.
|
|
396
|
+
|
|
397
|
+
Neither direction of the round trip touches these links. V3 records no
|
|
398
|
+
UPRN-to-title link, so `decompose` does not invent one and `recompose` ignores
|
|
399
|
+
any it is given. The manifest lists the link as `kind: "credential"`,
|
|
400
|
+
`scope: "standing"`. The five transaction-scoped credentials are listed with
|
|
401
|
+
`scope: "transaction"`.
|
|
402
|
+
|
|
346
403
|
### If you want an identifier everyone can mint
|
|
347
404
|
|
|
348
405
|
Three patterns, of which V3 already uses two:
|
package/index.js
CHANGED
|
@@ -829,6 +829,7 @@ const {
|
|
|
829
829
|
identifyV3Pointer,
|
|
830
830
|
resolveV3Pointer,
|
|
831
831
|
resolveV3PointerAll,
|
|
832
|
+
linkedTitles: v4LinkedTitles,
|
|
832
833
|
} = require("./src/utils/v4");
|
|
833
834
|
|
|
834
835
|
module.exports = {
|
|
@@ -861,4 +862,5 @@ module.exports = {
|
|
|
861
862
|
identifyV3Pointer,
|
|
862
863
|
resolveV3Pointer,
|
|
863
864
|
resolveV3PointerAll,
|
|
865
|
+
v4LinkedTitles,
|
|
864
866
|
};
|
package/package.json
CHANGED
|
@@ -6770,10 +6770,15 @@
|
|
|
6770
6770
|
"Not applicable"
|
|
6771
6771
|
],
|
|
6772
6772
|
"ta7ed5Enum": [
|
|
6773
|
+
"Managing Agent",
|
|
6773
6774
|
"Landlord",
|
|
6774
6775
|
"Management Company",
|
|
6776
|
+
"Not applicable",
|
|
6775
6777
|
"Other"
|
|
6776
|
-
]
|
|
6778
|
+
],
|
|
6779
|
+
"ta7ed5EnumNames": {
|
|
6780
|
+
"Managing Agent": "Yes"
|
|
6781
|
+
}
|
|
6777
6782
|
}
|
|
6778
6783
|
},
|
|
6779
6784
|
"discriminator": {
|
|
@@ -380,8 +380,17 @@
|
|
|
380
380
|
"ta7ed5Ref": "4.3.2",
|
|
381
381
|
"title": "Who collects the ground rent?",
|
|
382
382
|
"enum": [
|
|
383
|
+
"Managing Agent",
|
|
383
384
|
"Landlord",
|
|
384
385
|
"Management Company",
|
|
386
|
+
"Not applicable",
|
|
387
|
+
"Other"
|
|
388
|
+
],
|
|
389
|
+
"enumNames": [
|
|
390
|
+
"Yes",
|
|
391
|
+
"Landlord",
|
|
392
|
+
"Management Company",
|
|
393
|
+
"Not applicable",
|
|
385
394
|
"Other"
|
|
386
395
|
]
|
|
387
396
|
}
|
package/src/schemas/v4/Gift.json
CHANGED
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
"description": "A gift of funds towards a purchase, made by a giftor. Implies the Giftor role.",
|
|
7
7
|
"x-pdtf-source": {
|
|
8
8
|
"packageName": "@pdtf/schemas",
|
|
9
|
-
"packageVersion": "3.6.0-dev.
|
|
9
|
+
"packageVersion": "3.6.0-dev.32",
|
|
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": "83f31fddf82e57e67f2bc92844459095041b254b970d8881dd4318248c188f67"
|
|
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.32",
|
|
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": "83f31fddf82e57e67f2bc92844459095041b254b970d8881dd4318248c188f67"
|
|
13
13
|
},
|
|
14
14
|
"properties": {
|
|
15
15
|
"id": {
|
|
@@ -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.32",
|
|
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": "83f31fddf82e57e67f2bc92844459095041b254b970d8881dd4318248c188f67"
|
|
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.32",
|
|
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": "83f31fddf82e57e67f2bc92844459095041b254b970d8881dd4318248c188f67"
|
|
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.32",
|
|
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": "83f31fddf82e57e67f2bc92844459095041b254b970d8881dd4318248c188f67"
|
|
13
13
|
},
|
|
14
14
|
"properties": {
|
|
15
15
|
"address": {
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://trust.propdata.org.uk/schemas/v4/entities/propertyTitleLink.json",
|
|
4
|
+
"type": "object",
|
|
5
|
+
"title": "Property–Title Link Entity",
|
|
6
|
+
"description": "A standing assertion that a property (UPRN) is related to a title. Many-to-many: one UPRN can sit on several titles, and one title (a block freehold, say) can cover many UPRNs. Issued by HM Land Registry for a registered title, and by the seller's conveyancer, on the epitome of title, for unregistered land. References no Transaction: a Transaction selects the titles being sold from the linked titles through titlesToBeSold, and a linked title it does not select is related but not sold.",
|
|
7
|
+
"x-pdtf-source": {
|
|
8
|
+
"packageName": "@pdtf/schemas",
|
|
9
|
+
"packageVersion": "3.6.0-dev.32",
|
|
10
|
+
"v3SchemaId": "https://trust.propdata.org.uk/schemas/v3/pdtf-transaction.json",
|
|
11
|
+
"combinedPath": "src/schemas/v3/combined.json",
|
|
12
|
+
"combinedSha256": "83f31fddf82e57e67f2bc92844459095041b254b970d8881dd4318248c188f67"
|
|
13
|
+
},
|
|
14
|
+
"properties": {
|
|
15
|
+
"id": {
|
|
16
|
+
"title": "Property–Title Link URN",
|
|
17
|
+
"description": "Uniform Resource Name identifier for the link, urn:pdtf:propertyTitleLink:{id}",
|
|
18
|
+
"type": "string",
|
|
19
|
+
"pattern": "^urn:[a-z0-9][a-z0-9-]{0,31}:[a-zA-Z0-9._~:/?#\\[\\]@!$&'()*+,;=-]+$"
|
|
20
|
+
},
|
|
21
|
+
"property": {
|
|
22
|
+
"title": "Property Reference",
|
|
23
|
+
"description": "URN referencing the Property entity, urn:pdtf:uprn:{uprn}",
|
|
24
|
+
"type": "string",
|
|
25
|
+
"pattern": "^urn:[a-z0-9][a-z0-9-]{0,31}:[a-zA-Z0-9._~:/?#\\[\\]@!$&'()*+,;=-]+$"
|
|
26
|
+
},
|
|
27
|
+
"title": {
|
|
28
|
+
"title": "Title Reference",
|
|
29
|
+
"description": "URN referencing the Title entity: urn:pdtf:titleNumber:{number} for a registered title, urn:pdtf:unregisteredTitle:{id} for unregistered land",
|
|
30
|
+
"type": "string",
|
|
31
|
+
"pattern": "^urn:[a-z0-9][a-z0-9-]{0,31}:[a-zA-Z0-9._~:/?#\\[\\]@!$&'()*+,;=-]+$"
|
|
32
|
+
},
|
|
33
|
+
"basis": {
|
|
34
|
+
"title": "Basis of the link",
|
|
35
|
+
"description": "What the link rests on, and the date it speaks as at. A registered title is linked on the register's own UPRN-to-title data; unregistered land on the epitome of title.",
|
|
36
|
+
"type": "object",
|
|
37
|
+
"properties": {
|
|
38
|
+
"type": {
|
|
39
|
+
"title": "Basis type",
|
|
40
|
+
"type": "string",
|
|
41
|
+
"enum": [
|
|
42
|
+
"Register Dataset",
|
|
43
|
+
"Epitome of Title"
|
|
44
|
+
]
|
|
45
|
+
},
|
|
46
|
+
"asOf": {
|
|
47
|
+
"title": "As at",
|
|
48
|
+
"description": "The date the link speaks as at: the dataset's effective date, or the date the epitome was examined",
|
|
49
|
+
"type": "string",
|
|
50
|
+
"format": "date"
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
"required": [
|
|
54
|
+
"type",
|
|
55
|
+
"asOf"
|
|
56
|
+
],
|
|
57
|
+
"discriminator": {
|
|
58
|
+
"propertyName": "type"
|
|
59
|
+
},
|
|
60
|
+
"oneOf": [
|
|
61
|
+
{
|
|
62
|
+
"properties": {
|
|
63
|
+
"type": {
|
|
64
|
+
"type": "string",
|
|
65
|
+
"enum": [
|
|
66
|
+
"Register Dataset"
|
|
67
|
+
]
|
|
68
|
+
},
|
|
69
|
+
"dataset": {
|
|
70
|
+
"title": "Dataset",
|
|
71
|
+
"description": "The register dataset the link is taken from, e.g. \"HMLR Title Number and UPRN Look Up\"",
|
|
72
|
+
"type": "string"
|
|
73
|
+
},
|
|
74
|
+
"datasetRelease": {
|
|
75
|
+
"title": "Dataset release",
|
|
76
|
+
"description": "The release the link is taken from, e.g. the full-file name of HMLR's monthly release (\"LR_UPRN_FULL_APR.csv\"). The dataset records no date per row, so asOf is the release date.",
|
|
77
|
+
"type": "string"
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
"required": [
|
|
81
|
+
"dataset"
|
|
82
|
+
]
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"properties": {
|
|
86
|
+
"type": {
|
|
87
|
+
"type": "string",
|
|
88
|
+
"enum": [
|
|
89
|
+
"Epitome of Title"
|
|
90
|
+
]
|
|
91
|
+
},
|
|
92
|
+
"rootOfTitle": {
|
|
93
|
+
"title": "Root of title",
|
|
94
|
+
"description": "The good root of title the epitome starts from",
|
|
95
|
+
"type": "object",
|
|
96
|
+
"properties": {
|
|
97
|
+
"description": {
|
|
98
|
+
"title": "Description",
|
|
99
|
+
"description": "e.g. \"Conveyance on sale\"",
|
|
100
|
+
"type": "string"
|
|
101
|
+
},
|
|
102
|
+
"date": {
|
|
103
|
+
"title": "Date",
|
|
104
|
+
"description": "Date of the root document",
|
|
105
|
+
"type": "string",
|
|
106
|
+
"format": "date"
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
"epitomeDigest": {
|
|
111
|
+
"title": "Epitome digest",
|
|
112
|
+
"description": "SHA-256 digest of the epitome of title as examined, in the form sha256:{hex} (spec 02 §6.6)",
|
|
113
|
+
"type": "string",
|
|
114
|
+
"pattern": "^sha256:[0-9a-f]{64}$"
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
"required": [
|
|
118
|
+
"rootOfTitle"
|
|
119
|
+
]
|
|
120
|
+
}
|
|
121
|
+
]
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
"required": [
|
|
125
|
+
"property",
|
|
126
|
+
"title",
|
|
127
|
+
"basis"
|
|
128
|
+
],
|
|
129
|
+
"allOf": [
|
|
130
|
+
{
|
|
131
|
+
"if": {
|
|
132
|
+
"properties": {
|
|
133
|
+
"title": {
|
|
134
|
+
"type": "string",
|
|
135
|
+
"pattern": "^urn:pdtf:titleNumber:"
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
"required": [
|
|
139
|
+
"title"
|
|
140
|
+
]
|
|
141
|
+
},
|
|
142
|
+
"then": {
|
|
143
|
+
"properties": {
|
|
144
|
+
"basis": {
|
|
145
|
+
"type": "object",
|
|
146
|
+
"properties": {
|
|
147
|
+
"type": {
|
|
148
|
+
"type": "string",
|
|
149
|
+
"const": "Register Dataset"
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
"if": {
|
|
158
|
+
"properties": {
|
|
159
|
+
"title": {
|
|
160
|
+
"type": "string",
|
|
161
|
+
"pattern": "^urn:pdtf:unregisteredTitle:"
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
"required": [
|
|
165
|
+
"title"
|
|
166
|
+
]
|
|
167
|
+
},
|
|
168
|
+
"then": {
|
|
169
|
+
"properties": {
|
|
170
|
+
"basis": {
|
|
171
|
+
"type": "object",
|
|
172
|
+
"properties": {
|
|
173
|
+
"type": {
|
|
174
|
+
"type": "string",
|
|
175
|
+
"const": "Epitome of Title"
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
]
|
|
183
|
+
}
|
|
@@ -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.32",
|
|
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": "83f31fddf82e57e67f2bc92844459095041b254b970d8881dd4318248c188f67"
|
|
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.32",
|
|
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": "83f31fddf82e57e67f2bc92844459095041b254b970d8881dd4318248c188f67"
|
|
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.32",
|
|
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": "83f31fddf82e57e67f2bc92844459095041b254b970d8881dd4318248c188f67"
|
|
13
13
|
},
|
|
14
14
|
"properties": {
|
|
15
15
|
"titleNumber": {
|
|
@@ -11184,10 +11184,15 @@
|
|
|
11184
11184
|
"Not applicable"
|
|
11185
11185
|
],
|
|
11186
11186
|
"ta7ed5Enum": [
|
|
11187
|
+
"Managing Agent",
|
|
11187
11188
|
"Landlord",
|
|
11188
11189
|
"Management Company",
|
|
11190
|
+
"Not applicable",
|
|
11189
11191
|
"Other"
|
|
11190
|
-
]
|
|
11192
|
+
],
|
|
11193
|
+
"ta7ed5EnumNames": {
|
|
11194
|
+
"Managing Agent": "Yes"
|
|
11195
|
+
}
|
|
11191
11196
|
}
|
|
11192
11197
|
},
|
|
11193
11198
|
"discriminator": {
|
|
@@ -14655,6 +14660,16 @@
|
|
|
14655
14660
|
"description": "Uniform Resource Name identifier for the title",
|
|
14656
14661
|
"type": "string",
|
|
14657
14662
|
"pattern": "^urn:[a-z0-9][a-z0-9-]{0,31}:[a-zA-Z0-9._~:/?#\\[\\]@!$&'()*+,;=-]+$"
|
|
14663
|
+
},
|
|
14664
|
+
"alsoKnownAs": {
|
|
14665
|
+
"title": "Also Known As",
|
|
14666
|
+
"description": "Earlier identifiers for this title — on first registration, the urn:pdtf:unregisteredTitle:{id} it was known by before a title number was issued.",
|
|
14667
|
+
"type": "array",
|
|
14668
|
+
"items": {
|
|
14669
|
+
"type": "string",
|
|
14670
|
+
"pattern": "^urn:[a-z0-9][a-z0-9-]{0,31}:[a-zA-Z0-9._~:/?#\\[\\]@!$&'()*+,;=-]+$"
|
|
14671
|
+
},
|
|
14672
|
+
"uniqueItems": true
|
|
14658
14673
|
}
|
|
14659
14674
|
}
|
|
14660
14675
|
}
|
|
@@ -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.32",
|
|
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": "83f31fddf82e57e67f2bc92844459095041b254b970d8881dd4318248c188f67"
|
|
13
13
|
},
|
|
14
14
|
"properties": {
|
|
15
15
|
"transactionId": {
|
|
@@ -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.32",
|
|
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": "83f31fddf82e57e67f2bc92844459095041b254b970d8881dd4318248c188f67"
|
|
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.32",
|
|
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": "83f31fddf82e57e67f2bc92844459095041b254b970d8881dd4318248c188f67"
|
|
12
12
|
},
|
|
13
13
|
"entities": {
|
|
14
14
|
"Property": {
|
|
@@ -39,27 +39,38 @@
|
|
|
39
39
|
"Representation": {
|
|
40
40
|
"$id": "https://trust.propdata.org.uk/schemas/v4/entities/representation.json",
|
|
41
41
|
"kind": "credential",
|
|
42
|
-
"
|
|
42
|
+
"scope": "transaction",
|
|
43
|
+
"idScheme": "urn"
|
|
43
44
|
},
|
|
44
45
|
"TransactionRole": {
|
|
45
46
|
"$id": "https://trust.propdata.org.uk/schemas/v4/entities/transactionRole.json",
|
|
46
47
|
"kind": "credential",
|
|
47
|
-
"
|
|
48
|
+
"scope": "transaction",
|
|
49
|
+
"idScheme": "urn"
|
|
48
50
|
},
|
|
49
51
|
"Gift": {
|
|
50
52
|
"$id": "https://trust.propdata.org.uk/schemas/v4/entities/gift.json",
|
|
51
53
|
"kind": "credential",
|
|
52
|
-
"
|
|
54
|
+
"scope": "transaction",
|
|
55
|
+
"idScheme": "urn"
|
|
53
56
|
},
|
|
54
57
|
"SellerCapacity": {
|
|
55
58
|
"$id": "https://trust.propdata.org.uk/schemas/v4/entities/sellerCapacity.json",
|
|
56
59
|
"kind": "credential",
|
|
57
|
-
"
|
|
60
|
+
"scope": "transaction",
|
|
61
|
+
"idScheme": "urn"
|
|
58
62
|
},
|
|
59
63
|
"Offer": {
|
|
60
64
|
"$id": "https://trust.propdata.org.uk/schemas/v4/entities/offer.json",
|
|
61
65
|
"kind": "credential",
|
|
62
|
-
"
|
|
66
|
+
"scope": "transaction",
|
|
67
|
+
"idScheme": "urn"
|
|
68
|
+
},
|
|
69
|
+
"PropertyTitleLink": {
|
|
70
|
+
"$id": "https://trust.propdata.org.uk/schemas/v4/entities/propertyTitleLink.json",
|
|
71
|
+
"kind": "credential",
|
|
72
|
+
"scope": "standing",
|
|
73
|
+
"idScheme": "urn"
|
|
63
74
|
}
|
|
64
75
|
},
|
|
65
76
|
"roundTrip": {
|
|
@@ -74,7 +85,7 @@
|
|
|
74
85
|
"Gift",
|
|
75
86
|
"TransactionRole"
|
|
76
87
|
],
|
|
77
|
-
"note": "All of these are required to reconstruct a V3 instance. The credentials are not redundant copies: role and every relationship live only on them, so that revoking a credential removes what it asserts rather than leaving a second copy on the transaction. Organisation
|
|
88
|
+
"note": "All of these are required to reconstruct a V3 instance. The credentials are not redundant copies: role and every relationship live only on them, so that revoking a credential removes what it asserts rather than leaving a second copy on the transaction. Organisation and PropertyTitleLink are outside the round trip: V3 records a participant's firm only as free text, and records no UPRN-to-title link at all, so decompose does not invent either and recompose does not need them.",
|
|
78
89
|
"roleImpliedByCredential": {
|
|
79
90
|
"SellerCapacity": "Seller",
|
|
80
91
|
"Offer": "Buyer",
|
|
@@ -874,6 +885,22 @@
|
|
|
874
885
|
"notes": "An offer in Transaction.offers that no participant references stays transaction data and yields no credential, since Offer requires a buyer."
|
|
875
886
|
}
|
|
876
887
|
},
|
|
888
|
+
"standingCredentials": {
|
|
889
|
+
"PropertyTitleLink": {
|
|
890
|
+
"description": "A standing UPRN-to-title link. Not projected from V3. A Transaction's titlesToBeSold selects the titles being sold from the titles linked to its property; see linkedTitles() in src/utils/v4.js.",
|
|
891
|
+
"issuerByTitleScheme": {
|
|
892
|
+
"urn:pdtf:titleNumber:": {
|
|
893
|
+
"issuer": "HM Land Registry",
|
|
894
|
+
"basis": "Register Dataset"
|
|
895
|
+
},
|
|
896
|
+
"urn:pdtf:unregisteredTitle:": {
|
|
897
|
+
"issuer": "Seller's conveyancer",
|
|
898
|
+
"basis": "Epitome of Title"
|
|
899
|
+
}
|
|
900
|
+
},
|
|
901
|
+
"supersession": "On first registration the Title for the new title number lists the unregistered URN in alsoKnownAs. An HMLR link from the same UPRN to that title supersedes the conveyancer's link to the unregistered URN; the conveyancer's link stays in force until one exists."
|
|
902
|
+
}
|
|
903
|
+
},
|
|
877
904
|
"collisions": [
|
|
878
905
|
{
|
|
879
906
|
"key": "titleNumber",
|
|
@@ -20,6 +20,9 @@
|
|
|
20
20
|
* SellerCapacity — legal capacity of a seller against a title (derived from
|
|
21
21
|
* the V3 participants "Seller" oneOf branch)
|
|
22
22
|
* Offer — buyer offer against a transaction
|
|
23
|
+
* PropertyTitleLink — standing UPRN↔title relationship (HMLR for registered
|
|
24
|
+
* titles, the seller's conveyancer for unregistered land);
|
|
25
|
+
* the one credential that references no Transaction
|
|
23
26
|
*
|
|
24
27
|
* The run also emits src/schemas/v4/mapping.json — the machine-readable V3⇄V4
|
|
25
28
|
* pointer mapping consumed by src/utils/v4.js. See
|
|
@@ -203,6 +206,26 @@ const CREDENTIAL_ENTITIES = [
|
|
|
203
206
|
* role as their own discriminator: "what kind of representation is this" is a
|
|
204
207
|
* property of the representation, not a duplicated participant attribute.
|
|
205
208
|
*/
|
|
209
|
+
/**
|
|
210
|
+
* Standing credentials: assertions that are true independently of any sale, so
|
|
211
|
+
* they reference no Transaction (D32 as narrowed by D34 in spec 01).
|
|
212
|
+
*
|
|
213
|
+
* PropertyTitleLink says "this UPRN is related to this title". For a registered
|
|
214
|
+
* title HM Land Registry is the only issuer; for unregistered land, which HMLR
|
|
215
|
+
* holds nothing about, the seller's conveyancer issues it on the strength of the
|
|
216
|
+
* epitome of title. The two cases never overlap, so there is one issuer per
|
|
217
|
+
* fact. The seller *selects* from these links via Transaction.titlesToBeSold; a
|
|
218
|
+
* linked title that is not selected is related but not sold.
|
|
219
|
+
*
|
|
220
|
+
* V3 records no such link — it has one propertyPack and the titles being sold,
|
|
221
|
+
* nothing about which titles HMLR relates to the UPRN — so these are outside the
|
|
222
|
+
* round trip: decompose does not invent them and recompose does not need them.
|
|
223
|
+
*/
|
|
224
|
+
const STANDING_CREDENTIAL_ENTITIES = ["PropertyTitleLink"];
|
|
225
|
+
|
|
226
|
+
/** Entities whose identifier is a DID; every other entity is a URN. */
|
|
227
|
+
const DID_ENTITIES = ["Transaction", "Person", "Organisation"];
|
|
228
|
+
|
|
206
229
|
const ROLE_IMPLIED_BY_CREDENTIAL = {
|
|
207
230
|
SellerCapacity: "Seller",
|
|
208
231
|
Offer: "Buyer",
|
|
@@ -359,6 +382,19 @@ const generateTitle = () => {
|
|
|
359
382
|
"Uniform Resource Name identifier for the title"
|
|
360
383
|
);
|
|
361
384
|
|
|
385
|
+
// First registration (spec 01 §9.1 Q5): the Title issued on registration is
|
|
386
|
+
// keyed by its new title number and lists the unregistered identifier it
|
|
387
|
+
// replaces, so links and transactions that name the old URN still resolve.
|
|
388
|
+
// Not in V3, so never emitted by decompose.
|
|
389
|
+
titleItemProps.alsoKnownAs = {
|
|
390
|
+
title: "Also Known As",
|
|
391
|
+
description:
|
|
392
|
+
"Earlier identifiers for this title — on first registration, the urn:pdtf:unregisteredTitle:{id} it was known by before a title number was issued.",
|
|
393
|
+
type: "array",
|
|
394
|
+
items: { type: "string", pattern: URN_PATTERN },
|
|
395
|
+
uniqueItems: true,
|
|
396
|
+
};
|
|
397
|
+
|
|
362
398
|
const required = [
|
|
363
399
|
...new Set([
|
|
364
400
|
...(pp.titlesToBeSold.items.required || []),
|
|
@@ -839,6 +875,149 @@ const generateOffer = () => {
|
|
|
839
875
|
);
|
|
840
876
|
};
|
|
841
877
|
|
|
878
|
+
const generatePropertyTitleLink = () => {
|
|
879
|
+
// Hand-authored: V3 has nothing to derive it from. The first relationship
|
|
880
|
+
// credential with no `transaction` — it is standing, true of the UPRN and
|
|
881
|
+
// the title whether or not either is being sold (spec 01 D34).
|
|
882
|
+
//
|
|
883
|
+
// `basis` is part of what is asserted, not just how the issuer came by it:
|
|
884
|
+
// HMLR's UPRN-to-title data is neither complete nor wholly accurate, so a
|
|
885
|
+
// link is only ever "related as at this date, according to this source".
|
|
886
|
+
// How the issuer obtained it goes in the credential's own `evidence`
|
|
887
|
+
// (spec 02 §6) as usual.
|
|
888
|
+
const REGISTER = "Register Dataset";
|
|
889
|
+
const EPITOME = "Epitome of Title";
|
|
890
|
+
const dateField = (title, description) => ({
|
|
891
|
+
title,
|
|
892
|
+
description,
|
|
893
|
+
type: "string",
|
|
894
|
+
format: "date",
|
|
895
|
+
});
|
|
896
|
+
|
|
897
|
+
const schema = makeSchema(
|
|
898
|
+
"propertyTitleLink",
|
|
899
|
+
"Property–Title Link Entity",
|
|
900
|
+
"A standing assertion that a property (UPRN) is related to a title. Many-to-many: one UPRN can sit on several titles, and one title (a block freehold, say) can cover many UPRNs. Issued by HM Land Registry for a registered title, and by the seller's conveyancer, on the epitome of title, for unregistered land. References no Transaction: a Transaction selects the titles being sold from the linked titles through titlesToBeSold, and a linked title it does not select is related but not sold.",
|
|
901
|
+
{
|
|
902
|
+
id: urnRef(
|
|
903
|
+
"Property–Title Link URN",
|
|
904
|
+
"Uniform Resource Name identifier for the link, urn:pdtf:propertyTitleLink:{id}"
|
|
905
|
+
),
|
|
906
|
+
property: urnRef(
|
|
907
|
+
"Property Reference",
|
|
908
|
+
"URN referencing the Property entity, urn:pdtf:uprn:{uprn}"
|
|
909
|
+
),
|
|
910
|
+
title: urnRef(
|
|
911
|
+
"Title Reference",
|
|
912
|
+
"URN referencing the Title entity: urn:pdtf:titleNumber:{number} for a registered title, urn:pdtf:unregisteredTitle:{id} for unregistered land"
|
|
913
|
+
),
|
|
914
|
+
basis: {
|
|
915
|
+
title: "Basis of the link",
|
|
916
|
+
description:
|
|
917
|
+
"What the link rests on, and the date it speaks as at. A registered title is linked on the register's own UPRN-to-title data; unregistered land on the epitome of title.",
|
|
918
|
+
type: "object",
|
|
919
|
+
properties: {
|
|
920
|
+
type: {
|
|
921
|
+
title: "Basis type",
|
|
922
|
+
type: "string",
|
|
923
|
+
enum: [REGISTER, EPITOME],
|
|
924
|
+
},
|
|
925
|
+
asOf: dateField(
|
|
926
|
+
"As at",
|
|
927
|
+
"The date the link speaks as at: the dataset's effective date, or the date the epitome was examined"
|
|
928
|
+
),
|
|
929
|
+
},
|
|
930
|
+
required: ["type", "asOf"],
|
|
931
|
+
discriminator: { propertyName: "type" },
|
|
932
|
+
oneOf: [
|
|
933
|
+
{
|
|
934
|
+
properties: {
|
|
935
|
+
type: { type: "string", enum: [REGISTER] },
|
|
936
|
+
dataset: {
|
|
937
|
+
title: "Dataset",
|
|
938
|
+
description:
|
|
939
|
+
"The register dataset the link is taken from, e.g. \"HMLR Title Number and UPRN Look Up\"",
|
|
940
|
+
type: "string",
|
|
941
|
+
},
|
|
942
|
+
datasetRelease: {
|
|
943
|
+
title: "Dataset release",
|
|
944
|
+
description:
|
|
945
|
+
"The release the link is taken from, e.g. the full-file name of HMLR's monthly release (\"LR_UPRN_FULL_APR.csv\"). The dataset records no date per row, so asOf is the release date.",
|
|
946
|
+
type: "string",
|
|
947
|
+
},
|
|
948
|
+
},
|
|
949
|
+
required: ["dataset"],
|
|
950
|
+
},
|
|
951
|
+
{
|
|
952
|
+
properties: {
|
|
953
|
+
type: { type: "string", enum: [EPITOME] },
|
|
954
|
+
rootOfTitle: {
|
|
955
|
+
title: "Root of title",
|
|
956
|
+
description:
|
|
957
|
+
"The good root of title the epitome starts from",
|
|
958
|
+
type: "object",
|
|
959
|
+
properties: {
|
|
960
|
+
description: {
|
|
961
|
+
title: "Description",
|
|
962
|
+
description: "e.g. \"Conveyance on sale\"",
|
|
963
|
+
type: "string",
|
|
964
|
+
},
|
|
965
|
+
date: dateField("Date", "Date of the root document"),
|
|
966
|
+
},
|
|
967
|
+
},
|
|
968
|
+
epitomeDigest: {
|
|
969
|
+
title: "Epitome digest",
|
|
970
|
+
description:
|
|
971
|
+
"SHA-256 digest of the epitome of title as examined, in the form sha256:{hex} (spec 02 §6.6)",
|
|
972
|
+
type: "string",
|
|
973
|
+
pattern: "^sha256:[0-9a-f]{64}$",
|
|
974
|
+
},
|
|
975
|
+
},
|
|
976
|
+
required: ["rootOfTitle"],
|
|
977
|
+
},
|
|
978
|
+
],
|
|
979
|
+
},
|
|
980
|
+
},
|
|
981
|
+
["property", "title", "basis"]
|
|
982
|
+
);
|
|
983
|
+
|
|
984
|
+
// One issuer per fact, decided by registration status: a registered title is
|
|
985
|
+
// linked on the register, unregistered land on the epitome. The schema
|
|
986
|
+
// enforces the basis; the issuer (HMLR, or a conveyancer) is checked through
|
|
987
|
+
// the trust framework, since a credential subject cannot name its own signer.
|
|
988
|
+
schema.allOf = [
|
|
989
|
+
{
|
|
990
|
+
if: {
|
|
991
|
+
properties: { title: { type: "string", pattern: "^urn:pdtf:titleNumber:" } },
|
|
992
|
+
required: ["title"],
|
|
993
|
+
},
|
|
994
|
+
then: {
|
|
995
|
+
properties: {
|
|
996
|
+
basis: {
|
|
997
|
+
type: "object",
|
|
998
|
+
properties: { type: { type: "string", const: REGISTER } },
|
|
999
|
+
},
|
|
1000
|
+
},
|
|
1001
|
+
},
|
|
1002
|
+
},
|
|
1003
|
+
{
|
|
1004
|
+
if: {
|
|
1005
|
+
properties: { title: { type: "string", pattern: "^urn:pdtf:unregisteredTitle:" } },
|
|
1006
|
+
required: ["title"],
|
|
1007
|
+
},
|
|
1008
|
+
then: {
|
|
1009
|
+
properties: {
|
|
1010
|
+
basis: {
|
|
1011
|
+
type: "object",
|
|
1012
|
+
properties: { type: { type: "string", const: EPITOME } },
|
|
1013
|
+
},
|
|
1014
|
+
},
|
|
1015
|
+
},
|
|
1016
|
+
},
|
|
1017
|
+
];
|
|
1018
|
+
return schema;
|
|
1019
|
+
};
|
|
1020
|
+
|
|
842
1021
|
// ---------------------------------------------------------------------------
|
|
843
1022
|
// Main — generate all schemas and write to src/schemas/v4/
|
|
844
1023
|
// ---------------------------------------------------------------------------
|
|
@@ -853,6 +1032,7 @@ const generators = {
|
|
|
853
1032
|
Gift: generateGift,
|
|
854
1033
|
SellerCapacity: generateSellerCapacity,
|
|
855
1034
|
Offer: generateOffer,
|
|
1035
|
+
PropertyTitleLink: generatePropertyTitleLink,
|
|
856
1036
|
};
|
|
857
1037
|
|
|
858
1038
|
if (!fs.existsSync(V4_DIR)) {
|
|
@@ -1226,17 +1406,22 @@ const mapping = {
|
|
|
1226
1406
|
$id: schema.$id,
|
|
1227
1407
|
kind: DERIVED_ENTITIES.includes(name)
|
|
1228
1408
|
? "derived"
|
|
1229
|
-
: CREDENTIAL_ENTITIES.includes(name)
|
|
1409
|
+
: CREDENTIAL_ENTITIES.includes(name) ||
|
|
1410
|
+
STANDING_CREDENTIAL_ENTITIES.includes(name)
|
|
1230
1411
|
? "credential"
|
|
1231
1412
|
: "standalone",
|
|
1232
|
-
|
|
1413
|
+
// Relationship credentials are either scoped to one Transaction (and
|
|
1414
|
+
// carry `transaction`) or standing (and carry none) — D32 / D34.
|
|
1415
|
+
...(CREDENTIAL_ENTITIES.includes(name) && { scope: "transaction" }),
|
|
1416
|
+
...(STANDING_CREDENTIAL_ENTITIES.includes(name) && { scope: "standing" }),
|
|
1417
|
+
idScheme: DID_ENTITIES.includes(name) ? "did" : "urn",
|
|
1233
1418
|
},
|
|
1234
1419
|
])
|
|
1235
1420
|
),
|
|
1236
1421
|
roundTrip: {
|
|
1237
1422
|
entities: [...DERIVED_ENTITIES, ...CREDENTIAL_ENTITIES],
|
|
1238
1423
|
note:
|
|
1239
|
-
"All of these are required to reconstruct a V3 instance. The credentials are not redundant copies: role and every relationship live only on them, so that revoking a credential removes what it asserts rather than leaving a second copy on the transaction. Organisation
|
|
1424
|
+
"All of these are required to reconstruct a V3 instance. The credentials are not redundant copies: role and every relationship live only on them, so that revoking a credential removes what it asserts rather than leaving a second copy on the transaction. Organisation and PropertyTitleLink are outside the round trip: V3 records a participant's firm only as free text, and records no UPRN-to-title link at all, so decompose does not invent either and recompose does not need them.",
|
|
1240
1425
|
roleImpliedByCredential: ROLE_IMPLIED_BY_CREDENTIAL,
|
|
1241
1426
|
strippedKeySuffixes: ["Ref", "Required"],
|
|
1242
1427
|
knownExceptions: [
|
|
@@ -1327,6 +1512,24 @@ const mapping = {
|
|
|
1327
1512
|
"An offer in Transaction.offers that no participant references stays transaction data and yields no credential, since Offer requires a buyer.",
|
|
1328
1513
|
},
|
|
1329
1514
|
},
|
|
1515
|
+
standingCredentials: {
|
|
1516
|
+
PropertyTitleLink: {
|
|
1517
|
+
description:
|
|
1518
|
+
"A standing UPRN-to-title link. Not projected from V3. A Transaction's titlesToBeSold selects the titles being sold from the titles linked to its property; see linkedTitles() in src/utils/v4.js.",
|
|
1519
|
+
issuerByTitleScheme: {
|
|
1520
|
+
"urn:pdtf:titleNumber:": {
|
|
1521
|
+
issuer: "HM Land Registry",
|
|
1522
|
+
basis: "Register Dataset",
|
|
1523
|
+
},
|
|
1524
|
+
"urn:pdtf:unregisteredTitle:": {
|
|
1525
|
+
issuer: "Seller's conveyancer",
|
|
1526
|
+
basis: "Epitome of Title",
|
|
1527
|
+
},
|
|
1528
|
+
},
|
|
1529
|
+
supersession:
|
|
1530
|
+
"On first registration the Title for the new title number lists the unregistered URN in alsoKnownAs. An HMLR link from the same UPRN to that title supersedes the conveyancer's link to the unregistered URN; the conveyancer's link stays in force until one exists.",
|
|
1531
|
+
},
|
|
1532
|
+
},
|
|
1330
1533
|
collisions: titleCollisionKeys.map((key) => ({
|
|
1331
1534
|
key,
|
|
1332
1535
|
entity: "Title",
|
package/src/utils/v4.js
CHANGED
|
@@ -826,8 +826,126 @@ const resolveV3PointerAll = (pointer) => {
|
|
|
826
826
|
|
|
827
827
|
const resolveV3Pointer = (pointer) => resolveV3PointerAll(pointer)[0] || null;
|
|
828
828
|
|
|
829
|
+
// ---------------------------------------------------------------------------
|
|
830
|
+
// Standing Property–Title links (spec 01 D34)
|
|
831
|
+
// ---------------------------------------------------------------------------
|
|
832
|
+
const REGISTERED_TITLE = "urn:pdtf:titleNumber:";
|
|
833
|
+
const UNREGISTERED_TITLE = "urn:pdtf:unregisteredTitle:";
|
|
834
|
+
const REGISTER_BASIS = "Register Dataset";
|
|
835
|
+
const EPITOME_BASIS = "Epitome of Title";
|
|
836
|
+
|
|
837
|
+
/**
|
|
838
|
+
* Every title related to a property, and which of them a transaction sells.
|
|
839
|
+
*
|
|
840
|
+
* HMLR asserts, the seller selects: PropertyTitleLink credentials say which
|
|
841
|
+
* titles a UPRN sits on, and the Transaction's titlesToBeSold says which of
|
|
842
|
+
* those are being conveyed. Nothing else is needed to show a related title that
|
|
843
|
+
* is not being sold — a superior freehold in a leasehold flat sale, say — since
|
|
844
|
+
* it is simply a linked title the transaction does not select.
|
|
845
|
+
*
|
|
846
|
+
* Pass only links that have already been verified: signature, issuer and
|
|
847
|
+
* status (spec 14). This works on credential subjects and decides nothing about
|
|
848
|
+
* trust beyond the rule the schema also enforces, that a registered title is
|
|
849
|
+
* linked on the register and an unregistered one on the epitome.
|
|
850
|
+
*
|
|
851
|
+
* First registration: a Title whose alsoKnownAs lists an unregistered URN is
|
|
852
|
+
* the same title under its new number. Links and selections naming the old URN
|
|
853
|
+
* resolve to it, and an HMLR link from the same UPRN to the new number
|
|
854
|
+
* supersedes the conveyancer's link to the old one. Until HMLR's link exists the
|
|
855
|
+
* conveyancer's still stands, and the title is reported in `awaitingRegisterLink`.
|
|
856
|
+
*
|
|
857
|
+
* @param {object} args
|
|
858
|
+
* @param {string} args.property Property URN (urn:pdtf:uprn:{uprn})
|
|
859
|
+
* @param {object[]} args.links PropertyTitleLink credential subjects
|
|
860
|
+
* @param {object} [args.transaction] Transaction, for titlesToBeSold
|
|
861
|
+
* @param {object[]} [args.titles] Title subjects, for alsoKnownAs
|
|
862
|
+
* @returns {{
|
|
863
|
+
* property: string,
|
|
864
|
+
* linked: string[], every title currently linked to the property
|
|
865
|
+
* sold: string[], titlesToBeSold that are linked
|
|
866
|
+
* relatedNotSold: string[], linked titles the transaction does not sell
|
|
867
|
+
* unlinkedSold: string[], titlesToBeSold with no link behind them
|
|
868
|
+
* superseded: string[], ids of links superseded by first registration
|
|
869
|
+
* awaitingRegisterLink: string[], registered titles linked only on an epitome
|
|
870
|
+
* }}
|
|
871
|
+
*/
|
|
872
|
+
const linkedTitles = ({ property, links = [], transaction, titles = [] }) => {
|
|
873
|
+
if (!property) throw new Error("linkedTitles: property is required");
|
|
874
|
+
|
|
875
|
+
// Unregistered URN -> the registered title that now carries it.
|
|
876
|
+
const registeredAs = new Map();
|
|
877
|
+
for (const title of titles) {
|
|
878
|
+
for (const alias of title?.alsoKnownAs || []) {
|
|
879
|
+
const existing = registeredAs.get(alias);
|
|
880
|
+
if (existing && existing !== title.id) {
|
|
881
|
+
throw new Error(
|
|
882
|
+
`linkedTitles: "${alias}" is listed in alsoKnownAs by both "${existing}" and "${title.id}"`
|
|
883
|
+
);
|
|
884
|
+
}
|
|
885
|
+
registeredAs.set(alias, title.id);
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
const canonical = (urn) => registeredAs.get(urn) || urn;
|
|
889
|
+
|
|
890
|
+
const forProperty = links.filter((link) => link.property === property);
|
|
891
|
+
for (const link of forProperty) {
|
|
892
|
+
const basis = link.basis?.type;
|
|
893
|
+
const expected = link.title?.startsWith(REGISTERED_TITLE)
|
|
894
|
+
? REGISTER_BASIS
|
|
895
|
+
: link.title?.startsWith(UNREGISTERED_TITLE)
|
|
896
|
+
? EPITOME_BASIS
|
|
897
|
+
: null;
|
|
898
|
+
if (expected && basis !== expected) {
|
|
899
|
+
throw new Error(
|
|
900
|
+
`linkedTitles: link "${link.id || link.title}" to ${link.title} rests on "${basis}", not "${expected}"`
|
|
901
|
+
);
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
const fromRegister = new Set(
|
|
906
|
+
forProperty
|
|
907
|
+
.filter((link) => link.basis?.type === REGISTER_BASIS)
|
|
908
|
+
.map((link) => link.title)
|
|
909
|
+
);
|
|
910
|
+
|
|
911
|
+
const superseded = [];
|
|
912
|
+
const awaiting = new Set();
|
|
913
|
+
const linked = [];
|
|
914
|
+
const seen = new Set();
|
|
915
|
+
for (const link of forProperty) {
|
|
916
|
+
const title = canonical(link.title);
|
|
917
|
+
const isRegisteredAlias = title !== link.title;
|
|
918
|
+
if (isRegisteredAlias && fromRegister.has(title)) {
|
|
919
|
+
superseded.push(link.id || link.title);
|
|
920
|
+
continue;
|
|
921
|
+
}
|
|
922
|
+
if (isRegisteredAlias) awaiting.add(title);
|
|
923
|
+
if (!seen.has(title)) {
|
|
924
|
+
seen.add(title);
|
|
925
|
+
linked.push(title);
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
const selected = [
|
|
930
|
+
...new Set((transaction?.titlesToBeSold || []).map(canonical)),
|
|
931
|
+
];
|
|
932
|
+
const linkedSet = new Set(linked);
|
|
933
|
+
const selectedSet = new Set(selected);
|
|
934
|
+
|
|
935
|
+
return {
|
|
936
|
+
property,
|
|
937
|
+
linked,
|
|
938
|
+
sold: selected.filter((t) => linkedSet.has(t)),
|
|
939
|
+
relatedNotSold: linked.filter((t) => !selectedSet.has(t)),
|
|
940
|
+
unlinkedSold: selected.filter((t) => !linkedSet.has(t)),
|
|
941
|
+
superseded,
|
|
942
|
+
awaitingRegisterLink: linked.filter((t) => awaiting.has(t)),
|
|
943
|
+
};
|
|
944
|
+
};
|
|
945
|
+
|
|
829
946
|
module.exports = {
|
|
830
947
|
mapping,
|
|
948
|
+
linkedTitles,
|
|
831
949
|
decompose,
|
|
832
950
|
recompose,
|
|
833
951
|
projectRelationships,
|