@escape-game-over/atlas 0.1.10 → 0.1.12
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/package.json +1 -1
- package/src/index.ts +5 -0
- package/src/jsonld/creative-work.ts +125 -0
- package/src/jsonld/index.ts +5 -0
- package/src/jsonld/quantity.ts +2 -3
- package/src/money.ts +26 -14
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -125,6 +125,9 @@ export {
|
|
|
125
125
|
breadcrumbList,
|
|
126
126
|
businessId,
|
|
127
127
|
type CatalogEntry,
|
|
128
|
+
type CreativeWorkInput,
|
|
129
|
+
type CreativeWorkProperty,
|
|
130
|
+
creativeWork,
|
|
128
131
|
type FaqEntry,
|
|
129
132
|
faqPage,
|
|
130
133
|
geoCoordinates,
|
|
@@ -188,6 +191,8 @@ export {
|
|
|
188
191
|
formatQuantities,
|
|
189
192
|
type Price,
|
|
190
193
|
type PriceTier,
|
|
194
|
+
type Quantity,
|
|
195
|
+
type QuantityRange,
|
|
191
196
|
type TieredPrice,
|
|
192
197
|
tierRange,
|
|
193
198
|
} from "./money.ts";
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import type { Quantity } from "../money.ts";
|
|
2
|
+
import type { HttpsUrl } from "../url.ts";
|
|
3
|
+
import type { BusinessId, OrganizationId } from "./ids.ts";
|
|
4
|
+
import { type JsonLdNode, schemaType } from "./node.ts";
|
|
5
|
+
import { quantitativeValue } from "./quantity.ts";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* One measured fact about the work, as `additionalProperty` states one.
|
|
9
|
+
*
|
|
10
|
+
* schema.org has a named property for a few of these — `timeRequired` on a
|
|
11
|
+
* `CreativeWork`, `numberOfPlayers` on a `Game` — and nothing at all for most:
|
|
12
|
+
* how many puzzles a room holds, how much floor it needs, how hard it is. A
|
|
13
|
+
* `PropertyValue` is the vocabulary's own answer for exactly that, and using it
|
|
14
|
+
* for the whole set keeps one list rather than splitting a spec table across
|
|
15
|
+
* named properties and a leftovers bag by whichever half the vocabulary
|
|
16
|
+
* happened to name.
|
|
17
|
+
*
|
|
18
|
+
* The two unit properties are not alternatives to taste. `unitCode` is the
|
|
19
|
+
* standard one — a UN/CEFACT Common Code, `MIN` for minutes, `MTK` for square
|
|
20
|
+
* metres — and is what a consumer reads without parsing anything. `unitText` is
|
|
21
|
+
* free text, and schema.org states its purpose exactly: for when you *cannot*
|
|
22
|
+
* provide a standard code. Putting a code in `unitText` publishes `"MIN"` as a
|
|
23
|
+
* word rather than as a unit, which is the mistake this note exists to stop.
|
|
24
|
+
*
|
|
25
|
+
* Both may be given — a code for machines and a symbol for a reader — and
|
|
26
|
+
* neither need be: a count of puzzles is a number of nothing.
|
|
27
|
+
*
|
|
28
|
+
* Neither is checked against the code list, matching `LocalBusinessInput.type`:
|
|
29
|
+
* lib does not carry a copy of someone else's vocabulary to fall behind it.
|
|
30
|
+
*/
|
|
31
|
+
export interface CreativeWorkProperty {
|
|
32
|
+
/** In the reader's language: lib does not translate, see `genre`. */
|
|
33
|
+
readonly name: string;
|
|
34
|
+
readonly value: Quantity;
|
|
35
|
+
/**
|
|
36
|
+
* UN/CEFACT Common Code — `"MIN"`, `"MTK"` — a URL, or `prefix:code` for
|
|
37
|
+
* another scheme. Two or three alphanumerics, despite schema.org saying
|
|
38
|
+
* three: the list holds hundreds of two-character codes.
|
|
39
|
+
*/
|
|
40
|
+
readonly unitCode?: string;
|
|
41
|
+
/** A symbol a reader sees, e.g. `"m²"`. */
|
|
42
|
+
readonly unitText?: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface CreativeWorkInput {
|
|
46
|
+
readonly name: string;
|
|
47
|
+
/** This work's own page. */
|
|
48
|
+
readonly url: HttpsUrl;
|
|
49
|
+
readonly description?: string;
|
|
50
|
+
/** A picture of it, absolute. */
|
|
51
|
+
readonly image?: HttpsUrl;
|
|
52
|
+
/**
|
|
53
|
+
* What kind of thing it is, in words a reader would use.
|
|
54
|
+
*
|
|
55
|
+
* Already translated. lib holds no catalog and cannot turn a consumer's
|
|
56
|
+
* internal ids into names — a site passing its own slugs publishes
|
|
57
|
+
* `"sci-fi"` and `"live-act"` to a crawler, which is a fact about that
|
|
58
|
+
* site's database rather than about the work.
|
|
59
|
+
*/
|
|
60
|
+
readonly genre?: readonly string[];
|
|
61
|
+
/** The `@id` of whoever made it. See `ProductInput.seller`. */
|
|
62
|
+
readonly creator?: BusinessId | OrganizationId;
|
|
63
|
+
/**
|
|
64
|
+
* The spec table, if the work has one.
|
|
65
|
+
*
|
|
66
|
+
* Omitted entirely rather than emitted empty when a work has no measured
|
|
67
|
+
* facts yet — an unbuilt room with a name and a picture. An empty
|
|
68
|
+
* `additionalProperty` claims a table exists and is blank.
|
|
69
|
+
*/
|
|
70
|
+
readonly properties?: readonly CreativeWorkProperty[];
|
|
71
|
+
/** Further types alongside `CreativeWork`. See `ProductInput.alsoA`. */
|
|
72
|
+
readonly alsoA?: readonly string[];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Something the site made, described but not sold.
|
|
77
|
+
*
|
|
78
|
+
* The counterpart to `product`, and the distinction is the price: a `Product`
|
|
79
|
+
* exists in the graph to carry an `Offer`, so a thing with no price published
|
|
80
|
+
* as one is a product nobody can buy — an offer with no number is read as
|
|
81
|
+
* unknown availability rather than as "ask us". A room licensed to operators,
|
|
82
|
+
* a course, a film: `CreativeWork` says what it is and stops there.
|
|
83
|
+
*
|
|
84
|
+
* No `@id`. Nothing in the graph refers to a work — see the note in `ids.ts`
|
|
85
|
+
* for the test, and for why minting one before something points at it is a
|
|
86
|
+
* cost rather than a courtesy.
|
|
87
|
+
*
|
|
88
|
+
* Reference: <https://schema.org/CreativeWork>
|
|
89
|
+
*/
|
|
90
|
+
export function creativeWork(work: CreativeWorkInput): JsonLdNode {
|
|
91
|
+
return {
|
|
92
|
+
"@type": schemaType(["CreativeWork", ...(work.alsoA ?? [])]),
|
|
93
|
+
name: work.name,
|
|
94
|
+
url: work.url,
|
|
95
|
+
...(work.description === undefined
|
|
96
|
+
? {}
|
|
97
|
+
: { description: work.description }),
|
|
98
|
+
...(work.image === undefined ? {} : { image: work.image }),
|
|
99
|
+
...(work.genre === undefined || work.genre.length === 0
|
|
100
|
+
? {}
|
|
101
|
+
: { genre: work.genre }),
|
|
102
|
+
...(work.creator === undefined
|
|
103
|
+
? {}
|
|
104
|
+
: { creator: { "@id": work.creator } }),
|
|
105
|
+
...(work.properties === undefined || work.properties.length === 0
|
|
106
|
+
? {}
|
|
107
|
+
: { additionalProperty: work.properties.map(propertyValue) }),
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function propertyValue(property: CreativeWorkProperty): JsonLdNode {
|
|
112
|
+
return {
|
|
113
|
+
"@type": "PropertyValue",
|
|
114
|
+
name: property.name,
|
|
115
|
+
value: {
|
|
116
|
+
...quantitativeValue(property.value),
|
|
117
|
+
...(property.unitCode === undefined
|
|
118
|
+
? {}
|
|
119
|
+
: { unitCode: property.unitCode }),
|
|
120
|
+
...(property.unitText === undefined
|
|
121
|
+
? {}
|
|
122
|
+
: { unitText: property.unitText }),
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
}
|
package/src/jsonld/index.ts
CHANGED
|
@@ -32,6 +32,11 @@
|
|
|
32
32
|
export { type ArticleInput, article } from "./article.ts";
|
|
33
33
|
export { type BreadcrumbStep, breadcrumbList } from "./breadcrumb.ts";
|
|
34
34
|
export { type LocalBusinessInput, localBusiness } from "./business.ts";
|
|
35
|
+
export {
|
|
36
|
+
type CreativeWorkInput,
|
|
37
|
+
type CreativeWorkProperty,
|
|
38
|
+
creativeWork,
|
|
39
|
+
} from "./creative-work.ts";
|
|
35
40
|
export { type FaqEntry, faqPage } from "./faq.ts";
|
|
36
41
|
export {
|
|
37
42
|
type BusinessId,
|
package/src/jsonld/quantity.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
isContiguous,
|
|
3
3
|
type PriceTier,
|
|
4
|
+
type Quantity,
|
|
4
5
|
quantitiesOf,
|
|
5
6
|
quantityRange,
|
|
6
7
|
} from "../money.ts";
|
|
@@ -20,9 +21,7 @@ import type { JsonLdNode } from "./node.ts";
|
|
|
20
21
|
*
|
|
21
22
|
* Reference: <https://schema.org/QuantitativeValue>
|
|
22
23
|
*/
|
|
23
|
-
export function quantitativeValue(
|
|
24
|
-
quantity: number | { readonly min: number; readonly max: number }
|
|
25
|
-
): JsonLdNode {
|
|
24
|
+
export function quantitativeValue(quantity: Quantity): JsonLdNode {
|
|
26
25
|
return typeof quantity === "number"
|
|
27
26
|
? { "@type": "QuantitativeValue", value: quantity }
|
|
28
27
|
: {
|
package/src/money.ts
CHANGED
|
@@ -26,16 +26,32 @@ export interface Price {
|
|
|
26
26
|
readonly currency: CurrencyCode;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
/** A span between two counts, both ends included. */
|
|
30
|
+
export interface QuantityRange {
|
|
31
|
+
readonly min: number;
|
|
32
|
+
readonly max: number;
|
|
33
|
+
}
|
|
34
|
+
|
|
29
35
|
/**
|
|
30
|
-
*
|
|
36
|
+
* How many of something: an exact count, or a range.
|
|
37
|
+
*
|
|
38
|
+
* A scalar for the common case and an object when it genuinely varies, the same
|
|
39
|
+
* bargain `ThemeColor` strikes — the range alone would make the ordinary case
|
|
40
|
+
* noisy, and `{ min: 2, max: 2 }` five times over says nothing `2, 3, 4, 5, 6`
|
|
41
|
+
* does not.
|
|
31
42
|
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
43
|
+
* Named here, in the lowest module that needs it, because three unrelated
|
|
44
|
+
* things measure in it: a price band, a `QuantitativeValue`, and a spec on a
|
|
45
|
+
* `CreativeWork`. Spelled out at each it is three shapes that happen to match,
|
|
46
|
+
* and a caller moving a figure between them has to check that they still do.
|
|
47
|
+
*/
|
|
48
|
+
export type Quantity = number | QuantityRange;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* What one quantity band costs.
|
|
36
52
|
*/
|
|
37
53
|
export interface PriceTier {
|
|
38
|
-
readonly quantity:
|
|
54
|
+
readonly quantity: Quantity;
|
|
39
55
|
/**
|
|
40
56
|
* What one unit costs at this quantity — not the total for the band.
|
|
41
57
|
*
|
|
@@ -70,10 +86,7 @@ export interface TieredPrice {
|
|
|
70
86
|
}
|
|
71
87
|
|
|
72
88
|
/** The band a tier covers, with a bare count widened to a range of one. */
|
|
73
|
-
export function tierRange(tier: PriceTier): {
|
|
74
|
-
readonly min: number;
|
|
75
|
-
readonly max: number;
|
|
76
|
-
} {
|
|
89
|
+
export function tierRange(tier: PriceTier): QuantityRange {
|
|
77
90
|
return typeof tier.quantity === "number"
|
|
78
91
|
? { min: tier.quantity, max: tier.quantity }
|
|
79
92
|
: tier.quantity;
|
|
@@ -91,10 +104,9 @@ export function tierRange(tier: PriceTier): {
|
|
|
91
104
|
* a caller wants is `formatQuantities` or `quantitiesFor`, which pick between
|
|
92
105
|
* this and `quantitiesOf` by reading the tiers.
|
|
93
106
|
*/
|
|
94
|
-
export function quantityRange(
|
|
95
|
-
readonly
|
|
96
|
-
|
|
97
|
-
} {
|
|
107
|
+
export function quantityRange(
|
|
108
|
+
tiers: readonly [PriceTier, ...PriceTier[]]
|
|
109
|
+
): QuantityRange {
|
|
98
110
|
const first = tierRange(tiers[0]);
|
|
99
111
|
const last = tiers.at(-1);
|
|
100
112
|
return {
|