testing-package-xdsfdsfsc 1.0.19 → 1.0.20
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.
package/package.json
CHANGED
|
@@ -1,19 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "testing-package-xdsfdsfsc",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.20",
|
|
4
4
|
"description": "",
|
|
5
|
-
"main": "index.js",
|
|
6
5
|
"scripts": {
|
|
7
6
|
"test": "echo \"Error: no test specified\" && exit 1",
|
|
8
|
-
"
|
|
9
|
-
"postinstall": "node ./index.js",
|
|
10
|
-
"start": "node ./index.js"
|
|
7
|
+
"postinstall": "node ./index.js"
|
|
11
8
|
},
|
|
12
9
|
"files": [
|
|
13
|
-
"preinstall.js"
|
|
14
|
-
"index.js",
|
|
15
|
-
"examples",
|
|
16
|
-
"entitlement-check.js"
|
|
10
|
+
"preinstall.js"
|
|
17
11
|
],
|
|
18
12
|
"author": "",
|
|
19
13
|
"license": "ISC",
|
|
@@ -23,6 +17,6 @@
|
|
|
23
17
|
"xpack": {
|
|
24
18
|
"projectId": "cml5jzoa000014zxeq5lelhpl",
|
|
25
19
|
"apiKey": "pay_135abcf0612547dca4ea432d89f0cdb7",
|
|
26
|
-
"host": "https://
|
|
20
|
+
"host": "https://4373-2401-4900-8fcd-ac9c-6c82-85aa-e5d6-be4c.ngrok-free.app"
|
|
27
21
|
}
|
|
28
22
|
}
|
package/entitlement-check.js
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Runs inside the package on the server. Reads THIS package's package.json
|
|
4
|
-
* (same dir as this file), builds deviceId, calls backend. No consumer config needed.
|
|
5
|
-
*/
|
|
6
|
-
const { createHash } = require("crypto");
|
|
7
|
-
const { readFileSync, existsSync } = require("fs");
|
|
8
|
-
const { hostname, platform } = require("os");
|
|
9
|
-
const { join } = require("path");
|
|
10
|
-
|
|
11
|
-
function getXpackFromThisPackage() {
|
|
12
|
-
// This file lives in the package root; package.json is next to it
|
|
13
|
-
const pkgPath = join(__dirname, "package.json");
|
|
14
|
-
if (!existsSync(pkgPath)) return {};
|
|
15
|
-
try {
|
|
16
|
-
const raw = readFileSync(pkgPath, "utf-8");
|
|
17
|
-
const pkg = JSON.parse(raw);
|
|
18
|
-
return pkg.xpack || {};
|
|
19
|
-
} catch {
|
|
20
|
-
return {};
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Call from Server Component or API. Returns { allowed: boolean }.
|
|
26
|
-
* Uses this package's own package.json for xpack (host, projectId, apiKey).
|
|
27
|
-
*/
|
|
28
|
-
async function getEntitlement() {
|
|
29
|
-
const xpack = getXpackFromThisPackage();
|
|
30
|
-
const host = (xpack.host || "").replace(/\/+$/, "");
|
|
31
|
-
const projectId = xpack.projectId || "";
|
|
32
|
-
const apiKey = xpack.apiKey || "";
|
|
33
|
-
|
|
34
|
-
if (!projectId || !apiKey || !host) {
|
|
35
|
-
return { allowed: false };
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const deviceId = createHash("sha256")
|
|
39
|
-
.update(hostname() + "-" + platform())
|
|
40
|
-
.digest("hex");
|
|
41
|
-
|
|
42
|
-
try {
|
|
43
|
-
const res = await fetch(host + "/api/install/runtime-check", {
|
|
44
|
-
method: "POST",
|
|
45
|
-
headers: { "Content-Type": "application/json" },
|
|
46
|
-
body: JSON.stringify({ projectId, apiKey, deviceId }),
|
|
47
|
-
});
|
|
48
|
-
const data = await res.json();
|
|
49
|
-
return { allowed: data.allowed === true };
|
|
50
|
-
} catch {
|
|
51
|
-
return { allowed: false };
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
module.exports = { getEntitlement, getXpackFromThisPackage };
|
|
@@ -1,80 +0,0 @@
|
|
|
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
|
-
* Reads xpack (host, projectId, apiKey) from the PAID PACKAGE's package.json
|
|
7
|
-
* inside node_modules (e.g. node_modules/testing-package-xdsfdsfsc/package.json).
|
|
8
|
-
* No need to duplicate xpack in the root project — copy-paste node_modules and
|
|
9
|
-
* this route and it works.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { createHash } from "crypto";
|
|
13
|
-
import { readFileSync, readdirSync, existsSync } from "fs";
|
|
14
|
-
import { hostname, platform } from "os";
|
|
15
|
-
import { join } from "path";
|
|
16
|
-
|
|
17
|
-
type Xpack = { host?: string; projectId?: string; apiKey?: string };
|
|
18
|
-
|
|
19
|
-
function getXpackFromNodeModules(): Xpack {
|
|
20
|
-
const nodeModules = join(process.cwd(), "node_modules");
|
|
21
|
-
if (!existsSync(nodeModules)) return {};
|
|
22
|
-
try {
|
|
23
|
-
const names = readdirSync(nodeModules, { withFileTypes: true });
|
|
24
|
-
for (const dirent of names) {
|
|
25
|
-
if (dirent.name.startsWith(".") || !dirent.isDirectory()) continue;
|
|
26
|
-
const pkgPath = join(nodeModules, dirent.name, "package.json");
|
|
27
|
-
try {
|
|
28
|
-
const raw = readFileSync(pkgPath, "utf-8");
|
|
29
|
-
const pkg = JSON.parse(raw) as { xpack?: Xpack };
|
|
30
|
-
if (pkg.xpack && (pkg.xpack.projectId || pkg.xpack.apiKey)) {
|
|
31
|
-
return pkg.xpack;
|
|
32
|
-
}
|
|
33
|
-
} catch {
|
|
34
|
-
// skip invalid or missing package.json
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
} catch {
|
|
38
|
-
// no node_modules or unreadable
|
|
39
|
-
}
|
|
40
|
-
return {};
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function getXpack(): Xpack {
|
|
44
|
-
const fromModules = getXpackFromNodeModules();
|
|
45
|
-
if (fromModules.projectId || fromModules.apiKey) return fromModules;
|
|
46
|
-
// Fallback: root package.json (optional, for projects that still put xpack there)
|
|
47
|
-
try {
|
|
48
|
-
const raw = readFileSync(join(process.cwd(), "package.json"), "utf-8");
|
|
49
|
-
const pkg = JSON.parse(raw) as { xpack?: Xpack };
|
|
50
|
-
return pkg.xpack ?? {};
|
|
51
|
-
} catch {
|
|
52
|
-
return {};
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const xpack = getXpack();
|
|
57
|
-
const XPACK_HOST = xpack.host ?? "https://yourapp.com";
|
|
58
|
-
const XPACK_PROJECT_ID = xpack.projectId ?? "";
|
|
59
|
-
const XPACK_API_KEY = xpack.apiKey ?? "";
|
|
60
|
-
|
|
61
|
-
export async function GET() {
|
|
62
|
-
if (!XPACK_PROJECT_ID || !XPACK_API_KEY) {
|
|
63
|
-
return Response.json({ allowed: false }, { status: 200 });
|
|
64
|
-
}
|
|
65
|
-
const deviceId = createHash("sha256")
|
|
66
|
-
.update(hostname() + "-" + platform())
|
|
67
|
-
.digest("hex");
|
|
68
|
-
const host = XPACK_HOST.replace(/\/+$/, "");
|
|
69
|
-
const res = await fetch(host + "/api/install/runtime-check", {
|
|
70
|
-
method: "POST",
|
|
71
|
-
headers: { "Content-Type": "application/json" },
|
|
72
|
-
body: JSON.stringify({
|
|
73
|
-
projectId: XPACK_PROJECT_ID,
|
|
74
|
-
apiKey: XPACK_API_KEY,
|
|
75
|
-
deviceId,
|
|
76
|
-
}),
|
|
77
|
-
});
|
|
78
|
-
const data = await res.json();
|
|
79
|
-
return Response.json({ allowed: data.allowed === true });
|
|
80
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Server Component: runs entitlement check inside the package (reads this package's
|
|
3
|
-
* package.json, calls backend). No API route or root project config needed.
|
|
4
|
-
*
|
|
5
|
-
* Consumer usage (nothing else required):
|
|
6
|
-
* import { EntitlementProvider } from "testing-package-xdsfdsfsc/examples/entitlement-provider-server";
|
|
7
|
-
* import { Button } from "testing-package-xdsfdsfsc/examples/runtime-check-ui-example";
|
|
8
|
-
* <EntitlementProvider><Button /></EntitlementProvider>
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import React, { type ReactNode } from "react";
|
|
12
|
-
import { ClientEntitlementProvider } from "./runtime-check-ui-example";
|
|
13
|
-
|
|
14
|
-
async function getEntitlement() {
|
|
15
|
-
// entitlement-check.js is in package root; this file is in examples/
|
|
16
|
-
const mod = await import("../entitlement-check.js");
|
|
17
|
-
return mod.getEntitlement();
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export async function EntitlementProvider({ children }: { children: ReactNode }) {
|
|
21
|
-
const { allowed } = await getEntitlement();
|
|
22
|
-
return (
|
|
23
|
-
<ClientEntitlementProvider allowed={allowed}>
|
|
24
|
-
{children}
|
|
25
|
-
</ClientEntitlementProvider>
|
|
26
|
-
);
|
|
27
|
-
}
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
/**
|
|
3
|
-
* Paid UI: use EntitlementProvider (Server Component) + Button. No API route or root config.
|
|
4
|
-
*
|
|
5
|
-
* import { EntitlementProvider, Button } from "your-package/examples/runtime-check-ui-example";
|
|
6
|
-
* import { EntitlementProvider as ServerWrap } from "your-package/examples/entitlement-provider-server";
|
|
7
|
-
* export default function Layout({ children }) {
|
|
8
|
-
* return <ServerWrap><EntitlementProvider>{children}</EntitlementProvider></ServerWrap>;
|
|
9
|
-
* }
|
|
10
|
-
* // Or use XpackProvider + checkUrl if you prefer adding an API route.
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import React, {
|
|
14
|
-
createContext,
|
|
15
|
-
useContext,
|
|
16
|
-
useState,
|
|
17
|
-
useEffect,
|
|
18
|
-
type ReactNode,
|
|
19
|
-
} from "react";
|
|
20
|
-
|
|
21
|
-
type EntitlementContextValue =
|
|
22
|
-
| { status: "loading" }
|
|
23
|
-
| { status: "allowed" }
|
|
24
|
-
| { status: "denied"; payUrl?: string };
|
|
25
|
-
|
|
26
|
-
const EntitlementContext = createContext<EntitlementContextValue | null>(null);
|
|
27
|
-
|
|
28
|
-
export function XpackProvider({
|
|
29
|
-
children,
|
|
30
|
-
checkUrl,
|
|
31
|
-
payUrl,
|
|
32
|
-
}: {
|
|
33
|
-
children: ReactNode;
|
|
34
|
-
/** App's API route that returns { allowed: boolean } (server does deviceId + runtime-check). */
|
|
35
|
-
checkUrl: string;
|
|
36
|
-
/** Shown when denied; e.g. "https://yourapp.com/pay". */
|
|
37
|
-
payUrl?: string;
|
|
38
|
-
}) {
|
|
39
|
-
const [value, setValue] = useState<EntitlementContextValue>({ status: "loading" });
|
|
40
|
-
|
|
41
|
-
useEffect(() => {
|
|
42
|
-
let cancelled = false;
|
|
43
|
-
fetch(checkUrl)
|
|
44
|
-
.then((res) => res.json())
|
|
45
|
-
.then((data: { allowed?: boolean }) => {
|
|
46
|
-
if (cancelled) return;
|
|
47
|
-
setValue(
|
|
48
|
-
data.allowed
|
|
49
|
-
? { status: "allowed" }
|
|
50
|
-
: { status: "denied", payUrl }
|
|
51
|
-
);
|
|
52
|
-
})
|
|
53
|
-
.catch(() => {
|
|
54
|
-
if (!cancelled) setValue({ status: "denied", payUrl });
|
|
55
|
-
});
|
|
56
|
-
return () => {
|
|
57
|
-
cancelled = true;
|
|
58
|
-
};
|
|
59
|
-
}, [checkUrl, payUrl]);
|
|
60
|
-
|
|
61
|
-
return (
|
|
62
|
-
<EntitlementContext.Provider value={value}>
|
|
63
|
-
{children}
|
|
64
|
-
</EntitlementContext.Provider>
|
|
65
|
-
);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export function useEntitled(): EntitlementContextValue {
|
|
69
|
-
const ctx = useContext(EntitlementContext);
|
|
70
|
-
if (!ctx) {
|
|
71
|
-
return { status: "denied", payUrl: "" };
|
|
72
|
-
}
|
|
73
|
-
return ctx;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/** Client wrapper: receives server result and sets context. No fetch, no API route. */
|
|
77
|
-
export function ClientEntitlementProvider({
|
|
78
|
-
allowed,
|
|
79
|
-
children,
|
|
80
|
-
payUrl,
|
|
81
|
-
}: {
|
|
82
|
-
allowed: boolean;
|
|
83
|
-
children: React.ReactNode;
|
|
84
|
-
payUrl?: string;
|
|
85
|
-
}) {
|
|
86
|
-
const value: EntitlementContextValue = allowed
|
|
87
|
-
? { status: "allowed" }
|
|
88
|
-
: { status: "denied", payUrl };
|
|
89
|
-
return (
|
|
90
|
-
<EntitlementContext.Provider value={value}>
|
|
91
|
-
{children}
|
|
92
|
-
</EntitlementContext.Provider>
|
|
93
|
-
);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/** Example: your Button uses the hook and blocks when not entitled */
|
|
97
|
-
export function Button({
|
|
98
|
-
children,
|
|
99
|
-
...props
|
|
100
|
-
}: React.ButtonHTMLAttributes<HTMLButtonElement>) {
|
|
101
|
-
const entitlement = useEntitled();
|
|
102
|
-
|
|
103
|
-
if (entitlement.status === "loading") {
|
|
104
|
-
return <button disabled {...props}>Loading...</button>;
|
|
105
|
-
}
|
|
106
|
-
if (entitlement.status === "denied") {
|
|
107
|
-
return (
|
|
108
|
-
<button disabled {...props} title="Payment required">
|
|
109
|
-
🔒 Pay required
|
|
110
|
-
</button>
|
|
111
|
-
);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
return <button {...props}>{children}</button>;
|
|
115
|
-
}
|
package/index.js
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
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;
|