Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,14 @@ jobs:
- name: test
run: pnpm run test:coverage

- name: Upload all coverage reports to Codecov
if: github.ref == 'refs/heads/main'
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: coverage/coverage-final.json
fail_ci_if_error: false

- name: Setup NPM Authentication
if: ${{ needs.release-please.outputs.release_created }}
run: |
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ node_modules
.env
.env.local
.env.development
.env.production
.env.production
coverage/
1 change: 1 addition & 0 deletions dist/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ export declare const useCallback: (config: CallbackConfig) => {
isDataURIEncoded?: boolean;
}) => QueryPayloads;
watcher: (options?: WatcherOptions) => QueryPayloads | undefined;
generateUrl: (url: string, payload: SendPayloads, sendType?: string, sender?: string) => string;
};
export type { CallbackConfig, QueryPayloads, SendPayloads, WatcherOptions, SignIn, SignOut, OemSignOut, Troubleshoot, Recover, Replace, TrialExtend, TrialStart, Purchase, Redeem, Renew, Upgrade, UpdateOs, DowngradeOs, Manage, MyKeys, LinkKey, Activate, AccountActionTypes, AccountKeyActionTypes, PurchaseActionTypes, ServerActionTypes, ServerState, ServerData, UserInfo, ExternalSignIn, ExternalSignOut, ExternalKeyActions, ExternalUpdateOsAction, ServerPayload, ServerTroubleshoot, ExternalActions, UpcActions, ExternalPayload, UpcPayload, };
63 changes: 58 additions & 5 deletions dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import Utf8 from "crypto-js/enc-utf8.js";
import { createSharedComposable } from "@vueuse/core";
const _useCallback = (config) => {
const send = (url, payload, redirectType, sendType, sender) => {
// send() requires browser APIs and is client-only
if (typeof window === "undefined") {
throw new Error("send() can only be called on the client side");
}
const stringifiedData = JSON.stringify({
actions: [...payload],
sender: sender ?? window.location.href.replace("/Tools/Update", "/Tools"),
Expand All @@ -26,28 +30,77 @@ const _useCallback = (config) => {
? decodeURI(data)
: data;
const decryptedMessage = AES.decrypt(dataToParse, config.encryptionKey);
const decryptedData = JSON.parse(decryptedMessage.toString(Utf8));
return decryptedData;
let decryptedString;
try {
decryptedString = decryptedMessage.toString(Utf8);
}
catch (e) {
// Catch errors during UTF-8 conversion (likely due to bad decryption)
throw new Error('Decryption failed. Invalid key or corrupt data.');
}
// Check if decryption resulted in an empty string (another failure case)
if (!decryptedString) {
throw new Error('Decryption failed. Invalid key or corrupt data.');
}
try {
const decryptedData = JSON.parse(decryptedString);
return decryptedData;
}
catch (e) {
// Catch potential JSON parse errors even if decryption seemed successful
throw new Error('Failed to parse decrypted data.');
}
};
const watcher = (options = {}) => {
let urlToParse = "";
if (options?.baseUrl && !options.skipCurrentUrl) {
urlToParse = options.baseUrl;
}
else if (window && window.location && !options.skipCurrentUrl) {
else if (typeof window !== "undefined" && window.location && !options.skipCurrentUrl) {
urlToParse = window.location.toString();
}
const currentUrl = new URL(urlToParse);
const uriDecodedEncryptedData = decodeURI(options?.dataToParse ?? currentUrl?.searchParams.get("data") ?? "");
else if (!options?.dataToParse && !options?.baseUrl) {
// If no window and no explicit data/baseUrl provided, return undefined
return undefined;
}
// If we have dataToParse, use it directly; otherwise parse from URL
const uriDecodedEncryptedData = options?.dataToParse
? decodeURI(options.dataToParse)
: (() => {
try {
const currentUrl = new URL(urlToParse);
return decodeURI(currentUrl.searchParams.get("data") ?? "");
}
catch {
return "";
}
})();
if (!uriDecodedEncryptedData) {
return undefined;
}
return parse(uriDecodedEncryptedData);
};
const generateUrl = (url, payload, sendType, sender) => {
// generateUrl() works on both server and client
// If no sender provided and we're on client, use window.location; otherwise use empty string
const defaultSender = sender ?? (typeof window !== "undefined"
? window.location.href.replace("/Tools/Update", "/Tools")
: "");
const stringifiedData = JSON.stringify({
actions: [...payload],
sender: defaultSender,
type: sendType,
});
const encryptedMessage = AES.encrypt(stringifiedData, config.encryptionKey).toString();
const destinationUrl = new URL(url);
destinationUrl.searchParams.set("data", encodeURI(encryptedMessage));
return destinationUrl.toString();
};
return {
send,
parse,
watcher,
generateUrl,
};
};
export const useCallback = createSharedComposable(_useCallback);
65 changes: 40 additions & 25 deletions dist/types.d.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,42 @@
export type SignIn = 'signIn';
export type SignOut = 'signOut';
export type OemSignOut = 'oemSignOut';
export type Troubleshoot = 'troubleshoot';
export type Recover = 'recover';
export type Replace = 'replace';
export type TrialExtend = 'trialExtend';
export type TrialStart = 'trialStart';
export type Purchase = 'purchase';
export type Redeem = 'redeem';
export type Renew = 'renew';
export type Upgrade = 'upgrade';
export type UpdateOs = 'updateOs';
export type DowngradeOs = 'downgradeOs';
export type Manage = 'manage';
export type MyKeys = 'myKeys';
export type LinkKey = 'linkKey';
export type Activate = 'activate';
export type SignIn = "signIn";
export type SignOut = "signOut";
export type OemSignOut = "oemSignOut";
export type Troubleshoot = "troubleshoot";
export type Recover = "recover";
export type Replace = "replace";
export type TrialExtend = "trialExtend";
export type TrialStart = "trialStart";
export type Purchase = "purchase";
export type Redeem = "redeem";
export type Renew = "renew";
export type Upgrade = "upgrade";
export type UpdateOs = "updateOs";
export type DowngradeOs = "downgradeOs";
export type Manage = "manage";
export type MyKeys = "myKeys";
export type LinkKey = "linkKey";
export type Activate = "activate";
export type AccountActionTypes = Troubleshoot | SignIn | SignOut | OemSignOut | Manage | MyKeys | LinkKey;
export type AccountKeyActionTypes = Recover | Replace | TrialExtend | TrialStart | UpdateOs | DowngradeOs;
export type PurchaseActionTypes = Purchase | Redeem | Renew | Upgrade | Activate;
export type ServerActionTypes = AccountActionTypes | AccountKeyActionTypes | PurchaseActionTypes;
export type ServerState = 'BASIC' | 'PLUS' | 'PRO' | 'TRIAL' | 'EEXPIRED' | 'ENOKEYFILE' | 'EGUID' | 'EGUID1' | 'ETRIAL' | 'ENOKEYFILE2' | 'ENOKEYFILE1' | 'ENOFLASH' | 'ENOFLASH1' | 'ENOFLASH2' | 'ENOFLASH3' | 'ENOFLASH4' | 'ENOFLASH5' | 'ENOFLASH6' | 'ENOFLASH7' | 'EBLACKLISTED' | 'EBLACKLISTED1' | 'EBLACKLISTED2' | 'ENOCONN' | 'STARTER' | 'UNLEASHED' | 'LIFETIME' | 'STALE' | undefined;
export type ServerState = "BASIC" | "PLUS" | "PRO" | "TRIAL" | "EEXPIRED" | "ENOKEYFILE" | "EGUID" | "EGUID1" | "ETRIAL" | "ENOKEYFILE2" | "ENOKEYFILE1" | "ENOFLASH" | "ENOFLASH1" | "ENOFLASH2" | "ENOFLASH3" | "ENOFLASH4" | "ENOFLASH5" | "ENOFLASH6" | "ENOFLASH7" | "EBLACKLISTED" | "EBLACKLISTED1" | "EBLACKLISTED2" | "ENOCONN" | "STARTER" | "UNLEASHED" | "LIFETIME" | "STALE" | undefined;
export interface ActivationCodeData {
__typename?: "ActivationCode";
background?: string | null;
code?: string | null;
comment?: string | null;
header?: string | null;
headermetacolor?: string | null;
partnerName?: string | null;
partnerUrl?: string | null;
serverName?: string | null;
showBannerGradient?: boolean | null;
sysModel?: string | null;
theme?: string | null;
}
export interface ServerData {
activationCodeData?: ActivationCodeData | null;
description?: string;
deviceCount?: number;
expireTime?: number;
Expand All @@ -32,7 +47,7 @@ export interface ServerData {
locale?: string;
name?: string;
osVersion?: string;
osVersionBranch?: 'stable' | 'next' | 'preview' | 'test';
osVersionBranch?: "stable" | "next" | "preview" | "test";
registered: boolean;
regExp?: number;
regUpdatesExpired?: boolean;
Expand All @@ -43,14 +58,14 @@ export interface ServerData {
wanFQDN?: string;
}
export interface UserInfo {
'custom:ips_id'?: string;
"custom:ips_id"?: string;
email?: string;
email_verifed?: 'true' | 'false';
email_verifed?: "true" | "false";
preferred_username?: string;
sub?: string;
username?: string;
identities?: string;
'cognito:groups'?: string[];
"cognito:groups"?: string[];
}
export interface ExternalSignIn {
type: SignIn;
Expand Down Expand Up @@ -80,14 +95,14 @@ export type ExternalActions = ExternalSignIn | ExternalSignOut | ExternalKeyActi
export type UpcActions = ServerPayload | ServerTroubleshoot;
export type SendPayloads = ExternalActions[] | UpcActions[];
export interface ExternalPayload {
type: 'forUpc';
type: "forUpc";
actions: ExternalActions[];
sender: string;
}
export interface UpcPayload {
actions: UpcActions[];
sender: string;
type: 'fromUpc';
type: "fromUpc";
}
export type QueryPayloads = ExternalPayload | UpcPayload;
export interface WatcherOptions {
Expand Down
Loading