Compare commits

..

6 Commits

Author SHA1 Message Date
Anton Franzluebbers 885b27bfe1 persistence for any velnet component 2024-02-02 14:43:41 -05:00
Anton Franzluebbers 73c2ccea0a Merge branch 'main' into dev 2024-02-01 15:14:46 -05:00
Anton Franzluebbers 3e00fc6197 more example work 2024-01-19 23:03:14 -05:00
Anton Franzluebbers e717ed915f minified velconnect for browsers, more examples 2024-01-19 22:33:57 -05:00
Anton Franzluebbers 63ed7e264c Merge branch 'main' of anton.github.com:velaboratory/VEL-Connect 2024-01-19 19:42:44 -05:00
Anton Franzluebbers 00227e9b79 generated types, lots of simplification 2024-01-19 19:42:37 -05:00
63 changed files with 16554 additions and 933 deletions

1
.github/my.secrets vendored Normal file
View File

@ -0,0 +1 @@
NPM_TOKEN="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"

View File

@ -6,7 +6,7 @@ on:
paths: ["velconnect/**"] paths: ["velconnect/**"]
jobs: jobs:
run_pull: run_pull:
name: Pull new version name: Pull new version on Oracle
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: install ssh keys - name: install ssh keys

36
.github/workflows/publish_minjs.yml vendored Normal file
View File

@ -0,0 +1,36 @@
name: Release Workflow
on:
push:
tags:
- v*.*.*
jobs:
release:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: 18
- name: Install dependencies
run: npm install
- name: Build and minify
run: |
npm run build
npm run minify
- name: Release
uses: softprops/action-gh-release@v1
with:
draft: true
files: |
dist/velconnect.min.js
env:
GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }}

View File

@ -0,0 +1,25 @@
name: Publish Package to npmjs
on:
push:
branches: ["main"]
paths: ["velconnect-svelte-npm/**"]
defaults:
run:
working-directory: velconnect-svelte-npm
jobs:
pub_npmjs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
# Setup .npmrc file to publish to npm
- uses: actions/setup-node@v3
with:
node-version: "18"
registry-url: "https://registry.npmjs.org"
- run: npm install
- run: npm ci
- run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

25
.github/workflows/publish_ts_npm.yml vendored Normal file
View File

@ -0,0 +1,25 @@
name: Publish Package to npmjs
on:
push:
branches: ["main"]
paths: ["velconnect-npm/**"]
defaults:
run:
working-directory: velconnect-npm
jobs:
pub_npmjs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
# Setup .npmrc file to publish to npm
- uses: actions/setup-node@v3
with:
node-version: "18"
registry-url: "https://registry.npmjs.org"
- run: npm install
- run: npm ci
- run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

File diff suppressed because it is too large Load Diff

View File

@ -12,26 +12,29 @@
"format": "prettier --plugin-search-dir . --write ." "format": "prettier --plugin-search-dir . --write ."
}, },
"devDependencies": { "devDependencies": {
"@sveltejs/adapter-cloudflare": "^2.3.1", "@sveltejs/adapter-cloudflare": "^4.1.0",
"@sveltejs/kit": "^1.20.4", "@sveltejs/kit": "^2.4.1",
"@typescript-eslint/eslint-plugin": "^5.45.0", "@typescript-eslint/eslint-plugin": "^6.19.0",
"@typescript-eslint/parser": "^5.45.0", "@typescript-eslint/parser": "^6.19.0",
"eslint": "^8.28.0", "eslint": "^8.56.0",
"eslint-config-prettier": "^8.5.0", "eslint-config-prettier": "^9.1.0",
"eslint-plugin-svelte": "^2.30.0", "eslint-plugin-svelte": "^2.35.1",
"humanize-duration": "^3.29.0", "@sveltejs/vite-plugin-svelte": "^3.0.1",
"luxon": "^3.3.0", "humanize-duration": "^3.31.0",
"prettier": "^2.8.0", "luxon": "^3.4.4",
"prettier-plugin-svelte": "^2.10.1", "prettier": "^3.2.4",
"sass": "^1.63.6", "prettier-plugin-svelte": "^3.1.2",
"svelte": "^4.0.0", "sass": "^1.70.0",
"svelte-check": "^3.4.3", "svelte": "^4.2.9",
"svelte-preprocess": "^5.0.4", "svelte-check": "^3.6.3",
"tslib": "^2.4.1", "svelte-preprocess": "^5.1.3",
"typescript": "^5.0.0", "tslib": "^2.6.2",
"vite": "^4.3.6", "typescript": "^5.3.3",
"pocketbase": "^0.15.3", "vite": "^5.0.12",
"@velaboratory/velconnect-svelte": "^1.0.3" "pocketbase": "^0.20.3"
}, },
"type": "module" "type": "module",
"dependencies": {
"@velaboratory/velconnect": "../../VEL-Connect/velconnect-npm"
}
} }

View File

