Compare commits

...

8 Commits

Author SHA1 Message Date
tsukino
2bfc197316 fix: add backward compatibility exports for extension imports
- Add /src and /src/types paths to package.json exports
- Extension imports from @tlsn/plugin-sdk/src/types now work correctly
- Fixes 'npm run dev' in packages/extension
2026-01-14 17:15:39 +08:00
tsukino
78cec7651b feat: enable tree-shaking with separate styles entry point
- Add package.json exports field with @tlsn/plugin-sdk/styles entry point
- Configure Vite to build separate bundles for index and styles
- Remove styling exports from main SDK index (now only in /styles)
- Update all components to import from @tlsn/plugin-sdk/styles
- Update ts-plugin-sample tsconfig to use bundler module resolution
- Eliminate QuickJS imports from plugin build output

Benefits:
- Plugin bundle size reduced from 17.4kb to 12.7kb (-27%)
- SDK main bundle reduced from 51.74kb to 41.10kb (-21%)
- Styles bundle is only 7.71kb
- Proper tree-shaking prevents importing unused Host/QuickJS code
2026-01-14 17:07:27 +08:00
tsukino
40bf2cec82 feat: add comprehensive non-opinionated color palette
- Add full color scales (100-900) for gray, blue, purple, red, yellow, orange, green
- Add white and black as base colors
- Remove opinionated 'primary' color scheme
- Update OverlayHeader to use literal gradient instead of primary token
- Remove legacy 'colors' export from plugin-sdk
- Color system is now completely non-opinionated and Tailwind-compatible
2026-01-14 17:01:33 +08:00
tsukino
d6d80cf277 feat: move styling system to plugin-sdk package
- Move styles.ts from ts-plugin-sample to plugin-sdk for reusability
- Export all styling utilities from plugin-sdk package
- Update all components to import styling functions from @tlsn/plugin-sdk
- Add external dependencies to esbuild config to avoid bundling SDK runtime
- Styling functions are now available to all plugins via the SDK
2026-01-14 16:55:09 +08:00
tsukino
449d2843ea feat: implement Tailwind-like styling system with composable helpers
- Add inlineStyle() function that merges multiple style helpers and filters falsey values
- Create comprehensive style helper functions (color, padding, margin, display, etc.)
- Add design token system with semantic color, spacing, and typography tokens
- Support conditional styling with falsey value filtering (e.g., isPending && opacity('0.6'))
- Update all components to use new composable styling API
- Replace object-based styles with function-based helpers for better readability
2026-01-14 16:51:37 +08:00
tsukino
8d93ff679e refactor: organize ts-plugin-sample UI into reusable components
- Create centralized design system in src/styles.ts with colors, spacing, typography
- Extract UI into component files: FloatingButton, PluginOverlay, OverlayHeader, StatusIndicator, ProveButton, LoginPrompt
- Simplify main plugin file by composing components instead of inline DOM construction
- Add TypeScript interfaces for component props
- Improve code maintainability and readability
2026-01-14 16:39:04 +08:00
tsukino
2c1fc6daad chore: simplify build-wrapper by removing unnecessary transformation
esbuild already outputs the desired export format
2026-01-14 16:26:28 +08:00
tsukino
1cdf314e8d feat: add TypeScript plugin support with sample implementation
- Export plugin API types from SDK (DivFunction, ButtonFunction, etc.)
- Enhanced globals.d.ts with all plugin API function types
- Created ts-plugin-sample package with TypeScript implementation
- Build outputs single file with clean export default statement
- Inlined handler enums for standalone execution
- No external imports in build output
2026-01-14 16:24:25 +08:00
20 changed files with 2079 additions and 138 deletions

