@homepages/template-kit 0.0.0 → 0.1.0
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/CHANGELOG.md +182 -0
- package/README.md +71 -14
- package/dist/base.css +85 -0
- package/dist/browser.d.ts +26 -0
- package/dist/browser.js +55 -0
- package/dist/cli.js +1 -1
- package/dist/contracts/image-descriptor.d.ts +17 -0
- package/dist/contracts/markers.d.ts +20 -0
- package/dist/contracts/markers.js +21 -0
- package/dist/contracts/slot-catalog.d.ts +237 -0
- package/dist/contracts/slot-catalog.js +247 -0
- package/dist/contracts/transforms.d.ts +108 -0
- package/dist/contracts/transforms.js +117 -0
- package/dist/design-system/breakpoints.d.ts +5 -0
- package/dist/design-system/breakpoints.js +14 -0
- package/dist/design-system/theme.d.ts +307 -0
- package/dist/design-system/theme.js +95 -0
- package/dist/eslint/is-client-file.js +79 -0
- package/dist/eslint/rules/image-bare-needs-reason.js +38 -0
- package/dist/eslint/rules/no-client-directive-in-contract.js +26 -0
- package/dist/eslint/rules/no-client-runtime-in-server.js +99 -0
- package/dist/eslint/rules/no-css-import-from-render-path.js +23 -0
- package/dist/eslint/rules/no-hex.js +111 -0
- package/dist/eslint/rules/no-inline-style.js +23 -0
- package/dist/eslint/rules/no-nondeterminism.js +44 -0
- package/dist/eslint/rules/no-raw-element.js +32 -0
- package/dist/eslint/rules/props-from-schema.js +32 -0
- package/dist/eslint/rules/serializable-island-props.js +108 -0
- package/dist/eslint/rules/slot-marker-literal.js +33 -0
- package/dist/eslint.d.ts +4 -8
- package/dist/eslint.js +75 -11
- package/dist/fixtures/sample-property.d.ts +67 -0
- package/dist/fixtures/sample-property.js +707 -0
- package/dist/fixtures.d.ts +2 -0
- package/dist/fixtures.js +3 -0
- package/dist/index.d.ts +37 -13
- package/dist/index.js +34 -2
- package/dist/island-runtime.d.ts +30 -0
- package/dist/island-runtime.js +73 -0
- package/dist/islands/contract.d.ts +56 -0
- package/dist/islands/contract.js +71 -0
- package/dist/islands/marker.d.ts +16 -0
- package/dist/islands/marker.js +18 -0
- package/dist/package.js +5 -0
- package/dist/primitives/Image.d.ts +49 -0
- package/dist/primitives/Image.js +95 -0
- package/dist/primitives/Section.d.ts +22 -0
- package/dist/primitives/Section.js +26 -0
- package/dist/primitives/Slot.d.ts +50 -0
- package/dist/primitives/Slot.js +39 -0
- package/dist/primitives/SlotGroup.d.ts +38 -0
- package/dist/primitives/SlotGroup.js +24 -0
- package/dist/primitives/SlotItem.d.ts +43 -0
- package/dist/primitives/SlotItem.js +38 -0
- package/dist/primitives/index.d.ts +7 -0
- package/dist/primitives/picture-sources.d.ts +22 -0
- package/dist/primitives/picture-sources.js +52 -0
- package/dist/schema/fill-spec.d.ts +692 -0
- package/dist/schema/fill-spec.js +98 -0
- package/dist/schema/fixture-schema.d.ts +143 -0
- package/dist/schema/fixture-schema.js +113 -0
- package/dist/schema/index.d.ts +16 -0
- package/dist/schema/manifest.d.ts +527 -0
- package/dist/schema/manifest.js +73 -0
- package/dist/schema/property-facts.d.ts +44 -0
- package/dist/schema/resolve-variants.d.ts +11 -0
- package/dist/schema/resolve-variants.js +15 -0
- package/dist/schema/runtime-values.d.ts +45 -0
- package/dist/schema/section-nav.d.ts +7 -0
- package/dist/schema/section-props.d.ts +69 -0
- package/dist/schema/section-schema.d.ts +533 -0
- package/dist/schema/section-schema.js +72 -0
- package/dist/schema/slot-types.d.ts +117 -0
- package/dist/schema/slot-types.js +181 -0
- package/dist/schema/source.d.ts +13 -0
- package/dist/schema/source.js +12 -0
- package/dist/schema/synthesize-nullable.d.ts +18 -0
- package/dist/schema/synthesize-nullable.js +47 -0
- package/dist/styles.css +131 -9
- package/docs/INDEX.md +6 -4
- package/docs/eslint.md +90 -0
- package/docs/islands.md +150 -0
- package/docs/llms.txt +30 -3
- package/docs/primitives.md +214 -0
- package/docs/schema-system.md +240 -0
- package/docs/theme-and-css.md +179 -0
- package/package.json +30 -6
- package/tsconfig.base.json +16 -0
- package/tsconfig.json +2 -13
- package/dist/src-CrdHXijV.js +0 -23
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
//#region src/contracts/slot-catalog.d.ts
|
|
2
|
+
declare const CONTENT_SLOT_TYPES: readonly ["text", "number", "select", "image", "image_collection", "list", "object_list", "poi_list", "url"];
|
|
3
|
+
declare const REF_SLOT_TYPES: readonly [];
|
|
4
|
+
declare const SLOT_TYPES: readonly ["text", "number", "select", "image", "image_collection", "list", "object_list", "poi_list", "url"];
|
|
5
|
+
type ContentSlotType = (typeof CONTENT_SLOT_TYPES)[number];
|
|
6
|
+
type RefSlotType = (typeof REF_SLOT_TYPES)[number];
|
|
7
|
+
type SlotType = (typeof SLOT_TYPES)[number];
|
|
8
|
+
declare const REFERRAL_FIELDS: readonly ["name", "title", "email", "phone", "website", "instagram", "linkedin", "x", "facebook", "headshot", "logo_dark", "logo_light"];
|
|
9
|
+
type ReferralField = (typeof REFERRAL_FIELDS)[number];
|
|
10
|
+
declare const REFERRAL_IMAGE_FIELDS: readonly ["headshot", "logo_dark", "logo_light"];
|
|
11
|
+
type ReferralImageField = (typeof REFERRAL_IMAGE_FIELDS)[number];
|
|
12
|
+
type SourceFieldContainer = "property" | "unit" | "contact";
|
|
13
|
+
type SourceFieldValueType = "text" | "number" | "image" | "coordinates" | "unit_list" | "contact_list";
|
|
14
|
+
type WritebackTarget = {
|
|
15
|
+
kind: "property";
|
|
16
|
+
column: "listing_intent";
|
|
17
|
+
} | {
|
|
18
|
+
kind: "unit";
|
|
19
|
+
column: "unit_label" | "beds" | "baths" | "sqft" | "price";
|
|
20
|
+
} | {
|
|
21
|
+
kind: "contact";
|
|
22
|
+
field: string;
|
|
23
|
+
};
|
|
24
|
+
type SourceFieldDescriptor = {
|
|
25
|
+
container: SourceFieldContainer;
|
|
26
|
+
value_type: SourceFieldValueType;
|
|
27
|
+
image?: true;
|
|
28
|
+
writeback?: WritebackTarget;
|
|
29
|
+
};
|
|
30
|
+
declare const SOURCE_FIELDS: {
|
|
31
|
+
address: {
|
|
32
|
+
container: "property";
|
|
33
|
+
value_type: "text";
|
|
34
|
+
};
|
|
35
|
+
listing_intent: {
|
|
36
|
+
container: "property";
|
|
37
|
+
value_type: "text";
|
|
38
|
+
writeback: {
|
|
39
|
+
kind: "property";
|
|
40
|
+
column: "listing_intent";
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
status: {
|
|
44
|
+
container: "property";
|
|
45
|
+
value_type: "text";
|
|
46
|
+
};
|
|
47
|
+
property_type: {
|
|
48
|
+
container: "property";
|
|
49
|
+
value_type: "text";
|
|
50
|
+
};
|
|
51
|
+
year_built: {
|
|
52
|
+
container: "property";
|
|
53
|
+
value_type: "number";
|
|
54
|
+
};
|
|
55
|
+
lot_size: {
|
|
56
|
+
container: "property";
|
|
57
|
+
value_type: "text";
|
|
58
|
+
};
|
|
59
|
+
unit_label: {
|
|
60
|
+
container: "unit";
|
|
61
|
+
value_type: "text";
|
|
62
|
+
writeback: {
|
|
63
|
+
kind: "unit";
|
|
64
|
+
column: "unit_label";
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
beds: {
|
|
68
|
+
container: "unit";
|
|
69
|
+
value_type: "number";
|
|
70
|
+
writeback: {
|
|
71
|
+
kind: "unit";
|
|
72
|
+
column: "beds";
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
baths: {
|
|
76
|
+
container: "unit";
|
|
77
|
+
value_type: "number";
|
|
78
|
+
writeback: {
|
|
79
|
+
kind: "unit";
|
|
80
|
+
column: "baths";
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
sqft: {
|
|
84
|
+
container: "unit";
|
|
85
|
+
value_type: "number";
|
|
86
|
+
writeback: {
|
|
87
|
+
kind: "unit";
|
|
88
|
+
column: "sqft";
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
price: {
|
|
92
|
+
container: "unit";
|
|
93
|
+
value_type: "text";
|
|
94
|
+
writeback: {
|
|
95
|
+
kind: "unit";
|
|
96
|
+
column: "price";
|
|
97
|
+
};
|
|
98
|
+
};
|
|
99
|
+
notes: {
|
|
100
|
+
container: "unit";
|
|
101
|
+
value_type: "text";
|
|
102
|
+
};
|
|
103
|
+
units: {
|
|
104
|
+
container: "property";
|
|
105
|
+
value_type: "unit_list";
|
|
106
|
+
};
|
|
107
|
+
contacts: {
|
|
108
|
+
container: "property";
|
|
109
|
+
value_type: "contact_list";
|
|
110
|
+
};
|
|
111
|
+
name: {
|
|
112
|
+
container: "contact";
|
|
113
|
+
value_type: "text";
|
|
114
|
+
writeback: {
|
|
115
|
+
kind: "contact";
|
|
116
|
+
field: string;
|
|
117
|
+
};
|
|
118
|
+
};
|
|
119
|
+
title: {
|
|
120
|
+
container: "contact";
|
|
121
|
+
value_type: "text";
|
|
122
|
+
writeback: {
|
|
123
|
+
kind: "contact";
|
|
124
|
+
field: string;
|
|
125
|
+
};
|
|
126
|
+
};
|
|
127
|
+
bio: {
|
|
128
|
+
container: "contact";
|
|
129
|
+
value_type: "text";
|
|
130
|
+
writeback: {
|
|
131
|
+
kind: "contact";
|
|
132
|
+
field: string;
|
|
133
|
+
};
|
|
134
|
+
};
|
|
135
|
+
email: {
|
|
136
|
+
container: "contact";
|
|
137
|
+
value_type: "text";
|
|
138
|
+
writeback: {
|
|
139
|
+
kind: "contact";
|
|
140
|
+
field: string;
|
|
141
|
+
};
|
|
142
|
+
};
|
|
143
|
+
phone: {
|
|
144
|
+
container: "contact";
|
|
145
|
+
value_type: "text";
|
|
146
|
+
writeback: {
|
|
147
|
+
kind: "contact";
|
|
148
|
+
field: string;
|
|
149
|
+
};
|
|
150
|
+
};
|
|
151
|
+
website: {
|
|
152
|
+
container: "contact";
|
|
153
|
+
value_type: "text";
|
|
154
|
+
writeback: {
|
|
155
|
+
kind: "contact";
|
|
156
|
+
field: string;
|
|
157
|
+
};
|
|
158
|
+
};
|
|
159
|
+
instagram: {
|
|
160
|
+
container: "contact";
|
|
161
|
+
value_type: "text";
|
|
162
|
+
writeback: {
|
|
163
|
+
kind: "contact";
|
|
164
|
+
field: string;
|
|
165
|
+
};
|
|
166
|
+
};
|
|
167
|
+
linkedin: {
|
|
168
|
+
container: "contact";
|
|
169
|
+
value_type: "text";
|
|
170
|
+
writeback: {
|
|
171
|
+
kind: "contact";
|
|
172
|
+
field: string;
|
|
173
|
+
};
|
|
174
|
+
};
|
|
175
|
+
x: {
|
|
176
|
+
container: "contact";
|
|
177
|
+
value_type: "text";
|
|
178
|
+
writeback: {
|
|
179
|
+
kind: "contact";
|
|
180
|
+
field: string;
|
|
181
|
+
};
|
|
182
|
+
};
|
|
183
|
+
facebook: {
|
|
184
|
+
container: "contact";
|
|
185
|
+
value_type: "text";
|
|
186
|
+
writeback: {
|
|
187
|
+
kind: "contact";
|
|
188
|
+
field: string;
|
|
189
|
+
};
|
|
190
|
+
};
|
|
191
|
+
headshot: {
|
|
192
|
+
container: "contact";
|
|
193
|
+
value_type: "image";
|
|
194
|
+
image: true;
|
|
195
|
+
};
|
|
196
|
+
logo_dark: {
|
|
197
|
+
container: "contact";
|
|
198
|
+
value_type: "image";
|
|
199
|
+
image: true;
|
|
200
|
+
};
|
|
201
|
+
logo_light: {
|
|
202
|
+
container: "contact";
|
|
203
|
+
value_type: "image";
|
|
204
|
+
image: true;
|
|
205
|
+
};
|
|
206
|
+
};
|
|
207
|
+
type SourceFieldName = keyof typeof SOURCE_FIELDS;
|
|
208
|
+
declare const COLLECTIONS: {
|
|
209
|
+
readonly units: {
|
|
210
|
+
readonly idKey: "unit_id";
|
|
211
|
+
readonly ownership: "property";
|
|
212
|
+
};
|
|
213
|
+
readonly contacts: {
|
|
214
|
+
readonly idKey: "id";
|
|
215
|
+
readonly cap: 12;
|
|
216
|
+
readonly min: 1;
|
|
217
|
+
readonly ownership: "library";
|
|
218
|
+
};
|
|
219
|
+
};
|
|
220
|
+
type CollectionField = keyof typeof COLLECTIONS;
|
|
221
|
+
type CollectionIdKey = (typeof COLLECTIONS)[CollectionField]["idKey"];
|
|
222
|
+
type CollectionOwnership = (typeof COLLECTIONS)[CollectionField]["ownership"];
|
|
223
|
+
declare function collectionForSlot(slot: {
|
|
224
|
+
type?: string;
|
|
225
|
+
source?: {
|
|
226
|
+
kind?: string;
|
|
227
|
+
field?: string;
|
|
228
|
+
};
|
|
229
|
+
}): CollectionField | null;
|
|
230
|
+
declare const DERIVED_INPUT_FIELDS: readonly ["units", "address_line_parts", "contact_address_parts", "coordinates", "clock"];
|
|
231
|
+
type DerivedInputName = (typeof DERIVED_INPUT_FIELDS)[number];
|
|
232
|
+
declare const DECISION_TYPES: readonly ["image-assign", "image-collection", "text-block", "text-seed", "list-extract", "structured-list"];
|
|
233
|
+
type DecisionType = (typeof DECISION_TYPES)[number];
|
|
234
|
+
declare const FORMATTER_NAMES: readonly ["currency", "number"];
|
|
235
|
+
type FormatterName = (typeof FORMATTER_NAMES)[number];
|
|
236
|
+
//#endregion
|
|
237
|
+
export { COLLECTIONS, CONTENT_SLOT_TYPES, CollectionField, CollectionIdKey, CollectionOwnership, ContentSlotType, DECISION_TYPES, DERIVED_INPUT_FIELDS, DecisionType, DerivedInputName, FORMATTER_NAMES, FormatterName, REFERRAL_FIELDS, REFERRAL_IMAGE_FIELDS, REF_SLOT_TYPES, RefSlotType, ReferralField, ReferralImageField, SLOT_TYPES, SOURCE_FIELDS, SlotType, SourceFieldContainer, SourceFieldDescriptor, SourceFieldName, SourceFieldValueType, WritebackTarget, collectionForSlot };
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
//#region src/contracts/slot-catalog.ts
|
|
2
|
+
const CONTENT_SLOT_TYPES = [
|
|
3
|
+
"text",
|
|
4
|
+
"number",
|
|
5
|
+
"select",
|
|
6
|
+
"image",
|
|
7
|
+
"image_collection",
|
|
8
|
+
"list",
|
|
9
|
+
"object_list",
|
|
10
|
+
"poi_list",
|
|
11
|
+
"url"
|
|
12
|
+
];
|
|
13
|
+
const REF_SLOT_TYPES = [];
|
|
14
|
+
const SLOT_TYPES = [...CONTENT_SLOT_TYPES, ...REF_SLOT_TYPES];
|
|
15
|
+
const REFERRAL_FIELDS = [
|
|
16
|
+
"name",
|
|
17
|
+
"title",
|
|
18
|
+
"email",
|
|
19
|
+
"phone",
|
|
20
|
+
"website",
|
|
21
|
+
"instagram",
|
|
22
|
+
"linkedin",
|
|
23
|
+
"x",
|
|
24
|
+
"facebook",
|
|
25
|
+
"headshot",
|
|
26
|
+
"logo_dark",
|
|
27
|
+
"logo_light"
|
|
28
|
+
];
|
|
29
|
+
const REFERRAL_IMAGE_FIELDS = [
|
|
30
|
+
"headshot",
|
|
31
|
+
"logo_dark",
|
|
32
|
+
"logo_light"
|
|
33
|
+
];
|
|
34
|
+
const SOURCE_FIELDS = {
|
|
35
|
+
address: {
|
|
36
|
+
container: "property",
|
|
37
|
+
value_type: "text"
|
|
38
|
+
},
|
|
39
|
+
listing_intent: {
|
|
40
|
+
container: "property",
|
|
41
|
+
value_type: "text",
|
|
42
|
+
writeback: {
|
|
43
|
+
kind: "property",
|
|
44
|
+
column: "listing_intent"
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
status: {
|
|
48
|
+
container: "property",
|
|
49
|
+
value_type: "text"
|
|
50
|
+
},
|
|
51
|
+
property_type: {
|
|
52
|
+
container: "property",
|
|
53
|
+
value_type: "text"
|
|
54
|
+
},
|
|
55
|
+
year_built: {
|
|
56
|
+
container: "property",
|
|
57
|
+
value_type: "number"
|
|
58
|
+
},
|
|
59
|
+
lot_size: {
|
|
60
|
+
container: "property",
|
|
61
|
+
value_type: "text"
|
|
62
|
+
},
|
|
63
|
+
unit_label: {
|
|
64
|
+
container: "unit",
|
|
65
|
+
value_type: "text",
|
|
66
|
+
writeback: {
|
|
67
|
+
kind: "unit",
|
|
68
|
+
column: "unit_label"
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
beds: {
|
|
72
|
+
container: "unit",
|
|
73
|
+
value_type: "number",
|
|
74
|
+
writeback: {
|
|
75
|
+
kind: "unit",
|
|
76
|
+
column: "beds"
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
baths: {
|
|
80
|
+
container: "unit",
|
|
81
|
+
value_type: "number",
|
|
82
|
+
writeback: {
|
|
83
|
+
kind: "unit",
|
|
84
|
+
column: "baths"
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
sqft: {
|
|
88
|
+
container: "unit",
|
|
89
|
+
value_type: "number",
|
|
90
|
+
writeback: {
|
|
91
|
+
kind: "unit",
|
|
92
|
+
column: "sqft"
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
price: {
|
|
96
|
+
container: "unit",
|
|
97
|
+
value_type: "text",
|
|
98
|
+
writeback: {
|
|
99
|
+
kind: "unit",
|
|
100
|
+
column: "price"
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
notes: {
|
|
104
|
+
container: "unit",
|
|
105
|
+
value_type: "text"
|
|
106
|
+
},
|
|
107
|
+
units: {
|
|
108
|
+
container: "property",
|
|
109
|
+
value_type: "unit_list"
|
|
110
|
+
},
|
|
111
|
+
contacts: {
|
|
112
|
+
container: "property",
|
|
113
|
+
value_type: "contact_list"
|
|
114
|
+
},
|
|
115
|
+
name: {
|
|
116
|
+
container: "contact",
|
|
117
|
+
value_type: "text",
|
|
118
|
+
writeback: {
|
|
119
|
+
kind: "contact",
|
|
120
|
+
field: "name"
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
title: {
|
|
124
|
+
container: "contact",
|
|
125
|
+
value_type: "text",
|
|
126
|
+
writeback: {
|
|
127
|
+
kind: "contact",
|
|
128
|
+
field: "title"
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
bio: {
|
|
132
|
+
container: "contact",
|
|
133
|
+
value_type: "text",
|
|
134
|
+
writeback: {
|
|
135
|
+
kind: "contact",
|
|
136
|
+
field: "bio"
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
email: {
|
|
140
|
+
container: "contact",
|
|
141
|
+
value_type: "text",
|
|
142
|
+
writeback: {
|
|
143
|
+
kind: "contact",
|
|
144
|
+
field: "email"
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
phone: {
|
|
148
|
+
container: "contact",
|
|
149
|
+
value_type: "text",
|
|
150
|
+
writeback: {
|
|
151
|
+
kind: "contact",
|
|
152
|
+
field: "phone"
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
website: {
|
|
156
|
+
container: "contact",
|
|
157
|
+
value_type: "text",
|
|
158
|
+
writeback: {
|
|
159
|
+
kind: "contact",
|
|
160
|
+
field: "website"
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
instagram: {
|
|
164
|
+
container: "contact",
|
|
165
|
+
value_type: "text",
|
|
166
|
+
writeback: {
|
|
167
|
+
kind: "contact",
|
|
168
|
+
field: "socials.instagram"
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
linkedin: {
|
|
172
|
+
container: "contact",
|
|
173
|
+
value_type: "text",
|
|
174
|
+
writeback: {
|
|
175
|
+
kind: "contact",
|
|
176
|
+
field: "socials.linkedin"
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
x: {
|
|
180
|
+
container: "contact",
|
|
181
|
+
value_type: "text",
|
|
182
|
+
writeback: {
|
|
183
|
+
kind: "contact",
|
|
184
|
+
field: "socials.x"
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
facebook: {
|
|
188
|
+
container: "contact",
|
|
189
|
+
value_type: "text",
|
|
190
|
+
writeback: {
|
|
191
|
+
kind: "contact",
|
|
192
|
+
field: "socials.facebook"
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
headshot: {
|
|
196
|
+
container: "contact",
|
|
197
|
+
value_type: "image",
|
|
198
|
+
image: true
|
|
199
|
+
},
|
|
200
|
+
logo_dark: {
|
|
201
|
+
container: "contact",
|
|
202
|
+
value_type: "image",
|
|
203
|
+
image: true
|
|
204
|
+
},
|
|
205
|
+
logo_light: {
|
|
206
|
+
container: "contact",
|
|
207
|
+
value_type: "image",
|
|
208
|
+
image: true
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
const COLLECTIONS = {
|
|
212
|
+
units: {
|
|
213
|
+
idKey: "unit_id",
|
|
214
|
+
ownership: "property"
|
|
215
|
+
},
|
|
216
|
+
contacts: {
|
|
217
|
+
idKey: "id",
|
|
218
|
+
cap: 12,
|
|
219
|
+
min: 1,
|
|
220
|
+
ownership: "library"
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
function collectionForSlot(slot) {
|
|
224
|
+
if (slot.type !== "object_list") return null;
|
|
225
|
+
if (slot.source?.kind !== "direct") return null;
|
|
226
|
+
const field = slot.source.field;
|
|
227
|
+
return field !== void 0 && Object.hasOwn(COLLECTIONS, field) ? field : null;
|
|
228
|
+
}
|
|
229
|
+
const DERIVED_INPUT_FIELDS = [
|
|
230
|
+
"units",
|
|
231
|
+
"address_line_parts",
|
|
232
|
+
"contact_address_parts",
|
|
233
|
+
"coordinates",
|
|
234
|
+
"clock"
|
|
235
|
+
];
|
|
236
|
+
const DECISION_TYPES = [
|
|
237
|
+
"image-assign",
|
|
238
|
+
"image-collection",
|
|
239
|
+
"text-block",
|
|
240
|
+
"text-seed",
|
|
241
|
+
"list-extract",
|
|
242
|
+
"structured-list"
|
|
243
|
+
];
|
|
244
|
+
const FORMATTER_NAMES = ["currency", "number"];
|
|
245
|
+
|
|
246
|
+
//#endregion
|
|
247
|
+
export { COLLECTIONS, CONTENT_SLOT_TYPES, DECISION_TYPES, DERIVED_INPUT_FIELDS, FORMATTER_NAMES, REFERRAL_FIELDS, REFERRAL_IMAGE_FIELDS, REF_SLOT_TYPES, SLOT_TYPES, SOURCE_FIELDS, collectionForSlot };
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { DerivedInputName } from "./slot-catalog.js";
|
|
2
|
+
//#region src/contracts/transforms.d.ts
|
|
3
|
+
type TransformUnit = {
|
|
4
|
+
beds: number | null;
|
|
5
|
+
baths: number | null;
|
|
6
|
+
sqft: number | null;
|
|
7
|
+
price: string | null;
|
|
8
|
+
position: number;
|
|
9
|
+
};
|
|
10
|
+
type AddressParts = {
|
|
11
|
+
line1: string | null;
|
|
12
|
+
line2: string | null;
|
|
13
|
+
};
|
|
14
|
+
type Coordinates = {
|
|
15
|
+
lat: number;
|
|
16
|
+
lng: number;
|
|
17
|
+
};
|
|
18
|
+
type TransformInput = {
|
|
19
|
+
units: ReadonlyArray<TransformUnit>;
|
|
20
|
+
address?: string | null;
|
|
21
|
+
address_line_parts?: AddressParts | null;
|
|
22
|
+
contact_address_parts?: AddressParts | null;
|
|
23
|
+
coordinates?: Coordinates | null;
|
|
24
|
+
year?: number;
|
|
25
|
+
};
|
|
26
|
+
type TransformFn = (input: TransformInput) => string | number | null;
|
|
27
|
+
type TransformDescriptor = {
|
|
28
|
+
inputs: readonly DerivedInputName[];
|
|
29
|
+
impl: TransformFn;
|
|
30
|
+
};
|
|
31
|
+
declare const TRANSFORMS: {
|
|
32
|
+
"units.bedsPrimary": {
|
|
33
|
+
inputs: "units"[];
|
|
34
|
+
impl: ({ units }: TransformInput) => number | null;
|
|
35
|
+
};
|
|
36
|
+
"units.bathsPrimary": {
|
|
37
|
+
inputs: "units"[];
|
|
38
|
+
impl: ({ units }: TransformInput) => number | null;
|
|
39
|
+
};
|
|
40
|
+
"units.sqftPrimary": {
|
|
41
|
+
inputs: "units"[];
|
|
42
|
+
impl: ({ units }: TransformInput) => number | null;
|
|
43
|
+
};
|
|
44
|
+
"units.pricePrimary": {
|
|
45
|
+
inputs: "units"[];
|
|
46
|
+
impl: ({ units }: TransformInput) => string | null;
|
|
47
|
+
};
|
|
48
|
+
"units.bedsRange": {
|
|
49
|
+
inputs: "units"[];
|
|
50
|
+
impl: ({ units }: TransformInput) => string | null;
|
|
51
|
+
};
|
|
52
|
+
"units.priceStart": {
|
|
53
|
+
inputs: "units"[];
|
|
54
|
+
impl: ({ units }: TransformInput) => string | null;
|
|
55
|
+
};
|
|
56
|
+
"units.bedsMin": {
|
|
57
|
+
inputs: "units"[];
|
|
58
|
+
impl: ({ units }: TransformInput) => string | null;
|
|
59
|
+
};
|
|
60
|
+
"units.bedsMax": {
|
|
61
|
+
inputs: "units"[];
|
|
62
|
+
impl: ({ units }: TransformInput) => string | null;
|
|
63
|
+
};
|
|
64
|
+
"units.bathsMin": {
|
|
65
|
+
inputs: "units"[];
|
|
66
|
+
impl: ({ units }: TransformInput) => string | null;
|
|
67
|
+
};
|
|
68
|
+
"units.bathsMax": {
|
|
69
|
+
inputs: "units"[];
|
|
70
|
+
impl: ({ units }: TransformInput) => string | null;
|
|
71
|
+
};
|
|
72
|
+
"units.sqftMax": {
|
|
73
|
+
inputs: "units"[];
|
|
74
|
+
impl: ({ units }: TransformInput) => string | null;
|
|
75
|
+
};
|
|
76
|
+
"address.line1": {
|
|
77
|
+
inputs: "address_line_parts"[];
|
|
78
|
+
impl: ({ address_line_parts: p }: TransformInput) => string | null;
|
|
79
|
+
};
|
|
80
|
+
"address.line2": {
|
|
81
|
+
inputs: "address_line_parts"[];
|
|
82
|
+
impl: ({ address_line_parts: p }: TransformInput) => string | null;
|
|
83
|
+
};
|
|
84
|
+
"contact_address.line1": {
|
|
85
|
+
inputs: "contact_address_parts"[];
|
|
86
|
+
impl: ({ contact_address_parts: p }: TransformInput) => string | null;
|
|
87
|
+
};
|
|
88
|
+
"contact_address.line2": {
|
|
89
|
+
inputs: "contact_address_parts"[];
|
|
90
|
+
impl: ({ contact_address_parts: p }: TransformInput) => string | null;
|
|
91
|
+
};
|
|
92
|
+
"clock.currentYear": {
|
|
93
|
+
inputs: "clock"[];
|
|
94
|
+
impl: () => number;
|
|
95
|
+
};
|
|
96
|
+
"coordinate.lat": {
|
|
97
|
+
inputs: "coordinates"[];
|
|
98
|
+
impl: ({ coordinates }: TransformInput) => number | null;
|
|
99
|
+
};
|
|
100
|
+
"coordinate.lng": {
|
|
101
|
+
inputs: "coordinates"[];
|
|
102
|
+
impl: ({ coordinates }: TransformInput) => number | null;
|
|
103
|
+
};
|
|
104
|
+
};
|
|
105
|
+
type TransformName = keyof typeof TRANSFORMS;
|
|
106
|
+
declare const TRANSFORM_NAMES: readonly TransformName[];
|
|
107
|
+
//#endregion
|
|
108
|
+
export { AddressParts, Coordinates, TRANSFORMS, TRANSFORM_NAMES, TransformDescriptor, TransformFn, TransformInput, TransformName, TransformUnit };
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
//#region src/contracts/transforms.ts
|
|
2
|
+
function primary(units) {
|
|
3
|
+
if (units.length === 0) return null;
|
|
4
|
+
return [...units].sort((a, b) => a.position - b.position)[0];
|
|
5
|
+
}
|
|
6
|
+
function digits(price) {
|
|
7
|
+
if (price == null) return null;
|
|
8
|
+
const s = price.trim().replace(/^\$/, "").replace(/,/g, "").trim();
|
|
9
|
+
return /^\d+$/.test(s) ? Number(s) : null;
|
|
10
|
+
}
|
|
11
|
+
function readYear() {
|
|
12
|
+
const fixed = process.env.TR_FIXED_YEAR;
|
|
13
|
+
if (fixed) {
|
|
14
|
+
const n = Number(fixed);
|
|
15
|
+
if (!Number.isInteger(n)) throw new Error(`TR_FIXED_YEAR not an integer: ${fixed}`);
|
|
16
|
+
return n;
|
|
17
|
+
}
|
|
18
|
+
return (/* @__PURE__ */ new Date()).getUTCFullYear();
|
|
19
|
+
}
|
|
20
|
+
const enUS = new Intl.NumberFormat("en-US");
|
|
21
|
+
function aggFmt(values, pick) {
|
|
22
|
+
if (values.length === 0) return null;
|
|
23
|
+
return enUS.format(pick === "max" ? Math.max(...values) : Math.min(...values));
|
|
24
|
+
}
|
|
25
|
+
function numVals(units, k) {
|
|
26
|
+
return units.map((u) => u[k]).filter((v) => v != null);
|
|
27
|
+
}
|
|
28
|
+
function firstUnitPriceText(units) {
|
|
29
|
+
for (const u of [...units].sort((a, b) => a.position - b.position)) if (u.price != null && u.price.trim() !== "" && digits(u.price) == null) return u.price;
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
const TRANSFORMS = {
|
|
33
|
+
"units.bedsPrimary": {
|
|
34
|
+
inputs: ["units"],
|
|
35
|
+
impl: ({ units }) => primary(units)?.beds ?? null
|
|
36
|
+
},
|
|
37
|
+
"units.bathsPrimary": {
|
|
38
|
+
inputs: ["units"],
|
|
39
|
+
impl: ({ units }) => primary(units)?.baths ?? null
|
|
40
|
+
},
|
|
41
|
+
"units.sqftPrimary": {
|
|
42
|
+
inputs: ["units"],
|
|
43
|
+
impl: ({ units }) => primary(units)?.sqft ?? null
|
|
44
|
+
},
|
|
45
|
+
"units.pricePrimary": {
|
|
46
|
+
inputs: ["units"],
|
|
47
|
+
impl: ({ units }) => primary(units)?.price ?? null
|
|
48
|
+
},
|
|
49
|
+
"units.bedsRange": {
|
|
50
|
+
inputs: ["units"],
|
|
51
|
+
impl: ({ units }) => {
|
|
52
|
+
const beds = units.map((u) => u.beds).filter((b) => b != null);
|
|
53
|
+
if (beds.length === 0) return null;
|
|
54
|
+
const lo = Math.min(...beds), hi = Math.max(...beds);
|
|
55
|
+
return lo === hi ? String(lo) : `${lo}–${hi}`;
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
"units.priceStart": {
|
|
59
|
+
inputs: ["units"],
|
|
60
|
+
impl: ({ units }) => {
|
|
61
|
+
const prices = units.map((u) => digits(u.price)).filter((n) => n != null);
|
|
62
|
+
return prices.length === 0 ? firstUnitPriceText(units) : String(Math.min(...prices));
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
"units.bedsMin": {
|
|
66
|
+
inputs: ["units"],
|
|
67
|
+
impl: ({ units }) => aggFmt(numVals(units, "beds"), "min")
|
|
68
|
+
},
|
|
69
|
+
"units.bedsMax": {
|
|
70
|
+
inputs: ["units"],
|
|
71
|
+
impl: ({ units }) => aggFmt(numVals(units, "beds"), "max")
|
|
72
|
+
},
|
|
73
|
+
"units.bathsMin": {
|
|
74
|
+
inputs: ["units"],
|
|
75
|
+
impl: ({ units }) => aggFmt(numVals(units, "baths"), "min")
|
|
76
|
+
},
|
|
77
|
+
"units.bathsMax": {
|
|
78
|
+
inputs: ["units"],
|
|
79
|
+
impl: ({ units }) => aggFmt(numVals(units, "baths"), "max")
|
|
80
|
+
},
|
|
81
|
+
"units.sqftMax": {
|
|
82
|
+
inputs: ["units"],
|
|
83
|
+
impl: ({ units }) => aggFmt(numVals(units, "sqft"), "max")
|
|
84
|
+
},
|
|
85
|
+
"address.line1": {
|
|
86
|
+
inputs: ["address_line_parts"],
|
|
87
|
+
impl: ({ address_line_parts: p }) => p?.line1 ?? null
|
|
88
|
+
},
|
|
89
|
+
"address.line2": {
|
|
90
|
+
inputs: ["address_line_parts"],
|
|
91
|
+
impl: ({ address_line_parts: p }) => p?.line2 ?? null
|
|
92
|
+
},
|
|
93
|
+
"contact_address.line1": {
|
|
94
|
+
inputs: ["contact_address_parts"],
|
|
95
|
+
impl: ({ contact_address_parts: p }) => p?.line1 ?? null
|
|
96
|
+
},
|
|
97
|
+
"contact_address.line2": {
|
|
98
|
+
inputs: ["contact_address_parts"],
|
|
99
|
+
impl: ({ contact_address_parts: p }) => p?.line2 ?? null
|
|
100
|
+
},
|
|
101
|
+
"clock.currentYear": {
|
|
102
|
+
inputs: ["clock"],
|
|
103
|
+
impl: () => readYear()
|
|
104
|
+
},
|
|
105
|
+
"coordinate.lat": {
|
|
106
|
+
inputs: ["coordinates"],
|
|
107
|
+
impl: ({ coordinates }) => coordinates?.lat ?? null
|
|
108
|
+
},
|
|
109
|
+
"coordinate.lng": {
|
|
110
|
+
inputs: ["coordinates"],
|
|
111
|
+
impl: ({ coordinates }) => coordinates?.lng ?? null
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
const TRANSFORM_NAMES = Object.keys(TRANSFORMS);
|
|
115
|
+
|
|
116
|
+
//#endregion
|
|
117
|
+
export { TRANSFORMS, TRANSFORM_NAMES };
|