@reforma/site 0.1.2 → 1.0.1

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.
@@ -12,6 +12,7 @@ type TrackOptions = {
12
12
  /**
13
13
  * Start first-party pageview collection for a published Reforma site.
14
14
  * Reads the project site config. Only the first call installs history hooks.
15
+ * In the Reforma editor's canvas it reports the address to the editor and counts nothing.
15
16
  */
16
17
  export declare function initAnalytics(config: {
17
18
  analyticsEnabled: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"analytics.d.ts","sourceRoot":"","sources":["../src/analytics.ts"],"names":[],"mappings":"AAAA,KAAK,gBAAgB,GAAG;IACpB,wDAAwD;IACxD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,kDAAkD;IAClD,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAChC,CAAC;AAEF,KAAK,YAAY,GAAG;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC,CAAC;AASF;;;GAGG;AACH,wBAAgB,aAAa,CACzB,MAAM,EAAE;IAAE,gBAAgB,EAAE,OAAO,CAAC;IAAC,iBAAiB,EAAE,MAAM,CAAC;IAAC,iBAAiB,EAAE,MAAM,CAAA;CAAE,EAC3F,OAAO,GAAE,gBAAqB,GAC/B,IAAI,CAqCN;AAED;;;GAGG;AACH,wBAAgB,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,IAAI,CA6DpE"}
1
+ {"version":3,"file":"analytics.d.ts","sourceRoot":"","sources":["../src/analytics.ts"],"names":[],"mappings":"AAAA,KAAK,gBAAgB,GAAG;IACpB,wDAAwD;IACxD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,kDAAkD;IAClD,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAChC,CAAC;AAEF,KAAK,YAAY,GAAG;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC,CAAC;AAcF;;;;GAIG;AACH,wBAAgB,aAAa,CACzB,MAAM,EAAE;IAAE,gBAAgB,EAAE,OAAO,CAAC;IAAC,iBAAiB,EAAE,MAAM,CAAC;IAAC,iBAAiB,EAAE,MAAM,CAAA;CAAE,EAC3F,OAAO,GAAE,gBAAqB,GAC/B,IAAI,CAoDN;AAED;;;GAGG;AACH,wBAAgB,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,IAAI,CA6DpE"}
package/dist/analytics.js CHANGED
@@ -1,3 +1,7 @@
1
+ /** The Reforma editor names its canvas iframe this; the site checks it because it cannot see the parent's origin. */
2
+ const EDITOR_FRAME_NAME = 'reforma-editor';
3
+ /** What the editor listens for to follow the site's address. */
4
+ const LOCATION_MESSAGE = 'reforma:location';
1
5
  let started = false;
2
6
  let enabled = false;
3
7
  let writeKey = '';
@@ -7,6 +11,7 @@ let unhook = null;
7
11
  /**
8
12
  * Start first-party pageview collection for a published Reforma site.
9
13
  * Reads the project site config. Only the first call installs history hooks.
14
+ * In the Reforma editor's canvas it reports the address to the editor and counts nothing.
10
15
  */
11
16
  export function initAnalytics(config, options = {}) {
12
17
  if (typeof window === 'undefined') {
@@ -22,6 +27,16 @@ export function initAnalytics(config, options = {}) {
22
27
  return;
23
28
  }
24
29
  started = true;
30
+ if (window.name === EDITOR_FRAME_NAME && window.parent !== window) {
31
+ enabled = false;
32
+ const report = () => {
33
+ // nosemgrep: javascript.browser.security.wildcard-postmessage-configuration.wildcard-postmessage-configuration -- only the page path, and the site cannot know the editor's origin
34
+ window.parent.postMessage({ type: LOCATION_MESSAGE, pathname: window.location.pathname }, '*');
35
+ };
36
+ report();
37
+ unhook = installHistoryHook(report);
38
+ return;
39
+ }
25
40
  enabled = config.analyticsEnabled;
26
41
  writeKey = config.analyticsWriteKey.trim();
27
42
  endpoint = config.analyticsEndpoint.trim();
package/dist/index.d.ts CHANGED
@@ -1,7 +1,5 @@
1
1
  export { initAnalytics, track } from './analytics.js';
2
2
  export { ErrorState } from './error-state.js';
3
3
  export { projectMetadata, projectRobots } from './metadata.js';
4
- export { SiteRoot, useMode } from './root.js';
5
4
  export type { FixErrorDetail } from './error-state.js';
6
- export type { Appearance, ModeAppearances, ModeDocument, SystemModes } from './root.js';
7
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC/D,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC9C,YAAY,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACvD,YAAY,EAAE,UAAU,EAAE,eAAe,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC/D,YAAY,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC"}
package/dist/index.js CHANGED
@@ -1,4 +1,3 @@
1
1
  export { initAnalytics, track } from './analytics.js';
2
2
  export { ErrorState } from './error-state.js';
3
3
  export { projectMetadata, projectRobots } from './metadata.js';
4
- export { SiteRoot, useMode } from './root.js';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@reforma/site",
3
- "version": "0.1.2",
4
- "description": "Visitor runtime for a Reforma site: mode, publish metadata, and the PostCSS url guard.",
3
+ "version": "1.0.1",
4
+ "description": "Visitor runtime for a Reforma site: publish metadata, analytics, and the PostCSS url guard.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
7
7
  "author": "Reforma, Inc. <dev@reforma.ai>",
package/dist/root.d.ts DELETED
@@ -1,34 +0,0 @@
1
- import { type ReactNode } from 'react';
2
- export type Appearance = 'light' | 'dark';
3
- export type ModeDocument = {
4
- default?: string | null;
5
- modifiers?: {
6
- name: string;
7
- contexts?: string[];
8
- }[];
9
- };
10
- export type ModeAppearances = Record<string, Appearance>;
11
- export type SystemModes = {
12
- light: string;
13
- dark: string;
14
- } | null;
15
- type ModeState = {
16
- mode: string;
17
- siteMode: string;
18
- preview: string | null;
19
- preference: string | null;
20
- appearance: Appearance;
21
- };
22
- type ModeContextValue = ModeState & {
23
- setMode: (mode: string | null) => void;
24
- };
25
- /** Visitor mode. Without `SiteRoot`, returns the default light appearance. */
26
- export declare function useMode(): ModeContextValue;
27
- export declare function SiteRoot({ children, modes, appearances, systemModes, }: {
28
- children: ReactNode;
29
- modes: ModeDocument;
30
- appearances?: ModeAppearances;
31
- systemModes?: SystemModes;
32
- }): import("react").JSX.Element;
33
- export {};
34
- //# sourceMappingURL=root.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"root.d.ts","sourceRoot":"","sources":["../src/root.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAgE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAErG,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,MAAM,CAAC;AAE1C,MAAM,MAAM,YAAY,GAAG;IACvB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,EAAE,CAAC;CACvD,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;AAEzD,MAAM,MAAM,WAAW,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC;AAEjE,KAAK,SAAS,GAAG;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,UAAU,CAAC;CAC1B,CAAC;AAEF,KAAK,gBAAgB,GAAG,SAAS,GAAG;IAAE,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAA;CAAE,CAAC;AAe/E,8EAA8E;AAC9E,wBAAgB,OAAO,qBAEtB;AAMD,wBAAgB,QAAQ,CAAC,EACrB,QAAQ,EACR,KAAK,EACL,WAAgC,EAChC,WAAkB,GACrB,EAAE;IACC,QAAQ,EAAE,SAAS,CAAC;IACpB,KAAK,EAAE,YAAY,CAAC;IACpB,WAAW,CAAC,EAAE,eAAe,CAAC;IAC9B,WAAW,CAAC,EAAE,WAAW,CAAC;CAC7B,+BA6FA"}
package/dist/root.js DELETED
@@ -1,109 +0,0 @@
1
- 'use client';
2
- import { jsx as _jsx } from "react/jsx-runtime";
3
- import { createContext, useContext, useLayoutEffect, useRef, useState } from 'react';
4
- const defaultAppearances = { light: 'light', dark: 'dark' };
5
- const detachedMode = {
6
- mode: '',
7
- siteMode: '',
8
- preview: null,
9
- preference: null,
10
- appearance: 'light',
11
- setMode: () => { },
12
- };
13
- const ModeContext = createContext(null);
14
- /** Visitor mode. Without `SiteRoot`, returns the default light appearance. */
15
- export function useMode() {
16
- return useContext(ModeContext) ?? detachedMode;
17
- }
18
- function modeNames(modes) {
19
- return modes.modifiers?.find(modifier => modifier.name === 'mode')?.contexts ?? [];
20
- }
21
- export function SiteRoot({ children, modes, appearances = defaultAppearances, systemModes = null, }) {
22
- const defaultMode = modes.default ?? '';
23
- const [modeState, setModeState] = useState({
24
- mode: defaultMode,
25
- siteMode: defaultMode,
26
- preview: null,
27
- preference: null,
28
- appearance: appearances[defaultMode] ?? 'light',
29
- });
30
- const latestState = useRef(null);
31
- const selectVisitorMode = useRef(null);
32
- // Hydrate the server default first, then apply visitor preferences without masking mismatches.
33
- useLayoutEffect(() => {
34
- const storageKey = 'project-mode';
35
- const names = modeNames(modes);
36
- const defaultMode = modes.default ?? '';
37
- const valid = (mode) => mode !== null && names.includes(mode) ? mode : null;
38
- const media = systemModes ? window.matchMedia('(prefers-color-scheme: dark)') : null;
39
- let preference = latestState.current?.preference ?? null;
40
- if (!latestState.current) {
41
- try {
42
- preference = localStorage.getItem(storageKey);
43
- }
44
- catch { /* Storage may be blocked; in-memory selection still works. */ }
45
- }
46
- preference = valid(preference);
47
- let preview = process.env.NODE_ENV !== 'production' ? valid(latestState.current?.preview ?? null) : null;
48
- const publishMode = () => {
49
- const systemMode = systemModes && media
50
- ? valid(systemModes[media.matches ? 'dark' : 'light'])
51
- : null;
52
- const siteMode = preference ?? systemMode ?? defaultMode;
53
- const mode = preview ?? siteMode;
54
- const appearance = appearances[mode] === 'dark' ? 'dark' : 'light';
55
- const next = { mode, siteMode, preview, preference, appearance };
56
- document.documentElement.setAttribute('data-theme', mode);
57
- document.documentElement.setAttribute('data-appearance', appearance);
58
- document.documentElement.style.colorScheme = appearance;
59
- latestState.current = next;
60
- setModeState(next);
61
- window.dispatchEvent(new CustomEvent('project:mode-change', { detail: next }));
62
- };
63
- const onStorage = (event) => {
64
- if (event.storageArea && event.storageArea !== window.localStorage)
65
- return;
66
- if (event.key !== storageKey && event.key !== null)
67
- return;
68
- preference = valid(event.newValue);
69
- publishMode();
70
- };
71
- selectVisitorMode.current = mode => {
72
- preference = valid(mode);
73
- try {
74
- if (preference === null)
75
- localStorage.removeItem(storageKey);
76
- else
77
- localStorage.setItem(storageKey, preference);
78
- }
79
- catch { /* Keep selection usable when persistence is unavailable. */ }
80
- publishMode();
81
- };
82
- const onPreview = (event) => {
83
- const request = event.detail;
84
- if (request.preview !== undefined) {
85
- if (request.preview !== null && !names.includes(request.preview)) {
86
- request.error = 'This mode is no longer available. Reload the project variables.';
87
- return;
88
- }
89
- preview = request.preview;
90
- publishMode();
91
- }
92
- if (latestState.current) {
93
- request.state = latestState.current;
94
- }
95
- };
96
- window.addEventListener('storage', onStorage);
97
- media?.addEventListener('change', publishMode);
98
- if (process.env.NODE_ENV !== 'production')
99
- window.addEventListener('reforma:token-mode', onPreview);
100
- publishMode();
101
- return () => {
102
- window.removeEventListener('storage', onStorage);
103
- selectVisitorMode.current = null;
104
- media?.removeEventListener('change', publishMode);
105
- window.removeEventListener('reforma:token-mode', onPreview);
106
- };
107
- }, [appearances, modes, systemModes]);
108
- return (_jsx(ModeContext, { value: { ...modeState, setMode: mode => selectVisitorMode.current?.(mode) }, children: children }));
109
- }