@valbuild/shared 0.148.2 → 0.148.3

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 CHANGED
@@ -1,5 +1,48 @@
1
1
  # @valbuild/shared
2
2
 
3
+ ## 0.148.3
4
+
5
+ ### Patch Changes
6
+
7
+ - [#881](https://github.com/valbuild/val/pull/881) [`0139436`](https://github.com/valbuild/val/commit/0139436426142d91cbb29ec6c696dfb86f34ab9d) Thanks [@freekh](https://github.com/freekh)! - The content analytics script. With analytics on (it is off by default, and turned on in a hosted project's settings), a TanStack site counts which content each visitor's page **rendered** and which they actually **viewed** (at least half on screen for a second), and sends it to `/api/val/analytics`.
8
+
9
+ - No cookies, no storage, no visitor IDs. Each page view also says where it came from (the referring site's host and any `utm_source` / `utm_medium` / `utm_campaign`) and whether it was a phone, tablet or desktop.
10
+ - A page is sent as its route's pattern (`/posts/$postId`), never its address, so a path that holds something about one visitor (an order number, a token) is never stored. `startValAnalytics` takes this as `routeFor`; `ValProvider` passes TanStack's matched route.
11
+ - Editors are never counted: nothing is sent in draft mode, in the Studio, with Val enabled, or at a proposal's address, and nothing from automated browsers. A page the browser prerenders counts nothing until it is shown.
12
+ - The script is the new `@valbuild/react/analytics` entry, loaded only for visitors of a site with analytics on, after the page has hydrated. It imports nothing else from Val.
13
+ - Storing the counts and showing them is part of Val's hosting, which takes these beacons before they reach the site. Anywhere else, `/api/val/analytics` checks each beacon and stores nothing; in local development, with `VAL_DEBUG=1`, it logs a line per beacon, so you can see it working.
14
+ - `ValProvider`'s Studio check now matches `/val` and what is below it only, not every route starting with those letters (`/values`). The same fix is in `@valbuild/next`.
15
+
16
+ - [#896](https://github.com/valbuild/val/pull/896) [`5eff8a0`](https://github.com/valbuild/val/commit/5eff8a055a4ef50d476797c28f9c3e50deb73c89) Thanks [@freekh](https://github.com/freekh)! - A demo's tour can now be written by the site: `<ValApp demo={{ tour }}>`, with each step as data (`ValDemoTourStep`), so what it says changes with the site rather than with a Val release.
17
+
18
+ It is drawn as a ring around what a step is about, with nothing dimmed and the Studio usable underneath, and a step can move on by itself once the visitor has done what it asks:
19
+
20
+ ```ts
21
+ const tour: ValDemoTourStep[] = [
22
+ {
23
+ id: "click",
24
+ title: "This is Val",
25
+ body: "Click the heading on the page.",
26
+ target: "canvas",
27
+ page: HEADING, // outlined on the page
28
+ moveOn: { open: HEADING },
29
+ },
30
+ {
31
+ id: "type",
32
+ title: "Change a word",
33
+ body: "The page changes as you type.",
34
+ field: HEADING, // ringed wherever the field is shown
35
+ moveOn: {
36
+ edit: true,
37
+ path: HEADING,
38
+ value: "Coffee, roasted this weekend",
39
+ },
40
+ },
41
+ ];
42
+ ```
43
+
44
+ A step that moves on has **Do it for me**, which does it the way the Studio would, and **Skip**. On a phone the card is a sheet along the bottom. The Studio's own tour is unchanged.
45
+
3
46
  ## 0.148.2
4
47
 
5
48
  ### Patch Changes
@@ -45,6 +45,27 @@ type CookieValue = EnableCookieValue | {
45
45
  * this. Shared so client and server agree on the limit.
46
46
  */
47
47
  export declare const JSON_ENTRIES_BATCH_MAX = 100;
48
+ export declare const AnalyticsBeacon: z.ZodObject<{
49
+ v: z.ZodLiteral<1>;
50
+ route: z.ZodString;
51
+ cv: z.ZodOptional<z.ZodString>;
52
+ view: z.ZodOptional<z.ZodObject<{
53
+ ref: z.ZodOptional<z.ZodString>;
54
+ utm: z.ZodOptional<z.ZodObject<{
55
+ source: z.ZodOptional<z.ZodString>;
56
+ medium: z.ZodOptional<z.ZodString>;
57
+ campaign: z.ZodOptional<z.ZodString>;
58
+ }, z.core.$strip>>;
59
+ device: z.ZodEnum<{
60
+ phone: "phone";
61
+ tablet: "tablet";
62
+ desktop: "desktop";
63
+ }>;
64
+ }, z.core.$strip>>;
65
+ rendered: z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString>>;
66
+ viewed: z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString>>;
67
+ }, z.core.$strip>;
68
+ export type AnalyticsBeacon = z.infer<typeof AnalyticsBeacon>;
48
69
  export declare const Api: {
49
70
  "/draft/enable": {
50
71
  GET: {
@@ -98,6 +119,24 @@ export declare const Api: {
98
119
  }, z.core.$strip>]>;
99
120
  };
100
121
  };
122
+ "/analytics": {
123
+ POST: {
124
+ req: {
125
+ body: z.ZodUnknown;
126
+ };
127
+ res: z.ZodUnion<readonly [z.ZodObject<{
128
+ status: z.ZodLiteral<200>;
129
+ json: z.ZodObject<{
130
+ counted: z.ZodBoolean;
131
+ }, z.core.$strip>;
132
+ }, z.core.$strip>, z.ZodObject<{
133
+ status: z.ZodLiteral<400>;
134
+ json: z.ZodObject<{
135
+ message: z.ZodString;
136
+ }, z.core.$strip>;
137
+ }, z.core.$strip>]>;
138
+ };
139
+ };
101
140
  "/draft/stat": {
102
141
  GET: {
103
142
  req: {
@@ -23,6 +23,82 @@ export type ValDemoRuntime = {
23
23
  * refuses uploads before sending anything, and says why.
24
24
  */
25
25
  uploads: boolean;
26
+ /**
27
+ * The tour the site wrote, in place of the Studio's own demo tour. See
28
+ * `ValDemoTourStep`. Absent: the Studio's.
29
+ */
30
+ tour?: ValDemoTourStep[];
31
+ };
32
+ /**
33
+ * What the site starts the demo with: `<ValApp demo={{ tour }}>`.
34
+ *
35
+ * `demo` alone (`true`) is the same with nothing given.
36
+ */
37
+ export type ValDemoOptions = {
38
+ tour?: ValDemoTourStep[];
39
+ };
40
+ /**
41
+ * A control of the Studio's a step can point at.
42
+ *
43
+ * The `data-val-tour` markers the Studio puts on them. One that is not on
44
+ * screen (the rail on a phone) leaves the card on its own, so a step's words
45
+ * must work without the arrow: never "this button here".
46
+ */
47
+ export type ValDemoTourTarget = "canvas" | "pages" | "media" | "data" | "review" | "preview" | "publish" | "ai" | "locale" | "demo" | "utility";
48
+ /** A panel a step opens, so what it is about is on screen behind the card. */
49
+ export type ValDemoTourPanel = "pages" | "media" | "data" | "settings" | "ai";
50
+ /**
51
+ * What moves a step on by itself. A step without one has Next.
52
+ *
53
+ * Each is something the visitor does, because doing it is the lesson, and
54
+ * each has a "Do it for me" that does it the way the Studio would:
55
+ *
56
+ * - `open`: a field at or below this source path is opened -- by clicking it
57
+ * on the page, or anywhere else. Do it for me opens it.
58
+ * - `edit`: a change is made. Do it for me writes `value` at `path`; without
59
+ * them, it is offered nowhere and the step waits for the visitor.
60
+ * - `panel`: that panel is opened. Do it for me opens it.
61
+ * - `locale`: the language shown is changed. Do it for me picks the next one.
62
+ */
63
+ export type ValDemoTourMoveOn = {
64
+ open: string;
65
+ } | {
66
+ edit: true;
67
+ path?: string;
68
+ value?: string;
69
+ } | {
70
+ panel: ValDemoTourPanel;
71
+ } | {
72
+ locale: true;
73
+ };
74
+ /**
75
+ * One stop of the demo's tour, as the site writes it.
76
+ *
77
+ * Data rather than components, so the tour can be rewritten -- and tried on
78
+ * people -- without a Val release: the Studio draws it, the site says what it
79
+ * says. Written for someone who has never seen Val.
80
+ */
81
+ export type ValDemoTourStep = {
82
+ /** Stable, so a test can name a step without matching its words. */
83
+ id: string;
84
+ title: string;
85
+ body: string;
86
+ /** The control this step is about. */
87
+ target?: ValDemoTourTarget;
88
+ /**
89
+ * A field this step is about, as a source path: ringed wherever the Studio
90
+ * shows it -- the page's fields, or the module open in the editor. Takes
91
+ * precedence over `target`, which is what is ringed when the field is not
92
+ * on screen.
93
+ */
94
+ field?: string;
95
+ /**
96
+ * A place on the page this step is about, as a source path: the page
97
+ * outlines it and brings it into view.
98
+ */
99
+ page?: string;
100
+ panel?: ValDemoTourPanel;
101
+ moveOn?: ValDemoTourMoveOn;
26
102
  };
27
103
  declare global {
28
104
  var __VAL_DEMO__: ValDemoRuntime | undefined;
@@ -1782,6 +1782,48 @@ var onlyOneIntQueryParam = onlyOneStringQueryParam.refine(function (arg) {
1782
1782
  * this. Shared so client and server agree on the limit.
1783
1783
  */
1784
1784
  var JSON_ENTRIES_BATCH_MAX = 100;
1785
+
1786
+ /**
1787
+ * Content analytics, wire format version 1: the beacon the browser script
1788
+ * (`@valbuild/react/analytics`) sends to `POST /api/val/analytics`. Its
1789
+ * `AnalyticsBeacon` type is written by hand there, because that code is on
1790
+ * every visitor's page and must not load zod; `analytics/beacon.test.ts` in
1791
+ * `@valbuild/react` checks that what it builds parses here.
1792
+ */
1793
+ /*
1794
+ * The limits the browser script keeps to (`MAX_*` in
1795
+ * `@valbuild/react/analytics`'s `beacon.ts`, which cannot import this). The
1796
+ * endpoint takes anyone's POST, so it checks what it is sent rather than
1797
+ * trusting the sender; `beacon.test.ts` builds beacons at the limits and
1798
+ * parses them here.
1799
+ */
1800
+ var MAX_ANALYTICS_PATHS_PER_BEACON = 200;
1801
+ var AnalyticsPaths = zod.z.record(zod.z.string().max(512), zod.z.array(zod.z.string().max(1024)).max(MAX_ANALYTICS_PATHS_PER_BEACON));
1802
+ var countAnalyticsPaths = function countAnalyticsPaths(paths) {
1803
+ return Object.values(paths).reduce(function (n, list) {
1804
+ return n + list.length;
1805
+ }, 0);
1806
+ };
1807
+ var AnalyticsBeacon = zod.z.object({
1808
+ v: zod.z.literal(1),
1809
+ route: zod.z.string().max(512),
1810
+ cv: zod.z.string().max(128).optional(),
1811
+ view: zod.z.object({
1812
+ ref: zod.z.string().max(253).optional(),
1813
+ utm: zod.z.object({
1814
+ source: zod.z.string().max(100).optional(),
1815
+ medium: zod.z.string().max(100).optional(),
1816
+ campaign: zod.z.string().max(100).optional()
1817
+ }).optional(),
1818
+ device: zod.z["enum"](["phone", "tablet", "desktop"])
1819
+ }).optional(),
1820
+ rendered: AnalyticsPaths,
1821
+ viewed: AnalyticsPaths
1822
+ }).refine(function (beacon) {
1823
+ return countAnalyticsPaths(beacon.rendered) + countAnalyticsPaths(beacon.viewed) <= MAX_ANALYTICS_PATHS_PER_BEACON;
1824
+ }, {
1825
+ message: "At most ".concat(MAX_ANALYTICS_PATHS_PER_BEACON, " paths a beacon")
1826
+ });
1785
1827
  var Api = {
1786
1828
  "/draft/enable": {
1787
1829
  GET: {
@@ -1827,6 +1869,27 @@ var Api = {
1827
1869
  })])
1828
1870
  }
1829
1871
  },
1872
+ "/analytics": {
1873
+ POST: {
1874
+ // Parsed by the handler rather than here, so a beacon of a version this
1875
+ // server does not know is ignored instead of refused.
1876
+ req: {
1877
+ body: zod.z.unknown()
1878
+ },
1879
+ res: zod.z.union([
1880
+ // `counted` is false wherever nothing stores the counts -- every
1881
+ // server but the hosted platform's, for now.
1882
+ zod.z.object({
1883
+ status: zod.z.literal(200),
1884
+ json: zod.z.object({
1885
+ counted: zod.z["boolean"]()
1886
+ })
1887
+ }), zod.z.object({
1888
+ status: zod.z.literal(400),
1889
+ json: GenericError
1890
+ })])
1891
+ }
1892
+ },
1830
1893
  "/draft/stat": {
1831
1894
  GET: {
1832
1895
  req: {
@@ -4093,6 +4156,44 @@ function isRetryable(error) {
4093
4156
  * modules. See `docs/plans/demo-mode.md`.
4094
4157
  */
4095
4158
 
4159
+ /**
4160
+ * What the site starts the demo with: `<ValApp demo={{ tour }}>`.
4161
+ *
4162
+ * `demo` alone (`true`) is the same with nothing given.
4163
+ */
4164
+
4165
+ /**
4166
+ * A control of the Studio's a step can point at.
4167
+ *
4168
+ * The `data-val-tour` markers the Studio puts on them. One that is not on
4169
+ * screen (the rail on a phone) leaves the card on its own, so a step's words
4170
+ * must work without the arrow: never "this button here".
4171
+ */
4172
+
4173
+ /** A panel a step opens, so what it is about is on screen behind the card. */
4174
+
4175
+ /**
4176
+ * What moves a step on by itself. A step without one has Next.
4177
+ *
4178
+ * Each is something the visitor does, because doing it is the lesson, and
4179
+ * each has a "Do it for me" that does it the way the Studio would:
4180
+ *
4181
+ * - `open`: a field at or below this source path is opened -- by clicking it
4182
+ * on the page, or anywhere else. Do it for me opens it.
4183
+ * - `edit`: a change is made. Do it for me writes `value` at `path`; without
4184
+ * them, it is offered nowhere and the step waits for the visitor.
4185
+ * - `panel`: that panel is opened. Do it for me opens it.
4186
+ * - `locale`: the language shown is changed. Do it for me picks the next one.
4187
+ */
4188
+
4189
+ /**
4190
+ * One stop of the demo's tour, as the site writes it.
4191
+ *
4192
+ * Data rather than components, so the tour can be rewritten -- and tried on
4193
+ * people -- without a Val release: the Studio draws it, the site says what it
4194
+ * says. Written for someone who has never seen Val.
4195
+ */
4196
+
4096
4197
  /*
4097
4198
  * A global `var` rather than a `Window` member: this package is compiled
4098
4199
  * without the DOM's types, and a `var` in global scope is also a property of
@@ -8943,6 +9044,7 @@ exports.parseRoutePattern = getSitemapTree.parseRoutePattern;
8943
9044
  exports.withValCanvasParam = getSitemapTree.withValCanvasParam;
8944
9045
  exports.AA_LARGE = AA_LARGE;
8945
9046
  exports.AA_TEXT = AA_TEXT;
9047
+ exports.AnalyticsBeacon = AnalyticsBeacon;
8946
9048
  exports.Api = Api;
8947
9049
  exports.BRAND_CONTRAST_PAIRS = BRAND_CONTRAST_PAIRS;
8948
9050
  exports.BinaryFileRef = BinaryFileRef;
@@ -1782,6 +1782,48 @@ var onlyOneIntQueryParam = onlyOneStringQueryParam.refine(function (arg) {
1782
1782
  * this. Shared so client and server agree on the limit.
1783
1783
  */
1784
1784
  var JSON_ENTRIES_BATCH_MAX = 100;
1785
+
1786
+ /**
1787
+ * Content analytics, wire format version 1: the beacon the browser script
1788
+ * (`@valbuild/react/analytics`) sends to `POST /api/val/analytics`. Its
1789
+ * `AnalyticsBeacon` type is written by hand there, because that code is on
1790
+ * every visitor's page and must not load zod; `analytics/beacon.test.ts` in
1791
+ * `@valbuild/react` checks that what it builds parses here.
1792
+ */
1793
+ /*
1794
+ * The limits the browser script keeps to (`MAX_*` in
1795
+ * `@valbuild/react/analytics`'s `beacon.ts`, which cannot import this). The
1796
+ * endpoint takes anyone's POST, so it checks what it is sent rather than
1797
+ * trusting the sender; `beacon.test.ts` builds beacons at the limits and
1798
+ * parses them here.
1799
+ */
1800
+ var MAX_ANALYTICS_PATHS_PER_BEACON = 200;
1801
+ var AnalyticsPaths = zod.z.record(zod.z.string().max(512), zod.z.array(zod.z.string().max(1024)).max(MAX_ANALYTICS_PATHS_PER_BEACON));
1802
+ var countAnalyticsPaths = function countAnalyticsPaths(paths) {
1803
+ return Object.values(paths).reduce(function (n, list) {
1804
+ return n + list.length;
1805
+ }, 0);
1806
+ };
1807
+ var AnalyticsBeacon = zod.z.object({
1808
+ v: zod.z.literal(1),
1809
+ route: zod.z.string().max(512),
1810
+ cv: zod.z.string().max(128).optional(),
1811
+ view: zod.z.object({
1812
+ ref: zod.z.string().max(253).optional(),
1813
+ utm: zod.z.object({
1814
+ source: zod.z.string().max(100).optional(),
1815
+ medium: zod.z.string().max(100).optional(),
1816
+ campaign: zod.z.string().max(100).optional()
1817
+ }).optional(),
1818
+ device: zod.z["enum"](["phone", "tablet", "desktop"])
1819
+ }).optional(),
1820
+ rendered: AnalyticsPaths,
1821
+ viewed: AnalyticsPaths
1822
+ }).refine(function (beacon) {
1823
+ return countAnalyticsPaths(beacon.rendered) + countAnalyticsPaths(beacon.viewed) <= MAX_ANALYTICS_PATHS_PER_BEACON;
1824
+ }, {
1825
+ message: "At most ".concat(MAX_ANALYTICS_PATHS_PER_BEACON, " paths a beacon")
1826
+ });
1785
1827
  var Api = {
1786
1828
  "/draft/enable": {
1787
1829
  GET: {
@@ -1827,6 +1869,27 @@ var Api = {
1827
1869
  })])
1828
1870
  }
1829
1871
  },
1872
+ "/analytics": {
1873
+ POST: {
1874
+ // Parsed by the handler rather than here, so a beacon of a version this
1875
+ // server does not know is ignored instead of refused.
1876
+ req: {
1877
+ body: zod.z.unknown()
1878
+ },
1879
+ res: zod.z.union([
1880
+ // `counted` is false wherever nothing stores the counts -- every
1881
+ // server but the hosted platform's, for now.
1882
+ zod.z.object({
1883
+ status: zod.z.literal(200),
1884
+ json: zod.z.object({
1885
+ counted: zod.z["boolean"]()
1886
+ })
1887
+ }), zod.z.object({
1888
+ status: zod.z.literal(400),
1889
+ json: GenericError
1890
+ })])
1891
+ }
1892
+ },
1830
1893
  "/draft/stat": {
1831
1894
  GET: {
1832
1895
  req: {
@@ -4093,6 +4156,44 @@ function isRetryable(error) {
4093
4156
  * modules. See `docs/plans/demo-mode.md`.
4094
4157
  */
4095
4158
 
4159
+ /**
4160
+ * What the site starts the demo with: `<ValApp demo={{ tour }}>`.
4161
+ *
4162
+ * `demo` alone (`true`) is the same with nothing given.
4163
+ */
4164
+
4165
+ /**
4166
+ * A control of the Studio's a step can point at.
4167
+ *
4168
+ * The `data-val-tour` markers the Studio puts on them. One that is not on
4169
+ * screen (the rail on a phone) leaves the card on its own, so a step's words
4170
+ * must work without the arrow: never "this button here".
4171
+ */
4172
+
4173
+ /** A panel a step opens, so what it is about is on screen behind the card. */
4174
+
4175
+ /**
4176
+ * What moves a step on by itself. A step without one has Next.
4177
+ *
4178
+ * Each is something the visitor does, because doing it is the lesson, and
4179
+ * each has a "Do it for me" that does it the way the Studio would:
4180
+ *
4181
+ * - `open`: a field at or below this source path is opened -- by clicking it
4182
+ * on the page, or anywhere else. Do it for me opens it.
4183
+ * - `edit`: a change is made. Do it for me writes `value` at `path`; without
4184
+ * them, it is offered nowhere and the step waits for the visitor.
4185
+ * - `panel`: that panel is opened. Do it for me opens it.
4186
+ * - `locale`: the language shown is changed. Do it for me picks the next one.
4187
+ */
4188
+
4189
+ /**
4190
+ * One stop of the demo's tour, as the site writes it.
4191
+ *
4192
+ * Data rather than components, so the tour can be rewritten -- and tried on
4193
+ * people -- without a Val release: the Studio draws it, the site says what it
4194
+ * says. Written for someone who has never seen Val.
4195
+ */
4196
+
4096
4197
  /*
4097
4198
  * A global `var` rather than a `Window` member: this package is compiled
4098
4199
  * without the DOM's types, and a `var` in global scope is also a property of
@@ -8943,6 +9044,7 @@ exports.parseRoutePattern = getSitemapTree.parseRoutePattern;
8943
9044
  exports.withValCanvasParam = getSitemapTree.withValCanvasParam;
8944
9045
  exports.AA_LARGE = AA_LARGE;
8945
9046
  exports.AA_TEXT = AA_TEXT;
9047
+ exports.AnalyticsBeacon = AnalyticsBeacon;
8946
9048
  exports.Api = Api;
8947
9049
  exports.BRAND_CONTRAST_PAIRS = BRAND_CONTRAST_PAIRS;
8948
9050
  exports.BinaryFileRef = BinaryFileRef;
@@ -1775,6 +1775,48 @@ var onlyOneIntQueryParam = onlyOneStringQueryParam.refine(function (arg) {
1775
1775
  * this. Shared so client and server agree on the limit.
1776
1776
  */
1777
1777
  var JSON_ENTRIES_BATCH_MAX = 100;
1778
+
1779
+ /**
1780
+ * Content analytics, wire format version 1: the beacon the browser script
1781
+ * (`@valbuild/react/analytics`) sends to `POST /api/val/analytics`. Its
1782
+ * `AnalyticsBeacon` type is written by hand there, because that code is on
1783
+ * every visitor's page and must not load zod; `analytics/beacon.test.ts` in
1784
+ * `@valbuild/react` checks that what it builds parses here.
1785
+ */
1786
+ /*
1787
+ * The limits the browser script keeps to (`MAX_*` in
1788
+ * `@valbuild/react/analytics`'s `beacon.ts`, which cannot import this). The
1789
+ * endpoint takes anyone's POST, so it checks what it is sent rather than
1790
+ * trusting the sender; `beacon.test.ts` builds beacons at the limits and
1791
+ * parses them here.
1792
+ */
1793
+ var MAX_ANALYTICS_PATHS_PER_BEACON = 200;
1794
+ var AnalyticsPaths = z.record(z.string().max(512), z.array(z.string().max(1024)).max(MAX_ANALYTICS_PATHS_PER_BEACON));
1795
+ var countAnalyticsPaths = function countAnalyticsPaths(paths) {
1796
+ return Object.values(paths).reduce(function (n, list) {
1797
+ return n + list.length;
1798
+ }, 0);
1799
+ };
1800
+ var AnalyticsBeacon = z.object({
1801
+ v: z.literal(1),
1802
+ route: z.string().max(512),
1803
+ cv: z.string().max(128).optional(),
1804
+ view: z.object({
1805
+ ref: z.string().max(253).optional(),
1806
+ utm: z.object({
1807
+ source: z.string().max(100).optional(),
1808
+ medium: z.string().max(100).optional(),
1809
+ campaign: z.string().max(100).optional()
1810
+ }).optional(),
1811
+ device: z["enum"](["phone", "tablet", "desktop"])
1812
+ }).optional(),
1813
+ rendered: AnalyticsPaths,
1814
+ viewed: AnalyticsPaths
1815
+ }).refine(function (beacon) {
1816
+ return countAnalyticsPaths(beacon.rendered) + countAnalyticsPaths(beacon.viewed) <= MAX_ANALYTICS_PATHS_PER_BEACON;
1817
+ }, {
1818
+ message: "At most ".concat(MAX_ANALYTICS_PATHS_PER_BEACON, " paths a beacon")
1819
+ });
1778
1820
  var Api = {
1779
1821
  "/draft/enable": {
1780
1822
  GET: {
@@ -1820,6 +1862,27 @@ var Api = {
1820
1862
  })])
1821
1863
  }
1822
1864
  },
1865
+ "/analytics": {
1866
+ POST: {
1867
+ // Parsed by the handler rather than here, so a beacon of a version this
1868
+ // server does not know is ignored instead of refused.
1869
+ req: {
1870
+ body: z.unknown()
1871
+ },
1872
+ res: z.union([
1873
+ // `counted` is false wherever nothing stores the counts -- every
1874
+ // server but the hosted platform's, for now.
1875
+ z.object({
1876
+ status: z.literal(200),
1877
+ json: z.object({
1878
+ counted: z["boolean"]()
1879
+ })
1880
+ }), z.object({
1881
+ status: z.literal(400),
1882
+ json: GenericError
1883
+ })])
1884
+ }
1885
+ },
1823
1886
  "/draft/stat": {
1824
1887
  GET: {
1825
1888
  req: {
@@ -4086,6 +4149,44 @@ function isRetryable(error) {
4086
4149
  * modules. See `docs/plans/demo-mode.md`.
4087
4150
  */
4088
4151
 
4152
+ /**
4153
+ * What the site starts the demo with: `<ValApp demo={{ tour }}>`.
4154
+ *
4155
+ * `demo` alone (`true`) is the same with nothing given.
4156
+ */
4157
+
4158
+ /**
4159
+ * A control of the Studio's a step can point at.
4160
+ *
4161
+ * The `data-val-tour` markers the Studio puts on them. One that is not on
4162
+ * screen (the rail on a phone) leaves the card on its own, so a step's words
4163
+ * must work without the arrow: never "this button here".
4164
+ */
4165
+
4166
+ /** A panel a step opens, so what it is about is on screen behind the card. */
4167
+
4168
+ /**
4169
+ * What moves a step on by itself. A step without one has Next.
4170
+ *
4171
+ * Each is something the visitor does, because doing it is the lesson, and
4172
+ * each has a "Do it for me" that does it the way the Studio would:
4173
+ *
4174
+ * - `open`: a field at or below this source path is opened -- by clicking it
4175
+ * on the page, or anywhere else. Do it for me opens it.
4176
+ * - `edit`: a change is made. Do it for me writes `value` at `path`; without
4177
+ * them, it is offered nowhere and the step waits for the visitor.
4178
+ * - `panel`: that panel is opened. Do it for me opens it.
4179
+ * - `locale`: the language shown is changed. Do it for me picks the next one.
4180
+ */
4181
+
4182
+ /**
4183
+ * One stop of the demo's tour, as the site writes it.
4184
+ *
4185
+ * Data rather than components, so the tour can be rewritten -- and tried on
4186
+ * people -- without a Val release: the Studio draws it, the site says what it
4187
+ * says. Written for someone who has never seen Val.
4188
+ */
4189
+
4089
4190
  /*
4090
4191
  * A global `var` rather than a `Window` member: this package is compiled
4091
4192
  * without the DOM's types, and a `var` in global scope is also a property of
@@ -8915,4 +9016,4 @@ function schemaErrorsOf(schema, moduleFilePath, source) {
8915
9016
  return errors;
8916
9017
  }
8917
9018
 
8918
- export { AA_LARGE, AA_TEXT, Api, BRAND_CONTRAST_PAIRS, BinaryFileRef, CodeOptions, ColorOptions, DEFAULT_ERROR_MESSAGE, DateOptions, ENCODE_DEFAULTS, ENCODE_MIME_TYPE_OF, FileOptions, HistoricalCommit, HistoricalModule, HistoricalPatchSet, HistoryError, ImageEncodeOption, ImageOptions, JSONValue, JSON_ENTRIES_BATCH_MAX, ModuleComparison, NEVER_ENCODED, ParentRef$1 as ParentRef, Patch, PatchBlock, PatchId, PatchJSON, PatchSets, PlanRefusalError, PublishProtocolError, RAMP_STEPS, RemirrorBr, RemirrorBulletList, RemirrorHeading, RemirrorImage, RemirrorJSON, RemirrorLinkMark, RemirrorListItem, RemirrorOrderedList, RemirrorParagraph, RemirrorText, RemirrorTextMark, RestoreVerdict, RichTextOptions, SCHEMA_SOURCE_FIXES, STUDIO_FIXES, SerializedArraySchema, SerializedBooleanSchema, SerializedCodeSchema, SerializedColorSchema, SerializedDateSchema, SerializedDateTimeSchema, SerializedDiscriminatedUnionSchema, SerializedEnumSchema, SerializedFileSchema, SerializedImageSchema, SerializedKeyOfSchema, SerializedLiteralSchema, SerializedLocaleSchema, SerializedNumberSchema, SerializedObjectSchema, SerializedRecordSchema, SerializedRichTextSchema, SerializedRouteSchema, SerializedSchema, SerializedSettingsSchema, SerializedStringSchema, SerializedValViewSchema, SerializedVideoSchema, SharedValConfig, VAL_ENABLE_COOKIE_NAME, VAL_GREEN_RAMP, VAL_SESSION_COOKIE, VAL_STATE_COOKIE, VAL_STUDIO_HEADER, ValCommit, ValDeployment, VideoOptions, VideoStreamOption, WASM_ENCODED_MIME_TYPES, accentRamp, buildDuplicatePatch, buildEmptyAtPathPatch, buildFileRefValue, buildImageGalleryPatch, buildRemoveImageGalleryEntryPatch, buildSearchIndex, chainHeadOf, checkCompatibility, chooseEncoded, concatModulePath, contrastRatio, createRegExpFromPattern, createSearchIndex, createValClient, declaredLocales, describeContainerAtPath, emptyOf, enumLocaleTag, explainIncompatible, fallbackMimeType, filterBlockingValidationErrors, filterRoutesByPatterns, findSimilar, fitWithin, flattenRichText, forEachRichTextImage, getErrorMessageFromUnknownJson, getFilenameFromRef, getInlineImgInfo, getKeyOfRecordAt, getRefParts, getRoutesOf, getRoutesWithModulePaths, getSourceAt, getValDemo, indexModule, isInsidePatchSetPath, isLocaleSchema, isMimeTypeAccepted, isRemoteSchema, isRepairedOnSave, isSchemaSourceFixError, isSkippedSource, isStudioFix, joinSharedChanges, keyOfRecordPath, levenshtein, localeAt, localeMeantBy, memoizePerRequest, newestCommitSha, parseArtifacts, parseBuildTarget, parseCancel, parseDeclare, parseDiscard, parseJob, parseNewestCiRun, parsePress, parseProblems, parseProjectSource, parsePromote, parsePublishToken, parseRenew, parseRequestStatus, parseStatus, parseUpdateTarget, parseVerify, partitionValidationErrors, performSearch, relativeLuminance, remirrorToRichTextSource, removeModule, resolveEncodeSettings, resolvePairGround, resolveSchemaSourceFixForError, resolveSchemaSourceFixes, resolveSerializedSchemaAtPath, richTextImageSchema, richTextToRemirror, safeParsePatch, schemaErrorsOf, schemaTypesOfPath, selectionClosure, shouldEncodeWithWasm, sideFiles, sideSource, sourcePathOfChild, themeCustomProperties, toolNames, traverseSchemaSource, urlOf, validateRoutePatterns, withExtension };
9019
+ export { AA_LARGE, AA_TEXT, AnalyticsBeacon, Api, BRAND_CONTRAST_PAIRS, BinaryFileRef, CodeOptions, ColorOptions, DEFAULT_ERROR_MESSAGE, DateOptions, ENCODE_DEFAULTS, ENCODE_MIME_TYPE_OF, FileOptions, HistoricalCommit, HistoricalModule, HistoricalPatchSet, HistoryError, ImageEncodeOption, ImageOptions, JSONValue, JSON_ENTRIES_BATCH_MAX, ModuleComparison, NEVER_ENCODED, ParentRef$1 as ParentRef, Patch, PatchBlock, PatchId, PatchJSON, PatchSets, PlanRefusalError, PublishProtocolError, RAMP_STEPS, RemirrorBr, RemirrorBulletList, RemirrorHeading, RemirrorImage, RemirrorJSON, RemirrorLinkMark, RemirrorListItem, RemirrorOrderedList, RemirrorParagraph, RemirrorText, RemirrorTextMark, RestoreVerdict, RichTextOptions, SCHEMA_SOURCE_FIXES, STUDIO_FIXES, SerializedArraySchema, SerializedBooleanSchema, SerializedCodeSchema, SerializedColorSchema, SerializedDateSchema, SerializedDateTimeSchema, SerializedDiscriminatedUnionSchema, SerializedEnumSchema, SerializedFileSchema, SerializedImageSchema, SerializedKeyOfSchema, SerializedLiteralSchema, SerializedLocaleSchema, SerializedNumberSchema, SerializedObjectSchema, SerializedRecordSchema, SerializedRichTextSchema, SerializedRouteSchema, SerializedSchema, SerializedSettingsSchema, SerializedStringSchema, SerializedValViewSchema, SerializedVideoSchema, SharedValConfig, VAL_ENABLE_COOKIE_NAME, VAL_GREEN_RAMP, VAL_SESSION_COOKIE, VAL_STATE_COOKIE, VAL_STUDIO_HEADER, ValCommit, ValDeployment, VideoOptions, VideoStreamOption, WASM_ENCODED_MIME_TYPES, accentRamp, buildDuplicatePatch, buildEmptyAtPathPatch, buildFileRefValue, buildImageGalleryPatch, buildRemoveImageGalleryEntryPatch, buildSearchIndex, chainHeadOf, checkCompatibility, chooseEncoded, concatModulePath, contrastRatio, createRegExpFromPattern, createSearchIndex, createValClient, declaredLocales, describeContainerAtPath, emptyOf, enumLocaleTag, explainIncompatible, fallbackMimeType, filterBlockingValidationErrors, filterRoutesByPatterns, findSimilar, fitWithin, flattenRichText, forEachRichTextImage, getErrorMessageFromUnknownJson, getFilenameFromRef, getInlineImgInfo, getKeyOfRecordAt, getRefParts, getRoutesOf, getRoutesWithModulePaths, getSourceAt, getValDemo, indexModule, isInsidePatchSetPath, isLocaleSchema, isMimeTypeAccepted, isRemoteSchema, isRepairedOnSave, isSchemaSourceFixError, isSkippedSource, isStudioFix, joinSharedChanges, keyOfRecordPath, levenshtein, localeAt, localeMeantBy, memoizePerRequest, newestCommitSha, parseArtifacts, parseBuildTarget, parseCancel, parseDeclare, parseDiscard, parseJob, parseNewestCiRun, parsePress, parseProblems, parseProjectSource, parsePromote, parsePublishToken, parseRenew, parseRequestStatus, parseStatus, parseUpdateTarget, parseVerify, partitionValidationErrors, performSearch, relativeLuminance, remirrorToRichTextSource, removeModule, resolveEncodeSettings, resolvePairGround, resolveSchemaSourceFixForError, resolveSchemaSourceFixes, resolveSerializedSchemaAtPath, richTextImageSchema, richTextToRemirror, safeParsePatch, schemaErrorsOf, schemaTypesOfPath, selectionClosure, shouldEncodeWithWasm, sideFiles, sideSource, sourcePathOfChild, themeCustomProperties, toolNames, traverseSchemaSource, urlOf, validateRoutePatterns, withExtension };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@valbuild/shared",
3
- "version": "0.148.2",
3
+ "version": "0.148.3",
4
4
  "private": false,
5
5
  "description": "Val shared types and utilities",
6
6
  "repository": {