feat: validate embedded data structure

This commit is contained in:
EnixCoda 2023-12-15 17:35:29 +08:00
parent d29f935e64
commit b606a4bb7d
5 changed files with 97 additions and 14 deletions

View file

@ -45,6 +45,7 @@
"react-use": "^17.3.2",
"react-window": "^1.8.7",
"styled-components": "^5.3.5",
"superstruct": "^1.0.3",
"webext-domain-permission-toggle": "^3.0.0",
"webext-dynamic-content-scripts": "^8.1.1",
"webextension-polyfill": "^0.10.0"

View file

@ -1,9 +1,11 @@
import { raiseError } from 'analytics'
import { Clippy, ClippyClassName } from 'components/Clippy'
import * as React from 'react'
import * as s from 'superstruct'
import { $ } from 'utils/$'
import { formatClass, parseIntFromElement } from 'utils/DOMHelper'
import { renderReact } from 'utils/general'
import { embeddedDataStruct } from './embeddedDataStructures'
const selectors = {
normal: {
@ -44,16 +46,11 @@ const getDOMJSON = (selector: string) =>
}
})
function getMetaFromPayload(payload: any) {
function getMetaFromPayload(payload: s.Infer<typeof embeddedDataStruct.repoPayload>) {
const { repo, refInfo } = payload
if (!repo || !refInfo) return
const { defaultBranch, name: repoName, ownerLogin: userName } = repo
const { name: branchName } = refInfo
// TODO: use runtime type check
if (!defaultBranch || !repoName || !userName || !branchName) return
return {
defaultBranch,
metaData: {
@ -66,15 +63,14 @@ function getMetaFromPayload(payload: any) {
// in code page, there is a JSON script tag in DOM with meta data
function resolveEmbeddedAppData() {
const appData = getDOMJSON(selectors.globalNavigation.embeddedData.app)
const payload = appData?.payload
if (payload) return getMetaFromPayload(payload)
const data = getDOMJSON(selectors.globalNavigation.embeddedData.app)
if (s.is(data, embeddedDataStruct.app)) return getMetaFromPayload(data.payload)
}
function resolveEmbeddedReposOverviewData() {
const reposOverviewData = getDOMJSON(selectors.globalNavigation.embeddedData.reposOverview)
const payload = reposOverviewData?.props?.initialPayload
if (payload) return getMetaFromPayload(payload)
const data = getDOMJSON(selectors.globalNavigation.embeddedData.reposOverview)
if (s.is(data, embeddedDataStruct.reposOverview))
return getMetaFromPayload(data.props.initialPayload)
}
export function resolveEmbeddedData(): {

View file

@ -0,0 +1,81 @@
import * as s from 'superstruct'
const repo = s.object({
id: s.number(),
defaultBranch: s.string(),
name: s.string(),
ownerLogin: s.string(),
currentUserCanPush: s.boolean(),
isFork: s.boolean(),
isEmpty: s.boolean(),
createdAt: s.string(),
ownerAvatar: s.string(),
public: s.boolean(),
private: s.boolean(),
isOrgOwned: s.boolean(),
})
const user = s.object({
id: s.number(),
login: s.string(),
userEmail: s.string(),
})
const rel = s.object({
name: s.string(),
listCacheKey: s.string(),
canEdit: s.boolean(),
refType: s.string(),
currentOid: s.string(),
})
const treeItem = s.object({
name: s.string(),
path: s.string(),
contentType: s.string(),
})
const tree = s.object({
items: s.array(treeItem),
templateDirectorySuggestionUrl: s.nullable(s.never()),
readme: s.nullable(s.never()),
totalCount: s.number(),
showBranchInfobar: s.boolean(),
})
const repoPayload = s.object({
allShortcutsEnabled: s.boolean(),
path: s.string(),
repo: repo,
currentUser: user,
refInfo: rel,
tree: tree,
fileTree: s.nullable(s.never()),
fileTreeProcessingTime: s.nullable(s.never()),
foldersToFetch: s.array(s.unknown()),
treeExpanded: s.boolean(),
symbolsExpanded: s.boolean(),
isOverview: s.boolean(),
overview: s.unknown(),
})
const reposOverview = s.object({
props: s.object({
initialPayload: repoPayload,
appPayload: s.unknown(),
}),
})
const app = s.object({
payload: repoPayload,
})
export const embeddedDataStruct = {
repo,
user,
rel,
treeItem,
tree,
repoPayload,
reposOverview,
app,
}

View file

@ -113,10 +113,10 @@ module.exports = {
sideEffects: false,
},
{
test: /\.js$/,
test: /\.m?js$/,
loader: 'babel-loader',
// Transpile as least files under node_modules
include: /node_modules\/(webext-content-scripts|webext-detect-page)\/.*\.js$/,
include: /node_modules\/(webext-content-scripts|webext-detect-page|superstruct)\/.*\.m?js$/,
options: {
cacheDirectory: true,
},

View file

@ -11325,6 +11325,11 @@ stylis@^4.0.6:
resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.1.1.tgz#e46c6a9bbf7c58db1e65bb730be157311ae1fe12"
integrity sha512-lVrM/bNdhVX2OgBFNa2YJ9Lxj7kPzylieHd3TNjuGE0Re9JB7joL5VUKOVH1kdNNJTgGPpT8hmwIAPLaSyEVFQ==
superstruct@^1.0.3:
version "1.0.3"
resolved "https://registry.npmmirror.com/superstruct/-/superstruct-1.0.3.tgz#de626a5b49c6641ff4d37da3c7598e7a87697046"
integrity sha512-8iTn3oSS8nRGn+C2pgXSKPI3jmpm6FExNazNpjvqS6ZUJQCej3PUXEKM8NjHBOs54ExM+LPW/FBRhymrdcCiSg==
supports-color@6.1.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3"