diff --git a/.changeset/empty-schools-return.md b/.changeset/empty-schools-return.md
new file mode 100644
index 000000000..a742ead2c
--- /dev/null
+++ b/.changeset/empty-schools-return.md
@@ -0,0 +1,6 @@
+---
+"@ckb-ccc/core": patch
+---
+
+fix(core): occupy witness while adding output
+
\ No newline at end of file
diff --git a/.changeset/odd-experts-win.md b/.changeset/odd-experts-win.md
new file mode 100644
index 000000000..c2893bce6
--- /dev/null
+++ b/.changeset/odd-experts-win.md
@@ -0,0 +1,6 @@
+---
+"@ckb-ccc/co-build": minor
+---
+
+feat(co-build): new CoBuild package
+
\ No newline at end of file
diff --git a/packages/co-build/.npmignore b/packages/co-build/.npmignore
new file mode 100644
index 000000000..7a88408aa
--- /dev/null
+++ b/packages/co-build/.npmignore
@@ -0,0 +1,21 @@
+node_modules/
+misc/
+
+*test.js
+*test.ts
+*test.d.ts
+*test.d.ts.map
+*spec.js
+*spec.ts
+*spec.d.ts
+*spec.d.ts.map
+
+tsconfig.json
+tsconfig.*.json
+eslint.config.mjs
+.prettierrc
+.prettierignore
+
+tsconfig.tsbuildinfo
+tsconfig.*.tsbuildinfo
+.github/
diff --git a/packages/co-build/.prettierignore b/packages/co-build/.prettierignore
new file mode 100644
index 000000000..aef5d239c
--- /dev/null
+++ b/packages/co-build/.prettierignore
@@ -0,0 +1,15 @@
+node_modules/
+
+dist/
+dist.commonjs/
+
+.npmignore
+.prettierrc
+tsconfig.json
+eslint.config.mjs
+prettier.config.*
+
+tsconfig.tsbuildinfo
+.github/
+
+CHANGELOG.md
diff --git a/packages/co-build/README.md b/packages/co-build/README.md
new file mode 100644
index 000000000..0075a73ae
--- /dev/null
+++ b/packages/co-build/README.md
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+ @ckb-ccc/co-build
+
+
+
+ CCC - CKBers' Codebase is a one-stop solution for your CKB JS/TS ecosystem development.
+
+
+## Overview
+
+`@ckb-ccc/co-build` provides tools and Molecule codecs to support the CKB Co-build Protocol, a standard for coordinating multi-party signers and complex script operations (such as Spore actions) within a single transaction.
+
+## Quick Example
+
+```typescript
+import { ccc } from "@ckb-ccc/core";
+import { CoBuild, ScriptInfo } from "@ckb-ccc/co-build";
+
+const coBuild = new CoBuild(
+ {
+ codeHash: "0x...",
+ hashType: "type",
+ args: "0x...",
+ },
+ {
+ name: "MyScript",
+ url: "https://example.com",
+ schema: "MySchema",
+ messageType: "MyMessageType",
+ },
+);
+
+// Build action
+const action = coBuild.buildAction(ccc.bytesFrom("action payload"));
+
+// Append to transaction
+const { tx } = await coBuild.appendActions(transaction, action);
+```
diff --git a/packages/co-build/eslint.config.mjs b/packages/co-build/eslint.config.mjs
new file mode 100644
index 000000000..b6132c277
--- /dev/null
+++ b/packages/co-build/eslint.config.mjs
@@ -0,0 +1,62 @@
+// @ts-check
+
+import eslint from "@eslint/js";
+import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended";
+import tseslint from "typescript-eslint";
+
+import { dirname } from "path";
+import { fileURLToPath } from "url";
+
+export default [
+ ...tseslint.config({
+ files: ["**/*.ts"],
+ extends: [
+ eslint.configs.recommended,
+ ...tseslint.configs.recommendedTypeChecked,
+ ],
+ rules: {
+ "@typescript-eslint/no-unused-vars": [
+ "error",
+ {
+ args: "all",
+ argsIgnorePattern: "^_",
+ caughtErrors: "all",
+ caughtErrorsIgnorePattern: "^_",
+ destructuredArrayIgnorePattern: "^_",
+ varsIgnorePattern: "^_",
+ ignoreRestSiblings: true,
+ },
+ ],
+ "@typescript-eslint/unbound-method": ["error", { ignoreStatic: true }],
+ "@typescript-eslint/no-unsafe-member-access": "off",
+ "@typescript-eslint/require-await": "off",
+ "@typescript-eslint/only-throw-error": [
+ "error",
+ {
+ allowThrowingAny: true,
+ allowThrowingUnknown: true,
+ allowRethrowing: true,
+ },
+ ],
+ "@typescript-eslint/prefer-promise-reject-errors": [
+ "error",
+ {
+ allowThrowingAny: true,
+ allowThrowingUnknown: true,
+ },
+ ],
+ "no-empty": "off",
+ "prefer-const": [
+ "error",
+ { ignoreReadBeforeAssign: true, destructuring: "all" },
+ ],
+ },
+ languageOptions: {
+ parserOptions: {
+ project: true,
+ tsconfigRootDir: dirname(fileURLToPath(import.meta.url)),
+ },
+ },
+ }),
+ eslintPluginPrettierRecommended,
+];
diff --git a/packages/co-build/misc/basedirs/dist.commonjs/package.json b/packages/co-build/misc/basedirs/dist.commonjs/package.json
new file mode 100644
index 000000000..5bbefffba
--- /dev/null
+++ b/packages/co-build/misc/basedirs/dist.commonjs/package.json
@@ -0,0 +1,3 @@
+{
+ "type": "commonjs"
+}
diff --git a/packages/co-build/misc/basedirs/dist/package.json b/packages/co-build/misc/basedirs/dist/package.json
new file mode 100644
index 000000000..3dbc1ca59
--- /dev/null
+++ b/packages/co-build/misc/basedirs/dist/package.json
@@ -0,0 +1,3 @@
+{
+ "type": "module"
+}
diff --git a/packages/co-build/package.json b/packages/co-build/package.json
new file mode 100644
index 000000000..acc6e3704
--- /dev/null
+++ b/packages/co-build/package.json
@@ -0,0 +1,54 @@
+{
+ "name": "@ckb-ccc/co-build",
+ "version": "0.0.0",
+ "description": "CoBuild",
+ "author": "Hanssen <0@hanssen0.com>",
+ "license": "MIT",
+ "private": false,
+ "homepage": "https://github.com/ckb-devrel/ccc",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/ckb-devrel/ccc.git"
+ },
+ "main": "./dist.commonjs/index.js",
+ "module": "./dist/index.mjs",
+ "exports": {
+ ".": {
+ "import": "./dist/index.mjs",
+ "require": "./dist.commonjs/index.js"
+ },
+ "./barrel": {
+ "import": "./dist/barrel.mjs",
+ "require": "./dist.commonjs/barrel.js"
+ },
+ "./package.json": "./package.json"
+ },
+ "scripts": {
+ "test": "vitest",
+ "test:ci": "vitest run",
+ "build": "tsdown",
+ "lint": "tsc --noEmit && eslint ./src",
+ "format": "prettier --write . && eslint --fix ./src"
+ },
+ "devDependencies": {
+ "@eslint/js": "^10.0.1",
+ "@types/node": "^26.0.0",
+ "eslint": "^10.5.0",
+ "eslint-config-prettier": "^10.1.8",
+ "eslint-plugin-prettier": "^5.5.6",
+ "prettier": "^3.8.4",
+ "prettier-plugin-organize-imports": "^4.3.0",
+ "tsdown": "^0.22.3",
+ "typescript": "^6.0.3",
+ "typescript-eslint": "^8.61.1",
+ "vitest": "^4.1.9"
+ },
+ "publishConfig": {
+ "access": "public"
+ },
+ "dependencies": {
+ "@ckb-ccc/core": "workspace:*"
+ },
+ "packageManager": "pnpm@11.8.0",
+ "types": "./dist.commonjs/index.d.ts"
+}
diff --git a/packages/co-build/prettier.config.cjs b/packages/co-build/prettier.config.cjs
new file mode 100644
index 000000000..5e1810363
--- /dev/null
+++ b/packages/co-build/prettier.config.cjs
@@ -0,0 +1,11 @@
+/**
+ * @see https://prettier.io/docs/configuration
+ * @type {import("prettier").Config}
+ */
+const config = {
+ singleQuote: false,
+ trailingComma: "all",
+ plugins: [require.resolve("prettier-plugin-organize-imports")],
+};
+
+module.exports = config;
diff --git a/packages/co-build/src/barrel.ts b/packages/co-build/src/barrel.ts
new file mode 100644
index 000000000..dd4cbfcd4
--- /dev/null
+++ b/packages/co-build/src/barrel.ts
@@ -0,0 +1,2 @@
+export * from "./coBuild.js";
+export * from "./codec/index.js";
diff --git a/packages/co-build/src/coBuild.test.ts b/packages/co-build/src/coBuild.test.ts
new file mode 100644
index 000000000..393d92027
--- /dev/null
+++ b/packages/co-build/src/coBuild.test.ts
@@ -0,0 +1,168 @@
+import { ccc } from "@ckb-ccc/core";
+import { describe, expect, test } from "vitest";
+import { CoBuild } from "./coBuild.js";
+import { Action, ScriptInfo } from "./codec/index.js";
+
+describe("CoBuild", () => {
+ const script = ccc.Script.from({
+ codeHash: "0x" + "22".repeat(32),
+ hashType: "type",
+ args: "0x3344",
+ });
+
+ const scriptInfo = ScriptInfo.from({
+ name: "TestScript",
+ url: "https://example.com",
+ scriptHash: script.hash(),
+ schema: "TestSchema",
+ messageType: "TestMessageType",
+ });
+
+ const coBuild = new CoBuild(script, scriptInfo);
+
+ test("buildAction should prefill script context", () => {
+ const payload = "0x556677";
+ const action = coBuild.buildAction(payload);
+
+ expect(action.scriptInfoHash).toBe(scriptInfo.hash());
+ expect(action.scriptHash).toBe(script.hash());
+ expect(action.data).toBe(payload);
+ });
+
+ test("buildAction with ccc.Entity", () => {
+ const entity = ccc.Script.from({
+ codeHash: "0x" + "44".repeat(32),
+ hashType: "type",
+ args: "0x",
+ });
+ const action = coBuild.buildAction(entity);
+
+ expect(action.data).toBe(ccc.hexFrom(entity.toBytes()));
+ });
+
+ test("appendActions and parseActions with single action", async () => {
+ const tx = ccc.Transaction.from({
+ version: 0,
+ cellDeps: [],
+ headerDeps: [],
+ inputs: [],
+ outputs: [],
+ outputsData: [],
+ witnesses: [],
+ });
+
+ const action = coBuild.buildAction("0xaabb");
+ const { tx: txUpdated } = await coBuild.appendActions(tx, action);
+
+ expect(txUpdated.witnesses.length).toBe(1);
+
+ const { actions } = coBuild.parseActions(txUpdated);
+ expect(actions.length).toBe(1);
+ expect(actions[0].data).toBe("0xaabb");
+ expect(actions[0].scriptHash).toBe(script.hash());
+ expect(actions[0].scriptInfoHash).toBe(scriptInfo.hash());
+ });
+
+ test("appendActions with iterable and async iterable", async () => {
+ const action1 = coBuild.buildAction("0x11");
+ const action2 = coBuild.buildAction("0x22");
+
+ // Test sync iterable (array)
+ const txSyncBase = ccc.Transaction.from({
+ version: 0,
+ cellDeps: [],
+ headerDeps: [],
+ inputs: [],
+ outputs: [],
+ outputsData: [],
+ witnesses: [],
+ });
+ const { tx: txSync } = await coBuild.appendActions(txSyncBase, [
+ action1,
+ action2,
+ ]);
+ const { actions: actionsSync } = coBuild.parseActions(txSync);
+ expect(actionsSync.length).toBe(2);
+ expect(actionsSync[0].data).toBe("0x11");
+ expect(actionsSync[1].data).toBe("0x22");
+
+ // Test async iterable
+ const txAsyncBase = ccc.Transaction.from({
+ version: 0,
+ cellDeps: [],
+ headerDeps: [],
+ inputs: [],
+ outputs: [],
+ outputsData: [],
+ witnesses: [],
+ });
+ async function* asyncGenerator() {
+ yield action1;
+ yield action2;
+ }
+ const { tx: txAsync } = await coBuild.appendActions(
+ txAsyncBase,
+ asyncGenerator(),
+ );
+ const { actions: actionsAsync } = coBuild.parseActions(txAsync);
+ expect(actionsAsync.length).toBe(2);
+ expect(actionsAsync[0].data).toBe("0x11");
+ expect(actionsAsync[1].data).toBe("0x22");
+ });
+
+ test("findActions query methods", async () => {
+ const tx = ccc.Transaction.from({
+ version: 0,
+ cellDeps: [],
+ headerDeps: [],
+ inputs: [],
+ outputs: [],
+ outputsData: [],
+ witnesses: [],
+ });
+
+ const otherScript = ccc.Script.from({
+ codeHash: "0x" + "55".repeat(32),
+ hashType: "type",
+ args: "0x",
+ });
+
+ const actionCurrent = coBuild.buildAction("0x11");
+ const actionOther = Action.from({
+ scriptInfoHash: "0x" + "66".repeat(32),
+ scriptHash: otherScript.hash(),
+ data: "0x22",
+ });
+
+ const { tx: txUpdated } = await coBuild.appendActions(tx, [
+ actionCurrent,
+ actionOther,
+ ]);
+
+ // findActions matching current script
+ const foundActions1 = coBuild.findActions(txUpdated);
+ expect(foundActions1.length).toBe(1);
+ expect(foundActions1[0].data).toBe("0x11");
+
+ // findActions matching other script
+ const foundActions2 = coBuild.findActions(txUpdated, otherScript);
+ expect(foundActions2.length).toBe(1);
+ expect(foundActions2[0].data).toBe("0x22");
+
+ // findActionsByHash matching other script hash
+ const foundActions3 = coBuild.findActionsByHash(
+ txUpdated,
+ otherScript.hash(),
+ );
+ expect(foundActions3.length).toBe(1);
+ expect(foundActions3[0].data).toBe("0x22");
+
+ // findActionsByScriptInfo matching current script info
+ const foundActions4 = coBuild.findActionsByScriptInfo(
+ txUpdated,
+ scriptInfo,
+ );
+ expect(foundActions4.length).toBe(1);
+ expect(foundActions4[0].data).toBe("0x11");
+ });
+});
diff --git a/packages/co-build/src/coBuild.ts b/packages/co-build/src/coBuild.ts
new file mode 100644
index 000000000..474844047
--- /dev/null
+++ b/packages/co-build/src/coBuild.ts
@@ -0,0 +1,387 @@
+import { ccc } from "@ckb-ccc/core";
+import {
+ Action,
+ ActionLike,
+ ScriptInfo,
+ ScriptInfoLike,
+ WitnessLayout,
+ WitnessLayoutVariant,
+} from "./codec/index.js";
+
+/**
+ * Parses the CoBuild actions from the last witness of a transaction.
+ * @param txLike The transaction containing the witnesses.
+ * @param shouldRemoveParsedWitness If true, removes the last witness from the returned transaction.
+ * @returns An object containing the parsed actions and the transaction.
+ */
+export function parseActions(
+ txLike: ccc.TransactionLike,
+ shouldRemoveParsedWitness: boolean = false,
+): {
+ actions: Action[];
+ tx: ccc.Transaction;
+} {
+ const tx = ccc.Transaction.from(txLike);
+
+ if (tx.witnesses.length === 0) {
+ return { actions: [], tx };
+ }
+
+ try {
+ return WitnessLayout.decode(tx.witnesses[tx.witnesses.length - 1]).match({
+ SighashAll: ({ message: { actions } }) => {
+ if (shouldRemoveParsedWitness) {
+ tx.witnesses.pop();
+ }
+ return { actions, tx };
+ },
+ _: () => {
+ // In the original CoBuild design, the SighashAll can be anywhere in the transaction witnesses.
+ // I don't think that design will come to reality in a short time, so I will just ignore other possibilities for now.
+ return { actions: [], tx };
+ },
+ });
+ } catch {
+ return { actions: [], tx };
+ }
+}
+
+/**
+ * Parses existing actions, executes a manipulator function to modify or add actions,
+ * and encodes/updates the witness layout at the correct index.
+ * @param txLike The target transaction.
+ * @param manipulator A function that takes the current actions and returns updated actions.
+ * @returns The updated transaction and the index of the modified witness.
+ */
+export async function manipulateActions(
+ txLike: ccc.TransactionLike,
+ manipulator: (actions: Action[]) => Action[] | Promise,
+): Promise<{
+ tx: ccc.Transaction;
+ witnessIndex: number;
+}> {
+ let tx = ccc.Transaction.from(txLike);
+ const parsedRes = parseActions(tx, true);
+ tx = parsedRes.tx;
+
+ const witnessIndex = Math.max(
+ tx.witnesses.length,
+ tx.inputs.length,
+ tx.outputs.length,
+ );
+
+ tx.setWitnessAt(
+ witnessIndex,
+ WitnessLayout.encode({
+ type: WitnessLayoutVariant.SighashAll,
+ value: {
+ seal: "0x",
+ message: {
+ actions: await manipulator(parsedRes.actions),
+ },
+ },
+ }),
+ );
+
+ return {
+ tx,
+ witnessIndex,
+ };
+}
+
+/**
+ * Appends actions to the transaction's CoBuild witness.
+ * Supports single action, iterables, or async iterables of actions.
+ * @param txLike The target transaction.
+ * @param actions The action(s) to append. Can be a single ActionLike, or an iterable/async iterable of ActionLike.
+ * @returns The updated transaction and the index of the witness.
+ */
+export async function appendActions(
+ txLike: ccc.TransactionLike,
+ actions: ActionLike | Iterable | AsyncIterable,
+): Promise<{
+ tx: ccc.Transaction;
+ witnessIndex: number;
+}> {
+ const actionList: ActionLike[] = [];
+ if (typeof actions === "object" && actions !== null) {
+ if (Symbol.iterator in actions) {
+ for (const action of actions) {
+ actionList.push(action);
+ }
+ } else if (Symbol.asyncIterator in actions) {
+ for await (const action of actions) {
+ actionList.push(action);
+ }
+ } else {
+ actionList.push(actions);
+ }
+ }
+
+ return manipulateActions(txLike, (existedActions) =>
+ existedActions.concat(actionList.map(Action.from)),
+ );
+}
+
+/**
+ * Filters actions in a transaction matching the given CKB Script.
+ * @param txLike The transaction containing the witnesses.
+ * @param scriptLike The script to match actions against.
+ * @returns The list of matching actions.
+ */
+export function findActions(
+ txLike: ccc.TransactionLike,
+ scriptLike: ccc.ScriptLike,
+): Action[] {
+ const scriptHash = ccc.Script.from(scriptLike).hash();
+ return findActionsByHash(txLike, scriptHash);
+}
+
+/**
+ * Filters actions in a transaction matching the given script hash.
+ * @param txLike The transaction containing the witnesses.
+ * @param scriptHashLike The script hash to match.
+ * @returns The list of matching actions.
+ */
+export function findActionsByHash(
+ txLike: ccc.TransactionLike,
+ scriptHashLike: ccc.HexLike,
+): Action[] {
+ const scriptHash = ccc.hexFrom(scriptHashLike);
+ const { actions } = parseActions(txLike);
+ return actions.filter((action) => action.scriptHash === scriptHash);
+}
+
+/**
+ * Filters actions in a transaction matching the given ScriptInfo.
+ * @param txLike The transaction containing the witnesses.
+ * @param scriptInfoLike The ScriptInfo metadata to match.
+ * @returns The list of matching actions.
+ */
+export function findActionsByScriptInfo(
+ txLike: ccc.TransactionLike,
+ scriptInfoLike: ScriptInfoLike,
+): Action[] {
+ const scriptInfoHash = ScriptInfo.from(scriptInfoLike).hash();
+ return findActionsByScriptInfoHash(txLike, scriptInfoHash);
+}
+
+/**
+ * Filters actions in a transaction matching the given ScriptInfo hash.
+ * @param txLike The transaction containing the witnesses.
+ * @param scriptInfoHashLike The ScriptInfo hash to match.
+ * @returns The list of matching actions.
+ */
+export function findActionsByScriptInfoHash(
+ txLike: ccc.TransactionLike,
+ scriptInfoHashLike: ccc.HexLike,
+): Action[] {
+ const scriptInfoHash = ccc.hexFrom(scriptInfoHashLike);
+ const { actions } = parseActions(txLike);
+ return actions.filter((action) => action.scriptInfoHash === scriptInfoHash);
+}
+
+/**
+ * CoBuild helper class associated with a specific script and script info,
+ * delegating actions to top-level helper functions.
+ */
+export class CoBuild {
+ /**
+ * The ScriptInfo metadata used for building actions.
+ */
+ public readonly scriptInfo: ScriptInfo;
+ /**
+ * The CKB script executing the actions.
+ */
+ public readonly script: ccc.Script;
+ /**
+ * Hex-encoded hash of the ScriptInfo.
+ */
+ public readonly scriptInfoHash: ccc.Hex;
+ /**
+ * Hex-encoded script hash.
+ */
+ public readonly scriptHash: ccc.Hex;
+
+ /**
+ * Constructs a CoBuild instance.
+ * @param script The target CKB Script.
+ * @param scriptInfo The ScriptInfo metadata.
+ */
+ constructor(
+ script: ccc.ScriptLike,
+ scriptInfo?: Omit | null,
+ ) {
+ this.script = ccc.Script.from(script);
+ this.scriptHash = this.script.hash();
+ this.scriptInfo = ScriptInfo.from({
+ ...(scriptInfo ?? {}),
+ scriptHash: this.script.hash(),
+ });
+ this.scriptInfoHash = this.scriptInfo.hash();
+ }
+
+ /**
+ * Helper to construct an Action with prefilled scriptInfoHash and scriptHash.
+ * @param data The custom action payload data (either raw bytes or a serialized ccc Entity).
+ * @returns A new Action instance.
+ */
+ buildAction(data: ccc.BytesLike | ccc.Entity): Action {
+ return Action.from({
+ scriptInfoHash: this.scriptInfoHash,
+ scriptHash: this.scriptHash,
+ data: data instanceof ccc.Entity ? data.toBytes() : data,
+ });
+ }
+
+ /**
+ * Parses the CoBuild actions from the last witness of a transaction.
+ * @param txLike The transaction containing the witnesses.
+ * @param shouldRemoveParsedWitness If true, removes the last witness from the returned transaction.
+ * @returns An object containing the parsed actions and the transaction.
+ */
+ parseActions(
+ txLike: ccc.TransactionLike,
+ shouldRemoveParsedWitness: boolean = false,
+ ): {
+ actions: Action[];
+ tx: ccc.Transaction;
+ } {
+ return parseActions(txLike, shouldRemoveParsedWitness);
+ }
+
+ /**
+ * Parses existing actions, executes a manipulator function to modify or add actions,
+ * and encodes/updates the witness layout at the correct index.
+ * @param txLike The target transaction.
+ * @param manipulator A function that takes the current actions and returns updated actions.
+ * @returns The updated transaction and the index of the modified witness.
+ */
+ manipulateActions(
+ txLike: ccc.TransactionLike,
+ manipulator: (actions: Action[]) => Action[] | Promise,
+ ): Promise<{
+ tx: ccc.Transaction;
+ witnessIndex: number;
+ }> {
+ return manipulateActions(txLike, manipulator);
+ }
+
+ /**
+ * Appends actions to the transaction's CoBuild witness using the script context.
+ * Supports single, iterables, or async iterables of actions or entities.
+ * @param txLike The target transaction.
+ * @param actions The action(s) to append.
+ * @returns The updated transaction and the index of the witness.
+ */
+ async appendActions(
+ txLike: ccc.TransactionLike,
+ actions:
+ | ActionLike
+ | ccc.Entity
+ | Iterable
+ | AsyncIterable,
+ ): Promise<{
+ tx: ccc.Transaction;
+ witnessIndex: number;
+ }> {
+ const actionList: (ActionLike | ccc.Entity)[] = [];
+ if (typeof actions === "object" && actions !== null) {
+ if (Symbol.iterator in actions) {
+ for (const action of actions) {
+ actionList.push(action);
+ }
+ } else if (Symbol.asyncIterator in actions) {
+ for await (const action of actions) {
+ actionList.push(action);
+ }
+ } else {
+ actionList.push(actions);
+ }
+ }
+
+ return this.manipulateActions(txLike, (existedActions) =>
+ existedActions.concat(
+ actionList.map((action) => {
+ if (typeof action === "object" && action !== null) {
+ if (
+ "scriptInfoHash" in action &&
+ "scriptHash" in action &&
+ "data" in action
+ ) {
+ return Action.from(action);
+ }
+ }
+ return this.buildAction(action);
+ }),
+ ),
+ );
+ }
+
+ /**
+ * Filters actions in a transaction matching the given CKB Script.
+ * If omitted or null, defaults to the helper's script.
+ * @param txLike The transaction containing the witnesses.
+ * @param scriptLike The script to match actions against.
+ * @returns The list of matching actions.
+ */
+ findActions(
+ txLike: ccc.TransactionLike,
+ scriptLike?: ccc.ScriptLike | null,
+ ): Action[] {
+ return findActionsByHash(
+ txLike,
+ scriptLike ? ccc.Script.from(scriptLike).hash() : this.scriptHash,
+ );
+ }
+
+ /**
+ * Filters actions in a transaction matching the given script hash.
+ * If omitted or null, defaults to the helper's script hash.
+ * @param txLike The transaction containing the witnesses.
+ * @param scriptHashLike The script hash to match.
+ * @returns The list of matching actions.
+ */
+ findActionsByHash(
+ txLike: ccc.TransactionLike,
+ scriptHashLike?: ccc.HexLike | null,
+ ): Action[] {
+ return findActionsByHash(txLike, scriptHashLike ?? this.scriptHash);
+ }
+
+ /**
+ * Filters actions in a transaction matching the given ScriptInfo.
+ * If omitted or null, defaults to the helper's script info.
+ * @param txLike The transaction containing the witnesses.
+ * @param scriptInfoLike The ScriptInfo metadata to match.
+ * @returns The list of matching actions.
+ */
+ findActionsByScriptInfo(
+ txLike: ccc.TransactionLike,
+ scriptInfoLike?: ScriptInfoLike | null,
+ ): Action[] {
+ return findActionsByScriptInfoHash(
+ txLike,
+ scriptInfoLike
+ ? ScriptInfo.from(scriptInfoLike).hash()
+ : this.scriptInfoHash,
+ );
+ }
+
+ /**
+ * Filters actions in a transaction matching the given ScriptInfo hash.
+ * If omitted or null, defaults to the helper's script info hash.
+ * @param txLike The transaction containing the witnesses.
+ * @param scriptInfoHashLike The ScriptInfo hash to match.
+ * @returns The list of matching actions.
+ */
+ findActionsByScriptInfoHash(
+ txLike: ccc.TransactionLike,
+ scriptInfoHashLike?: ccc.HexLike | null,
+ ): Action[] {
+ return findActionsByScriptInfoHash(
+ txLike,
+ scriptInfoHashLike ?? this.scriptInfoHash,
+ );
+ }
+}
diff --git a/packages/co-build/src/codec/buildingPacket.ts b/packages/co-build/src/codec/buildingPacket.ts
new file mode 100644
index 000000000..baa0afd94
--- /dev/null
+++ b/packages/co-build/src/codec/buildingPacket.ts
@@ -0,0 +1,166 @@
+import { ccc, mol } from "@ckb-ccc/core";
+
+export const ActionCodec = mol.table({
+ scriptInfoHash: mol.Byte32,
+ scriptHash: mol.Byte32,
+ data: mol.Bytes,
+});
+
+/**
+ * Representation of an Action's properties.
+ */
+export type ActionLike = ccc.EncodableType;
+
+/**
+ * Represents a single Action inside the CoBuild protocol.
+ */
+@ccc.codec(ActionCodec)
+export class Action extends ccc.Entity.Base() {
+ public scriptInfoHash: ccc.Hex;
+ public scriptHash: ccc.Hex;
+ public data: ccc.Hex;
+
+ /**
+ * Constructs an Action instance.
+ * @param scriptInfoHash The hex-encoded hash of the ScriptInfo.
+ * @param scriptHash The hex-encoded script hash.
+ * @param data The hex-encoded action payload data.
+ */
+ constructor({
+ scriptInfoHash,
+ scriptHash,
+ data,
+ }: ccc.DecodedType) {
+ super();
+
+ this.scriptInfoHash = scriptInfoHash;
+ this.scriptHash = scriptHash;
+ this.data = data;
+ }
+}
+
+/**
+ * Molecule vector of Action codecs.
+ */
+export const ActionVec = mol.vector(Action);
+
+export const MessageCodec = mol.table({
+ actions: ActionVec,
+});
+
+/**
+ * Representation of a Message's properties.
+ */
+export type MessageLike = ccc.EncodableType;
+
+/**
+ * Represents a Message containing a list of actions.
+ */
+@ccc.codec(MessageCodec)
+export class Message extends ccc.Entity.Base() {
+ public actions: Action[];
+
+ /**
+ * Constructs a Message instance.
+ * @param actions The list of Action instances.
+ */
+ constructor({ actions }: ccc.DecodedType) {
+ super();
+
+ this.actions = actions;
+ }
+}
+
+export const ScriptInfoCodec = mol.table({
+ name: mol.String,
+ url: mol.String,
+ scriptHash: mol.Byte32,
+ schema: mol.String,
+ messageType: mol.String,
+});
+
+/**
+ * Representation of a ScriptInfo's properties.
+ */
+export type ScriptInfoLike = {
+ /**
+ * The developer/dapp name.
+ */
+ name?: string | null;
+ /**
+ * The url associated with the script or dapp.
+ */
+ url?: string | null;
+ /**
+ * The script hash.
+ */
+ scriptHash: ccc.BytesLike;
+ /**
+ * The action schema description.
+ */
+ schema?: string | null;
+ /**
+ * The entry action type name used in the WitnessLayout.
+ */
+ messageType?: string | null;
+};
+
+/**
+ * Represents script metadata information (ScriptInfo).
+ */
+@ccc.codec(ScriptInfoCodec)
+export class ScriptInfo extends ccc.Entity.Base() {
+ public name: string;
+ public url: string;
+ public scriptHash: ccc.Hex;
+ public schema: string;
+ public messageType: string;
+
+ /**
+ * Constructs a ScriptInfo instance.
+ * @param name The developer/dapp name.
+ * @param url The url associated with the script or dapp.
+ * @param scriptHash The hex-encoded script hash.
+ * @param schema The action schema.
+ * @param messageType The message type.
+ */
+ constructor({
+ name,
+ url,
+ scriptHash,
+ schema,
+ messageType,
+ }: ccc.DecodedType) {
+ super();
+
+ this.name = name;
+ this.url = url;
+ this.scriptHash = scriptHash;
+ this.schema = schema;
+ this.messageType = messageType;
+ }
+
+ /**
+ * Creates a ScriptInfo instance from a ScriptInfoLike object.
+ * @param scriptInfoLike The source ScriptInfoLike object or ScriptInfo instance.
+ * @returns A new or existing ScriptInfo instance.
+ */
+ static from(scriptInfoLike: ScriptInfoLike): ScriptInfo {
+ if (scriptInfoLike instanceof ScriptInfo) {
+ return scriptInfoLike;
+ }
+
+ return super.from({
+ name: scriptInfoLike.name ?? "",
+ url: scriptInfoLike.url ?? "",
+ scriptHash: scriptInfoLike.scriptHash,
+ schema: scriptInfoLike.schema ?? "",
+ messageType: scriptInfoLike.messageType ?? "",
+ });
+ }
+}
+
+/**
+ * Molecule vector of ScriptInfo codecs.
+ */
+export const ScriptInfoVec = mol.vector(ScriptInfo);
diff --git a/packages/co-build/src/codec/index.ts b/packages/co-build/src/codec/index.ts
new file mode 100644
index 000000000..d157535a7
--- /dev/null
+++ b/packages/co-build/src/codec/index.ts
@@ -0,0 +1,2 @@
+export * from "./buildingPacket.js";
+export * from "./witnessLayout.js";
diff --git a/packages/co-build/src/codec/witnessLayout.ts b/packages/co-build/src/codec/witnessLayout.ts
new file mode 100644
index 000000000..735ad3cc3
--- /dev/null
+++ b/packages/co-build/src/codec/witnessLayout.ts
@@ -0,0 +1,66 @@
+import { ccc, mol } from "@ckb-ccc/core";
+import { Message } from "./buildingPacket.js";
+
+export const SighashAllCodec = mol.table({
+ seal: mol.Bytes,
+ message: Message,
+});
+
+/**
+ * Representation of a SighashAll's properties.
+ */
+export type SighashAllLike = ccc.EncodableType;
+
+/**
+ * Represents a SighashAll witness structure.
+ */
+@ccc.codec(SighashAllCodec)
+export class SighashAll extends ccc.Entity.Base() {
+ public seal: ccc.Hex;
+ public message: Message;
+
+ /**
+ * Constructs a SighashAll instance.
+ * @param seal The hex-encoded signature or seal.
+ * @param message The Message instance.
+ */
+ constructor({ seal, message }: ccc.DecodedType) {
+ super();
+
+ this.seal = seal;
+ this.message = message;
+ }
+}
+
+/**
+ * WitnessLayout union variant names.
+ */
+export const WitnessLayoutVariant = {
+ SighashAll: "SighashAll",
+} as const;
+
+/**
+ * WitnessLayout union variant numeric identifiers.
+ */
+export const WitnessLayoutVariantId = {
+ SighashAll: 4278190081,
+};
+
+/**
+ * Molecule union representing the layout of the transaction witness.
+ */
+export const WitnessLayoutCodec = mol.union(
+ {
+ SighashAll,
+ },
+ WitnessLayoutVariantId,
+);
+
+type WitnessLayoutLike = ccc.EncodableType;
+
+@ccc.codec(WitnessLayoutCodec)
+export class WitnessLayout extends ccc.Entity.BaseUnion<
+ typeof WitnessLayoutCodec,
+ WitnessLayoutLike,
+ WitnessLayout
+>() {}
diff --git a/packages/co-build/src/index.ts b/packages/co-build/src/index.ts
new file mode 100644
index 000000000..cb3c22bcd
--- /dev/null
+++ b/packages/co-build/src/index.ts
@@ -0,0 +1,2 @@
+export * from "./barrel.js";
+export * as coBuild from "./barrel.js";
diff --git a/packages/co-build/tsconfig.json b/packages/co-build/tsconfig.json
new file mode 100644
index 000000000..3399c6f67
--- /dev/null
+++ b/packages/co-build/tsconfig.json
@@ -0,0 +1,25 @@
+{
+ "compilerOptions": {
+ "target": "es2022",
+ "incremental": true,
+ "allowJs": true,
+ "importHelpers": false,
+ "declaration": true,
+ "declarationMap": true,
+ "experimentalDecorators": true,
+ "useDefineForClassFields": false,
+ "esModuleInterop": true,
+ "strict": true,
+ "noImplicitAny": true,
+ "strictBindCallApply": true,
+ "strictNullChecks": true,
+ "alwaysStrict": true,
+ "noFallthroughCasesInSwitch": true,
+ "forceConsistentCasingInFileNames": true,
+ "skipLibCheck": true,
+ "module": "ESNext",
+ "moduleResolution": "Bundler",
+ "rootDir": "./src"
+ },
+ "include": ["src/**/*"]
+}
diff --git a/packages/co-build/tsdown.config.mts b/packages/co-build/tsdown.config.mts
new file mode 100644
index 000000000..9453d8b6d
--- /dev/null
+++ b/packages/co-build/tsdown.config.mts
@@ -0,0 +1,41 @@
+import { defineConfig } from "tsdown";
+
+const common = {
+ minify: true,
+ dts: true,
+ platform: "neutral" as const,
+ sourcemap: true,
+ exports: true,
+};
+
+const entry = {
+ index: "src/index.ts",
+ barrel: "src/barrel.ts",
+} as const;
+
+const bundleDeps: string[] = [];
+
+export default defineConfig(
+ (
+ [
+ {
+ entry,
+ deps: {
+ onlyBundle: [] as string[],
+ },
+ format: "esm",
+ copy: "./misc/basedirs/dist/*",
+ },
+ {
+ entry,
+ deps: {
+ alwaysBundle: bundleDeps,
+ onlyBundle: bundleDeps,
+ },
+ format: "cjs",
+ outDir: "dist.commonjs",
+ copy: "./misc/basedirs/dist.commonjs/*",
+ },
+ ] as const
+ ).map((c) => ({ ...c, ...common })),
+);
diff --git a/packages/co-build/typedoc.json b/packages/co-build/typedoc.json
new file mode 100644
index 000000000..9ca821498
--- /dev/null
+++ b/packages/co-build/typedoc.json
@@ -0,0 +1,6 @@
+{
+ "$schema": "https://typedoc.org/schema.json",
+ "entryPoints": ["./src/index.ts"],
+ "extends": ["../../typedoc.base.json"],
+ "name": "@ckb-ccc co-build"
+}
diff --git a/packages/core/src/ckb/transaction.test.ts b/packages/core/src/ckb/transaction.test.ts
index c6a3a9d46..b9981bdb5 100644
--- a/packages/core/src/ckb/transaction.test.ts
+++ b/packages/core/src/ckb/transaction.test.ts
@@ -1153,5 +1153,95 @@ describe("Transaction", () => {
expect(cell.capacityFree).toBe(expectedFreeCapacity);
});
});
+
+ describe("Witness Alignment", () => {
+ it("should not misalign witnesses when adding input with more outputs than inputs", () => {
+ const tx = ccc.Transaction.default();
+ tx.addInput({
+ previousOutput: {
+ txHash: "0x" + "0".repeat(64),
+ index: 0,
+ },
+ since: 0n,
+ });
+ tx.addOutput({ lock });
+ tx.addOutput({ lock });
+
+ // Set witness for output 1 at index 1
+ tx.setWitnessAt(1, "0x2222");
+ expect(tx.witnesses).toEqual(["0x", "0x2222"]);
+
+ // Add input 1. Since outputs (2) > inputs (1), it should not splice "0x" at index 1.
+ tx.addInput({
+ previousOutput: {
+ txHash: "0x" + "0".repeat(64),
+ index: 1,
+ },
+ since: 0n,
+ });
+
+ // The witness at index 1 should remain "0x2222", aligning with both input 1 and output 1
+ expect(tx.witnesses).toEqual(["0x", "0x2222"]);
+ });
+
+ it("should not misalign witnesses when adding output with more inputs than outputs", () => {
+ const tx = ccc.Transaction.default();
+ tx.addInput({
+ previousOutput: {
+ txHash: "0x" + "0".repeat(64),
+ index: 0,
+ },
+ since: 0n,
+ });
+ tx.addInput({
+ previousOutput: {
+ txHash: "0x" + "0".repeat(64),
+ index: 1,
+ },
+ since: 0n,
+ });
+ tx.addOutput({ lock });
+
+ // Set witness for input 1 at index 1
+ tx.setWitnessAt(1, "0x1111");
+ expect(tx.witnesses).toEqual(["0x", "0x1111"]);
+
+ // Add output 1. Since inputs (2) > outputs (1), it should not splice "0x" at index 1.
+ tx.addOutput({ lock });
+
+ // The witness at index 1 should remain "0x1111", aligning with both input 1 and output 1
+ expect(tx.witnesses).toEqual(["0x", "0x1111"]);
+ });
+
+ it("should splice dummy witness when witnesses length is greater than both inputs and outputs", () => {
+ const tx = ccc.Transaction.default();
+ tx.addInput({
+ previousOutput: {
+ txHash: "0x" + "0".repeat(64),
+ index: 0,
+ },
+ since: 0n,
+ });
+ tx.addOutput({ lock });
+
+ // Set an extra witness (e.g., CoBuild SighashAll) at index 1
+ tx.setWitnessAt(1, "0x3333");
+ expect(tx.witnesses).toEqual(["0x", "0x3333"]);
+
+ // Add input 1. Since inputs.length (1) >= outputs.length (1), it splices "0x" to shift the extra witness.
+ tx.addInput({
+ previousOutput: {
+ txHash: "0x" + "0".repeat(64),
+ index: 1,
+ },
+ since: 0n,
+ });
+ expect(tx.witnesses).toEqual(["0x", "0x", "0x3333"]);
+
+ // Add output 1. Since outputs.length (1) >= inputs.length (2) is false, it does not splice.
+ tx.addOutput({ lock });
+ expect(tx.witnesses).toEqual(["0x", "0x", "0x3333"]);
+ });
+ });
});
});
diff --git a/packages/core/src/ckb/transaction.ts b/packages/core/src/ckb/transaction.ts
index 7f07f0460..6b52537cb 100644
--- a/packages/core/src/ckb/transaction.ts
+++ b/packages/core/src/ckb/transaction.ts
@@ -1654,7 +1654,10 @@ export class Transaction extends Entity.Base() {
* ```
*/
addInput(inputLike: CellInputLike): number {
- if (this.witnesses.length > this.inputs.length) {
+ if (
+ this.witnesses.length > this.inputs.length &&
+ this.inputs.length >= this.outputs.length
+ ) {
this.witnesses.splice(this.inputs.length, 0, "0x");
}
@@ -1755,6 +1758,13 @@ export class Transaction extends Entity.Base() {
outputData: outputDataLike,
});
+ if (
+ this.witnesses.length > this.outputs.length &&
+ this.outputs.length >= this.inputs.length
+ ) {
+ this.witnesses.splice(this.outputs.length, 0, "0x");
+ }
+
const len = this.outputs.push(cell.cellOutput);
this.setOutputDataAt(len - 1, cell.outputData);
diff --git a/packages/shell/package.json b/packages/shell/package.json
index cd8d90328..cc69dbcb2 100644
--- a/packages/shell/package.json
+++ b/packages/shell/package.json
@@ -56,6 +56,7 @@
},
"dependencies": {
"@ckb-ccc/core": "workspace:*",
+ "@ckb-ccc/co-build": "workspace:*",
"@ckb-ccc/did-ckb": "workspace:*",
"@ckb-ccc/spore": "workspace:*",
"@ckb-ccc/ssri": "workspace:*",
diff --git a/packages/shell/src/barrel.ts b/packages/shell/src/barrel.ts
index e9d7adb7a..0be10ba5c 100644
--- a/packages/shell/src/barrel.ts
+++ b/packages/shell/src/barrel.ts
@@ -1,3 +1,4 @@
+export { coBuild } from "@ckb-ccc/co-build";
export * from "@ckb-ccc/core/barrel";
export { didCkb } from "@ckb-ccc/did-ckb";
export { spore } from "@ckb-ccc/spore";
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 024ac367a..0d80ce240 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -13,7 +13,7 @@ importers:
version: 0.7.0
'@changesets/cli':
specifier: ^2.31.0
- version: 2.31.0(@types/node@26.0.0)
+ version: 2.31.0(@types/node@26.0.1)
'@types/jest':
specifier: ^30.0.0
version: 30.0.0
@@ -22,10 +22,10 @@ importers:
version: 4.1.9(vitest@4.1.9)
jest:
specifier: 30.4.2
- version: 30.4.2(@types/node@26.0.0)(ts-node@10.9.2(@types/node@26.0.1)(typescript@6.0.3))
+ version: 30.4.2(@types/node@26.0.1)(ts-node@10.9.2(@types/node@26.0.1)(typescript@6.0.3))
ts-jest:
specifier: ^29.4.11
- version: 29.4.11(@babel/core@7.29.7)(@jest/transform@30.4.1)(@jest/types@30.4.1)(babel-jest@30.4.1(@babel/core@7.29.7))(jest-util@30.4.1)(jest@30.4.2(@types/node@26.0.0)(ts-node@10.9.2(@types/node@26.0.1)(typescript@6.0.3)))(typescript@6.0.3)
+ version: 29.4.11(@babel/core@7.29.7)(@jest/transform@30.4.1)(@jest/types@30.4.1)(babel-jest@30.4.1(@babel/core@7.29.7))(jest-util@30.4.1)(jest@30.4.2(@types/node@26.0.1)(ts-node@10.9.2(@types/node@26.0.1)(typescript@6.0.3)))(typescript@6.0.3)
typedoc:
specifier: 0.28.19
version: 0.28.19(typescript@6.0.3)
@@ -43,7 +43,7 @@ importers:
version: 6.0.3
vitest:
specifier: ^4.1.9
- version: 4.1.9(@types/node@26.0.0)(@vitest/coverage-v8@4.1.9)(vite@7.3.5(@types/node@26.0.1)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))
+ version: 4.1.9(@types/node@26.0.1)(@vitest/coverage-v8@4.1.9)(vite@7.3.5(@types/node@26.0.1)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))
packages/ccc:
dependencies:
@@ -137,6 +137,46 @@ importers:
specifier: ^8.61.1
version: 8.62.1(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3)
+ packages/co-build:
+ dependencies:
+ '@ckb-ccc/core':
+ specifier: workspace:*
+ version: link:../core
+ devDependencies:
+ '@eslint/js':
+ specifier: ^10.0.1
+ version: 10.0.1(eslint@10.6.0(jiti@2.7.0))
+ '@types/node':
+ specifier: ^26.0.0
+ version: 26.0.1
+ eslint:
+ specifier: ^10.5.0
+ version: 10.6.0(jiti@2.7.0)
+ eslint-config-prettier:
+ specifier: ^10.1.8
+ version: 10.1.8(eslint@10.6.0(jiti@2.7.0))
+ eslint-plugin-prettier:
+ specifier: ^5.5.6
+ version: 5.5.6(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@10.6.0(jiti@2.7.0)))(eslint@10.6.0(jiti@2.7.0))(prettier@3.9.4)
+ prettier:
+ specifier: ^3.8.4
+ version: 3.9.4
+ prettier-plugin-organize-imports:
+ specifier: ^4.3.0
+ version: 4.3.0(prettier@3.9.4)(typescript@6.0.3)
+ tsdown:
+ specifier: ^0.22.3
+ version: 0.22.3(tsx@4.22.4)(typescript@6.0.3)(unrun@0.2.22(synckit@0.11.13))
+ typescript:
+ specifier: ^6.0.3
+ version: 6.0.3
+ typescript-eslint:
+ specifier: ^8.61.1
+ version: 8.62.1(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3)
+ vitest:
+ specifier: ^4.1.9
+ version: 4.1.9(@types/node@26.0.1)(@vitest/coverage-v8@4.1.9)(vite@7.3.5(@types/node@26.0.1)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))
+
packages/connector:
dependencies:
'@ckb-ccc/ccc':
@@ -276,7 +316,7 @@ importers:
version: 8.62.1(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3)
vitest:
specifier: ^4.1.9
- version: 4.1.9(@types/node@26.0.0)(@vitest/coverage-v8@4.1.9)(vite@7.3.5(@types/node@26.0.1)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))
+ version: 4.1.9(@types/node@26.0.1)(@vitest/coverage-v8@4.1.9)(vite@7.3.5(@types/node@26.0.1)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))
packages/demo:
dependencies:
@@ -1003,6 +1043,9 @@ importers:
packages/shell:
dependencies:
+ '@ckb-ccc/co-build':
+ specifier: workspace:*
+ version: link:../co-build
'@ckb-ccc/core':
specifier: workspace:*
version: link:../core
@@ -4083,9 +4126,6 @@ packages:
'@types/node@22.7.5':
resolution: {integrity: sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==}
- '@types/node@26.0.0':
- resolution: {integrity: sha512-vf2YFi1iY9lHGwNJMs01biZFbKJkrZR1T6/MlzjhJLPdntOHLhTrDSnSVcdtvjihi4VQNlrFRIxLsDBlQpAipA==}
-
'@types/node@26.0.1':
resolution: {integrity: sha512-fc3KiUoBt6kie0N9bIW3E47vZsuaMf0PM2AaUpLCLT0s/LvX1nxAim6Fc049cNxODPpGm6qRAuUOB86SkRuPQw==}
@@ -9714,7 +9754,7 @@ snapshots:
transitivePeerDependencies:
- encoding
- '@changesets/cli@2.31.0(@types/node@26.0.0)':
+ '@changesets/cli@2.31.0(@types/node@26.0.1)':
dependencies:
'@changesets/apply-release-plan': 7.1.1
'@changesets/assemble-release-plan': 6.0.10
@@ -9730,7 +9770,7 @@ snapshots:
'@changesets/should-skip-package': 0.1.2
'@changesets/types': 6.1.0
'@changesets/write': 0.4.0
- '@inquirer/external-editor': 1.0.3(@types/node@26.0.0)
+ '@inquirer/external-editor': 1.0.3(@types/node@26.0.1)
'@manypkg/get-packages': 1.1.3
ansi-colors: 4.1.3
enquirer: 2.4.1
@@ -10444,13 +10484,6 @@ snapshots:
optionalDependencies:
'@types/node': 26.0.1
- '@inquirer/external-editor@1.0.3(@types/node@26.0.0)':
- dependencies:
- chardet: 2.2.0
- iconv-lite: 0.7.2
- optionalDependencies:
- '@types/node': 26.0.0
-
'@inquirer/external-editor@1.0.3(@types/node@26.0.1)':
dependencies:
chardet: 2.2.0
@@ -12135,10 +12168,6 @@ snapshots:
dependencies:
undici-types: 6.19.8
- '@types/node@26.0.0':
- dependencies:
- undici-types: 8.3.0
-
'@types/node@26.0.1':
dependencies:
undici-types: 8.3.0
@@ -12194,7 +12223,7 @@ snapshots:
'@types/ws@8.18.1':
dependencies:
- '@types/node': 26.0.0
+ '@types/node': 26.0.1
'@types/yargs-parser@21.0.3': {}
@@ -12476,7 +12505,7 @@ snapshots:
obug: 2.1.3
std-env: 4.1.0
tinyrainbow: 3.1.0
- vitest: 4.1.9(@types/node@26.0.0)(@vitest/coverage-v8@4.1.9)(vite@7.3.5(@types/node@26.0.1)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))
+ vitest: 4.1.9(@types/node@26.0.1)(@vitest/coverage-v8@4.1.9)(vite@7.3.5(@types/node@26.0.1)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))
'@vitest/expect@4.1.9':
dependencies:
@@ -15232,25 +15261,6 @@ snapshots:
- babel-plugin-macros
- supports-color
- jest-cli@30.4.2(@types/node@26.0.0)(ts-node@10.9.2(@types/node@26.0.1)(typescript@6.0.3)):
- dependencies:
- '@jest/core': 30.4.2(ts-node@10.9.2(@types/node@26.0.1)(typescript@6.0.3))
- '@jest/test-result': 30.4.1
- '@jest/types': 30.4.1
- chalk: 4.1.2
- exit-x: 0.2.2
- import-local: 3.2.0
- jest-config: 30.4.2(@types/node@26.0.0)(ts-node@10.9.2(@types/node@26.0.1)(typescript@6.0.3))
- jest-util: 30.4.1
- jest-validate: 30.4.1
- yargs: 17.7.3
- transitivePeerDependencies:
- - '@types/node'
- - babel-plugin-macros
- - esbuild-register
- - supports-color
- - ts-node
-
jest-cli@30.4.2(@types/node@26.0.1)(ts-node@10.9.2(@types/node@26.0.1)(typescript@6.0.3)):
dependencies:
'@jest/core': 30.4.2(ts-node@10.9.2(@types/node@26.0.1)(typescript@6.0.3))
@@ -15270,38 +15280,6 @@ snapshots:
- supports-color
- ts-node
- jest-config@30.4.2(@types/node@26.0.0)(ts-node@10.9.2(@types/node@26.0.1)(typescript@6.0.3)):
- dependencies:
- '@babel/core': 7.29.7
- '@jest/get-type': 30.1.0
- '@jest/pattern': 30.4.0
- '@jest/test-sequencer': 30.4.1
- '@jest/types': 30.4.1
- babel-jest: 30.4.1(@babel/core@7.29.7)
- chalk: 4.1.2
- ci-info: 4.4.0
- deepmerge: 4.3.1
- glob: 10.5.0
- graceful-fs: 4.2.11
- jest-circus: 30.4.2
- jest-docblock: 30.4.0
- jest-environment-node: 30.4.1
- jest-regex-util: 30.4.0
- jest-resolve: 30.4.1
- jest-runner: 30.4.2
- jest-util: 30.4.1
- jest-validate: 30.4.1
- parse-json: 5.2.0
- pretty-format: 30.4.1
- slash: 3.0.0
- strip-json-comments: 3.1.1
- optionalDependencies:
- '@types/node': 26.0.0
- ts-node: 10.9.2(@types/node@26.0.1)(typescript@6.0.3)
- transitivePeerDependencies:
- - babel-plugin-macros
- - supports-color
-
jest-config@30.4.2(@types/node@26.0.1)(ts-node@10.9.2(@types/node@26.0.1)(typescript@6.0.3)):
dependencies:
'@babel/core': 7.29.7
@@ -15599,19 +15577,6 @@ snapshots:
merge-stream: 2.0.0
supports-color: 8.1.1
- jest@30.4.2(@types/node@26.0.0)(ts-node@10.9.2(@types/node@26.0.1)(typescript@6.0.3)):
- dependencies:
- '@jest/core': 30.4.2(ts-node@10.9.2(@types/node@26.0.1)(typescript@6.0.3))
- '@jest/types': 30.4.1
- import-local: 3.2.0
- jest-cli: 30.4.2(@types/node@26.0.0)(ts-node@10.9.2(@types/node@26.0.1)(typescript@6.0.3))
- transitivePeerDependencies:
- - '@types/node'
- - babel-plugin-macros
- - esbuild-register
- - supports-color
- - ts-node
-
jest@30.4.2(@types/node@26.0.1)(ts-node@10.9.2(@types/node@26.0.1)(typescript@6.0.3)):
dependencies:
'@jest/core': 30.4.2(ts-node@10.9.2(@types/node@26.0.1)(typescript@6.0.3))
@@ -17797,26 +17762,6 @@ snapshots:
ts-dedent@2.3.0: {}
- ts-jest@29.4.11(@babel/core@7.29.7)(@jest/transform@30.4.1)(@jest/types@30.4.1)(babel-jest@30.4.1(@babel/core@7.29.7))(jest-util@30.4.1)(jest@30.4.2(@types/node@26.0.0)(ts-node@10.9.2(@types/node@26.0.1)(typescript@6.0.3)))(typescript@6.0.3):
- dependencies:
- bs-logger: 0.2.6
- fast-json-stable-stringify: 2.1.0
- handlebars: 4.7.9
- jest: 30.4.2(@types/node@26.0.0)(ts-node@10.9.2(@types/node@26.0.1)(typescript@6.0.3))
- json5: 2.2.3
- lodash.memoize: 4.1.2
- make-error: 1.3.6
- semver: 7.8.5
- type-fest: 4.41.0
- typescript: 6.0.3
- yargs-parser: 21.1.1
- optionalDependencies:
- '@babel/core': 7.29.7
- '@jest/transform': 30.4.1
- '@jest/types': 30.4.1
- babel-jest: 30.4.1(@babel/core@7.29.7)
- jest-util: 30.4.1
-
ts-jest@29.4.11(@babel/core@7.29.7)(@jest/transform@30.4.1)(@jest/types@30.4.1)(babel-jest@30.4.1(@babel/core@7.29.7))(jest-util@30.4.1)(jest@30.4.2(@types/node@26.0.1)(ts-node@10.9.2(@types/node@26.0.1)(typescript@6.0.3)))(typescript@6.0.3):
dependencies:
bs-logger: 0.2.6
@@ -18245,34 +18190,6 @@ snapshots:
tsx: 4.22.4
yaml: 2.9.0
- vitest@4.1.9(@types/node@26.0.0)(@vitest/coverage-v8@4.1.9)(vite@7.3.5(@types/node@26.0.1)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0)):
- dependencies:
- '@vitest/expect': 4.1.9
- '@vitest/mocker': 4.1.9(vite@7.3.5(@types/node@26.0.1)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))
- '@vitest/pretty-format': 4.1.9
- '@vitest/runner': 4.1.9
- '@vitest/snapshot': 4.1.9
- '@vitest/spy': 4.1.9
- '@vitest/utils': 4.1.9
- es-module-lexer: 2.1.0
- expect-type: 1.3.0
- magic-string: 0.30.21
- obug: 2.1.3
- pathe: 2.0.3
- picomatch: 4.0.4
- std-env: 4.1.0
- tinybench: 2.9.0
- tinyexec: 1.2.4
- tinyglobby: 0.2.17
- tinyrainbow: 3.1.0
- vite: 7.3.5(@types/node@26.0.1)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0)
- why-is-node-running: 2.3.0
- optionalDependencies:
- '@types/node': 26.0.0
- '@vitest/coverage-v8': 4.1.9(vitest@4.1.9)
- transitivePeerDependencies:
- - msw
-
vitest@4.1.9(@types/node@26.0.1)(@vitest/coverage-v8@4.1.9)(vite@7.3.5(@types/node@26.0.1)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0)):
dependencies:
'@vitest/expect': 4.1.9
diff --git a/vitest.config.mts b/vitest.config.mts
index 9b64c1358..31436983b 100644
--- a/vitest.config.mts
+++ b/vitest.config.mts
@@ -1,6 +1,6 @@
import { defineConfig, coverageConfigDefaults } from "vitest/config";
-const packages = ["packages/core", "packages/did-ckb", "packages/type-id"];
+const packages = ["packages/core", "packages/did-ckb", "packages/type-id", "packages/co-build"];
export default defineConfig({
test: {