diff --git a/bskyogcard/package.json b/bskyogcard/package.json index 31abd366a6..a390febb33 100644 --- a/bskyogcard/package.json +++ b/bskyogcard/package.json @@ -10,8 +10,8 @@ "install-fonts": "node --loader ts-node/esm scripts/install-fonts.ts" }, "dependencies": { - "@atproto/api": "0.14.0-next.2", - "@atproto/common": "^0.4.0", + "@atproto/api": "^0.15.14", + "@atproto/common": "^0.4.11", "@resvg/resvg-js": "^2.6.2", "express": "^4.19.2", "http-terminator": "^3.2.0", diff --git a/bskyogcard/src/components/Avatar.tsx b/bskyogcard/src/components/Avatar.tsx index 4118244a13..0903c66616 100644 --- a/bskyogcard/src/components/Avatar.tsx +++ b/bskyogcard/src/components/Avatar.tsx @@ -1,5 +1,3 @@ -/* eslint-disable react-native-a11y/has-valid-accessibility-ignores-invert-colors */ -import React from 'react' import {AppBskyActorDefs, moderateProfile} from '@atproto/api' import {ModeratorData} from '../data/getModeratorData.js' diff --git a/bskyogcard/src/components/Box.tsx b/bskyogcard/src/components/Box.tsx index b6439703d1..f657133b3f 100644 --- a/bskyogcard/src/components/Box.tsx +++ b/bskyogcard/src/components/Box.tsx @@ -1,5 +1,3 @@ -import React from 'react' - import {atoms as a, style as s} from '../theme/index.js' export function Box({ diff --git a/bskyogcard/src/components/Butterfly.tsx b/bskyogcard/src/components/Butterfly.tsx index 5a4124975c..f84e816d1e 100644 --- a/bskyogcard/src/components/Butterfly.tsx +++ b/bskyogcard/src/components/Butterfly.tsx @@ -1,5 +1,3 @@ -import React from 'react' - export function Butterfly(props: React.SVGAttributes) { return ( ) { return ( , 'src'> & {src: Buffer}, ) { diff --git a/bskyogcard/src/components/MediaInsetBorder.tsx b/bskyogcard/src/components/MediaInsetBorder.tsx index 7734638044..ebde70ebf7 100644 --- a/bskyogcard/src/components/MediaInsetBorder.tsx +++ b/bskyogcard/src/components/MediaInsetBorder.tsx @@ -1,6 +1,4 @@ -import React from 'react' - -import {atoms as a, StyleProp,theme as t} from '../theme/index.js' +import {atoms as a, StyleProp, theme as t} from '../theme/index.js' import {Box} from './Box.js' /** diff --git a/bskyogcard/src/components/Post/index.tsx b/bskyogcard/src/components/Post/index.tsx index 9860edce69..b901e3e39c 100644 --- a/bskyogcard/src/components/Post/index.tsx +++ b/bskyogcard/src/components/Post/index.tsx @@ -7,6 +7,7 @@ import { import {ModeratorData} from '../../data/getModeratorData.js' import {PostData} from '../../data/getPostData.js' import {atoms as a, theme as t} from '../../theme/index.js' +import * as bsky from '../../types/bsky.js' import {formatCount} from '../../util/formatCount.js' import {formatDate} from '../../util/formatDate.js' import {moderatePost} from '../../util/moderatePost.js' @@ -36,7 +37,12 @@ export function Post({ mat?: boolean } }) { - if (AppBskyFeedPost.isValidRecord(post.record)) { + if ( + bsky.dangerousIsType( + post.record, + AppBskyFeedPost.isRecord, + ) + ) { const avatar = data.images.get(post.author.avatar) const rt = post.record.text ? new RichTextApi({ diff --git a/bskyogcard/src/components/PostEmbed.tsx b/bskyogcard/src/components/PostEmbed.tsx index 0e4ce5d516..70790894d1 100644 --- a/bskyogcard/src/components/PostEmbed.tsx +++ b/bskyogcard/src/components/PostEmbed.tsx @@ -7,6 +7,7 @@ import { import {ModeratorData} from '../data/getModeratorData.js' import {Image as ImageSource, PostData} from '../data/getPostData.js' import {atoms as a, theme as t} from '../theme/index.js' +import * as bsky from '../types/bsky.js' import {getModerationCauseInfo} from '../util/getModerationCauseInfo.js' import {getStarterPackImageUri} from '../util/getStarterPackImageUri.js' import {Embed, EmbedType, parseEmbed} from '../util/parseEmbed.js' @@ -148,7 +149,14 @@ export function StarterPackEmbed({ embed: EmbedType<'starter_pack'> }) { const uri = getStarterPackImageUri(embed.view) - if (!AppBskyGraphStarterpack.isValidRecord(embed.view.record)) return null + if ( + !bsky.dangerousIsType( + embed.view.record, + AppBskyGraphStarterpack.isRecord, + ) + ) { + return null + } const {name, description} = embed.view.record const image = rest.data.images.get(uri) return ( diff --git a/bskyogcard/src/components/PostEmbed/QuotePost.tsx b/bskyogcard/src/components/PostEmbed/QuotePost.tsx index 310624329b..b1dda7d23f 100644 --- a/bskyogcard/src/components/PostEmbed/QuotePost.tsx +++ b/bskyogcard/src/components/PostEmbed/QuotePost.tsx @@ -1,4 +1,3 @@ -import React from 'react' import { AppBskyEmbedRecord, AppBskyFeedPost, @@ -8,6 +7,7 @@ import { import {ModeratorData} from '../../data/getModeratorData.js' import {PostData} from '../../data/getPostData.js' import {atoms as a, theme as t} from '../../theme/index.js' +import * as bsky from '../../types/bsky.js' import {getModerationCauseInfo} from '../../util/getModerationCauseInfo.js' import {moderatePost} from '../../util/moderatePost.js' import {sanitizeHandle} from '../../util/sanitizeHandle.js' @@ -32,7 +32,10 @@ export function QuotePost({ const {author, value: post, embeds} = embed const avatar = data.images.get(author.avatar || '') const rt = - AppBskyFeedPost.isValidRecord(post) && post.text + bsky.dangerousIsType( + post, + AppBskyFeedPost.isRecord, + ) && post.text ? new RichTextApi({ text: post.text, facets: post.facets, diff --git a/bskyogcard/src/components/RichText.tsx b/bskyogcard/src/components/RichText.tsx index b24e79711c..9d867fbcc0 100644 --- a/bskyogcard/src/components/RichText.tsx +++ b/bskyogcard/src/components/RichText.tsx @@ -1,4 +1,3 @@ -import React from 'react' import {AppBskyRichtextFacet, RichText as RichTextApi} from '@atproto/api' import {atoms as a, style as s, theme as t} from '../theme/index.js' diff --git a/bskyogcard/src/components/StarterPack.tsx b/bskyogcard/src/components/StarterPack.tsx index e3c4112c31..9ae2663e2a 100644 --- a/bskyogcard/src/components/StarterPack.tsx +++ b/bskyogcard/src/components/StarterPack.tsx @@ -1,6 +1,6 @@ -/* eslint-disable bsky-internal/avoid-unwrapped-text */ import {AppBskyGraphDefs, AppBskyGraphStarterpack} from '@atproto/api' +import * as bsky from '../types/bsky.js' import {Butterfly} from './Butterfly.js' import {Img} from './Img.js' @@ -17,7 +17,10 @@ export function StarterPack(props: { images: Map }) { const {starterPack, images} = props - const record = AppBskyGraphStarterpack.isValidRecord(starterPack.record) + const record = bsky.dangerousIsType( + starterPack.record, + AppBskyGraphStarterpack.isRecord, + ) ? starterPack.record : null const imagesArray = [...images.values()] diff --git a/bskyogcard/src/components/Text.tsx b/bskyogcard/src/components/Text.tsx index 752a6da4a5..46078c8ddf 100644 --- a/bskyogcard/src/components/Text.tsx +++ b/bskyogcard/src/components/Text.tsx @@ -1,5 +1,3 @@ -import React from 'react' - import {atoms as a, style as s, theme as t} from '../theme/index.js' const SCALE_MULTIPLIER = 1 - 0.0625 diff --git a/bskyogcard/src/components/icons/CircleInfo.tsx b/bskyogcard/src/components/icons/CircleInfo.tsx index 4ebd27fc51..93dc085bba 100644 --- a/bskyogcard/src/components/icons/CircleInfo.tsx +++ b/bskyogcard/src/components/icons/CircleInfo.tsx @@ -1,5 +1,3 @@ -import React from 'react' - import {IconProps} from './types.js' export function CircleInfo({size, fill}: IconProps) { diff --git a/bskyogcard/src/components/icons/EyeSlash.tsx b/bskyogcard/src/components/icons/EyeSlash.tsx index f2e2ffcae6..91570babce 100644 --- a/bskyogcard/src/components/icons/EyeSlash.tsx +++ b/bskyogcard/src/components/icons/EyeSlash.tsx @@ -1,5 +1,3 @@ -import React from 'react' - import {IconProps} from './types.js' export function EyeSlash({size, fill}: IconProps) { diff --git a/bskyogcard/src/components/icons/Heart.tsx b/bskyogcard/src/components/icons/Heart.tsx index ef52d38cef..b3ab664007 100644 --- a/bskyogcard/src/components/icons/Heart.tsx +++ b/bskyogcard/src/components/icons/Heart.tsx @@ -1,5 +1,3 @@ -import React from 'react' - import {IconProps} from './types.js' export function Heart({size, fill}: IconProps) { diff --git a/bskyogcard/src/components/icons/Logomark.tsx b/bskyogcard/src/components/icons/Logomark.tsx index b1daa63574..067a0e502c 100644 --- a/bskyogcard/src/components/icons/Logomark.tsx +++ b/bskyogcard/src/components/icons/Logomark.tsx @@ -1,5 +1,3 @@ -import React from 'react' - import {IconProps} from './types.js' export function Logomark({size, fill}: IconProps) { diff --git a/bskyogcard/src/components/icons/Logotype.tsx b/bskyogcard/src/components/icons/Logotype.tsx index 26537b2a3d..bbd0717f03 100644 --- a/bskyogcard/src/components/icons/Logotype.tsx +++ b/bskyogcard/src/components/icons/Logotype.tsx @@ -1,5 +1,3 @@ -import React from 'react' - import {IconProps} from './types.js' export function Logotype({size, fill}: IconProps) { diff --git a/bskyogcard/src/components/icons/Play.tsx b/bskyogcard/src/components/icons/Play.tsx index 364e8ff5c6..3162ec63e8 100644 --- a/bskyogcard/src/components/icons/Play.tsx +++ b/bskyogcard/src/components/icons/Play.tsx @@ -1,5 +1,3 @@ -import React from 'react' - import {IconProps} from './types.js' export function PlayFilled({size, fill}: IconProps) { diff --git a/bskyogcard/src/components/icons/Repost.tsx b/bskyogcard/src/components/icons/Repost.tsx index 25353d11b9..958055e200 100644 --- a/bskyogcard/src/components/icons/Repost.tsx +++ b/bskyogcard/src/components/icons/Repost.tsx @@ -1,5 +1,3 @@ -import React from 'react' - import {IconProps} from './types.js' export function Repost({size, fill}: IconProps) { diff --git a/bskyogcard/src/components/icons/StarterPack.tsx b/bskyogcard/src/components/icons/StarterPack.tsx index a2c50f1dcf..515e9c1369 100644 --- a/bskyogcard/src/components/icons/StarterPack.tsx +++ b/bskyogcard/src/components/icons/StarterPack.tsx @@ -1,5 +1,3 @@ -import React from 'react' - import {IconProps} from './types.js' export function StarterPack({size, fill}: IconProps) { diff --git a/bskyogcard/src/components/icons/Warning.tsx b/bskyogcard/src/components/icons/Warning.tsx index d261c21b06..230432c315 100644 --- a/bskyogcard/src/components/icons/Warning.tsx +++ b/bskyogcard/src/components/icons/Warning.tsx @@ -1,5 +1,3 @@ -import React from 'react' - import {IconProps} from './types.js' export function Warning({size, fill}: IconProps) { diff --git a/bskyogcard/src/types/bsky.ts b/bskyogcard/src/types/bsky.ts new file mode 100644 index 0000000000..a48006ff62 --- /dev/null +++ b/bskyogcard/src/types/bsky.ts @@ -0,0 +1,47 @@ +import {ValidationResult} from '@atproto/lexicon' + +/** + * Fast type checking without full schema validation, for use with data we + * trust, or for non-critical path use cases. Why? Our SDK's `is*` identity + * utils do not assert the type of the entire object, only the `$type` string. + * + * For full validation of the object schema, use the `validate` export from + * this file. + * + * Usage: + * ```ts + * import * as bsky from '#/types/bsky' + * + * if (bsky.dangerousIsType(item, AppBskyFeedPost.isRecord)) { + * // `item` has type `$Typed` here + * } + * ``` + */ +export function dangerousIsType( + record: unknown, + identity: (v: V) => v is V & {$type: NonNullable}, +): record is R { + return identity(record) +} + +/** + * Fully validates the object schema, which has a performance cost. + * + * For faster checks with data we trust, like that from our app view, use the + * `dangerousIsType` export from this same file. + * + * Usage: + * ```ts + * import * as bsky from '#/types/bsky' + * + * if (bsky.validate(item, AppBskyFeedPost.validateRecord)) { + * // `item` has type `$Typed` here + * } + * ``` + */ +export function validate( + record: unknown, + validator: (v: unknown) => ValidationResult, +): record is R { + return validator(record).success +} diff --git a/bskyogcard/tsconfig.json b/bskyogcard/tsconfig.json index a5c3beecb1..44f56ae34e 100644 --- a/bskyogcard/tsconfig.json +++ b/bskyogcard/tsconfig.json @@ -5,7 +5,8 @@ "esModuleInterop": true, "moduleResolution": "NodeNext", "jsx": "react-jsx", - "outDir": "dist" + "outDir": "dist", + "skipLibCheck": true }, "include": ["./src/index.ts", "./src/bin.ts"] } diff --git a/bskyogcard/yarn.lock b/bskyogcard/yarn.lock index ef1e8c0bbc..9ec774859a 100644 --- a/bskyogcard/yarn.lock +++ b/bskyogcard/yarn.lock @@ -2,74 +2,64 @@ # yarn lockfile v1 -"@atproto/api@0.14.0-next.2": - version "0.14.0-next.2" - resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.14.0-next.2.tgz#c42313217dc5cbbd81438706018963d4232a4ba6" - integrity sha512-1FADZQAklDozrGDxrb6ydkrlBBES4sKP7wJZTtwzqb1bSUOqRD8vjuvVvklt2ahhAQvslsBUC1wBrMa7VGS6hA== +"@atproto/api@^0.15.14": + version "0.15.14" + resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.15.14.tgz#41ff6ce2e7603119a005b7b5ce8e64551ec84879" + integrity sha512-FHEMAdscG+r2OFcZUIzPyTDpwzRAyinRsIIaTcuqe0MgZWF4CEGNAKPos0IbecBzMxTOzUHE18dQDKhoXMdgvg== dependencies: - "@atproto/common-web" "^0.3.2" - "@atproto/lexicon" "^0.4.5" - "@atproto/syntax" "^0.3.1" - "@atproto/xrpc" "^0.6.6" + "@atproto/common-web" "^0.4.2" + "@atproto/lexicon" "^0.4.11" + "@atproto/syntax" "^0.4.0" + "@atproto/xrpc" "^0.7.0" await-lock "^2.2.2" multiformats "^9.9.0" tlds "^1.234.0" zod "^3.23.8" -"@atproto/common-web@^0.3.0": - version "0.3.0" - resolved "https://registry.npmjs.org/@atproto/common-web/-/common-web-0.3.0.tgz" - integrity sha512-67VnV6JJyX+ZWyjV7xFQMypAgDmjVaR9ZCuU/QW+mqlqI7fex2uL4Fv+7/jHadgzhuJHVd6OHOvNn0wR5WZYtA== - dependencies: - graphemer "^1.4.0" - multiformats "^9.9.0" - uint8arrays "3.0.0" - zod "^3.21.4" - -"@atproto/common-web@^0.3.2": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@atproto/common-web/-/common-web-0.3.2.tgz#4cf78ad4d24fed801882f3d35afc39bceccdff51" - integrity sha512-Vx0JtL1/CssJbFAb0UOdvTrkbUautsDfHNOXNTcX2vyPIxH9xOameSqLLunM1hZnOQbJwyjmQCt6TV+bhnanDg== +"@atproto/common-web@^0.4.2": + version "0.4.2" + resolved "https://registry.yarnpkg.com/@atproto/common-web/-/common-web-0.4.2.tgz#6e3add6939da93d3dfbc8f87e26dc4f57fad7259" + integrity sha512-vrXwGNoFGogodjQvJDxAeP3QbGtawgZute2ed1XdRO0wMixLk3qewtikZm06H259QDJVu6voKC5mubml+WgQUw== dependencies: graphemer "^1.4.0" multiformats "^9.9.0" uint8arrays "3.0.0" zod "^3.23.8" -"@atproto/common@^0.4.0": - version "0.4.0" - resolved "https://registry.npmjs.org/@atproto/common/-/common-0.4.0.tgz" - integrity sha512-yOXuPlCjT/OK9j+neIGYn9wkxx/AlxQSucysAF0xgwu0Ji8jAtKBf9Jv6R5ObYAjAD/kVUvEYumle+Yq/R9/7g== +"@atproto/common@^0.4.11": + version "0.4.11" + resolved "https://registry.yarnpkg.com/@atproto/common/-/common-0.4.11.tgz#9291b7c26f8b3507e280f7ecbdf1695ab5ea62f6" + integrity sha512-Knv0viYXNMfCdIE7jLUiWJKnnMfEwg+vz2epJQi8WOjqtqCFb3W/3Jn72ZiuovIfpdm13MaOiny6w2NErUQC6g== dependencies: - "@atproto/common-web" "^0.3.0" + "@atproto/common-web" "^0.4.2" "@ipld/dag-cbor" "^7.0.3" cbor-x "^1.5.1" iso-datestring-validator "^2.2.2" multiformats "^9.9.0" - pino "^8.15.0" + pino "^8.21.0" -"@atproto/lexicon@^0.4.5": - version "0.4.5" - resolved "https://registry.yarnpkg.com/@atproto/lexicon/-/lexicon-0.4.5.tgz#4fcf3731193c674286e9e8d677bbab5dd530b817" - integrity sha512-fljWqMGKn+XWtTprBcS3F1hGBREnQYh6qYHv2sjENucc7REms1gtmZXSerB9N6pVeHVNOnXiILdukeAcic5OEw== +"@atproto/lexicon@^0.4.11": + version "0.4.11" + resolved "https://registry.yarnpkg.com/@atproto/lexicon/-/lexicon-0.4.11.tgz#d5d09be1faf1d28d1e57051dab4064101f8b1617" + integrity sha512-btefdnvNz2Ao2I+qbmj0F06HC8IlrM/IBz6qOBS50r0S6uDf5tOO+Mv2tSVdimFkdzyDdLtBI1sV36ONxz2cOw== dependencies: - "@atproto/common-web" "^0.3.2" - "@atproto/syntax" "^0.3.1" + "@atproto/common-web" "^0.4.2" + "@atproto/syntax" "^0.4.0" iso-datestring-validator "^2.2.2" multiformats "^9.9.0" zod "^3.23.8" -"@atproto/syntax@^0.3.1": - version "0.3.1" - resolved "https://registry.yarnpkg.com/@atproto/syntax/-/syntax-0.3.1.tgz#4346418728f9643d783d2ffcf7c77e132e1f53d4" - integrity sha512-fzW0Mg1QUOVCWUD3RgEsDt6d1OZ6DdFmbKcDdbzUfh0t4rhtRAC05KbZYmxuMPWDAiJ4BbbQ5dkAc/mNypMXkw== +"@atproto/syntax@^0.4.0": + version "0.4.0" + resolved "https://registry.yarnpkg.com/@atproto/syntax/-/syntax-0.4.0.tgz#bec71552087bb24c208a06ef418c0040b65542f2" + integrity sha512-b9y5ceHS8YKOfP3mdKmwAx5yVj9294UN7FG2XzP6V5aKUdFazEYRnR9m5n5ZQFKa3GNvz7de9guZCJ/sUTcOAA== -"@atproto/xrpc@^0.6.6": - version "0.6.6" - resolved "https://registry.yarnpkg.com/@atproto/xrpc/-/xrpc-0.6.6.tgz#28f58270ef4a8056f7f718bd52512e74bcd3702f" - integrity sha512-umXEYVMo9/pyIBoKmIAIi64RXDW9tSXY+wqztlQ6I2GZtjLfNZqmAWU+wADk3SxUe54mvjxxGyA4TtyGtDMfhA== +"@atproto/xrpc@^0.7.0": + version "0.7.0" + resolved "https://registry.yarnpkg.com/@atproto/xrpc/-/xrpc-0.7.0.tgz#7d1e497d682431fecd7085d7482e83d8a33821b0" + integrity sha512-SfhP9dGx2qclaScFDb58Jnrmim5nk4geZXCqg6sB0I/KZhZEkr9iIx1hLCp+sxkIfEsmEJjeWO4B0rjUIJW5cw== dependencies: - "@atproto/lexicon" "^0.4.5" + "@atproto/lexicon" "^0.4.11" zod "^3.23.8" "@cbor-extract/cbor-extract-darwin-arm64@2.2.0": @@ -1253,9 +1243,9 @@ pino-std-serializers@^7.0.0: resolved "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.0.0.tgz" integrity sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA== -pino@^8.15.0: +pino@^8.21.0: version "8.21.0" - resolved "https://registry.npmjs.org/pino/-/pino-8.21.0.tgz" + resolved "https://registry.yarnpkg.com/pino/-/pino-8.21.0.tgz#e1207f3675a2722940d62da79a7a55a98409f00d" integrity sha512-ip4qdzjkAyDDZklUaZkcRFb2iA118H9SgRh8yzTkSQK8HilsOJF7rSY8HoW5+I0M46AZgX/pxbprf2vvzQCE0Q== dependencies: atomic-sleep "^1.0.0" @@ -1762,7 +1752,7 @@ yoga-wasm-web@^0.3.3: resolved "https://registry.npmjs.org/yoga-wasm-web/-/yoga-wasm-web-0.3.3.tgz" integrity sha512-N+d4UJSJbt/R3wqY7Coqs5pcV0aUj2j9IaQ3rNj9bVCLld8tTGKRa2USARjnvZJWVx1NDmQev8EknoczaOQDOA== -zod@^3.21.4, zod@^3.23.8: +zod@^3.23.8: version "3.23.8" resolved "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz" integrity sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==