Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
35 changes: 35 additions & 0 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,38 @@ jobs:
run: pnpm build
- name: Run Tests
run: pnpm test

chat-runtime:
name: Chat PostgreSQL runtime
runs-on: ubuntu-latest
services:
postgres:
image: postgres:17
env:
POSTGRES_USER: tanchat_test
POSTGRES_PASSWORD: tanchat_test
POSTGRES_DB: tanchat_test_ci
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U tanchat_test -d tanchat_test_ci"
--health-interval 5s
--health-timeout 5s
--health-retries 10
env:
DATABASE_URL: postgresql://tanchat_test:tanchat_test@127.0.0.1:5432/tanchat_test_ci
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Setup Tools
uses: tanstack/config/.github/setup@e4b48f16568324f76f467aa4c2aac2f05db632c3 # main
- name: Create existing-site test boundary
run: psql "$DATABASE_URL" -v ON_ERROR_STOP=1 -f harness-tests/postgres/site-baseline.sql
- name: Migrate disposable database
run: pnpm db:migrate
- name: Check cascade lifecycle
run: psql "$DATABASE_URL" -v ON_ERROR_STOP=1 -f harness-tests/postgres/cascade-lifecycle.sql
- name: Run conversation runtime tests
run: pnpm test:chat-runtime
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,6 @@ test-results
src/routeTree.gen.ts
.og-preview/
.readme-preview/