546
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "tlsn-monorepo",
"version": "0.1.0",
"version": "0.1.0-alpha.13",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "tlsn-monorepo",
"version": "0.1.0",
"version": "0.1.0-alpha.13",
"license": "MIT",
"workspaces": [
"packages/*"
@@ -64,7 +64,6 @@
"version": "7.28.4",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/code-frame": "^7.27.1",
"@babel/generator": "^7.28.3",
@@ -580,7 +579,6 @@
"version": "7.27.1",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1"
},
@@ -1323,7 +1321,6 @@
"version": "7.27.1",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-annotate-as-pure": "^7.27.1",
"@babel/helper-module-imports": "^7.27.1",
@@ -1884,7 +1881,6 @@
"node_modules/@codemirror/view": {
"version": "6.38.6",
"license": "MIT",
"peer": true,
"dependencies": {
"@codemirror/state": "^6.5.0",
"crelt": "^1.0.6",
@@ -1994,7 +1990,6 @@
}
],
"license": "MIT",
"peer": true,
"engines": {
"node": "^14 || ^16 || >=18"
},
@@ -2016,7 +2011,6 @@
}
],
"license": "MIT",
"peer": true,
"engines": {
"node": "^14 || ^16 || >=18"
}
@@ -2093,7 +2087,6 @@
"version": "6.1.2",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"cssesc": "^3.0.0",
"util-deprecate": "^1.0.2"
@@ -2415,7 +2408,6 @@
"version": "6.1.2",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"cssesc": "^3.0.0",
"util-deprecate": "^1.0.2"
@@ -3907,6 +3899,7 @@
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"detect-libc": "^1.0.3",
"is-glob": "^4.0.3",
@@ -3948,6 +3941,7 @@
"os": [
"android"
],
"peer": true,
"engines": {
"node": ">= 10.0.0"
},
@@ -3966,6 +3960,7 @@
"os": [
"darwin"
],
"peer": true,
"engines": {
"node": ">= 10.0.0"
},
@@ -3986,6 +3981,7 @@
"os": [
"darwin"
],
"peer": true,
"engines": {
"node": ">= 10.0.0"
},
@@ -4006,6 +4002,7 @@
"os": [
"freebsd"
],
"peer": true,
"engines": {
"node": ">= 10.0.0"
},
@@ -4026,6 +4023,7 @@
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">= 10.0.0"
},
@@ -4046,6 +4044,7 @@
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">= 10.0.0"
},
@@ -4066,6 +4065,7 @@
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">= 10.0.0"
},
@@ -4086,6 +4086,7 @@
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">= 10.0.0"
},
@@ -4106,6 +4107,7 @@
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">= 10.0.0"
},
@@ -4126,6 +4128,7 @@
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">= 10.0.0"
},
@@ -4146,6 +4149,7 @@
"os": [
"win32"
],
"peer": true,
"engines": {
"node": ">= 10.0.0"
},
@@ -4166,6 +4170,7 @@
"os": [
"win32"
],
"peer": true,
"engines": {
"node": ">= 10.0.0"
},
@@ -4186,6 +4191,7 @@
"os": [
"win32"
],
"peer": true,
"engines": {
"node": ">= 10.0.0"
},
@@ -4637,7 +4643,6 @@
"version": "8.13.0",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.3",
"json-schema-traverse": "^1.0.0",
@@ -4859,6 +4864,10 @@
"resolved": "packages/plugin-sdk",
"link": true
},
"node_modules/@tlsn/ts-plugin-sample": {
"resolved": "packages/ts-plugin-sample",
"link": true
},
"node_modules/@tlsnotary/demo": {
"resolved": "packages/demo",
"link": true
@@ -5270,7 +5279,6 @@
"version": "5.62.0",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@eslint-community/regexpp": "^4.4.0",
"@typescript-eslint/scope-manager": "5.62.0",
@@ -5333,7 +5341,6 @@
"version": "5.62.0",
"dev": true,
"license": "BSD-2-Clause",
"peer": true,
"dependencies": {
"@typescript-eslint/scope-manager": "5.62.0",
"@typescript-eslint/types": "5.62.0",
@@ -5646,7 +5653,6 @@
"version": "3.2.4",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@testing-library/dom": "^10.4.0",
"@testing-library/user-event": "^14.6.1",
@@ -6212,7 +6218,6 @@
"version": "8.15.0",
"devOptional": true,
"license": "MIT",
"peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -6243,7 +6248,6 @@
"version": "6.12.6",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
@@ -7067,7 +7071,6 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.8.3",
"caniuse-lite": "^1.0.30001741",
@@ -7825,7 +7828,6 @@
"version": "6.1.2",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"cssesc": "^3.0.0",
"util-deprecate": "^1.0.2"
@@ -8205,6 +8207,7 @@
"version": "1.0.3",
"license": "Apache-2.0",
"optional": true,
"peer": true,
"bin": {
"detect-libc": "bin/detect-libc.js"
},
@@ -8662,7 +8665,6 @@
"version": "8.57.1",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.6.1",
@@ -11634,7 +11636,8 @@
"node_modules/node-addon-api": {
"version": "7.1.1",
"license": "MIT",
"optional": true
"optional": true,
"peer": true
},
"node_modules/node-forge": {
"version": "1.3.3",
@@ -12390,7 +12393,6 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"nanoid": "^3.3.11",
"picocolors": "^1.1.1",
@@ -13151,7 +13153,6 @@
"version": "6.1.2",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"cssesc": "^3.0.0",
"util-deprecate": "^1.0.2"
@@ -13429,7 +13430,6 @@
"version": "3.6.2",
"dev": true,
"license": "MIT",
"peer": true,
"bin": {
"prettier": "bin/prettier.cjs"
},
@@ -13714,7 +13714,6 @@
"node_modules/react": {
"version": "18.3.1",
"license": "MIT",
"peer": true,
"dependencies": {
"loose-envify": "^1.1.0"
},
@@ -13725,7 +13724,6 @@
"node_modules/react-dom": {
"version": "18.3.1",
"license": "MIT",
"peer": true,
"dependencies": {
"loose-envify": "^1.1.0",
"scheduler": "^0.23.2"
@@ -13783,7 +13781,6 @@
"version": "0.14.2",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -13877,7 +13874,6 @@
"node_modules/redux": {
"version": "4.2.1",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/runtime": "^7.9.2"
}
@@ -14123,7 +14119,6 @@
"node_modules/rollup": {
"version": "4.52.3",
"license": "MIT",
"peer": true,
"dependencies": {
"@types/estree": "1.0.8"
},
@@ -14267,7 +14262,6 @@
"version": "1.93.2",
"devOptional": true,
"license": "MIT",
"peer": true,
"dependencies": {
"chokidar": "^4.0.0",
"immutable": "^5.0.2",
@@ -14346,7 +14340,6 @@
"node_modules/schema-utils/node_modules/ajv": {
"version": "8.17.1",
"license": "MIT",
"peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
@@ -15636,7 +15629,6 @@
"node_modules/tinyglobby/node_modules/picomatch": {
"version": "4.0.3",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},
@@ -15794,8 +15786,7 @@
},
"node_modules/tslib": {
"version": "2.8.1",
"license": "0BSD",
"peer": true
"license": "0BSD"
},
"node_modules/tsutils": {
"version": "3.21.0",
@@ -15831,7 +15822,6 @@
"version": "3.13.1",
"dev": true,
"license": "(MIT OR CC0-1.0)",
"peer": true,
"engines": {
"node": ">=14.16"
},
@@ -15926,7 +15916,6 @@
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"devOptional": true,
"license": "Apache-2.0",
"peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -16155,7 +16144,6 @@
"resolved": "https://registry.npmjs.org/vite/-/vite-7.3.0.tgz",
"integrity": "sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==",
"license": "MIT",
"peer": true,
"dependencies": {
"esbuild": "^0.27.0",
"fdir": "^6.5.0",
@@ -16291,7 +16279,6 @@
"node_modules/vite/node_modules/picomatch": {
"version": "4.0.3",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},
@@ -16303,7 +16290,6 @@
"version": "3.2.4",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@types/chai": "^5.2.2",
"@vitest/expect": "3.2.4",
@@ -16461,7 +16447,6 @@
"version": "5.102.0",
"devOptional": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@types/eslint-scope": "^3.7.7",
"@types/estree": "^1.0.8",
@@ -16509,7 +16494,6 @@
"version": "4.10.0",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@discoveryjs/json-ext": "^0.5.0",
"@webpack-cli/configtest": "^1.2.0",
@@ -16772,7 +16756,6 @@
"version": "3.3.3",
"devOptional": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=10.13.0"
}
@@ -17343,7 +17326,6 @@
"integrity": "sha512-E4t7DJ9pESL6E3I8nFjPa4xGUd3PmiWDLsDztS2qXSJWfHtbQnwAWylaBvSNY48I3vr8PTqIZlyK8TE3V3CA4Q==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@vitest/expect": "4.0.16",
"@vitest/mocker": "4.0.16",
@@ -17434,7 +17416,7 @@
}
},
"packages/extension": {
"version": "0.1.0",
"version": "0.1.0.1300",
"license": "MIT",
"dependencies": {
"@codemirror/lang-javascript": "^6.2.4",
@@ -17575,6 +17557,484 @@
"name": "tlsn-wasm",
"version": "0.1.0-alpha.13",
"license": "MIT OR Apache-2.0"
},
"packages/ts-plugin-sample": {
"name": "@tlsn/ts-plugin-sample",
"version": "0.1.0-alpha.13",
"license": "MIT",
"dependencies": {
"@tlsn/plugin-sdk": "*"
},
"devDependencies": {
"esbuild": "^0.24.2",
"typescript": "^5.5.4"
}
},
"packages/ts-plugin-sample/node_modules/@esbuild/aix-ppc64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz",
"integrity": "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"aix"
],
"engines": {
"node": ">=18"
}
},
"packages/ts-plugin-sample/node_modules/@esbuild/android-arm": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.2.tgz",
"integrity": "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"packages/ts-plugin-sample/node_modules/@esbuild/android-arm64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz",
"integrity": "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"packages/ts-plugin-sample/node_modules/@esbuild/android-x64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.2.tgz",
"integrity": "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"packages/ts-plugin-sample/node_modules/@esbuild/darwin-arm64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz",
"integrity": "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=18"
}
},
"packages/ts-plugin-sample/node_modules/@esbuild/darwin-x64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz",
"integrity": "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=18"
}
},
"packages/ts-plugin-sample/node_modules/@esbuild/freebsd-arm64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz",
"integrity": "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=18"
}
},
"packages/ts-plugin-sample/node_modules/@esbuild/freebsd-x64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz",
"integrity": "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=18"
}
},
"packages/ts-plugin-sample/node_modules/@esbuild/linux-arm": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz",
"integrity": "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"packages/ts-plugin-sample/node_modules/@esbuild/linux-arm64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz",
"integrity": "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"packages/ts-plugin-sample/node_modules/@esbuild/linux-ia32": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz",
"integrity": "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"packages/ts-plugin-sample/node_modules/@esbuild/linux-loong64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz",
"integrity": "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==",
"cpu": [
"loong64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"packages/ts-plugin-sample/node_modules/@esbuild/linux-mips64el": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz",
"integrity": "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==",
"cpu": [
"mips64el"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"packages/ts-plugin-sample/node_modules/@esbuild/linux-ppc64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz",
"integrity": "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"packages/ts-plugin-sample/node_modules/@esbuild/linux-riscv64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz",
"integrity": "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==",
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"packages/ts-plugin-sample/node_modules/@esbuild/linux-s390x": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz",
"integrity": "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==",
"cpu": [
"s390x"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"packages/ts-plugin-sample/node_modules/@esbuild/linux-x64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz",
"integrity": "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"packages/ts-plugin-sample/node_modules/@esbuild/netbsd-arm64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz",
"integrity": "sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=18"
}
},
"packages/ts-plugin-sample/node_modules/@esbuild/netbsd-x64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz",
"integrity": "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=18"
}
},
"packages/ts-plugin-sample/node_modules/@esbuild/openbsd-arm64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz",
"integrity": "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=18"
}
},
"packages/ts-plugin-sample/node_modules/@esbuild/openbsd-x64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz",
"integrity": "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=18"
}
},
"packages/ts-plugin-sample/node_modules/@esbuild/sunos-x64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz",
"integrity": "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"sunos"
],
"engines": {
"node": ">=18"
}
},
"packages/ts-plugin-sample/node_modules/@esbuild/win32-arm64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz",
"integrity": "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"packages/ts-plugin-sample/node_modules/@esbuild/win32-ia32": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz",
"integrity": "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"packages/ts-plugin-sample/node_modules/@esbuild/win32-x64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz",
"integrity": "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"packages/ts-plugin-sample/node_modules/esbuild": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz",
"integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"bin": {
"esbuild": "bin/esbuild"
},
"engines": {
"node": ">=18"
},
"optionalDependencies": {
"@esbuild/aix-ppc64": "0.24.2",
"@esbuild/android-arm": "0.24.2",
"@esbuild/android-arm64": "0.24.2",
"@esbuild/android-x64": "0.24.2",
"@esbuild/darwin-arm64": "0.24.2",
"@esbuild/darwin-x64": "0.24.2",
"@esbuild/freebsd-arm64": "0.24.2",
"@esbuild/freebsd-x64": "0.24.2",
"@esbuild/linux-arm": "0.24.2",
"@esbuild/linux-arm64": "0.24.2",
"@esbuild/linux-ia32": "0.24.2",
"@esbuild/linux-loong64": "0.24.2",
"@esbuild/linux-mips64el": "0.24.2",
"@esbuild/linux-ppc64": "0.24.2",
"@esbuild/linux-riscv64": "0.24.2",
"@esbuild/linux-s390x": "0.24.2",
"@esbuild/linux-x64": "0.24.2",
"@esbuild/netbsd-arm64": "0.24.2",
"@esbuild/netbsd-x64": "0.24.2",
"@esbuild/openbsd-arm64": "0.24.2",
"@esbuild/openbsd-x64": "0.24.2",
"@esbuild/sunos-x64": "0.24.2",
"@esbuild/win32-arm64": "0.24.2",
"@esbuild/win32-ia32": "0.24.2",
"@esbuild/win32-x64": "0.24.2"
}
}
}
}

