@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.
Files changed (90) hide show
  1. package/CHANGELOG.md +182 -0
  2. package/README.md +71 -14
  3. package/dist/base.css +85 -0
  4. package/dist/browser.d.ts +26 -0
  5. package/dist/browser.js +55 -0
  6. package/dist/cli.js +1 -1
  7. package/dist/contracts/image-descriptor.d.ts +17 -0
  8. package/dist/contracts/markers.d.ts +20 -0
  9. package/dist/contracts/markers.js +21 -0
  10. package/dist/contracts/slot-catalog.d.ts +237 -0
  11. package/dist/contracts/slot-catalog.js +247 -0
  12. package/dist/contracts/transforms.d.ts +108 -0
  13. package/dist/contracts/transforms.js +117 -0
  14. package/dist/design-system/breakpoints.d.ts +5 -0
  15. package/dist/design-system/breakpoints.js +14 -0
  16. package/dist/design-system/theme.d.ts +307 -0
  17. package/dist/design-system/theme.js +95 -0
  18. package/dist/eslint/is-client-file.js +79 -0
  19. package/dist/eslint/rules/image-bare-needs-reason.js +38 -0
  20. package/dist/eslint/rules/no-client-directive-in-contract.js +26 -0
  21. package/dist/eslint/rules/no-client-runtime-in-server.js +99 -0
  22. package/dist/eslint/rules/no-css-import-from-render-path.js +23 -0
  23. package/dist/eslint/rules/no-hex.js +111 -0
  24. package/dist/eslint/rules/no-inline-style.js +23 -0
  25. package/dist/eslint/rules/no-nondeterminism.js +44 -0
  26. package/dist/eslint/rules/no-raw-element.js +32 -0
  27. package/dist/eslint/rules/props-from-schema.js +32 -0
  28. package/dist/eslint/rules/serializable-island-props.js +108 -0
  29. package/dist/eslint/rules/slot-marker-literal.js +33 -0
  30. package/dist/eslint.d.ts +4 -8
  31. package/dist/eslint.js +75 -11
  32. package/dist/fixtures/sample-property.d.ts +67 -0
  33. package/dist/fixtures/sample-property.js +707 -0
  34. package/dist/fixtures.d.ts +2 -0
  35. package/dist/fixtures.js +3 -0
  36. package/dist/index.d.ts +37 -13
  37. package/dist/index.js +34 -2
  38. package/dist/island-runtime.d.ts +30 -0
  39. package/dist/island-runtime.js +73 -0
  40. package/dist/islands/contract.d.ts +56 -0
  41. package/dist/islands/contract.js +71 -0
  42. package/dist/islands/marker.d.ts +16 -0
  43. package/dist/islands/marker.js +18 -0
  44. package/dist/package.js +5 -0
  45. package/dist/primitives/Image.d.ts +49 -0
  46. package/dist/primitives/Image.js +95 -0
  47. package/dist/primitives/Section.d.ts +22 -0
  48. package/dist/primitives/Section.js +26 -0
  49. package/dist/primitives/Slot.d.ts +50 -0
  50. package/dist/primitives/Slot.js +39 -0
  51. package/dist/primitives/SlotGroup.d.ts +38 -0
  52. package/dist/primitives/SlotGroup.js +24 -0
  53. package/dist/primitives/SlotItem.d.ts +43 -0
  54. package/dist/primitives/SlotItem.js +38 -0
  55. package/dist/primitives/index.d.ts +7 -0
  56. package/dist/primitives/picture-sources.d.ts +22 -0
  57. package/dist/primitives/picture-sources.js +52 -0
  58. package/dist/schema/fill-spec.d.ts +692 -0
  59. package/dist/schema/fill-spec.js +98 -0
  60. package/dist/schema/fixture-schema.d.ts +143 -0
  61. package/dist/schema/fixture-schema.js +113 -0
  62. package/dist/schema/index.d.ts +16 -0
  63. package/dist/schema/manifest.d.ts +527 -0
  64. package/dist/schema/manifest.js +73 -0
  65. package/dist/schema/property-facts.d.ts +44 -0
  66. package/dist/schema/resolve-variants.d.ts +11 -0
  67. package/dist/schema/resolve-variants.js +15 -0
  68. package/dist/schema/runtime-values.d.ts +45 -0
  69. package/dist/schema/section-nav.d.ts +7 -0
  70. package/dist/schema/section-props.d.ts +69 -0
  71. package/dist/schema/section-schema.d.ts +533 -0
  72. package/dist/schema/section-schema.js +72 -0
  73. package/dist/schema/slot-types.d.ts +117 -0
  74. package/dist/schema/slot-types.js +181 -0
  75. package/dist/schema/source.d.ts +13 -0
  76. package/dist/schema/source.js +12 -0
  77. package/dist/schema/synthesize-nullable.d.ts +18 -0
  78. package/dist/schema/synthesize-nullable.js +47 -0
  79. package/dist/styles.css +131 -9
  80. package/docs/INDEX.md +6 -4
  81. package/docs/eslint.md +90 -0
  82. package/docs/islands.md +150 -0
  83. package/docs/llms.txt +30 -3
  84. package/docs/primitives.md +214 -0
  85. package/docs/schema-system.md +240 -0
  86. package/docs/theme-and-css.md +179 -0
  87. package/package.json +30 -6
  88. package/tsconfig.base.json +16 -0
  89. package/tsconfig.json +2 -13
  90. 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 };
@@ -0,0 +1,5 @@
1
+ //#region src/design-system/breakpoints.d.ts
2
+ declare const VERIFICATION_WIDTHS: readonly [350, 390, 425, 512, 768, 1024, 1440, 1920];
3
+ type VerificationWidth = (typeof VERIFICATION_WIDTHS)[number];
4
+ //#endregion
5
+ export { VERIFICATION_WIDTHS, VerificationWidth };