# Generated native desktop helper
/desktop/native/folder-access
20 changes: 20 additions & 0 deletions .oxlintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,26 @@
"react/exhaustive-deps": "warn"
},
"plugins": ["react", "jsx-a11y"]
},
{
"files": ["desktop/preload.cjs"],
"rules": {
"typescript/no-require-imports": "off"
}
},
{
"files": ["**/SavedFiles.tsx"],
"plugins": ["react", "jsx-a11y"],
"rules": {
"jsx-a11y/no-noninteractive-tabindex": [
"error",
{
"tags": [],
"roles": ["tabpanel", "region"],
"allowExpressionValues": true
}
]
}
}
]
}
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,11 @@ To edit docs for a project, make changes in its `docs/` folder (e.g., `../form/d
… and more at <a href="https://tanstack.com"><b>TanStack.com »</b></a>

<!-- Use the force, Luke -->

### TanChat alpha access

TanChat is invite-only, independent of Builder access. Signing in to TanStack does not unlock it. Admins and TanStack maintainers with a linked GitHub account have automatic access and unlimited invites. Maintainer identity comes from the verified GitHub account ID and the site's maintainer catalog, not an editable profile name.

Other users unlock TanChat by redeeming a single-use invite and receive three invites to share. Issuing a link consumes one invite, even if the link expires unused. Links expire after seven days. Account settings links to `/chat-access`, where users can create and copy invites. The main site menu links to TanChat, and accounts without access see the invite page.

Migration `0039_tanchat_invites` adds grants and hashed invite tokens. Server functions and chat HTTP endpoints enforce access independently of the UI. Quota checks lock the issuer account, and redemption locks the invite while granting access and consuming it in one transaction. The local app uses the shared configured database, so invite grants also persist for future production releases.
21 changes: 21 additions & 0 deletions desktop/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# TanStack desktop

This is the Electron host ported from Gum. It opens `https://tanstack.com/chat` in packaged builds and retains the native browser, folder grants, connected-device transport, and update controller.

Install dependencies from the repository root with `pnpm install`.

For development, start the website, then run:

```sh
TANSTACK_APP_URL=http://127.0.0.1:3000 pnpm dev:desktop
```

Use the origin and port of your running website. The development command installs the declared Electron binary using Electron’s own `install-electron` command. Packaged builds ignore development URL overrides.

`pnpm test:desktop` builds the native folder helper and runs the desktop tests. These also run in the repository’s normal test command.

`pnpm pack:desktop` creates a local app directory in `dist/desktop`. Electron Builder may use an available signing identity. This command does not publish an update.

For a distributable macOS build, configure `TANSTACK_UPDATE_URL` with the HTTPS update directory and provide Apple notarization credentials through `APPLE_KEYCHAIN_PROFILE`, an Apple API key, or the existing Apple ID credential variables. Run `pnpm dist:desktop`. Upload the generated artifacts and update metadata to that same directory after verifying signing and notarization. The command does not publish automatically.

The original release scripts are retained in scripts/desktop-release.mjs and scripts/desktop-publish.mjs. `pnpm verify:desktop-release` checks signing, notarization, artifacts, and update metadata without publishing. `pnpm finalize:desktop-release` can submit notarization and regenerate hashes and blockmaps. `pnpm publish:desktop-release` verifies first, then publishes to the explicitly configured TANSTACK_RELEASE_BUCKET and TANSTACK_UPDATE_URL. The update URL must expose that bucket at its configured directory. Provisioning and publishing still require release approval.
5 changes: 5 additions & 0 deletions desktop/assets/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Desktop artwork

The TanStack desktop app uses the existing square TanStack logo at `public/images/logos/logo-color-600.png`. Electron Builder converts that PNG into the platform icon format.

The Kody images in this directory are historical source assets from the Gum desktop port. They are not used by the TanStack package. The original koala artwork came from https://github.com/kentcdodds/kody/blob/main/packages/worker/public/logo.png. The squircle and square files were edited variants.
Binary file added desktop/assets/kody-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added desktop/assets/kody-square.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added desktop/assets/kody-squircle.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 18 additions & 0 deletions desktop/build-device-helper.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { execFileSync } from 'node:child_process'
import { fileURLToPath } from 'node:url'
import { mkdirSync } from 'node:fs'
const directory = fileURLToPath(new URL('./native/', import.meta.url))
mkdirSync(directory, { recursive: true })
execFileSync(
'cc',
[
'-O2',
'-Wall',
'-Wextra',
'-Werror',
directory + 'folder-access.c',
'-o',
directory + 'folder-access',
],
{ stdio: 'inherit' },
)
67 changes: 67 additions & 0 deletions desktop/builder.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { execFileSync } from 'node:child_process'
import { fileURLToPath } from 'node:url'

const updateUrl = process.env.TANSTACK_UPDATE_URL
if (updateUrl) {
const url = new URL(updateUrl)
if (
url.protocol !== 'https:' ||
url.username ||
url.password ||
url.search ||
url.hash
) {
throw new Error(
'TANSTACK_UPDATE_URL must be an HTTPS directory URL without credentials or query parameters.',
)
}
}

export default {
beforePack: ({ packager }) => {
execFileSync(
process.execPath,
[fileURLToPath(new URL('./build-device-helper.mjs', import.meta.url))],
{ stdio: 'inherit' },
)
if (!packager.config.forceCodeSigning) return
if (!updateUrl)
throw new Error(
'Set TANSTACK_UPDATE_URL to the hosted native update directory before making a release.',
)
const env = process.env
const notarization =
env.APPLE_KEYCHAIN_PROFILE ||
(env.APPLE_API_KEY && env.APPLE_API_KEY_ID && env.APPLE_API_ISSUER) ||
(env.APPLE_ID && env.APPLE_APP_SPECIFIC_PASSWORD && env.APPLE_TEAM_ID)
if (!notarization)
throw new Error(
'Configure Apple notarization credentials before making a release.',
)
},
appId: 'com.tanstack.desktop',
productName: 'TanStack',
asar: true,
asarUnpack: ['native/folder-access'],
forceCodeSigning: true,
directories: { output: '../dist/desktop' },
files: [
'*.mjs',
'!*.test.mjs',
'!builder.config.mjs',
'preload.cjs',
'package.json',
'native/folder-access',
],
extraMetadata: { nativeUpdates: Boolean(updateUrl) },
publish: updateUrl ? [{ provider: 'generic', url: updateUrl }] : null,
mac: {
icon: '../public/images/logos/logo-color-600.png',
category: 'public.app-category.productivity',
target: ['dmg', 'zip'],
hardenedRuntime: true,
notarize: true,
},
artifactName: 'TanStack-${version}-${arch}.${ext}',
dmg: { sign: true },
}
Loading
Loading