mirror of
https://github.com/sinanyuntu/trade-message-center.git
synced 2026-09-17 13:22:11 +08:00
Merge pull request #46 from sinanyuntu/09-12-normalize-onetalk-contract-package
将 OneTalk 合约包迁移至 packages 目录
This commit is contained in:
+2
-1
@@ -2,6 +2,7 @@ node_modules/
|
||||
.pnpm-store/
|
||||
dist/
|
||||
apps/*/dist/
|
||||
packages/*/dist/
|
||||
.vite/
|
||||
*.tsbuildinfo
|
||||
.env
|
||||
@@ -13,4 +14,4 @@ apps/chrome-extension/.keys/
|
||||
|
||||
mock-log.text
|
||||
|
||||
.claude/skills/gitnexus
|
||||
.claude/skills/gitnexus
|
||||
|
||||
@@ -164,7 +164,7 @@ packages:
|
||||
mind-test-harness:
|
||||
path: apps/mind-test-harness
|
||||
onetalk-contract:
|
||||
path: apps/onetalk-contract
|
||||
path: packages/onetalk-contract
|
||||
server:
|
||||
path: apps/server
|
||||
default_package: @trade-message-center/chrome-extension
|
||||
|
||||
@@ -288,7 +288,7 @@ decodeOneTalkMessageContent(source: OneTalkMessageContentSource) ->
|
||||
| { status: "anomaly", code: OneTalkMediaAnomalyCode, mediaKind: "image" | "file" | "card" };
|
||||
```
|
||||
|
||||
`apps/onetalk-contract/src/content.ts` 是这六个 kind、exact-shape guard 和跨层读取模型的唯一 owner。`message-observer/content-decoder.ts` 是历史 SDK tuple 分类、受控 Base64 摘要解码和白名单投影的唯一生产点;它对名片只生产 marker,不生产客户资料 view。`read-projection.ts` 是服务端读取时将 marker 与同一账号/会话的当前 profile 组合为 `OneTalkBusinessCardViewContent` 的唯一 owner。
|
||||
`packages/onetalk-contract/src/content.ts` 是这六个 kind、exact-shape guard 和跨层读取模型的唯一 owner。`message-observer/content-decoder.ts` 是历史 SDK tuple 分类、受控 Base64 摘要解码和白名单投影的唯一生产点;它对名片只生产 marker,不生产客户资料 view。`read-projection.ts` 是服务端读取时将 marker 与同一账号/会话的当前 profile 组合为 `OneTalkBusinessCardViewContent` 的唯一 owner。
|
||||
|
||||
### 7.3 Contracts
|
||||
|
||||
|
||||
@@ -84,13 +84,13 @@ const retryLimit = options.retryLimit ?? DEFAULT_RETRY_LIMIT;
|
||||
### 1. Scope / Trigger
|
||||
|
||||
- Trigger:根 package 版本跨 workspace package metadata、扩展构建/watch 和 release CI 传播,且这些边界不得各自补偿或推导版本。
|
||||
- Scope:根 `package.json:version` 是唯一可编辑事实源;`apps/*/package.json` 的具体版本是同步镜像;扩展最终只加载生成的 `dist/manifest.json`。
|
||||
- Scope:根 `package.json:version` 是唯一可编辑事实源;`apps/*/package.json` 与 `packages/*/package.json` 的具体版本是同步镜像;扩展最终只加载生成的 `dist/manifest.json`。
|
||||
|
||||
### 2. Signatures
|
||||
|
||||
- `validatePackageVersion(value: unknown, source?: string): string`:验证同时满足 package metadata 和 Chrome Manifest 的三段数字版本。
|
||||
- `readRootPackageVersion(rootDir?: string): string`:只读取并验证 workspace 根 `package.json`。
|
||||
- `pnpm version:sync`:把根版本写入所有 `apps/*/package.json` 镜像。
|
||||
- `pnpm version:sync`:把根版本写入所有 `apps/*/package.json` 与 `packages/*/package.json` 镜像。
|
||||
- `pnpm version:check`:只检查镜像,不自动修复。
|
||||
- `pnpm version:ensure`:先执行 `version:sync`,再执行 `version:check`;根级 dev/build/typecheck/test/format:check 通过它获得稳定的一致版本环境。
|
||||
- `TMC_PACKAGE_VERSION`:由 workspace 根命令注入给下游扩展命令;扩展直接执行时缺失必须失败。
|
||||
|
||||
@@ -86,7 +86,7 @@ export const isObjectRecord = (value: unknown): value is Record<string, unknown>
|
||||
|
||||
### 3. Contracts
|
||||
|
||||
- `apps/onetalk-contract/src/guards.ts` owns strict `isPlainRecord` and the
|
||||
- `packages/onetalk-contract/src/guards.ts` owns strict `isPlainRecord` and the
|
||||
contract root facade is the only cross-package import path. It accepts
|
||||
current-realm plain objects and null-prototype objects, and rejects arrays,
|
||||
primitives, class/custom-prototype values, and cross-realm ordinary objects.
|
||||
|
||||
+5
-5
@@ -8,11 +8,11 @@ scope_lanes:
|
||||
path: /Users/ybf/work/trade-message-center
|
||||
role: task-branch
|
||||
owns:
|
||||
- apps/onetalk-contract/src/content.ts
|
||||
- apps/onetalk-contract/src/guards.ts
|
||||
- apps/onetalk-contract/src/contact-profiles.ts
|
||||
- apps/onetalk-contract/src/index.ts
|
||||
- apps/onetalk-contract/test/contract.test.ts
|
||||
- packages/onetalk-contract/src/content.ts
|
||||
- packages/onetalk-contract/src/guards.ts
|
||||
- packages/onetalk-contract/src/contact-profiles.ts
|
||||
- packages/onetalk-contract/src/index.ts
|
||||
- packages/onetalk-contract/test/contract.test.ts
|
||||
- apps/chrome-extension/src/onetalk/main-page/message-observer/content-decoder.ts
|
||||
- apps/chrome-extension/src/onetalk/main-page/message-observer/history.ts
|
||||
- apps/chrome-extension/src/onetalk/page-bridge/model.ts
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
{"file":".trellis/spec/project/missing-values.md","reason":"Review the root version synchronization and build-environment invariants after multi-root workspace discovery changes."}
|
||||
{"file":".trellis/spec/project/module-ownership.md","reason":"Review that the contract's canonical public package import and unique ownership remain unchanged by the physical move."}
|
||||
{"file":".trellis/spec/mind-test-harness/development/boundary.md","reason":"Review that the test harness remains a one-way consumer and is not accidentally included in production build scope."}
|
||||
@@ -0,0 +1,50 @@
|
||||
# OneTalk Contract Package Migration Design
|
||||
|
||||
## Boundary
|
||||
|
||||
`@trade-message-center/onetalk-contract` remains the same private workspace package. Its
|
||||
package name, `exports`, TypeScript source, test suite, and every consumer's `workspace:*`
|
||||
dependency stay unchanged. The migration changes only its physical root from
|
||||
`apps/onetalk-contract` to `packages/onetalk-contract` and updates repository metadata that
|
||||
names the physical root.
|
||||
|
||||
## Workspace Layout
|
||||
|
||||
The root workspace declarations will explicitly include both application and shared-library
|
||||
directories:
|
||||
|
||||
```yaml
|
||||
packages:
|
||||
- apps/*
|
||||
- packages/*
|
||||
```
|
||||
|
||||
The root `package.json#workspaces` mirrors those two globs. pnpm will then rewrite the
|
||||
lockfile importer from `apps/onetalk-contract` to `packages/onetalk-contract`, and the
|
||||
relative workspace links from app consumers will resolve as `../../packages/onetalk-contract`.
|
||||
No consumer manifest needs a dependency-name change.
|
||||
|
||||
## Build and Release Metadata
|
||||
|
||||
All root commands retain their package-name filters. Only physical path references change:
|
||||
|
||||
- Docker copies the package manifest and source from `packages/onetalk-contract`.
|
||||
- Generated-dist cleanup deletes `packages/onetalk-contract/dist`.
|
||||
- `.gitignore` covers dist for both `apps/*` and `packages/*`.
|
||||
- `listWorkspacePackagePaths` enumerates a named, deterministic list of both workspace root
|
||||
directories. This preserves root-version mirroring for every package and is covered by
|
||||
fixtures containing one app and one shared package.
|
||||
|
||||
## Documentation and Task Coordination
|
||||
|
||||
Current specs, the package registry, and non-archived technical docs will point at the new
|
||||
path. Archived task records remain historical evidence and retain their original locations.
|
||||
The live structured-message task's worktree registry will receive only the directory mapping;
|
||||
its source and acceptance plan are not changed.
|
||||
|
||||
## Compatibility and Rollback
|
||||
|
||||
The package name and public API are stable, so imports and runtime resolution remain
|
||||
compatible. The only operational compatibility condition is using the updated lockfile together
|
||||
with the new workspace glob. Reverting is a single Git commit revert that restores the previous
|
||||
directory and paths; no data migration or deployed protocol rollback is needed.
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
{"file":".trellis/spec/project/missing-values.md","reason":"Defines root version mirroring and workspace quality-command invariants affected by adding packages/* discovery."}
|
||||
{"file":".trellis/spec/project/module-ownership.md","reason":"Defines the OneTalk contract as the canonical cross-package facade and constrains its stable public import path."}
|
||||
{"file":".trellis/spec/mind-test-harness/development/boundary.md","reason":"Records harness dependency direction and contract consumption that must remain unchanged after relocation."}
|
||||
@@ -0,0 +1,27 @@
|
||||
# Implementation Plan
|
||||
|
||||
1. Establish the path-impact baseline: run GitNexus impact analysis for
|
||||
`listWorkspacePackagePaths`, then confirm the package and generated-dist path references
|
||||
found during planning.
|
||||
2. Move the complete contract package unchanged from `apps/onetalk-contract/` to
|
||||
`packages/onetalk-contract/` with Git-aware renames.
|
||||
3. Update root workspace globs, Docker build copies, generated-dist cleanup, ignore patterns,
|
||||
Trellis package registry, active-task coordination registry, and current documentation/spec
|
||||
links. Leave archive paths unchanged.
|
||||
4. Generalize workspace package discovery to both top-level workspace roots and update the
|
||||
package-version/release fixtures to assert version mirroring and release file selection for
|
||||
a shared package.
|
||||
5. Regenerate the pnpm lockfile through pnpm so importer and link paths are canonical; verify
|
||||
the frozen lockfile install path without changing dependency versions.
|
||||
6. Run targeted script tests, contract build/test, root typecheck/test/build, formatting, and
|
||||
a Dockerfile path/static check. Review the diff for source-content changes under the moved
|
||||
package, stale non-archive paths, lockfile-only dependency churn, and unexpected execution
|
||||
flow changes.
|
||||
|
||||
## Risk Gates
|
||||
|
||||
- Do not alter files below `packages/onetalk-contract/src/` or `test/` other than their Git
|
||||
rename. A source-content diff is a stop-and-investigate condition.
|
||||
- Do not update archive task/doc paths: they describe prior checkout state.
|
||||
- If the live structured-message checkout begins editing the old package during this migration,
|
||||
pause before destructive conflict resolution; preserve its work and report the required rebase.
|
||||
@@ -0,0 +1,63 @@
|
||||
# 规范化 OneTalk 合约包位置
|
||||
|
||||
## Goal
|
||||
|
||||
将共享 `@trade-message-center/onetalk-contract` 从应用目录迁移到共享库目录,使
|
||||
monorepo 的目录语义、pnpm workspace 发现、构建和镜像打包路径一致,同时保持包名与
|
||||
消费者行为不变。
|
||||
|
||||
## Confirmed Facts
|
||||
|
||||
- 根 `package.json` 和 `pnpm-workspace.yaml` 当前仅发现 `apps/*`;contract 因此被当作
|
||||
app 纳入 workspace。
|
||||
- contract 是 chrome extension、server 和 Mind test harness 的共同依赖,包名均为
|
||||
`@trade-message-center/onetalk-contract`,依赖声明均为 `workspace:*`。
|
||||
- 根开发、构建、类型检查和测试脚本都按 package name 过滤 contract,因此迁移无需改变
|
||||
filter 或消费者 import。
|
||||
- `Dockerfile.server`、`scripts/clean-generated-dist.mjs`、lockfile importer、Trellis
|
||||
package registry,以及当前非归档文档/规范中存在旧物理路径。
|
||||
- `scripts/package-version.mjs` 当前只枚举 `apps/`,若 contract 移至 `packages/` 而
|
||||
不调整,该共享包将脱离根版本镜像的校验。
|
||||
- 另一个进行中的任务
|
||||
`09-11-onetalk-structured-message-information-collection-classification` 在独立 checkout
|
||||
中拥有 contract 源码与测试文件;其 task worktree registry 仍列出旧路径。
|
||||
|
||||
## Requirements
|
||||
|
||||
- R1:将 contract 的完整源码、测试、package manifest 与 TypeScript 配置迁移到
|
||||
`packages/onetalk-contract/`;包名、exports、版本和 public API 不变。
|
||||
- R2:pnpm workspace 发现范围须同时包含 `apps/*` 与 `packages/*`;根 `workspaces` 声明
|
||||
与 `pnpm-workspace.yaml` 保持一致。
|
||||
- R3:所有生产构建、开发启动、清理和 Docker copy 路径必须指向新目录,且 contract 仍
|
||||
在其消费者启动/构建前完成构建。
|
||||
- R4:根版本同步/检查必须枚举 apps 和 packages 下全部 workspace package,且测试覆盖
|
||||
该跨目录发现行为。
|
||||
- R5:更新反映当前结构的 Trellis package registry、项目/包级规范与非归档技术文档;
|
||||
不重写 archive 中的历史路径证据。
|
||||
- R6:更新 pnpm lockfile importer 和 workspace links,使 frozen install 与依赖解析从
|
||||
新位置工作。
|
||||
- R7:立即执行迁移;仅做目录与路径/配置修正,不改变 contract 源码内容、协议、导出或
|
||||
消费者业务逻辑。同步当前结构化消息任务的 worktree registry 路径,避免其后续工作误占
|
||||
已失效目录。
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] AC1:`packages/onetalk-contract/package.json` 存在,`apps/onetalk-contract/` 不再
|
||||
存在,且 Git 识别为源码迁移而非内容重写。
|
||||
- [ ] AC2:`pnpm --filter @trade-message-center/onetalk-contract build`、contract test、
|
||||
根 typecheck/test/build 在相应范围内成功,并证明 server、extension、harness 仍解析
|
||||
相同的 workspace package。
|
||||
- [ ] AC3:`pnpm install --frozen-lockfile` 不因 importer 或 link 路径失效;lockfile 的
|
||||
importer 和消费者 workspace link 均指向 `packages/onetalk-contract`。
|
||||
- [ ] AC4:版本镜像检查覆盖并通过 `apps/*` 与 `packages/*`,并有测试防止将来遗漏共享
|
||||
package。
|
||||
- [ ] AC5:Docker build context 中 contract manifest 与源码 copy 均使用新路径;开发
|
||||
启动的 dist 清理列表也使用新路径。
|
||||
- [ ] AC6:当前规范/文档和 Trellis package registry 只指向新目录;归档资料保留原始
|
||||
历史路径。
|
||||
|
||||
## Out of Scope
|
||||
|
||||
- 不改变 OneTalk 协议、类型、导出、消费者 import、运行时行为或 package name。
|
||||
- 不合并、迁移或改写当前进行中任务在另一 checkout 的未合入源码变更。
|
||||
- 不修改 archive 中作为历史证据记录的路径。
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"id": "normalize-onetalk-contract-package",
|
||||
"name": "normalize-onetalk-contract-package",
|
||||
"title": "规范化 OneTalk 合约包位置",
|
||||
"description": "将共享 onetalk-contract 从 apps 迁移到 packages,并更新 workspace 与构建路径引用",
|
||||
"status": "completed",
|
||||
"dev_type": null,
|
||||
"scope": null,
|
||||
"package": null,
|
||||
"priority": "P2",
|
||||
"creator": "ybf",
|
||||
"assignee": "ybf",
|
||||
"createdAt": "2026-09-12",
|
||||
"completedAt": "2026-09-12",
|
||||
"branch": "09-12-normalize-onetalk-contract-package",
|
||||
"base_branch": "main",
|
||||
"worktree_path": null,
|
||||
"commit": null,
|
||||
"pr_url": null,
|
||||
"subtasks": [],
|
||||
"children": [],
|
||||
"parent": null,
|
||||
"relatedFiles": [],
|
||||
"notes": "",
|
||||
"meta": {}
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
<!-- @@@auto:current-status -->
|
||||
- **Active File**: `journal-1.md`
|
||||
- **Total Sessions**: 61
|
||||
- **Total Sessions**: 62
|
||||
- **Last Active**: 2026-09-12
|
||||
<!-- @@@/auto:current-status -->
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<!-- @@@auto:active-documents -->
|
||||
| File | Lines | Status |
|
||||
|------|-------|--------|
|
||||
| `journal-1.md` | ~1321 | Active |
|
||||
| `journal-1.md` | ~1343 | Active |
|
||||
<!-- @@@/auto:active-documents -->
|
||||
|
||||
---
|
||||
@@ -29,6 +29,7 @@
|
||||
<!-- @@@auto:session-history -->
|
||||
| # | Date | Title | Commits | Branch |
|
||||
|---|------|-------|---------|--------|
|
||||
| 62 | 2026-09-12 | 规范化 OneTalk 合约包位置 | `4d46b5b` | `09-12-normalize-onetalk-contract-package` |
|
||||
| 61 | 2026-09-12 | 收敛 OneTalk WebSocket 路由分发 | `bd9cc6a` | `09-11-websocket-routing-single-source-of-truth` |
|
||||
| 60 | 2026-09-11 | 隔离发送观测 pending | `1a899a9` | `send-observation-pending` |
|
||||
| 59 | 2026-09-11 | 移除 pending-send 终态历史 | `63d9e5b` | `fix/remove-terminal-send-history` |
|
||||
|
||||
@@ -1319,3 +1319,25 @@ Moved Mind-only and Plugin-only WebSocket implementations to canonical endpoint
|
||||
### Status
|
||||
|
||||
[OK] **Completed**
|
||||
|
||||
|
||||
## Session 62: 规范化 OneTalk 合约包位置
|
||||
<!-- trellis-session: v=2 fp=bd1df2949d2c5cd9 -->
|
||||
|
||||
**Date**: 2026-09-12
|
||||
**Task**: 规范化 OneTalk 合约包位置
|
||||
**Branch**: `09-12-normalize-onetalk-contract-package`
|
||||
|
||||
### Summary
|
||||
|
||||
将共享 onetalk-contract 从 apps 迁至 packages;同步 workspace、构建、锁文件、版本镜像、文档和任务路径,并完成独立验证。
|
||||
|
||||
### Git Commits
|
||||
|
||||
| Hash | Message |
|
||||
|------|---------|
|
||||
| `4d46b5b` | refactor(onetalk-contract): move shared contract to packages |
|
||||
|
||||
### Status
|
||||
|
||||
[OK] **Completed**
|
||||
|
||||
+2
-2
@@ -8,13 +8,13 @@ RUN corepack enable && corepack prepare pnpm@11.7.0 --activate
|
||||
ENV HUSKY=0
|
||||
|
||||
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml tsconfig.base.json ./
|
||||
COPY apps/onetalk-contract/package.json apps/onetalk-contract/package.json
|
||||
COPY packages/onetalk-contract/package.json packages/onetalk-contract/package.json
|
||||
COPY apps/server/package.json apps/server/package.json
|
||||
COPY apps/chrome-extension/package.json apps/chrome-extension/package.json
|
||||
|
||||
RUN pnpm install --frozen-lockfile --registry=https://registry.npmjs.org
|
||||
|
||||
COPY apps/onetalk-contract apps/onetalk-contract
|
||||
COPY packages/onetalk-contract packages/onetalk-contract
|
||||
COPY apps/server apps/server
|
||||
|
||||
RUN pnpm --filter @trade-message-center/server build && \
|
||||
|
||||
@@ -436,7 +436,7 @@ Base64
|
||||
→ normalized image content
|
||||
```
|
||||
|
||||
输出合同定义于 [`content.ts`](../apps/onetalk-contract/src/content.ts):
|
||||
输出合同定义于 [`content.ts`](../packages/onetalk-contract/src/content.ts):
|
||||
|
||||
```ts
|
||||
type OneTalkImageContent = {
|
||||
@@ -688,7 +688,7 @@ raw `originalData` 只应在 MAIN world 短暂存在。send correlator 应消费
|
||||
- live frame 分发:[`index.ts`](../apps/chrome-extension/src/onetalk/main-page/message-observer/index.ts)
|
||||
- live message 解析:[`new.ts`](../apps/chrome-extension/src/onetalk/main-page/message-observer/new.ts)
|
||||
- MAIN 内容归一化:[`content-decoder.ts`](../apps/chrome-extension/src/onetalk/main-page/message-observer/content-decoder.ts)
|
||||
- 归一化媒体合同:[`content.ts`](../apps/onetalk-contract/src/content.ts)
|
||||
- 归一化媒体合同:[`content.ts`](../packages/onetalk-contract/src/content.ts)
|
||||
- observer/correlator 接线:[`page-script-entry.ts`](../apps/chrome-extension/src/onetalk/main-page/page-script-entry.ts)
|
||||
- 当前文本发送确认:[`send-observation.ts`](../apps/chrome-extension/src/onetalk/main-page/message-observer/send-observation.ts)
|
||||
- 当前页面命令入口:[`page-command.ts`](../apps/chrome-extension/src/onetalk/main-page/current-conversation-history/page-command.ts)
|
||||
|
||||
@@ -416,7 +416,7 @@ nodeName
|
||||
|
||||
### 10.1 共享内容解码器
|
||||
|
||||
在 `apps/onetalk-contract` 或其最近共同父目录增加一个唯一的内容投影器,输入原始 `content`,输出判别联合:
|
||||
在 `packages/onetalk-contract` 或其最近共同父目录增加一个唯一的内容投影器,输入原始 `content`,输出判别联合:
|
||||
|
||||
```text
|
||||
text
|
||||
|
||||
@@ -902,8 +902,8 @@ aliIdEncrypt
|
||||
| 历史只读 SDK | `apps/chrome-extension/src/onetalk/main-page/current-conversation-history/sdk.ts` | 调用 `fetchMessagesWithoutUpdateToRead` |
|
||||
| 页面到 SW | `apps/chrome-extension/src/onetalk/service-worker/sync-engine/helpers.ts` | 原样复制观察字段 |
|
||||
| IndexedDB 校验 | `apps/chrome-extension/src/onetalk/service-worker/storage.ts` | 只检查 JSON 与基础字段类型 |
|
||||
| 共享合同 | `apps/onetalk-contract/src/model.ts` | `content` 为任意 JSON value |
|
||||
| 共享 decoder | `apps/onetalk-contract/src/decoder.ts` | 不理解 text/image/file 语义 |
|
||||
| 共享合同 | `packages/onetalk-contract/src/model.ts` | `content` 为任意 JSON value |
|
||||
| 共享 decoder | `packages/onetalk-contract/src/decoder.ts` | 不理解 text/image/file 语义 |
|
||||
| Bright 清洗 | `apps/server/src/onetalk/service.ts` | 按键过滤对象,字符串原样通过 |
|
||||
| Bright 持久化 | `apps/server/src/database/schema/onetalk.ts` | `content jsonb`、`text nullable` |
|
||||
| 开发展示 | `apps/mind-test-harness/src/harness.ts` | 只展示 raw JSON |
|
||||
|
||||
+2
-1
@@ -3,7 +3,8 @@
|
||||
"version": "0.8.27",
|
||||
"private": true,
|
||||
"workspaces": [
|
||||
"apps/*"
|
||||
"apps/*",
|
||||
"packages/*"
|
||||
],
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
Generated
+12
-12
@@ -25,7 +25,7 @@ importers:
|
||||
dependencies:
|
||||
'@trade-message-center/onetalk-contract':
|
||||
specifier: workspace:*
|
||||
version: link:../onetalk-contract
|
||||
version: link:../../packages/onetalk-contract
|
||||
devDependencies:
|
||||
'@types/node':
|
||||
specifier: ^22.10.2
|
||||
@@ -41,16 +41,7 @@ importers:
|
||||
dependencies:
|
||||
'@trade-message-center/onetalk-contract':
|
||||
specifier: workspace:*
|
||||
version: link:../onetalk-contract
|
||||
devDependencies:
|
||||
'@types/node':
|
||||
specifier: ^22.10.2
|
||||
version: 22.20.1
|
||||
typescript:
|
||||
specifier: ^5.7.3
|
||||
version: 5.7.3
|
||||
|
||||
apps/onetalk-contract:
|
||||
version: link:../../packages/onetalk-contract
|
||||
devDependencies:
|
||||
'@types/node':
|
||||
specifier: ^22.10.2
|
||||
@@ -66,7 +57,7 @@ importers:
|
||||
version: 11.3.0
|
||||
'@trade-message-center/onetalk-contract':
|
||||
specifier: workspace:*
|
||||
version: link:../onetalk-contract
|
||||
version: link:../../packages/onetalk-contract
|
||||
drizzle-orm:
|
||||
specifier: ^0.45.2
|
||||
version: 0.45.2(postgres@3.4.9)
|
||||
@@ -87,6 +78,15 @@ importers:
|
||||
specifier: ^5.7.3
|
||||
version: 5.7.3
|
||||
|
||||
packages/onetalk-contract:
|
||||
devDependencies:
|
||||
'@types/node':
|
||||
specifier: ^22.10.2
|
||||
version: 22.20.1
|
||||
typescript:
|
||||
specifier: ^5.7.3
|
||||
version: 5.7.3
|
||||
|
||||
packages:
|
||||
|
||||
'@drizzle-team/brocli@0.10.2':
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
packages:
|
||||
- apps/*
|
||||
- packages/*
|
||||
allowBuilds:
|
||||
esbuild: true
|
||||
|
||||
@@ -6,7 +6,7 @@ import { fileURLToPath, pathToFileURL } from "node:url";
|
||||
|
||||
const workspaceDir = resolve(fileURLToPath(new URL(".", import.meta.url)), "..");
|
||||
export const GENERATED_DIST_RELATIVE_PATHS = [
|
||||
"apps/onetalk-contract/dist",
|
||||
"packages/onetalk-contract/dist",
|
||||
"apps/server/dist",
|
||||
"apps/chrome-extension/dist",
|
||||
];
|
||||
|
||||
@@ -25,13 +25,19 @@ const readJsonFile = (filePath) => {
|
||||
}
|
||||
};
|
||||
|
||||
/** 枚举 apps 下可参与版本镜像的 workspace package。 */
|
||||
const workspacePackageDirectories = ["apps", "packages"];
|
||||
|
||||
/** 按 workspace 根目录顺序枚举可参与版本镜像的 package。 */
|
||||
export const listWorkspacePackagePaths = (rootDir = workspaceDir) =>
|
||||
readdirSync(resolve(rootDir, "apps"), { withFileTypes: true })
|
||||
.filter((entry) => entry.isDirectory())
|
||||
.map((entry) => resolve(rootDir, "apps", entry.name, "package.json"))
|
||||
.filter((packagePath) => existsSync(packagePath))
|
||||
.sort();
|
||||
workspacePackageDirectories.flatMap((directory) => {
|
||||
const directoryPath = resolve(rootDir, directory);
|
||||
if (!existsSync(directoryPath)) return [];
|
||||
return readdirSync(directoryPath, { withFileTypes: true })
|
||||
.filter((entry) => entry.isDirectory())
|
||||
.map((entry) => resolve(directoryPath, entry.name, "package.json"))
|
||||
.filter((packagePath) => existsSync(packagePath))
|
||||
.sort();
|
||||
});
|
||||
|
||||
/** 校验同时满足 npm 与 Chrome Manifest 的三段版本。 */
|
||||
export const validatePackageVersion = (version, source = "package.json") => {
|
||||
|
||||
@@ -3,13 +3,14 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { mkdtempSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { join, relative } from "node:path";
|
||||
import test from "node:test";
|
||||
|
||||
import {
|
||||
bumpPackageVersion,
|
||||
readRootPackageVersion,
|
||||
validatePackageVersion,
|
||||
listWorkspacePackagePaths,
|
||||
writeRootPackageVersion,
|
||||
} from "./package-version.mjs";
|
||||
import { checkPackageVersions } from "./check-package-versions.mjs";
|
||||
@@ -18,16 +19,17 @@ import { syncPackageVersions } from "./sync-package-versions.mjs";
|
||||
const createWorkspace = (rootVersion, packageVersions) => {
|
||||
const rootDir = mkdtempSync(join(tmpdir(), "tmc-package-version-"));
|
||||
mkdirSync(join(rootDir, "apps"));
|
||||
mkdirSync(join(rootDir, "packages"));
|
||||
writeFileSync(
|
||||
join(rootDir, "package.json"),
|
||||
`${JSON.stringify({ name: "fixture", version: rootVersion }, null, 4)}\n`,
|
||||
);
|
||||
for (const [name, version] of Object.entries(packageVersions)) {
|
||||
const packageDir = join(rootDir, "apps", name);
|
||||
for (const [relativePackagePath, version] of Object.entries(packageVersions)) {
|
||||
const packageDir = join(rootDir, relativePackagePath);
|
||||
mkdirSync(packageDir);
|
||||
writeFileSync(
|
||||
join(packageDir, "package.json"),
|
||||
`${JSON.stringify({ name, version }, null, 4)}\n`,
|
||||
`${JSON.stringify({ name: relativePackagePath, version }, null, 4)}\n`,
|
||||
);
|
||||
}
|
||||
return rootDir;
|
||||
@@ -72,13 +74,25 @@ test("bumps and writes the root version without creating another version source"
|
||||
assert.equal(readRootPackageVersion(rootDir), "0.8.17");
|
||||
});
|
||||
|
||||
test("syncs and checks every app package against the root version", () => {
|
||||
const rootDir = createWorkspace("1.2.3", { extension: "0.1.0", server: "0.1.0" });
|
||||
test("syncs and checks app and shared workspace package mirrors against the root version", () => {
|
||||
const rootDir = createWorkspace("1.2.3", {
|
||||
"apps/extension": "0.1.0",
|
||||
"apps/server": "0.1.0",
|
||||
"packages/contract": "0.1.0",
|
||||
});
|
||||
|
||||
assert.throws(() => checkPackageVersions(rootDir), /must equal root version 1.2.3/u);
|
||||
syncPackageVersions(rootDir);
|
||||
assert.equal(readRootPackageVersion(rootDir), "1.2.3");
|
||||
checkPackageVersions(rootDir);
|
||||
assert.deepEqual(
|
||||
listWorkspacePackagePaths(rootDir).map((packagePath) => relative(rootDir, packagePath)),
|
||||
[
|
||||
"apps/extension/package.json",
|
||||
"apps/server/package.json",
|
||||
"packages/contract/package.json",
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
test("syncs version mirrors before root quality commands check them", () => {
|
||||
|
||||
@@ -11,12 +11,17 @@ import { prepareRelease, publishRelease } from "./release.mjs";
|
||||
const createWorkspace = (version = "0.8.16") => {
|
||||
const rootDir = mkdtempSync(join(tmpdir(), "tmc-release-"));
|
||||
mkdirSync(join(rootDir, "apps"));
|
||||
mkdirSync(join(rootDir, "packages"));
|
||||
writeFileSync(
|
||||
join(rootDir, "package.json"),
|
||||
`${JSON.stringify({ name: "fixture", version }, null, 4)}\n`,
|
||||
);
|
||||
for (const name of ["extension", "server"]) {
|
||||
const packageDir = join(rootDir, "apps", name);
|
||||
for (const [directory, name] of [
|
||||
["apps", "extension"],
|
||||
["apps", "server"],
|
||||
["packages", "contract"],
|
||||
]) {
|
||||
const packageDir = join(rootDir, directory, name);
|
||||
mkdirSync(packageDir);
|
||||
writeFileSync(
|
||||
join(packageDir, "package.json"),
|
||||
@@ -41,7 +46,12 @@ const createRunner = (responses) => {
|
||||
return { calls, execute };
|
||||
};
|
||||
|
||||
const expectedFiles = ["package.json", "apps/extension/package.json", "apps/server/package.json"];
|
||||
const expectedFiles = [
|
||||
"package.json",
|
||||
"apps/extension/package.json",
|
||||
"apps/server/package.json",
|
||||
"packages/contract/package.json",
|
||||
];
|
||||
|
||||
test("prepares a patch release using only root-derived version mirrors", () => {
|
||||
const rootDir = createWorkspace();
|
||||
@@ -63,9 +73,11 @@ test("prepares a patch release using only root-derived version mirrors", () => {
|
||||
|
||||
assert.equal(prepareRelease({ rootDir, execute }), "0.8.17");
|
||||
assert.equal(JSON.parse(readFileSync(join(rootDir, "package.json"), "utf8")).version, "0.8.17");
|
||||
for (const packageName of ["extension", "server"]) {
|
||||
const packagePath = join(rootDir, "apps", packageName, "package.json");
|
||||
assert.equal(JSON.parse(readFileSync(packagePath, "utf8")).version, "0.8.17");
|
||||
for (const packagePath of expectedFiles.slice(1)) {
|
||||
assert.equal(
|
||||
JSON.parse(readFileSync(join(rootDir, packagePath), "utf8")).version,
|
||||
"0.8.17",
|
||||
);
|
||||
}
|
||||
assert.deepEqual(
|
||||
calls.filter(({ command }) => command === "pnpm").map(({ arguments_ }) => arguments_),
|
||||
|
||||
Reference in New Issue
Block a user