testing-package-xdsfdsfsc 1.0.12 → 1.0.14
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.
Potentially problematic release.
This version of testing-package-xdsfdsfsc might be problematic. Click here for more details.
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Example: API route the CONSUMER APP adds so the UI package can check entitlement.
|
|
3
|
+
* (e.g. Next.js: app/api/entitlement-check/route.ts)
|
|
4
|
+
*
|
|
5
|
+
* This runs on the server (Node), so we have crypto + os for deviceId.
|
|
6
|
+
* Set XPACK_HOST, XPACK_PROJECT_ID, XPACK_API_KEY in env (from package xpack or .env).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { createHash } from "crypto";
|
|
10
|
+
import { hostname, platform } from "os";
|
|
11
|
+
|
|
12
|
+
const XPACK_HOST = process.env.XPACK_HOST || "https://yourapp.com";
|
|
13
|
+
const XPACK_PROJECT_ID = process.env.XPACK_PROJECT_ID || "";
|
|
14
|
+
const XPACK_API_KEY = process.env.XPACK_API_KEY || "";
|
|
15
|
+
|
|
16
|
+
export async function GET() {
|
|
17
|
+
if (!XPACK_PROJECT_ID || !XPACK_API_KEY) {
|
|
18
|
+
return Response.json({ allowed: false }, { status: 200 });
|
|
19
|
+
}
|
|
20
|
+
const deviceId = createHash("sha256")
|
|
21
|
+
.update(hostname() + "-" + platform())
|
|
22
|
+
.digest("hex");
|
|
23
|
+
const host = XPACK_HOST.replace(/\/+$/, "");
|
|
24
|
+
const res = await fetch(host + "/api/install/runtime-check", {
|
|
25
|
+
method: "POST",
|
|
26
|
+
headers: { "Content-Type": "application/json" },
|
|
27
|
+
body: JSON.stringify({
|
|
28
|
+
projectId: XPACK_PROJECT_ID,
|
|
29
|
+
apiKey: XPACK_API_KEY,
|
|
30
|
+
deviceId,
|
|
31
|
+
}),
|
|
32
|
+
});
|
|
33
|
+
const data = await res.json();
|
|
34
|
+
return Response.json({ allowed: data.allowed === true });
|
|
35
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Example: paid UI component library (like shadcn).
|
|
3
|
+
*
|
|
4
|
+
* Consumer usage:
|
|
5
|
+
*
|
|
6
|
+
* import { XpackProvider, Button } from "@my-paid-ui";
|
|
7
|
+
*
|
|
8
|
+
* function App() {
|
|
9
|
+
* return (
|
|
10
|
+
* <XpackProvider checkUrl="/api/entitlement-check">
|
|
11
|
+
* <Button>Click me</Button>
|
|
12
|
+
* </XpackProvider>
|
|
13
|
+
* );
|
|
14
|
+
* }
|
|
15
|
+
*
|
|
16
|
+
* The app must add an API route (e.g. /api/entitlement-check) that runs the
|
|
17
|
+
* runtime check on the server (Node has hostname/platform for deviceId) and
|
|
18
|
+
* returns { allowed: boolean }. The UI package only calls that URL.
|
|
19
|
+
*
|
|
20
|
+
* All your components use useEntitled() and render nothing (or a pay wall)
|
|
21
|
+
* if not entitled.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import React, {
|
|
25
|
+
createContext,
|
|
26
|
+
useContext,
|
|
27
|
+
useState,
|
|
28
|
+
useEffect,
|
|
29
|
+
type ReactNode,
|
|
30
|
+
} from "react";
|
|
31
|
+
|
|
32
|
+
type EntitlementContextValue =
|
|
33
|
+
| { status: "loading" }
|
|
34
|
+
| { status: "allowed" }
|
|
35
|
+
| { status: "denied"; payUrl?: string };
|
|
36
|
+
|
|
37
|
+
const EntitlementContext = createContext<EntitlementContextValue | null>(null);
|
|
38
|
+
|
|
39
|
+
export function XpackProvider({
|
|
40
|
+
children,
|
|
41
|
+
checkUrl,
|
|
42
|
+
payUrl,
|
|
43
|
+
}: {
|
|
44
|
+
children: ReactNode;
|
|
45
|
+
/** App's API route that returns { allowed: boolean } (server does deviceId + runtime-check). */
|
|
46
|
+
checkUrl: string;
|
|
47
|
+
/** Shown when denied; e.g. "https://yourapp.com/pay". */
|
|
48
|
+
payUrl?: string;
|
|
49
|
+
}) {
|
|
50
|
+
const [value, setValue] = useState<EntitlementContextValue>({ status: "loading" });
|
|
51
|
+
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
let cancelled = false;
|
|
54
|
+
fetch(checkUrl)
|
|
55
|
+
.then((res) => res.json())
|
|
56
|
+
.then((data: { allowed?: boolean }) => {
|
|
57
|
+
if (cancelled) return;
|
|
58
|
+
setValue(
|
|
59
|
+
data.allowed
|
|
60
|
+
? { status: "allowed" }
|
|
61
|
+
: { status: "denied", payUrl }
|
|
62
|
+
);
|
|
63
|
+
})
|
|
64
|
+
.catch(() => {
|
|
65
|
+
if (!cancelled) setValue({ status: "denied", payUrl });
|
|
66
|
+
});
|
|
67
|
+
return () => {
|
|
68
|
+
cancelled = true;
|
|
69
|
+
};
|
|
70
|
+
}, [checkUrl, payUrl]);
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
<EntitlementContext.Provider value={value}>
|
|
74
|
+
{children}
|
|
75
|
+
</EntitlementContext.Provider>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function useEntitled(): EntitlementContextValue {
|
|
80
|
+
const ctx = useContext(EntitlementContext);
|
|
81
|
+
if (!ctx) {
|
|
82
|
+
return { status: "denied", payUrl: "" };
|
|
83
|
+
}
|
|
84
|
+
return ctx;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** Example: your Button uses the hook and blocks when not entitled */
|
|
88
|
+
export function Button({
|
|
89
|
+
children,
|
|
90
|
+
...props
|
|
91
|
+
}: React.ButtonHTMLAttributes<HTMLButtonElement>) {
|
|
92
|
+
const entitlement = useEntitled();
|
|
93
|
+
|
|
94
|
+
if (entitlement.status === "loading") {
|
|
95
|
+
return <button disabled {...props}>Loading...</button>;
|
|
96
|
+
}
|
|
97
|
+
if (entitlement.status === "denied") {
|
|
98
|
+
return (
|
|
99
|
+
<button disabled {...props} title="Payment required">
|
|
100
|
+
🔒 Pay required
|
|
101
|
+
</button>
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return <button {...props}>{children}</button>;
|
|
106
|
+
}
|
package/index.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Example: main entry for a paid package (CommonJS).
|
|
3
|
+
* Export a Promise that resolves to your API after the runtime check.
|
|
4
|
+
*
|
|
5
|
+
* Consumer usage:
|
|
6
|
+
* const api = await require("your-paid-package");
|
|
7
|
+
* api.hello();
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const crypto = require("crypto");
|
|
11
|
+
const os = require("os");
|
|
12
|
+
const pkg = require("./package.json");
|
|
13
|
+
const xpack = pkg.xpack || {};
|
|
14
|
+
|
|
15
|
+
async function assertEntitled() {
|
|
16
|
+
if (!xpack.projectId || !xpack.apiKey || !xpack.host) return;
|
|
17
|
+
const deviceId = crypto
|
|
18
|
+
.createHash("sha256")
|
|
19
|
+
.update(os.hostname() + "-" + os.platform())
|
|
20
|
+
.digest("hex");
|
|
21
|
+
const host = (xpack.host || "").replace(/\/+$/, "");
|
|
22
|
+
const res = await fetch(host + "/api/install/runtime-check", {
|
|
23
|
+
method: "POST",
|
|
24
|
+
headers: { "Content-Type": "application/json" },
|
|
25
|
+
body: JSON.stringify({
|
|
26
|
+
projectId: xpack.projectId,
|
|
27
|
+
apiKey: xpack.apiKey,
|
|
28
|
+
deviceId,
|
|
29
|
+
}),
|
|
30
|
+
});
|
|
31
|
+
const { allowed } = await res.json();
|
|
32
|
+
if (!allowed) throw new Error("This device is not entitled. Pay at " + host);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const apiPromise = (async () => {
|
|
36
|
+
await assertEntitled();
|
|
37
|
+
// Your real API
|
|
38
|
+
return {
|
|
39
|
+
hello: () => "hi developer",
|
|
40
|
+
};
|
|
41
|
+
})();
|
|
42
|
+
|
|
43
|
+
// When run directly (e.g. postinstall or `node index.js`), print greeting to terminal
|
|
44
|
+
if (require.main === module) {
|
|
45
|
+
apiPromise
|
|
46
|
+
.then((api) => {
|
|
47
|
+
console.log(api.hello());
|
|
48
|
+
})
|
|
49
|
+
.catch((err) => {
|
|
50
|
+
console.error(err.message);
|
|
51
|
+
process.exit(1);
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
module.exports = apiPromise;
|
package/package.json
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "testing-package-xdsfdsfsc",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.14",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"test": "echo \"Error: no test specified\" && exit 1",
|
|
8
|
-
"install": "node ./preinstall.js"
|
|
8
|
+
"install": "node ./preinstall.js",
|
|
9
|
+
"postinstall": "node ./index.js",
|
|
10
|
+
"start": "node ./index.js"
|
|
9
11
|
},
|
|
10
12
|
"files": [
|
|
11
|
-
"preinstall.js"
|
|
13
|
+
"preinstall.js",
|
|
14
|
+
"index.js",
|
|
15
|
+
"examples"
|
|
12
16
|
],
|
|
13
17
|
"author": "",
|
|
14
18
|
"license": "ISC",
|