@decocms/apps-website 7.2.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/package.json +48 -0
- package/src/client.ts +21 -0
- package/src/components/Analytics.tsx +146 -0
- package/src/components/OneDollarStats.test.ts +218 -0
- package/src/components/OneDollarStats.tsx +283 -0
- package/src/components/Seo.tsx +139 -0
- package/src/components/Theme.tsx +47 -0
- package/src/components/Video.tsx +44 -0
- package/src/index.ts +22 -0
- package/src/loaders/environment.test.ts +21 -0
- package/src/loaders/environment.ts +45 -0
- package/src/loaders/fonts/googleFonts.test.ts +62 -0
- package/src/loaders/fonts/googleFonts.ts +119 -0
- package/src/loaders/fonts/local.test.ts +75 -0
- package/src/loaders/fonts/local.ts +85 -0
- package/src/loaders/secret.test.ts +31 -0
- package/src/loaders/secret.ts +60 -0
- package/src/loaders/secretString.ts +18 -0
- package/src/mod.test.ts +45 -0
- package/src/mod.ts +154 -0
- package/src/sections/Analytics/Analytics.tsx +7 -0
- package/src/sections/Seo/Seo.tsx +14 -0
- package/src/sections/Seo/SeoV2.tsx +58 -0
- package/src/types.ts +125 -0
- package/src/utils/html.test.ts +24 -0
- package/src/utils/html.ts +1 -0
- package/src/utils/location.test.ts +40 -0
- package/src/utils/location.ts +20 -0
- package/src/utils/multivariate.ts +20 -0
- package/tsconfig.json +7 -0
package/src/types.ts
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core types for the website app.
|
|
3
|
+
*
|
|
4
|
+
* Defines the matcher/flag system types locally so we don't depend on
|
|
5
|
+
* @deco/deco/blocks — everything is self-contained in @decocms/apps.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// -------------------------------------------------------------------------
|
|
9
|
+
// Script
|
|
10
|
+
// -------------------------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
export type Script = { src: string | ((req: Request) => string) };
|
|
13
|
+
|
|
14
|
+
// -------------------------------------------------------------------------
|
|
15
|
+
// Matcher system
|
|
16
|
+
// -------------------------------------------------------------------------
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Context passed to matchers at request time.
|
|
20
|
+
* The framework populates this from the incoming request.
|
|
21
|
+
*/
|
|
22
|
+
export interface MatchContext {
|
|
23
|
+
request: Request;
|
|
24
|
+
device: "mobile" | "tablet" | "desktop";
|
|
25
|
+
siteId: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* A matcher is a function that evaluates request context and returns a boolean.
|
|
30
|
+
*/
|
|
31
|
+
export type Matcher = (ctx: MatchContext) => boolean;
|
|
32
|
+
|
|
33
|
+
// -------------------------------------------------------------------------
|
|
34
|
+
// Flag system
|
|
35
|
+
// -------------------------------------------------------------------------
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* A feature flag with a matcher and two branches.
|
|
39
|
+
* The framework evaluates the matcher at request time and selects the
|
|
40
|
+
* appropriate branch value.
|
|
41
|
+
*/
|
|
42
|
+
export interface FlagObj<T> {
|
|
43
|
+
matcher: Matcher;
|
|
44
|
+
true: T;
|
|
45
|
+
false: T;
|
|
46
|
+
name: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* A multivariate flag with multiple variants, each with its own matcher.
|
|
51
|
+
*/
|
|
52
|
+
export interface MultivariateFlag<T> {
|
|
53
|
+
variants: Variant<T>[];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* A single variant in a multivariate flag.
|
|
58
|
+
*/
|
|
59
|
+
export interface Variant<T> {
|
|
60
|
+
matcher?: Matcher;
|
|
61
|
+
value: T;
|
|
62
|
+
weight?: number;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// -------------------------------------------------------------------------
|
|
66
|
+
// Theme / Font types
|
|
67
|
+
// -------------------------------------------------------------------------
|
|
68
|
+
|
|
69
|
+
export interface Variable {
|
|
70
|
+
name: string;
|
|
71
|
+
value: string;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export type Font = {
|
|
75
|
+
family: string;
|
|
76
|
+
styleSheet: string;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// -------------------------------------------------------------------------
|
|
80
|
+
// SEO types
|
|
81
|
+
// -------------------------------------------------------------------------
|
|
82
|
+
|
|
83
|
+
/** @description Recommended: 1200 x 630 px (up to 5MB) */
|
|
84
|
+
export type ImageWidget = string;
|
|
85
|
+
|
|
86
|
+
export type OGType = "website" | "article";
|
|
87
|
+
|
|
88
|
+
export interface SeoConfig {
|
|
89
|
+
title?: string;
|
|
90
|
+
/**
|
|
91
|
+
* @title Title template
|
|
92
|
+
* @description add a %s whenever you want it to be replaced with the product name, category name or search term
|
|
93
|
+
* @default %s
|
|
94
|
+
*/
|
|
95
|
+
titleTemplate?: string;
|
|
96
|
+
description?: string;
|
|
97
|
+
/**
|
|
98
|
+
* @title Description template
|
|
99
|
+
* @description add a %s whenever you want it to be replaced with the product name, category name or search term
|
|
100
|
+
* @default %s
|
|
101
|
+
*/
|
|
102
|
+
descriptionTemplate?: string;
|
|
103
|
+
/** @default website */
|
|
104
|
+
type?: OGType;
|
|
105
|
+
/** @description Recommended: 1200 x 630 px (up to 5MB) */
|
|
106
|
+
image?: ImageWidget;
|
|
107
|
+
/** @description Recommended: 16 x 16 px */
|
|
108
|
+
favicon?: ImageWidget;
|
|
109
|
+
/** @description Suggested color that browsers should use to customize the display */
|
|
110
|
+
themeColor?: string;
|
|
111
|
+
/**
|
|
112
|
+
* @title Disable indexing
|
|
113
|
+
* @description In testing, you can use this to prevent search engines from indexing your site
|
|
114
|
+
*/
|
|
115
|
+
noIndexing?: boolean;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// -------------------------------------------------------------------------
|
|
119
|
+
// Website app config
|
|
120
|
+
// -------------------------------------------------------------------------
|
|
121
|
+
|
|
122
|
+
export interface WebsiteConfig {
|
|
123
|
+
/** @title Seo */
|
|
124
|
+
seo?: SeoConfig;
|
|
125
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { stripHTML } from "./html";
|
|
3
|
+
|
|
4
|
+
describe("stripHTML", () => {
|
|
5
|
+
it("strips HTML tags from string", () => {
|
|
6
|
+
expect(stripHTML("<p>Hello <strong>World</strong></p>")).toBe("Hello World");
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it("returns plain text unchanged", () => {
|
|
10
|
+
expect(stripHTML("no tags here")).toBe("no tags here");
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it("handles empty string", () => {
|
|
14
|
+
expect(stripHTML("")).toBe("");
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it("strips self-closing tags", () => {
|
|
18
|
+
expect(stripHTML("Hello<br/>World")).toBe("HelloWorld");
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("strips nested tags", () => {
|
|
22
|
+
expect(stripHTML("<div><p><span>text</span></p></div>")).toBe("text");
|
|
23
|
+
});
|
|
24
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const stripHTML = (str: string) => str.replace(/(<([^>]+)>)/gi, "");
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { haversine, toRadians } from "./location";
|
|
3
|
+
|
|
4
|
+
describe("toRadians", () => {
|
|
5
|
+
it("converts 0 degrees to 0 radians", () => {
|
|
6
|
+
expect(toRadians(0)).toBe(0);
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it("converts 180 degrees to PI radians", () => {
|
|
10
|
+
expect(toRadians(180)).toBeCloseTo(Math.PI);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it("converts 90 degrees to PI/2 radians", () => {
|
|
14
|
+
expect(toRadians(90)).toBeCloseTo(Math.PI / 2);
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
describe("haversine", () => {
|
|
19
|
+
it("returns 0 for same coordinates", () => {
|
|
20
|
+
expect(haversine("-23.5505,-46.6333", "-23.5505,-46.6333")).toBeCloseTo(0, 0);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("calculates distance between São Paulo and Rio (~360km)", () => {
|
|
24
|
+
const sp = "-23.5505,-46.6333";
|
|
25
|
+
const rj = "-22.9068,-43.1729";
|
|
26
|
+
const distance = haversine(sp, rj);
|
|
27
|
+
// ~360km
|
|
28
|
+
expect(distance).toBeGreaterThan(350000);
|
|
29
|
+
expect(distance).toBeLessThan(380000);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("calculates distance between New York and London (~5570km)", () => {
|
|
33
|
+
const ny = "40.7128,-74.0060";
|
|
34
|
+
const london = "51.5074,-0.1278";
|
|
35
|
+
const distance = haversine(ny, london);
|
|
36
|
+
// ~5570km
|
|
37
|
+
expect(distance).toBeGreaterThan(5500000);
|
|
38
|
+
expect(distance).toBeLessThan(5700000);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export const toRadians = (degrees: number): number => {
|
|
2
|
+
return degrees * (Math.PI / 180);
|
|
3
|
+
};
|
|
4
|
+
|
|
5
|
+
export const haversine = (coordinate1: string, coordinate2: string): number => {
|
|
6
|
+
const [lat1, lng1] = coordinate1.split(",").map((value) => Number(value));
|
|
7
|
+
const [lat2, lng2] = coordinate2.split(",").map((value) => Number(value));
|
|
8
|
+
|
|
9
|
+
const earthRadius = 6371000; // Earth radius in meters
|
|
10
|
+
const dLat = toRadians(lat1 - lat2);
|
|
11
|
+
const dLon = toRadians(lng1 - lng2);
|
|
12
|
+
|
|
13
|
+
const halfChordLengthSquared =
|
|
14
|
+
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
|
|
15
|
+
Math.sin(dLon / 2) * Math.sin(dLon / 2) * Math.cos(toRadians(lat1)) * Math.cos(toRadians(lat2));
|
|
16
|
+
|
|
17
|
+
const angularDistance =
|
|
18
|
+
2 * Math.atan2(Math.sqrt(halfChordLengthSquared), Math.sqrt(1 - halfChordLengthSquared));
|
|
19
|
+
return earthRadius * angularDistance;
|
|
20
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { MultivariateFlag, Variant } from "../types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @title Multivariate
|
|
5
|
+
*/
|
|
6
|
+
export interface MultivariateProps<T> {
|
|
7
|
+
/**
|
|
8
|
+
* @minItems 1
|
|
9
|
+
* @addBehavior 1
|
|
10
|
+
*/
|
|
11
|
+
variants: Variant<T>[];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @title Variant
|
|
16
|
+
* @label hidden
|
|
17
|
+
*/
|
|
18
|
+
export default function multivariate<T>(props: MultivariateProps<T>): MultivariateFlag<T> {
|
|
19
|
+
return props;
|
|
20
|
+
}
|