at.tensei.mapping
Schema Diff
+42 -6
Compatibility Analysis
Breaking Changes Detected
3 breaking changes, 6 non-breaking changes.
Breaking Changes (3)
- RequiredEdgeAdded RequiredEdgeAdded { vertex_id: "at.tensei.mapping#fixedPoint", src: "at.tensei.mapping#fixedPoint", tgt: "at.tensei.mapping#fixedPoint.value", kind: "prop", name: Some("value") }
- RequiredEdgeAdded RequiredEdgeAdded { vertex_id: "at.tensei.mapping#scale", src: "at.tensei.mapping#scale", tgt: "at.tensei.mapping#scale.min", kind: "prop", name: Some("min") }
- RequiredEdgeAdded RequiredEdgeAdded { vertex_id: "at.tensei.mapping#scale", src: "at.tensei.mapping#scale", tgt: "at.tensei.mapping#scale.max", kind: "prop", name: Some("max") }
Non-Breaking Changes (6)
- AddedVertex AddedVertex { vertex_id: "at.tensei.mapping#fixedPoint" }
- AddedVertex AddedVertex { vertex_id: "at.tensei.mapping#fixedPoint.value" }
- AddedVertex AddedVertex { vertex_id: "at.tensei.mapping#mappingField.confidence" }
- AddedVertex AddedVertex { vertex_id: "at.tensei.mapping#scale.max" }
- AddedVertex AddedVertex { vertex_id: "at.tensei.mapping#scale.min" }
- AddedEdge AddedEdge { src: "at.tensei.mapping#mappingField", tgt: "at.tensei.mapping#mappingField.confidence", kind: "prop", name: Some("confidence") }
Migration Guidance
Added Elements
AddedVertex { vertex_id: "at.tensei.mapping#fixedPoint" }AddedVertex { vertex_id: "at.tensei.mapping#fixedPoint.value" }AddedVertex { vertex_id: "at.tensei.mapping#mappingField.confidence" }AddedVertex { vertex_id: "at.tensei.mapping#scale.max" }AddedVertex { vertex_id: "at.tensei.mapping#scale.min" }
Additional Notes
- Breaking: RequiredEdgeAdded { vertex_id: "at.tensei.mapping#scale", src: "at.tensei.mapping#scale", tgt: "at.tensei.mapping#scale.min", kind: "prop", name: Some("min") }
- Breaking: RequiredEdgeAdded { vertex_id: "at.tensei.mapping#scale", src: "at.tensei.mapping#scale", tgt: "at.tensei.mapping#scale.max", kind: "prop", name: Some("max") }
- Breaking: RequiredEdgeAdded { vertex_id: "at.tensei.mapping#fixedPoint", src: "at.tensei.mapping#fixedPoint", tgt: "at.tensei.mapping#fixedPoint.value", kind: "prop", name: Some("value") }
- Non-breaking: AddedEdge { src: "at.tensei.mapping#mappingField", tgt: "at.tensei.mapping#mappingField.confidence", kind: "prop", name: Some("confidence") }
1
1
{
2
2
"$type": "com.atproto.lexicon.schema",
3
3
"defs": {
4
4
"fieldDefinition": {
5
5
"description": "A field's declared type and authored requiredness (§3.1). `required` is always written; validation supplies no default.",
6
6
"properties": {
7
7
"required": {
8
8
"type": "boolean"
9
9
},
10
10
"type": {
11
11
"ref": "#fieldType",
12
12
"type": "ref"
13
13
}
14
14
},
15
15
"required": [
16
16
"type",
17
17
"required"
18
18
],
19
19
"type": "object"
20
20
},
21
21
"fieldType": {
22
22
"description": "A declared output type (§3.1). `namespace` is required when `kind` is key and names the key namespace (§3.6).",
23
23
"properties": {
24
24
"kind": {
25
25
"enum": [
26
26
"text",
27
27
"text[]",
28
28
"number",
29
29
"bool",
30
30
"datetime",
31
31
"uri",
32
32
"did",
33
33
"ref",
34
34
"media",
35
35
"score",
36
36
"key",
37
37
"json"
38
38
],
39
39
"type": "string"
40
40
},
41
41
"namespace": {
42
42
"ref": "#keyNamespace",
43
43
"type": "ref"
44
44
}
45
45
},
46
46
"required": [
47
47
"kind"
48
48
],
49
49
"type": "object"
50
50
},
51
+
"fixedPoint": {
52
+
"description": "A number that is not an integer, where no type is declared for it (§1.5): `value` is the number times 10⁶, rounded to six decimal places, so 0.4 is {value: 400000}. An integer is never written this way; it is written as it is. Wherever `$type` names this def, the object is that number. Not stated by the lexicon, and enforced by the reader: it holds no property but `$type` and `value`.",
53
+
"properties": {
54
+
"value": {
55
+
"maximum": 999999999999999,
56
+
"minimum": -999999999999999,
57
+
"type": "integer"
58
+
}
59
+
},
60
+
"required": [
61
+
"value"
62
+
],
63
+
"type": "object"
64
+
},
51
65
"guard": {
52
-
"description": "A §5 leaf. `field` is a §4.2 source path. `value` is a scalar (a string, a finite number or a boolean; never null or an object), or for in/notIn an array of scalars, and is absent for exists/notExists; the lexicon language has no type for a scalar of any kind, so `value` is not declared.",
66
+
"description": "A §5 leaf. `field` is a §4.2 source path. `value` is a scalar (a string, an integer, a #fixedPoint for any other number, or a boolean; never null or an object), or for in/notIn an array of scalars, and is absent for exists/notExists; the lexicon language has no type for a scalar of any kind, so `value` is not declared.",
53
67
"properties": {
54
68
"field": {
55
69
"minLength": 1,
56
70
"type": "string"
57
71
},
58
72
"op": {
59
73
"ref": "#predicateOp",
60
74
"type": "ref"
61
75
}
62
76
},
63
77
"required": [
64
78
"field",
65
79
"op"
66
80
],
67
81
"type": "object"
68
82
},
69
83
"keyNamespace": {
70
84
"enum": [
71
85
"isbn",
72
86
"olwork",
73
87
"goodreads",
74
88
"hive",
75
89
"tmdb",
76
90
"url"
77
91
],
78
92
"type": "string"
79
93
},
80
94
"main": {
81
95
"description": "One source collection → user-shaped output (§4.1). Also defines the field, expression and presentation types that at.tensei.pipeline refers to.",
82
96
"key": "tid",
83
97
"record": {
84
98
"properties": {
85
99
"createdAt": {
86
100
"description": "When the mapping was created: a datetime as §3.1 reads one, which accepts forms atproto's `datetime` format does not, so the format is not declared.",
87
101
"type": "string"
88
102
},
89
103
"guards": {
90
104
"description": "§5 predicate leaves over source paths (§4.1); no all/any/not groups.",
91
105
"items": {
92
106
"ref": "#guard",
93
107
"type": "ref"
94
108
},
95
109
"type": "array"
96
110
},
97
111
"origin": {
98
112
"description": "Present on an adopted copy: the exact version copied (§1.3). Attribution and update-check input; execution never reads it.",
99
113
"ref": "#origin",
100
114
"type": "ref"
101
115
},
102
116
"presentation": {
103
117
"description": "For a standalone preview and to keep a migrated draft's choices; a pipeline's own presentation is authoritative after downstream edits (§3.8). Its entries must name fields in slots.",
104
118
"ref": "#presentation",
105
119
"type": "ref"
106
120
},
107
121
"schema": {
108
122
"ref": "#schemaPin",
109
123
"type": "ref"
110
124
},
111
125
"slots": {
112
126
"description": "The output fields: an object keyed by field names the user chooses, any non-empty name and not limited to a preset's, each value a #mappingField. The lexicon language has no map type, so the names and values are stated here rather than declared.",
113
127
"type": "unknown"
114
128
},
115
129
"source": {
116
130
"description": "The NSID of the collection this mapping reads, and the only place its field names appear.",
117
131
"format": "nsid",
118
132
"type": "string"
119
133
},
120
134
"version": {
121
135
"const": 1,
122
136
"description": "The document version. A document without one is the Phase-2 fixed-preset form, which is migrated explicitly to version 1 (§8.3) and never published as it is.",
123
137
"type": "integer"
124
138
},
125
139
"viewType": {
126
140
"description": "The built-in preset this mapping started from, as attribution only. It never adds, limits or requires a field (§3.8).",
127
141
"enum": [
128
142
"review",
129
143
"bookmark"
130
144
],
131
145
"type": "string"
132
146
}
133
147
},
134
148
"required": [
135
149
"version",
136
150
"source",
137
151
"slots"
138
152
],
139
153
"type": "object"
140
154
},
141
155
"type": "record"
142
156
},
143
157
"mappingExpression": {
144
-
"description": "A source-mapping expression (§4.3): path {path}, constant {value}, operation {op, inputs, args?} or lookup {input, from, to}. Not stated by the lexicon: which properties each kind requires; `value`, any JSON value; `args`, whose count and types each operation fixes (a string separator for split, join and concat, a string find and replacement for replace, an optional string format for parseDate, a finite non-zero divisor for divide, none otherwise); the input count (one, or one or more for coalesce and concat); §4.2 path syntax; and a nesting depth of at most 64 levels.",
158
+
"description": "A source-mapping expression (§4.3): path {path}, constant {value}, operation {op, inputs, args?} or lookup {input, from, to}. Not stated by the lexicon: which properties each kind requires; `value`, any JSON value, in which a number that is not an integer is a #fixedPoint; `args`, whose count and types each operation fixes (a string separator for split, join and concat, a string find and replacement for replace, an optional string format for parseDate, a non-zero divisor for divide, none otherwise; a number that is not an integer is a #fixedPoint); the input count (one, or one or more for coalesce and concat); §4.2 path syntax; and a nesting depth of at most 64 levels.",
145
159
"properties": {
146
160
"from": {
147
161
"ref": "#keyNamespace",
148
162
"type": "ref"
149
163
},
150
164
"input": {
151
165
"ref": "#mappingExpression",
152
166
"type": "ref"
153
167
},
154
168
"inputs": {
155
169
"items": {
156
170
"ref": "#mappingExpression",
157
171
"type": "ref"
158
172
},
159
173
"type": "array"
160
174
},
161
175
"kind": {
162
176
"enum": [
163
177
"path",
164
178
"constant",
165
179
"operation",
166
180
"lookup"
167
181
],
168
182
"type": "string"
169
183
},
170
184
"op": {
171
185
"ref": "#operationName",
172
186
"type": "ref"
173
187
},
174
188
"path": {
175
189
"minLength": 1,
176
190
"type": "string"
177
191
},
178
192
"to": {
179
193
"ref": "#keyNamespace",
180
194
"type": "ref"
181
195
}
182
196
},
183
197
"required": [
184
198
"kind"
185
199
],
186
200
"type": "object"
187
201
},
188
202
"mappingField": {
189
-
"description": "One output field of a mapping (§4.1): a #fieldDefinition plus how this source fills it. An unmapped field has no `expr`. `confidence`, a number from 0 to 1, is proposal metadata; the lexicon language has no non-integer number type, so it is not declared.",
203
+
"description": "One output field of a mapping (§4.1): a #fieldDefinition plus how this source fills it. An unmapped field has no `expr`.",
190
204
"properties": {
191
205
"by": {
192
206
"enum": [
193
207
"constraint",
194
208
"name",
195
209
"stats",
196
210
"model",
197
211
"human"
198
212
],
199
213
"type": "string"
200
214
},
215
+
"confidence": {
216
+
"description": "Proposal metadata: a number from 0 to 1, in fixed point, the value times 10⁶ (§1.5), as a bare integer: 0.4 is 400000.",
217
+
"maximum": 1000000,
218
+
"minimum": 0,
219
+
"type": "integer"
220
+
},
201
221
"expr": {
202
222
"ref": "#mappingExpression",
203
223
"type": "ref"
204
224
},
205
225
"normalization": {
206
226
"ref": "#normalization",
207
227
"type": "ref"
208
228
},
209
229
"required": {
210
230
"type": "boolean"
211
231
},
212
232
"type": {
213
233
"ref": "#fieldType",
214
234
"type": "ref"
215
235
}
216
236
},
217
237
"required": [
218
238
"type",
219
239
"required"
220
240
],
221
241
"type": "object"
222
242
},
223
243
"normalization": {
224
-
"description": "Source-specific input normalization (§4.1), not part of the shared field type. A mapped score field needs {kind: score, scale}, a mapped media field {kind: media, mediaKind}, and canonical {namespace} belongs on a uri field only. `scale` is {min, max}, finite numbers with min < max; the lexicon language has no non-integer number type, so they are not declared.",
244
+
"description": "Source-specific input normalization (§4.1), not part of the shared field type. A mapped score field needs {kind: score, scale}, a mapped media field {kind: media, mediaKind}, and canonical {namespace} belongs on a uri field only.",
225
245
"properties": {
226
246
"kind": {
227
247
"enum": [
228
248
"score",
229
249
"media",
230
250
"canonical"
231
251
],
232
252
"type": "string"
233
253
},
234
254
"mediaKind": {
235
255
"enum": [
236
256
"blob",
237
257
"url"
238
258
],
239
259
"type": "string"
240
260
},
241
261
"namespace": {
242
262
"ref": "#keyNamespace",
243
263
"type": "ref"
244
264
},
245
265
"scale": {
246
266
"ref": "#scale",
247
267
"type": "ref"
248
268
}
249
269
},
250
270
"required": [
251
271
"kind"
252
272
],
253
273
"type": "object"
254
274
},
255
275
"operationName": {
256
276
"description": "§4.3's closed list of operations. A new operation is a spec change, not an extension point.",
257
277
"enum": [
258
278
"split",
259
279
"join",
260
280
"wrap",
261
281
"first",
262
282
"trim",
263
283
"lower",
264
284
"replace",
265
285
"divide",
266
286
"parseDate",
267
287
"coalesce",
268
288
"concat"
269
289
],
270
290
"type": "string"
271
291
},
272
292
"origin": {
273
293
"description": "The original record an independent copy was made from (§1.3, §8.3).",
274
294
"properties": {
275
295
"cid": {
276
296
"description": "The CID of the exact version copied. The `cid` format accepts any spelling of a CID; the validator accepts only the form atproto writes: version 1, in base32, over a SHA-256 digest.",
277
297
"format": "cid",
278
298
"type": "string"
279
299
},
280
300
"uri": {
281
301
"description": "The record copied. Not stated by the lexicon, and enforced by the validator: exactly at://<DID>/<collection NSID>/<record key>, with a DID rather than a handle for its authority.",
282
302
"format": "at-uri",
283
303
"type": "string"
284
304
}
285
305
},
286
306
"required": [
287
307
"uri",
288
308
"cid"
289
309
],
290
310
"type": "object"
291
311
},
292
312
"predicateOp": {
293
313
"description": "§5's closed list of operators.",
294
314
"enum": [
295
315
"eq",
296
316
"ne",
297
317
"lt",
298
318
"lte",
299
319
"gt",
300
320
"gte",
301
321
"contains",
302
322
"notContains",
303
323
"startsWith",
304
324
"matches",
305
325
"exists",
306
326
"notExists",
307
327
"in",
308
328
"notIn"
309
329
],
310
330
"type": "string"
311
331
},
312
332
"presentation": {
313
333
"description": "How the output is shown (§3.8); it never changes data or requiredness. Entries are unique by `field`, each names a field of the shape it presents, and `display` is allowed on a score field only.",
314
334
"properties": {
315
335
"fields": {
316
336
"items": {
317
337
"ref": "#presentationField",
318
338
"type": "ref"
319
339
},
320
340
"type": "array"
321
341
},
322
342
"layout": {
323
343
"enum": [
324
344
"list",
325
345
"table",
326
346
"review",
327
347
"bookmark"
328
348
],
329
349
"type": "string"
330
350
}
331
351
},
332
352
"required": [
333
353
"layout",
334
354
"fields"
335
355
],
336
356
"type": "object"
337
357
},
338
358
"presentationField": {
339
359
"properties": {
340
360
"display": {
341
361
"enum": [
342
362
"stars5",
343
363
"stars10",
344
364
"percent",
345
365
"raw"
346
366
],
347
367
"type": "string"
348
368
},
349
369
"field": {
350
370
"minLength": 1,
351
371
"type": "string"
352
372
},
353
373
"label": {
354
374
"type": "string"
355
375
},
356
376
"visible": {
357
377
"type": "boolean"
358
378
}
359
379
},
360
380
"required": [
361
381
"field",
362
382
"visible"
363
383
],
364
384
"type": "object"
365
385
},
366
386
"scale": {
367
-
"description": "A source's score scale (§3.4): {min, max}, finite numbers with min < max, not declared because they need not be integers.",
387
+
"description": "A source's score scale (§3.4), each bound in fixed point, the value times 10⁶ (§1.5), as a bare integer: a scale of 1 to 10 is 1000000 to 10000000. Not stated by the lexicon, and enforced by the validator: min < max.",
388
+
"properties": {
389
+
"max": {
390
+
"maximum": 999999999999999,
391
+
"minimum": -999999999999999,
392
+
"type": "integer"
393
+
},
394
+
"min": {
395
+
"maximum": 999999999999999,
396
+
"minimum": -999999999999999,
397
+
"type": "integer"
398
+
}
399
+
},
400
+
"required": [
401
+
"min",
402
+
"max"
403
+
],
368
404
"type": "object"
369
405
},
370
406
"schemaPin": {
371
407
"description": "The schema version the mapping was written against (§4.1, §11). Not stated by the lexicon, and enforced by the validator: `nsid` is the mapping's `source`; a published pin has `cid` and no `hash`, an inferred one `hash` and no `cid`.",
372
408
"properties": {
373
409
"cid": {
374
410
"description": "A published pin's version: the CID of the lexicon record. The `cid` format accepts any spelling of a CID; the validator accepts only the form atproto writes: version 1, in base32, over a SHA-256 digest.",
375
411
"format": "cid",
376
412
"type": "string"
377
413
},
378
414
"hash": {
379
415
"description": "An inferred pin's version: `sha256:` and 64 lowercase hex digits, a form the lexicon language cannot state.",
380
416
"minLength": 1,
381
417
"type": "string"
382
418
},
383
419
"nsid": {
384
420
"format": "nsid",
385
421
"type": "string"
386
422
},
387
423
"provenance": {
388
424
"enum": [
389
425
"published",
390
426
"inferred"
391
427
],
392
428
"type": "string"
393
429
},
394
430
"resolvedAt": {
395
431
"description": "When the version was resolved: a datetime as §3.1 reads one, which accepts forms atproto's `datetime` format does not, so the format is not declared.",
396
432
"type": "string"
397
433
}
398
434
},
399
435
"required": [
400
436
"nsid",
401
437
"provenance"
402
438
],
403
439
"type": "object"
404
440
}
405
441
},
406
-
"description": "A Tensei mapping, version 1 (spec §4): records of one source collection mapped into output fields the user authors. Built-in presets are starting points, never a contract: any field name is allowed, and a field is mandatory only when the user marks it `required`. Where the lexicon language cannot state a rule the version-1 validator enforces, the property is left permissive here and the rule is named in its description.",
442
+
"description": "A Tensei mapping, version 1 (spec §4): records of one source collection mapped into output fields the user authors. Built-in presets are starting points, never a contract: any field name is allowed, and a field is mandatory only when the user marks it `required`. The atproto data model has integers only, so a number in the record is an integer written as it is, or any other number in fixed point, the value times 10⁶ (spec §1.5): a bare integer where this lexicon declares it — a #mappingField's `confidence` and a #scale's `min` and `max` — and a #fixedPoint everywhere else. Where the lexicon language cannot state a rule the version-1 validator enforces, the property is left permissive here and the rule is named in its description.",
407
443
"id": "at.tensei.mapping",
408
444
"lexicon": 1
409
445
}