View File

@@ -5,6 +5,24 @@
"main": "dist/index.js",
"types": "dist/index.d.ts",
"type": "module",
"exports": {
".": {
"import": "./dist/index.js",
"types": "./dist/index.d.ts"
},
"./styles": {
"import": "./dist/styles.js",
"types": "./dist/styles.d.ts"
},
"./src": {
"import": "./dist/index.js",
"types": "./dist/index.d.ts"
},
"./src/types": {
"import": "./dist/index.js",
"types": "./dist/index.d.ts"
}
},
"scripts": {
"build": "vite build",
"test": "vitest",

View File

@@ -2,9 +2,7 @@
* Global type declarations for TLSNotary plugin runtime environment
*
* These functions are injected at runtime by the plugin sandbox.
* Import this file in your plugin to get TypeScript support:
*
* /// <reference types="@tlsn/plugin-sdk/globals" />
* They are automatically available as globals in TypeScript plugins.
*/
import type {
@@ -15,83 +13,123 @@ import type {
DomJson,
} from './types';
/**
* Create a div DOM element
*/
export type DivFunction = {
(options?: DomOptions, children?: DomJson[]): DomJson;
(children: DomJson[]): DomJson;
};
/**
* Create a button DOM element
*/
export type ButtonFunction = {
(options?: DomOptions, children?: DomJson[]): DomJson;
(children: DomJson[]): DomJson;
};
/**
* Open a new browser window
*/
export type OpenWindowFunction = (
url: string,
options?: {
width?: number;
height?: number;
showOverlay?: boolean;
}
) => Promise<{
windowId: number;
uuid: string;
tabId: number;
}>;
/**
* React-like effect hook that runs when dependencies change
*/
export type UseEffectFunction = (callback: () => void, deps: any[]) => void;
/**
* Subscribe to intercepted HTTP headers with filtering
*/
export type UseHeadersFunction = (
filter: (headers: InterceptedRequestHeader[]) => InterceptedRequestHeader[]
) => InterceptedRequestHeader[];
/**
* Subscribe to intercepted HTTP requests with filtering
*/
export type UseRequestsFunction = (
filter: (requests: InterceptedRequest[]) => InterceptedRequest[]
) => InterceptedRequest[];
/**
* Get state value (does not trigger re-render)
*/
export type UseStateFunction = <T>(key: string, defaultValue: T) => T;
/**
* Set state value (triggers UI re-render)
*/
export type SetStateFunction = <T>(key: string, value: T) => void;
/**
* Generate TLS proof using the unified prove() API
*/
export type ProveFunction = (
requestOptions: {
url: string;
method: string;
headers: Record<string, string | undefined>;
body?: string;
},
proverOptions: {
verifierUrl: string;
proxyUrl: string;
maxRecvData?: number;
maxSentData?: number;
handlers: Handler[];
}
) => Promise<any>;
/**
* Complete plugin execution and return result
*/
export type DoneFunction = (result?: any) => void;
/**
* Complete Plugin API surface available in the QuickJS sandbox
*/
export interface PluginAPI {
div: DivFunction;
button: ButtonFunction;
openWindow: OpenWindowFunction;
useEffect: UseEffectFunction;
useHeaders: UseHeadersFunction;
useRequests: UseRequestsFunction;
useState: UseStateFunction;
setState: SetStateFunction;
prove: ProveFunction;
done: DoneFunction;
}
/**
* Global declarations for plugin environment
*
* These are automatically available in TypeScript plugins without imports.
*/
declare global {
/**
* Create a div element
*/
function div(options?: DomOptions, children?: (DomJson | string)[]): DomJson;
function div(children?: (DomJson | string)[]): DomJson;
/**
* Create a button element
*/
function button(options?: DomOptions, children?: (DomJson | string)[]): DomJson;
function button(children?: (DomJson | string)[]): DomJson;
/**
* Get or initialize state value (React-like useState)
*/
function useState<T>(key: string, initialValue: T): T;
/**
* Update state value
*/
function setState<T>(key: string, value: T): void;
/**
* Run side effect when dependencies change (React-like useEffect)
*/
function useEffect(effect: () => void, deps: any[]): void;
/**
* Subscribe to intercepted HTTP headers
*/
function useHeaders(
filter: (headers: InterceptedRequestHeader[]) => InterceptedRequestHeader[],
): [InterceptedRequestHeader | undefined];
/**
* Subscribe to intercepted HTTP requests
*/
function useRequests(
filter: (requests: InterceptedRequest[]) => InterceptedRequest[],
): [InterceptedRequest | undefined];
/**
* Open a new browser window for user interaction
*/
function openWindow(
url: string,
options?: {
width?: number;
height?: number;
showOverlay?: boolean;
},
): Promise<void>;
/**
* Generate a TLS proof for an HTTP request
*/
function prove(
requestOptions: {
url: string;
method: string;
headers: Record<string, string>;
body?: string;
},
proverOptions: {
verifierUrl: string;
proxyUrl: string;
maxRecvData?: number;
maxSentData?: number;
handlers: Handler[];
},
): Promise<any>;
/**
* Complete plugin execution and return result
*/
function done(result?: any): void;
const div: DivFunction;
const button: ButtonFunction;
const openWindow: OpenWindowFunction;
const useEffect: UseEffectFunction;
const useHeaders: UseHeadersFunction;
const useRequests: UseRequestsFunction;
const useState: UseStateFunction;
const setState: SetStateFunction;
const prove: ProveFunction;
const done: DoneFunction;
}
export {};

View File

@@ -796,7 +796,40 @@ export async function extractConfig(code: string): Promise<PluginConfig | null>
}
// Export types
export type { PluginConfig, RequestPermission };
export type {
PluginConfig,
RequestPermission,
Handler,
StartLineHandler,
HeadersHandler,
BodyHandler,
AllHandler,
HandlerType,
HandlerPart,
HandlerAction,
InterceptedRequest,
InterceptedRequestHeader,
DomJson,
DomOptions,
OpenWindowResponse,
WindowMessage,
ExecutionContext,
} from './types';
// Export Plugin API types
export type {
PluginAPI,
DivFunction,
ButtonFunction,
OpenWindowFunction,
UseEffectFunction,
UseHeadersFunction,
UseRequestsFunction,
UseStateFunction,
SetStateFunction,
ProveFunction,
DoneFunction,
} from './globals';
// Re-export LogLevel for consumers
export { LogLevel } from '@tlsn/common';

View File

@@ -0,0 +1,361 @@
/**
* Tailwind-like style utilities for plugin UI components
*/
// =============================================================================
// DESIGN TOKENS
// =============================================================================
/**
* Color palette with Tailwind-like naming
* Non-opinionated color scales from 100-900
*/
const colorTokens = {
// Neutral
'white': '#ffffff',
'black': '#000000',
'transparent': 'transparent',
// Gray scale
'gray-50': '#f9fafb',
'gray-100': '#f3f4f6',
'gray-200': '#e5e7eb',
'gray-300': '#d1d5db',
'gray-400': '#9ca3af',
'gray-500': '#6b7280',
'gray-600': '#4b5563',
'gray-700': '#374151',
'gray-800': '#1f2937',
'gray-900': '#111827',
// Blue
'blue-100': '#dbeafe',
'blue-200': '#bfdbfe',
'blue-300': '#93c5fd',
'blue-400': '#60a5fa',
'blue-500': '#3b82f6',
'blue-600': '#2563eb',
'blue-700': '#1d4ed8',
'blue-800': '#1e40af',
'blue-900': '#1e3a8a',
// Purple
'purple-100': '#f3e8ff',
'purple-200': '#e9d5ff',
'purple-300': '#d8b4fe',
'purple-400': '#c084fc',
'purple-500': '#a855f7',
'purple-600': '#9333ea',
'purple-700': '#7e22ce',
'purple-800': '#6b21a8',
'purple-900': '#581c87',
// Red
'red-100': '#fee2e2',
'red-200': '#fecaca',
'red-300': '#fca5a5',
'red-400': '#f87171',
'red-500': '#ef4444',
'red-600': '#dc2626',
'red-700': '#b91c1c',
'red-800': '#991b1b',
'red-900': '#7f1d1d',
// Yellow
'yellow-100': '#fef3c7',
'yellow-200': '#fde68a',
'yellow-300': '#fcd34d',
'yellow-400': '#fbbf24',
'yellow-500': '#f59e0b',
'yellow-600': '#d97706',
'yellow-700': '#b45309',
'yellow-800': '#92400e',
'yellow-900': '#78350f',
// Orange
'orange-100': '#ffedd5',
'orange-200': '#fed7aa',
'orange-300': '#fdba74',
'orange-400': '#fb923c',
'orange-500': '#f97316',
'orange-600': '#ea580c',
'orange-700': '#c2410c',
'orange-800': '#9a3412',
'orange-900': '#7c2d12',
// Green
'green-100': '#d1fae5',
'green-200': '#a7f3d0',
'green-300': '#6ee7b7',
'green-400': '#34d399',
'green-500': '#10b981',
'green-600': '#059669',
'green-700': '#047857',
'green-800': '#065f46',
'green-900': '#064e3b',
} as const;
/**
* Spacing scale
*/
const spacingTokens = {
'0': '0',
'1': '4px',
'2': '8px',
'3': '12px',
'4': '16px',
'5': '20px',
'6': '24px',
'8': '32px',
'10': '40px',
'12': '48px',
// Named aliases
'xs': '8px',
'sm': '12px',
'md': '16px',
'lg': '20px',
'xl': '24px',
} as const;
/**
* Font sizes
*/
const fontSizeTokens = {
'xs': '12px',
'sm': '14px',
'md': '15px',
'base': '16px',
'lg': '18px',
'xl': '20px',
'2xl': '24px',
} as const;
/**
* Font weights
*/
const fontWeightTokens = {
'normal': '400',
'medium': '500',
'semibold': '600',
'bold': '700',
} as const;
/**
* Border radius
*/
const borderRadiusTokens = {
'none': '0',
'sm': '6px',
'md': '8px',
'lg': '12px',
'full': '9999px',
'circle': '50%',
} as const;
/**
* Box shadows
*/
const shadowTokens = {
'sm': '0 2px 4px rgba(0,0,0,0.1)',
'md': '0 -2px 10px rgba(0,0,0,0.1)',
'lg': '0 4px 8px rgba(0,0,0,0.3)',
'xl': '0 10px 25px rgba(0,0,0,0.2)',
} as const;
// =============================================================================
// TYPE DEFINITIONS
// =============================================================================
type StyleObject = Record<string, string>;
type StyleHelper = StyleObject | false | null | undefined;
// =============================================================================
// HELPER FUNCTIONS
// =============================================================================
/**
* Resolve a color token to its CSS value
*/
function resolveColor(token: string): string {
return colorTokens[token as keyof typeof colorTokens] || token;
}
/**
* Resolve a spacing token to its CSS value
*/
function resolveSpacing(token: string): string {
return spacingTokens[token as keyof typeof spacingTokens] || token;
}
/**
* Resolve a font size token to its CSS value
*/
function resolveFontSize(token: string): string {
return fontSizeTokens[token as keyof typeof fontSizeTokens] || token;
}
/**
* Resolve a font weight token to its CSS value
*/
function resolveFontWeight(token: string): string {
return fontWeightTokens[token as keyof typeof fontWeightTokens] || token;
}
/**
* Resolve a border radius token to its CSS value
*/
function resolveBorderRadius(token: string): string {
return borderRadiusTokens[token as keyof typeof borderRadiusTokens] || token;
}
/**
* Resolve a shadow token to its CSS value
*/
function resolveShadow(token: string): string {
return shadowTokens[token as keyof typeof shadowTokens] || token;
}
// =============================================================================
// STYLE HELPER FUNCTIONS
// =============================================================================
// Color helpers
export const color = (value: string): StyleObject => ({ color: resolveColor(value) });
export const bgColor = (value: string): StyleObject => ({ backgroundColor: resolveColor(value) });
export const borderColor = (value: string): StyleObject => ({ borderColor: resolveColor(value) });
export const bg = bgColor; // Alias
// Spacing helpers - Padding
export const padding = (value: string): StyleObject => ({ padding: resolveSpacing(value) });
export const paddingX = (value: string): StyleObject => {
const val = resolveSpacing(value);
return { paddingLeft: val, paddingRight: val };
};
export const paddingY = (value: string): StyleObject => {
const val = resolveSpacing(value);
return { paddingTop: val, paddingBottom: val };
};
export const paddingTop = (value: string): StyleObject => ({ paddingTop: resolveSpacing(value) });
export const paddingBottom = (value: string): StyleObject => ({ paddingBottom: resolveSpacing(value) });
export const paddingLeft = (value: string): StyleObject => ({ paddingLeft: resolveSpacing(value) });
export const paddingRight = (value: string): StyleObject => ({ paddingRight: resolveSpacing(value) });
// Aliases
export const p = padding;
export const px = paddingX;
export const py = paddingY;
export const pt = paddingTop;
export const pb = paddingBottom;
export const pl = paddingLeft;
export const pr = paddingRight;
// Spacing helpers - Margin
export const margin = (value: string): StyleObject => ({ margin: resolveSpacing(value) });
export const marginX = (value: string): StyleObject => {
const val = resolveSpacing(value);
return { marginLeft: val, marginRight: val };
};
export const marginY = (value: string): StyleObject => {
const val = resolveSpacing(value);
return { marginTop: val, marginBottom: val };
};
export const marginTop = (value: string): StyleObject => ({ marginTop: resolveSpacing(value) });
export const marginBottom = (value: string): StyleObject => ({ marginBottom: resolveSpacing(value) });
export const marginLeft = (value: string): StyleObject => ({ marginLeft: resolveSpacing(value) });
export const marginRight = (value: string): StyleObject => ({ marginRight: resolveSpacing(value) });
// Aliases
export const m = margin;
export const mx = marginX;
export const my = marginY;
export const mt = marginTop;
export const mb = marginBottom;
export const ml = marginLeft;
export const mr = marginRight;
// Typography helpers
export const fontSize = (value: string): StyleObject => ({ fontSize: resolveFontSize(value) });
export const fontWeight = (value: string): StyleObject => ({ fontWeight: resolveFontWeight(value) });
export const textAlign = (value: string): StyleObject => ({ textAlign: value });
export const fontFamily = (value: string): StyleObject => ({ fontFamily: value });
// Layout helpers
export const display = (value: string): StyleObject => ({ display: value });
export const position = (value: string): StyleObject => ({ position: value });
export const width = (value: string): StyleObject => ({ width: value });
export const height = (value: string): StyleObject => ({ height: value });
export const minWidth = (value: string): StyleObject => ({ minWidth: value });
export const minHeight = (value: string): StyleObject => ({ minHeight: value });
export const maxWidth = (value: string): StyleObject => ({ maxWidth: value });
export const maxHeight = (value: string): StyleObject => ({ maxHeight: value });
// Flexbox helpers
export const flex = (value: string = '1'): StyleObject => ({ flex: value });
export const flexDirection = (value: string): StyleObject => ({ flexDirection: value });
export const alignItems = (value: string): StyleObject => ({ alignItems: value });
export const justifyContent = (value: string): StyleObject => ({ justifyContent: value });
export const flexWrap = (value: string): StyleObject => ({ flexWrap: value });
// Positioning helpers
export const top = (value: string): StyleObject => ({ top: resolveSpacing(value) });
export const bottom = (value: string): StyleObject => ({ bottom: resolveSpacing(value) });
export const left = (value: string): StyleObject => ({ left: resolveSpacing(value) });
export const right = (value: string): StyleObject => ({ right: resolveSpacing(value) });
// Border helpers
export const border = (value: string): StyleObject => ({ border: value });
export const borderRadius = (value: string): StyleObject => ({ borderRadius: resolveBorderRadius(value) });
export const borderWidth = (value: string): StyleObject => ({ borderWidth: value });
// Visual helpers
export const boxShadow = (value: string): StyleObject => ({ boxShadow: resolveShadow(value) });
export const opacity = (value: string): StyleObject => ({ opacity: value });
export const overflow = (value: string): StyleObject => ({ overflow: value });
export const zIndex = (value: string): StyleObject => ({ zIndex: value });
// Interaction helpers
export const cursor = (value: string): StyleObject => ({ cursor: value });
export const pointerEvents = (value: string): StyleObject => ({ pointerEvents: value });
// Transition/Animation helpers
export const transition = (value: string = 'all 0.2s ease'): StyleObject => ({ transition: value });
// Background helpers
export const background = (value: string): StyleObject => ({ background: value });
// =============================================================================
// MAIN INLINE STYLE FUNCTION
// =============================================================================
/**
* Combine multiple style helpers into a single style object
* Automatically filters out falsey values for conditional styling
*
* @example
* inlineStyle(
* textAlign('center'),
* color('gray-500'),
* padding('sm'),
* bgColor('yellow-100'),
* isPending && display('none'),
* { borderRadius: '12px' }
* )
*/
export function inlineStyle(...styles: StyleHelper[]): StyleObject {
return styles.reduce<StyleObject>((acc, style) => {
if (style) {
Object.assign(acc, style);
}
return acc;
}, {});
}
// =============================================================================
// EXPORTS
// =============================================================================
/**
* Common font family
*/
export const defaultFontFamily = '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif';

View File

@@ -15,26 +15,16 @@ export default defineConfig({
build: {
target: 'es2020',
lib: {
entry: path.resolve(__dirname, 'src/index.ts'),
name: 'TLSNPluginSDK',
formats: ['es', 'cjs', 'umd'],
fileName: (format) => {
if (format === 'es') return 'index.js';
if (format === 'cjs') return 'index.cjs';
if (format === 'umd') return 'index.umd.js';
return `index.${format}.js`;
entry: {
index: path.resolve(__dirname, 'src/index.ts'),
styles: path.resolve(__dirname, 'src/styles.ts'),
},
formats: ['es'],
},
rollupOptions: {
// Externalize QuickJS and Node.js dependencies
external: ['@sebastianwessel/quickjs', '@jitl/quickjs-ng-wasmfile-release-sync', /^node:.*/],
external: ['@sebastianwessel/quickjs', '@jitl/quickjs-ng-wasmfile-release-sync', /^node:.*/, '@tlsn/common'],
output: {
// Provide global variables to use in the UMD build
// for externalized deps
globals: {
'@sebastianwessel/quickjs': 'QuickJS',
'@jitl/quickjs-ng-wasmfile-release-sync': 'QuickJSVariant',
},
exports: 'named',
},
},

19
packages/ts-plugin-sample/.gitignore vendored Normal file
View File

@@ -0,0 +1,19 @@
# Build output
build/
*.js
*.js.map
*.d.ts
# Dependencies
node_modules/
# Editor
.vscode/
.idea/
*.swp
*.swo
*~
# OS
.DS_Store
Thumbs.db

View File

@@ -0,0 +1,312 @@
# TypeScript Plugin Sample
A TypeScript implementation of the X Profile Prover plugin demonstrating how to write type-safe TLSN plugins.
## Overview
This package shows how to:
- Write TLSN plugins in TypeScript with full type safety
- Import types from `@tlsn/plugin-sdk`
- Compile TypeScript plugins to JavaScript for execution
- Use all plugin API features (prove, openWindow, UI rendering, hooks)
## Quick Start
### Installation
```bash
cd packages/ts-plugin-sample
npm install
```
### Build
```bash
npm run build
```
This bundles `src/index.ts` and `src/config.ts` into a single `build/index.js` file with clean `export default` statement.
### Development Mode
```bash
npm run dev
```
Watches for changes and rebuilds automatically.
### Type Checking
```bash
npm run typecheck
```
Runs TypeScript type checking without emitting files.
## Project Structure
```
ts-plugin-sample/
├── package.json # Dependencies and build scripts
├── tsconfig.json # TypeScript compiler configuration
├── build-wrapper.cjs # Custom build script for clean exports
├── src/
│ ├── index.ts # TypeScript plugin implementation
│ └── config.ts # Plugin configuration
├── build/
│ ├── index.js # Bundled plugin with export default
│ └── index.js.map # Source map for debugging
└── README.md
```
## TypeScript Features
### Type Imports
Import types from the plugin SDK for compile-time checking:
```typescript
import type {
PluginConfig,
RequestPermission,
Handler,
HandlerType,
HandlerPart,
HandlerAction,
InterceptedRequestHeader,
DomJson,
} from '@tlsn/plugin-sdk';
```
### Plugin Config Type Safety
```typescript
const config: PluginConfig = {
name: 'X Profile Prover',
description: 'This plugin will prove your X.com profile.',
version: '0.1.0',
author: 'TLSN Team',
requests: [
{
method: 'GET',
host: 'api.x.com',
pathname: '/1.1/account/settings.json',
verifierUrl: 'https://verifier.tlsnotary.org',
} satisfies RequestPermission,
],
urls: ['https://x.com/*'],
};
```
### Plugin API Globals
The plugin execution environment (QuickJS sandbox) provides these globals:
```typescript
// Declare types for globals injected by the sandbox
declare function div(options?: DomOptions, children?: DomJson[]): DomJson;
declare function button(options?: DomOptions, children?: DomJson[]): DomJson;
declare function openWindow(url: string, options?: {...}): Promise<{...}>;
declare function useEffect(callback: () => void, deps: any[]): void;
declare function useHeaders(filter: (headers: InterceptedRequestHeader[]) => InterceptedRequestHeader[]): InterceptedRequestHeader[];
declare function useState<T>(key: string, defaultValue: T): T;
declare function setState<T>(key: string, value: T): void;
declare function prove(requestOptions: {...}, proverOptions: {...}): Promise<any>;
declare function done(result?: any): void;
```
### Type-Safe Handlers
```typescript
const handlers: Handler[] = [
{
type: 'SENT' as HandlerType,
part: 'START_LINE' as HandlerPart,
action: 'REVEAL' as HandlerAction,
},
{
type: 'RECV' as HandlerType,
part: 'BODY' as HandlerPart,
action: 'REVEAL' as HandlerAction,
params: {
type: 'json',
path: 'screen_name',
},
},
];
```
## Key Differences from JavaScript
### 1. Type Annotations
```typescript
// JavaScript
function onClick() {
const isRequestPending = useState('isRequestPending', false);
// ...
}
// TypeScript
async function onClick(): Promise<void> {
const isRequestPending = useState<boolean>('isRequestPending', false);
// ...
}
```
### 2. Interface Compliance
TypeScript ensures your config matches the `PluginConfig` interface:
```typescript
const config: PluginConfig = {
name: 'X Profile Prover', // ✓ Required
description: 'Proves X profile', // ✓ Required
version: '0.1.0', // ✓ Optional
requests: [...], // ✓ Optional
urls: [...], // ✓ Optional
// TypeScript will error if required fields are missing!
};
```
### 3. Compile-Time Errors
```typescript
// This will error at compile time:
const handler: Handler = {
type: 'INVALID', // ❌ Type '"INVALID"' is not assignable to type 'HandlerType'
part: 'BODY',
action: 'REVEAL',
};
// This will pass:
const handler: Handler = {
type: 'RECV', // ✓ Valid HandlerType
part: 'BODY',
action: 'REVEAL',
};
```
## Build Configuration
### Build Tool: esbuild + Custom Wrapper
The plugin uses **esbuild** with a custom build wrapper:
- **Single file output:** All code bundled into `build/index.js` (7.2KB, 257 lines)
- **ES Module format:** Standard `export default` statement
- **No external imports:** All dependencies bundled inline
- **Inlined enums:** Handler enums included directly (no SDK imports)
- **Source maps:** Generated for debugging (`build/index.js.map`)
- **Fast builds:** ~10ms typical build time
The build wrapper (`build-wrapper.cjs`) transforms the esbuild output to use a clean `export default` statement matching the JavaScript plugin format.
### TypeScript Config (`tsconfig.json`)
TypeScript is used for type checking only (`npm run typecheck`):
- **Target:** ES2020 (modern browser features)
- **Strict:** Full type checking enabled
- **Global types:** Includes SDK globals for plugin API functions
## Loading in Extension
After building, the compiled `build/index.js` can be loaded in the TLSN extension:
1. Build the plugin: `npm run build`
2. The output is `build/index.js` with clean ES module export:
```javascript
export default {
main,
onClick,
expandUI,
minimizeUI,
config,
};
```
3. Load and execute in the extension:
```javascript
const pluginCode = fs.readFileSync('build/index.js', 'utf8');
const plugin = await sandbox.eval(pluginCode);
// plugin = { main, onClick, expandUI, minimizeUI, config }
```
4. The plugin executes with full type safety verified at compile time
**Output Characteristics:**
- ✅ Single file with `export default` statement
- ✅ No external imports (all dependencies bundled)
- ✅ Inlined enums (no SDK runtime dependency)
- ✅ ES Module format
- ✅ Matches JavaScript plugin structure
## Comparison with JavaScript Plugin
See `packages/demo/generated/twitter.js` for the equivalent JavaScript implementation.
**Advantages of TypeScript:**
- Compile-time type checking
- IDE autocomplete and IntelliSense
- Catches errors before runtime
- Better documentation via types
- Refactoring safety
**Trade-offs:**
- Requires build step
- Slightly more verbose (type annotations)
- Need to maintain type declarations
## Development Tips
### 1. Use Type Inference
TypeScript can infer many types:
```typescript
// Explicit (verbose)
const header: InterceptedRequestHeader | undefined = useHeaders(...)[0];
// Inferred (cleaner)
const [header] = useHeaders(...); // Type inferred from useHeaders return type
```
### 2. Use `satisfies` for Config
```typescript
// Good: Type-checked but allows literal types
requests: [
{
method: 'GET',
host: 'api.x.com',
// ...
} satisfies RequestPermission,
]
// Also good: Full type annotation
const request: RequestPermission = {
method: 'GET',
// ...
};
```
### 3. Enable Strict Mode
Keep `"strict": true` in `tsconfig.json` for maximum type safety.
### 4. Check Build Errors
```bash
npm run build
# Check for type errors without building
npx tsc --noEmit
```
## Resources
- [TypeScript Documentation](https://www.typescriptlang.org/docs/)
- [Plugin SDK Types](../plugin-sdk/src/types.ts)
- [JavaScript Plugin Example](../demo/generated/twitter.js)
- [TLSN Extension Docs](../../CLAUDE.md)
## License
MIT

View File

@@ -0,0 +1,20 @@
#!/usr/bin/env node
/**
* Build wrapper to create clean export default statement
*/
const fs = require('fs');
const { execSync } = require('child_process');
// Run esbuild
console.log('Building with esbuild...');
execSync('esbuild src/index.ts --bundle --format=esm --outfile=build/index.js --sourcemap --external:@sebastianwessel/quickjs --external:@jitl/quickjs-ng-wasmfile-release-sync --external:uuid --external:fast-deep-equal', {
stdio: 'inherit'
});
// Read the generated code
let code = fs.readFileSync('build/index.js', 'utf8');
// Write back
fs.writeFileSync('build/index.js', code);
console.log('✓ Build complete: build/index.js');

View File

@@ -0,0 +1,33 @@
{
"name": "@tlsn/ts-plugin-sample",
"version": "0.1.0-alpha.13",
"description": "TypeScript plugin sample for TLSN extension",
"type": "module",
"main": "build/index.js",
"scripts": {
"build": "node build-wrapper.cjs",
"clean": "rm -rf build",
"dev": "esbuild src/index.ts --bundle --format=esm --outfile=build/index.js --sourcemap --watch",
"typecheck": "tsc --noEmit"
},
"keywords": [
"tlsn",
"plugin",
"typescript",
"example"
],
"author": "TLSN Team",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/tlsnotary/tlsn-extension.git",
"directory": "packages/ts-plugin-sample"
},
"dependencies": {
"@tlsn/plugin-sdk": "*"
},
"devDependencies": {
"esbuild": "^0.24.2",
"typescript": "^5.5.4"
}
}

View File

@@ -0,0 +1,58 @@
/**
* FloatingButton Component
*
* Minimized floating action button
*/
import type { DomJson } from '@tlsn/plugin-sdk';
import {
inlineStyle,
position,
bottom,
right,
width,
height,
borderRadius,
bgColor,
boxShadow,
zIndex,
display,
alignItems,
justifyContent,
cursor,
fontSize,
color,
transition,
} from '@tlsn/plugin-sdk/styles';
export interface FloatingButtonProps {
onClick: string;
icon?: string;
}
export function FloatingButton({ onClick, icon = '🔐' }: FloatingButtonProps): DomJson {
return div(
{
style: inlineStyle(
position('fixed'),
bottom('lg'),
right('lg'),
width('60px'),
height('60px'),
borderRadius('circle'),
bgColor('#4CAF50'),
boxShadow('lg'),
zIndex('999999'),
display('flex'),
alignItems('center'),
justifyContent('center'),
cursor('pointer'),
fontSize('2xl'),
color('white'),
transition()
),
onclick: onClick,
},
[icon]
);
}

View File

@@ -0,0 +1,32 @@
/**
* LoginPrompt Component
*
* Displays a message prompting the user to login
*/
import type { DomJson } from '@tlsn/plugin-sdk';
import {
inlineStyle,
textAlign,
color,
padding,
bgColor,
borderRadius,
border,
} from '@tlsn/plugin-sdk/styles';
export function LoginPrompt(): DomJson {
return div(
{
style: inlineStyle(
textAlign('center'),
color('gray-600'),
padding('sm'),
bgColor('yellow-100'),
borderRadius('sm'),
border('1px solid #ffeaa7')
),
},
['Please login to x.com to continue']
);
}

View File

@@ -0,0 +1,75 @@
/**
* OverlayHeader Component
*
* Header bar with title and minimize button
*/
import type { DomJson } from '@tlsn/plugin-sdk';
import {
inlineStyle,
background,
paddingY,
paddingX,
display,
justifyContent,
alignItems,
color,
fontWeight,
fontSize,
border,
cursor,
padding,
width,
height,
} from '@tlsn/plugin-sdk/styles';
export interface OverlayHeaderProps {
title: string;
onMinimize: string;
}
export function OverlayHeader({ title, onMinimize }: OverlayHeaderProps): DomJson {
return div(
{
style: inlineStyle(
background('linear-gradient(135deg, #667eea 0%, #764ba2 100%)'),
paddingY('sm'),
paddingX('md'),
display('flex'),
justifyContent('space-between'),
alignItems('center'),
color('white')
),
},
[
div(
{
style: inlineStyle(
fontWeight('semibold'),
fontSize('lg')
),
},
[title]
),
button(
{
style: inlineStyle(
background('transparent'),
border('none'),
color('white'),
fontSize('xl'),
cursor('pointer'),
padding('0'),
width('24px'),
height('24px'),
display('flex'),
alignItems('center'),
justifyContent('center')
),
onclick: onMinimize,
},
['']
),
]
);
}

View File

@@ -0,0 +1,85 @@
/**
* PluginOverlay Component
*
* Main plugin UI overlay container
*/
import type { DomJson } from '@tlsn/plugin-sdk';
import {
inlineStyle,
position,
bottom,
right,
width,
borderRadius,
bgColor,
boxShadow,
zIndex,
fontSize,
fontFamily,
overflow,
padding,
defaultFontFamily,
} from '@tlsn/plugin-sdk/styles';
import { OverlayHeader } from './OverlayHeader';
import { StatusIndicator } from './StatusIndicator';
import { ProveButton } from './ProveButton';
import { LoginPrompt } from './LoginPrompt';
export interface PluginOverlayProps {
title: string;
isConnected: boolean;
isPending: boolean;
onMinimize: string;
onProve: string;
}
export function PluginOverlay({
title,
isConnected,
isPending,
onMinimize,
onProve,
}: PluginOverlayProps): DomJson {
return div(
{
style: inlineStyle(
position('fixed'),
bottom('0'),
right('xs'),
width('280px'),
borderRadius('md'),
{ borderRadius: '8px 8px 0 0' }, // Custom override for specific corner rounding
bgColor('white'),
boxShadow('md'),
zIndex('999999'),
fontSize('sm'),
fontFamily(defaultFontFamily),
overflow('hidden')
),
},
[
// Header
OverlayHeader({ title, onMinimize }),
// Content area
div(
{
style: inlineStyle(
padding('lg'),
bgColor('gray-100')
),
},
[
// Status indicator
StatusIndicator({ isConnected }),
// Conditional content: button or login prompt
isConnected
? ProveButton({ onClick: onProve, isPending })
: LoginPrompt(),
]
),
]
);
}

View File

@@ -0,0 +1,49 @@
/**
* ProveButton Component
*
* Button for initiating proof generation
*/
import type { DomJson } from '@tlsn/plugin-sdk';
import {
inlineStyle,
width,
padding,
background,
color,
border,
borderRadius,
fontSize,
fontWeight,
cursor,
transition,
opacity,
} from '@tlsn/plugin-sdk/styles';
export interface ProveButtonProps {
onClick: string;
isPending: boolean;
}
export function ProveButton({ onClick, isPending }: ProveButtonProps): DomJson {
return button(
{
style: inlineStyle(
width('100%'),
padding('sm'),
background('linear-gradient(135deg, #667eea 0%, #764ba2 100%)'),
color('white'),
border('none'),
borderRadius('sm'),
fontSize('md'),
fontWeight('semibold'),
cursor('pointer'),
transition(),
isPending && opacity('0.6'),
isPending && cursor('not-allowed')
),
onclick: onClick,
},
[isPending ? 'Generating Proof...' : 'Prove']
);
}

View File

@@ -0,0 +1,61 @@
/**
* StatusIndicator Component
*
* Shows connection status with visual indicator
*/
import type { DomJson } from '@tlsn/plugin-sdk';
import {
inlineStyle,
display,
alignItems,
marginBottom,
width,
height,
borderRadius,
bgColor,
marginRight,
fontSize,
color,
} from '@tlsn/plugin-sdk/styles';
export interface StatusIndicatorProps {
isConnected: boolean;
}
export function StatusIndicator({ isConnected }: StatusIndicatorProps): DomJson {
return div(
{
style: inlineStyle(
display('flex'),
alignItems('center'),
marginBottom('md')
),
},
[
// Status dot
div(
{
style: inlineStyle(
width('8px'),
height('8px'),
borderRadius('circle'),
bgColor(isConnected ? '#48bb78' : '#cbd5e0'),
marginRight('2')
),
},
[]
),
// Status text
div(
{
style: inlineStyle(
fontSize('sm'),
color('gray-700')
),
},
[isConnected ? 'Connected' : 'Waiting for connection...']
),
]
);
}

View File

@@ -0,0 +1,22 @@
/**
* Component exports
*
* Centralized export point for all UI components
*/
export { FloatingButton } from './FloatingButton';
export type { FloatingButtonProps } from './FloatingButton';
export { PluginOverlay } from './PluginOverlay';
export type { PluginOverlayProps } from './PluginOverlay';
export { OverlayHeader } from './OverlayHeader';
export type { OverlayHeaderProps } from './OverlayHeader';
export { StatusIndicator } from './StatusIndicator';
export type { StatusIndicatorProps } from './StatusIndicator';
export { ProveButton } from './ProveButton';
export type { ProveButtonProps } from './ProveButton';
export { LoginPrompt } from './LoginPrompt';

View File

@@ -0,0 +1,24 @@
/**
* Plugin Configuration
*
* Defines metadata and permissions for the X Profile Prover plugin.
*/
// Type imports only (stripped at compile time)
import type { PluginConfig, RequestPermission } from '@tlsn/plugin-sdk';
export const config: PluginConfig = {
name: 'X Profile Prover',
description: 'This plugin will prove your X.com profile.',
version: '0.1.0',
author: 'TLSN Team',
requests: [
{
method: 'GET',
host: 'api.x.com',
pathname: '/1.1/account/settings.json',
verifierUrl: 'http://localhost:7047',
} satisfies RequestPermission,
],
urls: ['https://x.com/*'],
};

View File

@@ -0,0 +1,211 @@
/**
* X Profile Prover - TypeScript Plugin Sample
*
* This is a TypeScript implementation of the X.com profile prover plugin.
* It demonstrates how to write type-safe TLSN plugins using TypeScript.
*/
// =============================================================================
// IMPORTS
// =============================================================================
/**
* Import types from the plugin SDK (type-only, stripped at compile time).
*
* The plugin API functions (div, button, openWindow, etc.) are declared globally
* via the SDK type declarations.
*/
import type { Handler, DomJson } from '@tlsn/plugin-sdk';
import { config } from './config';
import { FloatingButton, PluginOverlay } from './components';
// =============================================================================
// HANDLER ENUMS (Inlined for standalone execution)
// =============================================================================
/**
* These enum values are inlined instead of imported to create a standalone
* JavaScript file with no external dependencies.
*/
enum HandlerType {
SENT = 'SENT',
RECV = 'RECV',
}
enum HandlerPart {
START_LINE = 'START_LINE',
PROTOCOL = 'PROTOCOL',
METHOD = 'METHOD',
REQUEST_TARGET = 'REQUEST_TARGET',
STATUS_CODE = 'STATUS_CODE',
HEADERS = 'HEADERS',
BODY = 'BODY',
ALL = 'ALL',
}
enum HandlerAction {
REVEAL = 'REVEAL',
PEDERSEN = 'PEDERSEN',
}
// =============================================================================
// PROOF GENERATION CALLBACK
// =============================================================================
/**
* This function is triggered when the user clicks the "Prove" button.
* It extracts authentication headers from intercepted requests and generates
* a TLSNotary proof using the unified prove() API.
*/
async function onClick(): Promise<void> {
const isRequestPending = useState<boolean>('isRequestPending', false);
if (isRequestPending) return;
setState('isRequestPending', true);
// Step 1: Get the intercepted header from the X.com API request
const [header] = useHeaders((headers) => {
return headers.filter((header) =>
header.url.includes('https://api.x.com/1.1/account/settings.json')
);
});
if (!header) {
setState('isRequestPending', false);
return;
}
// Step 2: Extract authentication headers from the intercepted request
const headers: Record<string, string | undefined> = {
cookie: header.requestHeaders.find((h) => h.name === 'Cookie')?.value,
'x-csrf-token': header.requestHeaders.find((h) => h.name === 'x-csrf-token')?.value,
'x-client-transaction-id': header.requestHeaders.find(
(h) => h.name === 'x-client-transaction-id'
)?.value,
Host: 'api.x.com',
authorization: header.requestHeaders.find((h) => h.name === 'authorization')?.value,
'Accept-Encoding': 'identity',
Connection: 'close',
};
// Step 3: Generate TLS proof using the unified prove() API
const resp = await prove(
// Request options
{
url: 'https://api.x.com/1.1/account/settings.json',
method: 'GET',
headers: headers,
},
// Prover options
{
verifierUrl: 'http://localhost:7047',
proxyUrl: 'ws://localhost:7047/proxy?token=api.x.com',
maxRecvData: 4000,
maxSentData: 2000,
handlers: [
// Reveal the request start line
{
type: HandlerType.SENT,
part: HandlerPart.START_LINE,
action: HandlerAction.REVEAL,
} satisfies Handler,
// Reveal the response start line
{
type: HandlerType.RECV,
part: HandlerPart.START_LINE,
action: HandlerAction.REVEAL,
} satisfies Handler,
// Reveal the 'date' header from the response
{
type: HandlerType.RECV,
part: HandlerPart.HEADERS,
action: HandlerAction.REVEAL,
params: {
key: 'date',
},
} satisfies Handler,
// Reveal the 'screen_name' field from the JSON response body
{
type: HandlerType.RECV,
part: HandlerPart.BODY,
action: HandlerAction.REVEAL,
params: {
type: 'json' as const,
path: 'screen_name',
},
} satisfies Handler,
],
}
);
// Step 4: Complete plugin execution and return the proof result
done(JSON.stringify(resp));
}
/**
* Expand the minimized UI to show full plugin interface
*/
function expandUI(): void {
setState('isMinimized', false);
}
/**
* Minimize the UI to a floating action button
*/
function minimizeUI(): void {
setState('isMinimized', true);
}
// =============================================================================
// MAIN UI FUNCTION
// =============================================================================
/**
* The main() function is called reactively whenever plugin state changes.
* It returns a DOM structure that is rendered as the plugin UI.
*/
function main(): DomJson {
// Subscribe to intercepted headers for the X.com API endpoint
const [header] = useHeaders((headers) =>
headers.filter((header) => header.url.includes('https://api.x.com/1.1/account/settings.json'))
);
const isMinimized = useState<boolean>('isMinimized', false);
const isRequestPending = useState<boolean>('isRequestPending', false);
// Run once on plugin load: Open X.com in a new window
useEffect(() => {
openWindow('https://x.com');
}, []);
// If minimized, show floating action button
if (isMinimized) {
return FloatingButton({ onClick: 'expandUI' });
}
// Render the plugin UI overlay
return PluginOverlay({
title: 'X Profile Prover',
isConnected: !!header,
isPending: isRequestPending,
onMinimize: 'minimizeUI',
onProve: 'onClick',
});
}
// =============================================================================
// PLUGIN EXPORTS
// =============================================================================
/**
* All plugins must export an object with these properties:
* - main: The reactive UI rendering function
* - onClick: Click handler callback for buttons
* - config: Plugin metadata
*
* Additional exported functions (expandUI, minimizeUI) are also available
* as click handlers referenced by the 'onclick' property in DOM elements.
*/
export default {
main,
onClick,
expandUI,
minimizeUI,
config,
};

View File

@@ -0,0 +1,40 @@
{
"compilerOptions": {
/* Language and Environment */
"target": "ES2020",
"lib": ["ES2020"],
/* Modules */
"module": "ES2020",
"moduleResolution": "bundler",
"resolveJsonModule": true,
/* Emit */
"outDir": "./build",
"sourceMap": true,
"removeComments": false,
/* Interop Constraints */
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
/* Type Checking */
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
/* Completeness */
"skipLibCheck": true
},
"include": [
"src/**/*",
"../plugin-sdk/src/globals.d.ts"
],
"exclude": ["node_modules", "build"]
}