@ -1,11 +1,11 @@
import { vitePreprocess } from '@sveltejs/kit/vite'; import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
import adapter from '@sveltejs/adapter-cloudflare'; import adapter from '@sveltejs/adapter-cloudflare';
/** @type {import('@sveltejs/kit').Config} */ /** @type {import('@sveltejs/kit').Config} */
const config = { const config = {
// Consult https://kit.svelte.dev/docs/integrations#preprocessors // Consult https://kit.svelte.dev/docs/integrations#preprocessors
// for more information about preprocessors // for more information about preprocessors
preprocess: [vitePreprocess({})], preprocess: [vitePreprocess()],
kit: { kit: {
// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list. // adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.

21
example_dashboard_astro/.gitignore vendored Normal file
View File

@ -0,0 +1,21 @@
# build output
dist/
# generated types
.astro/
# dependencies
node_modules/
# logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# environment variables
.env
.env.production
# macOS-specific files
.DS_Store

View File

@ -0,0 +1,4 @@
{
"recommendations": ["astro-build.astro-vscode"],
"unwantedRecommendations": []
}

View File

@ -0,0 +1,11 @@
{
"version": "0.2.0",
"configurations": [
{
"command": "./node_modules/.bin/astro dev",
"name": "Development server",
"request": "launch",
"type": "node-terminal"
}
]
}

View File

@ -0,0 +1,54 @@
# Astro Starter Kit: Basics
```sh
npm create astro@latest -- --template basics
```
[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/withastro/astro/tree/latest/examples/basics)
[![Open with CodeSandbox](https://assets.codesandbox.io/github/button-edit-lime.svg)](https://codesandbox.io/p/sandbox/github/withastro/astro/tree/latest/examples/basics)
[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/withastro/astro?devcontainer_path=.devcontainer/basics/devcontainer.json)
> 🧑‍🚀 **Seasoned astronaut?** Delete this file. Have fun!
![just-the-basics](https://github.com/withastro/astro/assets/2244813/a0a5533c-a856-4198-8470-2d67b1d7c554)
## 🚀 Project Structure
Inside of your Astro project, you'll see the following folders and files:
```text
/
├── public/
│ └── favicon.svg
├── src/
│ ├── components/
│ │ └── Card.astro
│ ├── layouts/
│ │ └── Layout.astro
│ └── pages/
│ └── index.astro
└── package.json
```
Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page is exposed as a route based on its file name.
There's nothing special about `src/components/`, but that's where we like to put any Astro/React/Vue/Svelte/Preact components.
Any static assets, like images, can be placed in the `public/` directory.
## 🧞 Commands
All commands are run from the root of the project, from a terminal:
| Command | Action |
| :------------------------ | :----------------------------------------------- |
| `npm install` | Installs dependencies |
| `npm run dev` | Starts local dev server at `localhost:4321` |
| `npm run build` | Build your production site to `./dist/` |
| `npm run preview` | Preview your build locally, before deploying |
| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` |
| `npm run astro -- --help` | Get help using the Astro CLI |
## 👀 Want to learn more?
Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat).

View File

@ -0,0 +1,6 @@
import { defineConfig } from 'astro/config';
// https://astro.build/config
export default defineConfig({
integrations: []
});

6610
example_dashboard_astro/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,19 @@
{
"name": "example_dashboard_astro",
"type": "module",
"version": "0.0.1",
"scripts": {
"dev": "astro dev",
"start": "astro dev",
"build": "astro check && astro build",
"preview": "astro preview",
"astro": "astro"
},
"dependencies": {
"@astrojs/check": "^0.4.1",
"@velaboratory/velconnect": "../../VEL-Connect/velconnect-npm",
"astro": "^4.2.1",
"simpledotcss": "^2.2.1",
"typescript": "^5.3.3"
}
}

View File

@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 128 128">
<path d="M50.4 78.5a75.1 75.1 0 0 0-28.5 6.9l24.2-65.7c.7-2 1.9-3.2 3.4-3.2h29c1.5 0 2.7 1.2 3.4 3.2l24.2 65.7s-11.6-7-28.5-7L67 45.5c-.4-1.7-1.6-2.8-2.9-2.8-1.3 0-2.5 1.1-2.9 2.7L50.4 78.5Zm-1.1 28.2Zm-4.2-20.2c-2 6.6-.6 15.8 4.2 20.2a17.5 17.5 0 0 1 .2-.7 5.5 5.5 0 0 1 5.7-4.5c2.8.1 4.3 1.5 4.7 4.7.2 1.1.2 2.3.2 3.5v.4c0 2.7.7 5.2 2.2 7.4a13 13 0 0 0 5.7 4.9v-.3l-.2-.3c-1.8-5.6-.5-9.5 4.4-12.8l1.5-1a73 73 0 0 0 3.2-2.2 16 16 0 0 0 6.8-11.4c.3-2 .1-4-.6-6l-.8.6-1.6 1a37 37 0 0 1-22.4 2.7c-5-.7-9.7-2-13.2-6.2Z" />
<style>
path { fill: #000; }
@media (prefers-color-scheme: dark) {
path { fill: #FFF; }
}
</style>
</svg>

After

Width:  |  Height:  |  Size: 749 B

View File

@ -0,0 +1,3 @@
:root {
--accent: #ee2222;
}

View File

@ -0,0 +1,22 @@
---
interface Props {
title: string;
body: string;
href: string;
}
const { href, title, body } = Astro.props;
---
<li class="link-card">
<a href={href}>
<h2>
{title}
<span>&rarr;</span>
</h2>
<p>
{body}
</p>
</a>
</li>
<style></style>

1
example_dashboard_astro/src/env.d.ts vendored Normal file
View File

@ -0,0 +1 @@
/// <reference types="astro/client" />

View File

@ -0,0 +1,26 @@
---
import "simpledotcss/simple.min.css";
import "../app.css";
interface Props {
title: string;
}
const { title } = Astro.props;
---
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="description" content="Astro description" />
<meta name="viewport" content="width=device-width" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="generator" content={Astro.generator} />
<title>{title}</title>
</head>
<body>
<slot />
</body>
</html>

View File

@ -0,0 +1,32 @@
---
import Layout from "../layouts/Layout.astro";
import Card from "../components/Card.astro";
---
<Layout title="VEL-Connect">
<main>
<h1><span class="text-gradient">VEL</span>-Connect</h1>
<div>
<form>
<label>Username<input id="username" /></label>
<label>Password<input id="password" type="password" /></label>
<button>Log in</button>
</form>
<button>Log out</button>
</div>
<p></p>
<h4>Devices</h4>
<ul></ul>
<h4>Profiles</h4>
<ul></ul>
</main>
</Layout>
<style>
.text-gradient {
background-image: linear-gradient(45deg, var(--accent), white 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
</style>

View File

@ -0,0 +1,3 @@
{
"extends": "astro/tsconfigs/strict"
}

View File

@ -0,0 +1,78 @@
<html>
<head>
<title>VEL-Connect</title>
<link rel="stylesheet" href="simple.min.css">
<link rel="stylesheet" href="style.css">
<script src="./velconnect.min.js"></script>
</head>
<body>
<br />
<h1><span class="text-gradient">VEL</span>-Connect</h1>
<div>
<form onsubmit="login(); return false;" id="loginForm">
<label>Username<input id="username" /></label>
<label>Password<input id="password" type="password" /></label>
<button type="submit">Log in</button>
</form>
<button id="logoutButton" style="display: none;" onclick="logout();">Log out</button>
</div>
<div id="content">
<h4>Devices</h4>
<ul id="devicesList"></ul>
<h4>Profiles</h4>
<ul id="profilesList"></ul>
</div>
<script>
let pb = window.velconnect.pb;
if (pb.authStore.isValid) {
loginForm.style.display = 'none';
logoutButton.style.display = 'block';
} else {
console.log("Not logged in")
}
pb.authStore.onChange(async () => {
if (pb.authStore.isValid) {
content.style.display = 'block';
loginForm.style.display = 'none';
logoutButton.style.display = 'block';
devicesList.innerHTML = '';
profilesList.innerHTML = '';
let user = await pb.collection('Users').getOne(pb.authStore.model.id, { expand: 'devices,profiles' })
for (let device of user.devices) {
let li = document.createElement('li');
li.innerHTML = device;
devicesList.appendChild(li);
}
for (let profile of user.profiles) {
let li = document.createElement('li');
li.innerHTML = profile;
profilesList.appendChild(li);
}
} else {
content.style.display = 'none';
loginForm.style.display = 'block';
logoutButton.style.display = 'none';
}
}, true);
async function login() {
await pb.collection('Users').authWithPassword(username.value, password.value);
}
function logout() {
pb.authStore.clear();
location.reload();
}
</script>
</body>
</html>

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,3 @@
:root {
--accent: #ee1111;
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,13 @@
.DS_Store
node_modules
/build
/.svelte-kit
/package
.env
.env.*
!.env.example
# Ignore files for PNPM, NPM and YARN
pnpm-lock.yaml
package-lock.json
yarn.lock

View File

@ -0,0 +1,31 @@
/** @type { import("eslint").Linter.Config } */
module.exports = {
root: true,
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:svelte/recommended',
'prettier'
],
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint'],
parserOptions: {
sourceType: 'module',
ecmaVersion: 2020,
extraFileExtensions: ['.svelte']
},
env: {
browser: true,
es2017: true,
node: true
},
overrides: [
{
files: ['*.svelte'],
parser: 'svelte-eslint-parser',
parserOptions: {
parser: '@typescript-eslint/parser'
}
}
]
};

12
example_dashboard_sveltekit/.gitignore vendored Normal file
View File

@ -0,0 +1,12 @@
.DS_Store
node_modules
/build
/.svelte-kit
/package
.env
.env.*
!.env.example
.vercel
.output
vite.config.js.timestamp-*
vite.config.ts.timestamp-*

View File

@ -0,0 +1 @@
engine-strict=true

View File

@ -0,0 +1,4 @@
# Ignore files for PNPM, NPM and YARN
pnpm-lock.yaml
package-lock.json
yarn.lock

View File

@ -0,0 +1,8 @@
{
"useTabs": true,
"singleQuote": true,
"trailingComma": "none",
"printWidth": 100,
"plugins": ["prettier-plugin-svelte"],
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
}

View File

@ -0,0 +1,38 @@
# create-svelte
Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/main/packages/create-svelte).
## Creating a project
If you're seeing this, you've probably already done this step. Congrats!
```bash
# create a new project in the current directory
npm create svelte@latest
# create a new project in my-app
npm create svelte@latest my-app
```
## Developing
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
```bash
npm run dev
# or start the server and open the app in a new browser tab
npm run dev -- --open
```
## Building
To create a production version of your app:
```bash
npm run build
```
You can preview the production build with `npm run preview`.
> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,40 @@
{
"name": "example-dashboard-sveltekit",
"version": "0.0.1",
"scripts": {
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"lint": "prettier --check . && eslint .",
"format": "prettier --write ."
},
"devDependencies": {
"@fontsource/fira-mono": "^4.5.10",
"@neoconfetti/svelte": "^1.0.0",
"@sveltejs/adapter-auto": "^3.0.0",
"@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^3.0.0",
"@types/eslint": "8.56.0",
"@typescript-eslint/eslint-plugin": "^6.0.0",
"@typescript-eslint/parser": "^6.0.0",
"autoprefixer": "^10.4.17",
"eslint": "^8.56.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-svelte": "^2.35.1",
"postcss": "^8.4.33",
"prettier": "^3.1.1",
"prettier-plugin-svelte": "^3.1.2",
"svelte": "^4.2.7",
"svelte-check": "^3.6.0",
"tailwindcss": "^3.4.1",
"tslib": "^2.4.1",
"typescript": "^5.0.0",
"vite": "^5.0.3"
},
"type": "module",
"dependencies": {
"@velaboratory/velconnect": "../velconnect-npm"
}
}

View File

@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {}
}
};

View File

@ -0,0 +1,13 @@
// See https://kit.svelte.dev/docs/types#app
// for information about these interfaces
declare global {
namespace App {
// interface Error {}
// interface Locals {}
// interface PageData {}
// interface PageState {}
// interface Platform {}
}
}
export {};

View File

@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover">
<div style="display: contents">%sveltekit.body%</div>
</body>
</html>

View File

@ -0,0 +1,16 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-3 -3 30 30">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M12 2C6.47715 2 2 6.47715 2 12C2 17.5229 6.47715 22 12 22C17.5229 22 22 17.5229 22 12C22 6.47715 17.5229 2 12 2ZM0 12C0 5.3726 5.3726 0 12 0C18.6274 0 24 5.3726 24 12C24 18.6274 18.6274 24 12 24C5.3726 24 0 18.6274 0 12Z"
fill="rgba(0,0,0,0.7)"
stroke="none"
/>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M9.59162 22.7357C9.49492 22.6109 9.49492 21.4986 9.59162 19.399C8.55572 19.4347 7.90122 19.3628 7.62812 19.1833C7.21852 18.9139 6.80842 18.0833 6.44457 17.4979C6.08072 16.9125 5.27312 16.8199 4.94702 16.6891C4.62091 16.5582 4.53905 16.0247 5.84562 16.4282C7.15222 16.8316 7.21592 17.9303 7.62812 18.1872C8.04032 18.4441 9.02572 18.3317 9.47242 18.1259C9.91907 17.9201 9.88622 17.1538 9.96587 16.8503C10.0666 16.5669 9.71162 16.5041 9.70382 16.5018C9.26777 16.5018 6.97697 16.0036 6.34772 13.7852C5.71852 11.5669 6.52907 10.117 6.96147 9.49369C7.24972 9.07814 7.22422 8.19254 6.88497 6.83679C8.11677 6.67939 9.06732 7.06709 9.73672 7.99999C9.73737 8.00534 10.6143 7.47854 12.0001 7.47854C13.386 7.47854 13.8777 7.90764 14.2571 7.99999C14.6365 8.09234 14.94 6.36699 17.2834 6.83679C16.7942 7.79839 16.3844 8.99999 16.6972 9.49369C17.0099 9.98739 18.2372 11.5573 17.4833 13.7852C16.9807 15.2706 15.9927 16.1761 14.5192 16.5018C14.3502 16.5557 14.2658 16.6427 14.2658 16.7627C14.2658 16.9427 14.4942 16.9624 14.8233 17.8058C15.0426 18.368 15.0585 19.9739 14.8708 22.6234C14.3953 22.7445 14.0254 22.8257 13.7611 22.8673C13.2924 22.9409 12.7835 22.9822 12.2834 22.9982C11.7834 23.0141 11.6098 23.0123 10.9185 22.948C10.4577 22.9051 10.0154 22.8343 9.59162 22.7357Z"
fill="rgba(0,0,0,0.7)"
stroke="none"
/>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -0,0 +1,16 @@
import { pb } from '@velaboratory/velconnect';
import type { UsersResponse } from '@velaboratory/velconnect/src/types/pocketbase-types';
import { writable } from 'svelte/store';
export const userModel = writable(pb.authStore.model);
export const userData = writable<UsersResponse | null>();
pb.authStore.onChange(async (token, model) => {
userModel.set(model);
if (model) {
const u = await pb.collection('Users').getOne(model.id, { expand: 'devices,profiles' });
console.log(u);
userData.set(u);
} else {
userData.set(null);
}
}, true);

View File

@ -0,0 +1,53 @@
<script>
import Header from './Header.svelte';
import './styles.css';
</script>
<div class="app">
<Header />
<main>
<slot />
</main>
<footer>
<p>visit <a href="https://kit.svelte.dev">kit.svelte.dev</a> to learn SvelteKit</p>
</footer>
</div>
<style>
.app {
display: flex;
flex-direction: column;
min-height: 100vh;
}
main {
flex: 1;
display: flex;
flex-direction: column;
padding: 1rem;
width: 100%;
max-width: 64rem;
margin: 0 auto;
box-sizing: border-box;
}
footer {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 12px;
}
footer a {
font-weight: bold;
}
@media (min-width: 480px) {
footer {
padding: 12px 0;
}
}
</style>

View File

@ -0,0 +1,85 @@
<script lang="ts">
import { pb } from '@velaboratory/velconnect';
import { userData, userModel } from '$lib/stores';
let username: string;
let password: string;
</script>
<svelte:head>
<title>VEL-Connect</title>
<meta name="description" content="VEL-Connect Example" />
</svelte:head>
<section>
<h1>VEL-Connect</h1>
<p></p>
<div>
{#if $userModel}
<button
on:click={() => {
pb.authStore.clear();
}}>Log out</button
>
{:else}
<form
on:submit|preventDefault={async () => {
await pb.collection('Users').authWithPassword(username, password);
}}
>
<label>Username<input bind:value={username} /></label>
<label>Password<input bind:value={password} type="password" /></label>
<button type="submit">Log in</button>
</form>
{/if}
</div>
{#if $userData}
<div id="content">
<h4>Devices</h4>
<table>
<tr>
<th>ID</th>
<th>Name</th>
<th>Room</th>
</tr>
{#each $userData.expand?.devices as device}
<tr>
<td>{device.id}</td>
<td>{device.friendly_name}</td>
<td>{device.current_room}</td>
</tr>
{/each}
</table>
<h4>Profiles</h4>
<table>
<tr>
<th>ID</th>
<th>Data</th>
</tr>
{#each $userData.expand?.profiles as profile}
<tr>
<td>{profile.id}</td>
<td><pre><code>{JSON.stringify(profile.data, null, 2)}</code></pre></td>
</tr>
{/each}
</table>
</div>
{/if}
</section>
<style>
section {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
flex: 0.6;
}
h1 {
width: 100%;
}
</style>

View File

@ -0,0 +1,3 @@
// since there's no dynamic data here, we can prerender
// it so that it gets served as a static asset in production
export const prerender = true;

View File

@ -0,0 +1,116 @@
<script>
import { page } from '$app/stores';
import github from '$lib/images/github.svg';
</script>
<header>
<nav>
<svg viewBox="0 0 2 3" aria-hidden="true">
<path d="M0,0 L1,2 C1.5,3 1.5,3 2,3 L2,0 Z" />
</svg>
<ul>
<li aria-current={$page.url.pathname === '/' ? 'page' : undefined}>
<a href="/">Home</a>
</li>
</ul>
<svg viewBox="0 0 2 3" aria-hidden="true">
<path d="M0,0 L0,3 C0.5,3 0.5,3 1,2 L2,0 Z" />
</svg>
</nav>
<div class="corner">
<a href="https://github.com/sveltejs/kit">
<img src={github} alt="GitHub" />
</a>
</div>
</header>
<style>
header {
display: flex;
justify-content: space-between;
}
.corner {
width: 3em;
height: 3em;
}
.corner a {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
}
.corner img {
width: 2em;
height: 2em;
object-fit: contain;
}
nav {
display: flex;
justify-content: center;
--background: rgba(255, 255, 255, 0.7);
}
svg {
width: 2em;
height: 3em;
display: block;
}
path {
fill: var(--background);
}
ul {
position: relative;
padding: 0;
margin: 0;
height: 3em;
display: flex;
justify-content: center;
align-items: center;
list-style: none;
background: var(--background);
background-size: contain;
}
li {
position: relative;
height: 100%;
}
li[aria-current='page']::before {
--size: 6px;
content: '';
width: 0;
height: 0;
position: absolute;
top: 0;
left: calc(50% - var(--size));
border: var(--size) solid transparent;
border-top: var(--size) solid var(--color-theme-1);
}
nav a {
display: flex;
height: 100%;
align-items: center;
padding: 0 0.5rem;
color: var(--color-text);
font-weight: 700;
font-size: 0.8rem;
text-transform: uppercase;
letter-spacing: 0.1em;
text-decoration: none;
transition: color 0.2s linear;
}
a:hover {
color: var(--color-theme-1);
}
</style>

View File

@ -0,0 +1,110 @@
@import '@fontsource/fira-mono';
@tailwind base;
@tailwind components;
@tailwind utilities;
:root {
--font-body: Arial, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu,
Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
--font-mono: 'Fira Mono', monospace;
--color-bg-0: rgb(202, 216, 228);
--color-bg-1: hsl(209, 36%, 86%);
--color-bg-2: hsl(224, 44%, 95%);
--color-theme-1: #ff3e00;
--color-theme-2: #4075a6;
--color-text: rgba(0, 0, 0, 0.7);
--column-width: 42rem;
--column-margin-top: 4rem;
font-family: var(--font-body);
color: var(--color-text);
}
body {
min-height: 100vh;
margin: 0;
background-attachment: fixed;
background-color: var(--color-bg-1);
background-size: 100vw 100vh;
background-image: radial-gradient(
50% 50% at 50% 50%,
rgba(255, 255, 255, 0.75) 0%,
rgba(255, 255, 255, 0) 100%
),
linear-gradient(180deg, var(--color-bg-0) 0%, var(--color-bg-1) 15%, var(--color-bg-2) 50%);
}
h1,
h2,
p {
font-weight: 400;
}
p {
line-height: 1.5;
}
a {
color: var(--color-theme-1);
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
h1 {
font-size: 2rem;
text-align: center;
}
h2 {
font-size: 1rem;
}
pre {
font-size: 16px;
font-family: var(--font-mono);
background-color: rgba(255, 255, 255, 0.45);
border-radius: 3px;
box-shadow: 2px 2px 6px rgb(255 255 255 / 25%);
padding: 0.5em;
overflow-x: auto;
color: var(--color-text);
}
.text-column {
display: flex;
max-width: 48rem;
flex: 0.6;
flex-direction: column;
justify-content: center;
margin: 0 auto;
}
input,
button {
font-size: inherit;
font-family: inherit;
}
button:focus:not(:focus-visible) {
outline: none;
}
@media (min-width: 720px) {
h1 {
font-size: 2.4rem;
}
}
.visually-hidden {
border: 0;
clip: rect(0 0 0 0);
height: auto;
margin: 0;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;
white-space: nowrap;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:

View File

@ -0,0 +1,18 @@
import adapter from '@sveltejs/adapter-auto';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
/** @type {import('@sveltejs/kit').Config} */
const config = {
// Consult https://kit.svelte.dev/docs/integrations#preprocessors
// for more information about preprocessors
preprocess: vitePreprocess(),
kit: {
// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
// If your environment is not supported or you settled on a specific environment, switch out the adapter.
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
adapter: adapter()
}
};
export default config;

View File

@ -0,0 +1,8 @@
/** @type {import('tailwindcss').Config} */
export default {
content: ['./src/**/*.{html,js,svelte,ts}'],
theme: {
extend: {}
},
plugins: []
};

View File

@ -0,0 +1,18 @@
{
"extends": "./.svelte-kit/tsconfig.json",
"compilerOptions": {
"allowJs": true,
"checkJs": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"sourceMap": true,
"strict": true,
"moduleResolution": "bundler"
}
// Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
//
// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
// from the referenced tsconfig.json - TypeScript does not merge them in
}

View File

@ -0,0 +1,6 @@
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';
export default defineConfig({
plugins: [sveltekit()]
});

View File

@ -1,6 +1,6 @@
{ {
"name": "VEL-Connect", "name": "VEL-Connect",
"rootNamespace": "VEL-Connect", "rootNamespace": "VELConnect",
"references": [ "references": [
"GUID:1e55e2c4387020247a1ae212bbcbd381", "GUID:1e55e2c4387020247a1ae212bbcbd381",
"GUID:343deaaf83e0cee4ca978e7df0b80d21", "GUID:343deaaf83e0cee4ca978e7df0b80d21",

View File

@ -917,7 +917,7 @@ namespace VELConnect
case UnityWebRequest.Result.ConnectionError: case UnityWebRequest.Result.ConnectionError:
case UnityWebRequest.Result.DataProcessingError: case UnityWebRequest.Result.DataProcessingError:
case UnityWebRequest.Result.ProtocolError: case UnityWebRequest.Result.ProtocolError:
Debug.LogError(url + ": Error: " + webRequest.error + "\n" + Environment.StackTrace); Debug.LogError(url + ": Error: " + webRequest.error + "\n" + webRequest.downloadHandler.text + "\n" + Environment.StackTrace);
failureCallback?.Invoke(webRequest.error); failureCallback?.Invoke(webRequest.error);
break; break;
case UnityWebRequest.Result.Success: case UnityWebRequest.Result.Success:
@ -929,6 +929,23 @@ namespace VELConnect
webRequest.Dispose(); webRequest.Dispose();
} }
public static void SetDataBlock(string blockId, State.DataBlock dataBlock)
{
PostRequestCallback(instance.velConnectUrl + "/data_block/" + blockId, JsonConvert.SerializeObject(dataBlock, Formatting.None, new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore
}));
}
public static void GetDataBlock(string blockId, Action<State.DataBlock> successCallback = null, Action<string> failureCallback = null)
{
GetRequestCallback(instance.velConnectUrl + "/data_block/" + blockId, data =>
{
State.DataBlock dict = JsonConvert.DeserializeObject<State.DataBlock>(data);
successCallback?.Invoke(dict);
}, failureCallback);
}
private void OnApplicationFocus(bool focus) private void OnApplicationFocus(bool focus)
{ {
UpdateUserCount(!focus); UpdateUserCount(!focus);

View File

@ -0,0 +1,85 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using VelNet;
namespace VELConnect
{
public class VelNetPersist : MonoBehaviour
{
public SyncState syncState;
private string Id => $"{Application.productName}_{VelNetManager.Room}_{syncState.networkObject.sceneNetworkId}_{syncState.networkObject.syncedComponents.IndexOf(syncState)}";
private const float interval = 5f;
private double nextUpdate;
private bool loading;
private const bool debugLogs = false;
private void Update()
{
if (Time.timeAsDouble > nextUpdate && VelNetManager.InRoom && !loading)
{
nextUpdate = Time.timeAsDouble + interval + UnityEngine.Random.Range(0, interval);
if (syncState.networkObject.IsMine)
{
Save();
}
}
}
private void OnEnable()
{
VelNetManager.OnJoinedRoom += OnJoinedRoom;
}
private void OnDisable()
{
VelNetManager.OnJoinedRoom -= OnJoinedRoom;
}
private void OnJoinedRoom(string roomName)
{
Load();
}
private void Load()
{
loading = true;
if (debugLogs) Debug.Log($"[VelNetPersist] Loading {Id}");
VELConnectManager.GetDataBlock(Id, data =>
{
if (!data.data.TryGetValue("state", out string d))
{
Debug.LogError($"[VelNetPersist] Failed to parse {Id}");
return;
}
if (syncState == null)
{
Debug.LogError("[VelNetPersist] Object doesn't exist anymore");
}
syncState.UnpackState(Convert.FromBase64String(d));
if (debugLogs) Debug.Log($"[VelNetPersist] Loaded {Id}");
loading = false;
}, s =>
{
Debug.LogError(s);
loading = false;
});
}
private void Save()
{
if (debugLogs) Debug.Log($"[VelNetPersist] Saving {Id}");
VELConnectManager.SetDataBlock(Id, new VELConnectManager.State.DataBlock()
{
category = "object_persist",
data = new Dictionary<string, string>
{
{ "name", syncState.networkObject.name },
{ "state", Convert.ToBase64String(syncState.PackState()) }
}
});
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cd4cd3f927d9998449837165f749c9c2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,7 +1,7 @@
{ {
"name": "edu.uga.engr.vel.vel-connect", "name": "edu.uga.engr.vel.vel-connect",
"displayName": "VEL-Connect", "displayName": "VEL-Connect",
"version": "4.0.0", "version": "4.0.1",
"unity": "2019.1", "unity": "2019.1",
"description": "Web-based configuration for VR applications", "description": "Web-based configuration for VR applications",
"keywords": [], "keywords": [],

14
velconnect-npm/.vscode/tasks.json vendored Normal file
View File

@ -0,0 +1,14 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "Build Pocketbase Types",
"type": "shell",
"command": "npm run generate-types",
"detail": "Generates pocketbase types from types/exported_schema.json",
"problemMatcher": []
}
]
}

File diff suppressed because it is too large Load Diff

View File

@ -8,14 +8,18 @@
"src" "src"
], ],
"scripts": { "scripts": {
"build": "tsc --module commonjs" "build": "tsc --module commonjs",
"generate-types": "npx pocketbase-typegen --json src/types/exported_schema.json -o src/types/pocketbase-types.ts",
"minify": "webpack --mode=production"
}, },
"author": "VEL", "author": "VEL",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"pocketbase": "^0.19.0" "pocketbase": "^0.20.3"
}, },
"devDependencies": { "devDependencies": {
"typescript": "^5.3.3" "typescript": "^5.3.3",
"webpack": "^5.89.0",
"webpack-cli": "^5.1.4"
} }
} }

View File

@ -1,272 +1,74 @@
import PocketBase from "pocketbase"; import PocketBase from "pocketbase";
import type { Record } from "pocketbase"; import { TypedPocketBase } from "./types/pocketbase-types";
type VelConnectOptions = {
url?: string;
debugLog?: boolean;
};
export interface Device extends Record { export let pb: TypedPocketBase = new PocketBase(
os_info: string; "https://velconnect-v4.ugavel.com"
friendly_name: string; ) as TypedPocketBase;
current_room: string; let debugLog = false;
current_app: string; let initialized = false;
pairing_code: string;
data: string;
expand: { data?: DataBlock };
}
export interface DataBlock extends Record {
block_id: string;
owner: string;
data: { [key: string]: string };
}
export class VELConnect { export async function initVelConnect(options: VelConnectOptions = {}) {
if (options.debugLog) {
pb: PocketBase; debugLog = true;
debugLog = false;
constructor() {
this.pb = new PocketBase();
this.pb.authStore.onChange((auth) => {
console.log("authStore changed", auth);
currentUser.set(pb.authStore.model);
if (pb.authStore.isValid) {
}
});
} }
if (!initialized) {
export const currentUser = writable(pb.authStore.model); pb = new PocketBase(
options.url ? options.url : "https://velconnect-v4.ugavel.com"
) as TypedPocketBase;
log(`Initialized velconnect on ${pb.baseUrl}`);
export const pairedDevices = writable<string[]>([]);
export const currentDeviceId = writable("");
// const device = get(currentDevice);
// if (device == '' && device.length > 0) {
// currentDevice.set(device[0]);
// }
let unsubscribeDeviceFields: () => void;
let unsubscribeDeviceData: () => void;
let unsubscribeRoomData: () => void;
let unsubscribeCurrentDevice: () => void;
let unsubscribeCurrentUser: () => void;
export let deviceFields = writable<Device | null>(null);
export let deviceData = writable<DataBlock | null>(null);
export let roomData = writable<DataBlock | null>(null);
export let sending = false;
export async function startListening(baseUrl: string) {
pb.baseUrl = baseUrl;
if (get(currentDeviceId) != "") {
const d = (await pb.collection("Device").getOne(get(currentDeviceId), {
expand: "data",
})) as Device;
deviceData.set(d.expand.data as DataBlock);
// we don't need expand anymore, since it doesn't work in subscribe()
d.expand = {};
deviceFields.set(d);
} }
await pb.collection("Users").authRefresh();
unsubscribeCurrentDevice = currentDeviceId.subscribe(async (val) => { initialized = true;
log("currentDeviceId subscribe change event");
unsubscribeDeviceFields?.();
unsubscribeDeviceData?.();
if (val != "") {
const d = (await pb
.collection("Device")
.getOne(get(currentDeviceId), { expand: "data" })) as Device;
deviceData.set(d.expand.data as DataBlock);
// we don't need expand anymore, since it doesn't work in subscribe()
d.expand = {};
deviceFields.set(d);
unsubscribeDeviceData = await pb
.collection("DataBlock")
.subscribe(d.data, async (data) => {
log("deviceData subscribe change event");
deviceData.set(data.record as DataBlock);
});
unsubscribeDeviceFields = await pb
.collection("Device")
.subscribe(val, async (data) => {
log("deviceFields subscribe change event");
const d = data.record as Device;
deviceFields.set(d);
// if the devie changes, the devicedata could change, so we need to resubscribe
unsubscribeDeviceData?.();
unsubscribeDeviceData = await pb
.collection("DataBlock")
.subscribe(d.data, async (data) => {
log("deviceData subscribe change event");
deviceData.set(data.record as DataBlock);
});
getRoomData(d);
});
if (d != null) getRoomData(d);
} else {
deviceFields.set(null);
deviceData.set(null);
roomData.set(null);
}
});
unsubscribeCurrentUser = currentUser.subscribe((user) => {
log(`currentUser changed ${user}`);
pairedDevices.set(user?.["devices"] ?? []);
currentDeviceId.set(get(pairedDevices)[0] ?? "");
});
} }
export function stopListening() { export function signOut() {
unsubscribeCurrentDevice?.(); pb.authStore.clear();
unsubscribeDeviceFields?.();
unsubscribeDeviceData?.();
unsubscribeRoomData?.();
unsubscribeCurrentUser?.();
console.log("Stop listening");
} }
async function getRoomData(device: Device) { export async function pair(pairingCode: string) {
unsubscribeRoomData?.();
// create or just fetch room by name
let r: DataBlock | null = null;
try { try {
r = (await pb log("Pairing...");
.collection("DataBlock")
.getFirstListItem(
`block_id="${device.current_app}_${device.current_room}"`
)) as DataBlock;
} catch (e: any) {
r = (await pb.collection("DataBlock").create({
block_id: `${device.current_app}_${device.current_room}`,
category: "room",
data: {},
})) as DataBlock;
}
roomData.set(r);
if (r != null) {
unsubscribeRoomData = await pb
.collection("DataBlock")
.subscribe(r.id, (data) => {
log("roomData subscribe change event");
roomData.set(data.record as DataBlock);
});
} else {
console.error("Failed to get or create room");
}
}
let abortController = new AbortController();
export function delayedSend() {
console.log("fn: delayedSend()");
// abort the previous send
abortController.abort();
const newAbortController = new AbortController();
abortController = newAbortController;
setTimeout(() => {
if (!newAbortController.signal.aborted) {
send();
} else {
console.log("aborted");
}
}, 1000);
}
export function send() {
console.log("sending...");
sending = true;
let promises: Promise<any>[] = [];
const device = get(deviceFields);
const data = get(deviceData);
const room = get(roomData);
// TODO send changes only
if (device) {
promises.push(pb.collection("Device").update(device.id, device));
}
if (data) {
promises.push(pb.collection("DataBlock").update(data.id, data));
}
if (room) {
promises.push(pb.collection("DataBlock").update(room.id, room));
}
Promise.all(promises).then(() => {
sending = false;
});
}
export function removeDevice(d: string) {
pairedDevices.set(get(pairedDevices).filter((i) => i != d));
if (get(currentDeviceId) == d) {
console.log("Removed current device");
// if there are still devices left
if (get(pairedDevices).length > 0) {
currentDeviceId.set(get(pairedDevices)[0] ?? "");
} else {
currentDeviceId.set("");
}
}
const user = get(currentUser);
if (user) {
user["devices"] = user["devices"].filter((i: string) => i != d);
pb.collection("Users").update(user.id, user);
}
}
async function pair(pairingCode: string) {
try {
// find the device by pairing code // find the device by pairing code
const device = (await pb const device = await pb
.collection("Device") .collection("Device")
.getFirstListItem(`pairing_code="${pairingCode}"`)) as Device; .getFirstListItem(`pairing_code="${pairingCode}"`);
// add it to the local data
currentDeviceId.set(device.id);
if (!get(pairedDevices).includes(device.id)) {
pairedDevices.set([...get(pairedDevices), device.id]);
}
// add it to my account if logged in // add it to my account if logged in
const u = get(currentUser); const u = pb.authStore.model;
if (u) { if (u) {
// add the device to the user's devices // add the device to the user's devices
u["devices"].push(device.id); u["devices"].push(device.id);
// add the account data to the device // add the account data to the device
if ( if (
u.user_data == null || u["user_data"] == null ||
u.user_data == undefined || u["user_data"] == undefined ||
u.user_data == "" u["user_data"] == ""
) { ) {
// create a new user data block if it doesn't exist on the user already // create a new user data block if it doesn't exist on the user already
const userDataBlock = await pb.collection("DataBlock").create({ const userDataBlock = await pb.collection("DataBlock").create({
category: "device", category: "device",
data: {}, data: {},
owner: u.id, owner: u["id"],
}); });
u.user_data = userDataBlock.id; u["user_data"] = userDataBlock.id;
} }
device["data"] = u.user_data; device["data"] = u["user_data"];
device["owner"] = u.id; device["owner"] = u["id"];
device["past_owners"] = [...device["past_owners"], u.id]; device["past_owners"] = [...device["past_owners"], u["id"]];
await pb.collection("Device").update(device.id, device); await pb.collection("Device").update(device.id, device);
await pb.collection("Users").update(u.id, u); await pb.collection("Users").update(u["id"], u);
} }
return { error: null }; return { error: null, deviceId: device.id };
} catch (e) { } catch (e) {
console.error("Not found: " + e); console.error("Not found: " + e);
if (e == "ClientResponseError 404: The requested resource wasn't found.") { if (e == "ClientResponseError 404: The requested resource wasn't found.") {
@ -282,10 +84,12 @@ async function pair(pairingCode: string) {
export async function login(username: string, password: string) { export async function login(username: string, password: string) {
try { try {
await pb.collection("Users").authWithPassword(username, password); const ret = await pb
return {}; .collection("Users")
} catch (err: any) { .authWithPassword(username, password);
return err; return { ret };
} catch (error: any) {
return { error };
} }
} }
@ -303,15 +107,8 @@ export async function signUp(username: string, password: string) {
} }
} }
export function signOut() { function log(msg: any) {
pb.authStore.clear();
}
function log(msg: string) {
if (debugLog) { if (debugLog) {
console.log(msg); console.log(msg);
} }
} }
}

View File

@ -0,0 +1,451 @@
[
{
"id": "ve85cwsj7syqvxu",
"name": "UserCount",
"type": "base",
"system": false,
"schema": [
{
"system": false,
"id": "pnhtdbcx",
"name": "app_id",
"type": "text",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
},
{
"system": false,
"id": "wkf3zyyb",
"name": "room_id",
"type": "text",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
},
{
"system": false,
"id": "f7k9hdoc",
"name": "total_users",
"type": "number",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"noDecimal": false
}
},
{
"system": false,
"id": "uevek8os",
"name": "room_users",
"type": "number",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"noDecimal": false
}
},
{
"system": false,
"id": "coilxuep",
"name": "version",
"type": "text",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
},
{
"system": false,
"id": "zee0a2yb",
"name": "platform",
"type": "text",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
}
],
"indexes": [],
"listRule": null,
"viewRule": null,
"createRule": "",
"updateRule": null,
"deleteRule": null,
"options": {}
},
{
"id": "fupstz47c55s69f",
"name": "Device",
"type": "base",
"system": false,
"schema": [
{
"system": false,
"id": "1tkrnxqf",
"name": "os_info",
"type": "text",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
},
{
"system": false,
"id": "knspamfx",
"name": "friendly_name",
"type": "text",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
},
{
"system": false,
"id": "qfalwg3c",
"name": "modified_by",
"type": "text",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
},
{
"system": false,
"id": "x0zlup7v",
"name": "current_app",
"type": "text",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
},
{
"system": false,
"id": "vpzen2th",
"name": "current_room",
"type": "text",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
},
{
"system": false,
"id": "d0ckgjhm",
"name": "pairing_code",
"type": "text",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
},
{
"system": false,
"id": "hglbl7da",
"name": "last_online",
"type": "date",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": "",
"max": ""
}
},
{
"system": false,
"id": "qxsvm1rf",
"name": "data",
"type": "relation",
"required": true,
"presentable": false,
"unique": false,
"options": {
"collectionId": "3qwwkz4wb0lyi78",
"cascadeDelete": false,
"minSelect": null,
"maxSelect": 1,
"displayFields": [
"data"
]
}
},
{
"system": false,
"id": "nfernq2q",
"name": "owner",
"type": "relation",
"required": false,
"presentable": false,
"unique": false,
"options": {
"collectionId": "_pb_users_auth_",
"cascadeDelete": false,
"minSelect": null,
"maxSelect": 1,
"displayFields": [
"username"
]
}
},
{
"system": false,
"id": "p1aruqz5",
"name": "past_owners",
"type": "relation",
"required": false,
"presentable": false,
"unique": false,
"options": {
"collectionId": "_pb_users_auth_",
"cascadeDelete": false,
"minSelect": null,
"maxSelect": null,
"displayFields": [
"username"
]
}
}
],
"indexes": [],
"listRule": "",
"viewRule": "",
"createRule": null,
"updateRule": "",
"deleteRule": null,
"options": {}
},
{
"id": "_pb_users_auth_",
"name": "Users",
"type": "auth",
"system": false,
"schema": [
{
"system": false,
"id": "users_name",
"name": "name",
"type": "text",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
},
{
"system": false,
"id": "users_avatar",
"name": "avatar",
"type": "file",
"required": false,
"presentable": false,
"unique": false,
"options": {
"mimeTypes": [
"image/jpeg",
"image/png",
"image/svg+xml",
"image/gif",
"image/webp"
],
"thumbs": null,
"maxSelect": 1,
"maxSize": 5242880,
"protected": false
}
},
{
"system": false,
"id": "1hwaooub",
"name": "devices",
"type": "relation",
"required": false,
"presentable": false,
"unique": false,
"options": {
"collectionId": "fupstz47c55s69f",
"cascadeDelete": false,
"minSelect": null,
"maxSelect": null,
"displayFields": []
}
},
{
"system": false,
"id": "xvw8arlm",
"name": "profiles",
"type": "relation",
"required": false,
"presentable": false,
"unique": false,
"options": {
"collectionId": "3qwwkz4wb0lyi78",
"cascadeDelete": false,
"minSelect": null,
"maxSelect": null,
"displayFields": [
"data"
]
}
}
],
"indexes": [],
"listRule": "id = @request.auth.id",
"viewRule": "id = @request.auth.id",
"createRule": "",
"updateRule": "id = @request.auth.id",
"deleteRule": "id = @request.auth.id",
"options": {
"allowEmailAuth": true,
"allowOAuth2Auth": true,
"allowUsernameAuth": true,
"exceptEmailDomains": null,
"manageRule": null,
"minPasswordLength": 6,
"onlyEmailDomains": null,
"onlyVerified": false,
"requireEmail": false
}
},
{
"id": "3qwwkz4wb0lyi78",
"name": "DataBlock",
"type": "base",
"system": false,
"schema": [
{
"system": false,
"id": "wbifl8pv",
"name": "category",
"type": "text",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
},
{
"system": false,
"id": "5a3nwg7m",
"name": "modified_by",
"type": "text",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
},
{
"system": false,
"id": "mkzyfsng",
"name": "data",
"type": "json",
"required": false,
"presentable": true,
"unique": false,
"options": {
"maxSize": 2000000
}
},
{
"system": false,
"id": "a3d7pkoh",
"name": "owner",
"type": "relation",
"required": false,
"presentable": false,
"unique": false,
"options": {
"collectionId": "_pb_users_auth_",
"cascadeDelete": false,
"minSelect": null,
"maxSelect": 1,
"displayFields": [
"username"
]
}
},
{
"system": false,
"id": "80tmi6fm",
"name": "block_id",
"type": "text",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
}
],
"indexes": [
"CREATE INDEX `idx_aYVfg1q` ON `DataBlock` (`block_id`)"
],
"listRule": "",
"viewRule": "",
"createRule": "",
"updateRule": "",
"deleteRule": null,
"options": {}
}
]

View File

@ -0,0 +1,106 @@
/**
* This file was @generated using pocketbase-typegen
*/
import type PocketBase from 'pocketbase'
import type { RecordService } from 'pocketbase'
export enum Collections {
DataBlock = "DataBlock",
Device = "Device",
UserCount = "UserCount",
Users = "Users",
}
// Alias types for improved usability
export type IsoDateString = string
export type RecordIdString = string
export type HTMLString = string
// System fields
export type BaseSystemFields<T = never> = {
id: RecordIdString
created: IsoDateString
updated: IsoDateString
collectionId: string
collectionName: Collections
expand?: T
}
export type AuthSystemFields<T = never> = {
email: string
emailVisibility: boolean
username: string
verified: boolean
} & BaseSystemFields<T>
// Record types for each collection
export type DataBlockRecord<Tdata = unknown> = {
block_id?: string
category?: string
data?: null | Tdata
modified_by?: string
owner?: RecordIdString
}
export type DeviceRecord = {
current_app?: string
current_room?: string
data: RecordIdString
friendly_name?: string
last_online?: IsoDateString
modified_by?: string
os_info?: string
owner?: RecordIdString
pairing_code?: string
past_owners?: RecordIdString[]
}
export type UserCountRecord = {
app_id?: string
platform?: string
room_id?: string
room_users?: number
total_users?: number
version?: string
}
export type UsersRecord = {
avatar?: string
devices?: RecordIdString[]
name?: string
profiles?: RecordIdString[]
}
// Response types include system fields and match responses from the PocketBase API
export type DataBlockResponse<Tdata = unknown, Texpand = unknown> = Required<DataBlockRecord<Tdata>> & BaseSystemFields<Texpand>
export type DeviceResponse<Texpand = unknown> = Required<DeviceRecord> & BaseSystemFields<Texpand>
export type UserCountResponse<Texpand = unknown> = Required<UserCountRecord> & BaseSystemFields<Texpand>
export type UsersResponse<Texpand = unknown> = Required<UsersRecord> & AuthSystemFields<Texpand>
// Types containing all Records and Responses, useful for creating typing helper functions
export type CollectionRecords = {
DataBlock: DataBlockRecord
Device: DeviceRecord
UserCount: UserCountRecord
Users: UsersRecord
}
export type CollectionResponses = {
DataBlock: DataBlockResponse
Device: DeviceResponse
UserCount: UserCountResponse
Users: UsersResponse
}
// Type for usage with type asserted PocketBase instance
// https://github.com/pocketbase/js-sdk#specify-typescript-definitions
export type TypedPocketBase = PocketBase & {
collection(idOrName: 'DataBlock'): RecordService<DataBlockResponse>
collection(idOrName: 'Device'): RecordService<DeviceResponse>
collection(idOrName: 'UserCount'): RecordService<UserCountResponse>
collection(idOrName: 'Users'): RecordService<UsersResponse>
}

View File

@ -0,0 +1,11 @@
const path = require('path');
module.exports = {
entry: './dist/index.js',
output: {
filename: 'velconnect.min.js',
path: path.resolve(__dirname, 'dist'),
library: 'velconnect',
libraryTarget: 'umd',
},
};