@escape-game-over/atlas 0.1.24 → 0.1.26

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 (50) hide show
  1. package/README.md +27 -44
  2. package/bin/use-project.mjs +18 -13
  3. package/docs/NOT-BUILT.md +1 -1
  4. package/docs/client-scripts.md +96 -193
  5. package/docs/rich-text.md +18 -20
  6. package/package.json +9 -15
  7. package/src/analytics/google.ts +6 -6
  8. package/src/analytics/index.ts +4 -3
  9. package/src/analytics/tags.ts +13 -58
  10. package/src/analytics/umami.ts +8 -8
  11. package/src/astro/AtlasElement.astro +26 -0
  12. package/src/astro/ConsentBanner.astro +25 -0
  13. package/src/astro/ConsentElement.astro +67 -0
  14. package/src/astro/Document.astro +44 -0
  15. package/src/astro/Image.astro +101 -0
  16. package/src/astro/MetaTags.astro +30 -40
  17. package/src/astro/RichText.astro +88 -0
  18. package/src/astro/Zoom.astro +61 -0
  19. package/src/astro/client.ts +18 -9
  20. package/src/astro/consent.ts +20 -0
  21. package/src/astro/dev-log.ts +8 -14
  22. package/src/astro/element.ts +76 -112
  23. package/src/astro/filters-view.ts +48 -64
  24. package/src/astro/filters.ts +42 -35
  25. package/src/astro/index.ts +2 -9
  26. package/src/astro/ref.ts +46 -0
  27. package/src/astro/site-routes.ts +9 -15
  28. package/src/config.ts +23 -36
  29. package/src/content/index.ts +1 -1
  30. package/src/content/marks.ts +13 -13
  31. package/src/content/rich.ts +26 -42
  32. package/src/hours.ts +48 -11
  33. package/src/i18n/define.ts +14 -74
  34. package/src/index.ts +40 -57
  35. package/src/meta/index.ts +7 -13
  36. package/src/meta/share-image.ts +2 -26
  37. package/src/meta/tag.ts +1 -45
  38. package/src/money.ts +161 -6
  39. package/src/project.ts +84 -73
  40. package/src/routes/define.ts +8 -44
  41. package/src/routes/resolve.ts +1 -1
  42. package/src/site/api.ts +7 -33
  43. package/src/site/create.ts +6 -10
  44. package/src/site/define.ts +120 -0
  45. package/src/site/index.ts +2 -5
  46. package/src/site/page.ts +4 -2
  47. package/src/sitemap.ts +2 -35
  48. package/src/warn.ts +16 -17
  49. package/src/astro/dom.ts +0 -35
  50. package/src/astro/markup.ts +0 -656
package/src/index.ts CHANGED
@@ -1,27 +1,19 @@
1
1
  /**
2
2
  * The public API. A consuming project should need nothing outside this module.
3
3
  *
4
- * The flow is four steps, each inferring its types from the values of the last:
4
+ * Everything starts from `defineSite`:
5
5
  *
6
6
  * ```ts
7
- * // 1. site.config.ts — which languages exist, how URLs are shaped
8
- * export default defineSiteConfig({ locales: {...}, defaultRouting: {...} })
9
- *
10
- * // 2. src/data — the base copy and routes
11
- * export const baseMessages = defineMessages(config, {...})
12
- * export const baseRoutes = defineRoutes(config, {...})
13
- *
14
- * // 3. projects/<name>/project.ts — one deployment's overlay
15
- * export default defineProject(config, baseMessages, baseRoutes, {...})
16
- *
17
- * // 4. src/site.ts — the API the pages use
18
- * export const site = createSite(config, baseMessages, baseRoutes, project)
7
+ * export const atlas = defineSite({ locales: {...}, defaultRouting: {...} }) // site.config.ts
8
+ * export const defaultMessages = atlas.messages({...}) // messages.ts
9
+ * export const defaultRoutes = atlas.routes({...}) // routes.ts
10
+ * export default atlas.project(defaultMessages, defaultRoutes, {...}) // projects/<name>/project.ts
11
+ * export const site = atlas.site(defaultMessages, defaultRoutes, project) // site.ts
19
12
  * ```
20
13
  */
21
14
 
