nskd-lbr 1.1.3 → 1.1.6

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/README.md CHANGED
@@ -64,6 +64,7 @@ const noskid = new NskdLbr({
64
64
  debug: false, // Enable debug logging
65
65
  timeout: 10000, // Request timeout (ms)
66
66
  strictCheck: true, // Validate local vs API data
67
+ allowAchievements: true, // Allow achievement-boosted certificates
67
68
  useLegacyAPI: false, // Use legacy API format
68
69
 
69
70
  // Logging
@@ -79,6 +80,7 @@ const noskid = new NskdLbr({
79
80
  | `debug` | `boolean` | `false` | Enable console debug messages |
80
81
  | `timeout` | `number` | `10000` | API request timeout in milliseconds |
81
82
  | `strictCheck` | `boolean` | `true` | Compare local certificate data with API response |
83
+ | `allowAchievements` | `boolean` | `true` | Allow certificates that used achievement boosts |
82
84
  | `useLegacyAPI` | `boolean` | `false` | Use legacy API format (affects username/nickname field) |
83
85
  | `onLog` | `function` | `null` | `(message, level) => {}` - Custom logging function |
84
86
 
@@ -293,11 +295,14 @@ The library provides detailed error messages for different failure scenarios:
293
295
 
294
296
  ### Common Errors
295
297
 
298
+ ### 5. Common Errors table - add new row:
299
+ ```markdown
296
300
  | Error Type | Cause | Solution |
297
301
  |------------|-------|----------|
298
302
  | `"No file provided"` | File input is empty | Check file selection |
299
303
  | `"File must be a PNG image"` | Wrong file format | Upload a PNG certificate |
300
304
  | `"No valid verification key found"` | Certificate missing key | Check certificate validity |
305
+ | `"Certificate uses achievements boost"` | Achievement boost not allowed | Set `allowAchievements: true` or use non-boosted certificate |
301
306
  | `"Request timeout"` | Network/server issues | Check connection, increase timeout |
302
307
  | `"Data mismatch"` | Local vs API data differs | Disable `strictCheck` or verify certificate |
303
308
 
package/package.json CHANGED
@@ -1,23 +1,23 @@
1
- {
2
- "name": "nskd-lbr",
3
- "version": "1.1.3",
4
- "description": "A JavaScript library for working with NoSkid certificates.",
5
- "main": "src/nskd-lbr.js",
6
- "scripts": {
7
- "test": "echo \"Error: no test specified\" && exit 1"
8
- },
9
- "repository": {
10
- "type": "git",
11
- "url": "git+https://github.com/dpipstudio/noskid.today.git"
12
- },
13
- "keywords": [
14
- "noskid",
15
- "certificate"
16
- ],
17
- "author": "Douxx",
18
- "license": "SEE LICENSE IN LICENSE",
19
- "bugs": {
20
- "url": "https://github.com/dpipstudio/noskid.today/issues"
21
- },
22
- "homepage": "https://github.com/dpipstudio/noskid.today#readme"
23
- }
1
+ {
2
+ "name": "nskd-lbr",
3
+ "version": "1.1.6",
4
+ "description": "A JavaScript library for working with NoSkid certificates.",
5
+ "main": "src/nskd-lbr.js",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/dpipstudio/noskid.today.git"
12
+ },
13
+ "keywords": [
14
+ "noskid",
15
+ "certificate"
16
+ ],
17
+ "author": "Douxx",
18
+ "license": "SEE LICENSE IN LICENSE",
19
+ "bugs": {
20
+ "url": "https://github.com/dpipstudio/noskid.today/issues"
21
+ },
22
+ "homepage": "https://github.com/dpipstudio/noskid.today#readme"
23
+ }
package/src/nskd-lbr.js CHANGED
@@ -2,14 +2,15 @@
2
2
  * NoSkid Certificate Library
3
3
  * A JavaScript library for working with NoSkid certificates
4
4
  *
5
- * @version 1.1.0
5
+ * @version 1.1.5
6
6
  * @author Douxx <douxx@douxx.tech>
7
7
  * @param {string} [options.apiUrl='https://check.noskid.today/'] - Logs debug messages to console
8
8
  * @param {boolean} [options.debug=false] - Logs debug messages to console
9
9
  * @param {boolean} [options.strictCheck=true] - Whether to validate local data against API response
10
+ * @param {boolean} [options.allowAchievements=true] - Whether to allow achievements boosted certificates
10
11
  * @param {integer} [options.timeout=10000] - API request timeout in milliseconds
11
12
  * @param {boolean} [options.useLegacyAPI=false] - Whether to use the legacy API format
12
- * @param {function} [options.onLog=null] - Whether to validate local data against API response
13
+ * @param {function} [options.onLog=null] - Log function for callbacks
13
14
  */
14
15
 
15
16
  class NskdLbr {
@@ -18,6 +19,7 @@ class NskdLbr {
18
19
  this.debug = options.debug || false;
19
20
  this.timeout = options.timeout || 10000;
20
21
  this.strictCheck = options.strictCheck !== undefined ? options.strictCheck : true;
22
+ this.allowAchievements = options.allowAchievements !== undefined ? options.allowAchievements : true;
21
23
  this.onLog = options.onLog || null;
22
24
  this.useLegacyAPI = options.useLegacyAPI || false;
23
25
  this.certificateData = null;
@@ -344,10 +346,26 @@ Certificate Details:
344
346
  return {
345
347
  valid: false,
346
348
  message: apiData.message,
347
- cached: apiData.cached || false
349
+ cached: apiData.cached || false,
350
+ strictCheck: this.strictCheck
348
351
  };
349
352
  }
350
353
 
354
+ if (apiData.data.boosted && !this.allowAchievements) {
355
+ this.isValid = false;
356
+ this.nskdLbrLog('Certificate uses achievements boost, which is not allowed by allowAchievements', 'error');
357
+ return {
358
+ valid: false,
359
+ message: 'Certificate uses achievements boost',
360
+ cached: apiData.cached || false,
361
+ strictCheck: this.strictCheck
362
+ };
363
+ }
364
+
365
+ if (apiData.data.boosted) {
366
+ this.nskdLbrLog('Certificate is achievement-boosted', 'warning');
367
+ }
368
+
351
369
  // Compare local data with API data if available and strictCheck is enabled
352
370
  if (this.localData && this.strictCheck) {
353
371
  const apiUsername = this.useLegacyAPI ? apiData.data.username : (apiData.data.nickname || apiData.data.username);
@@ -378,6 +396,7 @@ Certificate Details:
378
396
  valid: true,
379
397
  message: 'Certificate verified successfully',
380
398
  data: apiData.data,
399
+ query: apiData.query || this.verificationKey,
381
400
  cached: apiData.cached || false,
382
401
  strictCheck: this.strictCheck
383
402
  };
@@ -1,8 +1,8 @@
1
- class NskdLbr{constructor(t={}){this.apiUrl=t.apiUrl||"https://check.noskid.today/",this.debug=t.debug||!1,this.timeout=t.timeout||1e4,this.strictCheck=void 0===t.strictCheck||t.strictCheck,this.onLog=t.onLog||null,this.useLegacyAPI=t.useLegacyAPI||!1,this.certificateData=null,this.verificationKey=null,this.localData=null,this.isValid=!1}nskdLbrLog(t,e="info"){if(this.debug){var r=`[${(new Date).toLocaleTimeString()}] NoSkid:`;switch(e){case"error":console.error(r,t);break;case"warning":console.warn(r,t);break;case"success":console.log(`%c${r} `+t,"color: green");break;default:console.log(r,t)}}this.onLog&&"function"==typeof this.onLog&&this.onLog(t,e)}async loadFromFile(t){try{if(this.nskdLbrLog("Starting certificate verification process...","info"),!t)throw new Error("No file provided");if(!t.name.toLowerCase().endsWith(".png"))throw new Error("File must be a PNG image");this.nskdLbrLog("Processing certificate file: "+t.name,"info");var e=await this.readFileAsArrayBuffer(t),r=await this.extractTextFromPng(e);if(!r)throw new Error("Could not extract verification data from file");if(this.verificationKey=this.extractVerificationKey(r),this.verificationKey)return this.nskdLbrLog("Successfully extracted verification key","success"),this.localData=this.extractLocalData(r),this.localData&&(this.nskdLbrLog("Local certificate data extracted:","info"),this.nskdLbrLog("Username: "+this.localData.username,"info"),this.nskdLbrLog("Creation Date: "+this.localData.creationDate,"info")),await this.verifyWithAPI();throw new Error("No valid verification key found in certificate")}catch(t){throw this.nskdLbrLog("Error loading certificate: "+t.message,"error"),t}}async verifyWithKey(t){try{if(!t||"string"!=typeof t)throw new Error("Invalid verification key provided");if(/^[a-f0-9]{64}$/i.test(t))return this.verificationKey=t.toLowerCase(),this.nskdLbrLog(`Verifying certificate with key: ${this.verificationKey.substring(0,16)}...`,"info"),await this.verifyWithAPI();throw new Error("Verification key must be a 64-character hexadecimal string")}catch(t){throw this.nskdLbrLog("Error verifying certificate: "+t.message,"error"),t}}getCertificateData(){return this.certificateData?{...this.certificateData,key:this.verificationKey,localUsername:this.localData?this.localData.username:null,localCreationDate:this.localData?this.localData.creationDate:null}:null}isValidCertificate(){return this.isValid}getFormattedDetails(){var t,e;return this.certificateData?(t=this.certificateData,e=!this.useLegacyAPI&&t.nickname||t.username,`
1
+ class NskdLbr{constructor(e={}){this.apiUrl=e.apiUrl||"https://check.noskid.today/",this.debug=e.debug||!1,this.timeout=e.timeout||1e4,this.strictCheck=void 0===e.strictCheck||e.strictCheck,this.allowAchievements=void 0===e.allowAchievements||e.allowAchievements,this.onLog=e.onLog||null,this.useLegacyAPI=e.useLegacyAPI||!1,this.certificateData=null,this.verificationKey=null,this.localData=null,this.isValid=!1}nskdLbrLog(e,t="info"){if(this.debug){var i=`[${(new Date).toLocaleTimeString()}] NoSkid:`;switch(t){case"error":console.error(i,e);break;case"warning":console.warn(i,e);break;case"success":console.log(`%c${i} `+e,"color: green");break;default:console.log(i,e)}}this.onLog&&"function"==typeof this.onLog&&this.onLog(e,t)}async loadFromFile(e){try{if(this.nskdLbrLog("Starting certificate verification process...","info"),!e)throw new Error("No file provided");if(!e.name.toLowerCase().endsWith(".png"))throw new Error("File must be a PNG image");this.nskdLbrLog("Processing certificate file: "+e.name,"info");var t=await this.readFileAsArrayBuffer(e),i=await this.extractTextFromPng(t);if(!i)throw new Error("Could not extract verification data from file");if(this.verificationKey=this.extractVerificationKey(i),this.verificationKey)return this.nskdLbrLog("Successfully extracted verification key","success"),this.localData=this.extractLocalData(i),this.localData&&(this.nskdLbrLog("Local certificate data extracted:","info"),this.nskdLbrLog("Username: "+this.localData.username,"info"),this.nskdLbrLog("Creation Date: "+this.localData.creationDate,"info")),await this.verifyWithAPI();throw new Error("No valid verification key found in certificate")}catch(e){throw this.nskdLbrLog("Error loading certificate: "+e.message,"error"),e}}async verifyWithKey(e){try{if(!e||"string"!=typeof e)throw new Error("Invalid verification key provided");if(/^[a-f0-9]{64}$/i.test(e))return this.verificationKey=e.toLowerCase(),this.nskdLbrLog(`Verifying certificate with key: ${this.verificationKey.substring(0,16)}...`,"info"),await this.verifyWithAPI();throw new Error("Verification key must be a 64-character hexadecimal string")}catch(e){throw this.nskdLbrLog("Error verifying certificate: "+e.message,"error"),e}}getCertificateData(){return this.certificateData?{...this.certificateData,key:this.verificationKey,localUsername:this.localData?this.localData.username:null,localCreationDate:this.localData?this.localData.creationDate:null}:null}isValidCertificate(){return this.isValid}getFormattedDetails(){var e,t;return this.certificateData?(e=this.certificateData,t=!this.useLegacyAPI&&e.nickname||e.username,`
2
2
  Certificate Details:
3
- - Certificate #: ${t.certificate_number}
4
- - Username: ${e}
5
- - Percentage: ${t.percentage}%
6
- - Creation Date: ${t.creationDate}
7
- - Country: ${t.country} (${t.countryCode})
8
- `.trim()):"No certificate data available"}reset(){this.certificateData=null,this.verificationKey=null,this.localData=null,this.isValid=!1,this.nskdLbrLog("Certificate data reset","info")}readFileAsArrayBuffer(a){return new Promise((e,t)=>{var r=new FileReader;r.onload=t=>e(t.target.result),r.onerror=()=>t(new Error("Error reading file")),r.readAsArrayBuffer(a)})}async extractTextFromPng(r){try{var a=new Uint8Array(r);if(137!==a[0]||80!==a[1]||78!==a[2]||71!==a[3])throw new Error("Not a valid PNG file");let t=8,e=null;for(;t<a.length-12;){var i=a[t]<<24|a[t+1]<<16|a[t+2]<<8|a[t+3];if("tEXt"===String.fromCharCode(a[t+4],a[t+5],a[t+6],a[t+7])){var s=a.slice(t+8,t+8+i),o=new TextDecoder("utf-8").decode(s),n=o.indexOf("\0");if(-1!==n){var c=o.substring(0,n),l=o.substring(n+1);if("noskid-key"===c){e=l;break}}}t+=8+i+4}return e?(this.nskdLbrLog("Certificate data extracted successfully from PNG","success"),e):(this.nskdLbrLog("No 'noskid-key' text chunk found in PNG","error"),null)}catch(t){return this.nskdLbrLog("Error extracting text from PNG: "+t.message,"error"),null}}extractVerificationKey(t){try{var e=/-*BEGIN NOSKID KEY-*\s*([a-f0-9]{64})/i,r=t.match(e);return r?r[1].toLowerCase():null}catch(t){return this.nskdLbrLog("Error extracting verification key: "+t.message,"error"),null}}extractLocalData(t){try{var e,r,a,i,s,o=/-----BEGIN NOSKID KEY-----\s*([a-f0-9]+)\s*([A-Za-z0-9+/=]+)\s*([A-Za-z0-9+/=]+)\s*-----END NOSKID KEY-----/,n=t.match(o);return n?(e=n[2],a=(r=atob(e.replace(/=/g,"")).match(/CERT-\d+-(.+)/))?r[1]:null,i=n[3],{username:a,creationDate:(s=atob(i.replace(/=/g,"")).match(/CREATED-(.+)/))?s[1]:null}):null}catch(t){return this.nskdLbrLog("Error extracting local data: "+t.message,"error"),null}}async verifyWithAPI(){try{this.nskdLbrLog("Verifying certificate with server...","info");let t=new AbortController;var e=setTimeout(()=>t.abort(),this.timeout),r=await fetch(this.apiUrl+"?key="+encodeURIComponent(this.verificationKey),{signal:t.signal,headers:{"User-Agent":"NskdLbr/1.1.0"}});if(clearTimeout(e),!r.ok)throw new Error(`HTTP ${r.status}: `+r.statusText);var a=await r.json();if(!a.success)return this.isValid=!1,this.nskdLbrLog("Certificate verification failed: "+a.message,"error"),{valid:!1,message:a.message,cached:a.cached||!1};if(this.localData&&this.strictCheck){var i=!this.useLegacyAPI&&a.data.nickname||a.data.username,s=this.compareData(this.localData,{...a.data,username:i});if(!s.valid)return this.isValid=!1,this.nskdLbrLog("Certificate data mismatch!","error"),this.nskdLbrLog("Mismatch reason: "+s.reason,"error"),this.nskdLbrLog("Note: Strict checking is enabled. Set strictCheck to false to skip local data validation.","warning"),{valid:!1,message:"Data mismatch: "+s.reason,cached:a.cached||!1,strictCheck:!0};this.nskdLbrLog("Local data validation passed","success")}else this.localData&&!this.strictCheck&&this.nskdLbrLog("Strict checking disabled - skipping local data validation","warning");return this.isValid=!0,this.certificateData=a.data,this.nskdLbrLog("Certificate is VALID!","success"),{valid:!0,message:"Certificate verified successfully",data:a.data,cached:a.cached||!1,strictCheck:this.strictCheck}}catch(t){if("AbortError"===t.name)throw new Error("Request timeout - server took too long to respond");throw new Error("API verification failed: "+t.message)}}compareData(t,e){return t&&e?t.username!==e.username?{valid:!1,reason:`Username mismatch: Local=${t.username}, API=`+e.username}:(t=t.creationDate.substring(0,16))!==(e=e.creationDate.substring(0,16))?{valid:!1,reason:`Creation date mismatch: Local=${t}, API=`+e}:{valid:!0}:{valid:!1,reason:"Missing data for comparison"}}}"undefined"!=typeof module&&module.exports?module.exports=NskdLbr:window.NskdLbr=NskdLbr;
3
+ - Certificate #: ${e.certificate_number}
4
+ - Username: ${t}
5
+ - Percentage: ${e.percentage}%
6
+ - Creation Date: ${e.creationDate}
7
+ - Country: ${e.country} (${e.countryCode})
8
+ `.trim()):"No certificate data available"}reset(){this.certificateData=null,this.verificationKey=null,this.localData=null,this.isValid=!1,this.nskdLbrLog("Certificate data reset","info")}readFileAsArrayBuffer(a){return new Promise((t,e)=>{var i=new FileReader;i.onload=e=>t(e.target.result),i.onerror=()=>e(new Error("Error reading file")),i.readAsArrayBuffer(a)})}async extractTextFromPng(i){try{var a=new Uint8Array(i);if(137!==a[0]||80!==a[1]||78!==a[2]||71!==a[3])throw new Error("Not a valid PNG file");let e=8,t=null;for(;e<a.length-12;){var r=a[e]<<24|a[e+1]<<16|a[e+2]<<8|a[e+3];if("tEXt"===String.fromCharCode(a[e+4],a[e+5],a[e+6],a[e+7])){var s=a.slice(e+8,e+8+r),o=new TextDecoder("utf-8").decode(s),c=o.indexOf("\0");if(-1!==c){var n=o.substring(0,c),l=o.substring(c+1);if("noskid-key"===n){t=l;break}}}e+=8+r+4}return t?(this.nskdLbrLog("Certificate data extracted successfully from PNG","success"),t):(this.nskdLbrLog("No 'noskid-key' text chunk found in PNG","error"),null)}catch(e){return this.nskdLbrLog("Error extracting text from PNG: "+e.message,"error"),null}}extractVerificationKey(e){try{var t=/-*BEGIN NOSKID KEY-*\s*([a-f0-9]{64})/i,i=e.match(t);return i?i[1].toLowerCase():null}catch(e){return this.nskdLbrLog("Error extracting verification key: "+e.message,"error"),null}}extractLocalData(e){try{var t,i,a,r,s,o=/-----BEGIN NOSKID KEY-----\s*([a-f0-9]+)\s*([A-Za-z0-9+/=]+)\s*([A-Za-z0-9+/=]+)\s*-----END NOSKID KEY-----/,c=e.match(o);return c?(t=c[2],a=(i=atob(t.replace(/=/g,"")).match(/CERT-\d+-(.+)/))?i[1]:null,r=c[3],{username:a,creationDate:(s=atob(r.replace(/=/g,"")).match(/CREATED-(.+)/))?s[1]:null}):null}catch(e){return this.nskdLbrLog("Error extracting local data: "+e.message,"error"),null}}async verifyWithAPI(){try{this.nskdLbrLog("Verifying certificate with server...","info");let e=new AbortController;var t=setTimeout(()=>e.abort(),this.timeout),i=await fetch(this.apiUrl+"?key="+encodeURIComponent(this.verificationKey),{signal:e.signal,headers:{"User-Agent":"NskdLbr/1.1.0"}});if(clearTimeout(t),!i.ok)throw new Error(`HTTP ${i.status}: `+i.statusText);var a=await i.json();if(!a.success)return this.isValid=!1,this.nskdLbrLog("Certificate verification failed: "+a.message,"error"),{valid:!1,message:a.message,cached:a.cached||!1,strictCheck:this.strictCheck};if(a.data.boosted&&!this.allowAchievements)return this.isValid=!1,this.nskdLbrLog("Certificate uses achievements boost, which is not allowed by allowAchievements","error"),{valid:!1,message:"Certificate uses achievements boost",cached:a.cached||!1,strictCheck:this.strictCheck};if(a.data.boosted&&this.nskdLbrLog("Certificate is achievement-boosted","warning"),this.localData&&this.strictCheck){var r=!this.useLegacyAPI&&a.data.nickname||a.data.username,s=this.compareData(this.localData,{...a.data,username:r});if(!s.valid)return this.isValid=!1,this.nskdLbrLog("Certificate data mismatch!","error"),this.nskdLbrLog("Mismatch reason: "+s.reason,"error"),this.nskdLbrLog("Note: Strict checking is enabled. Set strictCheck to false to skip local data validation.","warning"),{valid:!1,message:"Data mismatch: "+s.reason,cached:a.cached||!1,strictCheck:!0};this.nskdLbrLog("Local data validation passed","success")}else this.localData&&!this.strictCheck&&this.nskdLbrLog("Strict checking disabled - skipping local data validation","warning");return this.isValid=!0,this.certificateData=a.data,this.nskdLbrLog("Certificate is VALID!","success"),{valid:!0,message:"Certificate verified successfully",data:a.data,query:a.query||this.verificationKey,cached:a.cached||!1,strictCheck:this.strictCheck}}catch(e){if("AbortError"===e.name)throw new Error("Request timeout - server took too long to respond");throw new Error("API verification failed: "+e.message)}}compareData(e,t){return e&&t?e.username!==t.username?{valid:!1,reason:`Username mismatch: Local=${e.username}, API=`+t.username}:(e=e.creationDate.substring(0,16))!==(t=t.creationDate.substring(0,16))?{valid:!1,reason:`Creation date mismatch: Local=${e}, API=`+t}:{valid:!0}:{valid:!1,reason:"Missing data for comparison"}}}"undefined"!=typeof module&&module.exports?module.exports=NskdLbr:window.NskdLbr=NskdLbr;