@sesamy/sesamy-js 1.3.0 → 1.3.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.
- package/.env +2 -0
- package/.env.production +2 -0
- package/.eslintignore +4 -0
- package/.eslintrc +18 -0
- package/.github/workflows/release.yml +23 -0
- package/.nvmrc +1 -0
- package/.prettierignore +4 -0
- package/.prettierrc +5 -0
- package/CHANGELOG.md +132 -0
- package/article.html +65 -0
- package/contracts.html +23 -0
- package/dts-bundle-generator.config.ts +11 -0
- package/entitlements.html +23 -0
- package/index.html +24 -0
- package/package.json +1 -5
- package/public/sesamy.png +0 -0
- package/renovate.json +4 -0
- package/src/SesamyContracts.ts +86 -0
- package/src/SesamyEntitlements.ts +85 -0
- package/src/app.ts +101 -0
- package/src/constants.ts +2 -0
- package/src/controllers/index.ts +1 -0
- package/src/controllers/paywall.ts +14 -0
- package/src/entitlementsStyle.css +147 -0
- package/src/events/ready.ts +12 -0
- package/src/index.ts +36 -0
- package/src/javascript-api.ts +84 -0
- package/src/services/analytics/element-tracker.ts +117 -0
- package/src/services/analytics/index.ts +234 -0
- package/src/services/analytics/listeners/index.ts +2 -0
- package/src/services/analytics/listeners/route.ts +9 -0
- package/src/services/analytics/listeners/scroll.ts +62 -0
- package/src/services/analytics/session-id.ts +11 -0
- package/src/services/analytics/types/analytics-activity-utils.d.ts +54 -0
- package/src/services/analytics/types/analytics-router-utils.d.ts +7 -0
- package/src/services/analytics/types/analytics-scroll-utils.d.ts +4 -0
- package/src/services/analytics/types/track-event.ts +70 -0
- package/src/services/auth/index.ts +74 -0
- package/src/services/sesamy/index.ts +160 -0
- package/src/state.ts +3 -0
- package/src/style.css +99 -0
- package/src/types/Bills.ts +12 -0
- package/src/types/Config.ts +11 -0
- package/src/types/Contracts.ts +12 -0
- package/src/types/Entitlement.ts +9 -0
- package/src/types/Events.ts +6 -0
- package/src/types/Tag.ts +16 -0
- package/src/vite-env.d.ts +1 -0
- package/tsconfig.json +23 -0
- package/vite.config.ts +43 -0
- package/vite.dev.config.ts +14 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/// <reference path="../types/analytics-scroll-utils.d.ts" />
|
|
2
|
+
import { onScrollChange } from '@analytics/scroll-utils';
|
|
3
|
+
import { TrackEvents } from '../types/track-event';
|
|
4
|
+
import { trackEvent } from '..';
|
|
5
|
+
|
|
6
|
+
export type ScrollEvent = {
|
|
7
|
+
direction: string;
|
|
8
|
+
range: number[];
|
|
9
|
+
scrollMax: number;
|
|
10
|
+
scrollMin: number;
|
|
11
|
+
trigger: number;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export function addScrollListener(itemSrc: string, publisherContentId: string) {
|
|
15
|
+
onScrollChange({
|
|
16
|
+
25: (scrollData: ScrollEvent) => {
|
|
17
|
+
trackEvent({
|
|
18
|
+
name: TrackEvents.Scroll,
|
|
19
|
+
scrollData: scrollData.direction,
|
|
20
|
+
percentage: scrollData.trigger,
|
|
21
|
+
itemSrc,
|
|
22
|
+
publisherContentId,
|
|
23
|
+
});
|
|
24
|
+
},
|
|
25
|
+
50: (scrollData: ScrollEvent) => {
|
|
26
|
+
trackEvent({
|
|
27
|
+
name: TrackEvents.Scroll,
|
|
28
|
+
scrollData: scrollData.direction,
|
|
29
|
+
percentage: scrollData.trigger,
|
|
30
|
+
itemSrc,
|
|
31
|
+
publisherContentId,
|
|
32
|
+
});
|
|
33
|
+
},
|
|
34
|
+
75: (scrollData: ScrollEvent) => {
|
|
35
|
+
trackEvent({
|
|
36
|
+
name: TrackEvents.Scroll,
|
|
37
|
+
scrollData: scrollData.direction,
|
|
38
|
+
percentage: scrollData.trigger,
|
|
39
|
+
itemSrc,
|
|
40
|
+
publisherContentId,
|
|
41
|
+
});
|
|
42
|
+
},
|
|
43
|
+
90: (scrollData: ScrollEvent) => {
|
|
44
|
+
trackEvent({
|
|
45
|
+
name: TrackEvents.Scroll,
|
|
46
|
+
scrollData: scrollData.direction,
|
|
47
|
+
percentage: scrollData.trigger,
|
|
48
|
+
itemSrc,
|
|
49
|
+
publisherContentId,
|
|
50
|
+
});
|
|
51
|
+
},
|
|
52
|
+
100: (scrollData: ScrollEvent) => {
|
|
53
|
+
trackEvent({
|
|
54
|
+
name: TrackEvents.Scroll,
|
|
55
|
+
scrollData: scrollData.direction,
|
|
56
|
+
percentage: scrollData.trigger,
|
|
57
|
+
itemSrc,
|
|
58
|
+
publisherContentId,
|
|
59
|
+
});
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
const SESSION_ID_KEY = 'sesamy_session_id';
|
|
2
|
+
|
|
3
|
+
export function getSessionId() {
|
|
4
|
+
let sessionId = sessionStorage.getItem(SESSION_ID_KEY);
|
|
5
|
+
|
|
6
|
+
if (!sessionId) {
|
|
7
|
+
sessionId = Math.random().toString(36).slice(2, 9);
|
|
8
|
+
sessionStorage.setItem(SESSION_ID_KEY, sessionId);
|
|
9
|
+
}
|
|
10
|
+
return sessionId;
|
|
11
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
interface OnDomActivityOptions {
|
|
2
|
+
throttle?: number;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
interface AnalyticsEvent {}
|
|
6
|
+
|
|
7
|
+
interface OnUserActivityOptions {
|
|
8
|
+
onIdle?: (elapsedTime: number, event?: AnalyticsEvent) => void;
|
|
9
|
+
onWakeUp?: (elapsedTime: number, event?: AnalyticsEvent) => void;
|
|
10
|
+
onHeartbeat?: (elapsedTime: number, event?: AnalyticsEvent) => void;
|
|
11
|
+
timeout?: number;
|
|
12
|
+
throttle?: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface UserActivityStatus {
|
|
16
|
+
disable: () => () => void;
|
|
17
|
+
getStatus: () => {
|
|
18
|
+
isIdle: boolean;
|
|
19
|
+
isDisabled: boolean;
|
|
20
|
+
active: number;
|
|
21
|
+
idle: number;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
declare module '@analytics/activity-utils' {
|
|
26
|
+
/**
|
|
27
|
+
* Function to handle user inactivity.
|
|
28
|
+
* @param onIdle Function to be called when the user is idle.
|
|
29
|
+
* @param opts Optional configuration object.
|
|
30
|
+
* @returns An object with methods to control and get the status of the listener.
|
|
31
|
+
*/
|
|
32
|
+
function onIdle(
|
|
33
|
+
onIdle: (elapsedTime: number, event?: AnalyticsEvent) => void,
|
|
34
|
+
opts?: OnUserActivityOptions,
|
|
35
|
+
): UserActivityStatus;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Function to handle user wake up from idle.
|
|
39
|
+
* @param onWakeUp Function to be called when the user wakes up from idle.
|
|
40
|
+
* @param opts Optional configuration object.
|
|
41
|
+
* @returns An object with methods to control and get the status of the listener.
|
|
42
|
+
*/
|
|
43
|
+
function onWakeUp(
|
|
44
|
+
onWakeUp: (elapsedTime: number, event?: AnalyticsEvent) => void,
|
|
45
|
+
opts?: OnUserActivityOptions,
|
|
46
|
+
): UserActivityStatus;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Function to handle user activity.
|
|
50
|
+
* @param options Configuration object for user activity.
|
|
51
|
+
* @returns An object with methods to control and get the status of the listener.
|
|
52
|
+
*/
|
|
53
|
+
function onUserActivity(options: OnUserActivityOptions): UserActivityStatus;
|
|
54
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
export enum TrackEvents {
|
|
2
|
+
ViewArticle = 'viewArticle',
|
|
3
|
+
ActiveDuration = 'activeDuration',
|
|
4
|
+
IdleDuration = 'idleDuration',
|
|
5
|
+
Scroll = 'scroll',
|
|
6
|
+
ChangeView = 'changeView',
|
|
7
|
+
AddToCart = 'addToCart',
|
|
8
|
+
ViewArticleListing = 'viewArticleListing',
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface ViewArticleListingProps {
|
|
12
|
+
name: TrackEvents.ViewArticleListing;
|
|
13
|
+
publisherContentId?: string;
|
|
14
|
+
itemSrc: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface AddToCartEventProps {
|
|
18
|
+
name: TrackEvents.AddToCart;
|
|
19
|
+
publisherContentId?: string;
|
|
20
|
+
itemSrc: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface ChangeViewEventProps {
|
|
24
|
+
name: TrackEvents.ChangeView;
|
|
25
|
+
view: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface ScrollEventProps {
|
|
29
|
+
name: TrackEvents.Scroll;
|
|
30
|
+
publisherContentId?: string;
|
|
31
|
+
itemSrc: string;
|
|
32
|
+
scrollData: string;
|
|
33
|
+
percentage: number;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface IdleDurationEventProps {
|
|
37
|
+
name: TrackEvents.IdleDuration;
|
|
38
|
+
duration: number;
|
|
39
|
+
publisherContentId?: string;
|
|
40
|
+
itemSrc: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
interface ActiveDurationEventProps {
|
|
44
|
+
name: TrackEvents.ActiveDuration;
|
|
45
|
+
duration: number;
|
|
46
|
+
publisherContentId?: string;
|
|
47
|
+
itemSrc: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
interface ViewArticleEventProps {
|
|
51
|
+
name: TrackEvents.ViewArticle;
|
|
52
|
+
itemSrc: string;
|
|
53
|
+
publisherContentId?: string;
|
|
54
|
+
state: 'public' | 'locked' | 'unlocked' | 'logged-in';
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
interface Flushing {
|
|
58
|
+
flushing?: boolean;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export type TrackEvent = Flushing &
|
|
62
|
+
(
|
|
63
|
+
| ViewArticleEventProps
|
|
64
|
+
| ActiveDurationEventProps
|
|
65
|
+
| IdleDurationEventProps
|
|
66
|
+
| ScrollEventProps
|
|
67
|
+
| ChangeViewEventProps
|
|
68
|
+
| AddToCartEventProps
|
|
69
|
+
| ViewArticleListingProps
|
|
70
|
+
);
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { Auth0Client, createAuth0Client } from '@auth0/auth0-spa-js';
|
|
2
|
+
import { BASE_URL_DOMAIN } from '../../constants';
|
|
3
|
+
import { triggerEvent } from '../../events/ready';
|
|
4
|
+
import { Events } from '../../types/Events';
|
|
5
|
+
|
|
6
|
+
let auth0Client: Auth0Client;
|
|
7
|
+
|
|
8
|
+
export async function init({ clientId }: { clientId: string }) {
|
|
9
|
+
auth0Client = await createAuth0Client({
|
|
10
|
+
domain: `token.${BASE_URL_DOMAIN}`,
|
|
11
|
+
clientId,
|
|
12
|
+
cacheLocation: 'localstorage',
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
if (window.location.search.includes('code=')) {
|
|
16
|
+
try {
|
|
17
|
+
await auth0Client.handleRedirectCallback();
|
|
18
|
+
const user = await auth0Client.getUser();
|
|
19
|
+
triggerEvent(Events.AUTHENTICATED, user);
|
|
20
|
+
window.history.replaceState({}, document.title, '/');
|
|
21
|
+
const accessToken = await auth0Client.getTokenSilently();
|
|
22
|
+
localStorage.setItem('accessToken', accessToken);
|
|
23
|
+
} catch (err) {
|
|
24
|
+
// Fail silently
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export async function isAuthenticated() {
|
|
30
|
+
if (!auth0Client) {
|
|
31
|
+
throw new Error('Auth0 client not initialized');
|
|
32
|
+
}
|
|
33
|
+
return auth0Client.isAuthenticated();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export async function getUser() {
|
|
37
|
+
if (!auth0Client) {
|
|
38
|
+
throw new Error('Auth0 client not initialized');
|
|
39
|
+
}
|
|
40
|
+
return auth0Client.getUser();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function loginWithRedirect() {
|
|
44
|
+
if (!auth0Client) {
|
|
45
|
+
throw new Error('Auth0 client not initialized');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return auth0Client.loginWithRedirect({
|
|
49
|
+
authorizationParams: {
|
|
50
|
+
redirect_uri: window.location.href,
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export async function getTokenSilently() {
|
|
56
|
+
if (!auth0Client) {
|
|
57
|
+
throw new Error('Auth0 client not initialized');
|
|
58
|
+
}
|
|
59
|
+
return auth0Client.getTokenSilently();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export async function logout() {
|
|
63
|
+
if (!auth0Client) {
|
|
64
|
+
throw new Error('Auth0 client not initialized');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
triggerEvent(Events.LOGOUT, {});
|
|
68
|
+
|
|
69
|
+
return auth0Client.logout({
|
|
70
|
+
logoutParams: {
|
|
71
|
+
returnTo: window.location.href,
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { dedupe, retry } from "wretch/middlewares";
|
|
2
|
+
import wretch, {
|
|
3
|
+
ConfiguredMiddleware,
|
|
4
|
+
WretchOptions,
|
|
5
|
+
WretchResponse,
|
|
6
|
+
} from "wretch";
|
|
7
|
+
import { BASE_URL_DOMAIN } from "../../constants";
|
|
8
|
+
import { getTokenSilently } from "../auth";
|
|
9
|
+
import { Entitlement } from "../../types/Entitlement";
|
|
10
|
+
import { Contract } from "../../types/Contracts";
|
|
11
|
+
import { Bill } from "../../types/Bills";
|
|
12
|
+
import { Tag, TagValue } from "../../types/Tag";
|
|
13
|
+
|
|
14
|
+
const auth0Middleware: ConfiguredMiddleware =
|
|
15
|
+
next =>
|
|
16
|
+
(url: string, options: WretchOptions): Promise<WretchResponse> => {
|
|
17
|
+
return new Promise(async (resolve, reject) => {
|
|
18
|
+
try {
|
|
19
|
+
const accessToken = await getTokenSilently();
|
|
20
|
+
|
|
21
|
+
const modifiedOptions = {
|
|
22
|
+
...options,
|
|
23
|
+
headers: {
|
|
24
|
+
...options.headers,
|
|
25
|
+
Authorization: `Bearer ${accessToken}`,
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
resolve(next(url, modifiedOptions));
|
|
30
|
+
} catch (error) {
|
|
31
|
+
console.error('Error fetching access token:', error);
|
|
32
|
+
reject(error);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const basicAuthMiddleware: ConfiguredMiddleware =
|
|
38
|
+
next =>
|
|
39
|
+
(url: string, options: WretchOptions): Promise<WretchResponse> => {
|
|
40
|
+
const anonymousId = localStorage.getItem('__anon_id');
|
|
41
|
+
const accessToken = btoa(`${anonymousId}:`);
|
|
42
|
+
|
|
43
|
+
const modifiedOptions = {
|
|
44
|
+
...options,
|
|
45
|
+
headers: {
|
|
46
|
+
...options.headers,
|
|
47
|
+
Authorization: `Basic ${accessToken}`,
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
return next(url, modifiedOptions);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const api = wretch(`https://api2.${BASE_URL_DOMAIN}`)
|
|
55
|
+
.headers({ 'Content-Type': 'application/json' })
|
|
56
|
+
.middlewares([
|
|
57
|
+
auth0Middleware,
|
|
58
|
+
dedupe(),
|
|
59
|
+
retry({
|
|
60
|
+
delayTimer: 1000,
|
|
61
|
+
delayRamp: (delay, nbOfAttempts) => delay * nbOfAttempts,
|
|
62
|
+
maxAttempts: 3,
|
|
63
|
+
until: response => response?.ok || false,
|
|
64
|
+
retryOnNetworkError: false,
|
|
65
|
+
}),
|
|
66
|
+
]);
|
|
67
|
+
|
|
68
|
+
const apiWithBasicAuth = wretch(`https://api2.${BASE_URL_DOMAIN}`)
|
|
69
|
+
.headers({ 'Content-Type': 'application/json' })
|
|
70
|
+
.middlewares([
|
|
71
|
+
basicAuthMiddleware,
|
|
72
|
+
dedupe(),
|
|
73
|
+
retry({
|
|
74
|
+
delayTimer: 1000,
|
|
75
|
+
delayRamp: (delay, nbOfAttempts) => delay * nbOfAttempts,
|
|
76
|
+
maxAttempts: 3,
|
|
77
|
+
until: response => response?.ok || false,
|
|
78
|
+
retryOnNetworkError: false,
|
|
79
|
+
}),
|
|
80
|
+
]);
|
|
81
|
+
|
|
82
|
+
export async function getEntitlements(): Promise<Entitlement[]> {
|
|
83
|
+
return api.get('/entitlements').json();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export async function getEntitlement(entitlementId: string) {
|
|
87
|
+
const entitlements: Entitlement[] = await api.get('/entitlements').json();
|
|
88
|
+
|
|
89
|
+
return entitlements?.find(entitlement => entitlement.id === entitlementId);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export async function getContracts(): Promise<Contract[]> {
|
|
93
|
+
return api.get('/contracts').json();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export async function getContract(contractId: string) {
|
|
97
|
+
const contracts: Contract[] = await api.get('/contracts').json();
|
|
98
|
+
|
|
99
|
+
return contracts?.find(contract => contract.id === contractId);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export async function getBills(): Promise<Bill[]> {
|
|
103
|
+
return api.get('/bills').json();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export async function getBill(billId: string) {
|
|
107
|
+
const bills: Bill[] = await api.get('/bills').json();
|
|
108
|
+
|
|
109
|
+
return bills?.find(bill => bill.id === billId);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export async function getTags() {
|
|
113
|
+
const tags: { [id: string]: TagValue }[] = await apiWithBasicAuth
|
|
114
|
+
.get("/tags")
|
|
115
|
+
.json();
|
|
116
|
+
|
|
117
|
+
return tags;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export async function setTag(id: string, value: string | number | (string | number)[]) {
|
|
121
|
+
const tags: { [id: string]: Tag }[] = await apiWithBasicAuth
|
|
122
|
+
.url(`/tags/${id}`)
|
|
123
|
+
.put({
|
|
124
|
+
value,
|
|
125
|
+
})
|
|
126
|
+
.json();
|
|
127
|
+
|
|
128
|
+
return tags;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export async function pushTag(id: string, value: string | number) {
|
|
132
|
+
const tags: { [id: string]: TagValue }[] = await apiWithBasicAuth
|
|
133
|
+
.url(`/tags/${id}/push`)
|
|
134
|
+
.put({
|
|
135
|
+
value,
|
|
136
|
+
})
|
|
137
|
+
.json();
|
|
138
|
+
|
|
139
|
+
return tags;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Deprecated. Use tags instead
|
|
143
|
+
export interface AccessArticleParams {
|
|
144
|
+
articleId: string;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export async function accessArticle(params: AccessArticleParams): Promise<{ status: string }> {
|
|
148
|
+
const userId = localStorage.getItem('__anon_id');
|
|
149
|
+
if (!userId) {
|
|
150
|
+
throw new Error('User id not found');
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return apiWithBasicAuth
|
|
154
|
+
.url('/articles/access')
|
|
155
|
+
.post({
|
|
156
|
+
articleId: params.articleId,
|
|
157
|
+
userId,
|
|
158
|
+
})
|
|
159
|
+
.json();
|
|
160
|
+
}
|
package/src/state.ts
ADDED
package/src/style.css
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
|
|
3
|
+
line-height: 1.5;
|
|
4
|
+
font-weight: 400;
|
|
5
|
+
|
|
6
|
+
color-scheme: light dark;
|
|
7
|
+
color: rgba(255, 255, 255, 0.87);
|
|
8
|
+
background-color: #242424;
|
|
9
|
+
|
|
10
|
+
font-synthesis: none;
|
|
11
|
+
text-rendering: optimizeLegibility;
|
|
12
|
+
-webkit-font-smoothing: antialiased;
|
|
13
|
+
-moz-osx-font-smoothing: grayscale;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
a {
|
|
17
|
+
font-weight: 500;
|
|
18
|
+
color: #646cff;
|
|
19
|
+
text-decoration: inherit;
|
|
20
|
+
}
|
|
21
|
+
a:hover {
|
|
22
|
+
color: #535bf2;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
body {
|
|
26
|
+
margin: 0;
|
|
27
|
+
display: flex;
|
|
28
|
+
place-items: center;
|
|
29
|
+
min-width: 320px;
|
|
30
|
+
min-height: 100vh;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
h1 {
|
|
34
|
+
font-size: 3.2em;
|
|
35
|
+
line-height: 1.1;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
sesamy-app {
|
|
39
|
+
max-width: 1280px;
|
|
40
|
+
margin: 0 auto;
|
|
41
|
+
padding: 2rem;
|
|
42
|
+
text-align: center;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.logo {
|
|
46
|
+
height: 6em;
|
|
47
|
+
padding: 1.5em;
|
|
48
|
+
will-change: filter;
|
|
49
|
+
transition: filter 300ms;
|
|
50
|
+
}
|
|
51
|
+
.logo:hover {
|
|
52
|
+
filter: drop-shadow(0 0 2em #646cffaa);
|
|
53
|
+
}
|
|
54
|
+
.logo.vanilla:hover {
|
|
55
|
+
filter: drop-shadow(0 0 2em #3178c6aa);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.card {
|
|
59
|
+
padding: 2em;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.read-the-docs {
|
|
63
|
+
color: #888;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
button {
|
|
67
|
+
border-radius: 8px;
|
|
68
|
+
border: 1px solid transparent;
|
|
69
|
+
padding: 0.6em 1.2em;
|
|
70
|
+
font-size: 1em;
|
|
71
|
+
font-weight: 500;
|
|
72
|
+
font-family: inherit;
|
|
73
|
+
background-color: #1a1a1a;
|
|
74
|
+
cursor: pointer;
|
|
75
|
+
transition: border-color 0.25s;
|
|
76
|
+
}
|
|
77
|
+
button:hover {
|
|
78
|
+
border-color: #646cff;
|
|
79
|
+
}
|
|
80
|
+
button:focus,
|
|
81
|
+
button:focus-visible {
|
|
82
|
+
outline: 4px auto -webkit-focus-ring-color;
|
|
83
|
+
}
|
|
84
|
+
.hidden {
|
|
85
|
+
display: none;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
@media (prefers-color-scheme: light) {
|
|
89
|
+
:root {
|
|
90
|
+
color: #213547;
|
|
91
|
+
background-color: #ffffff;
|
|
92
|
+
}
|
|
93
|
+
a:hover {
|
|
94
|
+
color: #747bff;
|
|
95
|
+
}
|
|
96
|
+
button {
|
|
97
|
+
background-color: #f9f9f9;
|
|
98
|
+
}
|
|
99
|
+
}
|
package/src/types/Tag.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export type TagValue = string | number | (string | number)[];
|
|
2
|
+
|
|
3
|
+
export type Tag = {
|
|
4
|
+
id: string;
|
|
5
|
+
value: TagValue;
|
|
6
|
+
policies?: {
|
|
7
|
+
// ttl in seconds
|
|
8
|
+
ttl?: number;
|
|
9
|
+
maxItems?: number;
|
|
10
|
+
unique?: boolean;
|
|
11
|
+
overflow?: "block" | "fifo";
|
|
12
|
+
ipSessionSharing?: boolean;
|
|
13
|
+
};
|
|
14
|
+
updatedAt: string;
|
|
15
|
+
createdAt: string;
|
|
16
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/// <reference types="vite/client" />
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"useDefineForClassFields": true,
|
|
5
|
+
"module": "ESNext",
|
|
6
|
+
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
|
7
|
+
|
|
8
|
+
/* Bundler mode */
|
|
9
|
+
"moduleResolution": "bundler",
|
|
10
|
+
"allowImportingTsExtensions": true,
|
|
11
|
+
"resolveJsonModule": true,
|
|
12
|
+
"isolatedModules": true,
|
|
13
|
+
"noEmit": true,
|
|
14
|
+
|
|
15
|
+
/* Linting */
|
|
16
|
+
"strict": true,
|
|
17
|
+
"noUnusedLocals": true,
|
|
18
|
+
"noUnusedParameters": true,
|
|
19
|
+
"noFallthroughCasesInSwitch": true,
|
|
20
|
+
"types": ["vite/client"]
|
|
21
|
+
},
|
|
22
|
+
"include": ["src"]
|
|
23
|
+
}
|
package/vite.config.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/// <reference types="vitest" />
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { defineConfig } from 'vite';
|
|
4
|
+
|
|
5
|
+
const getPackageName = () => {
|
|
6
|
+
return 'sesamy-js';
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const getPackageNameCamelCase = () => {
|
|
10
|
+
try {
|
|
11
|
+
return getPackageName().replace(/-./g, char => char[1].toUpperCase());
|
|
12
|
+
} catch (err) {
|
|
13
|
+
throw new Error('Name property in package.json is missing.');
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const fileName = {
|
|
18
|
+
es: `${getPackageName()}.mjs`,
|
|
19
|
+
cjs: `${getPackageName()}.cjs`,
|
|
20
|
+
iife: `${getPackageName()}.iife.js`,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const formats = Object.keys(fileName) as Array<keyof typeof fileName>;
|
|
24
|
+
|
|
25
|
+
module.exports = defineConfig({
|
|
26
|
+
base: './',
|
|
27
|
+
build: {
|
|
28
|
+
outDir: './build/dist',
|
|
29
|
+
lib: {
|
|
30
|
+
entry: path.resolve(__dirname, 'src/index.ts'),
|
|
31
|
+
name: getPackageNameCamelCase(),
|
|
32
|
+
formats,
|
|
33
|
+
fileName: format => fileName[format],
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
test: {},
|
|
37
|
+
resolve: {
|
|
38
|
+
alias: [
|
|
39
|
+
{ find: '@', replacement: path.resolve(__dirname, 'src') },
|
|
40
|
+
{ find: '@@', replacement: path.resolve(__dirname) },
|
|
41
|
+
],
|
|
42
|
+
},
|
|
43
|
+
});
|