merge origin/master into codex/wechat-mini-program-virtual-payment
This commit is contained in:
@@ -4,6 +4,14 @@ name = "Genarrative"
|
||||
|
||||
[setup]
|
||||
script = '''
|
||||
cp "$CODEX_SOURCE_TREE_PATH\.env.secrets.local" "$CODEX_WORKTREE_PATH\.env.secrets.local"
|
||||
npm install
|
||||
npm run codegraph:init
|
||||
npm run codegraph:index
|
||||
'''
|
||||
|
||||
[setup.win32]
|
||||
script = '''
|
||||
cp "$env:CODEX_SOURCE_TREE_PATH\.env.secrets.local" "$env:CODEX_WORKTREE_PATH\.env.secrets.local"
|
||||
npm install
|
||||
npm run codegraph:init
|
||||
|
||||
@@ -23,6 +23,13 @@
|
||||
- 影响范围:`src/services/payment/paymentPlatform.ts`、`src/components/rpg-entry/RpgEntryHomeView.tsx`、`miniprogram/pages/wechat-pay/`、`server-rs/crates/api-server/src/runtime_profile.rs`、`server-rs/crates/shared-contracts/src/runtime.rs`、`packages/shared/src/contracts/runtime.ts`、微信登录态存储。
|
||||
- 验证方式:泥点和会员商品在小程序运行态都请求 `wechat_mp_virtual`;小程序页能按 payload 调用 `wx.requestVirtualPayment` / `wx.requestPayment`;`cargo check -p api-server --manifest-path server-rs/Cargo.toml` 与支付相关前端测试通过。
|
||||
- 关联文档:`docs/【技术方案】微信虚拟支付接入-2026-05-26.md`。
|
||||
## 2026-05-30 Linux 本地 dev 端口段按系统级注册表分配
|
||||
|
||||
- 背景:同一台 Linux 开发机上有多个用户同时跑 `npm run dev` 时,单纯靠各自 `GENARRATIVE_DEV_PORT_RANGE` 容易撞段,且同一用户并发起两个 dev 会话时也会把相同端口段重复拿走。
|
||||
- 决策:Linux 上的本地 dev 端口段分配统一收口到系统级注册表 `/var/tmp/genarrative-dev-port-ranges/registry.json`,锁文件为 `/var/tmp/genarrative-dev-port-ranges/registry.lock`,可通过 `GENARRATIVE_DEV_PORT_RANGE_REGISTRY_DIR` 覆盖目录。未手动指定时自动从 `10000-10099` 开始按 100 端口块分配,后续块按 `10100-10199`、`10200-10299` 递增;端口段映射固定为 `web = start`、`api = start + 1`、`spacetime = start + 2`、`admin-web = start + 3`;注册表会拒绝不同用户的相同或重叠段,并让同一用户后续启动继续复用自己已占用的固定段。`GENARRATIVE_DEV_PORT_RANGE` 与 `--port-range` 仍可手动指定端口段,但只在 Linux 生效,Windows 继续沿用原有端口探测与漂移逻辑,不读注册表。
|
||||
- 影响范围:`scripts/dev-stack-port-utils.mjs`、`scripts/dev.mjs`、`scripts/dev-stack-port-utils.test.ts`、`scripts/dev.test.ts`、`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`、本条决策记录、`development-workflow.md`。
|
||||
- 验证方式:`node --check scripts/dev-stack-port-utils.mjs`、`node --check scripts/dev.mjs`、`node node_modules/vitest/vitest.mjs run scripts/dev-stack-port-utils.test.ts scripts/dev.test.ts` 通过;Linux 下能看到 `[dev] port-range:` 与 `registry.json` 路径日志,自动分配从 `10000-10099` 起步,Windows 不出现注册表分配日志。
|
||||
- 关联文档:`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`。
|
||||
|
||||
## 2026-05-27 生成页总进度圆弧锁定固定画布
|
||||
|
||||
|
||||
@@ -50,6 +50,8 @@ npm install
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Linux 多用户共享同一台机器开发时,本地 dev 脚本会为当前 Linux 用户分配一个固定端口段并写入系统级注册表 `/var/tmp/genarrative-dev-port-ranges/registry.json`,自动分配从 `10000-10099` 开始,每段 100 个端口,四个 dev 服务依次使用 `start` 到 `start + 3`。可用 `GENARRATIVE_DEV_PORT_RANGE` 或 `npm run dev -- --port-range` 手动指定端口段用于特殊场景;注册表会阻止不同用户使用相同或重叠段,并让同一用户后续启动继续复用自己已占用的固定段。该机制只在 Linux 生效,Windows 仍沿用原有端口探测与漂移逻辑。
|
||||
|
||||
该命令会启动:
|
||||
|
||||
- SpacetimeDB standalone
|
||||
|
||||
@@ -151,6 +151,14 @@
|
||||
- 验证:检查 `jenkins/Jenkinsfile.production-stdb-module-publish` 文件开头字节不再是 `EF BB BF`,并用 Jenkins `validateDeclarativePipeline` 或重放 `Genarrative-Stdb-Module-Publish`,不应再停在 `No such DSL method 'pipeline'`。
|
||||
- 关联:`jenkins/Jenkinsfile.production-stdb-module-publish`、`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`。
|
||||
|
||||
## Linux 多用户 dev 端口冲突先查系统级端口段注册表
|
||||
|
||||
- 现象:同一台 Linux 机器上多个用户同时开发时,`npm run dev` 报端口段已被其他用户占用、同一用户已有活跃端口段,或 SpacetimeDB 复用记录指向当前用户端口段之外的地址;未手动指定时自动分配应从 `10000-10099` 起步。
|
||||
- 原因:Linux dev 脚本会通过 `/var/tmp/genarrative-dev-port-ranges/registry.json` 做系统级端口段分配,避免两个用户配置相同或重叠端口段;同一用户后续启动会继续复用自己已经占用的固定端口段。注册表会保留该用户的段记录,不会因为多开而要求重新分配。
|
||||
- 处理:先确认当前用户已经占用的端口段,再让后续 `npm run dev` / `dev:*` 继续沿用这段;如确实要切换段,手动释放或清掉对应 registry 记录后再重启。需要临时隔离测试时用 `GENARRATIVE_DEV_PORT_RANGE_REGISTRY_DIR=<tmp-dir>` 覆盖注册表目录。不要在 Windows 上按这个注册表排查,Windows 仍走原有端口探测与漂移逻辑。未指定端口段时,系统会从 `10000-10099` 开始顺序分配。
|
||||
- 验证:重新启动后终端应打印 `[dev] port-range: <start-end> (<user>)` 与 `[dev] port-range-registry: .../registry.json`;`node node_modules/vitest/vitest.mjs run scripts/dev-stack-port-utils.test.ts scripts/dev.test.ts` 应通过 Linux registry、自动分配 `10000-10099` 与 Windows bypass 用例。
|
||||
- 关联:`scripts/dev-stack-port-utils.mjs`、`scripts/dev.mjs`、`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`。
|
||||
|
||||
## SpacetimeDB 入口迁移 helper 合并时不要只保留调用
|
||||
|
||||
- 现象:`cargo check -p spacetime-module --manifest-path server-rs/Cargo.toml` 或 Jenkins `Genarrative-Stdb-Module-Build` 报 `E0425 cannot find function migrate_rpg_entry_from_old_hidden_default in this scope`,位置在 `server-rs/crates/spacetime-module/src/runtime/creation_entry_config.rs` 的默认入口配置播种流程。
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Genarrative 项目共享概览
|
||||
|
||||
更新时间:`2026-05-15`
|
||||
更新时间:`2026-05-29`
|
||||
|
||||
## 一句话定位
|
||||
|
||||
@@ -33,6 +33,8 @@ Genarrative / 陶泥儿是一个 AI 原生互动内容与小游戏平台,把 A
|
||||
server-rs + Axum + SpacetimeDB
|
||||
```
|
||||
|
||||
当前 SpacetimeDB crate、SDK、CLI / standalone、生成 bindings 和容器压测镜像统一按 `2.3.0` 对齐。
|
||||
|
||||
职责边界:
|
||||
|
||||
- `api-server`:HTTP / SSE / BFF 门面和外部副作用编排。
|
||||
|
||||
@@ -34,6 +34,8 @@ metadata:
|
||||
|
||||
端口不可用时,脚本会从优先端口开始向后寻找可用端口。后续流程必须以解析后的实际端口为准,不能继续使用默认端口。
|
||||
|
||||
Linux 多用户并发开发时,`GENARRATIVE_DEV_PORT_RANGE` 或 `--port-range` 会先向系统级注册表 `/var/tmp/genarrative-dev-port-ranges/registry.json` 申请一个端口段,再把该段映射为 `web = start`、`api = start + 1`、`spacetime = start + 2`、`adminWeb = start + 3`。注册表锁文件是 `/var/tmp/genarrative-dev-port-ranges/registry.lock`,可通过 `GENARRATIVE_DEV_PORT_RANGE_REGISTRY_DIR` 覆盖目录。自动分配从 `10000-10099` 起,每次占用 100 个端口块,后续块按 `10100-10199`、`10200-10299` 递增;当前口径是“一个用户固定占用一个段,后续启动继续复用这段并在段内漂移”;该注册表只在 Linux 上生效;Windows 继续沿用原有端口探测、漂移和复用逻辑,不读系统级注册表。
|
||||
|
||||
## 实现入口
|
||||
|
||||
- `package.json`
|
||||
@@ -43,10 +45,12 @@ metadata:
|
||||
- `isPortAvailable(...)`:探测端口是否可监听。
|
||||
- `findAvailablePort(...)`:从优先端口向后寻找可用端口,`0` 表示申请临时端口。
|
||||
- `resolveDevStackPorts(...)`:一次性解析 SpacetimeDB、api-server、主站 Vite、后台 Vite 端口,并避免本次解析结果互相冲突。
|
||||
- Linux 注册表分配:`reserveLinuxDevPortRange(...)` / `releaseLinuxDevPortRange(...)`,仅在 Linux 上启用系统级端口段登记与用户段复用,自动分配从 `10000-10099` 起。
|
||||
- CLI 模式:`node scripts/dev-stack-port-utils.mjs resolve-dev-stack spacetime:127.0.0.1:3101 api:127.0.0.1:8082 web:0.0.0.0:3000 adminWeb:127.0.0.1:3102`。
|
||||
- `scripts/dev.mjs`
|
||||
- 解析 CLI 参数后统一计算 client host、端口、`SPACETIME_SERVER`、`RUST_SERVER_TARGET`。
|
||||
- 完整栈按 SpacetimeDB、publish、api-server、主站 Vite、后台 Vite 顺序启动。
|
||||
- Linux 下会先申请系统级端口段并把它映射成四个 dev 端口;自动分配从 `10000-10099` 起,Windows 则直接沿用原有参数解析与端口漂移逻辑。
|
||||
- 单模块命令复用同一套参数和 env 解析。
|
||||
|
||||
## 必须保持的传递链路
|
||||
@@ -60,6 +64,7 @@ metadata:
|
||||
5. 主站 Vite:`RUST_SERVER_TARGET`、`GENARRATIVE_RUNTIME_SERVER_TARGET`、`ADMIN_WEB_TARGET`、`ADMIN_WEB_PORT`、`--port=${WEB_PORT}`、`--host=${WEB_HOST}`。
|
||||
6. 后台 Vite:`ADMIN_API_TARGET`、`GENARRATIVE_API_TARGET`、`GENARRATIVE_API_PORT`、`--port=${ADMIN_WEB_PORT}`。
|
||||
7. 控制台日志:`[dev:ports]` 和 `[dev] web/admin web/api-server/spacetime` 必须显示最终实际地址。
|
||||
8. Linux 端口段注册:`[dev] port-range:` 与 `[dev] port-range-registry:` 只在 Linux 输出,Windows 不应依赖系统级注册表。
|
||||
|
||||
如果只改了其中一段,通常会出现:浏览器打开的前端可用,但 `/api/*` 代理到旧端口;后台页面可用但后台 API 失败;SpacetimeDB 启动在新端口但 publish 仍发往旧端口。
|
||||
|
||||
@@ -76,6 +81,7 @@ metadata:
|
||||
4. 修改 watch 时保持模块边界:SpacetimeDB 只监听 `spacetime-module` 且改动后重新 publish,不重启 standalone 宿主;api-server 排除 `spacetime-module`;web/admin-web 源码变化交给 Vite 自身 HMR,外层调度器不要再监听前端目录重启 Vite。
|
||||
5. 修改 `dev:web` 时不要自动改后端目标策略;`dev:web` 只负责主站 Vite 端口可用性与已有后端目标选择。
|
||||
6. 同步更新技术文档和团队共享记忆。
|
||||
7. 如果修改 Linux 端口段注册口径,确认 Windows 分支仍保持旧行为,不要把系统级注册表逻辑扩散到 Windows。
|
||||
|
||||
## 测试与验证
|
||||
|
||||
@@ -113,6 +119,7 @@ node scripts/dev-stack-port-utils.mjs resolve-dev-stack spacetime:127.0.0.1:0 ap
|
||||
## 验收清单
|
||||
|
||||
- [ ] 端口工具有测试覆盖端口被占用和多端口互斥解析。
|
||||
- [ ] Linux 注册表分配、同用户复用固定段并继续漂移、自动分配从 `10000-10099` 起、Windows bypass 都有测试覆盖。
|
||||
- [ ] `scripts/dev.mjs` 通过 `node --check`。
|
||||
- [ ] `npm run dev` 的 SpacetimeDB、publish、api-server、主站 Vite、后台 Vite 都使用实际端口。
|
||||
- [ ] `npm run dev:web` 在主站端口不可用时能切换到可用端口。
|
||||
|
||||
@@ -16,6 +16,14 @@ export default defineConfig(({mode}) => {
|
||||
env.GENARRATIVE_API_TARGET ||
|
||||
`http://127.0.0.1:${env.GENARRATIVE_API_PORT || '3100'}`;
|
||||
const base = env.ADMIN_WEB_BASE || '/admin/';
|
||||
const ignoredWatchGlobs = [
|
||||
'**/.git/**',
|
||||
'**/.worktrees/**',
|
||||
'**/dist/**',
|
||||
'**/node_modules/**',
|
||||
'**/server-rs/**',
|
||||
'**/server-rs/target/**',
|
||||
];
|
||||
|
||||
return {
|
||||
root: adminWebRoot,
|
||||
@@ -23,6 +31,9 @@ export default defineConfig(({mode}) => {
|
||||
base,
|
||||
plugins: [react()],
|
||||
server: {
|
||||
watch: {
|
||||
ignored: ignoredWatchGlobs,
|
||||
},
|
||||
proxy: {
|
||||
'/admin/api': {
|
||||
target: apiTarget,
|
||||
|
||||
@@ -55,7 +55,7 @@ Linux Docker Engine 若要从宿主机 CLI 连到容器内服务,直接用 `ht
|
||||
|
||||
## 构建工具链
|
||||
|
||||
`api-server` 容器镜像只构建 Linux release API 二进制,不构建 `spacetime-module`。当前 `api-server -> spacetime-client -> spacetimedb-sdk 2.2.0` 依赖链要求 Rust 1.93,因此 `deploy/container/api-server.Dockerfile` 的 Rust builder 固定为 `rust:1.93-bookworm`。如果本机 Docker Hub 拉取失败,可以先在本机准备同名本地 builder 镜像,但不要把临时 bootstrap 容器或私有 registry 凭据写入仓库。
|
||||
`api-server` 容器镜像只构建 Linux release API 二进制,不构建 `spacetime-module`。当前 `api-server -> spacetime-client -> spacetimedb-sdk 2.3.0` 依赖链要求 Rust 1.93,因此 `deploy/container/api-server.Dockerfile` 的 Rust builder 固定为 `rust:1.93-bookworm`。如果本机 Docker Hub 拉取失败,可以先在本机准备同名本地 builder 镜像,但不要把临时 bootstrap 容器或私有 registry 凭据写入仓库。
|
||||
|
||||
## 启动与验证
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ name: genarrative-container-loadtest
|
||||
|
||||
services:
|
||||
spacetimedb:
|
||||
image: clockworklabs/spacetime:v2.2.0
|
||||
image: clockworklabs/spacetime:v2.3.0
|
||||
user: root
|
||||
command:
|
||||
[
|
||||
|
||||
@@ -16,6 +16,8 @@ server-rs + Axum + SpacetimeDB
|
||||
|
||||
`server-rs/Cargo.toml` 是 workspace 事实源。默认构建成员为 `crates/api-server`;第三方依赖版本和 workspace 内 crate path 统一放在 `[workspace.dependencies]`。
|
||||
|
||||
SpacetimeDB 版本口径:当前 Rust crate `spacetimedb`、`spacetimedb-sdk`、`spacetimedb-lib` 统一锁定 `2.3.0`;本地 `spacetime` CLI / standalone、生成的 `spacetime-client` bindings 和容器压测镜像也必须与 `2.3.0` 对齐,避免 BSATN / procedure result 反序列化错配。
|
||||
|
||||
当前主要 crate:
|
||||
|
||||
- HTTP 服务:`api-server`。
|
||||
|
||||
@@ -43,6 +43,8 @@ npm run dev:web
|
||||
npm run dev:api-server
|
||||
```
|
||||
|
||||
Linux 本机多用户并发开发时,`npm run dev` 和 `npm run dev:*` 单模块命令会先在系统级端口段注册表里给当前用户分配一个端口段,再把该段映射为 `web = start`、`api = start + 1`、`spacetime = start + 2`、`admin-web = start + 3`。默认注册表目录是 `/var/tmp/genarrative-dev-port-ranges/`,其中 `registry.json` 记录各用户的活跃段,`registry.lock` 负责串行化分配;可以用 `GENARRATIVE_DEV_PORT_RANGE_REGISTRY_DIR` 覆盖目录。系统自动分配时从 `10000-10099` 开始,每次占用 100 个端口块,后续块按 `10100-10199`、`10200-10299` 递增;`GENARRATIVE_DEV_PORT_RANGE` 或 `--port-range` 只在 Linux 上生效,Windows 仍按原来的 3000 / 8082 / 3101 / 3102 端口探测与漂移逻辑运行,不读这个系统级注册表。
|
||||
|
||||
后端日志默认写入 `logs/api-server/`。后端 API smoke 使用 `npm run dev:api-server` 并检查 `/healthz`;不要使用旧 `api-server:maincloud` 或任何 `GENARRATIVE_SPACETIME_MAINCLOUD_*` 口径。
|
||||
|
||||
开发态 `npm run dev` 与 `npm run dev:api-server` 会默认注入 `GENARRATIVE_DEV_PASSWORD_ENTRY_AUTO_REGISTER_ENABLED=true`,因此密码登录在本地开发环境可直接注册未知手机号账号;生产环境仍按 `api-server` 配置默认关闭该开关。
|
||||
@@ -61,7 +63,7 @@ spacetime sql <database> "SELECT * FROM puzzle_gallery_card_view LIMIT 1" --serv
|
||||
|
||||
本地 `npm run dev:spacetime` 发布模块时必须显式忽略仓库根目录的 `spacetime.json`,由脚本固定追加 `--no-config` 并使用命令参数里传入的数据库名和 `--server http://127.0.0.1:3101`。否则 CLI 可能把发布目标改写到配置文件里的其他数据库,导致 `dev:spacetime` 启动后又因发布失败自动退出,浏览器随后会在 `ws://127.0.0.1:3101/v1/database/.../subscribe` 看到连接拒绝。
|
||||
|
||||
本地 `spacetime` CLI / standalone 版本必须和 `server-rs/Cargo.toml` 里锁定的 `spacetimedb` 版本一致。若版本错配,procedure 返回值可能在宿主侧触发 `Failed to BSATN deserialize procedure return value`,api-server 最终表现为敲木鱼等创作动作的 `SpacetimeDB procedure 调用超时`。排障时先运行 `spacetime --version`,再对照 `server-rs/Cargo.toml` 的 `spacetimedb = "..."`;需要切版本时执行 `spacetime version install <version> && spacetime version use <version>`,然后重新启动 `npm run dev:spacetime`。当前 `scripts/dev.mjs` 会在启动和复用本地 SpacetimeDB 前写入并校验 `dev-spacetime-tool-version`,避免把旧 standalone 继续带进新一轮创作。
|
||||
本地 `spacetime` CLI / standalone 版本必须和 `server-rs/Cargo.toml` 里锁定的 `spacetimedb` 版本一致;当前统一版本为 `2.3.0`。若版本错配,procedure 返回值可能在宿主侧触发 `Failed to BSATN deserialize procedure return value`,api-server 最终表现为敲木鱼等创作动作的 `SpacetimeDB procedure 调用超时`。排障时先运行 `spacetime --version`,再对照 `server-rs/Cargo.toml` 的 `spacetimedb = "..."`;需要切版本时执行 `spacetime version install <version> && spacetime version use <version>`,然后重新启动 `npm run dev:spacetime`。当前 `scripts/dev.mjs` 会在启动和复用本地 SpacetimeDB 前写入并校验 `dev-spacetime-tool-version`,避免把旧 standalone 继续带进新一轮创作。
|
||||
|
||||
本地 `.env`、`.env.local` 或 `.env.secrets.local` 修改后必须重启 `api-server` 才会生效;若已经通过 `npm run dev` 启动完整联调,可在该终端输入 `rs api-server`。排查 RPG / 拼图 / 抓大鹅等 VectorEngine 生图链路时,确认 `VECTOR_ENGINE_BASE_URL`、`VECTOR_ENGINE_API_KEY` 和 `VECTOR_ENGINE_IMAGE_REQUEST_TIMEOUT_MS` 只在本地或服务器密钥文件中配置,不能写入 Git。VectorEngine `gpt-image-2` 图片协议、URL / base64 响应解析、远端图片下载和 provider 侧结构化日志在 `server-rs/crates/platform-image`;`api-server` 只做配置、玩法编排、OSS / asset 持久化、计费和失败审计落库。开局 CG 故事板、首图、背景和图集都属于长耗时图片请求;后端默认会把 `VECTOR_ENGINE_IMAGE_REQUEST_TIMEOUT_MS` 下限收口到 `1000000`,旧进程仍可能沿用重启前的短超时。若开局 CG 故事板在 `send()` 阶段失败且日志显示 `SendRequest`,先看同一 request_id 的 `request_body_bytes`、`reference_data_url_bytes`、`sourceChain` 和 `rootSource`;当前开局 CG 会把角色图与首幕背景图压到单边 768 的 JPEG 后再作为 generations `image` 数组发送,`/v1/images/generations` 使用默认 HTTP 协商,只有 multipart `/v1/images/edits` 单独强制 HTTP/1.1。
|
||||
|
||||
|
||||
@@ -504,4 +504,4 @@ BASH
|
||||
echo "Server provision 完成: target=${params.DEPLOY_TARGET}, dryRun=${params.DRY_RUN}, nginxConfigMode=${params.NGINX_CONFIG_MODE}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,13 @@
|
||||
import {createServer} from 'node:net';
|
||||
import {mkdtempSync, readFileSync, rmSync} from 'node:fs';
|
||||
import {tmpdir} from 'node:os';
|
||||
import {join} from 'node:path';
|
||||
import {describe, expect, it} from 'vitest';
|
||||
import {
|
||||
findAvailablePort,
|
||||
mapDevPortsToPortRange,
|
||||
parsePortRangeSpec,
|
||||
reserveLinuxDevPortRange,
|
||||
resolveDevStackPorts,
|
||||
} from './dev-stack-port-utils.mjs';
|
||||
|
||||
@@ -18,6 +24,20 @@ function reservePort(port) {
|
||||
}
|
||||
|
||||
describe('dev stack port utils', () => {
|
||||
it('解析端口段并映射到四个 dev 端口', () => {
|
||||
expect(parsePortRangeSpec('10000-10099')).toEqual({
|
||||
start: 10000,
|
||||
end: 10099,
|
||||
label: '10000-10099',
|
||||
});
|
||||
expect(mapDevPortsToPortRange('10000-10099')).toMatchObject({
|
||||
webPort: 10000,
|
||||
apiPort: 10001,
|
||||
spacetimePort: 10002,
|
||||
adminWebPort: 10003,
|
||||
});
|
||||
});
|
||||
|
||||
it('使用端口可用性检查为被占用端口寻找后续可用端口', async () => {
|
||||
const firstServer = await reservePort(0);
|
||||
const firstPort = firstServer.address().port;
|
||||
@@ -38,6 +58,16 @@ describe('dev stack port utils', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('端口查找不会越过 Linux 用户端口段', async () => {
|
||||
await expect(
|
||||
findAvailablePort({
|
||||
host: '127.0.0.1',
|
||||
preferredPort: 9999,
|
||||
portRange: {start: 10000, end: 10099, label: '10000-10099'},
|
||||
}),
|
||||
).rejects.toThrow('不在允许端口段');
|
||||
});
|
||||
|
||||
it('为 npm run dev 的所有后续流程解析互不冲突的端口', async () => {
|
||||
const resolvedPorts = await resolveDevStackPorts({
|
||||
spacetime: {host: '127.0.0.1', preferredPort: 0},
|
||||
@@ -48,4 +78,191 @@ describe('dev stack port utils', () => {
|
||||
|
||||
expect(new Set(Object.values(resolvedPorts)).size).toBe(4);
|
||||
});
|
||||
|
||||
it('端口段内会一直漂移到段尾,不会被默认 200 次尝试截断', async () => {
|
||||
const rangeStart = 10000;
|
||||
const rangeEnd = 10300;
|
||||
const reservedPorts = new Set(
|
||||
Array.from({length: 201}, (_, index) => rangeStart + index),
|
||||
);
|
||||
|
||||
const availablePort = await findAvailablePort({
|
||||
host: '127.0.0.1',
|
||||
preferredPort: rangeStart,
|
||||
reservedPorts,
|
||||
portRange: {start: rangeStart, end: rangeEnd, label: `${rangeStart}-${rangeEnd}`},
|
||||
});
|
||||
|
||||
expect(availablePort).toBeGreaterThan(rangeStart + 200);
|
||||
expect(availablePort).toBeLessThanOrEqual(rangeEnd);
|
||||
});
|
||||
|
||||
const linuxIt = process.platform === 'linux' ? it : it.skip;
|
||||
|
||||
linuxIt('Linux 未手动指定端口段时从 10000 开始按 100 端口块自动分配', async () => {
|
||||
const tempRoot = mkdtempSync(join(tmpdir(), 'genarrative-port-range-'));
|
||||
const registryPath = join(tempRoot, 'registry.json');
|
||||
const lockPath = join(tempRoot, 'registry.lock');
|
||||
|
||||
try {
|
||||
const aliceAllocation = await reserveLinuxDevPortRange({
|
||||
env: {
|
||||
USER: 'alice',
|
||||
LOGNAME: 'alice',
|
||||
GENARRATIVE_DEV_PORT_RANGE_REGISTRY_DIR: tempRoot,
|
||||
},
|
||||
username: 'alice',
|
||||
requestedRange: null,
|
||||
registryPath,
|
||||
lockPath,
|
||||
});
|
||||
const bobAllocation = await reserveLinuxDevPortRange({
|
||||
env: {
|
||||
USER: 'bob',
|
||||
LOGNAME: 'bob',
|
||||
GENARRATIVE_DEV_PORT_RANGE_REGISTRY_DIR: tempRoot,
|
||||
},
|
||||
username: 'bob',
|
||||
requestedRange: null,
|
||||
registryPath,
|
||||
lockPath,
|
||||
});
|
||||
|
||||
expect(aliceAllocation.range.label).toBe('10000-10099');
|
||||
expect(aliceAllocation.source).toBe('auto');
|
||||
expect(bobAllocation.range.label).toBe('10100-10199');
|
||||
expect(bobAllocation.source).toBe('auto');
|
||||
} finally {
|
||||
rmSync(tempRoot, {recursive: true, force: true});
|
||||
}
|
||||
});
|
||||
|
||||
linuxIt('Linux 系统级端口段记录会阻止两个用户拿到同一段', async () => {
|
||||
const tempRoot = mkdtempSync(join(tmpdir(), 'genarrative-port-range-'));
|
||||
const registryPath = join(tempRoot, 'registry.json');
|
||||
const lockPath = join(tempRoot, 'registry.lock');
|
||||
|
||||
try {
|
||||
const baseEnv = {
|
||||
USER: 'alice',
|
||||
LOGNAME: 'alice',
|
||||
GENARRATIVE_DEV_PORT_RANGE_REGISTRY_DIR: tempRoot,
|
||||
GENARRATIVE_DEV_PORT_RANGE: '10000-10099',
|
||||
};
|
||||
|
||||
const aliceAllocation = await reserveLinuxDevPortRange({
|
||||
env: baseEnv,
|
||||
username: 'alice',
|
||||
registryPath,
|
||||
lockPath,
|
||||
});
|
||||
|
||||
expect(aliceAllocation.range.label).toBe('10000-10099');
|
||||
|
||||
const registryAfterAlice = JSON.parse(readFileSync(registryPath, 'utf8'));
|
||||
expect(registryAfterAlice.allocations.alice.range.label).toBe('10000-10099');
|
||||
|
||||
await expect(
|
||||
reserveLinuxDevPortRange({
|
||||
env: {
|
||||
...baseEnv,
|
||||
USER: 'bob',
|
||||
LOGNAME: 'bob',
|
||||
},
|
||||
username: 'bob',
|
||||
requestedRange: '10000-10099',
|
||||
registryPath,
|
||||
lockPath,
|
||||
}),
|
||||
).rejects.toThrow('已被用户 alice 占用');
|
||||
|
||||
const aliceReuse = await reserveLinuxDevPortRange({
|
||||
env: {
|
||||
...baseEnv,
|
||||
USER: 'alice',
|
||||
LOGNAME: 'alice',
|
||||
},
|
||||
username: 'alice',
|
||||
registryPath,
|
||||
lockPath,
|
||||
});
|
||||
|
||||
expect(aliceReuse.range.label).toBe('10000-10099');
|
||||
expect(aliceReuse.source).toBe('manual');
|
||||
expect(
|
||||
(
|
||||
await reserveLinuxDevPortRange({
|
||||
env: {
|
||||
...baseEnv,
|
||||
USER: 'alice',
|
||||
LOGNAME: 'alice',
|
||||
GENARRATIVE_DEV_PORT_RANGE: '10100-10199',
|
||||
},
|
||||
username: 'alice',
|
||||
requestedRange: '10100-10199',
|
||||
registryPath,
|
||||
lockPath,
|
||||
})
|
||||
).range.label,
|
||||
).toBe('10000-10099');
|
||||
|
||||
const registryAfterReuse = JSON.parse(readFileSync(registryPath, 'utf8'));
|
||||
expect(registryAfterReuse.allocations.alice.range.label).toBe('10000-10099');
|
||||
|
||||
const bobAllocation = await reserveLinuxDevPortRange({
|
||||
env: {
|
||||
...baseEnv,
|
||||
USER: 'bob',
|
||||
LOGNAME: 'bob',
|
||||
GENARRATIVE_DEV_PORT_RANGE: '10100-10199',
|
||||
},
|
||||
username: 'bob',
|
||||
requestedRange: '10100-10199',
|
||||
registryPath,
|
||||
lockPath,
|
||||
});
|
||||
|
||||
expect(bobAllocation.range.label).toBe('10100-10199');
|
||||
} finally {
|
||||
rmSync(tempRoot, {recursive: true, force: true});
|
||||
}
|
||||
});
|
||||
|
||||
linuxIt('Linux 同一用户第二个 dev 会话会复用同一用户段并继续在段内漂移', async () => {
|
||||
const tempRoot = mkdtempSync(join(tmpdir(), 'genarrative-port-range-'));
|
||||
const registryPath = join(tempRoot, 'registry.json');
|
||||
const lockPath = join(tempRoot, 'registry.lock');
|
||||
|
||||
try {
|
||||
const baseEnv = {
|
||||
USER: 'alice',
|
||||
LOGNAME: 'alice',
|
||||
GENARRATIVE_DEV_PORT_RANGE_REGISTRY_DIR: tempRoot,
|
||||
GENARRATIVE_DEV_PORT_RANGE: '10000-10099',
|
||||
};
|
||||
|
||||
const first = await reserveLinuxDevPortRange({
|
||||
env: baseEnv,
|
||||
username: 'alice',
|
||||
registryPath,
|
||||
lockPath,
|
||||
});
|
||||
|
||||
const second = await reserveLinuxDevPortRange({
|
||||
env: baseEnv,
|
||||
username: 'alice',
|
||||
registryPath,
|
||||
lockPath,
|
||||
});
|
||||
|
||||
expect(first.range.label).toBe('10000-10099');
|
||||
expect(second.range.label).toBe('10000-10099');
|
||||
expect(second.username).toBe('alice');
|
||||
expect(JSON.parse(readFileSync(registryPath, 'utf8')).allocations.alice.range.label).toBe(
|
||||
'10000-10099',
|
||||
);
|
||||
} finally {
|
||||
rmSync(tempRoot, {recursive: true, force: true});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,7 +15,11 @@ import {fileURLToPath} from 'node:url';
|
||||
|
||||
import {
|
||||
formatPortDecision,
|
||||
getLinuxDevPortRangeRegistryPaths,
|
||||
getLinuxDevPortRangeUsername,
|
||||
mapDevPortsToPortRange,
|
||||
normalizePort,
|
||||
reserveLinuxDevPortRange,
|
||||
resolveDevStackPorts,
|
||||
} from './dev-stack-port-utils.mjs';
|
||||
import {
|
||||
@@ -59,6 +63,7 @@ function usage() {
|
||||
--spacetime-port <port> SpacetimeDB 端口
|
||||
--spacetime-data-dir <path> SpacetimeDB 本地数据目录
|
||||
--database <name> SpacetimeDB 数据库名
|
||||
--port-range <start-end> Linux 用户端口段,手动指定示例 10000-10099;默认自动从 10000-10099 起分配
|
||||
--watch 文件改动后刷新/重启对应模块
|
||||
--no-interactive 关闭交互式手动命令
|
||||
|
||||
@@ -109,6 +114,7 @@ function parseArgs(argv, baseEnv) {
|
||||
spacetimePort: normalizePort(env.SPACETIME_PORT, 3101),
|
||||
spacetimeDataDir: resolve(serverRsDir, '.spacetimedb/local/data'),
|
||||
spacetimeServerUrl: String(env.GENARRATIVE_SPACETIME_SERVER_URL ?? '').trim(),
|
||||
portRangeSpec: String(env.GENARRATIVE_DEV_PORT_RANGE ?? '').trim(),
|
||||
database:
|
||||
readLocalSpacetimeDatabase() ||
|
||||
String(env.GENARRATIVE_SPACETIME_DATABASE ?? '').trim() ||
|
||||
@@ -184,6 +190,10 @@ function parseArgs(argv, baseEnv) {
|
||||
options.database = readValue();
|
||||
explicitOptions.add('database');
|
||||
break;
|
||||
case '--port-range':
|
||||
options.portRangeSpec = readValue();
|
||||
explicitOptions.add('portRangeSpec');
|
||||
break;
|
||||
case '--log':
|
||||
options.apiLog = readValue();
|
||||
break;
|
||||
@@ -549,6 +559,8 @@ class DevRunner {
|
||||
adminWebTargetHost: resolveClientHost(options.adminWebHost),
|
||||
spacetimeServer: initialSpacetimeServer,
|
||||
apiTarget: `http://${resolveClientHost(options.apiHost)}:${options.apiPort}`,
|
||||
portRange: null,
|
||||
portRangeReservation: null,
|
||||
};
|
||||
this.services = new Map();
|
||||
this.watchers = [];
|
||||
@@ -572,12 +584,57 @@ class DevRunner {
|
||||
ensureSpacetimeToolVersionMatchesWorkspace();
|
||||
}
|
||||
|
||||
await this.prepareLinuxPortRange(command);
|
||||
await this.tryReuseExistingSpacetime(command);
|
||||
await this.resolvePorts(command);
|
||||
this.registerServices();
|
||||
this.printSummary(command);
|
||||
}
|
||||
|
||||
async prepareLinuxPortRange(command) {
|
||||
if (process.platform !== 'linux') {
|
||||
return;
|
||||
}
|
||||
|
||||
const requestedRange = String(this.options.portRangeSpec ?? '').trim();
|
||||
const allocation = await reserveLinuxDevPortRange({
|
||||
env: {
|
||||
...this.baseEnv,
|
||||
GENARRATIVE_DEV_PORT_RANGE: requestedRange,
|
||||
},
|
||||
username: getLinuxDevPortRangeUsername(this.baseEnv),
|
||||
});
|
||||
|
||||
if (!allocation) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.state.portRangeReservation = allocation;
|
||||
this.state.portRange = allocation.range;
|
||||
this.baseEnv.GENARRATIVE_DEV_PORT_RANGE = allocation.range.label;
|
||||
|
||||
const mappedPorts = mapDevPortsToPortRange(allocation.range);
|
||||
if (!mappedPorts) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.explicitOptions.has('webPort')) {
|
||||
this.options.webPort = mappedPorts.webPort;
|
||||
}
|
||||
if (!this.explicitOptions.has('apiPort')) {
|
||||
this.options.apiPort = mappedPorts.apiPort;
|
||||
}
|
||||
if (!this.explicitOptions.has('spacetimePort')) {
|
||||
this.options.spacetimePort = mappedPorts.spacetimePort;
|
||||
}
|
||||
if (!this.explicitOptions.has('adminWebPort')) {
|
||||
this.options.adminWebPort = mappedPorts.adminWebPort;
|
||||
}
|
||||
|
||||
this.state.spacetimeServer = `http://${this.options.spacetimeHost}:${this.options.spacetimePort}`;
|
||||
this.state.apiTarget = `http://${this.state.apiTargetHost}:${this.options.apiPort}`;
|
||||
}
|
||||
|
||||
shouldValidateSpacetimeToolVersion(command) {
|
||||
if (command === 'spacetime') {
|
||||
return true;
|
||||
@@ -626,6 +683,10 @@ class DevRunner {
|
||||
]);
|
||||
|
||||
for (const candidate of candidates) {
|
||||
if (!this.isCandidateSpacetimeWithinAssignedRange(candidate)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const pingUrl = buildUrl(candidate, '/v1/ping');
|
||||
if (!pingUrl || !(await isHttpReady(pingUrl))) {
|
||||
continue;
|
||||
@@ -653,6 +714,21 @@ class DevRunner {
|
||||
);
|
||||
}
|
||||
|
||||
isCandidateSpacetimeWithinAssignedRange(candidateUrl) {
|
||||
const range = this.state.portRange;
|
||||
if (!range) {
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
const url = new URL(candidateUrl);
|
||||
const port = Number(url.port);
|
||||
return Number.isInteger(port) && port >= range.start && port <= range.end;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async resolvePorts(command) {
|
||||
const {options} = this;
|
||||
const portConfig = {};
|
||||
@@ -662,6 +738,7 @@ class DevRunner {
|
||||
portConfig.spacetime = {
|
||||
host: options.spacetimeHost,
|
||||
preferredPort: options.spacetimePort,
|
||||
portRange: this.state.portRange,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -670,6 +747,7 @@ class DevRunner {
|
||||
portConfig.api = {
|
||||
host: options.apiHost,
|
||||
preferredPort: options.apiPort,
|
||||
portRange: this.state.portRange,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -677,6 +755,7 @@ class DevRunner {
|
||||
portConfig.web = {
|
||||
host: options.webHost,
|
||||
preferredPort: options.webPort,
|
||||
portRange: this.state.portRange,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -684,6 +763,7 @@ class DevRunner {
|
||||
portConfig.adminWeb = {
|
||||
host: options.adminWebHost,
|
||||
preferredPort: options.adminWebPort,
|
||||
portRange: this.state.portRange,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -747,6 +827,15 @@ class DevRunner {
|
||||
console.log(`[dev] repo: ${repoRoot}`);
|
||||
console.log(`[dev] command: ${command}`);
|
||||
console.log(`[dev] watch: ${options.watch ? 'on' : 'off'}`);
|
||||
if (state.portRange) {
|
||||
const owner =
|
||||
state.portRangeReservation?.username ||
|
||||
getLinuxDevPortRangeUsername(this.baseEnv);
|
||||
console.log(`[dev] port-range: ${state.portRange.label} (${owner})`);
|
||||
console.log(
|
||||
`[dev] port-range-registry: ${getLinuxDevPortRangeRegistryPaths(this.baseEnv).registryPath}`,
|
||||
);
|
||||
}
|
||||
console.log(`[dev] web: http://127.0.0.1:${options.webPort}`);
|
||||
console.log(`[dev] admin web: http://${state.adminWebTargetHost}:${options.adminWebPort}/admin/`);
|
||||
console.log(`[dev] api-server: ${state.apiTarget}`);
|
||||
|
||||
+67
-4
@@ -35,6 +35,8 @@ function workspaceSpacetimeVersionForTest() {
|
||||
}
|
||||
|
||||
describe('dev scheduler argument routing', () => {
|
||||
const linuxTest = process.platform === 'linux' ? test : test.skip;
|
||||
|
||||
test('完整 dev 栈覆盖前端代理到本次解析出的 api-server 地址', () => {
|
||||
const {command, explicitOptions, options} = parseArgs([], {
|
||||
GENARRATIVE_API_PORT: '8090',
|
||||
@@ -88,6 +90,67 @@ describe('dev scheduler argument routing', () => {
|
||||
'http://127.0.0.1:3100',
|
||||
);
|
||||
});
|
||||
|
||||
linuxTest('Linux 启动时按系统级端口段映射四个 dev 端口', async () => {
|
||||
const tempDir = mkdtempSync(join(tmpdir(), 'genarrative-dev-port-range-'));
|
||||
try {
|
||||
const {command, explicitOptions, options} = parseArgs([], {
|
||||
USER: 'alice',
|
||||
LOGNAME: 'alice',
|
||||
GENARRATIVE_DEV_PORT_RANGE: '22000-22099',
|
||||
GENARRATIVE_DEV_PORT_RANGE_REGISTRY_DIR: tempDir,
|
||||
});
|
||||
const runner = new DevRunner(options, {
|
||||
USER: 'alice',
|
||||
LOGNAME: 'alice',
|
||||
GENARRATIVE_DEV_PORT_RANGE: '22000-22099',
|
||||
GENARRATIVE_DEV_PORT_RANGE_REGISTRY_DIR: tempDir,
|
||||
}, explicitOptions);
|
||||
|
||||
await runner.prepareLinuxPortRange(command);
|
||||
|
||||
expect(runner.state.portRange.label).toBe('22000-22099');
|
||||
expect(runner.options.webPort).toBe(22000);
|
||||
expect(runner.options.apiPort).toBe(22001);
|
||||
expect(runner.options.spacetimePort).toBe(22002);
|
||||
expect(runner.options.adminWebPort).toBe(22003);
|
||||
expect(runner.state.apiTarget).toBe('http://127.0.0.1:22001');
|
||||
expect(runner.state.spacetimeServer).toBe('http://127.0.0.1:22002');
|
||||
} finally {
|
||||
rmSync(tempDir, {recursive: true, force: true});
|
||||
}
|
||||
});
|
||||
|
||||
test('Windows 仍沿用原有端口解析,不启用 Linux 端口段登记', async () => {
|
||||
const originalPlatform = Object.getOwnPropertyDescriptor(process, 'platform');
|
||||
Object.defineProperty(process, 'platform', {
|
||||
configurable: true,
|
||||
value: 'win32',
|
||||
});
|
||||
|
||||
try {
|
||||
const {command, explicitOptions, options} = parseArgs([], {
|
||||
USER: 'alice',
|
||||
GENARRATIVE_DEV_PORT_RANGE: '22000-22099',
|
||||
});
|
||||
const runner = new DevRunner(options, {
|
||||
USER: 'alice',
|
||||
GENARRATIVE_DEV_PORT_RANGE: '22000-22099',
|
||||
}, explicitOptions);
|
||||
|
||||
await runner.prepareLinuxPortRange(command);
|
||||
|
||||
expect(runner.state.portRange).toBeNull();
|
||||
expect(runner.options.webPort).toBe(3000);
|
||||
expect(runner.options.apiPort).toBe(8082);
|
||||
expect(runner.options.spacetimePort).toBe(3101);
|
||||
expect(runner.options.adminWebPort).toBe(3102);
|
||||
} finally {
|
||||
if (originalPlatform) {
|
||||
Object.defineProperty(process, 'platform', originalPlatform);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('dev scheduler api-server env', () => {
|
||||
@@ -252,18 +315,18 @@ describe('dev scheduler watch routing', () => {
|
||||
describe('dev scheduler spacetime refresh', () => {
|
||||
test('解析 spacetime --version 输出里的 tool version', () => {
|
||||
const version = parseSpacetimeToolVersion(`
|
||||
A new version of SpacetimeDB is available: v2.2.0 (current: v2.1.0)
|
||||
spacetimedb tool version 2.2.0; spacetimedb-lib version 2.2.0;
|
||||
A new version of SpacetimeDB is available: v2.3.0 (current: v2.2.0)
|
||||
spacetimedb tool version 2.3.0; spacetimedb-lib version 2.3.0;
|
||||
`);
|
||||
|
||||
expect(version).toBe('2.2.0');
|
||||
expect(version).toBe('2.3.0');
|
||||
});
|
||||
|
||||
test('本机 spacetime 版本和 workspace 锁定版本不一致时直接报清楚', () => {
|
||||
expect(() =>
|
||||
assertSpacetimeToolVersionMatchesWorkspace({
|
||||
toolVersion: '2.1.0',
|
||||
workspaceVersion: '2.2.0',
|
||||
workspaceVersion: '2.3.0',
|
||||
}),
|
||||
).toThrow('procedure 返回值 BSATN 反序列化失败');
|
||||
});
|
||||
|
||||
Generated
+32
-32
@@ -1310,7 +1310,7 @@ dependencies = [
|
||||
"libc",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"socket2 0.6.3",
|
||||
"socket2 0.5.10",
|
||||
"tokio",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
@@ -2605,7 +2605,7 @@ dependencies = [
|
||||
"quinn-udp",
|
||||
"rustc-hash",
|
||||
"rustls",
|
||||
"socket2 0.6.3",
|
||||
"socket2 0.5.10",
|
||||
"thiserror 2.0.18",
|
||||
"tokio",
|
||||
"tracing",
|
||||
@@ -2642,7 +2642,7 @@ dependencies = [
|
||||
"cfg_aliases",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"socket2 0.6.3",
|
||||
"socket2 0.5.10",
|
||||
"tracing",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
@@ -3423,9 +3423,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "spacetimedb"
|
||||
version = "2.2.0"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1306cc3a9ed9c89f43b263614a529357cc53a067e3d06c1cbb485e3b577b118b"
|
||||
checksum = "62aa9a940d32178e4afa7a14c9bbda76685d71d5487798905e0139b807182092"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytemuck",
|
||||
@@ -3446,9 +3446,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "spacetimedb-bindings-macro"
|
||||
version = "2.2.0"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51567ec01cd323438a00c134c16f26ffcde5f9dbe6a42a52e54578285bf49d73"
|
||||
checksum = "5feb5d55f04f3209764d9b94949226708a4a8578e92ac5c32abfd31dbdfc928c"
|
||||
dependencies = [
|
||||
"heck 0.4.1",
|
||||
"humantime",
|
||||
@@ -3460,18 +3460,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "spacetimedb-bindings-sys"
|
||||
version = "2.2.0"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b40fa1bea26664085febe2b4455568c8b47dea2cb0245406b27e30963df2ba1"
|
||||
checksum = "28f9c900c9371fd7e84d34b8cb2bf90562060dc2473ae9c44e970d4026e7d7d9"
|
||||
dependencies = [
|
||||
"spacetimedb-primitives",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spacetimedb-client-api-messages"
|
||||
version = "2.2.0"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dfc9eeb20a555bad07029cbee4efe3a305cb5c1e40e21a07cbbbbed16a106014"
|
||||
checksum = "349296ad43e6ecdced74ad8b3fd2c6abbdbb40cdbd06ac329c0726c6b911fa73"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"bytestring",
|
||||
@@ -3491,9 +3491,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "spacetimedb-data-structures"
|
||||
version = "2.2.0"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "748fd5850a757823c5b8948065d9e4dc5092968a051aa3f34f170e91d95e493b"
|
||||
checksum = "8b86ed7c567d723378a405317e30413293cc0bc9e2aac2f7843580d744b43c31"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"crossbeam-queue",
|
||||
@@ -3506,9 +3506,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "spacetimedb-lib"
|
||||
version = "2.2.0"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5612611d09d358f535438275d2a0d6a5e2fa56fa583dcfdbeddd623974df1d5e"
|
||||
checksum = "a15a98adf6f030e8188df4b038e140a54771e6eeb50ad05a6c3e46939b8de853"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags 2.11.1",
|
||||
@@ -3530,9 +3530,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "spacetimedb-memory-usage"
|
||||
version = "2.2.0"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c3a0d08fc5d8688a47e3ffcb803275519663b7ea1fba7ad25e608182de4ec6d"
|
||||
checksum = "c68aa8ed30c15a1d665bf3a8c689955508ce75ca784068ec0232b4cdd511b4c8"
|
||||
dependencies = [
|
||||
"decorum",
|
||||
"ethnum",
|
||||
@@ -3540,9 +3540,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "spacetimedb-metrics"
|
||||
version = "2.2.0"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca2d647201339aa17ba438a07463e96ed64ba214fb0c182588e262b055efa7f3"
|
||||
checksum = "f8455b0a92dd632757f7e7c22d5e438aa33da5f48d14483e5ee79dfc5468a4db"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"itertools",
|
||||
@@ -3552,9 +3552,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "spacetimedb-primitives"
|
||||
version = "2.2.0"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b668b51e7318207ae7eebcd4cae0c5d43bf713e7f229ac309ea2614a486ffde"
|
||||
checksum = "b20cc4bf97377f1dce9e75b2f6ce94bc5c7c2a243040a7a2016ac5cdb002793d"
|
||||
dependencies = [
|
||||
"bitflags 2.11.1",
|
||||
"either",
|
||||
@@ -3566,18 +3566,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "spacetimedb-query-builder"
|
||||
version = "2.2.0"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0186b1a2b3bf25bdd0f2676b61801fd754013ca6a58e1e24cc5148945388bc9d"
|
||||
checksum = "afa17878dbc23b4bfc06a45165c7afd34c8d29bba6dfde81625840c11380abce"
|
||||
dependencies = [
|
||||
"spacetimedb-lib",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spacetimedb-sats"
|
||||
version = "2.2.0"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11780ed69f178bf3784b7599da5171450e4b7ac6fd66b79e2e1861c867cef1a6"
|
||||
checksum = "74216db354eab5cefad9572a350654761495968478e83e51ef2c530cdf6cb1d4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arrayvec",
|
||||
@@ -3608,9 +3608,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "spacetimedb-schema"
|
||||
version = "2.2.0"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e4e9f8aa596e0e7034f0c8b3649d3fa3cc7bde340761519c3a3c60f10ec8888"
|
||||
checksum = "ff3ff36f901f6875907ff1df2b610fc396937b88f6793dfa04b0d9f298d74946"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"convert_case 0.6.0",
|
||||
@@ -3639,9 +3639,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "spacetimedb-sdk"
|
||||
version = "2.2.0"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41e82f20034b8aaeaa081871b07895aab45be1f0fc35e114ab64ae8e7e5c1a54"
|
||||
checksum = "26897e31aa58acd6cd0bcf12cf56a4ecd0cb4fc48053478626729e868f042e54"
|
||||
dependencies = [
|
||||
"anymap3",
|
||||
"base64 0.21.7",
|
||||
@@ -3671,9 +3671,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "spacetimedb-sql-parser"
|
||||
version = "2.2.0"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec5c77a2d4e3f42ede59598c56cb81a0fe54fd1974e2707f7140d1d5f41d08a7"
|
||||
checksum = "19428d4ddc8cf1eb34e58715ed512820ee9a3de187a89f88f80e18e914a086ae"
|
||||
dependencies = [
|
||||
"derive_more",
|
||||
"spacetimedb-lib",
|
||||
@@ -4569,7 +4569,7 @@ version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||
dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -115,9 +115,9 @@ serde_urlencoded = "0.7"
|
||||
sha1 = "0.10"
|
||||
sha2 = "0.10"
|
||||
socket2 = "0.6"
|
||||
spacetimedb = "2.2.0"
|
||||
spacetimedb-sdk = "2.2.0"
|
||||
spacetimedb-lib = { version = "2.2.0", default-features = false }
|
||||
spacetimedb = "2.3.0"
|
||||
spacetimedb-sdk = "2.3.0"
|
||||
spacetimedb-lib = { version = "2.3.0", default-features = false }
|
||||
time = "0.3"
|
||||
tokio = "1"
|
||||
tokio-stream = "0.1"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
// This was generated using spacetimedb cli version 2.2.0 (commit eb11e2f5c41dce6979715ad407996270d61329f6).
|
||||
// This was generated using spacetimedb cli version 2.3.0 (commit aa73d1c35b4b346b98eeba10a3d756b4ae72162f).
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
@@ -4995,19 +4995,19 @@ impl __sdk::SubscriptionHandle for SubscriptionHandle {
|
||||
/// either a [`DbConnection`] or an [`EventContext`] and operate on either.
|
||||
pub trait RemoteDbContext:
|
||||
__sdk::DbContext<
|
||||
DbView = RemoteTables,
|
||||
Reducers = RemoteReducers,
|
||||
SubscriptionBuilder = __sdk::SubscriptionBuilder<RemoteModule>,
|
||||
>
|
||||
DbView = RemoteTables,
|
||||
Reducers = RemoteReducers,
|
||||
SubscriptionBuilder = __sdk::SubscriptionBuilder<RemoteModule>,
|
||||
>
|
||||
{
|
||||
}
|
||||
impl<
|
||||
Ctx: __sdk::DbContext<
|
||||
Ctx: __sdk::DbContext<
|
||||
DbView = RemoteTables,
|
||||
Reducers = RemoteReducers,
|
||||
SubscriptionBuilder = __sdk::SubscriptionBuilder<RemoteModule>,
|
||||
>,
|
||||
> RemoteDbContext for Ctx
|
||||
> RemoteDbContext for Ctx
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -47,11 +47,9 @@ pub trait accept_quest {
|
||||
&self,
|
||||
input: QuestRecordInput,
|
||||
|
||||
callback: impl FnOnce(
|
||||
&super::ReducerEventContext,
|
||||
Result<Result<(), String>, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
callback: impl FnOnce(&super::ReducerEventContext, Result<Result<(), String>, __sdk::InternalError>)
|
||||
+ Send
|
||||
+ 'static,
|
||||
) -> __sdk::Result<()>;
|
||||
}
|
||||
|
||||
@@ -60,11 +58,9 @@ impl accept_quest for super::RemoteReducers {
|
||||
&self,
|
||||
input: QuestRecordInput,
|
||||
|
||||
callback: impl FnOnce(
|
||||
&super::ReducerEventContext,
|
||||
Result<Result<(), String>, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
callback: impl FnOnce(&super::ReducerEventContext, Result<Result<(), String>, __sdk::InternalError>)
|
||||
+ Send
|
||||
+ 'static,
|
||||
) -> __sdk::Result<()> {
|
||||
self.imp
|
||||
.invoke_reducer_with_callback(AcceptQuestArgs { input }, callback)
|
||||
|
||||
+6
-10
@@ -47,11 +47,9 @@ pub trait acknowledge_quest_completion {
|
||||
&self,
|
||||
input: QuestCompletionAckInput,
|
||||
|
||||
callback: impl FnOnce(
|
||||
&super::ReducerEventContext,
|
||||
Result<Result<(), String>, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
callback: impl FnOnce(&super::ReducerEventContext, Result<Result<(), String>, __sdk::InternalError>)
|
||||
+ Send
|
||||
+ 'static,
|
||||
) -> __sdk::Result<()>;
|
||||
}
|
||||
|
||||
@@ -60,11 +58,9 @@ impl acknowledge_quest_completion for super::RemoteReducers {
|
||||
&self,
|
||||
input: QuestCompletionAckInput,
|
||||
|
||||
callback: impl FnOnce(
|
||||
&super::ReducerEventContext,
|
||||
Result<Result<(), String>, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
callback: impl FnOnce(&super::ReducerEventContext, Result<Result<(), String>, __sdk::InternalError>)
|
||||
+ Send
|
||||
+ 'static,
|
||||
) -> __sdk::Result<()> {
|
||||
self.imp
|
||||
.invoke_reducer_with_callback(AcknowledgeQuestCompletionArgs { input }, callback)
|
||||
|
||||
+8
-8
@@ -31,10 +31,10 @@ pub trait admin_disable_profile_redeem_code {
|
||||
input: RuntimeProfileRedeemCodeAdminDisableInput,
|
||||
|
||||
__callback: impl FnOnce(
|
||||
&super::ProcedureEventContext,
|
||||
Result<RuntimeProfileRedeemCodeAdminProcedureResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
&super::ProcedureEventContext,
|
||||
Result<RuntimeProfileRedeemCodeAdminProcedureResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -44,10 +44,10 @@ impl admin_disable_profile_redeem_code for super::RemoteProcedures {
|
||||
input: RuntimeProfileRedeemCodeAdminDisableInput,
|
||||
|
||||
__callback: impl FnOnce(
|
||||
&super::ProcedureEventContext,
|
||||
Result<RuntimeProfileRedeemCodeAdminProcedureResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
&super::ProcedureEventContext,
|
||||
Result<RuntimeProfileRedeemCodeAdminProcedureResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
) {
|
||||
self.imp
|
||||
.invoke_procedure_with_callback::<_, RuntimeProfileRedeemCodeAdminProcedureResult>(
|
||||
|
||||
+8
-8
@@ -31,10 +31,10 @@ pub trait admin_disable_profile_task_config {
|
||||
input: RuntimeProfileTaskConfigAdminDisableInput,
|
||||
|
||||
__callback: impl FnOnce(
|
||||
&super::ProcedureEventContext,
|
||||
Result<RuntimeProfileTaskConfigAdminProcedureResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
&super::ProcedureEventContext,
|
||||
Result<RuntimeProfileTaskConfigAdminProcedureResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -44,10 +44,10 @@ impl admin_disable_profile_task_config for super::RemoteProcedures {
|
||||
input: RuntimeProfileTaskConfigAdminDisableInput,
|
||||
|
||||
__callback: impl FnOnce(
|
||||
&super::ProcedureEventContext,
|
||||
Result<RuntimeProfileTaskConfigAdminProcedureResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
&super::ProcedureEventContext,
|
||||
Result<RuntimeProfileTaskConfigAdminProcedureResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
) {
|
||||
self.imp
|
||||
.invoke_procedure_with_callback::<_, RuntimeProfileTaskConfigAdminProcedureResult>(
|
||||
|
||||
+8
-8
@@ -31,10 +31,10 @@ pub trait admin_list_profile_invite_codes {
|
||||
input: RuntimeProfileInviteCodeAdminListInput,
|
||||
|
||||
__callback: impl FnOnce(
|
||||
&super::ProcedureEventContext,
|
||||
Result<RuntimeProfileInviteCodeAdminListProcedureResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
&super::ProcedureEventContext,
|
||||
Result<RuntimeProfileInviteCodeAdminListProcedureResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -44,10 +44,10 @@ impl admin_list_profile_invite_codes for super::RemoteProcedures {
|
||||
input: RuntimeProfileInviteCodeAdminListInput,
|
||||
|
||||
__callback: impl FnOnce(
|
||||
&super::ProcedureEventContext,
|
||||
Result<RuntimeProfileInviteCodeAdminListProcedureResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
&super::ProcedureEventContext,
|
||||
Result<RuntimeProfileInviteCodeAdminListProcedureResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
) {
|
||||
self.imp
|
||||
.invoke_procedure_with_callback::<_, RuntimeProfileInviteCodeAdminListProcedureResult>(
|
||||
|
||||
+8
-8
@@ -34,10 +34,10 @@ pub trait admin_list_profile_recharge_products {
|
||||
input: RuntimeProfileRechargeProductAdminListInput,
|
||||
|
||||
__callback: impl FnOnce(
|
||||
&super::ProcedureEventContext,
|
||||
Result<RuntimeProfileRechargeProductAdminListProcedureResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
&super::ProcedureEventContext,
|
||||
Result<RuntimeProfileRechargeProductAdminListProcedureResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -47,10 +47,10 @@ impl admin_list_profile_recharge_products for super::RemoteProcedures {
|
||||
input: RuntimeProfileRechargeProductAdminListInput,
|
||||
|
||||
__callback: impl FnOnce(
|
||||
&super::ProcedureEventContext,
|
||||
Result<RuntimeProfileRechargeProductAdminListProcedureResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
&super::ProcedureEventContext,
|
||||
Result<RuntimeProfileRechargeProductAdminListProcedureResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
) {
|
||||
self.imp.invoke_procedure_with_callback::<_, RuntimeProfileRechargeProductAdminListProcedureResult>(
|
||||
"admin_list_profile_recharge_products",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user