diff --git a/.env.example b/.env.example index 1b6ffce..78a8703 100644 --- a/.env.example +++ b/.env.example @@ -5,15 +5,7 @@ HOST_DATABASE_URL=file:./database.db HOST_DATABASE_AUTH_TOKEN= # for Turso # API Plugin Database (path relative to host/) -API_DATABASE_URL=file:../api/api.db -API_DATABASE_AUTH_TOKEN= # for Turso - -# Host Database -HOST_DATABASE_URL=file:./database.db -HOST_DATABASE_AUTH_TOKEN= # for Turso - -# API Plugin Database (path relative to host/) -API_DATABASE_URL=file:../api/api.db +API_DATABASE_URL=file:./api/api.db API_DATABASE_AUTH_TOKEN= # for Turso # NEAR AI Cloud diff --git a/api/drizzle.config.ts b/api/drizzle.config.ts index 327cd25..ede4554 100644 --- a/api/drizzle.config.ts +++ b/api/drizzle.config.ts @@ -5,7 +5,7 @@ export default defineConfig({ out: './src/db/migrations', dialect: 'turso', dbCredentials: { - url: process.env.API_DATABASE_URL || 'file:./database.db', + url: process.env.API_DATABASE_URL || 'file:./api.db', authToken: process.env.API_DATABASE_AUTH_TOKEN, }, verbose: true, diff --git a/api/package.json b/api/package.json index 58a69db..4e43fa6 100644 --- a/api/package.json +++ b/api/package.json @@ -18,17 +18,19 @@ "effect": "catalog:", "nanoid": "^5.1.6", "near-kit": "catalog:", - "openai": "^6.16.0" + "openai": "^6.16.0", + "@orpc/contract": "^1.13.4", + "@orpc/server": "^1.13.4" }, "devDependencies": { - "@effect/language-service": "^0.72.0", + "@effect/language-service": "^0.41.1", "@module-federation/node": "^2.7.25", - "@rspack/cli": "^1.7.3", - "@rspack/core": "^1.7.3", + "@rspack/cli": "^1.6.8", + "@rspack/core": "^1.6.8", "@types/node": "catalog:", "dotenv": "^17.2.3", - "every-plugin": "0.8.8", - "vite-tsconfig-paths": "^6.0.5", + "every-plugin": "catalog:", + "vite-tsconfig-paths": "^5.1.4", "vitest": "catalog:", "zephyr-rspack-plugin": "^0.1.2" }, @@ -42,6 +44,7 @@ "db:generate": "drizzle-kit generate", "db:migrate": "drizzle-kit migrate", "db:push": "drizzle-kit push", - "db:studio": "drizzle-kit studio" + "db:studio": "drizzle-kit studio", + "deploy": "DEPLOY=true rspack build" } -} +} \ No newline at end of file diff --git a/api/plugin.dev.ts b/api/plugin.dev.ts index f9226d8..8b5ac73 100644 --- a/api/plugin.dev.ts +++ b/api/plugin.dev.ts @@ -1,17 +1,17 @@ import "dotenv/config"; +import type { PluginConfigInput } from "every-plugin"; +import packageJson from "./package.json" with { type: "json" }; +import type Plugin from "./src/index"; export default { - pluginId: "api", + pluginId: packageJson.name, port: 3014, config: { variables: { - NEAR_AI_MODEL: process.env.NEAR_AI_MODEL || "deepseek-ai/DeepSeek-V3.1", }, secrets: { API_DATABASE_URL: process.env.API_DATABASE_URL || "file:./database.db", - API_DATABASE_AUTH_TOKEN: process.env.API_DATABASE_AUTH_TOKEN || "", - NEAR_AI_API_KEY: process.env.NEAR_AI_API_KEY || "", - NEAR_AI_BASE_URL: process.env.NEAR_AI_BASE_URL || "https://cloud-api.near.ai/v1", + API_DATABASE_AUTH_TOKEN: process.env.API_DATABASE_AUTH_TOKEN, }, - }, + } satisfies PluginConfigInput, }; diff --git a/api/rspack.config.cjs b/api/rspack.config.cjs index 53ada8b..ffa29b6 100644 --- a/api/rspack.config.cjs +++ b/api/rspack.config.cjs @@ -4,7 +4,7 @@ const { EveryPluginDevServer } = require("every-plugin/build/rspack"); const { withZephyr } = require("zephyr-rspack-plugin"); const pkg = require("./package.json"); -const isProduction = process.env.NODE_ENV === 'production'; +const shouldDeploy = process.env.DEPLOY === 'true'; function updateHostConfig(name, url) { try { @@ -35,7 +35,7 @@ const baseConfig = { stats: 'errors-warnings', }; -module.exports = isProduction +module.exports = shouldDeploy ? withZephyr({ hooks: { onDeployComplete: (info) => { diff --git a/bos.config.json b/bos.config.json index 87990ff..b8bd087 100644 --- a/bos.config.json +++ b/bos.config.json @@ -1,40 +1,110 @@ { "account": "example.near", - "create": { - "project": "nearbuilders/cyborg", - "ui": "nearbuilders/cyborg/ui", - "api": "nearbuilders/cyborg/api", - "host": "nearbuilders/cyborg/host" + "template": "near-everything/every-plugin/demo", + "shared": { + "ui": { + "react": { + "requiredVersion": "19.2.4", + "singleton": true, + "eager": true + }, + "react-dom": { + "requiredVersion": "19.2.4", + "singleton": true, + "eager": true + }, + "@tanstack/react-query": { + "requiredVersion": "5.90.20", + "singleton": true, + "eager": true + }, + "@tanstack/react-router": { + "requiredVersion": "1.157.16", + "singleton": true, + "eager": true + }, + "@hot-labs/near-connect": { + "requiredVersion": "0.8.2", + "singleton": true + }, + "near-kit": { + "requiredVersion": "0.10.0", + "singleton": true + }, + "better-auth": { + "requiredVersion": "1.4.17", + "singleton": true + }, + "better-near-auth": { + "requiredVersion": "0.3.4", + "singleton": true + } + } + }, + "gateway": { + "development": "http://localhost:8787", + "production": "https://everything.dev" }, "app": { "host": { "title": "NEAR Agent", "description": "Template for building NEAR AI applications", "development": "http://localhost:3000", - "production": "https://elliot-braem-1818-host-every-plugin-near-everythi-fdf320911-ze.zephyrcloud.app", + "production": "https://elliot-braem-1910-host-every-plugin-near-everythi-9ac1c3d34-ze.zephyrcloud.app", "secrets": [ "HOST_DATABASE_URL", "HOST_DATABASE_AUTH_TOKEN", "BETTER_AUTH_SECRET", "BETTER_AUTH_URL" - ] + ], + "template": "near-everything/every-plugin/demo/host", + "files": [ + "rsbuild.config.ts", + "tsconfig.json", + "vitest.config.ts", + "drizzle.config.ts" + ], + "variables": {} }, "ui": { "name": "ui", "development": "http://localhost:3002", - "production": "https://elliot-braem-1786-ui-cyborg-nearbuilders-46d98f16b-ze.zephyrcloud.app", + "production": "https://elliot-braem-1914-ui-cyborg-nearbuilders-ee224954c-ze.zephyrcloud.app", "exposes": { - "App": "./App", - "components": "./components", - "providers": "./providers", - "types": "./types" + "./Router": "./src/router.tsx", + "./Hydrate": "./src/hydrate.tsx", + "./remote": "./src/remote/index.ts", + "./components": "./src/components/index.ts", + "./providers": "./src/providers/index.tsx", + "./hooks": "./src/hooks/index.ts", + "./types": "./src/types/index.ts" }, - "ssr": "https://elliot-braem-1899-ui-cyborg-nearbuilders-3c6c03f0e-ze.zephyrcloud.app" + "ssr": "https://elliot-braem-1915-ui-cyborg-nearbuilders-7859071fb-ze.zephyrcloud.app", + "template": "near-everything/every-plugin/demo/ui", + "files": [ + "rsbuild.config.ts", + "tsconfig.json", + "postcss.config.mjs", + "components.json", + "src/router.tsx", + "src/router.server.tsx", + "src/hydrate.tsx", + "src/env.d.ts", + "src/remote", + "src/utils/orpc.ts", + "src/types/index.ts", + "src/routes/__root.tsx", + "src/lib/auth-client.ts", + "src/lib/auth-utils.ts" + ], + "secrets": [], + "variables": {} }, "api": { "name": "api", "development": "http://localhost:3014", - "production": "https://elliot-braem-1788-api-cyborg-nearbuilders-ac0e7b73a-ze.zephyrcloud.app", + "production": "https://elliot-braem-1916-api-cyborg-nearbuilders-a9be6ebfa-ze.zephyrcloud.app", + "proxy": "https://elliot-braem-1811-api-every-plugin-near-everythin-2defcb105-ze.zephyrcloud.app", "variables": { "NEAR_AI_MODEL": "deepseek-ai/DeepSeek-V3.1", "NEAR_RPC_URL": "https://rpc.mainnet.near.org", @@ -46,6 +116,14 @@ "API_DATABASE_AUTH_TOKEN", "NEAR_AI_API_KEY", "NEAR_AI_BASE_URL" + ], + "template": "near-everything/every-plugin/demo/api", + "files": [ + "rspack.config.cjs", + "tsconfig.json", + "vitest.config.ts", + "drizzle.config.ts", + "plugin.dev.ts" ] } } diff --git a/host/bun.lock b/host/bun.lock deleted file mode 100644 index 8cf6c74..0000000 --- a/host/bun.lock +++ /dev/null @@ -1,261 +0,0 @@ -{ - "lockfileVersion": 1, - "workspaces": { - "": { - "name": "host", - "dependencies": { - "@hono/node-server": "^1.19.9", - "@libsql/client": "^0.17.0", - "@types/better-sqlite3": "^7.6.13", - "better-auth": "^1.4.15", - "better-near-auth": "^0.3.4", - "better-sqlite3": "^12.6.2", - "drizzle-orm": "^0.45.1", - "hono": "^4.6.0", - }, - }, - }, - "packages": { - "@better-auth/core": ["@better-auth/core@1.4.17", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "zod": "^4.3.5" }, "peerDependencies": { "@better-auth/utils": "0.3.0", "@better-fetch/fetch": "1.1.21", "better-call": "1.1.8", "jose": "^6.1.0", "kysely": "^0.28.5", "nanostores": "^1.0.1" } }, "sha512-WSaEQDdUO6B1CzAmissN6j0lx9fM9lcslEYzlApB5UzFaBeAOHNUONTdglSyUs6/idiZBoRvt0t/qMXCgIU8ug=="], - - "@better-auth/telemetry": ["@better-auth/telemetry@1.4.17", "", { "dependencies": { "@better-auth/utils": "0.3.0", "@better-fetch/fetch": "1.1.21" }, "peerDependencies": { "@better-auth/core": "1.4.17" } }, "sha512-R1BC4e/bNjQbXu7lG6ubpgmsPj7IMqky5DvMlzAtnAJWJhh99pMh/n6w5gOHa0cqDZgEAuj75IPTxv+q3YiInA=="], - - "@better-auth/utils": ["@better-auth/utils@0.3.0", "", {}, "sha512-W+Adw6ZA6mgvnSnhOki270rwJ42t4XzSK6YWGF//BbVXL6SwCLWfyzBc1lN2m/4RM28KubdBKQ4X5VMoLRNPQw=="], - - "@better-fetch/fetch": ["@better-fetch/fetch@1.1.21", "", {}, "sha512-/ImESw0sskqlVR94jB+5+Pxjf+xBwDZF/N5+y2/q4EqD7IARUTSpPfIo8uf39SYpCxyOCtbyYpUrZ3F/k0zT4A=="], - - "@hono/node-server": ["@hono/node-server@1.19.9", "", { "peerDependencies": { "hono": "^4" } }, "sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw=="], - - "@hot-labs/near-connect": ["@hot-labs/near-connect@0.8.2", "", {}, "sha512-cvi5q9mp7k575CIPSUEKNUwpwV04oV6i9SJBkwaaT2IS3rFaIlE0fnl8zje7Eenj7Futt+L0/mQfJgtek6E0bQ=="], - - "@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="], - - "@libsql/client": ["@libsql/client@0.17.0", "", { "dependencies": { "@libsql/core": "^0.17.0", "@libsql/hrana-client": "^0.9.0", "js-base64": "^3.7.5", "libsql": "^0.5.22", "promise-limit": "^2.7.0" } }, "sha512-TLjSU9Otdpq0SpKHl1tD1Nc9MKhrsZbCFGot3EbCxRa8m1E5R1mMwoOjKMMM31IyF7fr+hPNHLpYfwbMKNusmg=="], - - "@libsql/core": ["@libsql/core@0.17.0", "", { "dependencies": { "js-base64": "^3.7.5" } }, "sha512-hnZRnJHiS+nrhHKLGYPoJbc78FE903MSDrFJTbftxo+e52X+E0Y0fHOCVYsKWcg6XgB7BbJYUrz/xEkVTSaipw=="], - - "@libsql/darwin-arm64": ["@libsql/darwin-arm64@0.5.22", "", { "os": "darwin", "cpu": "arm64" }, "sha512-4B8ZlX3nIDPndfct7GNe0nI3Yw6ibocEicWdC4fvQbSs/jdq/RC2oCsoJxJ4NzXkvktX70C1J4FcmmoBy069UA=="], - - "@libsql/darwin-x64": ["@libsql/darwin-x64@0.5.22", "", { "os": "darwin", "cpu": "x64" }, "sha512-ny2HYWt6lFSIdNFzUFIJ04uiW6finXfMNJ7wypkAD8Pqdm6nAByO+Fdqu8t7sD0sqJGeUCiOg480icjyQ2/8VA=="], - - "@libsql/hrana-client": ["@libsql/hrana-client@0.9.0", "", { "dependencies": { "@libsql/isomorphic-ws": "^0.1.5", "cross-fetch": "^4.0.0", "js-base64": "^3.7.5", "node-fetch": "^3.3.2" } }, "sha512-pxQ1986AuWfPX4oXzBvLwBnfgKDE5OMhAdR/5cZmRaB4Ygz5MecQybvwZupnRz341r2CtFmbk/BhSu7k2Lm+Jw=="], - - "@libsql/isomorphic-ws": ["@libsql/isomorphic-ws@0.1.5", "", { "dependencies": { "@types/ws": "^8.5.4", "ws": "^8.13.0" } }, "sha512-DtLWIH29onUYR00i0GlQ3UdcTRC6EP4u9w/h9LxpUZJWRMARk6dQwZ6Jkd+QdwVpuAOrdxt18v0K2uIYR3fwFg=="], - - "@libsql/linux-arm-gnueabihf": ["@libsql/linux-arm-gnueabihf@0.5.22", "", { "os": "linux", "cpu": "arm" }, "sha512-3Uo3SoDPJe/zBnyZKosziRGtszXaEtv57raWrZIahtQDsjxBVjuzYQinCm9LRCJCUT5t2r5Z5nLDPJi2CwZVoA=="], - - "@libsql/linux-arm-musleabihf": ["@libsql/linux-arm-musleabihf@0.5.22", "", { "os": "linux", "cpu": "arm" }, "sha512-LCsXh07jvSojTNJptT9CowOzwITznD+YFGGW+1XxUr7fS+7/ydUrpDfsMX7UqTqjm7xG17eq86VkWJgHJfvpNg=="], - - "@libsql/linux-arm64-gnu": ["@libsql/linux-arm64-gnu@0.5.22", "", { "os": "linux", "cpu": "arm64" }, "sha512-KSdnOMy88c9mpOFKUEzPskSaF3VLflfSUCBwas/pn1/sV3pEhtMF6H8VUCd2rsedwoukeeCSEONqX7LLnQwRMA=="], - - "@libsql/linux-arm64-musl": ["@libsql/linux-arm64-musl@0.5.22", "", { "os": "linux", "cpu": "arm64" }, "sha512-mCHSMAsDTLK5YH//lcV3eFEgiR23Ym0U9oEvgZA0667gqRZg/2px+7LshDvErEKv2XZ8ixzw3p1IrBzLQHGSsw=="], - - "@libsql/linux-x64-gnu": ["@libsql/linux-x64-gnu@0.5.22", "", { "os": "linux", "cpu": "x64" }, "sha512-kNBHaIkSg78Y4BqAdgjcR2mBilZXs4HYkAmi58J+4GRwDQZh5fIUWbnQvB9f95DkWUIGVeenqLRFY2pcTmlsew=="], - - "@libsql/linux-x64-musl": ["@libsql/linux-x64-musl@0.5.22", "", { "os": "linux", "cpu": "x64" }, "sha512-UZ4Xdxm4pu3pQXjvfJiyCzZop/9j/eA2JjmhMaAhe3EVLH2g11Fy4fwyUp9sT1QJYR1kpc2JLuybPM0kuXv/Tg=="], - - "@libsql/win32-x64-msvc": ["@libsql/win32-x64-msvc@0.5.22", "", { "os": "win32", "cpu": "x64" }, "sha512-Fj0j8RnBpo43tVZUVoNK6BV/9AtDUM5S7DF3LB4qTYg1LMSZqi3yeCneUTLJD6XomQJlZzbI4mst89yspVSAnA=="], - - "@napi-rs/keyring": ["@napi-rs/keyring@1.2.0", "", { "optionalDependencies": { "@napi-rs/keyring-darwin-arm64": "1.2.0", "@napi-rs/keyring-darwin-x64": "1.2.0", "@napi-rs/keyring-freebsd-x64": "1.2.0", "@napi-rs/keyring-linux-arm-gnueabihf": "1.2.0", "@napi-rs/keyring-linux-arm64-gnu": "1.2.0", "@napi-rs/keyring-linux-arm64-musl": "1.2.0", "@napi-rs/keyring-linux-riscv64-gnu": "1.2.0", "@napi-rs/keyring-linux-x64-gnu": "1.2.0", "@napi-rs/keyring-linux-x64-musl": "1.2.0", "@napi-rs/keyring-win32-arm64-msvc": "1.2.0", "@napi-rs/keyring-win32-ia32-msvc": "1.2.0", "@napi-rs/keyring-win32-x64-msvc": "1.2.0" } }, "sha512-d0d4Oyxm+v980PEq1ZH2PmS6cvpMIRc17eYpiU47KgW+lzxklMu6+HOEOPmxrpnF/XQZ0+Q78I2mgMhbIIo/dg=="], - - "@napi-rs/keyring-darwin-arm64": ["@napi-rs/keyring-darwin-arm64@1.2.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-CA83rDeyONDADO25JLZsh3eHY8yTEtm/RS6ecPsY+1v+dSawzT9GywBMu2r6uOp1IEhQs/xAfxgybGAFr17lSA=="], - - "@napi-rs/keyring-darwin-x64": ["@napi-rs/keyring-darwin-x64@1.2.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-dBHjtKRCj4ByfnfqIKIJLo3wueQNJhLRyuxtX/rR4K/XtcS7VLlRD01XXizjpre54vpmObj63w+ZpHG+mGM8uA=="], - - "@napi-rs/keyring-freebsd-x64": ["@napi-rs/keyring-freebsd-x64@1.2.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-DPZFr11pNJSnaoh0dzSUNF+T6ORhy3CkzUT3uGixbA71cAOPJ24iG8e8QrLOkuC/StWrAku3gBnth2XMWOcR3Q=="], - - "@napi-rs/keyring-linux-arm-gnueabihf": ["@napi-rs/keyring-linux-arm-gnueabihf@1.2.0", "", { "os": "linux", "cpu": "arm" }, "sha512-8xv6DyEMlvRdqJzp4F39RLUmmTQsLcGYYv/3eIfZNZN1O5257tHxTrFYqAsny659rJJK2EKeSa7PhrSibQqRWQ=="], - - "@napi-rs/keyring-linux-arm64-gnu": ["@napi-rs/keyring-linux-arm64-gnu@1.2.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-Pu2V6Py+PBt7inryEecirl+t+ti8bhZphjP+W68iVaXHUxLdWmkgL9KI1VkbRHbx5k8K5Tew9OP218YfmVguIA=="], - - "@napi-rs/keyring-linux-arm64-musl": ["@napi-rs/keyring-linux-arm64-musl@1.2.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-8TDymrpC4P1a9iDEaegT7RnrkmrJN5eNZh3Im3UEV5PPYGtrb82CRxsuFohthCWQW81O483u1bu+25+XA4nKUw=="], - - "@napi-rs/keyring-linux-riscv64-gnu": ["@napi-rs/keyring-linux-riscv64-gnu@1.2.0", "", { "os": "linux", "cpu": "none" }, "sha512-awsB5XI1MYL7fwfjMDGmKOWvNgJEO7mM7iVEMS0fO39f0kVJnOSjlu7RHcXAF0LOx+0VfF3oxbWqJmZbvRCRHw=="], - - "@napi-rs/keyring-linux-x64-gnu": ["@napi-rs/keyring-linux-x64-gnu@1.2.0", "", { "os": "linux", "cpu": "x64" }, "sha512-8E+7z4tbxSJXxIBqA+vfB1CGajpCDRyTyqXkBig5NtASrv4YXcntSo96Iah2QDR5zD3dSTsmbqJudcj9rKKuHQ=="], - - "@napi-rs/keyring-linux-x64-musl": ["@napi-rs/keyring-linux-x64-musl@1.2.0", "", { "os": "linux", "cpu": "x64" }, "sha512-8RZ8yVEnmWr/3BxKgBSzmgntI7lNEsY7xouNfOsQkuVAiCNmxzJwETspzK3PQ2FHtDxgz5vHQDEBVGMyM4hUHA=="], - - "@napi-rs/keyring-win32-arm64-msvc": ["@napi-rs/keyring-win32-arm64-msvc@1.2.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-AoqaDZpQ6KPE19VBLpxyORcp+yWmHI9Xs9Oo0PJ4mfHma4nFSLVdhAubJCxdlNptHe5va7ghGCHj3L9Akiv4cQ=="], - - "@napi-rs/keyring-win32-ia32-msvc": ["@napi-rs/keyring-win32-ia32-msvc@1.2.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-EYL+EEI6bCsYi3LfwcQdnX3P/R76ENKNn+3PmpGheBsUFLuh0gQuP7aMVHM4rTw6UVe+L3vCLZSptq/oeacz0A=="], - - "@napi-rs/keyring-win32-x64-msvc": ["@napi-rs/keyring-win32-x64-msvc@1.2.0", "", { "os": "win32", "cpu": "x64" }, "sha512-xFlx/TsmqmCwNU9v+AVnEJgoEAlBYgzFF5Ihz1rMpPAt4qQWWkMd4sCyM1gMJ1A/GnRqRegDiQpwaxGUHFtFbA=="], - - "@neon-rs/load": ["@neon-rs/load@0.0.4", "", {}, "sha512-kTPhdZyTQxB+2wpiRcFWrDcejc4JI6tkPuS7UZCG4l6Zvc5kU/gGQ/ozvHTh1XR5tS+UlfAfGuPajjzQjCiHCw=="], - - "@noble/ciphers": ["@noble/ciphers@2.1.1", "", {}, "sha512-bysYuiVfhxNJuldNXlFEitTVdNnYUc+XNJZd7Qm2a5j1vZHgY+fazadNFWFaMK/2vye0JVlxV3gHmC0WDfAOQw=="], - - "@noble/curves": ["@noble/curves@2.0.1", "", { "dependencies": { "@noble/hashes": "2.0.1" } }, "sha512-vs1Az2OOTBiP4q0pwjW5aF0xp9n4MxVrmkFBxc6EKZc6ddYx5gaZiAsZoq0uRRXWbi3AT/sBqn05eRPtn1JCPw=="], - - "@noble/hashes": ["@noble/hashes@2.0.1", "", {}, "sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw=="], - - "@scure/base": ["@scure/base@2.0.0", "", {}, "sha512-3E1kpuZginKkek01ovG8krQ0Z44E3DHPjc5S2rjJw9lZn3KSQOs8S7wqikF/AH7iRanHypj85uGyxk0XAyC37w=="], - - "@scure/bip32": ["@scure/bip32@2.0.1", "", { "dependencies": { "@noble/curves": "2.0.1", "@noble/hashes": "2.0.1", "@scure/base": "2.0.0" } }, "sha512-4Md1NI5BzoVP+bhyJaY3K6yMesEFzNS1sE/cP+9nuvE7p/b0kx9XbpDHHFl8dHtufcbdHRUUQdRqLIPHN/s7yA=="], - - "@scure/bip39": ["@scure/bip39@2.0.1", "", { "dependencies": { "@noble/hashes": "2.0.1", "@scure/base": "2.0.0" } }, "sha512-PsxdFj/d2AcJcZDX1FXN3dDgitDDTmwf78rKZq1a6c1P1Nan1X/Sxc7667zU3U+AN60g7SxxP0YCVw2H/hBycg=="], - - "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], - - "@types/better-sqlite3": ["@types/better-sqlite3@7.6.13", "", { "dependencies": { "@types/node": "*" } }, "sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA=="], - - "@types/node": ["@types/node@25.0.10", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg=="], - - "@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="], - - "@zorsh/zorsh": ["@zorsh/zorsh@0.4.0", "", {}, "sha512-lDzbhmGFgk5yQOI78v99186nyk+6XW9rGW2BTz/RT1gVM+oiUTtDoTg5/tUYFe+u/+cy9XPmpWWFgRyfYfQcUA=="], - - "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], - - "better-auth": ["better-auth@1.4.17", "", { "dependencies": { "@better-auth/core": "1.4.17", "@better-auth/telemetry": "1.4.17", "@better-auth/utils": "0.3.0", "@better-fetch/fetch": "1.1.21", "@noble/ciphers": "^2.0.0", "@noble/hashes": "^2.0.0", "better-call": "1.1.8", "defu": "^6.1.4", "jose": "^6.1.0", "kysely": "^0.28.5", "nanostores": "^1.0.1", "zod": "^4.3.5" }, "peerDependencies": { "@lynx-js/react": "*", "@prisma/client": "^5.0.0 || ^6.0.0 || ^7.0.0", "@sveltejs/kit": "^2.0.0", "@tanstack/react-start": "^1.0.0", "@tanstack/solid-start": "^1.0.0", "better-sqlite3": "^12.0.0", "drizzle-kit": ">=0.31.4", "drizzle-orm": ">=0.41.0", "mongodb": "^6.0.0 || ^7.0.0", "mysql2": "^3.0.0", "next": "^14.0.0 || ^15.0.0 || ^16.0.0", "pg": "^8.0.0", "prisma": "^5.0.0 || ^6.0.0 || ^7.0.0", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0", "solid-js": "^1.0.0", "svelte": "^4.0.0 || ^5.0.0", "vitest": "^2.0.0 || ^3.0.0 || ^4.0.0", "vue": "^3.0.0" }, "optionalPeers": ["@lynx-js/react", "@prisma/client", "@sveltejs/kit", "@tanstack/react-start", "@tanstack/solid-start", "better-sqlite3", "drizzle-kit", "drizzle-orm", "mongodb", "mysql2", "next", "pg", "prisma", "react", "react-dom", "solid-js", "svelte", "vitest", "vue"] }, "sha512-VmHGQyKsEahkEs37qguROKg/6ypYpNF13D7v/lkbO7w7Aivz0Bv2h+VyUkH4NzrGY0QBKXi1577mGhDCVwp0ew=="], - - "better-call": ["better-call@1.1.8", "", { "dependencies": { "@better-auth/utils": "^0.3.0", "@better-fetch/fetch": "^1.1.4", "rou3": "^0.7.10", "set-cookie-parser": "^2.7.1" }, "peerDependencies": { "zod": "^4.0.0" }, "optionalPeers": ["zod"] }, "sha512-XMQ2rs6FNXasGNfMjzbyroSwKwYbZ/T3IxruSS6U2MJRsSYh3wYtG3o6H00ZlKZ/C/UPOAD97tqgQJNsxyeTXw=="], - - "better-near-auth": ["better-near-auth@0.3.4", "", { "dependencies": { "@hot-labs/near-connect": "^0.8.2", "nanostores": "^1.1.0", "near-kit": "^0.7.0", "near-sign-verify": "^0.4.5", "zod": "^4.3.5" }, "peerDependencies": { "better-auth": "^1.4.4", "typescript": "^5.9.3" } }, "sha512-ZdidOGwujITiyVqF3QbSJcGQ3/jWE4o2QnmOs4N0DaeYpTCtPh9XkW640BbMrY7S4zaiYcPVEzqNBB7g7Gfibg=="], - - "better-sqlite3": ["better-sqlite3@12.6.2", "", { "dependencies": { "bindings": "^1.5.0", "prebuild-install": "^7.1.1" } }, "sha512-8VYKM3MjCa9WcaSAI3hzwhmyHVlH8tiGFwf0RlTsZPWJ1I5MkzjiudCo4KC4DxOaL/53A5B1sI/IbldNFDbsKA=="], - - "bindings": ["bindings@1.5.0", "", { "dependencies": { "file-uri-to-path": "1.0.0" } }, "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ=="], - - "bl": ["bl@4.1.0", "", { "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="], - - "buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="], - - "chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="], - - "cross-fetch": ["cross-fetch@4.1.0", "", { "dependencies": { "node-fetch": "^2.7.0" } }, "sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw=="], - - "data-uri-to-buffer": ["data-uri-to-buffer@4.0.1", "", {}, "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A=="], - - "decompress-response": ["decompress-response@6.0.0", "", { "dependencies": { "mimic-response": "^3.1.0" } }, "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ=="], - - "deep-extend": ["deep-extend@0.6.0", "", {}, "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="], - - "defu": ["defu@6.1.4", "", {}, "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="], - - "detect-libc": ["detect-libc@2.0.2", "", {}, "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw=="], - - "drizzle-orm": ["drizzle-orm@0.45.1", "", { "peerDependencies": { "@aws-sdk/client-rds-data": ">=3", "@cloudflare/workers-types": ">=4", "@electric-sql/pglite": ">=0.2.0", "@libsql/client": ">=0.10.0", "@libsql/client-wasm": ">=0.10.0", "@neondatabase/serverless": ">=0.10.0", "@op-engineering/op-sqlite": ">=2", "@opentelemetry/api": "^1.4.1", "@planetscale/database": ">=1.13", "@prisma/client": "*", "@tidbcloud/serverless": "*", "@types/better-sqlite3": "*", "@types/pg": "*", "@types/sql.js": "*", "@upstash/redis": ">=1.34.7", "@vercel/postgres": ">=0.8.0", "@xata.io/client": "*", "better-sqlite3": ">=7", "bun-types": "*", "expo-sqlite": ">=14.0.0", "gel": ">=2", "knex": "*", "kysely": "*", "mysql2": ">=2", "pg": ">=8", "postgres": ">=3", "sql.js": ">=1", "sqlite3": ">=5" }, "optionalPeers": ["@aws-sdk/client-rds-data", "@cloudflare/workers-types", "@electric-sql/pglite", "@libsql/client", "@libsql/client-wasm", "@neondatabase/serverless", "@op-engineering/op-sqlite", "@opentelemetry/api", "@planetscale/database", "@prisma/client", "@tidbcloud/serverless", "@types/better-sqlite3", "@types/pg", "@types/sql.js", "@upstash/redis", "@vercel/postgres", "@xata.io/client", "better-sqlite3", "bun-types", "expo-sqlite", "gel", "knex", "kysely", "mysql2", "pg", "postgres", "sql.js", "sqlite3"] }, "sha512-Te0FOdKIistGNPMq2jscdqngBRfBpC8uMFVwqjf6gtTVJHIQ/dosgV/CLBU2N4ZJBsXL5savCba9b0YJskKdcA=="], - - "end-of-stream": ["end-of-stream@1.4.5", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg=="], - - "expand-template": ["expand-template@2.0.3", "", {}, "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="], - - "fetch-blob": ["fetch-blob@3.2.0", "", { "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" } }, "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ=="], - - "file-uri-to-path": ["file-uri-to-path@1.0.0", "", {}, "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="], - - "formdata-polyfill": ["formdata-polyfill@4.0.10", "", { "dependencies": { "fetch-blob": "^3.1.2" } }, "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g=="], - - "fs-constants": ["fs-constants@1.0.0", "", {}, "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="], - - "github-from-package": ["github-from-package@0.0.0", "", {}, "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw=="], - - "hono": ["hono@4.11.7", "", {}, "sha512-l7qMiNee7t82bH3SeyUCt9UF15EVmaBvsppY2zQtrbIhl/yzBTny+YUxsVjSjQ6gaqaeVtZmGocom8TzBlA4Yw=="], - - "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], - - "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], - - "ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="], - - "jose": ["jose@6.1.3", "", {}, "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ=="], - - "js-base64": ["js-base64@3.7.8", "", {}, "sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow=="], - - "kysely": ["kysely@0.28.10", "", {}, "sha512-ksNxfzIW77OcZ+QWSAPC7yDqUSaIVwkTWnTPNiIy//vifNbwsSgQ57OkkncHxxpcBHM3LRfLAZVEh7kjq5twVA=="], - - "libsql": ["libsql@0.5.22", "", { "dependencies": { "@neon-rs/load": "^0.0.4", "detect-libc": "2.0.2" }, "optionalDependencies": { "@libsql/darwin-arm64": "0.5.22", "@libsql/darwin-x64": "0.5.22", "@libsql/linux-arm-gnueabihf": "0.5.22", "@libsql/linux-arm-musleabihf": "0.5.22", "@libsql/linux-arm64-gnu": "0.5.22", "@libsql/linux-arm64-musl": "0.5.22", "@libsql/linux-x64-gnu": "0.5.22", "@libsql/linux-x64-musl": "0.5.22", "@libsql/win32-x64-msvc": "0.5.22" }, "os": [ "linux", "win32", "darwin", ], "cpu": [ "arm", "x64", "arm64", ] }, "sha512-NscWthMQt7fpU8lqd7LXMvT9pi+KhhmTHAJWUB/Lj6MWa0MKFv0F2V4C6WKKpjCVZl0VwcDz4nOI3CyaT1DDiA=="], - - "mimic-response": ["mimic-response@3.1.0", "", {}, "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="], - - "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], - - "minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], - - "minizlib": ["minizlib@3.1.0", "", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw=="], - - "mkdirp-classic": ["mkdirp-classic@0.5.3", "", {}, "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="], - - "nanostores": ["nanostores@1.1.0", "", {}, "sha512-yJBmDJr18xy47dbNVlHcgdPrulSn1nhSE6Ns9vTG+Nx9VPT6iV1MD6aQFp/t52zpf82FhLLTXAXr30NuCnxvwA=="], - - "napi-build-utils": ["napi-build-utils@2.0.0", "", {}, "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA=="], - - "near-kit": ["near-kit@0.7.0", "", { "dependencies": { "@napi-rs/keyring": "^1.2.0", "@noble/curves": "^2.0.1", "@noble/hashes": "^2.0.1", "@scure/base": "^2.0.0", "@scure/bip32": "^2.0.1", "@scure/bip39": "^2.0.1", "@zorsh/zorsh": "^0.4.0", "tar": "^7.5.2", "zod": "^4.1.12" }, "peerDependencies": { "@hot-labs/near-connect": ">=0.6.0", "@near-wallet-selector/core": ">=8.0.0" }, "optionalPeers": ["@hot-labs/near-connect", "@near-wallet-selector/core"] }, "sha512-mqE9i8QRq0DV/mky8RY+tO1kdx+yuQm3X5nuWiJAXqrfVe17SvDoMjyZpDWENSUqempiU22kBQX4OjGod4B96g=="], - - "near-sign-verify": ["near-sign-verify@0.4.5", "", { "dependencies": { "@noble/curves": "^2.0.1", "@noble/hashes": "^2.0.1", "@scure/base": "^2.0.0", "@zorsh/zorsh": "^0.3.3" } }, "sha512-AOcPcKywejmKgCdQN4j77EyanK8AsIoZQzZnpKSk9wQMmxEi/97+GbDMb4p0TSAJEOuumfJRtJ5V5VYp+2qbKg=="], - - "node-abi": ["node-abi@3.87.0", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-+CGM1L1CgmtheLcBuleyYOn7NWPVu0s0EJH2C4puxgEZb9h8QpR9G2dBfZJOAUhi7VQxuBPMd0hiISWcTyiYyQ=="], - - "node-domexception": ["node-domexception@1.0.0", "", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="], - - "node-fetch": ["node-fetch@3.3.2", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="], - - "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], - - "prebuild-install": ["prebuild-install@7.1.3", "", { "dependencies": { "detect-libc": "^2.0.0", "expand-template": "^2.0.3", "github-from-package": "0.0.0", "minimist": "^1.2.3", "mkdirp-classic": "^0.5.3", "napi-build-utils": "^2.0.0", "node-abi": "^3.3.0", "pump": "^3.0.0", "rc": "^1.2.7", "simple-get": "^4.0.0", "tar-fs": "^2.0.0", "tunnel-agent": "^0.6.0" }, "bin": { "prebuild-install": "bin.js" } }, "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug=="], - - "promise-limit": ["promise-limit@2.7.0", "", {}, "sha512-7nJ6v5lnJsXwGprnGXga4wx6d1POjvi5Qmf1ivTRxTjH4Z/9Czja/UCMLVmB9N93GeWOU93XaFaEt6jbuoagNw=="], - - "pump": ["pump@3.0.3", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA=="], - - "rc": ["rc@1.2.8", "", { "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" }, "bin": { "rc": "./cli.js" } }, "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw=="], - - "readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], - - "rou3": ["rou3@0.7.12", "", {}, "sha512-iFE4hLDuloSWcD7mjdCDhx2bKcIsYbtOTpfH5MHHLSKMOUyjqQXTeZVa289uuwEGEKFoE/BAPbhaU4B774nceg=="], - - "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], - - "semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], - - "set-cookie-parser": ["set-cookie-parser@2.7.2", "", {}, "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw=="], - - "simple-concat": ["simple-concat@1.0.1", "", {}, "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="], - - "simple-get": ["simple-get@4.0.1", "", { "dependencies": { "decompress-response": "^6.0.0", "once": "^1.3.1", "simple-concat": "^1.0.0" } }, "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA=="], - - "string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="], - - "strip-json-comments": ["strip-json-comments@2.0.1", "", {}, "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="], - - "tar": ["tar@7.5.7", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.1.0", "yallist": "^5.0.0" } }, "sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ=="], - - "tar-fs": ["tar-fs@2.1.4", "", { "dependencies": { "chownr": "^1.1.1", "mkdirp-classic": "^0.5.2", "pump": "^3.0.0", "tar-stream": "^2.1.4" } }, "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ=="], - - "tar-stream": ["tar-stream@2.2.0", "", { "dependencies": { "bl": "^4.0.3", "end-of-stream": "^1.4.1", "fs-constants": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.1.1" } }, "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ=="], - - "tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="], - - "tunnel-agent": ["tunnel-agent@0.6.0", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w=="], - - "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], - - "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], - - "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], - - "web-streams-polyfill": ["web-streams-polyfill@3.3.3", "", {}, "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw=="], - - "webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="], - - "whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="], - - "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], - - "ws": ["ws@8.19.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg=="], - - "yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="], - - "zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], - - "cross-fetch/node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="], - - "near-sign-verify/@zorsh/zorsh": ["@zorsh/zorsh@0.3.3", "", {}, "sha512-Fu+iihpwuWPki/I93Bnocj4wmsDaOIfplzvbWGY1zvkGkvPq7y+npE2/Sub0bJJ0EB2C6fH/JvHkbH9B+9OIDQ=="], - - "prebuild-install/detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], - - "tar-fs/chownr": ["chownr@1.1.4", "", {}, "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="], - } -} diff --git a/host/package.json b/host/package.json deleted file mode 100644 index e1e1b1e..0000000 --- a/host/package.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "host", - "type": "module", - "scripts": { - "dev": "bun run server.ts", - "start": "NODE_ENV=production bun run server.ts" - }, - "dependencies": { - "@hono/node-server": "^1.19.9", - "@libsql/client": "^0.17.0", - "@types/better-sqlite3": "^7.6.13", - "better-auth": "^1.4.15", - "better-near-auth": "^0.3.4", - "better-sqlite3": "^12.6.2", - "drizzle-orm": "^0.45.1", - "hono": "^4.6.0" - } -} diff --git a/host/server.ts b/host/server.ts deleted file mode 100644 index 02f3a2d..0000000 --- a/host/server.ts +++ /dev/null @@ -1,214 +0,0 @@ -import { Hono } from "hono"; -import { cors } from "hono/cors"; -import { serve } from "@hono/node-server"; -import { auth } from "./src/auth"; -import { readFileSync, existsSync } from "fs"; -import { fileURLToPath } from "url"; - -const app = new Hono(); - -const PORT = Number(process.env.PORT) || 3000; -const API_URL = process.env.API_URL || "http://localhost:3014"; -const UI_URL = process.env.UI_URL || "http://localhost:3002"; - -// Development mode check -const isDev = process.env.NODE_ENV !== "production"; - -// CORS middleware - handle credentials properly -app.use( - "*", - cors({ - origin: (origin) => origin || `http://localhost:${PORT}`, - credentials: true, - allowMethods: ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"], - allowHeaders: ["Content-Type", "Authorization", "Accept", "X-Requested-With"], - exposeHeaders: ["Content-Length", "Content-Type", "Set-Cookie"], - }) -); - -// Health check -app.get("/health", (c) => c.text("OK")); - -// Better Auth handler - handle all /api/auth/* requests -app.all("/api/auth/*", async (c) => { - const response = await auth.handler(c.req.raw); - return response; -}); - -// Also handle /auth/* for backwards compatibility -app.all("/auth/*", async (c) => { - const response = await auth.handler(c.req.raw); - return response; -}); - -// Proxy all /api/* requests (except /api/auth/*) -app.all("/api/*", async (c) => { - const path = c.req.path; - const url = new URL(c.req.url); - const fullPath = path + url.search; - - console.log(`[API Proxy] ${c.req.method} ${path} -> ${API_URL}${fullPath}`); - return proxyRequest(c, `${API_URL}${fullPath}`, true); -}); - -// Helper function to get session from request -async function getSessionFromRequest(request: Request): Promise<{ nearAccountId?: string; role?: string } | null> { - try { - const session = await auth.api.getSession({ headers: request.headers }); - if (session?.user) { - // Get nearAccountId from the user's name (set during NEAR sign-in) - const nearAccountId = session.user.name; - const role = session.user.role || undefined; - return { nearAccountId, role }; - } - return null; - } catch (error) { - console.error("[Auth] Error getting session:", error); - return null; - } -} - -// Helper function to proxy requests -async function proxyRequest(c: any, targetUrl: string, injectContext = false) { - try { - const headers = new Headers(); - c.req.raw.headers.forEach((value: string, key: string) => { - if (!["host", "connection"].includes(key.toLowerCase())) { - headers.set(key, value); - } - }); - - // Extract session and inject context - if (injectContext) { - const sessionContext = await getSessionFromRequest(c.req.raw); - if (sessionContext?.nearAccountId) { - headers.set("x-near-account-id", sessionContext.nearAccountId); - if (sessionContext.role) { - headers.set("x-user-role", sessionContext.role); - } - console.log(`[Auth] Injecting context for user: ${sessionContext.nearAccountId}`); - } else if (isDev) { - // Only fallback in dev mode when no session - const devUser = process.env.DEV_USER || "test.near"; - headers.set("x-near-account-id", devUser); - console.log(`[Dev Mode] No session, using fallback user: ${devUser}`); - } - } - - const response = await fetch(targetUrl, { - method: c.req.method, - headers, - body: c.req.method !== "GET" && c.req.method !== "HEAD" ? c.req.raw.body : undefined, - // @ts-ignore - duplex: c.req.method !== "GET" && c.req.method !== "HEAD" ? "half" : undefined, - }); - - const contentType = response.headers.get("content-type") || ""; - - // For SSE streaming responses - if (contentType.includes("text/event-stream")) { - return new Response(response.body, { - status: response.status, - headers: { - "Content-Type": "text/event-stream", - "Cache-Control": "no-cache", - "Connection": "keep-alive", - "Access-Control-Allow-Origin": c.req.header("origin") || `http://localhost:${PORT}`, - "Access-Control-Allow-Credentials": "true", - }, - }); - } - - // For regular responses - const responseHeaders = new Headers(); - response.headers.forEach((value, key) => { - responseHeaders.set(key, value); - }); - - return new Response(response.body, { - status: response.status, - headers: responseHeaders, - }); - } catch (error) { - console.error("[Proxy] Error:", error); - return c.json({ error: "Failed to proxy request" }, 502); - } -} - -// Main router - check path manually for better control -app.all("*", async (c) => { - const path = c.req.path; - const url = new URL(c.req.url); - const fullPath = path + url.search; - - // Handle API requests - inject auth context - if (path.startsWith("/api/") || path === "/api") { - console.log(`[API Proxy] ${c.req.method} ${path} -> ${API_URL}${fullPath}`); - return proxyRequest(c, `${API_URL}${fullPath}`, true); - } - - // In development, proxy other requests to UI dev server - if (isDev) { - try { - const response = await fetch(`${UI_URL}${fullPath}`, { - headers: c.req.raw.headers, - }); - - const responseHeaders = new Headers(); - response.headers.forEach((value, key) => { - responseHeaders.set(key, value); - }); - - const contentType = response.headers.get("content-type") || ""; - - // Inject runtime config into HTML responses - if (contentType.includes("text/html")) { - let html = await response.text(); - const runtimeConfig = { - apiUrl: `http://localhost:${PORT}/api`, - assetsUrl: UI_URL, - }; - const configScript = ``; - html = html.replace("", `${configScript}`); - return new Response(html, { - status: response.status, - headers: { "Content-Type": "text/html" }, - }); - } - - return new Response(response.body, { - status: response.status, - headers: responseHeaders, - }); - } catch { - // Fallback to local index.html - } - } - - // Serve static index.html - try { - const indexPath = fileURLToPath(new URL("./dist/index.html", import.meta.url)); - if (existsSync(indexPath)) { - const content = readFileSync(indexPath, "utf-8"); - return new Response(content, { - headers: { "Content-Type": "text/html" }, - }); - } - } catch { - // ignore - } - - return c.text("Not Found", 404); -}); - -console.log(`[Host] Starting server on port ${PORT}`); -console.log(`[Host] API proxy: ${API_URL}`); -console.log(`[Host] UI proxy: ${UI_URL}`); -console.log(`[Host] Auth: Better Auth enabled`); - -serve({ - fetch: app.fetch, - port: PORT, -}, () => { - console.log(`[Host] Server listening on http://localhost:${PORT}`); -}); diff --git a/host/src/auth.ts b/host/src/auth.ts deleted file mode 100644 index a1f340f..0000000 --- a/host/src/auth.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { betterAuth } from "better-auth"; -import { admin } from "better-auth/plugins"; -import { siwn } from "better-near-auth"; -import Database from "better-sqlite3"; - -const db = new Database(process.env.HOST_DATABASE_URL?.replace("file:", "") || "./database.db"); - -export const auth = betterAuth({ - database: db, - basePath: "/api/auth", // Default Better Auth path - baseURL: process.env.BETTER_AUTH_URL || "http://localhost:3000", - secret: process.env.BETTER_AUTH_SECRET || "dev-secret-change-in-production", - trustedOrigins: [ - "http://localhost:3000", - "http://localhost:3002", - ], - plugins: [ - siwn({ - domain: process.env.NEAR_ACCOUNT || "example.near", - networkId: "mainnet", - }), - admin(), - ], -}); - -export type Auth = typeof auth; diff --git a/package.json b/package.json index ae8d024..9fb0128 100644 --- a/package.json +++ b/package.json @@ -7,17 +7,17 @@ "ui" ], "catalog": { - "react": "^19.2.3", - "react-dom": "^19.2.3", - "@tanstack/react-query": "^5.90.11", - "@tanstack/react-router": "^1.153.2", - "near-kit": "^0.10.0", - "@hot-labs/near-connect": "^0.8.2", + "react": "19.2.4", + "react-dom": "19.2.4", + "@tanstack/react-query": "5.90.20", + "@tanstack/react-router": "1.157.16", + "near-kit": "0.10.0", + "@hot-labs/near-connect": "0.8.2", "drizzle-orm": "^0.45.1", "drizzle-kit": "^0.31.8", "@libsql/client": "^0.17.0", - "better-auth": "^1.4.15", - "better-near-auth": "^0.3.4", + "better-auth": "1.4.17", + "better-near-auth": "0.3.4", "every-plugin": "0.8.8", "typescript": "^5.9.3", "vitest": "^4.0.17", @@ -30,21 +30,20 @@ "scripts": { "init": "bun run db:migrate", "bos": "bos", - "dev": "for port in 3000 3002 3005 3014; do lsof -ti :$port 2>/dev/null | xargs kill -9 2>/dev/null || true; done && bos dev --no-interactive", + "dev": "bos dev", "dev:ui": "bos dev --host=remote --api=remote", "dev:api": "bos dev", "dev:host": "bos dev", "build": "bos build", "build:api": "bun run --cwd api rsbuild build", - "build:host": "bun run --cwd host rsbuild build", "build:ui": "bun run --cwd ui rsbuild build", "deploy": "NODE_ENV=production bun run build:ui && bun run build:api", - "typecheck": "bun run --cwd host tsc --noEmit && bun run --cwd ui tsc --noEmit && bun run --cwd api tsc --noEmit", + "typecheck": "bun run --cwd ui tsc --noEmit && bun run --cwd api tsc --noEmit", "start": "NODE_ENV=production bos start --no-interactive", - "db:push": "bun run --cwd host drizzle-kit push", - "db:studio": "bun run --cwd host drizzle-kit studio", - "db:generate": "bun run --cwd host drizzle-kit generate", - "db:migrate": "bun run --cwd host drizzle-kit migrate" + "db:push": "bun run --cwd api drizzle-kit push", + "db:studio": "bun run --cwd api drizzle-kit studio", + "db:generate": "bun run --cwd api drizzle-kit generate", + "db:migrate": "bun run --cwd api drizzle-kit migrate" }, "packageManager": "bun@1.2.20", "module": "index.ts", @@ -58,4 +57,4 @@ "dependencies": { "everything-dev": "^0.0.8" } -} +} \ No newline at end of file diff --git a/ui/package.json b/ui/package.json index a4b16b4..7b20003 100644 --- a/ui/package.json +++ b/ui/package.json @@ -5,16 +5,18 @@ "type": "module", "scripts": { "dev": "rsbuild dev", - "build": "bun run build:client && bun run build:server", + "build": "bun run build:client && bun run build:ssr", "build:client": "BUILD_TARGET=client rsbuild build", "build:server": "BUILD_TARGET=server rsbuild build", "preview": "rsbuild preview", "test": "vitest run", "typecheck": "tsc --noEmit", - "build:types": "tsc --declaration --emitDeclarationOnly --outDir dist/types" + "build:types": "tsc --declaration --emitDeclarationOnly --outDir dist/types", + "deploy": "DEPLOY=true bun run build", + "build:ssr": "BUILD_TARGET=server rsbuild build" }, "dependencies": { - "@hot-labs/near-connect": "^0.8.2", + "@hot-labs/near-connect": "catalog:", "@orpc/client": "^1.12.3", "@orpc/contract": "^1.13.4", "@radix-ui/react-aspect-ratio": "^1.1.8", @@ -30,46 +32,46 @@ "@radix-ui/react-separator": "^1.1.8", "@radix-ui/react-slot": "^1.2.4", "@radix-ui/react-tabs": "^1.1.13", - "@tanstack/react-devtools": "^0.9.2", + "@tanstack/react-devtools": "^0.7.11", "@tanstack/react-query": "catalog:", "@tanstack/react-query-devtools": "^5.91.1", "@tanstack/react-router": "catalog:", - "@tanstack/react-router-devtools": "^1.157.15", + "@tanstack/react-router-devtools": "^1.139.14", "@tanstack/react-table": "^8.21.3", "better-auth": "catalog:", "better-near-auth": "catalog:", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", - "lucide-react": "^0.563.0", - "near-kit": "^0.10.0", + "lucide-react": "^0.544.0", + "near-kit": "catalog:", "near-social-js": "^2.0.4", "next-themes": "^0.4.6", - "react": "^19.2.3", - "react-dom": "^19.2.3", + "react": "catalog:", + "react-dom": "catalog:", "sonner": "^2.0.7", "tailwind-merge": "^3.4.0", "tw-animate-css": "^1.4.0", "zustand": "^5.0.9" }, "peerDependencies": { - "@hot-labs/near-connect": "^0.8.x", - "@tanstack/react-query": "^5.x", - "@tanstack/react-router": "^1.x", - "near-kit": "^0.10.0", - "react": "^19.2.0", - "react-dom": "^19.2.0" + "@hot-labs/near-connect": "catalog:", + "@tanstack/react-query": "catalog:", + "@tanstack/react-router": "catalog:", + "near-kit": "catalog:", + "react": "catalog:", + "react-dom": "catalog:" }, "devDependencies": { "every-plugin": "catalog:", - "@module-federation/enhanced": "^0.23.0", + "@module-federation/enhanced": "^0.21.6", "@module-federation/node": "^2.7.26", - "@module-federation/rsbuild-plugin": "^0.23.0", + "@module-federation/rsbuild-plugin": "^0.21.6", "@rsbuild/core": "^1.6.12", "@rsbuild/plugin-react": "^1.4.2", "@tailwindcss/postcss": "^4.1.17", - "@tanstack/router-plugin": "^1.157.15", + "@tanstack/router-plugin": "^1.139.14", "@testing-library/dom": "^10.4.1", - "@testing-library/react": "^16.3.2", + "@testing-library/react": "^16.3.0", "@types/node": "catalog:", "@types/react": "catalog:", "@types/react-dom": "catalog:", @@ -79,4 +81,4 @@ "vitest": "catalog:", "zephyr-rsbuild-plugin": "^0.1.2" } -} +} \ No newline at end of file diff --git a/ui/rsbuild.config.ts b/ui/rsbuild.config.ts index ca19150..0b5f852 100644 --- a/ui/rsbuild.config.ts +++ b/ui/rsbuild.config.ts @@ -6,17 +6,20 @@ import { pluginModuleFederation } from "@module-federation/rsbuild-plugin"; import { defineConfig } from "@rsbuild/core"; import { pluginReact } from "@rsbuild/plugin-react"; import { TanStackRouterRspack } from "@tanstack/router-plugin/rspack"; -import { getUISharedDependencies } from "every-plugin/build/rspack"; import { withZephyr } from "zephyr-rsbuild-plugin"; import pkg from "./package.json"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const normalizedName = pkg.name; -const isProduction = process.env.NODE_ENV === "production"; +const shouldDeploy = process.env.DEPLOY === "true"; const buildTarget = process.env.BUILD_TARGET as "client" | "server" | undefined; const isServerBuild = buildTarget === "server"; +const bosConfigPath = path.resolve(__dirname, "../bos.config.json"); +const bosConfig = JSON.parse(fs.readFileSync(bosConfigPath, "utf8")); +const uiSharedDeps = bosConfig.shared?.ui ?? {}; + function updateBosConfig(field: "production" | "ssr", url: string) { try { const configPath = path.resolve(__dirname, "../bos.config.json"); @@ -33,13 +36,11 @@ function updateBosConfig(field: "production" | "ssr", url: string) { } catch (err) { console.error( " ❌ Failed to update bos.config.json:", - (err as Error).message, + (err as Error).message ); } } -const uiSharedDeps = getUISharedDependencies(); - function createClientConfig() { const plugins = [ pluginReact(), @@ -50,6 +51,7 @@ function createClientConfig() { exposes: { "./Router": "./src/router.tsx", "./Hydrate": "./src/hydrate.tsx", + "./remote": "./src/remote/index.ts", "./components": "./src/components/index.ts", "./providers": "./src/providers/index.tsx", "./hooks": "./src/hooks/index.ts", @@ -59,7 +61,7 @@ function createClientConfig() { }), ]; - if (isProduction) { + if (shouldDeploy) { plugins.push( withZephyr({ hooks: { @@ -68,7 +70,7 @@ function createClientConfig() { updateBosConfig("production", info.url); }, }, - }), + }) ); } @@ -99,16 +101,6 @@ function createClientConfig() { "Access-Control-Allow-Methods": "GET, POST, OPTIONS", "Access-Control-Allow-Headers": "Content-Type", }, - proxy: { - "/api": { - target: "http://localhost:3014", - changeOrigin: true, - }, - "/auth": { - target: "http://localhost:3000", - changeOrigin: true, - }, - }, publicDir: { name: "dist", copyOnBuild: false, @@ -122,9 +114,6 @@ function createClientConfig() { }, infrastructureLogging: { level: "error" }, stats: "errors-warnings", - watchOptions: { - ignored: ["**/routeTree.gen.ts"], - }, plugins: [ TanStackRouterRspack({ target: "react", @@ -145,7 +134,7 @@ function createClientConfig() { function createServerConfig() { const plugins = [pluginReact()]; - if (isProduction) { + if (shouldDeploy) { plugins.push( withZephyr({ hooks: { @@ -154,7 +143,7 @@ function createServerConfig() { updateBosConfig("ssr", info.url); }, }, - }), + }) ); } @@ -180,21 +169,18 @@ function createServerConfig() { publicPath: "/", library: { type: "commonjs-module" }, }, - externals: [/^node:/], + externals: [ + /^node:/, + ], infrastructureLogging: { level: "error" }, stats: "errors-warnings", - watchOptions: { - ignored: ["**/routeTree.gen.ts"], - }, plugins: [ TanStackRouterRspack({ target: "react", autoCodeSplitting: false }), new ModuleFederationPlugin({ name: normalizedName, filename: "remoteEntry.server.js", dts: false, - runtimePlugins: [ - require.resolve("@module-federation/node/runtimePlugin"), - ], + runtimePlugins: [require.resolve("@module-federation/node/runtimePlugin")], library: { type: "commonjs-module" }, exposes: { "./Router": "./src/router.server.tsx" }, shared: uiSharedDeps, diff --git a/ui/src/hydrate.tsx b/ui/src/hydrate.tsx index 59176f7..e7cefb6 100644 --- a/ui/src/hydrate.tsx +++ b/ui/src/hydrate.tsx @@ -1,39 +1,35 @@ -import type { ClientRuntimeConfig } from "./types"; +import { getAssetsUrl, getRuntimeConfig } from "./remote/runtime"; export async function hydrate() { - const runtimeConfig = (window as any).__RUNTIME_CONFIG__ as ClientRuntimeConfig | undefined; + console.log("[Hydrate] Starting..."); + const runtimeConfig = getRuntimeConfig(); if (!runtimeConfig) { - console.error("[Hydrate] No runtime config"); + console.error("[Hydrate] No runtime config found"); return; } - const { createRoot } = await import("react-dom/client"); - const { RouterProvider } = await import("@tanstack/react-router"); + const { hydrateRoot } = await import("react-dom/client"); + const { RouterClient } = await import("@tanstack/react-router/ssr/client"); const { QueryClientProvider } = await import("@tanstack/react-query"); const { createRouter } = await import("./router"); const { router, queryClient } = createRouter({ context: { - assetsUrl: runtimeConfig.assetsUrl, + assetsUrl: getAssetsUrl(runtimeConfig), runtimeConfig, }, }); - const root = document.getElementById("root"); - if (root) { - createRoot(root).render( - - - - ); - } + console.log("[Hydrate] Calling hydrateRoot..."); + hydrateRoot( + document, + + + , + ); + + console.log("[Hydrate] Complete!"); } export default hydrate; - -// Run once -if (typeof window !== "undefined" && (window as any).__RUNTIME_CONFIG__ && !(window as any).__HYDRATED__) { - (window as any).__HYDRATED__ = true; - hydrate(); -} diff --git a/ui/src/lib/auth-client.ts b/ui/src/lib/auth-client.ts index 41d55f1..2a7515b 100644 --- a/ui/src/lib/auth-client.ts +++ b/ui/src/lib/auth-client.ts @@ -1,35 +1 @@ -import { adminClient } from "better-auth/client/plugins"; -import { createAuthClient as createBetterAuthClient } from "better-auth/react"; -import { siwnClient } from "better-near-auth/client"; - -function getAuthBaseUrl(): string { - if (typeof window === "undefined") return ""; - return window.__RUNTIME_CONFIG__?.hostUrl ?? window.location.origin; -} - -function createAuthClient() { - return createBetterAuthClient({ - baseURL: getAuthBaseUrl(), - fetchOptions: { credentials: "include" }, - plugins: [ - siwnClient({ - domain: typeof window !== "undefined" - ? (window.__RUNTIME_CONFIG__?.account ?? "every.near") - : "every.near", - networkId: "mainnet", - }), - adminClient(), - ], - }); -} - -let _authClient: ReturnType | undefined; - -export function getAuthClient() { - if (_authClient === undefined) { - _authClient = createAuthClient(); - } - return _authClient; -} - -export const authClient = getAuthClient(); +export * from "../remote/auth-client"; diff --git a/ui/src/lib/auth-utils.ts b/ui/src/lib/auth-utils.ts index fdf9c06..fb98af4 100644 --- a/ui/src/lib/auth-utils.ts +++ b/ui/src/lib/auth-utils.ts @@ -1,49 +1 @@ -export function getNearAccountId(linkedAccounts: any[]): string | null { - const nearAccount = linkedAccounts.find(account => account.providerId === 'siwn'); - return (nearAccount?.accountId)?.split(":")[0] || nearAccount?.providerId || null; -} - -export function getLinkedProviders(linkedAccounts: any[]): string[] { - return linkedAccounts.map(account => account.providerId); -} - -export function handleAccountLinkRefresh( - _componentState: any, - _setLinkedAccounts: React.Dispatch>, - refreshAccounts: () => Promise -) { - refreshAccounts(); - - const urlParams = new URLSearchParams(window.location.search); - const hasCallback = urlParams.has('code') || urlParams.has('state') || urlParams.has('callbackUrl'); - - if (hasCallback) { - const cleanUrl = window.location.pathname + window.location.hash; - window.history.replaceState(null, '', cleanUrl); - - setTimeout(() => { - refreshAccounts(); - }, 1000); - } - - return refreshAccounts; -} - -export function getProviderConfig(provider: string) { - switch (provider) { - case 'siwn': - return { - name: 'NEAR', - icon: '🔗', - color: 'text-white', - backgroundColor: 'bg-[#000000]' - }; - default: - return { - name: provider?.charAt(0).toUpperCase() + provider?.slice(1) || "Unknown", - icon: '🔗', - color: 'text-muted-foreground', - backgroundColor: 'bg-gray-100' - }; - } -} +export * from "../remote/auth-utils"; diff --git a/ui/src/remote/auth-client.ts b/ui/src/remote/auth-client.ts new file mode 100644 index 0000000..e0cdc04 --- /dev/null +++ b/ui/src/remote/auth-client.ts @@ -0,0 +1,29 @@ +import { adminClient } from "better-auth/client/plugins"; +import { createAuthClient as createBetterAuthClient } from "better-auth/react"; +import { siwnClient } from "better-near-auth/client"; +import { getAccount, getHostUrl } from "./runtime"; + +function createAuthClient() { + return createBetterAuthClient({ + baseURL: getHostUrl(), + fetchOptions: { credentials: "include" }, + plugins: [ + siwnClient({ + domain: getAccount(), + networkId: "mainnet", + }), + adminClient(), + ], + }); +} + +let _authClient: ReturnType | undefined; + +export function getAuthClient() { + if (_authClient === undefined) { + _authClient = createAuthClient(); + } + return _authClient; +} + +export const authClient = getAuthClient(); diff --git a/ui/src/remote/auth-utils.ts b/ui/src/remote/auth-utils.ts new file mode 100644 index 0000000..fdf9c06 --- /dev/null +++ b/ui/src/remote/auth-utils.ts @@ -0,0 +1,49 @@ +export function getNearAccountId(linkedAccounts: any[]): string | null { + const nearAccount = linkedAccounts.find(account => account.providerId === 'siwn'); + return (nearAccount?.accountId)?.split(":")[0] || nearAccount?.providerId || null; +} + +export function getLinkedProviders(linkedAccounts: any[]): string[] { + return linkedAccounts.map(account => account.providerId); +} + +export function handleAccountLinkRefresh( + _componentState: any, + _setLinkedAccounts: React.Dispatch>, + refreshAccounts: () => Promise +) { + refreshAccounts(); + + const urlParams = new URLSearchParams(window.location.search); + const hasCallback = urlParams.has('code') || urlParams.has('state') || urlParams.has('callbackUrl'); + + if (hasCallback) { + const cleanUrl = window.location.pathname + window.location.hash; + window.history.replaceState(null, '', cleanUrl); + + setTimeout(() => { + refreshAccounts(); + }, 1000); + } + + return refreshAccounts; +} + +export function getProviderConfig(provider: string) { + switch (provider) { + case 'siwn': + return { + name: 'NEAR', + icon: '🔗', + color: 'text-white', + backgroundColor: 'bg-[#000000]' + }; + default: + return { + name: provider?.charAt(0).toUpperCase() + provider?.slice(1) || "Unknown", + icon: '🔗', + color: 'text-muted-foreground', + backgroundColor: 'bg-gray-100' + }; + } +} diff --git a/ui/src/remote/head.ts b/ui/src/remote/head.ts new file mode 100644 index 0000000..2a6a92d --- /dev/null +++ b/ui/src/remote/head.ts @@ -0,0 +1,97 @@ +import type { ClientRuntimeConfig, HeadScript } from "./types"; + +export interface RemoteScriptsOptions { + assetsUrl: string; + runtimeConfig?: ClientRuntimeConfig; + containerName?: string; + hydratePath?: string; +} + +export interface SiteMetadata { + siteName: string; + siteUrl: string; + description: string; +} + +export function getRemoteEntryScript(assetsUrl: string): HeadScript { + return { + src: `${assetsUrl}/remoteEntry.js`, + }; +} + +export function getThemeInitScript(): HeadScript { + return { + children: `(function(){var t=localStorage.getItem('theme');if(t==='dark'||(!t&&window.matchMedia('(prefers-color-scheme: dark)').matches)){document.documentElement.classList.add('dark');}})();`, + }; +} + +export function getStructuredDataScript(metadata: SiteMetadata): HeadScript { + return { + type: "application/ld+json", + children: JSON.stringify({ + "@context": "https://schema.org", + "@type": "WebSite", + name: metadata.siteName, + url: metadata.siteUrl, + description: metadata.description, + }), + }; +} + +export function getHydrateScript( + runtimeConfig: ClientRuntimeConfig | undefined, + containerName = "ui", + hydratePath = "./Hydrate" +): HeadScript { + return { + children: ` +window.__RUNTIME_CONFIG__=${JSON.stringify(runtimeConfig)}; +function __hydrate(){ + var container = window['${containerName}']; + if (!container) { console.error('[Hydrate] Container not found'); return; } + container.init({}).then(function(){ + return container.get('${hydratePath}'); + }).then(function(mod){ + return mod().hydrate(); + }).catch(function(e){ + console.error('[Hydrate] Failed:', e); + }); +} +if(document.readyState==='loading'){document.addEventListener('DOMContentLoaded',__hydrate);}else{__hydrate();} + `.trim(), + }; +} + +export function getRemoteScripts(options: RemoteScriptsOptions): HeadScript[] { + const { assetsUrl, runtimeConfig, containerName, hydratePath } = options; + + return [ + getRemoteEntryScript(assetsUrl), + getThemeInitScript(), + getHydrateScript(runtimeConfig, containerName, hydratePath), + ]; +} + +export function getRemoteScriptsWithMetadata( + options: RemoteScriptsOptions, + metadata: SiteMetadata +): HeadScript[] { + const { assetsUrl, runtimeConfig, containerName, hydratePath } = options; + + return [ + getRemoteEntryScript(assetsUrl), + getThemeInitScript(), + getStructuredDataScript(metadata), + getHydrateScript(runtimeConfig, containerName, hydratePath), + ]; +} + +export function getBaseStyles(): string { + return ` +*, *::before, *::after { box-sizing: border-box; } +html { height: 100%; height: 100dvh; -webkit-text-size-adjust: 100%; text-size-adjust: 100%; color-scheme: light dark; } +body { min-height: 100%; min-height: 100dvh; margin: 0; background-color: var(--background); color: var(--foreground); -webkit-tap-highlight-color: transparent; touch-action: manipulation; } +#root { min-height: 100vh; } +@supports (min-height: 100dvh) { #root { min-height: 100dvh; } } + `.trim(); +} diff --git a/ui/src/remote/index.ts b/ui/src/remote/index.ts new file mode 100644 index 0000000..602c68f --- /dev/null +++ b/ui/src/remote/index.ts @@ -0,0 +1,6 @@ +export * from "./auth-client"; +export * from "./auth-utils"; +export * from "./head"; +export { API_URL, type ApiClient, type ApiContract, apiClient, queryClient } from "./orpc"; +export * from "./runtime"; +export * from "./types"; diff --git a/ui/src/remote/orpc.ts b/ui/src/remote/orpc.ts new file mode 100644 index 0000000..f0fbedc --- /dev/null +++ b/ui/src/remote/orpc.ts @@ -0,0 +1,72 @@ + +import { createORPCClient, onError } from '@orpc/client'; +import { RPCLink } from '@orpc/client/fetch'; +import type { ContractRouterClient } from '@orpc/contract'; +import { QueryCache, QueryClient } from '@tanstack/react-query'; +import { toast } from 'sonner'; +import type { contract } from '../../../api/src/contract'; +import { getApiBaseUrl } from './runtime'; + +export type ApiContract = typeof contract; +export type ApiClient = ContractRouterClient; + +declare global { + var $apiClient: ApiClient | undefined; +} + +export const queryClient = new QueryClient({ + queryCache: new QueryCache({ + onError: () => { }, + }), + defaultOptions: { + queries: { + staleTime: 5 * 60 * 1000, + gcTime: 10 * 60 * 1000, + retry: (failureCount, error) => { + if (error && typeof error === 'object' && 'message' in error) { + const message = String(error.message).toLowerCase(); + if (message.includes('fetch') || message.includes('network')) { + return false; + } + } + return failureCount < 2; + }, + refetchOnWindowFocus: false, + }, + }, +}); + +function createApiLink() { + return new RPCLink({ + url: getApiBaseUrl, + interceptors: [ + onError((error: unknown) => { + console.error('oRPC API Error:', error); + + if (error && typeof error === 'object' && 'message' in error) { + const message = String(error.message).toLowerCase(); + if (message.includes('fetch') || message.includes('network') || message.includes('failed to fetch')) { + toast.error('Unable to connect to API', { + id: 'api-connection-error', + description: 'The API is currently unavailable. Please try again later.', + }); + } + } + }), + ], + fetch(url, options) { + return fetch(url, { + ...options, + credentials: 'include', + }); + }, + }); +} + +function createClientSideApiClient(): ApiClient { + return createORPCClient(createApiLink()); +} + +export const apiClient: ApiClient = globalThis.$apiClient ?? createClientSideApiClient(); + +export const API_URL = typeof window !== 'undefined' ? `${window.location.origin}/api/rpc` : '/api/rpc'; diff --git a/ui/src/remote/runtime.ts b/ui/src/remote/runtime.ts new file mode 100644 index 0000000..23f0c5e --- /dev/null +++ b/ui/src/remote/runtime.ts @@ -0,0 +1,29 @@ +import type { ClientRuntimeConfig } from "./types"; + +export function getRuntimeConfig(): ClientRuntimeConfig | undefined { + if (typeof window === "undefined") return undefined; + return window.__RUNTIME_CONFIG__; +} + +export function getAssetsUrl(config?: ClientRuntimeConfig): string { + const cfg = config ?? getRuntimeConfig(); + return cfg?.assetsUrl ?? ""; +} + +export function getHostUrl(config?: ClientRuntimeConfig): string { + const cfg = config ?? getRuntimeConfig(); + if (typeof window === "undefined") return ""; + return cfg?.hostUrl ?? window.location.origin; +} + +export function getApiBaseUrl(config?: ClientRuntimeConfig): string { + const cfg = config ?? getRuntimeConfig(); + const base = cfg?.rpcBase; + if (typeof window === "undefined") return "/api/rpc"; + return base ? `${window.location.origin}${base}` : `${window.location.origin}/api/rpc`; +} + +export function getAccount(config?: ClientRuntimeConfig): string { + const cfg = config ?? getRuntimeConfig(); + return cfg?.account ?? "every.near"; +} diff --git a/ui/src/remote/types.ts b/ui/src/remote/types.ts new file mode 100644 index 0000000..ad070c1 --- /dev/null +++ b/ui/src/remote/types.ts @@ -0,0 +1,62 @@ +export type { Network } from "near-kit"; + +import type { QueryClient } from "@tanstack/react-query"; +import type { AnyRouteMatch, AnyRouter, RouterHistory } from "@tanstack/react-router"; + +export interface ClientRuntimeConfig { + env: string; + account: string; + title: string; + hostUrl: string; + assetsUrl: string; + apiBase: string; + rpcBase: string; +} + +export interface RouterContext { + queryClient: QueryClient; + assetsUrl: string; + runtimeConfig?: ClientRuntimeConfig; +} + +export interface CreateRouterOptions { + history?: RouterHistory; + context?: Partial; +} + +export type HeadMeta = NonNullable[number]; +export type HeadLink = NonNullable[number]; +export type HeadScript = NonNullable[number]; + +export interface HeadData { + meta: HeadMeta[]; + links: HeadLink[]; + scripts: HeadScript[]; +} + +export interface RenderOptions { + assetsUrl: string; + runtimeConfig: ClientRuntimeConfig; +} + +export interface RenderResult { + stream: ReadableStream; + statusCode: number; + headers: Headers; +} + +export interface RouterModule { + createRouter: (opts?: CreateRouterOptions) => { + router: AnyRouter; + queryClient: QueryClient; + }; + getRouteHead: ( + pathname: string, + context?: Partial + ) => Promise; + renderToStream: ( + request: Request, + options: RenderOptions + ) => Promise; + routeTree: AnyRouter["routeTree"]; +} diff --git a/ui/src/routes/__root.tsx b/ui/src/routes/__root.tsx index 008dbd4..19a81c5 100644 --- a/ui/src/routes/__root.tsx +++ b/ui/src/routes/__root.tsx @@ -1,17 +1,120 @@ -import { createRootRouteWithContext, Outlet } from "@tanstack/react-router"; +import { TanStackDevtools } from "@tanstack/react-devtools"; +import { + ClientOnly, + createRootRouteWithContext, + HeadContent, + Outlet, + Scripts, +} from "@tanstack/react-router"; +import { TanStackRouterDevtoolsPanel } from "@tanstack/react-router-devtools"; import { ThemeProvider } from "next-themes"; import { Toaster } from "sonner"; +import { getBaseStyles, getRemoteScriptsWithMetadata } from "@/remote/head"; import type { RouterContext } from "@/types"; +import TanStackQueryDevtools from "../integrations/tanstack-query/devtools"; export const Route = createRootRouteWithContext()({ + loader: ({ context }) => ({ + assetsUrl: context.assetsUrl || "", + runtimeConfig: context.runtimeConfig, + }), + head: ({ loaderData }) => { + const assetsUrl = loaderData?.assetsUrl || ""; + const runtimeConfig = loaderData?.runtimeConfig; + const siteUrl = runtimeConfig?.hostUrl || ""; + const title = runtimeConfig?.title || "demo.everything"; + const description = + "Demo application showcasing Module Federation with SSR, TanStack Router, and oRPC"; + const siteName = "Every Demo"; + const ogImage = `${assetsUrl}/metadata.png`; + + return { + meta: [ + { charSet: "utf-8" }, + { + name: "viewport", + content: "width=device-width, initial-scale=1.0, viewport-fit=cover", + }, + { title }, + { name: "description", content: description }, + { name: "theme-color", content: "#171717" }, + { name: "color-scheme", content: "light dark" }, + { name: "application-name", content: siteName }, + { name: "mobile-web-app-capable", content: "yes" }, + { + name: "apple-mobile-web-app-status-bar-style", + content: "black-translucent", + }, + { name: "format-detection", content: "telephone=no" }, + { name: "robots", content: "index, follow" }, + { property: "og:title", content: title }, + { property: "og:description", content: description }, + { property: "og:type", content: "website" }, + { property: "og:url", content: siteUrl }, + { property: "og:image", content: ogImage }, + { property: "og:site_name", content: siteName }, + { name: "twitter:card", content: "summary_large_image" }, + { name: "twitter:title", content: title }, + { name: "twitter:description", content: description }, + { name: "twitter:image", content: ogImage }, + ], + links: [ + { rel: "canonical", href: siteUrl }, + { rel: "stylesheet", href: `${assetsUrl}/static/css/async/style.css` }, + { rel: "preconnect", href: "https://fonts.googleapis.com" }, + { + rel: "preconnect", + href: "https://fonts.gstatic.com", + crossOrigin: "anonymous", + }, + { rel: "icon", type: "image/x-icon", href: `${assetsUrl}/favicon.ico` }, + { rel: "icon", type: "image/svg+xml", href: `${assetsUrl}/icon.svg` }, + { + rel: "apple-touch-icon", + sizes: "180x180", + href: `${assetsUrl}/apple-touch-icon.png`, + }, + { rel: "manifest", href: `${assetsUrl}/manifest.json` }, + ], + scripts: getRemoteScriptsWithMetadata( + { assetsUrl, runtimeConfig }, + { siteName, siteUrl, description }, + ), + }; + }, component: RootComponent, }); function RootComponent() { return ( - - - - + + + +