@scadable/privacy 0.1.0 → 0.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/dist/PolicyLive.cjs +85 -0
- package/dist/PolicyLive.d.cts +29 -0
- package/dist/PolicyLive.d.ts +29 -0
- package/dist/PolicyLive.js +63 -0
- package/dist/index.cjs +18 -11
- package/dist/index.d.cts +10 -7
- package/dist/index.d.ts +10 -7
- package/dist/index.js +16 -12
- package/package.json +2 -2
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
var React = require('react');
|
|
5
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
6
|
+
|
|
7
|
+
function _interopNamespace(e) {
|
|
8
|
+
if (e && e.__esModule) return e;
|
|
9
|
+
var n = Object.create(null);
|
|
10
|
+
if (e) {
|
|
11
|
+
Object.keys(e).forEach(function (k) {
|
|
12
|
+
if (k !== 'default') {
|
|
13
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
14
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
15
|
+
enumerable: true,
|
|
16
|
+
get: function () { return e[k]; }
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
n.default = e;
|
|
22
|
+
return Object.freeze(n);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
var React__namespace = /*#__PURE__*/_interopNamespace(React);
|
|
26
|
+
|
|
27
|
+
// src/client.ts
|
|
28
|
+
var DEFAULT_BASE_URL = "https://api.scadable.com";
|
|
29
|
+
async function fetchPolicy(token, options = {}) {
|
|
30
|
+
if (!token) {
|
|
31
|
+
throw new Error("@scadable/privacy: a policy token is required");
|
|
32
|
+
}
|
|
33
|
+
const baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, "");
|
|
34
|
+
const docType = options.docType ?? "privacy_policy";
|
|
35
|
+
const revalidate = options.revalidate ?? 3600;
|
|
36
|
+
const url = `${baseUrl}/policy/${encodeURIComponent(token)}?doc_type=${encodeURIComponent(docType)}&format=json`;
|
|
37
|
+
const init = revalidate === false ? { cache: "no-store" } : { next: { revalidate } };
|
|
38
|
+
const res = await fetch(url, init);
|
|
39
|
+
if (!res.ok) {
|
|
40
|
+
throw new Error(`@scadable/privacy: failed to load policy (${res.status}) for token "${token}"`);
|
|
41
|
+
}
|
|
42
|
+
return await res.json();
|
|
43
|
+
}
|
|
44
|
+
function PolicyLive({
|
|
45
|
+
token,
|
|
46
|
+
initialHtml,
|
|
47
|
+
initialVersion,
|
|
48
|
+
initialUpdatedAt,
|
|
49
|
+
className,
|
|
50
|
+
showVersion = false,
|
|
51
|
+
baseUrl,
|
|
52
|
+
docType
|
|
53
|
+
}) {
|
|
54
|
+
const [html, setHtml] = React__namespace.useState(initialHtml);
|
|
55
|
+
const [version, setVersion] = React__namespace.useState(initialVersion);
|
|
56
|
+
const [updatedAt, setUpdatedAt] = React__namespace.useState(initialUpdatedAt ?? null);
|
|
57
|
+
React__namespace.useEffect(() => {
|
|
58
|
+
let alive = true;
|
|
59
|
+
const opts = { revalidate: false };
|
|
60
|
+
if (baseUrl) opts.baseUrl = baseUrl;
|
|
61
|
+
if (docType) opts.docType = docType;
|
|
62
|
+
fetchPolicy(token, opts).then((p) => {
|
|
63
|
+
if (!alive || !p?.html) return;
|
|
64
|
+
setHtml(p.html);
|
|
65
|
+
setVersion(p.version);
|
|
66
|
+
setUpdatedAt(p.updated_at);
|
|
67
|
+
}).catch(() => {
|
|
68
|
+
});
|
|
69
|
+
return () => {
|
|
70
|
+
alive = false;
|
|
71
|
+
};
|
|
72
|
+
}, [token, baseUrl, docType]);
|
|
73
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, children: [
|
|
74
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { dangerouslySetInnerHTML: { __html: html } }),
|
|
75
|
+
showVersion && updatedAt ? /* @__PURE__ */ jsxRuntime.jsxs("p", { style: { fontSize: 12, color: "#9ca3af", marginTop: 24 }, children: [
|
|
76
|
+
"Version ",
|
|
77
|
+
version,
|
|
78
|
+
". Last updated ",
|
|
79
|
+
new Date(updatedAt).toLocaleDateString(),
|
|
80
|
+
"."
|
|
81
|
+
] }) : null
|
|
82
|
+
] });
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
exports.PolicyLive = PolicyLive;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
interface PolicyLiveProps {
|
|
4
|
+
/** The public token from the SCADABLE app. */
|
|
5
|
+
token: string;
|
|
6
|
+
/** HTML baked in at build / server-render time, so it is crawlable and paints instantly. */
|
|
7
|
+
initialHtml: string;
|
|
8
|
+
initialVersion?: number;
|
|
9
|
+
initialUpdatedAt?: string | null;
|
|
10
|
+
/** Optional wrapper class so you can style/position the policy. */
|
|
11
|
+
className?: string;
|
|
12
|
+
/** Show a small "Version N, last updated ..." line under the policy. Default false. */
|
|
13
|
+
showVersion?: boolean;
|
|
14
|
+
/** Override the API base (forwarded to the browser fetch). */
|
|
15
|
+
baseUrl?: string;
|
|
16
|
+
/** Which document to fetch (forwarded to the browser fetch). */
|
|
17
|
+
docType?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* The client half of <PrivacyPolicy>. It renders the build-time HTML immediately, so
|
|
21
|
+
* the legal text and the "by scadable.com" backlink are in the static HTML (crawlable
|
|
22
|
+
* for SEO, no layout shift), then re-fetches the live policy in the browser so edits
|
|
23
|
+
* made in SCADABLE show up with no redeploy on the customer's side. If the browser
|
|
24
|
+
* fetch is blocked (a strict Content-Security-Policy) or offline, it keeps the baked
|
|
25
|
+
* copy, so the page is never blank.
|
|
26
|
+
*/
|
|
27
|
+
declare function PolicyLive({ token, initialHtml, initialVersion, initialUpdatedAt, className, showVersion, baseUrl, docType, }: PolicyLiveProps): React.JSX.Element;
|
|
28
|
+
|
|
29
|
+
export { PolicyLive, type PolicyLiveProps };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
interface PolicyLiveProps {
|
|
4
|
+
/** The public token from the SCADABLE app. */
|
|
5
|
+
token: string;
|
|
6
|
+
/** HTML baked in at build / server-render time, so it is crawlable and paints instantly. */
|
|
7
|
+
initialHtml: string;
|
|
8
|
+
initialVersion?: number;
|
|
9
|
+
initialUpdatedAt?: string | null;
|
|
10
|
+
/** Optional wrapper class so you can style/position the policy. */
|
|
11
|
+
className?: string;
|
|
12
|
+
/** Show a small "Version N, last updated ..." line under the policy. Default false. */
|
|
13
|
+
showVersion?: boolean;
|
|
14
|
+
/** Override the API base (forwarded to the browser fetch). */
|
|
15
|
+
baseUrl?: string;
|
|
16
|
+
/** Which document to fetch (forwarded to the browser fetch). */
|
|
17
|
+
docType?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* The client half of <PrivacyPolicy>. It renders the build-time HTML immediately, so
|
|
21
|
+
* the legal text and the "by scadable.com" backlink are in the static HTML (crawlable
|
|
22
|
+
* for SEO, no layout shift), then re-fetches the live policy in the browser so edits
|
|
23
|
+
* made in SCADABLE show up with no redeploy on the customer's side. If the browser
|
|
24
|
+
* fetch is blocked (a strict Content-Security-Policy) or offline, it keeps the baked
|
|
25
|
+
* copy, so the page is never blank.
|
|
26
|
+
*/
|
|
27
|
+
declare function PolicyLive({ token, initialHtml, initialVersion, initialUpdatedAt, className, showVersion, baseUrl, docType, }: PolicyLiveProps): React.JSX.Element;
|
|
28
|
+
|
|
29
|
+
export { PolicyLive, type PolicyLiveProps };
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
4
|
+
|
|
5
|
+
// src/client.ts
|
|
6
|
+
var DEFAULT_BASE_URL = "https://api.scadable.com";
|
|
7
|
+
async function fetchPolicy(token, options = {}) {
|
|
8
|
+
if (!token) {
|
|
9
|
+
throw new Error("@scadable/privacy: a policy token is required");
|
|
10
|
+
}
|
|
11
|
+
const baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, "");
|
|
12
|
+
const docType = options.docType ?? "privacy_policy";
|
|
13
|
+
const revalidate = options.revalidate ?? 3600;
|
|
14
|
+
const url = `${baseUrl}/policy/${encodeURIComponent(token)}?doc_type=${encodeURIComponent(docType)}&format=json`;
|
|
15
|
+
const init = revalidate === false ? { cache: "no-store" } : { next: { revalidate } };
|
|
16
|
+
const res = await fetch(url, init);
|
|
17
|
+
if (!res.ok) {
|
|
18
|
+
throw new Error(`@scadable/privacy: failed to load policy (${res.status}) for token "${token}"`);
|
|
19
|
+
}
|
|
20
|
+
return await res.json();
|
|
21
|
+
}
|
|
22
|
+
function PolicyLive({
|
|
23
|
+
token,
|
|
24
|
+
initialHtml,
|
|
25
|
+
initialVersion,
|
|
26
|
+
initialUpdatedAt,
|
|
27
|
+
className,
|
|
28
|
+
showVersion = false,
|
|
29
|
+
baseUrl,
|
|
30
|
+
docType
|
|
31
|
+
}) {
|
|
32
|
+
const [html, setHtml] = React.useState(initialHtml);
|
|
33
|
+
const [version, setVersion] = React.useState(initialVersion);
|
|
34
|
+
const [updatedAt, setUpdatedAt] = React.useState(initialUpdatedAt ?? null);
|
|
35
|
+
React.useEffect(() => {
|
|
36
|
+
let alive = true;
|
|
37
|
+
const opts = { revalidate: false };
|
|
38
|
+
if (baseUrl) opts.baseUrl = baseUrl;
|
|
39
|
+
if (docType) opts.docType = docType;
|
|
40
|
+
fetchPolicy(token, opts).then((p) => {
|
|
41
|
+
if (!alive || !p?.html) return;
|
|
42
|
+
setHtml(p.html);
|
|
43
|
+
setVersion(p.version);
|
|
44
|
+
setUpdatedAt(p.updated_at);
|
|
45
|
+
}).catch(() => {
|
|
46
|
+
});
|
|
47
|
+
return () => {
|
|
48
|
+
alive = false;
|
|
49
|
+
};
|
|
50
|
+
}, [token, baseUrl, docType]);
|
|
51
|
+
return /* @__PURE__ */ jsxs("div", { className, children: [
|
|
52
|
+
/* @__PURE__ */ jsx("div", { dangerouslySetInnerHTML: { __html: html } }),
|
|
53
|
+
showVersion && updatedAt ? /* @__PURE__ */ jsxs("p", { style: { fontSize: 12, color: "#9ca3af", marginTop: 24 }, children: [
|
|
54
|
+
"Version ",
|
|
55
|
+
version,
|
|
56
|
+
". Last updated ",
|
|
57
|
+
new Date(updatedAt).toLocaleDateString(),
|
|
58
|
+
"."
|
|
59
|
+
] }) : null
|
|
60
|
+
] });
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export { PolicyLive };
|
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var PolicyLive = require('./PolicyLive.cjs');
|
|
3
4
|
var jsxRuntime = require('react/jsx-runtime');
|
|
4
5
|
|
|
5
6
|
// src/client.ts
|
|
@@ -26,19 +27,25 @@ async function PrivacyPolicy({
|
|
|
26
27
|
...options
|
|
27
28
|
}) {
|
|
28
29
|
const policy = await fetchPolicy(token, options);
|
|
29
|
-
return /* @__PURE__ */ jsxRuntime.
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
policy.
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
30
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
31
|
+
PolicyLive.PolicyLive,
|
|
32
|
+
{
|
|
33
|
+
token,
|
|
34
|
+
initialHtml: policy.html,
|
|
35
|
+
initialVersion: policy.version,
|
|
36
|
+
initialUpdatedAt: policy.updated_at,
|
|
37
|
+
className,
|
|
38
|
+
showVersion,
|
|
39
|
+
baseUrl: options.baseUrl,
|
|
40
|
+
docType: options.docType
|
|
41
|
+
}
|
|
42
|
+
);
|
|
40
43
|
}
|
|
41
44
|
|
|
45
|
+
Object.defineProperty(exports, "PolicyLive", {
|
|
46
|
+
enumerable: true,
|
|
47
|
+
get: function () { return PolicyLive.PolicyLive; }
|
|
48
|
+
});
|
|
42
49
|
exports.DEFAULT_BASE_URL = DEFAULT_BASE_URL;
|
|
43
50
|
exports.PrivacyPolicy = PrivacyPolicy;
|
|
44
51
|
exports.fetchPolicy = fetchPolicy;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
export { PolicyLive, PolicyLiveProps } from './PolicyLive';
|
|
2
3
|
|
|
3
4
|
interface Policy {
|
|
4
5
|
/** The name you gave the scope (your company / site). */
|
|
@@ -46,12 +47,14 @@ interface PrivacyPolicyProps extends FetchPolicyOptions {
|
|
|
46
47
|
showVersion?: boolean;
|
|
47
48
|
}
|
|
48
49
|
/**
|
|
49
|
-
*
|
|
50
|
+
* Renders your always-current privacy policy.
|
|
50
51
|
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
52
|
+
* Hybrid by design. The policy HTML is fetched at build / server-render time and baked
|
|
53
|
+
* into the static HTML, so the legal text and the "by scadable.com" backlink are
|
|
54
|
+
* crawlable for SEO and paint instantly. It is then re-fetched in the browser (see
|
|
55
|
+
* {@link PolicyLive}) so edits you make in SCADABLE go live with no redeploy on your
|
|
56
|
+
* side. If a strict Content-Security-Policy blocks the browser fetch, the baked copy
|
|
57
|
+
* stays put, so the page is never blank.
|
|
55
58
|
*
|
|
56
59
|
* ```tsx
|
|
57
60
|
* import { PrivacyPolicy } from '@scadable/privacy';
|
|
@@ -61,6 +64,6 @@ interface PrivacyPolicyProps extends FetchPolicyOptions {
|
|
|
61
64
|
* }
|
|
62
65
|
* ```
|
|
63
66
|
*/
|
|
64
|
-
declare function PrivacyPolicy({ token, className, showVersion, ...options }: PrivacyPolicyProps): Promise<
|
|
67
|
+
declare function PrivacyPolicy({ token, className, showVersion, ...options }: PrivacyPolicyProps): Promise<react.JSX.Element>;
|
|
65
68
|
|
|
66
69
|
export { DEFAULT_BASE_URL, type FetchPolicyOptions, type Policy, PrivacyPolicy, type PrivacyPolicyProps, fetchPolicy };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
export { PolicyLive, PolicyLiveProps } from './PolicyLive';
|
|
2
3
|
|
|
3
4
|
interface Policy {
|
|
4
5
|
/** The name you gave the scope (your company / site). */
|
|
@@ -46,12 +47,14 @@ interface PrivacyPolicyProps extends FetchPolicyOptions {
|
|
|
46
47
|
showVersion?: boolean;
|
|
47
48
|
}
|
|
48
49
|
/**
|
|
49
|
-
*
|
|
50
|
+
* Renders your always-current privacy policy.
|
|
50
51
|
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
52
|
+
* Hybrid by design. The policy HTML is fetched at build / server-render time and baked
|
|
53
|
+
* into the static HTML, so the legal text and the "by scadable.com" backlink are
|
|
54
|
+
* crawlable for SEO and paint instantly. It is then re-fetched in the browser (see
|
|
55
|
+
* {@link PolicyLive}) so edits you make in SCADABLE go live with no redeploy on your
|
|
56
|
+
* side. If a strict Content-Security-Policy blocks the browser fetch, the baked copy
|
|
57
|
+
* stays put, so the page is never blank.
|
|
55
58
|
*
|
|
56
59
|
* ```tsx
|
|
57
60
|
* import { PrivacyPolicy } from '@scadable/privacy';
|
|
@@ -61,6 +64,6 @@ interface PrivacyPolicyProps extends FetchPolicyOptions {
|
|
|
61
64
|
* }
|
|
62
65
|
* ```
|
|
63
66
|
*/
|
|
64
|
-
declare function PrivacyPolicy({ token, className, showVersion, ...options }: PrivacyPolicyProps): Promise<
|
|
67
|
+
declare function PrivacyPolicy({ token, className, showVersion, ...options }: PrivacyPolicyProps): Promise<react.JSX.Element>;
|
|
65
68
|
|
|
66
69
|
export { DEFAULT_BASE_URL, type FetchPolicyOptions, type Policy, PrivacyPolicy, type PrivacyPolicyProps, fetchPolicy };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { PolicyLive } from './PolicyLive.js';
|
|
2
|
+
export { PolicyLive } from './PolicyLive.js';
|
|
3
|
+
import { jsx } from 'react/jsx-runtime';
|
|
2
4
|
|
|
3
5
|
// src/client.ts
|
|
4
6
|
var DEFAULT_BASE_URL = "https://api.scadable.com";
|
|
@@ -24,17 +26,19 @@ async function PrivacyPolicy({
|
|
|
24
26
|
...options
|
|
25
27
|
}) {
|
|
26
28
|
const policy = await fetchPolicy(token, options);
|
|
27
|
-
return /* @__PURE__ */
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
policy.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
29
|
+
return /* @__PURE__ */ jsx(
|
|
30
|
+
PolicyLive,
|
|
31
|
+
{
|
|
32
|
+
token,
|
|
33
|
+
initialHtml: policy.html,
|
|
34
|
+
initialVersion: policy.version,
|
|
35
|
+
initialUpdatedAt: policy.updated_at,
|
|
36
|
+
className,
|
|
37
|
+
showVersion,
|
|
38
|
+
baseUrl: options.baseUrl,
|
|
39
|
+
docType: options.docType
|
|
40
|
+
}
|
|
41
|
+
);
|
|
38
42
|
}
|
|
39
43
|
|
|
40
44
|
export { DEFAULT_BASE_URL, PrivacyPolicy, fetchPolicy };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scadable/privacy",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Render your always-current SCADABLE privacy policy in a Next.js app.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"README.md"
|
|
21
21
|
],
|
|
22
22
|
"scripts": {
|
|
23
|
-
"build": "tsup",
|
|
23
|
+
"build": "tsup && node scripts/postbuild-directives.mjs",
|
|
24
24
|
"dev": "tsup --watch",
|
|
25
25
|
"typecheck": "tsc --noEmit",
|
|
26
26
|
"prepublishOnly": "npm run build"
|