22
15
  export {
23
16
  type AnalyticsSettings,
24
- type AnalyticsTag,
25
17
  type AnalyticsTags,
26
18
  CONSENT_UPDATE_GLOBAL,
27
19
  type ConsentDefaults,
@@ -32,14 +24,13 @@ export {
32
24
  type UmamiSettings,
33
25
  type UmamiTracker,
34
26
  } from "./analytics/index.ts";
35
- export {
36
- defineSiteConfig,
37
- type LanguageTag,
38
- type LocaleMeta,
39
- type LocalesOf,
40
- type ResolvedLocaleMeta,
41
- type RoutingConfig,
42
- type SiteConfigShape,
27
+ export type {
28
+ LanguageTag,
29
+ LocaleMeta,
30
+ LocalesOf,
31
+ ResolvedLocaleMeta,
32
+ RoutingConfig,
33
+ SiteConfigShape,
43
34
  } from "./config.ts";
44
35
  export {
45
36
  assertCoordinates,
@@ -100,13 +91,11 @@ export {
100
91
  type Weekday,
101
92
  type WeeklyHours,
102
93
  } from "./hours.ts";
103
- export {
104
- type BaseCatalog,
105
- type CatalogCovers,
106
- defineMessageOverrides,
107
- defineMessages,
108
- type MessageOverrides,
109
- type OverrideCatalog,
94
+ export type {
95
+ BaseCatalog,
96
+ CatalogCovers,
97
+ MessageOverrides,
98
+ OverrideCatalog,
110
99
  } from "./i18n/define.ts";
111
100
  export type {
112
101
  EntryPlaceholders,
@@ -171,10 +160,8 @@ export {
171
160
  type ArticleContent,
172
161
  buildMeta,
173
162
  buildNotFoundMeta,
174
- type ImageFormat,
175
163
  type MetaContent,
176
164
  type MetaTag,
177
- type OgImage,
178
165
  type OpenGraphType,
179
166
  type PageKind,
180
167
  type RobotsPolicy,
@@ -197,7 +184,7 @@ export {
197
184
  type TieredPrice,
198
185
  tierRange,
199
186
  } from "./money.ts";
200
- export { defineProject, type ProjectInput } from "./project.ts";
187
+ export type { ProjectInput } from "./project.ts";
201
188
  export {
202
189
  buildCloudflareRedirects,
203
190
  buildRedirects,
@@ -212,15 +199,14 @@ export {
212
199
  type ValidateRedirectTargets,
213
200
  } from "./redirects.ts";
214
201
  export type { RobotsConfig, RobotsGroup } from "./robots.ts";
215
- export {
216
- type ChangeFreq,
217
- defineRouteOverrides,
218
- defineRoutes,
219
- type EnabledRouteIdFor,
220
- type RouteData,
221
- type RouteOverride,
222
- type RouteRegistry,
223
- type RouteSitemap,
202
+ export type {
203
+ ChangeFreq,
204
+ EnabledRouteIdFor,
205
+ RouteData,
206
+ RouteOverride,
207
+ RouteOverrides,
208
+ RouteRegistry,
209
+ RouteSitemap,
224
210
  } from "./routes/define.ts";
225
211
  export {
226
212
  enableRoutes,
@@ -229,24 +215,21 @@ export {
229
215
  type FamilyOptions,
230
216
  familyGuard,
231
217
  type RouteIdsOf,
232
- routeFamily,
233
218
  } from "./routes/family.ts";
234
219
  export type { RouteEntry } from "./routes/resolve.ts";
235
- export {
236
- type Alternate,
237
- type Crumb,
238
- createSite,
239
- type LinkOptions,
240
- type LlmsOptions,
241
- type LocaleLink,
242
- type PageContent,
243
- type PageMeta,
244
- type PageProps,
245
- type PerRoute,
246
- type RouteIdOf,
247
- type Site,
248
- type StaticPath,
249
- type WhenEnabled,
220
+ export { defineSite } from "./site/define.ts";
221
+ export type {
222
+ Alternate,
223
+ Crumb,
224
+ LinkOptions,
225
+ LlmsOptions,
226
+ LocaleLink,
227
+ PageContent,
228
+ PageMeta,
229
+ PageProps,
230
+ PerRoute,
231
+ Site,
232
+ StaticPath,
250
233
  } from "./site/index.ts";
251
234
  export type { Sitemap, SitemapConfig } from "./sitemap.ts";
252
235
  export type { IsoDate, Letter, StringKeys } from "./types.ts";
package/src/meta/index.ts CHANGED
@@ -29,6 +29,7 @@ import {
29
29
  notFoundAnalytics,
30
30
  } from "../analytics/index.ts";
31
31
  import type { ResolvedLocaleMeta } from "../config.ts";
32
+ import type { ImageAsset } from "../image.ts";
32
33
  import {
33
34
  article,
34
35
  type BreadcrumbStep,
@@ -42,11 +43,10 @@ import { robotsContent, warnAboutRobots } from "./robots.ts";
42
43
  import {
43
44
  iconLinks,
44
45
  type OgImage,
45
- type SiteIcon,
46
46
  warnAboutShareImage,
47
47
  warnIfClipped,
48
48
  } from "./share-image.ts";
49
- import { asMetaTag, link, type MetaTag, meta, preamble } from "./tag.ts";
49
+ import { link, type MetaTag, meta, preamble } from "./tag.ts";
50
50
  import { type SiteVerification, verificationTags } from "./verification.ts";
51
51
 
52
52
  export type {
@@ -57,13 +57,7 @@ export type {
57
57
  ThemeColor,
58
58
  } from "./content.ts";
59
59
  export type { RobotsDirective, RobotsPolicy } from "./robots.ts";
60
- export {
61
- type ImageFormat,
62
- type OgImage,
63
- resolveShareImage,
64
- type ShareImage,
65
- type SiteIcon,
66
- } from "./share-image.ts";
60
+ export { resolveShareImage, type ShareImage } from "./share-image.ts";
67
61
  export type { MetaTag } from "./tag.ts";
68
62
  export type { SiteVerification } from "./verification.ts";
69
63
 
@@ -155,7 +149,7 @@ interface MetaInputDerived<L extends string> extends ChromeInput {
155
149
  */
156
150
  export interface ChromeInput {
157
151
  /** The site's square icon, used for every icon link. */
158
- readonly icon: SiteIcon;
152
+ readonly icon: ImageAsset;
159
153
  /**
160
154
  * Tints the browser UI — Android Chrome's bar, iOS Safari, an installed PWA.
161
155
  * Two values emit one tag per `prefers-color-scheme`.
@@ -250,8 +244,8 @@ export function buildMeta<L extends string>(input: MetaInput<L>): DocumentTags {
250
244
  // that must precede every tag reading them, and a tag that got there first
251
245
  // would silently apply the permissive implicit default instead.
252
246
  const analytics = analyticsScripts(input.analytics, input.canonical);
253
- for (const script of analytics.head) tags.push(asMetaTag(script));
254
- const bodyTags: MetaTag[] = analytics.body.map(asMetaTag);
247
+ tags.push(...analytics.head);
248
+ const bodyTags: MetaTag[] = [...analytics.body];
255
249
 
256
250
  if (input.llmsUrl !== undefined) {
257
251
  // Points a client at the file describing this page, per llmstxt.org.
@@ -473,7 +467,7 @@ export function buildNotFoundMeta(input: NotFoundInput): MetaTag[] {
473
467
  name: "robots",
474
468
  content: robotsContent({ index: false, follow: true }),
475
469
  }),
476
- ...notFoundAnalytics(input.analytics).map(asMetaTag),
470
+ ...notFoundAnalytics(input.analytics),
477
471
  ...chromeTags(input),
478
472
  ];
479
473
  }
@@ -3,14 +3,7 @@ import { absoluteUrl, type HttpsUrl } from "../url.ts";
3
3
  import { warn } from "../warn.ts";
4
4
  import { link, type MetaTag } from "./tag.ts";
5
5
 
6
- export type ImageFormat =
7
- | "png"
8
- | "jpg"
9
- | "jpeg"
10
- | "webp"
11
- | "avif"
12
- | "gif"
13
- | "svg";
6
+ type ImageFormat = "png" | "jpg" | "jpeg" | "webp" | "avif" | "gif" | "svg";
14
7
 
15
8
  const IMAGE_MIME: Readonly<Record<ImageFormat, string>> = {
16
9
  png: "image/png",
@@ -85,23 +78,6 @@ export interface ShareImage {
85
78
  readonly alt: string;
86
79
  }
87
80
 
88
- /**
89
- * The site's icon: one square asset, used for every icon link.
90
- *
91
- * Site-wide rather than per page, because search engines use one favicon per
92
- * hostname, read from the home page — a per-page icon changes the browser tab
93
- * and nothing else.
94
- *
95
- * One asset rather than a set, because one square PNG already covers every
96
- * consumer: browsers accept PNG for `rel="icon"`, and Apple scales it for
97
- * `apple-touch-icon`. A `/favicon.ico` in `public/` is still requested by
98
- * browsers on its own if you want the legacy fallback.
99
- *
100
- * Unlike `og:image`, the href stays as written — root-relative is correct for an
101
- * icon, and absolutising it would only hard-code the origin.
102
- */
103
- export type SiteIcon = ImageAsset;
104
-
105
81
  /** One page's resolved share image. */
106
82
  export interface OgImage {
107
83
  readonly url: HttpsUrl;
@@ -229,7 +205,7 @@ export function resolveShareImage(
229
205
  * already has the page, so root-relative is correct and making it absolute would
230
206
  * only hard-code the origin.
231
207
  */
232
- export function iconLinks(icon: SiteIcon): MetaTag[] {
208
+ export function iconLinks(icon: ImageAsset): MetaTag[] {
233
209
  const mime = imageMime(icon.format);
234
210
  const attrs = {
235
211
  href: icon.src,
package/src/meta/tag.ts CHANGED
@@ -1,11 +1,4 @@
1
- import type { AnalyticsTag } from "../analytics/index.ts";
2
-
3
- /**
4
- * A head tag as data, not markup.
5
- *
6
- * lib does not render HTML — that would tie it to one framework and to one
7
- * templating syntax. It describes the tags; the consumer spreads them.
8
- */
1
+ /** A head tag as data; `AtlasDocument` renders it. */
9
2
  export type MetaTag =
10
3
  | { readonly kind: "title"; readonly text: string }
11
4
  | {
@@ -68,43 +61,6 @@ export const link = (attrs: Readonly<Record<string, string>>): MetaTag => ({
68
61
  attrs,
69
62
  });
70
63
 
71
- /**
72
- * An analytics tag as a head tag. The two vocabularies overlap but are not the
73
- * same type: `analytics/` knows nothing of `MetaTag`, which is what keeps this
74
- * folder free to import it without a cycle.
75
- *
76
- * Every kind is named rather than left to a trailing `else`, so that adding one
77
- * to `AnalyticsTag` and forgetting it here is a *compile* error and not a tag
78
- * that quietly disappears: `script` narrows to `never` only while the list is
79
- * complete, and assigning anything else to `never` does not type-check.
80
- */
81
- export function asMetaTag(script: AnalyticsTag): MetaTag {
82
- if (script.kind === "inline") {
83
- return { kind: "script", content: script.content };
84
- }
85
- if (script.kind === "external") {
86
- return {
87
- kind: "externalScript",
88
- src: script.src,
89
- attrs: script.attributes,
90
- };
91
- }
92
- if (script.kind === "noscriptFrame") {
93
- return { kind: "noscriptFrame", src: script.src };
94
- }
95
- if (script.kind === "preconnect") {
96
- // A `link` rather than a kind of its own: the head vocabulary already
97
- // has the shape, and a preconnect is a link in every sense the renderer
98
- // cares about. See `AnalyticsTag` for why no `crossorigin`.
99
- return {
100
- kind: "link",
101
- attrs: { rel: "preconnect", href: script.origin },
102
- };
103
- }
104
- const unhandled: never = script;
105
- throw new Error(`Unhandled analytics tag: ${JSON.stringify(unhandled)}`);
106
- }
107
-
108
64
  const DEFAULT_VIEWPORT = "width=device-width, initial-scale=1";
109
65
 
110
66
  interface Preamble {
package/src/money.ts CHANGED
@@ -1,13 +1,168 @@
1
- import type { Letter } from "./types.ts";
2
-
3
1
  /**
4
2
  * An ISO 4217 currency code: `"EUR"`, `"RON"`, `"GBP"`.
5
3
  *
6
- * Three uppercase letters, which is what `priceCurrency` is read as. A symbol —
7
- * `€`, `lei` — is how a price is *printed* in one place for one audience, and
8
- * two currencies can share one symbol, so it identifies nothing.
4
+ * A code rather than a symbol — `€`, `lei` — because two currencies can share a
5
+ * symbol, and `priceCurrency` is read as a code.
6
+ *
7
+ * Circulating currencies from ISO 4217 List One (SIX, published 2026-09-17):
8
+ * fund, precious-metal, testing and accounting-unit codes left out.
9
9
  */
10
- export type CurrencyCode = `${Letter}${Letter}${Letter}`;
10
+ export type CurrencyCode =
11
+ | "AED"
12
+ | "AFN"
13
+ | "ALL"
14
+ | "AMD"
15
+ | "AOA"
16
+ | "ARS"
17
+ | "AUD"
18
+ | "AWG"
19
+ | "AZN"
20
+ | "BAM"
21
+ | "BBD"
22
+ | "BDT"
23
+ | "BHD"
24
+ | "BIF"
25
+ | "BMD"
26
+ | "BND"
27
+ | "BOB"
28
+ | "BRL"
29
+ | "BSD"
30
+ | "BTN"
31
+ | "BWP"
32
+ | "BYN"
33
+ | "BZD"
34
+ | "CAD"
35
+ | "CDF"
36
+ | "CHF"
37
+ | "CLP"
38
+ | "CNY"
39
+ | "COP"
40
+ | "CRC"
41
+ | "CUP"
42
+ | "CVE"
43
+ | "CZK"
44
+ | "DJF"
45
+ | "DKK"
46
+ | "DOP"
47
+ | "DZD"
48
+ | "EGP"
49
+ | "ERN"
50
+ | "ETB"
51
+ | "EUR"
52
+ | "FJD"
53
+ | "FKP"
54
+ | "GBP"
55
+ | "GEL"
56
+ | "GHS"
57
+ | "GIP"
58
+ | "GMD"
59
+ | "GNF"
60
+ | "GTQ"
61
+ | "GYD"
62
+ | "HKD"
63
+ | "HNL"
64
+ | "HTG"
65
+ | "HUF"
66
+ | "IDR"
67
+ | "ILS"
68
+ | "INR"
69
+ | "IQD"
70
+ | "IRR"
71
+ | "ISK"
72
+ | "JMD"
73
+ | "JOD"
74
+ | "JPY"
75
+ | "KES"
76
+ | "KGS"
77
+ | "KHR"
78
+ | "KMF"
79
+ | "KPW"
80
+ | "KRW"
81
+ | "KWD"
82
+ | "KYD"
83
+ | "KZT"
84
+ | "LAK"
85
+ | "LBP"
86
+ | "LKR"
87
+ | "LRD"
88
+ | "LSL"
89
+ | "LYD"
90
+ | "MAD"
91
+ | "MDL"
92
+ | "MGA"
93
+ | "MKD"
94
+ | "MMK"
95
+ | "MNT"
96
+ | "MOP"
97
+ | "MRU"
98
+ | "MUR"
99
+ | "MVR"
100
+ | "MWK"
101
+ | "MXN"
102
+ | "MYR"
103
+ | "MZN"
104
+ | "NAD"
105
+ | "NGN"
106
+ | "NIO"
107
+ | "NOK"
108
+ | "NPR"
109
+ | "NZD"
110
+ | "OMR"
111
+ | "PAB"
112
+ | "PEN"
113
+ | "PGK"
114
+ | "PHP"
115
+ | "PKR"
116
+ | "PLN"
117
+ | "PYG"
118
+ | "QAR"
119
+ | "RON"
120
+ | "RSD"
121
+ | "RUB"
122
+ | "RWF"
123
+ | "SAR"
124
+ | "SBD"
125
+ | "SCR"
126
+ | "SDG"
127
+ | "SEK"
128
+ | "SGD"
129
+ | "SHP"
130
+ | "SLE"
131
+ | "SOS"
132
+ | "SRD"
133
+ | "SSP"
134
+ | "STN"
135
+ | "SVC"
136
+ | "SYP"
137
+ | "SZL"
138
+ | "THB"
139
+ | "TJS"
140
+ | "TMT"
141
+ | "TND"
142
+ | "TOP"
143
+ | "TRY"
144
+ | "TTD"
145
+ | "TWD"
146
+ | "TZS"
147
+ | "UAH"
148
+ | "UGX"
149
+ | "USD"
150
+ | "UYU"
151
+ | "UZS"
152
+ | "VED"
153
+ | "VES"
154
+ | "VND"
155
+ | "VUV"
156
+ | "WST"
157
+ | "XAF"
158
+ | "XCD"
159
+ | "XCG"
160
+ | "XOF"
161
+ | "XPF"
162
+ | "YER"
163
+ | "ZAR"
164
+ | "ZMW"
165
+ | "ZWG";
11
166
 
12
167
  /**
13
168
  * An amount and what it is denominated in, kept together.