Compare commits
No commits in common. "gh-pages" and "main" have entirely different histories.
8
.Rbuildignore
Normal file
8
.Rbuildignore
Normal file
@ -0,0 +1,8 @@
|
||||
^LICENSE\.md$
|
||||
^README\.Rmd$
|
||||
^Makefile$
|
||||
node_modules
|
||||
^\.github$
|
||||
^_pkgdown\.yml$
|
||||
^docs$
|
||||
^pkgdown$
|
||||
1
.github/.gitignore
vendored
Normal file
1
.github/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
*.html
|
||||
29
.github/workflows/R-CMD-check.yaml
vendored
Normal file
29
.github/workflows/R-CMD-check.yaml
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples
|
||||
# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help
|
||||
on: [push, pull_request]
|
||||
name: R CMD check
|
||||
|
||||
permissions: read-all
|
||||
|
||||
jobs:
|
||||
R-CMD-check:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
|
||||
R_KEEP_PKG_SOURCE: yes
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: r-lib/actions/setup-r@v2
|
||||
with:
|
||||
use-public-rspm: true
|
||||
|
||||
- uses: r-lib/actions/setup-r-dependencies@v2
|
||||
with:
|
||||
extra-packages: any::rcmdcheck
|
||||
needs: check
|
||||
|
||||
- uses: r-lib/actions/check-r-package@v2
|
||||
with:
|
||||
upload-snapshots: true
|
||||
build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf")'
|
||||
42
.github/workflows/check-demo.yaml
vendored
Normal file
42
.github/workflows/check-demo.yaml
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples
|
||||
# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help
|
||||
on: [push, pull_request]
|
||||
name: Check demo
|
||||
|
||||
permissions: read-all
|
||||
|
||||
jobs:
|
||||
run-sampler:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
|
||||
R_KEEP_PKG_SOURCE: yes
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: r-lib/actions/setup-r@v2
|
||||
with:
|
||||
use-public-rspm: true
|
||||
|
||||
- uses: r-lib/actions/setup-r-dependencies@v2
|
||||
with:
|
||||
extra-packages: any::rcmdcheck, local::.
|
||||
needs: check
|
||||
|
||||
- uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12
|
||||
|
||||
# the rest of this must run from tests/testthat/sampler
|
||||
- run: pnpm install --frozen-lockfile
|
||||
working-directory: tests/testthat/sampler
|
||||
|
||||
- run: pnpm build
|
||||
working-directory: tests/testthat/sampler
|
||||
|
||||
- name: Start Rserve
|
||||
run: pnpm rserve &
|
||||
working-directory: tests/testthat/sampler
|
||||
|
||||
- run: pnpm start
|
||||
working-directory: tests/testthat/sampler
|
||||
35
.github/workflows/create-release.yaml
vendored
Normal file
35
.github/workflows/create-release.yaml
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
name: Releases
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "*"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: r-lib/actions/setup-r@v2
|
||||
with:
|
||||
r-version: latest
|
||||
- name: create_body
|
||||
run: |
|
||||
news <- readLines('NEWS.md')
|
||||
i <- grep("^# ts", news)[1:2]
|
||||
if (is.na(i[2])) i[2L] <- length(news) + 1L
|
||||
news <- news[(i[1L] + 1L):(i[2L] - 1L)]
|
||||
news <- news[trimws(news) != ""]
|
||||
writeLines(news, "body.md")
|
||||
shell: Rscript {0}
|
||||
- uses: ncipollo/release-action@v1
|
||||
with:
|
||||
bodyFile: "body.md"
|
||||
49
.github/workflows/pkgdown.yaml
vendored
Normal file
49
.github/workflows/pkgdown.yaml
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples
|
||||
# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help
|
||||
on:
|
||||
push:
|
||||
branches: [main, master]
|
||||
pull_request:
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch:
|
||||
|
||||
name: pkgdown.yaml
|
||||
|
||||
permissions: read-all
|
||||
|
||||
jobs:
|
||||
pkgdown:
|
||||
runs-on: ubuntu-latest
|
||||
# Only restrict concurrency for non-PR jobs
|
||||
concurrency:
|
||||
group: pkgdown-${{ github.event_name != 'pull_request' || github.run_id }}
|
||||
env:
|
||||
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: r-lib/actions/setup-pandoc@v2
|
||||
|
||||
- uses: r-lib/actions/setup-r@v2
|
||||
with:
|
||||
use-public-rspm: true
|
||||
|
||||
- uses: r-lib/actions/setup-r-dependencies@v2
|
||||
with:
|
||||
extra-packages: any::pkgdown, local::.
|
||||
needs: website
|
||||
|
||||
- name: Build site
|
||||
run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE)
|
||||
shell: Rscript {0}
|
||||
|
||||
- name: Deploy to GitHub pages 🚀
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: JamesIves/github-pages-deploy-action@v4.5.0
|
||||
with:
|
||||
clean: false
|
||||
branch: gh-pages
|
||||
folder: docs
|
||||
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
inst/doc
|
||||
docs
|
||||
80
404.html
80
404.html
@ -1,80 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<!-- Generated by pkgdown: do not edit by hand --><html lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<title>Page not found (404) • ts</title>
|
||||
<script src="http://tomelliott.co.nz/ts/deps/jquery-3.6.0/jquery-3.6.0.min.js"></script><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<link href="http://tomelliott.co.nz/ts/deps/bootstrap-5.3.1/bootstrap.min.css" rel="stylesheet">
|
||||
<script src="http://tomelliott.co.nz/ts/deps/bootstrap-5.3.1/bootstrap.bundle.min.js"></script><link href="http://tomelliott.co.nz/ts/deps/font-awesome-6.5.2/css/all.min.css" rel="stylesheet">
|
||||
<link href="http://tomelliott.co.nz/ts/deps/font-awesome-6.5.2/css/v4-shims.min.css" rel="stylesheet">
|
||||
<script src="http://tomelliott.co.nz/ts/deps/headroom-0.11.0/headroom.min.js"></script><script src="http://tomelliott.co.nz/ts/deps/headroom-0.11.0/jQuery.headroom.min.js"></script><script src="http://tomelliott.co.nz/ts/deps/bootstrap-toc-1.0.1/bootstrap-toc.min.js"></script><script src="http://tomelliott.co.nz/ts/deps/clipboard.js-2.0.11/clipboard.min.js"></script><script src="http://tomelliott.co.nz/ts/deps/search-1.0.0/autocomplete.jquery.min.js"></script><script src="http://tomelliott.co.nz/ts/deps/search-1.0.0/fuse.min.js"></script><script src="http://tomelliott.co.nz/ts/deps/search-1.0.0/mark.min.js"></script><!-- pkgdown --><script src="http://tomelliott.co.nz/ts/pkgdown.js"></script><meta property="og:title" content="Page not found (404)">
|
||||
</head>
|
||||
<body>
|
||||
<a href="http://tomelliott.co.nz/ts/#main" class="visually-hidden-focusable">Skip to contents</a>
|
||||
|
||||
|
||||
<nav class="navbar navbar-expand-lg fixed-top bg-light" data-bs-theme="light" aria-label="Site navigation"><div class="container">
|
||||
|
||||
<a class="navbar-brand me-2" href="http://tomelliott.co.nz/ts/index.html">ts</a>
|
||||
|
||||
<small class="nav-text text-muted me-auto" data-bs-toggle="tooltip" data-bs-placement="bottom" title="">0.1.0</small>
|
||||
|
||||
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
<div id="navbar" class="collapse navbar-collapse ms-3">
|
||||
<ul class="navbar-nav me-auto">
|
||||
<li class="nav-item"><a class="nav-link" href="http://tomelliott.co.nz/ts/reference/index.html">Reference</a></li>
|
||||
<li class="nav-item dropdown">
|
||||
<button class="nav-link dropdown-toggle" type="button" id="dropdown-articles" data-bs-toggle="dropdown" aria-expanded="false" aria-haspopup="true">Articles</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="dropdown-articles">
|
||||
<li><a class="dropdown-item" href="http://tomelliott.co.nz/ts/articles/simple-react-app.html">Build a simple ReactJS app</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="nav-item"><a class="nav-link" href="http://tomelliott.co.nz/ts/news/index.html">Changelog</a></li>
|
||||
</ul>
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item"><form class="form-inline" role="search">
|
||||
<input class="form-control" type="search" name="search-input" id="search-input" autocomplete="off" aria-label="Search site" placeholder="Search for" data-search-index="search.json">
|
||||
</form></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</nav><div class="container template-title-body">
|
||||
<div class="row">
|
||||
<main id="main" class="col-md-9"><div class="page-header">
|
||||
|
||||
<h1>Page not found (404)</h1>
|
||||
|
||||
</div>
|
||||
|
||||
Content not found. Please use links in the navbar.
|
||||
|
||||
</main>
|
||||
</div>
|
||||
|
||||
|
||||
<footer><div class="pkgdown-footer-left">
|
||||
<p>Developed by Tom Elliott.</p>
|
||||
</div>
|
||||
|
||||
<div class="pkgdown-footer-right">
|
||||
<p>Site built with <a href="https://pkgdown.r-lib.org/" class="external-link">pkgdown</a> 2.1.1.</p>
|
||||
</div>
|
||||
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
28
DESCRIPTION
Normal file
28
DESCRIPTION
Normal file
@ -0,0 +1,28 @@
|
||||
Package: ts
|
||||
Title: Helper Functions for Writing Type-safe Functions for rserve-ts
|
||||
Version: 0.1.0
|
||||
Authors@R: c(
|
||||
person("Tom", "Elliott", , "tom@inzight.co.nz",
|
||||
role = c("aut", "cre"),
|
||||
comment = c(
|
||||
ORCID = "0000-0002-7815-6318",
|
||||
Funding = "Supported by the Ngā Puanga Pūtaiao Fellowships from Government funding, administered by the Royal Society Te Apārangi, New Zealand."
|
||||
))
|
||||
)
|
||||
Description: A set of helper functions for specifying typed functions for writing and compiling TypesSript functions for use with rserve-ts. The package allows users to easily specify Javascript input types and Rserve output types for Ocap (object capability) functioned. The companion library 'rserve-ts', available on npm <https://www.npmjs.com/package/rserve-ts>, provides the necessary infrastructure to run these functions in a Typescript environment.
|
||||
License: MIT + file LICENSE
|
||||
Encoding: UTF-8
|
||||
Roxygen: list(markdown = TRUE)
|
||||
RoxygenNote: 7.3.1
|
||||
Config/testthat/edition: 3
|
||||
Depends:
|
||||
R (>= 4.1.0)
|
||||
Imports:
|
||||
rlang,
|
||||
Rserve
|
||||
Suggests:
|
||||
knitr,
|
||||
rmarkdown,
|
||||
testthat (>= 3.0.0)
|
||||
VignetteBuilder: knitr
|
||||
URL: http://tomelliott.co.nz/ts/
|
||||
@ -1,60 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<!-- Generated by pkgdown: do not edit by hand --><html lang="en"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"><title>License • ts</title><script src="deps/jquery-3.6.0/jquery-3.6.0.min.js"></script><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"><link href="deps/bootstrap-5.3.1/bootstrap.min.css" rel="stylesheet"><script src="deps/bootstrap-5.3.1/bootstrap.bundle.min.js"></script><link href="deps/font-awesome-6.5.2/css/all.min.css" rel="stylesheet"><link href="deps/font-awesome-6.5.2/css/v4-shims.min.css" rel="stylesheet"><script src="deps/headroom-0.11.0/headroom.min.js"></script><script src="deps/headroom-0.11.0/jQuery.headroom.min.js"></script><script src="deps/bootstrap-toc-1.0.1/bootstrap-toc.min.js"></script><script src="deps/clipboard.js-2.0.11/clipboard.min.js"></script><script src="deps/search-1.0.0/autocomplete.jquery.min.js"></script><script src="deps/search-1.0.0/fuse.min.js"></script><script src="deps/search-1.0.0/mark.min.js"></script><!-- pkgdown --><script src="pkgdown.js"></script><meta property="og:title" content="License"></head><body>
|
||||
<a href="#main" class="visually-hidden-focusable">Skip to contents</a>
|
||||
|
||||
|
||||
<nav class="navbar navbar-expand-lg fixed-top bg-light" data-bs-theme="light" aria-label="Site navigation"><div class="container">
|
||||
|
||||
<a class="navbar-brand me-2" href="index.html">ts</a>
|
||||
|
||||
<small class="nav-text text-muted me-auto" data-bs-toggle="tooltip" data-bs-placement="bottom" title="">0.1.0</small>
|
||||
|
||||
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
<div id="navbar" class="collapse navbar-collapse ms-3">
|
||||
<ul class="navbar-nav me-auto"><li class="nav-item"><a class="nav-link" href="reference/index.html">Reference</a></li>
|
||||
<li class="nav-item dropdown">
|
||||
<button class="nav-link dropdown-toggle" type="button" id="dropdown-articles" data-bs-toggle="dropdown" aria-expanded="false" aria-haspopup="true">Articles</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="dropdown-articles"><li><a class="dropdown-item" href="articles/simple-react-app.html">Build a simple ReactJS app</a></li>
|
||||
</ul></li>
|
||||
<li class="nav-item"><a class="nav-link" href="news/index.html">Changelog</a></li>
|
||||
</ul><ul class="navbar-nav"><li class="nav-item"><form class="form-inline" role="search">
|
||||
<input class="form-control" type="search" name="search-input" id="search-input" autocomplete="off" aria-label="Search site" placeholder="Search for" data-search-index="search.json"></form></li>
|
||||
</ul></div>
|
||||
|
||||
|
||||
</div>
|
||||
</nav><div class="container template-title-body">
|
||||
<div class="row">
|
||||
<main id="main" class="col-md-9"><div class="page-header">
|
||||
|
||||
<h1>License</h1>
|
||||
|
||||
</div>
|
||||
|
||||
<pre>YEAR: 2024
|
||||
COPYRIGHT HOLDER: Tom Elliott
|
||||
</pre>
|
||||
|
||||
</main></div>
|
||||
|
||||
|
||||
<footer><div class="pkgdown-footer-left">
|
||||
<p>Developed by Tom Elliott.</p>
|
||||
</div>
|
||||
|
||||
<div class="pkgdown-footer-right">
|
||||
<p>Site built with <a href="https://pkgdown.r-lib.org/" class="external-link">pkgdown</a> 2.1.1.</p>
|
||||
</div>
|
||||
|
||||
</footer></div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</body></html>
|
||||
|
||||
64
LICENSE.html
64
LICENSE.html
@ -1,64 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<!-- Generated by pkgdown: do not edit by hand --><html lang="en"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"><title>MIT License • ts</title><script src="deps/jquery-3.6.0/jquery-3.6.0.min.js"></script><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"><link href="deps/bootstrap-5.3.1/bootstrap.min.css" rel="stylesheet"><script src="deps/bootstrap-5.3.1/bootstrap.bundle.min.js"></script><link href="deps/font-awesome-6.5.2/css/all.min.css" rel="stylesheet"><link href="deps/font-awesome-6.5.2/css/v4-shims.min.css" rel="stylesheet"><script src="deps/headroom-0.11.0/headroom.min.js"></script><script src="deps/headroom-0.11.0/jQuery.headroom.min.js"></script><script src="deps/bootstrap-toc-1.0.1/bootstrap-toc.min.js"></script><script src="deps/clipboard.js-2.0.11/clipboard.min.js"></script><script src="deps/search-1.0.0/autocomplete.jquery.min.js"></script><script src="deps/search-1.0.0/fuse.min.js"></script><script src="deps/search-1.0.0/mark.min.js"></script><!-- pkgdown --><script src="pkgdown.js"></script><meta property="og:title" content="MIT License"></head><body>
|
||||
<a href="#main" class="visually-hidden-focusable">Skip to contents</a>
|
||||
|
||||
|
||||
<nav class="navbar navbar-expand-lg fixed-top bg-light" data-bs-theme="light" aria-label="Site navigation"><div class="container">
|
||||
|
||||
<a class="navbar-brand me-2" href="index.html">ts</a>
|
||||
|
||||
<small class="nav-text text-muted me-auto" data-bs-toggle="tooltip" data-bs-placement="bottom" title="">0.1.0</small>
|
||||
|
||||
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
<div id="navbar" class="collapse navbar-collapse ms-3">
|
||||
<ul class="navbar-nav me-auto"><li class="nav-item"><a class="nav-link" href="reference/index.html">Reference</a></li>
|
||||
<li class="nav-item dropdown">
|
||||
<button class="nav-link dropdown-toggle" type="button" id="dropdown-articles" data-bs-toggle="dropdown" aria-expanded="false" aria-haspopup="true">Articles</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="dropdown-articles"><li><a class="dropdown-item" href="articles/simple-react-app.html">Build a simple ReactJS app</a></li>
|
||||
</ul></li>
|
||||
<li class="nav-item"><a class="nav-link" href="news/index.html">Changelog</a></li>
|
||||
</ul><ul class="navbar-nav"><li class="nav-item"><form class="form-inline" role="search">
|
||||
<input class="form-control" type="search" name="search-input" id="search-input" autocomplete="off" aria-label="Search site" placeholder="Search for" data-search-index="search.json"></form></li>
|
||||
</ul></div>
|
||||
|
||||
|
||||
</div>
|
||||
</nav><div class="container template-title-body">
|
||||
<div class="row">
|
||||
<main id="main" class="col-md-9"><div class="page-header">
|
||||
|
||||
<h1>MIT License</h1>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="mit-license" class="section level1">
|
||||
|
||||
<p>Copyright (c) 2024 Tom Elliott</p>
|
||||
<p>Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:</p>
|
||||
<p>The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.</p>
|
||||
<p>THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.</p>
|
||||
</div>
|
||||
|
||||
</main></div>
|
||||
|
||||
|
||||
<footer><div class="pkgdown-footer-left">
|
||||
<p>Developed by Tom Elliott.</p>
|
||||
</div>
|
||||
|
||||
<div class="pkgdown-footer-right">
|
||||
<p>Site built with <a href="https://pkgdown.r-lib.org/" class="external-link">pkgdown</a> 2.1.1.</p>
|
||||
</div>
|
||||
|
||||
</footer></div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</body></html>
|
||||
|
||||
21
LICENSE.md
Normal file
21
LICENSE.md
Normal file
@ -0,0 +1,21 @@
|
||||
# MIT License
|
||||
|
||||
Copyright (c) 2024 Tom Elliott
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
22
Makefile
Normal file
22
Makefile
Normal file
@ -0,0 +1,22 @@
|
||||
default: README.md
|
||||
|
||||
rfiles: $(wildcard R/*.R)
|
||||
|
||||
document:
|
||||
Rscript -e "devtools::document()"
|
||||
|
||||
install: document
|
||||
R CMD INSTALL .
|
||||
|
||||
check:
|
||||
Rscript -e "devtools::check()"
|
||||
|
||||
README.md: README.Rmd install
|
||||
Rscript -e "rmarkdown::render('README.Rmd')"
|
||||
@rm README.html
|
||||
|
||||
test:
|
||||
Rscript -e "devtools::test()"
|
||||
|
||||
site: install
|
||||
Rscript -e "pkgdown::build_site()"
|
||||
33
NAMESPACE
Normal file
33
NAMESPACE
Normal file
@ -0,0 +1,33 @@
|
||||
# Generated by roxygen2: do not edit by hand
|
||||
|
||||
S3method(check_type,default)
|
||||
S3method(check_type,ts_function)
|
||||
S3method(check_type,ts_object)
|
||||
S3method(get_type,default)
|
||||
S3method(get_type,ts_function)
|
||||
S3method(get_type,ts_object)
|
||||
S3method(print,ts_function)
|
||||
S3method(print,ts_object)
|
||||
S3method(ts_app,default)
|
||||
S3method(ts_app,list)
|
||||
S3method(ts_app,ts_function)
|
||||
S3method(ts_compile,character)
|
||||
S3method(ts_compile,default)
|
||||
S3method(ts_compile,ts_function)
|
||||
export(check_type)
|
||||
export(get_type)
|
||||
export(is_ts_object)
|
||||
export(ts_app)
|
||||
export(ts_character)
|
||||
export(ts_compile)
|
||||
export(ts_dataframe)
|
||||
export(ts_deploy)
|
||||
export(ts_factor)
|
||||
export(ts_function)
|
||||
export(ts_integer)
|
||||
export(ts_list)
|
||||
export(ts_logical)
|
||||
export(ts_null)
|
||||
export(ts_numeric)
|
||||
export(ts_union)
|
||||
export(ts_void)
|
||||
14
NEWS.md
Normal file
14
NEWS.md
Normal file
@ -0,0 +1,14 @@
|
||||
# ts 0.1.0 - Initial release
|
||||
|
||||
The initial developmental release of `ts`.
|
||||
|
||||
API expected to change in future releases as it is tested in various example
|
||||
applications, so if you happen to use this in a project I suggest you
|
||||
pin the version:
|
||||
|
||||
```r
|
||||
# install.packages("remotes")
|
||||
remotes::install_github("ws1/ts@0.1.0")
|
||||
```
|
||||
|
||||
To get started, check the docs site at https://tomelliott.co.nz/ts/.
|
||||
68
R/compile.R
Normal file
68
R/compile.R
Normal file
@ -0,0 +1,68 @@
|
||||
#' Compile R functions
|
||||
#'
|
||||
#' Generates TypeScript schema for the given R function or file path. If a path, the R app is also generated.
|
||||
#'
|
||||
#' @param f A function or file path
|
||||
#' @param name The name of the function
|
||||
#' @param ... Additional arguments (passed to ts_deploy)
|
||||
#' @param filename The base file path to write the TypeScript schema and R app to (optional, uses `[path of f].rserve` by default). `.R` and `.ts` file extensions are appended automatically. If `""`, the output is printed to the standard output console (see `cat`).
|
||||
#' @return Character vector of TypeScript schema, or NULL if writing to file
|
||||
#' @md
|
||||
#' @export
|
||||
ts_compile <- function(f, ..., name, filename) {
|
||||
o <- UseMethod("ts_compile")
|
||||
}
|
||||
|
||||
#' @export
|
||||
ts_compile.ts_function <- function(f, ..., name = deparse(substitute(f))) {
|
||||
inputs <- f$args
|
||||
result <- f$result
|
||||
|
||||
inputs <- sapply(inputs, \(x) x$input_type)
|
||||
fn_args <- paste(paste(inputs), collapse = ", ")
|
||||
|
||||
sprintf(
|
||||
"const %s = Robj.ocap([%s], %s);", name, fn_args,
|
||||
result$return_type
|
||||
)
|
||||
}
|
||||
|
||||
#' @export
|
||||
ts_compile.character <- function(
|
||||
f,
|
||||
...,
|
||||
filename = sprintf("%s.rserve", tools::file_path_sans_ext(f))) {
|
||||
if (length(f) > 1) {
|
||||
return(sapply(f, ts_compile))
|
||||
}
|
||||
|
||||
if (!file.exists(f)) {
|
||||
warning(sprintf("File not found: %s", f))
|
||||
return()
|
||||
}
|
||||
e <- new.env()
|
||||
source(f, local = e)
|
||||
|
||||
x <- sapply(ls(e), \(x) ts_compile(e[[x]], filename = "", name = x))
|
||||
|
||||
src <- c(
|
||||
"import { Robj } from 'rserve-ts';",
|
||||
"import { z } from 'zod';",
|
||||
"\n",
|
||||
x,
|
||||
"\n",
|
||||
sprintf("export default {\n %s\n};", paste(ls(e), collapse = ",\n "))
|
||||
)
|
||||
|
||||
cat(src, file = sprintf("%s.ts", filename), sep = "\n")
|
||||
|
||||
# R file
|
||||
ts_deploy(f, file = sprintf("%s.R", filename), silent = TRUE, ...)
|
||||
|
||||
invisible()
|
||||
}
|
||||
|
||||
#' @export
|
||||
ts_compile.default <- function(f, ...) {
|
||||
warning("Not supported")
|
||||
}
|
||||
77
R/deploy.R
Normal file
77
R/deploy.R
Normal file
@ -0,0 +1,77 @@
|
||||
#' Deploy a ts Rserve app
|
||||
#'
|
||||
#' @param f The path to the application files
|
||||
#' @param file The file to write the deployment script to
|
||||
#' @param init Names of objects (ts_functions) to make available to
|
||||
#' the initialisation function
|
||||
#' @param port The port to deploy the app on
|
||||
#' @param run Whether to run the deployment script,
|
||||
#' takes values "no", "here", "background"
|
||||
#' @param silent Whether to print the deployment script
|
||||
#' @return NULL, called to open an Rserve instance
|
||||
#' @export
|
||||
#' @md
|
||||
ts_deploy <- function(f,
|
||||
file = sprintf("%s.rserve.R", tools::file_path_sans_ext(f)),
|
||||
init = NULL,
|
||||
port = 6311,
|
||||
run = c("no", "here", "background"),
|
||||
silent = FALSE) {
|
||||
if (length(f) != 1) stop("Expected a single path")
|
||||
if (!file.exists(f)) stop("File not found")
|
||||
|
||||
x <- readLines(f)
|
||||
|
||||
if (is.null(init)) init <- ls_ocaps(f)
|
||||
init <- sprintf(
|
||||
"list(\n %s\n)",
|
||||
paste(sapply(init, \(z) sprintf("%s = %s", z, z)), collapse = ",\n ")
|
||||
)
|
||||
|
||||
x <- x[!grepl("library\\(ts\\)", x)] # Remove library(ts) from the app
|
||||
|
||||
src <- c(
|
||||
"library(Rserve)",
|
||||
"library(ts)",
|
||||
"",
|
||||
x,
|
||||
sprintf("first.fns <- function() ts_app(%s)", init),
|
||||
"",
|
||||
sprintf("oc.init <- function() Rserve:::ocap(first.fns)"),
|
||||
"",
|
||||
sprintf(
|
||||
paste(
|
||||
"Rserve::run.Rserve(",
|
||||
" websockets.port = %s,",
|
||||
" websockets = TRUE,",
|
||||
" oob = TRUE,",
|
||||
" qap = FALSE,",
|
||||
" websockets.qap.oc = TRUE",
|
||||
")",
|
||||
sep = "\n"
|
||||
),
|
||||
port
|
||||
)
|
||||
)
|
||||
|
||||
writeLines(src, file)
|
||||
|
||||
run <- match.arg(run)
|
||||
switch(run,
|
||||
"no" = {
|
||||
if (!silent) {
|
||||
cat("Run the following command to deploy the app:\n")
|
||||
cat(sprintf("Rscript %s", file), "\n")
|
||||
}
|
||||
},
|
||||
"here" = source(file),
|
||||
"background" = system(sprintf("Rscript %s", file))
|
||||
)
|
||||
}
|
||||
|
||||
ls_ocaps <- function(f) {
|
||||
e <- new.env()
|
||||
source(f, local = e)
|
||||
x <- ls(e)
|
||||
x[sapply(x, \(z) class(e[[z]]) == "ts_function")]
|
||||
}
|
||||
57
R/docs_types.R
Normal file
57
R/docs_types.R
Normal file
@ -0,0 +1,57 @@
|
||||
#' @title
|
||||
#' Types in R and TypeScript
|
||||
#'
|
||||
#' @description
|
||||
#' This document provides an overview of the main types available to
|
||||
#' app developers, which includes the main types in R and their
|
||||
#' TypeScript counterparts.
|
||||
#'
|
||||
#' @details
|
||||
#' # TS objects
|
||||
#' The basic object in `ts` is a `ts_object` class. This is represented by
|
||||
#' input and return types, an optional default value, and a checking
|
||||
#' function.
|
||||
#'
|
||||
#' Input types describe the `zod` schema of objects that TypeScript can pass
|
||||
#' to Rserve functions.
|
||||
#'
|
||||
#' Return types describe the `zod` schema of objects that Rserve functions,
|
||||
#' and most of these utilise the `Robj` utility types in the `rserve-ts`
|
||||
#' library. The return types have additional properties added,
|
||||
#' namedly `r_type` and `r_attributes`, handled by the `Robj` utility types.
|
||||
#'
|
||||
#' **Scalar versus array ("vector") types**:
|
||||
#' In R, almost all types are vectors. In the 'rserve-js' library,
|
||||
#' primitive arrays of length one are converted into scalars, which
|
||||
#' leads to some issues with type checking when, for example, a return value
|
||||
#' has unknown length. `which(x > 5)` is one such example.
|
||||
#'
|
||||
#' To solve this, we add an `n` argument to the `ts_*` functions. When `n = 1`,
|
||||
#' the type takes the *scalar* form of the alue. When `n != 1`, the type takes
|
||||
#' the *array* form of the value (this includes 0). Otherwise, the type
|
||||
#' is the union of the scalar and array forms.
|
||||
#'
|
||||
#' This is the case for numbers, strings, and booleans.
|
||||
#'
|
||||
#' # Available types
|
||||
#'
|
||||
#' - `ts_boolean`: A boolean value. The array type is `Int8Array`.
|
||||
#' - `ts_integer`: An integer value. The array type is `Int32Array`.
|
||||
#' Javascript does not have a native integer type,
|
||||
#' so scalars are represented as a number
|
||||
#' (the same as `ts_numeric`).
|
||||
#' - `ts_numeric`: A numeric value. The array type is `Float64Array`.*
|
||||
#' - `ts_string`: A string value. The array type is `string[]`.
|
||||
#' - `ts_factor`: A factor value. The array type is `(level1 | level2 | ... | levelN)[]`, and this type does not have a scalar form.
|
||||
#' - `ts_list`: A list value, represented by a named object or an array.
|
||||
#' - `ts_dataframe`: A data frame value, represented by a named object.
|
||||
#' - `ts_null`: A null value.
|
||||
#' - `ts_void`: A void value, used for specifying return types of functions
|
||||
#' that do not return a value.
|
||||
#'
|
||||
#'
|
||||
#'
|
||||
#' @family type documentation
|
||||
#'
|
||||
#' @name type_objects
|
||||
NULL
|
||||
156
R/function.R
Normal file
156
R/function.R
Normal file
@ -0,0 +1,156 @@
|
||||
parse_args <- function(x, mc) {
|
||||
fmls <- lapply(x, eval)
|
||||
if (!all(names(mc) %in% names(fmls))) {
|
||||
stop(
|
||||
"Invalid argument(s): ",
|
||||
paste(setdiff(names(mc), names(fmls)), collapse = ", ")
|
||||
)
|
||||
}
|
||||
args <- lapply(names(fmls), function(n) {
|
||||
tryCatch(
|
||||
{
|
||||
fmls[[n]]$check(eval(mc[[n]]))
|
||||
},
|
||||
error = function(e) {
|
||||
stop("Invalid argument '", n, "': ", e$message, call. = FALSE)
|
||||
}
|
||||
)
|
||||
})
|
||||
names(args) <- names(fmls)
|
||||
args
|
||||
}
|
||||
|
||||
check_args <- function(args, fmls) {
|
||||
args <- as.list(args)[-1]
|
||||
fmls <- lapply(fmls, eval)
|
||||
lapply(names(fmls), function(n) {
|
||||
tryCatch(
|
||||
{
|
||||
fmls[[n]]$check(args[[n]])
|
||||
},
|
||||
error = function(e) {
|
||||
stop("Invalid argument '", n, "': ", e$message, call. = FALSE)
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
ts_result <- function(type, value) {
|
||||
if (type$check(value)) {
|
||||
return(value)
|
||||
}
|
||||
stop("Expected a value of type ", type$type)
|
||||
}
|
||||
|
||||
#' TS function definition
|
||||
#'
|
||||
#' @details
|
||||
#' Defining functions is the core of writing Rserve apps.
|
||||
#' Functions are referred to as *object capabilities* (ocaps),
|
||||
#' as they are 'objects' that allow Javascript to access capabilities
|
||||
#' of R with a restricted interface. Only arguments can be adjusted.
|
||||
#'
|
||||
#' TS functions can be defined using existing (named) or anonymous functions.
|
||||
#' Anonymous functions are useful in that the arguments to the functions
|
||||
#' can explicitly be defined with their types as formal arguments:
|
||||
#'
|
||||
#' ```
|
||||
#' ts_function(function(x = ts_integer(), y = ts_string()) { ... })
|
||||
#' ```
|
||||
#'
|
||||
#' @param f an R function
|
||||
#' @param ... argument definitions (only required if f does not specify these in its formals)
|
||||
#' @param result return type (ignored if overloads are provided)
|
||||
#' @export
|
||||
#' @md
|
||||
#'
|
||||
#' @return a ts function object which has a `call` method that will call the function with the given arguments, which will be checked for type correctness.
|
||||
#'
|
||||
#' @examples
|
||||
#' f <- ts_function(function(x = ts_integer(1), y = ts_character(1)) {
|
||||
#' x + nchar(y)
|
||||
#' }, result = ts_integer(1))
|
||||
#' f$call(1, "hello")
|
||||
ts_function <- function(f, ..., result = ts_void()) {
|
||||
args <- list(...)
|
||||
if (!is.null(result) && !is_ts_object(result)) {
|
||||
stop("Invalid return type")
|
||||
}
|
||||
|
||||
if (length(args) == 0) {
|
||||
args <- lapply(formals(f), eval)
|
||||
}
|
||||
|
||||
e <- new.env()
|
||||
e$f <- f
|
||||
# e$env <- env
|
||||
e$args <- args
|
||||
e$result <- result
|
||||
|
||||
e$call <- function(...) {
|
||||
mc <- match.call(e$f)
|
||||
.args <- parse_args(args, mc[-1])
|
||||
.res <- do.call(e$f, .args)
|
||||
check_type(result, .res)
|
||||
}
|
||||
|
||||
e$copy <- function(env = parent.frame()) {
|
||||
e2 <- e
|
||||
environment(e2$f) <- rlang::env_clone(environment(e$f), env)
|
||||
e2
|
||||
}
|
||||
|
||||
class(e) <- "ts_function"
|
||||
e
|
||||
}
|
||||
|
||||
|
||||
#' @export
|
||||
print.ts_function <- function(x, ...) {
|
||||
h3("Ocap function")
|
||||
|
||||
cat("Arguments:\n")
|
||||
args <- lapply(x$args, \(z) z$input_type)
|
||||
lapply(names(args), \(n) {
|
||||
cat("- ", n, ": ", args[[n]], "\n", sep = "")
|
||||
})
|
||||
cat("\n\n")
|
||||
|
||||
cat("Return type:\n")
|
||||
cat(x$result$return_type)
|
||||
}
|
||||
|
||||
#' Generate an Rserve app from a ts function
|
||||
#'
|
||||
#' Anything that is not a function simply returns itself.
|
||||
#' However, functions are wrapped with `Rserve::ocap()`,
|
||||
#' and the result is subsequently wrapped with `ts_app()`.
|
||||
#'
|
||||
#' @param x A ts function object (`ts_function()`)
|
||||
#' @export
|
||||
#' @md
|
||||
#'
|
||||
#' @returns An object of class 'OCref', see [Rserve::ocap()]
|
||||
#'
|
||||
#' @examples
|
||||
#' f <- ts_function(function(x = ts_integer(1), y = ts_character(1)) {
|
||||
#' x + nchar(y)
|
||||
#' }, result = ts_integer(1))
|
||||
#' app <- ts_app(f) # class of 'OCref'
|
||||
#' # this can now be used in an Rserve application, for example
|
||||
ts_app <- function(x) UseMethod("ts_app")
|
||||
|
||||
#' @export
|
||||
ts_app.default <- function(x) {
|
||||
x
|
||||
}
|
||||
|
||||
#' @export
|
||||
ts_app.list <- function(x) {
|
||||
lapply(x, ts_app)
|
||||
}
|
||||
|
||||
#' @export
|
||||
ts_app.ts_function <- function(x) {
|
||||
Rserve::ocap(function(...) ts_app(x$call(...)))
|
||||
}
|
||||
9
R/helpers.R
Normal file
9
R/helpers.R
Normal file
@ -0,0 +1,9 @@
|
||||
h3 <- function(x) {
|
||||
cat("\n---", x, "\n")
|
||||
}
|
||||
|
||||
|
||||
# TODO: figure out how to format (modern) JS code from R?
|
||||
format_js <- function(x) {
|
||||
x
|
||||
}
|
||||
458
R/types.R
Normal file
458
R/types.R
Normal file
@ -0,0 +1,458 @@
|
||||
#' Typed object (internal use only)
|
||||
#'
|
||||
#' This is the base type for all typed objects, and can be used to define
|
||||
#' custom types.
|
||||
#'
|
||||
#' @param input_type The type of the object that Typescript expect to send to R.
|
||||
#' @param return_type The type of the object that Typescript expects to recieve from R.
|
||||
#' @param default The default value of the object.
|
||||
#' @param check A function that checks the object and returns it if it is valid. This operates on the R side and is mostly for development and debugging purposes. It is up to the developer to ensure that all functions return the correct type of object always.
|
||||
#' @param generic logical, if `TRUE` then the object is a generic type.
|
||||
#'
|
||||
#' @md
|
||||
ts_object <- function(input_type = "any",
|
||||
return_type = "any",
|
||||
default = NULL,
|
||||
check = function() stop("Not implemented"),
|
||||
generic = FALSE) {
|
||||
e <- environment()
|
||||
|
||||
e$attr <- function(name, value) {
|
||||
e$attributes[[name]] <- value
|
||||
}
|
||||
|
||||
class(e) <- c("ts_object", class(e))
|
||||
e
|
||||
}
|
||||
|
||||
#' @export
|
||||
print.ts_object <- function(x, ...) {
|
||||
# name <- deparse(substitute(x))
|
||||
h3("Input type: ")
|
||||
if (nchar(x$input_type) > 50) {
|
||||
cat(format_js(x$input_type), "\n")
|
||||
} else {
|
||||
cat(x$input_type, "\n")
|
||||
}
|
||||
h3("Return type: ")
|
||||
if (nchar(x$return_type) > 50) {
|
||||
print(x$return_type)
|
||||
cat(format_js(x$return_type), "\n")
|
||||
} else {
|
||||
cat(x$return_type, "\n")
|
||||
}
|
||||
cat("\n")
|
||||
}
|
||||
|
||||
#' @describeIn ts_object Check if an object is a ts object
|
||||
#' @export
|
||||
is_ts_object <- function(x) {
|
||||
inherits(x, "ts_object")
|
||||
}
|
||||
|
||||
#' @describeIn ts_object Get the input type of a ts object
|
||||
#' @param x A ts object
|
||||
#' @param which Which type to get, either "input" or "return"
|
||||
#' @export
|
||||
get_type <- function(x, which = c("input", "return")) {
|
||||
UseMethod("get_type")
|
||||
}
|
||||
|
||||
#' @export
|
||||
get_type.ts_object <- function(x, which = c("input", "return")) {
|
||||
which <- match.arg(which)
|
||||
if (which == "input") {
|
||||
return(x$input_type)
|
||||
}
|
||||
x$return_type
|
||||
}
|
||||
|
||||
#' @export
|
||||
get_type.ts_function <- function(x, which = c("input", "return")) {
|
||||
which <- match.arg(which)
|
||||
if (which == "input") {
|
||||
return("z.function()")
|
||||
}
|
||||
"Robj.ocap()"
|
||||
}
|
||||
|
||||
#' @export
|
||||
get_type.default <- function(x, which) {
|
||||
stop("Invalid object")
|
||||
}
|
||||
|
||||
#' @describeIn ts_object Check if an object has the correct type
|
||||
#' @param type A ts object
|
||||
#' @param x An object
|
||||
#' @export
|
||||
check_type <- function(type, x) UseMethod("check_type")
|
||||
|
||||
#' @export
|
||||
check_type.default <- function(type, x) {
|
||||
stop("Invalid object")
|
||||
}
|
||||
|
||||
#' @export
|
||||
check_type.ts_object <- function(type, x) {
|
||||
type$check(x)
|
||||
}
|
||||
|
||||
#' @export
|
||||
check_type.ts_function <- function(type, x) {
|
||||
if ("ts_function" %in% class(x)) {
|
||||
return(x)
|
||||
}
|
||||
if (!is.function(x)) stop("Expected a function")
|
||||
do.call(
|
||||
ts_function,
|
||||
c(
|
||||
list(x), attr(type, "args"),
|
||||
list(result = attr(type, "result"))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
#' Union type
|
||||
#'
|
||||
#' Create a union of types. Currently this only accepts schemas as strings.
|
||||
#' @param ... Zod types to merge (as strings)
|
||||
#' @export
|
||||
#' @md
|
||||
#' @examples
|
||||
#' x <- ts_union("z.number()", "z.string()")
|
||||
ts_union <- function(...) sprintf("z.union([%s])", paste(..., sep = ", "))
|
||||
|
||||
ts_array <- function(type = c("z.number()", "z.boolean()", "z.string()")) {
|
||||
if (type == "z.number()") {
|
||||
return("z.instanceof(Float64Array)")
|
||||
}
|
||||
if (type == "z.boolean()") {
|
||||
return("z.instanceof(Uint8Array)")
|
||||
}
|
||||
return("z.array(z.string())")
|
||||
}
|
||||
|
||||
n_type <- function(n, type, pl = ts_array(type)) {
|
||||
if (n == 1) {
|
||||
return(type)
|
||||
}
|
||||
if (n == -1) {
|
||||
return(ts_union(type, pl))
|
||||
}
|
||||
pl
|
||||
}
|
||||
n_type_fun <- function(n, type) {
|
||||
sprintf("%s(%s)", type, ifelse(n < 0, "", n))
|
||||
}
|
||||
|
||||
#' Logical or boolean type
|
||||
#'
|
||||
#' Booleans are represented in Zod schema as either a boolean (`z.boolean()`),
|
||||
#' or a typed Uint8Array (`z.instanceof(Uint8Array)`).
|
||||
#'
|
||||
#' @param n The length of the boolean vector. If `n = 1` then a single boolean is expected. If `n = 0` then any length is expected. If `n > 1` then a boolean vector of length `n` is expected.
|
||||
#' @return A ts object that accepts logical scalars or vectors of length `n`.
|
||||
#' @export
|
||||
#' @md
|
||||
#' @examples
|
||||
#' x <- ts_logical(1)
|
||||
#' x$check(TRUE)
|
||||
#'
|
||||
#' \dontrun{
|
||||
#' # this will fail
|
||||
#' x$check(5)
|
||||
#' }
|
||||
ts_logical <- function(n = -1L) {
|
||||
ts_object(
|
||||
n_type(n, "z.boolean()"),
|
||||
n_type_fun(n, "Robj.logical"),
|
||||
check = function(x) {
|
||||
if (!is.logical(x)) stop("Expected a boolean")
|
||||
if (n > 0 && length(x) != n) {
|
||||
stop("Expected a boolean of length ", n)
|
||||
}
|
||||
x
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
#' Integer type
|
||||
#'
|
||||
#' Integers are represented in Zod schema as either a number (`z.number()`),
|
||||
#' or a Int32Array (`z.instanceof(Int32Array)`).
|
||||
#'
|
||||
#' @param n The length of the integer vector. If `n = 1` then a single integer is expected. If `n = 0` then any length is expected. If `n > 1` then an integer vector of length `n` is expected.
|
||||
#' @return A ts object that accepts integer scalars or vectors of length `n`.
|
||||
#' @export
|
||||
#' @md
|
||||
#' @examples
|
||||
#' x <- ts_integer(1)
|
||||
#' x$check(1L)
|
||||
#'
|
||||
#' \dontrun{
|
||||
#' # this will fail
|
||||
#' x$check(1:10)
|
||||
#' }
|
||||
ts_integer <- function(n = -1L) {
|
||||
ts_object(
|
||||
n_type(n, "z.number()", "z.instanceof(Int32Array)"),
|
||||
n_type_fun(n, "Robj.integer"),
|
||||
check = function(x) {
|
||||
if (!is.numeric(x)) stop("Expected a number")
|
||||
if (!all.equal(x, as.integer(x))) {
|
||||
# javascript only has one number type
|
||||
stop("Expected an integer")
|
||||
}
|
||||
if (n > 0 && length(x) != n) {
|
||||
stop("Expected an integer of length ", n)
|
||||
}
|
||||
x
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
#' Numeric type
|
||||
#'
|
||||
#' Numbers are represented in Zod schema as either a number (`z.number()`),
|
||||
#' or a Float64Array (`z.instanceof(Float64Array)`).
|
||||
#'
|
||||
#' @param n The length of the numeric vector. If `n = 1` then a single number is expected. If `n = 0` then any length is expected. If `n > 1` then a numeric vector of length `n` is expected.
|
||||
#' @return A ts object that accepts numeric scalars or vectors of length `n`.
|
||||
#' @export
|
||||
#' @md
|
||||
#' @examples
|
||||
#' x <- ts_numeric(1)
|
||||
#' x$check(1)
|
||||
#'
|
||||
#' \dontrun{
|
||||
#' # this will fail
|
||||
#' x$check(c(1, 2, 3))
|
||||
#' x$check("a")
|
||||
#' }
|
||||
ts_numeric <- function(n = -1L) {
|
||||
ts_object(
|
||||
n_type(n, "z.number()"),
|
||||
n_type_fun(n, "Robj.numeric"),
|
||||
check = function(x) {
|
||||
if (!is.numeric(x)) stop("Expected a number", call. = FALSE)
|
||||
if (n > 0 && length(x) != n) {
|
||||
stop("Expected a number of length ", n, , call. = FALSE)
|
||||
}
|
||||
x
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
#' Character or string type
|
||||
#'
|
||||
#' Strings are represented in Zod schema as either a string (`z.string()`),
|
||||
#' or a string array (`z.array(z.string())`).
|
||||
#' @param n The length of the string vector. If `n = 1` then a single string is expected. If `n = 0` then any length is expected. If `n > 1` then a string vector of length `n` is expected.
|
||||
#' @return A ts object that accepts strings or string vectors of length `n`.
|
||||
#' @export
|
||||
#' @md
|
||||
#' @examples
|
||||
#' x <- ts_character(1)
|
||||
#' x$check("a")
|
||||
ts_character <- function(n = -1L) {
|
||||
ts_object(
|
||||
n_type(n, "z.string()"),
|
||||
n_type_fun(n, "Robj.character"),
|
||||
check = function(x) {
|
||||
if (!is.character(x)) stop("Expected a string")
|
||||
if (n > 0 && length(x) != n) stop("Expected a string of length ", n)
|
||||
x
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
vector_as_ts_array <- function(x) {
|
||||
paste("[\"", paste(x, collapse = "\", \""), "\"]", sep = "")
|
||||
}
|
||||
|
||||
#' Typed factor
|
||||
#'
|
||||
#' Factors are integers with labels. On the JS side, these are *always* represented as a string array (even if only one value - yay!).
|
||||
#'
|
||||
#' @param levels A character vector of levels (optional).
|
||||
#' @return A ts object that accepts factors with the specified levels.
|
||||
#'
|
||||
#' @export
|
||||
#' @md
|
||||
#' @examples
|
||||
#' x <- ts_factor(levels = c("a", "b"))
|
||||
#' x$check(factor("a", levels = c("a", "b")))
|
||||
#'
|
||||
#' \dontrun{
|
||||
#' # this will fail
|
||||
#' x$check("a")
|
||||
#' x$check(factor("c", levels = c("a", "b", "c")))
|
||||
#' }
|
||||
ts_factor <- function(levels = NULL) {
|
||||
ts_object(
|
||||
ifelse(is.null(levels),
|
||||
ts_array("z.string()"),
|
||||
sprintf("(%s)[]", paste(levels, collapse = " | "))
|
||||
),
|
||||
if (is.null(levels)) {
|
||||
"Robj.factor()"
|
||||
} else {
|
||||
sprintf("Robj.factor(%s)", vector_as_ts_array(levels))
|
||||
},
|
||||
check = function(x) {
|
||||
if (!is.factor(x)) stop("Expected a factor")
|
||||
if (!is.null(levels) && !identical(levels, levels(x))) {
|
||||
stop(
|
||||
"Expected a factor with levels: ",
|
||||
paste(levels, collapse = ", ")
|
||||
)
|
||||
}
|
||||
x
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
# table?
|
||||
|
||||
#' Typed list
|
||||
#'
|
||||
#' A list is a vector of other robjects, which may or may not be named.
|
||||
#' @param ... A list of types, named or unnamed.
|
||||
#' @return A ts object that accepts lists with the specified types.
|
||||
#'
|
||||
#' @export
|
||||
#' @md
|
||||
#'
|
||||
#' @examples
|
||||
#' x <- ts_list(a = ts_integer(1), b = ts_character(1))
|
||||
#' x$check(list(a = 1L, b = "a"))
|
||||
ts_list <- function(...) {
|
||||
values <- list(...)
|
||||
|
||||
type <- "z.union([z.object({}), z.array(z.any())])"
|
||||
type_fn <- ""
|
||||
if (length(values)) {
|
||||
types <- sapply(values, get_type, which = "input")
|
||||
type_funs <- sapply(values, get_type, which = "return")
|
||||
if (!is.null(names(values))) {
|
||||
type <- sprintf(
|
||||
"{ %s }",
|
||||
paste(names(values), types, sep = ": ", collapse = ", ")
|
||||
)
|
||||
type_fn <- sprintf(
|
||||
"{ %s }",
|
||||
paste(names(values), type_funs, sep = ": ", collapse = ", ")
|
||||
)
|
||||
} else {
|
||||
type <- sprintf("[%s]", paste(types, collapse = ", "))
|
||||
type_fn <- sprintf("[%s]", paste(type_funs, collapse = ", "))
|
||||
}
|
||||
}
|
||||
|
||||
ts_object(
|
||||
type,
|
||||
ifelse(type_fn == "", "Robj.list()",
|
||||
sprintf("Robj.list(%s)", type_fn)
|
||||
),
|
||||
check = function(x) {
|
||||
if (!is.list(x)) stop("Expected a list")
|
||||
if (!is.null(values)) {
|
||||
if (!is.null(names(values))) {
|
||||
if (!identical(names(x), names(values))) {
|
||||
stop(
|
||||
"Expected a list with names: ",
|
||||
paste(names(values), collapse = ", ")
|
||||
)
|
||||
}
|
||||
}
|
||||
for (i in seq_along(values)) {
|
||||
x[[i]] <- check_type(values[[i]], x[[i]])
|
||||
}
|
||||
}
|
||||
x
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
#' Typed dataframe
|
||||
#'
|
||||
#' This is essentially a list, but the elements must have names and are all the same length.
|
||||
#'
|
||||
#' @param ... Named types.
|
||||
#' @return A ts object that accepts data frames with the specified types.
|
||||
#'
|
||||
#' @export
|
||||
#' @md
|
||||
#'
|
||||
#' @examples
|
||||
#' x <- ts_dataframe(a = ts_integer(1), b = ts_character(1))
|
||||
#' x$check(data.frame(a = 1L, b = "a"))
|
||||
ts_dataframe <- function(...) {
|
||||
values <- list(...)
|
||||
type <- "z.record(z.string(), z.any())"
|
||||
type_fn <- ""
|
||||
if (length(values)) {
|
||||
types <- sapply(values, get_type, which = "input")
|
||||
type_funs <- sapply(values, get_type, which = "return")
|
||||
if (is.null(names(values))) stop("Expected named elements")
|
||||
|
||||
type <- sprintf(
|
||||
"z.object({\n %s\n})",
|
||||
paste(names(values), types, sep = ": ", collapse = ",\n ")
|
||||
)
|
||||
type_fn <- sprintf(
|
||||
"{\n %s\n}",
|
||||
paste(names(values), type_funs, sep = ": ", collapse = ",\n ")
|
||||
)
|
||||
}
|
||||
|
||||
ts_object(
|
||||
type,
|
||||
sprintf("Robj.dataframe(%s)", type_fn),
|
||||
check = function(x) {
|
||||
if (!is.data.frame(x)) stop("Expected a data frame")
|
||||
x
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
#' Null type
|
||||
#'
|
||||
#' This is a type that only accepts `NULL`. For function return types, use `ts_void`.
|
||||
#'
|
||||
#' @return A ts object that only accepts `NULL`.
|
||||
#' @export
|
||||
#'
|
||||
#' @md
|
||||
#' @examples
|
||||
#' x <- ts_null()
|
||||
#' x$check(NULL)
|
||||
ts_null <- function() {
|
||||
ts_object(
|
||||
"z.null()",
|
||||
"Robj.null()",
|
||||
check = function(x) {
|
||||
if (!is.null(x)) stop("Expected NULL")
|
||||
x
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
#' Void type
|
||||
#'
|
||||
#' This is a type that accepts null values (this would typically be used for
|
||||
#' functions that return nothing).
|
||||
#' @return A ts object that accepts `NULL`.
|
||||
#' @export
|
||||
#' @md
|
||||
#' @seealso \code{\link{ts_null}}
|
||||
ts_void <- function() {
|
||||
ts_object(
|
||||
"z.void()",
|
||||
"null",
|
||||
check = function(x) {
|
||||
return(NULL)
|
||||
}
|
||||
)
|
||||
}
|
||||
97
README.Rmd
Normal file
97
README.Rmd
Normal file
@ -0,0 +1,97 @@
|
||||
---
|
||||
output: github_document
|
||||
---
|
||||
|
||||
<!-- README.md is generated from README.Rmd. Please edit that file -->
|
||||
|
||||
```{r, include = FALSE}
|
||||
knitr::opts_chunk$set(
|
||||
collapse = TRUE,
|
||||
comment = "#>",
|
||||
fig.path = "man/figures/README-",
|
||||
out.width = "100%"
|
||||
)
|
||||
```
|
||||
|
||||
# ts: Write apps with R, Rserve, and TypeScript
|
||||
|
||||
<!-- badges: start -->
|
||||
[](https://github.com/tmelliott/ts/actions/workflows/R-CMD-check.yaml)
|
||||
[](https://github.com/tmelliott/ts/actions/workflows/check-demo.yaml)
|
||||
<!-- badges: end -->
|
||||
|
||||
The **ts** package makes it easy for users to write functions that can be used in [**rserve-ts**](https://www.npmjs.com/package/rserve-ts) applications.
|
||||
|
||||
## Installation
|
||||
|
||||
You can install the development version of ts from [GitHub](https://github.com/) with:
|
||||
|
||||
``` r
|
||||
# install.packages("devtools")
|
||||
devtools::install_github("tmelliott/ts")
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
Writing functions is easy, just use the `ts_*()` functions to define formals and return types.
|
||||
|
||||
```r
|
||||
# demo.R
|
||||
library(ts)
|
||||
addFn <- ts_function(
|
||||
function(a = ts_numeric(1), b = ts_numeric(1)) a + b,
|
||||
result = ts_numeric(1)
|
||||
)
|
||||
sampleFn <- ts_function(
|
||||
function(x = ts_character(), n = ts_integer(1)) sample(x, n),
|
||||
result = ts_character()
|
||||
)
|
||||
app <- ts_function(
|
||||
function() {
|
||||
list(
|
||||
add = addFn,
|
||||
sample = sampleFn
|
||||
)
|
||||
},
|
||||
result = ts_list(
|
||||
add = addFn,
|
||||
sample = sampleFn
|
||||
)
|
||||
)
|
||||
|
||||
# TODO: specify exactly which functions to export in the entry point
|
||||
# ts_export(app)
|
||||
```
|
||||
|
||||
Then use `ts_compile()` to generate the TypeScript schemas:
|
||||
```typescript
|
||||
import { Robj } from 'rserve-ts';
|
||||
import { z } from 'zod';
|
||||
|
||||
const addFn = Robj.ocap([z.number(), z.number()], Robj.numeric(1));
|
||||
const app = Robj.ocap([],
|
||||
Robj.list({
|
||||
add: Robj.ocap(),
|
||||
sample: Robj.ocap()
|
||||
})
|
||||
);
|
||||
const sampleFn = Robj.ocap(
|
||||
[z.union([z.string(), z.array(z.string())]), z.number()],
|
||||
Robj.character()
|
||||
);
|
||||
|
||||
export default {
|
||||
addFn,
|
||||
app,
|
||||
sampleFn
|
||||
};
|
||||
```
|
||||
|
||||
You can then import this into your [rserve-ts](https://www.npmjs.com/package/rserve-ts) application. See `tests/testthat/sampler` for an example.
|
||||
|
||||
It is also possible to generate a sourceable file to deploy an Rserve instance with your app code using `ts_deploy()`:
|
||||
|
||||
```r
|
||||
ts_deploy(app)
|
||||
# run with: Rscript app.rserve.R
|
||||
```
|
||||
94
README.md
Normal file
94
README.md
Normal file
@ -0,0 +1,94 @@
|
||||
|
||||
<!-- README.md is generated from README.Rmd. Please edit that file -->
|
||||
|
||||
# ts: Write apps with R, Rserve, and TypeScript
|
||||
|
||||
<!-- badges: start -->
|
||||
|
||||
[](https://github.com/tmelliott/ts/actions/workflows/R-CMD-check.yaml)
|
||||
[](https://github.com/tmelliott/ts/actions/workflows/check-demo.yaml)
|
||||
<!-- badges: end -->
|
||||
|
||||
The **ts** package makes it easy for users to write functions that can
|
||||
be used in [**rserve-ts**](https://www.npmjs.com/package/rserve-ts)
|
||||
applications.
|
||||
|
||||
## Installation
|
||||
|
||||
You can install the development version of ts from
|
||||
[GitHub](https://github.com/) with:
|
||||
|
||||
``` r
|
||||
# install.packages("devtools")
|
||||
devtools::install_github("tmelliott/ts")
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
Writing functions is easy, just use the `ts_*()` functions to define
|
||||
formals and return types.
|
||||
|
||||
``` r
|
||||
# demo.R
|
||||
library(ts)
|
||||
addFn <- ts_function(
|
||||
function(a = ts_numeric(1), b = ts_numeric(1)) a + b,
|
||||
result = ts_numeric(1)
|
||||
)
|
||||
sampleFn <- ts_function(
|
||||
function(x = ts_character(), n = ts_integer(1)) sample(x, n),
|
||||
result = ts_character()
|
||||
)
|
||||
app <- ts_function(
|
||||
function() {
|
||||
list(
|
||||
add = addFn,
|
||||
sample = sampleFn
|
||||
)
|
||||
},
|
||||
result = ts_list(
|
||||
add = addFn,
|
||||
sample = sampleFn
|
||||
)
|
||||
)
|
||||
|
||||
# TODO: specify exactly which functions to export in the entry point
|
||||
# ts_export(app)
|
||||
```
|
||||
|
||||
Then use `ts_compile()` to generate the TypeScript schemas:
|
||||
|
||||
``` typescript
|
||||
import { Robj } from 'rserve-ts';
|
||||
import { z } from 'zod';
|
||||
|
||||
const addFn = Robj.ocap([z.number(), z.number()], Robj.numeric(1));
|
||||
const app = Robj.ocap([],
|
||||
Robj.list({
|
||||
add: Robj.ocap(),
|
||||
sample: Robj.ocap()
|
||||
})
|
||||
);
|
||||
const sampleFn = Robj.ocap(
|
||||
[z.union([z.string(), z.array(z.string())]), z.number()],
|
||||
Robj.character()
|
||||
);
|
||||
|
||||
export default {
|
||||
addFn,
|
||||
app,
|
||||
sampleFn
|
||||
};
|
||||
```
|
||||
|
||||
You can then import this into your
|
||||
[rserve-ts](https://www.npmjs.com/package/rserve-ts) application. See
|
||||
`tests/testthat/sampler` for an example.
|
||||
|
||||
It is also possible to generate a sourceable file to deploy an Rserve
|
||||
instance with your app code using `ts_deploy()`:
|
||||
|
||||
``` r
|
||||
ts_deploy(app)
|
||||
# run with: Rscript app.rserve.R
|
||||
```
|
||||
28
_pkgdown.yml
Normal file
28
_pkgdown.yml
Normal file
@ -0,0 +1,28 @@
|
||||
url: http://tomelliott.co.nz/ts/
|
||||
template:
|
||||
bootstrap: 5
|
||||
bootswatch: simplex
|
||||
|
||||
reference:
|
||||
- title: Building apps
|
||||
desc: Writing and compiling functions into Rserve apps.
|
||||
- contents:
|
||||
- ts_function
|
||||
- ts_app
|
||||
- ts_compile
|
||||
- ts_deploy
|
||||
- title: Types
|
||||
desc: Type helper functions for defining argument and return types.
|
||||
- contents:
|
||||
- type_objects
|
||||
- ts_logical
|
||||
- ts_integer
|
||||
- ts_numeric
|
||||
- ts_character
|
||||
- ts_factor
|
||||
- ts_list
|
||||
- ts_dataframe
|
||||
- ts_null
|
||||
- ts_void
|
||||
- ts_object
|
||||
- ts_union
|
||||
@ -1,62 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<!-- Generated by pkgdown: do not edit by hand --><html lang="en"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"><title>Articles • ts</title><script src="../deps/jquery-3.6.0/jquery-3.6.0.min.js"></script><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"><link href="../deps/bootstrap-5.3.1/bootstrap.min.css" rel="stylesheet"><script src="../deps/bootstrap-5.3.1/bootstrap.bundle.min.js"></script><link href="../deps/font-awesome-6.5.2/css/all.min.css" rel="stylesheet"><link href="../deps/font-awesome-6.5.2/css/v4-shims.min.css" rel="stylesheet"><script src="../deps/headroom-0.11.0/headroom.min.js"></script><script src="../deps/headroom-0.11.0/jQuery.headroom.min.js"></script><script src="../deps/bootstrap-toc-1.0.1/bootstrap-toc.min.js"></script><script src="../deps/clipboard.js-2.0.11/clipboard.min.js"></script><script src="../deps/search-1.0.0/autocomplete.jquery.min.js"></script><script src="../deps/search-1.0.0/fuse.min.js"></script><script src="../deps/search-1.0.0/mark.min.js"></script><!-- pkgdown --><script src="../pkgdown.js"></script><meta property="og:title" content="Articles"></head><body>
|
||||
<a href="#main" class="visually-hidden-focusable">Skip to contents</a>
|
||||
|
||||
|
||||
<nav class="navbar navbar-expand-lg fixed-top bg-light" data-bs-theme="light" aria-label="Site navigation"><div class="container">
|
||||
|
||||
<a class="navbar-brand me-2" href="../index.html">ts</a>
|
||||
|
||||
<small class="nav-text text-muted me-auto" data-bs-toggle="tooltip" data-bs-placement="bottom" title="">0.1.0</small>
|
||||
|
||||
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
<div id="navbar" class="collapse navbar-collapse ms-3">
|
||||
<ul class="navbar-nav me-auto"><li class="nav-item"><a class="nav-link" href="../reference/index.html">Reference</a></li>
|
||||
<li class="nav-item dropdown">
|
||||
<button class="nav-link dropdown-toggle" type="button" id="dropdown-articles" data-bs-toggle="dropdown" aria-expanded="false" aria-haspopup="true">Articles</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="dropdown-articles"><li><a class="dropdown-item" href="../articles/simple-react-app.html">Build a simple ReactJS app</a></li>
|
||||
</ul></li>
|
||||
<li class="nav-item"><a class="nav-link" href="../news/index.html">Changelog</a></li>
|
||||
</ul><ul class="navbar-nav"><li class="nav-item"><form class="form-inline" role="search">
|
||||
<input class="form-control" type="search" name="search-input" id="search-input" autocomplete="off" aria-label="Search site" placeholder="Search for" data-search-index="../search.json"></form></li>
|
||||
</ul></div>
|
||||
|
||||
|
||||
</div>
|
||||
</nav><div class="container template-article-index">
|
||||
<div class="row">
|
||||
<main id="main" class="col-md-9"><div class="page-header">
|
||||
|
||||
<h1>Articles</h1>
|
||||
</div>
|
||||
|
||||
<div class="section ">
|
||||
<h3>All vignettes</h3>
|
||||
<div class="section-desc"></div>
|
||||
|
||||
<dl><dt><a href="simple-react-app.html">Build a simple ReactJS app</a></dt>
|
||||
<dd>
|
||||
</dd></dl></div>
|
||||
</main></div>
|
||||
|
||||
|
||||
<footer><div class="pkgdown-footer-left">
|
||||
<p>Developed by Tom Elliott.</p>
|
||||
</div>
|
||||
|
||||
<div class="pkgdown-footer-right">
|
||||
<p>Site built with <a href="https://pkgdown.r-lib.org/" class="external-link">pkgdown</a> 2.1.1.</p>
|
||||
</div>
|
||||
|
||||
</footer></div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</body></html>
|
||||
|
||||
@ -1,371 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<!-- Generated by pkgdown: do not edit by hand --><html lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<title>Build a simple ReactJS app • ts</title>
|
||||
<script src="../deps/jquery-3.6.0/jquery-3.6.0.min.js"></script><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<link href="../deps/bootstrap-5.3.1/bootstrap.min.css" rel="stylesheet">
|
||||
<script src="../deps/bootstrap-5.3.1/bootstrap.bundle.min.js"></script><link href="../deps/font-awesome-6.5.2/css/all.min.css" rel="stylesheet">
|
||||
<link href="../deps/font-awesome-6.5.2/css/v4-shims.min.css" rel="stylesheet">
|
||||
<script src="../deps/headroom-0.11.0/headroom.min.js"></script><script src="../deps/headroom-0.11.0/jQuery.headroom.min.js"></script><script src="../deps/bootstrap-toc-1.0.1/bootstrap-toc.min.js"></script><script src="../deps/clipboard.js-2.0.11/clipboard.min.js"></script><script src="../deps/search-1.0.0/autocomplete.jquery.min.js"></script><script src="../deps/search-1.0.0/fuse.min.js"></script><script src="../deps/search-1.0.0/mark.min.js"></script><!-- pkgdown --><script src="../pkgdown.js"></script><meta property="og:title" content="Build a simple ReactJS app">
|
||||
</head>
|
||||
<body>
|
||||
<a href="#main" class="visually-hidden-focusable">Skip to contents</a>
|
||||
|
||||
|
||||
<nav class="navbar navbar-expand-lg fixed-top bg-light" data-bs-theme="light" aria-label="Site navigation"><div class="container">
|
||||
|
||||
<a class="navbar-brand me-2" href="../index.html">ts</a>
|
||||
|
||||
<small class="nav-text text-muted me-auto" data-bs-toggle="tooltip" data-bs-placement="bottom" title="">0.1.0</small>
|
||||
|
||||
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
<div id="navbar" class="collapse navbar-collapse ms-3">
|
||||
<ul class="navbar-nav me-auto">
|
||||
<li class="nav-item"><a class="nav-link" href="../reference/index.html">Reference</a></li>
|
||||
<li class="active nav-item dropdown">
|
||||
<button class="nav-link dropdown-toggle" type="button" id="dropdown-articles" data-bs-toggle="dropdown" aria-expanded="false" aria-haspopup="true">Articles</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="dropdown-articles">
|
||||
<li><a class="dropdown-item" href="../articles/simple-react-app.html">Build a simple ReactJS app</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="nav-item"><a class="nav-link" href="../news/index.html">Changelog</a></li>
|
||||
</ul>
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item"><form class="form-inline" role="search">
|
||||
<input class="form-control" type="search" name="search-input" id="search-input" autocomplete="off" aria-label="Search site" placeholder="Search for" data-search-index="../search.json">
|
||||
</form></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</nav><div class="container template-article">
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="row">
|
||||
<main id="main" class="col-md-9"><div class="page-header">
|
||||
|
||||
<h1>Build a simple ReactJS app</h1>
|
||||
|
||||
|
||||
|
||||
<div class="d-none name"><code>simple-react-app.Rmd</code></div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<p>We will create a simple <a href="https://reactjs.org/" class="external-link">ReactJS</a>
|
||||
application that implements our favourite <em>Old Faithful</em> dataset.
|
||||
Of course, this is not a great use-case for Rserve as it would be more
|
||||
appropriate to use a REST API, but it is a simple example to demonstrate
|
||||
how to use Rserve with a front-end application.</p>
|
||||
<div class="section level2">
|
||||
<h2 id="install-the-ts-package">Install the ts package<a class="anchor" aria-label="anchor" href="#install-the-ts-package"></a>
|
||||
</h2>
|
||||
<div class="sourceCode" id="cb1"><pre class="downlit sourceCode r">
|
||||
<code class="sourceCode R"><span><span class="fu">devtools</span><span class="fu">::</span><span class="fu">install_github</span><span class="op">(</span><span class="st">'tmelliott/ts'</span><span class="op">)</span></span></code></pre></div>
|
||||
</div>
|
||||
<div class="section level2">
|
||||
<h2 id="write-the-r-code">Write the R code<a class="anchor" aria-label="anchor" href="#write-the-r-code"></a>
|
||||
</h2>
|
||||
<p>The code is saved in a file called <code>faithful-app.R</code>, and
|
||||
we can preview the results by calling the functions:</p>
|
||||
<div class="sourceCode" id="cb2"><pre class="downlit sourceCode r">
|
||||
<code class="sourceCode R"><span><span class="fu"><a href="https://rdrr.io/r/base/cat.html" class="external-link">cat</a></span><span class="op">(</span><span class="fu"><a href="https://rdrr.io/r/base/readLines.html" class="external-link">readLines</a></span><span class="op">(</span><span class="st">'faithful-app.R'</span><span class="op">)</span>, sep <span class="op">=</span> <span class="st">'\n'</span><span class="op">)</span></span>
|
||||
<span><span class="co">#> library(ts)</span></span>
|
||||
<span><span class="co">#> </span></span>
|
||||
<span><span class="co">#> get_hist <- ts_function(</span></span>
|
||||
<span><span class="co">#> function(bins = ts_integer(1)) {</span></span>
|
||||
<span><span class="co">#> h <- hist(faithful$waiting, breaks = bins, plot = FALSE)</span></span>
|
||||
<span><span class="co">#> data.frame(x = h$mids, y = h$density)</span></span>
|
||||
<span><span class="co">#> },</span></span>
|
||||
<span><span class="co">#> result = ts_dataframe(x = ts_numeric(0), y = ts_numeric(0))</span></span>
|
||||
<span><span class="co">#> )</span></span>
|
||||
<span><span class="co">#> get_smoother <- ts_function(</span></span>
|
||||
<span><span class="co">#> function(bandwidth = ts_numeric(1)) {</span></span>
|
||||
<span><span class="co">#> d <- density(faithful$waiting, bw = bandwidth)</span></span>
|
||||
<span><span class="co">#> data.frame(x = d$x, y = d$y)</span></span>
|
||||
<span><span class="co">#> },</span></span>
|
||||
<span><span class="co">#> result = ts_dataframe(x = ts_numeric(0), y = ts_numeric(0))</span></span>
|
||||
<span><span class="co">#> )</span></span>
|
||||
<span></span>
|
||||
<span><span class="kw"><a href="https://rdrr.io/r/base/source.html" class="external-link">source</a></span><span class="op">(</span><span class="st">'faithful-app.R'</span><span class="op">)</span></span>
|
||||
<span></span>
|
||||
<span><span class="va">get_hist</span><span class="op">$</span><span class="fu">call</span><span class="op">(</span><span class="fl">10</span><span class="op">)</span></span>
|
||||
<span><span class="co">#> x y</span></span>
|
||||
<span><span class="co">#> 1 42.5 0.0029411765</span></span>
|
||||
<span><span class="co">#> 2 47.5 0.0161764706</span></span>
|
||||
<span><span class="co">#> 3 52.5 0.0242647059</span></span>
|
||||
<span><span class="co">#> 4 57.5 0.0176470588</span></span>
|
||||
<span><span class="co">#> 5 62.5 0.0102941176</span></span>
|
||||
<span><span class="co">#> 6 67.5 0.0073529412</span></span>
|
||||
<span><span class="co">#> 7 72.5 0.0198529412</span></span>
|
||||
<span><span class="co">#> 8 77.5 0.0397058824</span></span>
|
||||
<span><span class="co">#> 9 82.5 0.0404411765</span></span>
|
||||
<span><span class="co">#> 10 87.5 0.0169117647</span></span>
|
||||
<span><span class="co">#> 11 92.5 0.0036764706</span></span>
|
||||
<span><span class="co">#> 12 97.5 0.0007352941</span></span></code></pre></div>
|
||||
<p>That’s it! We’ll use <code><a href="../reference/ts_compile.html">ts_compile()</a></code> later to create the
|
||||
server code and Typescript schema for the app.</p>
|
||||
</div>
|
||||
<div class="section level2">
|
||||
<h2 id="create-the-react-app">Create the React app<a class="anchor" aria-label="anchor" href="#create-the-react-app"></a>
|
||||
</h2>
|
||||
<p>I’m using <a href="https://vite.dev/" class="external-link">Vite</a> to create the app, but
|
||||
you could use any framework. Whatever you use, you’ll need to be able to
|
||||
bundle the code (including libraries such as <a href="https://zod.dev" class="external-link">zod</a>).</p>
|
||||
<div class="sourceCode" id="cb3"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb3-1"><a href="#cb3-1" tabindex="-1"></a><span class="ex">pnpm</span> create vite faithful-demo <span class="at">--template</span> vanilla-ts</span>
|
||||
<span id="cb3-2"><a href="#cb3-2" tabindex="-1"></a><span class="bu">cd</span> faithful-demo</span>
|
||||
<span id="cb3-3"><a href="#cb3-3" tabindex="-1"></a><span class="ex">pnpm</span> install</span>
|
||||
<span id="cb3-4"><a href="#cb3-4" tabindex="-1"></a><span class="ex">pnpm</span> run dev</span></code></pre></div>
|
||||
<p>You should now be able to see the default Vite app running at
|
||||
<code>http://localhost:5173</code> (or similar, see the console
|
||||
output).</p>
|
||||
<p>Now install the <code>rserve-ts</code> and <code>zod</code>
|
||||
packages:</p>
|
||||
<div class="sourceCode" id="cb4"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb4-1"><a href="#cb4-1" tabindex="-1"></a><span class="ex">pnpm</span> install rserve-ts zod</span></code></pre></div>
|
||||
<div class="section level3">
|
||||
<h3 id="create-the-server-code">Create the server code<a class="anchor" aria-label="anchor" href="#create-the-server-code"></a>
|
||||
</h3>
|
||||
<p>We now use the <code><a href="../reference/ts_compile.html">ts_compile()</a></code> function to create two
|
||||
files:</p>
|
||||
<ul>
|
||||
<li>
|
||||
<code>faithful-app.rserve.R</code> is the file that will start the
|
||||
Rserve instance with your apps functions available.</li>
|
||||
<li>
|
||||
<code>faithful-app.rserve.ts</code> contains the TypeScript schema
|
||||
(using <a href="https://zod.dev" class="external-link">zod</a>) that will let you use the R
|
||||
functions directly in the app like any other typescript function!</li>
|
||||
</ul>
|
||||
<p>We’ll send these straight to the <code>faithful-demo/src</code>
|
||||
directory.</p>
|
||||
<div class="sourceCode" id="cb5"><pre class="downlit sourceCode r">
|
||||
<code class="sourceCode R"><span><span class="fu"><a href="../reference/ts_compile.html">ts_compile</a></span><span class="op">(</span><span class="st">'faithful-app.R'</span>, filename <span class="op">=</span> <span class="st">'faithful-demo/src/faithful-app.rserve'</span><span class="op">)</span></span></code></pre></div>
|
||||
</div>
|
||||
<div class="section level3">
|
||||
<h3 id="write-the-app">Write the app<a class="anchor" aria-label="anchor" href="#write-the-app"></a>
|
||||
</h3>
|
||||
<p>The rest of the process simply requires writing TypeScript code. I
|
||||
won’t go into detail since that’s not the focus of this vignette, but
|
||||
below you can see the code written with some basic comments. Copy and
|
||||
paste these to get the app running.</p>
|
||||
<div class="sourceCode" id="cb6"><pre class="sourceCode typescript"><code class="sourceCode typescript"><span id="cb6-1"><a href="#cb6-1" tabindex="-1"></a><span class="co">// main.ts</span></span>
|
||||
<span id="cb6-2"><a href="#cb6-2" tabindex="-1"></a><span class="im">import</span> <span class="st">"./style.css"</span><span class="op">;</span></span>
|
||||
<span id="cb6-3"><a href="#cb6-3" tabindex="-1"></a></span>
|
||||
<span id="cb6-4"><a href="#cb6-4" tabindex="-1"></a><span class="im">import</span> RserveClient <span class="im">from</span> <span class="st">"rserve-ts"</span><span class="op">;</span></span>
|
||||
<span id="cb6-5"><a href="#cb6-5" tabindex="-1"></a><span class="im">import</span> faithfulApp <span class="im">from</span> <span class="st">"./faithful-app.rserve"</span><span class="op">;</span></span>
|
||||
<span id="cb6-6"><a href="#cb6-6" tabindex="-1"></a><span class="im">import</span> { z } <span class="im">from</span> <span class="st">"zod"</span><span class="op">;</span></span>
|
||||
<span id="cb6-7"><a href="#cb6-7" tabindex="-1"></a></span>
|
||||
<span id="cb6-8"><a href="#cb6-8" tabindex="-1"></a><span class="bu">document</span><span class="op">.</span><span class="fu">querySelector</span><span class="op"><</span><span class="bu">HTMLDivElement</span><span class="op">></span>(<span class="st">"#app"</span>)<span class="op">!.</span><span class="at">innerHTML</span> <span class="op">=</span> <span class="vs">`</span></span>
|
||||
<span id="cb6-9"><a href="#cb6-9" tabindex="-1"></a><span class="vs"> <div></span></span>
|
||||
<span id="cb6-10"><a href="#cb6-10" tabindex="-1"></a><span class="vs"> <h1>Rserve and TypeScript</h1></span></span>
|
||||
<span id="cb6-11"><a href="#cb6-11" tabindex="-1"></a><span class="vs"> <div class="card"></span></span>
|
||||
<span id="cb6-12"><a href="#cb6-12" tabindex="-1"></a><span class="vs"> Number of bins:</span></span>
|
||||
<span id="cb6-13"><a href="#cb6-13" tabindex="-1"></a><span class="vs"> <input type="number" id="n" value="10" size="5" /></span></span>
|
||||
<span id="cb6-14"><a href="#cb6-14" tabindex="-1"></a><span class="vs"> <button id="counter" type="button">Make histogram</button></span></span>
|
||||
<span id="cb6-15"><a href="#cb6-15" tabindex="-1"></a><span class="vs"> </div></span></span>
|
||||
<span id="cb6-16"><a href="#cb6-16" tabindex="-1"></a><span class="vs"> <div id="hist" style="display: flex; align-items: flex-end; gap: 2px;"></div></span></span>
|
||||
<span id="cb6-17"><a href="#cb6-17" tabindex="-1"></a><span class="vs"> </div></span></span>
|
||||
<span id="cb6-18"><a href="#cb6-18" tabindex="-1"></a><span class="vs">`</span><span class="op">;</span></span>
|
||||
<span id="cb6-19"><a href="#cb6-19" tabindex="-1"></a></span>
|
||||
<span id="cb6-20"><a href="#cb6-20" tabindex="-1"></a><span class="kw">type</span> FaithfulApp <span class="op">=</span> z<span class="op">.</span><span class="at">infer</span><span class="op"><</span>z<span class="op">.</span><span class="at">ZodObject</span><span class="op"><</span><span class="kw">typeof</span> faithfulApp<span class="op">>>;</span></span>
|
||||
<span id="cb6-21"><a href="#cb6-21" tabindex="-1"></a></span>
|
||||
<span id="cb6-22"><a href="#cb6-22" tabindex="-1"></a><span class="kw">let</span> getHist<span class="op">:</span> FaithfulApp[<span class="st">"get_hist"</span>] <span class="op">|</span> <span class="dt">undefined</span> <span class="op">=</span> <span class="kw">undefined</span><span class="op">;</span></span>
|
||||
<span id="cb6-23"><a href="#cb6-23" tabindex="-1"></a></span>
|
||||
<span id="cb6-24"><a href="#cb6-24" tabindex="-1"></a><span class="kw">async</span> <span class="kw">function</span> <span class="fu">connectToRserve</span>() {</span>
|
||||
<span id="cb6-25"><a href="#cb6-25" tabindex="-1"></a> <span class="kw">const</span> client <span class="op">=</span> <span class="cf">await</span> RserveClient<span class="op">.</span><span class="fu">create</span>({ host<span class="op">:</span> <span class="st">"ws://localhost:6311"</span> })<span class="op">;</span></span>
|
||||
<span id="cb6-26"><a href="#cb6-26" tabindex="-1"></a> <span class="kw">const</span> app <span class="op">=</span> <span class="cf">await</span> client<span class="op">.</span><span class="fu">ocap</span>(faithfulApp)<span class="op">;</span></span>
|
||||
<span id="cb6-27"><a href="#cb6-27" tabindex="-1"></a> <span class="bu">console</span><span class="op">.</span><span class="fu">log</span>(<span class="st">"Connected to Rserve: "</span><span class="op">,</span> app)<span class="op">;</span></span>
|
||||
<span id="cb6-28"><a href="#cb6-28" tabindex="-1"></a> getHist <span class="op">=</span> app<span class="op">.</span><span class="at">get_hist</span><span class="op">;</span></span>
|
||||
<span id="cb6-29"><a href="#cb6-29" tabindex="-1"></a>}</span>
|
||||
<span id="cb6-30"><a href="#cb6-30" tabindex="-1"></a><span class="fu">connectToRserve</span>()<span class="op">;</span></span>
|
||||
<span id="cb6-31"><a href="#cb6-31" tabindex="-1"></a></span>
|
||||
<span id="cb6-32"><a href="#cb6-32" tabindex="-1"></a><span class="bu">document</span></span>
|
||||
<span id="cb6-33"><a href="#cb6-33" tabindex="-1"></a> <span class="op">.</span><span class="fu">querySelector</span><span class="op"><</span><span class="bu">HTMLButtonElement</span><span class="op">></span>(<span class="st">"#counter"</span>)<span class="op">!</span></span>
|
||||
<span id="cb6-34"><a href="#cb6-34" tabindex="-1"></a> <span class="op">.</span><span class="fu">addEventListener</span>(<span class="st">"click"</span><span class="op">,</span> <span class="kw">async</span> () <span class="kw">=></span> {</span>
|
||||
<span id="cb6-35"><a href="#cb6-35" tabindex="-1"></a> <span class="cf">if</span> (<span class="op">!</span>getHist) <span class="cf">return</span><span class="op">;</span></span>
|
||||
<span id="cb6-36"><a href="#cb6-36" tabindex="-1"></a> <span class="kw">const</span> Nbin <span class="op">=</span> <span class="pp">parseInt</span>(</span>
|
||||
<span id="cb6-37"><a href="#cb6-37" tabindex="-1"></a> <span class="bu">document</span><span class="op">.</span><span class="fu">querySelector</span><span class="op"><</span><span class="bu">HTMLInputElement</span><span class="op">></span>(<span class="st">"#n"</span>)<span class="op">!.</span><span class="at">value</span></span>
|
||||
<span id="cb6-38"><a href="#cb6-38" tabindex="-1"></a> )<span class="op">;</span></span>
|
||||
<span id="cb6-39"><a href="#cb6-39" tabindex="-1"></a> <span class="kw">const</span> hist <span class="op">=</span> <span class="cf">await</span> <span class="fu">getHist</span>(Nbin)<span class="op">;</span></span>
|
||||
<span id="cb6-40"><a href="#cb6-40" tabindex="-1"></a> <span class="kw">const</span> maxY <span class="op">=</span> <span class="bu">Math</span><span class="op">.</span><span class="fu">max</span>(<span class="op">...</span>hist<span class="op">.</span><span class="at">y</span>)<span class="op">;</span></span>
|
||||
<span id="cb6-41"><a href="#cb6-41" tabindex="-1"></a></span>
|
||||
<span id="cb6-42"><a href="#cb6-42" tabindex="-1"></a> <span class="kw">const</span> histDiv <span class="op">=</span> <span class="bu">document</span><span class="op">.</span><span class="fu">querySelector</span><span class="op"><</span><span class="bu">HTMLDivElement</span><span class="op">></span>(<span class="st">"#hist"</span>)<span class="op">!;</span></span>
|
||||
<span id="cb6-43"><a href="#cb6-43" tabindex="-1"></a> histDiv<span class="op">.</span><span class="at">innerHTML</span> <span class="op">=</span> <span class="vs">`</span></span>
|
||||
<span id="cb6-44"><a href="#cb6-44" tabindex="-1"></a><span class="vs"> </span><span class="sc">${</span><span class="bu">Array</span><span class="op">.</span><span class="fu">from</span>(hist<span class="op">.</span><span class="at">y</span>)</span>
|
||||
<span id="cb6-45"><a href="#cb6-45" tabindex="-1"></a> <span class="op">.</span><span class="fu">map</span>(</span>
|
||||
<span id="cb6-46"><a href="#cb6-46" tabindex="-1"></a> (yi) <span class="kw">=></span></span>
|
||||
<span id="cb6-47"><a href="#cb6-47" tabindex="-1"></a> <span class="vs">`<div style="height: </span><span class="sc">${</span></span>
|
||||
<span id="cb6-48"><a href="#cb6-48" tabindex="-1"></a> (yi <span class="op">/</span> maxY) <span class="op">*</span> <span class="dv">180</span></span>
|
||||
<span id="cb6-49"><a href="#cb6-49" tabindex="-1"></a> <span class="sc">}</span><span class="vs">px; flex: 1; background: pink;"></div>`</span></span>
|
||||
<span id="cb6-50"><a href="#cb6-50" tabindex="-1"></a> )</span>
|
||||
<span id="cb6-51"><a href="#cb6-51" tabindex="-1"></a> <span class="op">.</span><span class="fu">join</span>(<span class="st">""</span>)<span class="sc">}</span></span>
|
||||
<span id="cb6-52"><a href="#cb6-52" tabindex="-1"></a><span class="vs"> `</span><span class="op">;</span></span>
|
||||
<span id="cb6-53"><a href="#cb6-53" tabindex="-1"></a> })<span class="op">;</span></span></code></pre></div>
|
||||
<div class="sourceCode" id="cb7"><pre class="sourceCode css"><code class="sourceCode css"><span id="cb7-1"><a href="#cb7-1" tabindex="-1"></a><span class="er">// style.css</span></span>
|
||||
<span id="cb7-2"><a href="#cb7-2" tabindex="-1"></a><span class="in">:root</span> {</span>
|
||||
<span id="cb7-3"><a href="#cb7-3" tabindex="-1"></a> <span class="kw">font-family</span><span class="ch">:</span> <span class="dv">Inter</span><span class="op">,</span> <span class="dv">system-ui</span><span class="op">,</span> <span class="dv">Avenir</span><span class="op">,</span> <span class="dv">Helvetica</span><span class="op">,</span> <span class="dv">Arial</span><span class="op">,</span> <span class="dv">sans-serif</span><span class="op">;</span></span>
|
||||
<span id="cb7-4"><a href="#cb7-4" tabindex="-1"></a> <span class="kw">line-height</span><span class="ch">:</span> <span class="dv">1.5</span><span class="op">;</span></span>
|
||||
<span id="cb7-5"><a href="#cb7-5" tabindex="-1"></a> <span class="kw">font-weight</span><span class="ch">:</span> <span class="dv">400</span><span class="op">;</span></span>
|
||||
<span id="cb7-6"><a href="#cb7-6" tabindex="-1"></a></span>
|
||||
<span id="cb7-7"><a href="#cb7-7" tabindex="-1"></a> <span class="kw">color-scheme</span><span class="ch">:</span> <span class="dv">light</span> <span class="dv">dark</span><span class="op">;</span></span>
|
||||
<span id="cb7-8"><a href="#cb7-8" tabindex="-1"></a> <span class="kw">color</span><span class="ch">:</span> <span class="fu">rgba(</span><span class="dv">255</span><span class="op">,</span> <span class="dv">255</span><span class="op">,</span> <span class="dv">255</span><span class="op">,</span> <span class="dv">0.87</span><span class="fu">)</span><span class="op">;</span></span>
|
||||
<span id="cb7-9"><a href="#cb7-9" tabindex="-1"></a> <span class="kw">background-color</span><span class="ch">:</span> <span class="cn">#242424</span><span class="op">;</span></span>
|
||||
<span id="cb7-10"><a href="#cb7-10" tabindex="-1"></a></span>
|
||||
<span id="cb7-11"><a href="#cb7-11" tabindex="-1"></a> <span class="kw">font-synthesis</span><span class="ch">:</span> <span class="dv">none</span><span class="op">;</span></span>
|
||||
<span id="cb7-12"><a href="#cb7-12" tabindex="-1"></a> <span class="kw">text-rendering</span><span class="ch">:</span> <span class="dv">optimizeLegibility</span><span class="op">;</span></span>
|
||||
<span id="cb7-13"><a href="#cb7-13" tabindex="-1"></a> <span class="kw">-webkit-font-smoothing</span><span class="ch">:</span> <span class="dv">antialiased</span><span class="op">;</span></span>
|
||||
<span id="cb7-14"><a href="#cb7-14" tabindex="-1"></a> <span class="kw">-moz-osx-font-smoothing</span><span class="ch">:</span> <span class="dv">grayscale</span><span class="op">;</span></span>
|
||||
<span id="cb7-15"><a href="#cb7-15" tabindex="-1"></a>}</span>
|
||||
<span id="cb7-16"><a href="#cb7-16" tabindex="-1"></a></span>
|
||||
<span id="cb7-17"><a href="#cb7-17" tabindex="-1"></a>a {</span>
|
||||
<span id="cb7-18"><a href="#cb7-18" tabindex="-1"></a> <span class="kw">font-weight</span><span class="ch">:</span> <span class="dv">500</span><span class="op">;</span></span>
|
||||
<span id="cb7-19"><a href="#cb7-19" tabindex="-1"></a> <span class="kw">color</span><span class="ch">:</span> <span class="cn">#646cff</span><span class="op">;</span></span>
|
||||
<span id="cb7-20"><a href="#cb7-20" tabindex="-1"></a> <span class="kw">text-decoration</span><span class="ch">:</span> <span class="bu">inherit</span><span class="op">;</span></span>
|
||||
<span id="cb7-21"><a href="#cb7-21" tabindex="-1"></a>}</span>
|
||||
<span id="cb7-22"><a href="#cb7-22" tabindex="-1"></a>a<span class="in">:hover</span> {</span>
|
||||
<span id="cb7-23"><a href="#cb7-23" tabindex="-1"></a> <span class="kw">color</span><span class="ch">:</span> <span class="cn">#535bf2</span><span class="op">;</span></span>
|
||||
<span id="cb7-24"><a href="#cb7-24" tabindex="-1"></a>}</span>
|
||||
<span id="cb7-25"><a href="#cb7-25" tabindex="-1"></a></span>
|
||||
<span id="cb7-26"><a href="#cb7-26" tabindex="-1"></a>body {</span>
|
||||
<span id="cb7-27"><a href="#cb7-27" tabindex="-1"></a> <span class="kw">margin</span><span class="ch">:</span> <span class="dv">0</span><span class="op">;</span></span>
|
||||
<span id="cb7-28"><a href="#cb7-28" tabindex="-1"></a> <span class="kw">display</span><span class="ch">:</span> <span class="dv">flex</span><span class="op">;</span></span>
|
||||
<span id="cb7-29"><a href="#cb7-29" tabindex="-1"></a> <span class="kw">place-items</span><span class="ch">:</span> <span class="dv">center</span><span class="op">;</span></span>
|
||||
<span id="cb7-30"><a href="#cb7-30" tabindex="-1"></a> <span class="kw">min-width</span><span class="ch">:</span> <span class="dv">320</span><span class="dt">px</span><span class="op">;</span></span>
|
||||
<span id="cb7-31"><a href="#cb7-31" tabindex="-1"></a> <span class="kw">min-height</span><span class="ch">:</span> <span class="dv">100</span><span class="dt">vh</span><span class="op">;</span></span>
|
||||
<span id="cb7-32"><a href="#cb7-32" tabindex="-1"></a>}</span>
|
||||
<span id="cb7-33"><a href="#cb7-33" tabindex="-1"></a></span>
|
||||
<span id="cb7-34"><a href="#cb7-34" tabindex="-1"></a>h1 {</span>
|
||||
<span id="cb7-35"><a href="#cb7-35" tabindex="-1"></a> <span class="kw">font-size</span><span class="ch">:</span> <span class="dv">3.2</span><span class="dt">em</span><span class="op">;</span></span>
|
||||
<span id="cb7-36"><a href="#cb7-36" tabindex="-1"></a> <span class="kw">line-height</span><span class="ch">:</span> <span class="dv">1.1</span><span class="op">;</span></span>
|
||||
<span id="cb7-37"><a href="#cb7-37" tabindex="-1"></a>}</span>
|
||||
<span id="cb7-38"><a href="#cb7-38" tabindex="-1"></a></span>
|
||||
<span id="cb7-39"><a href="#cb7-39" tabindex="-1"></a><span class="pp">#app</span> {</span>
|
||||
<span id="cb7-40"><a href="#cb7-40" tabindex="-1"></a> <span class="kw">max-width</span><span class="ch">:</span> <span class="dv">1280</span><span class="dt">px</span><span class="op">;</span></span>
|
||||
<span id="cb7-41"><a href="#cb7-41" tabindex="-1"></a> <span class="kw">margin</span><span class="ch">:</span> <span class="dv">0</span> <span class="bu">auto</span><span class="op">;</span></span>
|
||||
<span id="cb7-42"><a href="#cb7-42" tabindex="-1"></a> <span class="kw">padding</span><span class="ch">:</span> <span class="dv">2</span><span class="dt">rem</span><span class="op">;</span></span>
|
||||
<span id="cb7-43"><a href="#cb7-43" tabindex="-1"></a> <span class="kw">text-align</span><span class="ch">:</span> <span class="dv">center</span><span class="op">;</span></span>
|
||||
<span id="cb7-44"><a href="#cb7-44" tabindex="-1"></a>}</span>
|
||||
<span id="cb7-45"><a href="#cb7-45" tabindex="-1"></a></span>
|
||||
<span id="cb7-46"><a href="#cb7-46" tabindex="-1"></a><span class="fu">.logo</span> {</span>
|
||||
<span id="cb7-47"><a href="#cb7-47" tabindex="-1"></a> <span class="kw">height</span><span class="ch">:</span> <span class="dv">6</span><span class="dt">em</span><span class="op">;</span></span>
|
||||
<span id="cb7-48"><a href="#cb7-48" tabindex="-1"></a> <span class="kw">padding</span><span class="ch">:</span> <span class="dv">1.5</span><span class="dt">em</span><span class="op">;</span></span>
|
||||
<span id="cb7-49"><a href="#cb7-49" tabindex="-1"></a> <span class="kw">will-change</span><span class="ch">:</span> <span class="dv">filter</span><span class="op">;</span></span>
|
||||
<span id="cb7-50"><a href="#cb7-50" tabindex="-1"></a> <span class="kw">transition</span><span class="ch">:</span> <span class="dv">filter 300</span><span class="dt">ms</span><span class="op">;</span></span>
|
||||
<span id="cb7-51"><a href="#cb7-51" tabindex="-1"></a>}</span>
|
||||
<span id="cb7-52"><a href="#cb7-52" tabindex="-1"></a><span class="fu">.logo</span><span class="in">:hover</span> {</span>
|
||||
<span id="cb7-53"><a href="#cb7-53" tabindex="-1"></a> <span class="kw">filter</span><span class="ch">:</span> <span class="fu">drop-shadow(</span><span class="dv">0</span> <span class="dv">0</span> <span class="dv">2</span><span class="dt">em</span> <span class="cn">#646cffaa</span><span class="fu">)</span><span class="op">;</span></span>
|
||||
<span id="cb7-54"><a href="#cb7-54" tabindex="-1"></a>}</span>
|
||||
<span id="cb7-55"><a href="#cb7-55" tabindex="-1"></a><span class="fu">.logo.vanilla</span><span class="in">:hover</span> {</span>
|
||||
<span id="cb7-56"><a href="#cb7-56" tabindex="-1"></a> <span class="kw">filter</span><span class="ch">:</span> <span class="fu">drop-shadow(</span><span class="dv">0</span> <span class="dv">0</span> <span class="dv">2</span><span class="dt">em</span> <span class="cn">#3178c6aa</span><span class="fu">)</span><span class="op">;</span></span>
|
||||
<span id="cb7-57"><a href="#cb7-57" tabindex="-1"></a>}</span>
|
||||
<span id="cb7-58"><a href="#cb7-58" tabindex="-1"></a></span>
|
||||
<span id="cb7-59"><a href="#cb7-59" tabindex="-1"></a><span class="fu">.card</span> {</span>
|
||||
<span id="cb7-60"><a href="#cb7-60" tabindex="-1"></a> <span class="kw">padding</span><span class="ch">:</span> <span class="dv">2</span><span class="dt">em</span><span class="op">;</span></span>
|
||||
<span id="cb7-61"><a href="#cb7-61" tabindex="-1"></a>}</span>
|
||||
<span id="cb7-62"><a href="#cb7-62" tabindex="-1"></a></span>
|
||||
<span id="cb7-63"><a href="#cb7-63" tabindex="-1"></a><span class="fu">.read-the-docs</span> {</span>
|
||||
<span id="cb7-64"><a href="#cb7-64" tabindex="-1"></a> <span class="kw">color</span><span class="ch">:</span> <span class="cn">#888</span><span class="op">;</span></span>
|
||||
<span id="cb7-65"><a href="#cb7-65" tabindex="-1"></a>}</span>
|
||||
<span id="cb7-66"><a href="#cb7-66" tabindex="-1"></a></span>
|
||||
<span id="cb7-67"><a href="#cb7-67" tabindex="-1"></a>button {</span>
|
||||
<span id="cb7-68"><a href="#cb7-68" tabindex="-1"></a> <span class="kw">border-radius</span><span class="ch">:</span> <span class="dv">8</span><span class="dt">px</span><span class="op">;</span></span>
|
||||
<span id="cb7-69"><a href="#cb7-69" tabindex="-1"></a> <span class="kw">border</span><span class="ch">:</span> <span class="dv">1</span><span class="dt">px</span> <span class="dv">solid</span> <span class="dv">transparent</span><span class="op">;</span></span>
|
||||
<span id="cb7-70"><a href="#cb7-70" tabindex="-1"></a> <span class="kw">padding</span><span class="ch">:</span> <span class="dv">0.6</span><span class="dt">em</span> <span class="dv">1.2</span><span class="dt">em</span><span class="op">;</span></span>
|
||||
<span id="cb7-71"><a href="#cb7-71" tabindex="-1"></a> <span class="kw">font-size</span><span class="ch">:</span> <span class="dv">1</span><span class="dt">em</span><span class="op">;</span></span>
|
||||
<span id="cb7-72"><a href="#cb7-72" tabindex="-1"></a> <span class="kw">font-weight</span><span class="ch">:</span> <span class="dv">500</span><span class="op">;</span></span>
|
||||
<span id="cb7-73"><a href="#cb7-73" tabindex="-1"></a> <span class="kw">font-family</span><span class="ch">:</span> <span class="bu">inherit</span><span class="op">;</span></span>
|
||||
<span id="cb7-74"><a href="#cb7-74" tabindex="-1"></a> <span class="kw">background-color</span><span class="ch">:</span> <span class="cn">#1a1a1a</span><span class="op">;</span></span>
|
||||
<span id="cb7-75"><a href="#cb7-75" tabindex="-1"></a> <span class="kw">cursor</span><span class="ch">:</span> <span class="dv">pointer</span><span class="op">;</span></span>
|
||||
<span id="cb7-76"><a href="#cb7-76" tabindex="-1"></a> <span class="kw">transition</span><span class="ch">:</span> <span class="dv">border-color 0.25</span><span class="dt">s</span><span class="op">;</span></span>
|
||||
<span id="cb7-77"><a href="#cb7-77" tabindex="-1"></a>}</span>
|
||||
<span id="cb7-78"><a href="#cb7-78" tabindex="-1"></a>button<span class="in">:hover</span> {</span>
|
||||
<span id="cb7-79"><a href="#cb7-79" tabindex="-1"></a> <span class="kw">border-color</span><span class="ch">:</span> <span class="cn">#646cff</span><span class="op">;</span></span>
|
||||
<span id="cb7-80"><a href="#cb7-80" tabindex="-1"></a>}</span>
|
||||
<span id="cb7-81"><a href="#cb7-81" tabindex="-1"></a>button<span class="in">:focus</span><span class="op">,</span></span>
|
||||
<span id="cb7-82"><a href="#cb7-82" tabindex="-1"></a>button<span class="in">:focus-visible</span> {</span>
|
||||
<span id="cb7-83"><a href="#cb7-83" tabindex="-1"></a> <span class="kw">outline</span><span class="ch">:</span> <span class="dv">4</span><span class="dt">px</span> <span class="bu">auto</span> <span class="dv">-webkit-focus-ring-color</span><span class="op">;</span></span>
|
||||
<span id="cb7-84"><a href="#cb7-84" tabindex="-1"></a>}</span>
|
||||
<span id="cb7-85"><a href="#cb7-85" tabindex="-1"></a></span>
|
||||
<span id="cb7-86"><a href="#cb7-86" tabindex="-1"></a>input {</span>
|
||||
<span id="cb7-87"><a href="#cb7-87" tabindex="-1"></a> <span class="kw">border-radius</span><span class="ch">:</span> <span class="dv">8</span><span class="dt">px</span><span class="op">;</span></span>
|
||||
<span id="cb7-88"><a href="#cb7-88" tabindex="-1"></a> <span class="kw">border</span><span class="ch">:</span> <span class="dv">1</span><span class="dt">px</span> <span class="dv">solid</span> <span class="dv">transparent</span><span class="op">;</span></span>
|
||||
<span id="cb7-89"><a href="#cb7-89" tabindex="-1"></a> <span class="kw">padding</span><span class="ch">:</span> <span class="dv">0.6</span><span class="dt">em</span> <span class="dv">1.2</span><span class="dt">em</span><span class="op">;</span></span>
|
||||
<span id="cb7-90"><a href="#cb7-90" tabindex="-1"></a> <span class="kw">font-size</span><span class="ch">:</span> <span class="dv">1</span><span class="dt">em</span><span class="op">;</span></span>
|
||||
<span id="cb7-91"><a href="#cb7-91" tabindex="-1"></a> <span class="kw">font-weight</span><span class="ch">:</span> <span class="dv">500</span><span class="op">;</span></span>
|
||||
<span id="cb7-92"><a href="#cb7-92" tabindex="-1"></a> <span class="kw">font-family</span><span class="ch">:</span> <span class="bu">inherit</span><span class="op">;</span></span>
|
||||
<span id="cb7-93"><a href="#cb7-93" tabindex="-1"></a> <span class="kw">background-color</span><span class="ch">:</span> <span class="cn">#1a1a1a</span><span class="op">;</span></span>
|
||||
<span id="cb7-94"><a href="#cb7-94" tabindex="-1"></a> <span class="kw">color</span><span class="ch">:</span> <span class="cn">#fff</span><span class="op">;</span></span>
|
||||
<span id="cb7-95"><a href="#cb7-95" tabindex="-1"></a> <span class="kw">outline</span><span class="ch">:</span> <span class="dv">none</span><span class="op">;</span></span>
|
||||
<span id="cb7-96"><a href="#cb7-96" tabindex="-1"></a>}</span>
|
||||
<span id="cb7-97"><a href="#cb7-97" tabindex="-1"></a>input<span class="in">:hover</span> {</span>
|
||||
<span id="cb7-98"><a href="#cb7-98" tabindex="-1"></a> <span class="kw">border-color</span><span class="ch">:</span> <span class="cn">#646cff</span><span class="op">;</span></span>
|
||||
<span id="cb7-99"><a href="#cb7-99" tabindex="-1"></a>}</span>
|
||||
<span id="cb7-100"><a href="#cb7-100" tabindex="-1"></a>input<span class="in">:focus</span><span class="op">,</span></span>
|
||||
<span id="cb7-101"><a href="#cb7-101" tabindex="-1"></a>input<span class="in">:focus-visible</span> {</span>
|
||||
<span id="cb7-102"><a href="#cb7-102" tabindex="-1"></a> <span class="kw">outline</span><span class="ch">:</span> <span class="dv">4</span><span class="dt">px</span> <span class="bu">auto</span> <span class="dv">-webkit-focus-ring-color</span><span class="op">;</span></span>
|
||||
<span id="cb7-103"><a href="#cb7-103" tabindex="-1"></a>}</span>
|
||||
<span id="cb7-104"><a href="#cb7-104" tabindex="-1"></a></span>
|
||||
<span id="cb7-105"><a href="#cb7-105" tabindex="-1"></a><span class="im">@media</span> <span class="fu">(</span><span class="kw">prefers-color-scheme</span><span class="ch">:</span> <span class="dv">light</span><span class="fu">)</span> {</span>
|
||||
<span id="cb7-106"><a href="#cb7-106" tabindex="-1"></a> <span class="in">:root</span> {</span>
|
||||
<span id="cb7-107"><a href="#cb7-107" tabindex="-1"></a> <span class="kw">color</span><span class="ch">:</span> <span class="cn">#213547</span><span class="op">;</span></span>
|
||||
<span id="cb7-108"><a href="#cb7-108" tabindex="-1"></a> <span class="kw">background-color</span><span class="ch">:</span> <span class="cn">#ffffff</span><span class="op">;</span></span>
|
||||
<span id="cb7-109"><a href="#cb7-109" tabindex="-1"></a> }</span>
|
||||
<span id="cb7-110"><a href="#cb7-110" tabindex="-1"></a> a<span class="in">:hover</span> {</span>
|
||||
<span id="cb7-111"><a href="#cb7-111" tabindex="-1"></a> <span class="kw">color</span><span class="ch">:</span> <span class="cn">#747bff</span><span class="op">;</span></span>
|
||||
<span id="cb7-112"><a href="#cb7-112" tabindex="-1"></a> }</span>
|
||||
<span id="cb7-113"><a href="#cb7-113" tabindex="-1"></a> button {</span>
|
||||
<span id="cb7-114"><a href="#cb7-114" tabindex="-1"></a> <span class="kw">background-color</span><span class="ch">:</span> <span class="cn">#f9f9f9</span><span class="op">;</span></span>
|
||||
<span id="cb7-115"><a href="#cb7-115" tabindex="-1"></a> }</span>
|
||||
<span id="cb7-116"><a href="#cb7-116" tabindex="-1"></a> input {</span>
|
||||
<span id="cb7-117"><a href="#cb7-117" tabindex="-1"></a> <span class="kw">background-color</span><span class="ch">:</span> <span class="cn">#f9f9f9</span><span class="op">;</span></span>
|
||||
<span id="cb7-118"><a href="#cb7-118" tabindex="-1"></a> <span class="kw">color</span><span class="ch">:</span> <span class="cn">#000</span><span class="op">;</span></span>
|
||||
<span id="cb7-119"><a href="#cb7-119" tabindex="-1"></a> }</span>
|
||||
<span id="cb7-120"><a href="#cb7-120" tabindex="-1"></a>}</span></code></pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section level2">
|
||||
<h2 id="run-the-app">Run the app<a class="anchor" aria-label="anchor" href="#run-the-app"></a>
|
||||
</h2>
|
||||
<p>To run the app, start the Rserve server:</p>
|
||||
<div class="sourceCode" id="cb8"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb8-1"><a href="#cb8-1" tabindex="-1"></a><span class="ex">Rscript</span> src/faithful-app.rserve.R</span></code></pre></div>
|
||||
<p>Then start the Vite server:</p>
|
||||
<div class="sourceCode" id="cb9"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb9-1"><a href="#cb9-1" tabindex="-1"></a><span class="ex">pnpm</span> run dev</span></code></pre></div>
|
||||
<p>You should now be able to see the app running at
|
||||
<code>http://localhost:5173</code> (or similar, see the console
|
||||
output).</p>
|
||||
</div>
|
||||
</main><aside class="col-md-3"><nav id="toc" aria-label="Table of contents"><h2>On this page</h2>
|
||||
</nav></aside>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<footer><div class="pkgdown-footer-left">
|
||||
<p>Developed by Tom Elliott.</p>
|
||||
</div>
|
||||
|
||||
<div class="pkgdown-footer-right">
|
||||
<p>Site built with <a href="https://pkgdown.r-lib.org/" class="external-link">pkgdown</a> 2.1.1.</p>
|
||||
</div>
|
||||
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
82
authors.html
82
authors.html
@ -1,82 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<!-- Generated by pkgdown: do not edit by hand --><html lang="en"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"><title>Authors and Citation • ts</title><script src="deps/jquery-3.6.0/jquery-3.6.0.min.js"></script><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"><link href="deps/bootstrap-5.3.1/bootstrap.min.css" rel="stylesheet"><script src="deps/bootstrap-5.3.1/bootstrap.bundle.min.js"></script><link href="deps/font-awesome-6.5.2/css/all.min.css" rel="stylesheet"><link href="deps/font-awesome-6.5.2/css/v4-shims.min.css" rel="stylesheet"><script src="deps/headroom-0.11.0/headroom.min.js"></script><script src="deps/headroom-0.11.0/jQuery.headroom.min.js"></script><script src="deps/bootstrap-toc-1.0.1/bootstrap-toc.min.js"></script><script src="deps/clipboard.js-2.0.11/clipboard.min.js"></script><script src="deps/search-1.0.0/autocomplete.jquery.min.js"></script><script src="deps/search-1.0.0/fuse.min.js"></script><script src="deps/search-1.0.0/mark.min.js"></script><!-- pkgdown --><script src="pkgdown.js"></script><meta property="og:title" content="Authors and Citation"></head><body>
|
||||
<a href="#main" class="visually-hidden-focusable">Skip to contents</a>
|
||||
|
||||
|
||||
<nav class="navbar navbar-expand-lg fixed-top bg-light" data-bs-theme="light" aria-label="Site navigation"><div class="container">
|
||||
|
||||
<a class="navbar-brand me-2" href="index.html">ts</a>
|
||||
|
||||
<small class="nav-text text-muted me-auto" data-bs-toggle="tooltip" data-bs-placement="bottom" title="">0.1.0</small>
|
||||
|
||||
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
<div id="navbar" class="collapse navbar-collapse ms-3">
|
||||
<ul class="navbar-nav me-auto"><li class="nav-item"><a class="nav-link" href="reference/index.html">Reference</a></li>
|
||||
<li class="nav-item dropdown">
|
||||
<button class="nav-link dropdown-toggle" type="button" id="dropdown-articles" data-bs-toggle="dropdown" aria-expanded="false" aria-haspopup="true">Articles</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="dropdown-articles"><li><a class="dropdown-item" href="articles/simple-react-app.html">Build a simple ReactJS app</a></li>
|
||||
</ul></li>
|
||||
<li class="nav-item"><a class="nav-link" href="news/index.html">Changelog</a></li>
|
||||
</ul><ul class="navbar-nav"><li class="nav-item"><form class="form-inline" role="search">
|
||||
<input class="form-control" type="search" name="search-input" id="search-input" autocomplete="off" aria-label="Search site" placeholder="Search for" data-search-index="search.json"></form></li>
|
||||
</ul></div>
|
||||
|
||||
|
||||
</div>
|
||||
</nav><div class="container template-citation-authors">
|
||||
<div class="row">
|
||||
<main id="main" class="col-md-9"><div class="page-header">
|
||||
|
||||
<h1>Authors and Citation</h1>
|
||||
</div>
|
||||
|
||||
<div class="section level2">
|
||||
<h2>Authors</h2>
|
||||
|
||||
<ul class="list-unstyled"><li>
|
||||
<p><strong>Tom Elliott</strong>. Author, maintainer. <a href="https://orcid.org/0000-0002-7815-6318" target="orcid.widget" aria-label="ORCID" class="external-link"><span class="fab fa-orcid orcid" aria-hidden="true"></span></a>
|
||||
<br><small>Supported by the Ngā Puanga Pūtaiao Fellowships from Government funding, administered by the Royal Society Te Apārangi, New Zealand.</small></p>
|
||||
</li>
|
||||
</ul></div>
|
||||
|
||||
<div class="section level2">
|
||||
<h2 id="citation">Citation</h2>
|
||||
<p></p>
|
||||
|
||||
<p>Elliott T (2025).
|
||||
<em>ts: Helper Functions for Writing Type-safe Functions for rserve-ts</em>.
|
||||
R package version 0.1.0, <a href="http://tomelliott.co.nz/ts/">http://tomelliott.co.nz/ts/</a>.
|
||||
</p>
|
||||
<pre>@Manual{,
|
||||
title = {ts: Helper Functions for Writing Type-safe Functions for rserve-ts},
|
||||
author = {Tom Elliott},
|
||||
year = {2025},
|
||||
note = {R package version 0.1.0},
|
||||
url = {http://tomelliott.co.nz/ts/},
|
||||
}</pre>
|
||||
</div>
|
||||
|
||||
</main><aside class="col-md-3"><nav id="toc" aria-label="Table of contents"><h2>On this page</h2>
|
||||
</nav></aside></div>
|
||||
|
||||
|
||||
<footer><div class="pkgdown-footer-left">
|
||||
<p>Developed by Tom Elliott.</p>
|
||||
</div>
|
||||
|
||||
<div class="pkgdown-footer-right">
|
||||
<p>Site built with <a href="https://pkgdown.r-lib.org/" class="external-link">pkgdown</a> 2.1.1.</p>
|
||||
</div>
|
||||
|
||||
</footer></div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</body></html>
|
||||
|
||||
7
deps/bootstrap-5.3.1/bootstrap.bundle.min.js
vendored
7
deps/bootstrap-5.3.1/bootstrap.bundle.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
5
deps/bootstrap-5.3.1/bootstrap.min.css
vendored
5
deps/bootstrap-5.3.1/bootstrap.min.css
vendored
File diff suppressed because one or more lines are too long
200
deps/bootstrap-5.3.1/font.css
vendored
200
deps/bootstrap-5.3.1/font.css
vendored
@ -1,200 +0,0 @@
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(fonts/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSKmu1aB.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(fonts/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSumu1aB.woff2) format('woff2');
|
||||
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(fonts/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSOmu1aB.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(fonts/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSymu1aB.woff2) format('woff2');
|
||||
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
|
||||
}
|
||||
/* hebrew */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(fonts/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTS2mu1aB.woff2) format('woff2');
|
||||
unicode-range: U+0307-0308, U+0590-05FF, U+200C-2010, U+20AA, U+25CC, U+FB1D-FB4F;
|
||||
}
|
||||
/* math */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(fonts/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTVOmu1aB.woff2) format('woff2');
|
||||
unicode-range: U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF;
|
||||
}
|
||||
/* symbols */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(fonts/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTUGmu1aB.woff2) format('woff2');
|
||||
unicode-range: U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, U+1FA8F-1FAC6, U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(fonts/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSCmu1aB.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(fonts/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSGmu1aB.woff2) format('woff2');
|
||||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(fonts/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTS-muw.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(fonts/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSKmu1aB.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(fonts/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSumu1aB.woff2) format('woff2');
|
||||
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(fonts/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSOmu1aB.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(fonts/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSymu1aB.woff2) format('woff2');
|
||||
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
|
||||
}
|
||||
/* hebrew */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(fonts/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTS2mu1aB.woff2) format('woff2');
|
||||
unicode-range: U+0307-0308, U+0590-05FF, U+200C-2010, U+20AA, U+25CC, U+FB1D-FB4F;
|
||||
}
|
||||
/* math */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(fonts/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTVOmu1aB.woff2) format('woff2');
|
||||
unicode-range: U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF;
|
||||
}
|
||||
/* symbols */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(fonts/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTUGmu1aB.woff2) format('woff2');
|
||||
unicode-range: U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, U+1FA8F-1FAC6, U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(fonts/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSCmu1aB.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(fonts/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSGmu1aB.woff2) format('woff2');
|
||||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-stretch: 100%;
|
||||
font-display: swap;
|
||||
src: url(fonts/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTS-muw.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user