npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

idata-client

v0.1.0

Published

数桥轻量连接器,帮助用户管理身份凭证、区域节点、业务节点,完成身份集成。

Readme

IData 统一身份登录 SDK

统一身份登录 SDK - 基于自定义协议调用桌面应用程序进行身份认证、身份注册、合约签署等功能

功能特性

  • 🔐 统一身份认证
  • 📝 数字合约签署
  • 🏢 身份注册与管理
  • 📋 身份凭证导入
  • 🖥️ 桌面应用程序集成
  • 📱 自定义协议支持
  • ⏱️ 智能轮询机制
  • 🔄 自动重试机制
  • 💪 TypeScript 支持
  • 🌐 多环境支持(浏览器、Electron)

安装

npm install idata-client

或者使用 yarn:

yarn add idata-client

快速开始

1. 统一身份登录

import { createIDataLogin } from 'idata-client';

// 区域/行业功能节点、业务节点ID
const nodeId = '212340000MB1M26392A340195BZZPGQX';

const IDataLogin = createIDataLogin({
  options: {
    nodeId, // 区域/行业功能节点、业务节点ID
  },
  callbacks: {
    onLoading: (loading) => {
      console.log('登录状态:', loading);
    },
    onSuccess: (token) => {
      console.log('登录成功,Token:', token);
      // 处理登录成功逻辑
      if (token) {
        // emit("login", token);
      }
    },
    onError: (error) => {
      console.error('登录失败:', error);
    },
    onMessage: (message, type) => {
      console.log(`消息[${type}]:`, message);
    },
  },
});

// 点击登录按钮,开始登录
const handleLogin = () => {
  IDataLogin.login();
};

2. 签署合约

import { createIDataSignContract } from 'idata-client';

const IDataSignContract = createIDataSignContract({
  options: {
    contractData: '{"contractId":"12345","title":"采购合同","content":"..."}', // 合约数据
    cn: 'example-company', // 主体标识
    type: 1, // 1-区域,2-业务
  },
  callbacks: {
    onLoading: (loading) => {
      console.log('签署状态:', loading);
    },
    onSuccess: (signature) => {
      console.log('签署成功,签名:', signature);
      // 处理签署成功逻辑,将签名发送到后端
      // api.submitContractSignature(signature);
    },
    onError: (error) => {
      console.error('签署失败:', error);
    },
    onMessage: (message, type) => {
      console.log(`消息[${type}]:`, message);
    },
  },
});

// 点击签署按钮,开始签署合约
const handleSignContract = () => {
  IDataSignContract.signContract();
};

3. 身份注册

import { createIDataRegister } from 'idata-client';

const IDataRegister = createIDataRegister({
  callbacks: {
    onLoading: (loading) => {
      console.log('注册状态:', loading);
    },
    onSuccess: (certData) => {
      console.log('注册成功,证书数据:', certData);
      // 处理注册成功逻辑 certData的值是一个JSON字符串:"{\"csrJson\":\"-----BEGIN "}"
      try {
        const { csrJson } = JSON.parse(certData);
        // emit("register", csrJson);
      } catch (error) {
        console.error('解析证书数据失败:', error);
      }
    },
    onError: (error) => {
      console.error('注册失败:', error);
    },
    onMessage: (message, type) => {
      console.log(`消息[${type}]:`, message);
    },
  },
});

const handleRegister = () => {
  // 凭证类型:1-接入主体身份凭证 2-接入连接器身份凭证 3-业务节点身份凭证
  const certType = '1';

  // 企业名称
  const orgName = '数据空间研究院';

  // 国家代码
  const countryCode = 'CN';

  // 所属主体的标识(可选,连接器身份和业务节点身份必选)
  // const ou = '研发部';

  const subject = [
    { name: 'O', value: orgName }, // 企业名称(Organization)
    { name: 'C', value: countryCode }, // 国家代码(Country)
    // { name: 'OU', value: ou }, // 所属主体的标识(Organizational Unit)
  ];
  IDataRegister.registerIdentity({
    certType, // 凭证类型,必选
    subject: JSON.stringify(subject), // 主体信息,必选
    // didMethod: 'ethr', // DID方法,可选
  });
};

### 参数说明

| 参数 | 类型 | 必选/可选 | 说明 |
|------|------|----------|------|
| `certType` | string | 必选 | 凭证类型。可选值:"1"(主体身份)、"2"(连接器身份)、"3"(业务节点身份) |
| `subject` | string \| object \| array | 必选 | 主体信息,支持三种格式:<br>1. JSON 字符串格式:`'{"O":"企业名称","C":"CN"}'`<br>2. 对象格式:`{O:"企业名称",C:"CN"}`<br>3. 数组格式:`[{name:"O",value:"企业名称"},{name:"C",value:"CN"}]` |
| `didMethod` | string | 可选 | DID方法 |

### subject 字段说明

| 字段 | 类型 | 必选/可选 | 说明 |
|------|------|----------|------|
| `O` | string | 必选 | 企业名称(Organization) |
| `OU` | string | 可选 | 所属主体的标识(Organizational Unit),主体身份可不填,业务节点、接入连接器身份必选 |
| `C` | string | 必选 | 国家代码(Country) |

### 回调函数说明

| 回调函数 | 类型 | 说明 |
|----------|------|------|
| `onLoading` | `(loading: boolean) => void` | 加载状态变化回调 |
| `onSuccess` | `(certData: string) => void` | 注册成功回调,参数 certData 是证书数据(JSON 字符串) |
| `onError` | `(error: Error \| any) => void` | 注册失败回调,参数 error 为错误信息 |
| `onMessage` | `(message: string, type?: string) => void` | 消息回调,参数 message 为消息内容,type 为消息类型 |

4. 身份凭证导入

import { createIDataImportVc } from 'idata-client';

const IDataImportVc = createIDataImportVc({
  callbacks: {
    onLoading: (loading) => {
      console.log('导入状态:', loading);
    },
    onSuccess: (result) => {
      console.log('导入成功:', result);
    },
    onError: (error) => {
      console.error('导入失败:', error);
    },
    onMessage: (message, type) => {
      console.log(`消息[${type}]:`, message);
    },
  },
});

const handleImport = () => {
  // 证书内容
  const crt = `-----BEGIN CERTIFICATE-----
MIIDNDCCAhygAwIBAgIUborye/q2vMhxsjmmqZpx+eEXxykwDQYJKoZIhvcNAQEF
BQAwJTEKMAgGA1UEAwwBYTEKMAgGA1UECgwBYTELMAkGA1UEBhMCQ04wHhcNMjUw
NzEzMDY1MzAwWhcNMjkwNzA1MDY1MDAwWjBkMQswCQYDVQQGEwJDTjEPMA0GA1UE
CAwG5YyX5LqsMQ8wDQYDVQQHDAbljJfkuqwxGzAZBgNVBAoMEuenkeaKgOaciemZ
kOWFrOWPuDEWMBQGA1UEAwwNd3d3LmJhaWR1LmNvbTCCASIwDQYJKoZIhvcNAQEB
BQADggEPADCCAQoCggEBAKtsbM7tL/gJmpwBH2WT1pmat6qWAWuWlLdiW4WiX7MZ
O8VHvMgmcnhFmJ5BRZ0VUZbHF2yWw/WoOCSMktFhqJ2NhjAYRxxTvd3sGbDncPEn
0/2VSfjtjuTp4JrQe+mR6Ki1KNTuNQTBLOb1lCImPhUt1PmMqr4MpPuJlqWCs86+
doTgtWZftP2SxeinkD+HAuQ4ohH47sINTWZgkjFI9Ctu05dGR3T/UdraELgs0u41
c/ANmrQX0KLEhDOSIYgRnnApFT21Jp+I0pfuVVretLyPinK351HfvSpiSRi9oHSp
CjK3Q70cNG7iyieqYmksWBo0dge9uvi7ufB+19viJI0CAwEAAaMdMBswCwYDVR0R
BAQwAoIAMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQEFBQADggEBAJkHSavndKwY
g7J0BFvGYxQQjNmWtN19zgehnNKQBRRSgVve710AjsiIhsnfF0JWY+bOMmTg3055
wp/8U9vMEsKYj7BOci8jMvI+7mcid6XgOPOgCV6t0mXQ5o7bWxVGNerVtNuPmcuY
5JCcnq9kGlzPXNsH7zc3T9HNtgn2Mjw8+3mLZElIApEz3XV4dtw1afRzuoM+bH04
Arz7S/A6RwVSa3twaihggmhLIGzxJyq5k60aeK35If5KNouPn5E12uUHAgjDs67+
asd9tWOH8zKR161kQ0xybhZfJPiUAJ3NWWqJFV+V4V7Z4KfzZ9oCLs+ObHCqCGky
dZOehfiSF2A=
-----END CERTIFICATE-----`;
  const certType = '1'; // 凭证类型 1-接入主体身份凭证 2-接入连接器身份凭证 3-业务节点身份凭证

  // 区域功能节点api地址
  const apiUrl = 'https://idata.com/api';

  const certData = {
    crt, // 证书内容
    certType, // 凭证类型
    apiUrl, // 区域功能节点api地址
  };

  IDataImportVc.importCert(certData);
};

Vue.js 集成示例

合约签署组件

<template>
  <div>
    <div class="contract-section">
      <h3>合约签署</h3>
      <div class="contract-info">
        <p><strong>合约标题:</strong>{{ contractTitle }}</p>
        <p><strong>合约ID:</strong>{{ contractId }}</p>
        <p><strong>合约摘要:</strong>{{ contractSummary }}</p>
      </div>
      <button
        @click="handleSignContract"
        :disabled="loading"
        class="sign-button"
      >
        {{ loading ? '签署中...' : '签署合约' }}
      </button>
      <div
        v-if="message"
        class="message"
      >
        {{ message }}
      </div>
      <div
        v-if="signature"
        class="signature-result"
      >
        <h4>签署成功</h4>
        <p>签名结果:</p>
        <pre>{{ signature }}</pre>
      </div>
    </div>
  </div>
</template>

<script>
import { createIDataSignContract } from 'idata-client';

export default {
  data() {
    return {
      loading: false,
      message: '',
      signature: '',
      contractTitle: '采购合同',
      contractId: 'CONTRACT-2024-001',
      contractSummary: '此合同规定了采购方与供应商之间的权利义务关系...',
      IDataSignContract: null,
    };
  },
  created() {
    this.IDataSignContract = createIDataSignContract({
      options: {
        contractData: JSON.stringify({
          contractId: this.contractId,
          title: this.contractTitle,
          content: '完整的合约内容...',
          parties: [
            { name: '采购方', role: 'buyer' },
            { name: '供应商', role: 'supplier' },
          ],
          effectiveDate: new Date().toISOString(),
          expirationDate: new Date(
            Date.now() + 365 * 24 * 60 * 60 * 1000,
          ).toISOString(),
        }),
        cn: 'example-company', // 主体标识
        type: 1, // 1-区域,2-业务
      },
      callbacks: {
        onLoading: (loading) => {
          this.loading = loading;
        },
        onSuccess: (signature) => {
          this.message = '合约签署成功!';
          this.signature = signature;
          // 将签名发送到后端
          this.submitSignatureToBackend(signature);
        },
        onError: (error) => {
          this.message = `签署失败: ${error.message}`;
        },
        onMessage: (message, type) => {
          this.message = message;
          if (type === 'error') {
            console.error('签署错误:', message);
          }
        },
      },
    });
  },
  methods: {
    handleSignContract() {
      this.signature = ''; // 清除之前的签名
      this.IDataSignContract.signContract();
    },
    async submitSignatureToBackend(signature) {
      try {
        // 调用后端API提交签名
        const response = await fetch('/api/contracts/sign', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
          },
          body: JSON.stringify({
            contractId: this.contractId,
            signature: signature,
            timestamp: new Date().toISOString(),
          }),
        });

        if (response.ok) {
          console.log('签名已成功提交到后端');
        } else {
          console.error('提交签名到后端失败');
        }
      } catch (error) {
        console.error('提交签名时出错:', error);
      }
    },
  },
  beforeDestroy() {
    if (this.IDataSignContract) {
      this.IDataSignContract.destroy();
    }
  },
};
</script>

登录组件

<template>
  <div>
    <button
      @click="handleLogin"
      :disabled="loading"
    >
      {{ loading ? '登录中...' : '统一身份登录' }}
    </button>
    <div v-if="message">{{ message }}</div>
  </div>
</template>

<script>
import { createIDataLogin } from 'idata-client';

export default {
  data() {
    return {
      loading: false,
      message: '',
      IDataLogin: null,
    };
  },
  created() {
    // 区域/行业功能节点、业务节点ID
    const nodeId = '212340000MB1M26392A340195BZZPGQX';

    this.IDataLogin = createIDataLogin({
      options: {
        nodeId, // 区域/行业功能节点、业务节点ID
      },
      callbacks: {
        onLoading: (loading) => {
          this.loading = loading;
        },
        onSuccess: (token) => {
          this.message = '登录成功!';
          localStorage.setItem('authToken', token);
          this.$router.push('/dashboard');
        },
        onError: (error) => {
          this.message = `登录失败: ${error.message}`;
        },
        onMessage: (message, type) => {
          this.message = message;
        },
      },
    });
  },
  methods: {
    handleLogin() {
      this.IDataLogin.login();
    },
  },
  beforeDestroy() {
    if (this.IDataLogin) {
      this.IDataLogin.destroy();
    }
  },
};
</script>

注册组件

<template>
  <div>
    <button
      @click="handleRegister"
      :disabled="loading"
    >
      {{ loading ? '注册中...' : '注册数字身份' }}
    </button>
    <div v-if="message">{{ message }}</div>
  </div>
</template>

<script>
import { createIDataRegister } from 'idata-client';

export default {
  data() {
    return {
      loading: false,
      message: '',
      IDataRegister: null,
    };
  },
  created() {
    this.IDataRegister = createIDataRegister({
      callbacks: {
        onLoading: (loading) => {
          this.loading = loading;
        },
        onSuccess: (certData) => {
          this.message = '注册成功!';
          console.log('证书数据:', certData);
          // 处理注册成功逻辑 - 解析证书数据
          try {
            const { csrJson } = JSON.parse(certData);
            console.log('解析后的CSR:', csrJson);
            // 可以将CSR数据保存到本地存储或发送到后端
            localStorage.setItem('csrData', csrJson);
          } catch (error) {
            console.error('解析证书数据失败:', error);
          }
        },
        onError: (error) => {
          this.message = `注册失败: ${error.message}`;
        },
        onMessage: (message, type) => {
          this.message = message;
        },
      },
    });
  },
  methods: {
    handleRegister() {
      // 凭证类型:1-接入主体身份凭证 2-接入连接器身份凭证 3-业务节点身份凭证
      const certType = '1';

      // 企业名称
      const orgName = '数据空间研究院';

      // 国家代码
      const countryCode = 'CN';

      // 所属主体的标识(可选,连接器身份和业务节点身份必选)
      // const ou = '研发部';

      const subject = [
        { name: 'O', value: orgName }, // 企业名称(Organization)
        { name: 'C', value: countryCode }, // 国家代码(Country)
        // { name: 'OU', value: ou }, // 所属主体的标识(Organizational Unit)
      ];
      this.IDataRegister.registerIdentity({
        certType, // 凭证类型,必选
        subject: JSON.stringify(subject), // 主体信息,必选
        // didMethod: 'ethr', // DID方法,可选
      });
    },
  },
  beforeDestroy() {
    if (this.IDataRegister) {
      this.IDataRegister.destroy();
    }
  },
};
</script>

React 集成示例

合约签署组件

import React, { useState, useEffect, useRef } from 'react';
import { createIDataSignContract } from 'idata-client';

function ContractSignComponent() {
  const [loading, setLoading] = useState(false);
  const [message, setMessage] = useState('');
  const [signature, setSignature] = useState('');
  const [contractData, setContractData] = useState({
    title: '采购合同',
    id: 'CONTRACT-2024-001',
    summary: '此合同规定了采购方与供应商之间的权利义务关系...',
  });

  const IDataSignContractRef = useRef(null);

  useEffect(() => {
    IDataSignContractRef.current = createIDataSignContract({
      options: {
        contractData: JSON.stringify({
          contractId: contractData.id,
          title: contractData.title,
          content: '完整的合约内容...',
          parties: [
            { name: '采购方', role: 'buyer' },
            { name: '供应商', role: 'supplier' },
          ],
          effectiveDate: new Date().toISOString(),
          expirationDate: new Date(
            Date.now() + 365 * 24 * 60 * 60 * 1000,
          ).toISOString(),
        }),
        cn: 'example-company',
        type: 1,
      },
      callbacks: {
        onLoading: setLoading,
        onSuccess: (signature) => {
          setMessage('合约签署成功!');
          setSignature(signature);
          // 将签名发送到后端
          submitSignatureToBackend(signature);
        },
        onError: (error) => {
          setMessage(`签署失败: ${error.message}`);
        },
        onMessage: (message) => {
          setMessage(message);
        },
      },
    });

    return () => {
      if (IDataSignContractRef.current) {
        IDataSignContractRef.current.destroy();
      }
    };
  }, [contractData]);

  const handleSignContract = () => {
    setSignature(''); // 清除之前的签名
    IDataSignContractRef.current?.signContract();
  };

  const submitSignatureToBackend = async (signature) => {
    try {
      const response = await fetch('/api/contracts/sign', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          contractId: contractData.id,
          signature: signature,
          timestamp: new Date().toISOString(),
        }),
      });

      if (response.ok) {
        console.log('签名已成功提交到后端');
      } else {
        console.error('提交签名到后端失败');
      }
    } catch (error) {
      console.error('提交签名时出错:', error);
    }
  };

  return (
    <div style={{ maxWidth: '600px', margin: '20px 0' }}>
      <div
        style={{
          padding: '20px',
          border: '1px solid #e0e0e0',
          borderRadius: '8px',
          backgroundColor: '#f9f9f9',
        }}
      >
        <h3>合约签署</h3>
        <div
          style={{
            marginBottom: '20px',
            padding: '15px',
            backgroundColor: 'white',
            borderRadius: '4px',
            borderLeft: '4px solid #007bff',
          }}
        >
          <p>
            <strong>合约标题:</strong>
            {contractData.title}
          </p>
          <p>
            <strong>合约ID:</strong>
            {contractData.id}
          </p>
          <p>
            <strong>合约摘要:</strong>
            {contractData.summary}
          </p>
        </div>

        <button
          onClick={handleSignContract}
          disabled={loading}
          style={{
            padding: '10px 20px',
            backgroundColor: loading ? '#ccc' : '#28a745',
            color: 'white',
            border: 'none',
            borderRadius: '4px',
            cursor: loading ? 'not-allowed' : 'pointer',
            fontSize: '16px',
            transition: 'background-color 0.3s',
          }}
        >
          {loading ? '签署中...' : '签署合约'}
        </button>

        {message && (
          <div
            style={{
              marginTop: '15px',
              padding: '10px',
              borderRadius: '4px',
              border: '1px solid #dee2e6',
            }}
          >
            {message}
          </div>
        )}

        {signature && (
          <div
            style={{
              marginTop: '20px',
              padding: '15px',
              backgroundColor: '#d4edda',
              border: '1px solid #c3e6cb',
              borderRadius: '4px',
            }}
          >
            <h4>签署成功</h4>
            <p>签名结果:</p>
            <pre
              style={{
                backgroundColor: '#f8f9fa',
                padding: '10px',
                borderRadius: '4px',
                overflowX: 'auto',
                fontSize: '12px',
              }}
            >
              {signature}
            </pre>
          </div>
        )}
      </div>
    </div>
  );
}

export default ContractSignComponent;

登录组件

import React, { useState, useEffect, useRef } from 'react';
import { createIDataLogin } from 'idata-client';

function LoginComponent() {
  const [loading, setLoading] = useState(false);
  const [message, setMessage] = useState('');
  const IDataLoginRef = useRef(null);

  useEffect(() => {
    // 区域/行业功能节点、业务节点ID
    const nodeId = '212340000MB1M26392A340195BZZPGQX';

    IDataLoginRef.current = createIDataLogin({
      options: {
        nodeId, // 区域/行业功能节点、业务节点ID
      },
      callbacks: {
        onLoading: setLoading,
        onSuccess: (token) => {
          setMessage('登录成功!');
          localStorage.setItem('authToken', token);
          // 跳转到主页或处理登录成功逻辑
        },
        onError: (error) => {
          setMessage(`登录失败: ${error.message}`);
        },
        onMessage: (message) => {
          setMessage(message);
        },
      },
    });

    return () => {
      if (IDataLoginRef.current) {
        IDataLoginRef.current.destroy();
      }
    };
  }, []);

  const handleLogin = () => {
    IDataLoginRef.current?.login();
  };

  return (
    <div>
      <button
        onClick={handleLogin}
        disabled={loading}
      >
        {loading ? '登录中...' : '统一身份登录'}
      </button>
      {message && <div>{message}</div>}
    </div>
  );
}

export default LoginComponent;

注册组件

import React, { useState, useEffect, useRef } from 'react';
import { createIDataRegister } from 'idata-client';

function RegisterComponent() {
  const [loading, setLoading] = useState(false);
  const [message, setMessage] = useState('');
  const IDataRegisterRef = useRef(null);

  useEffect(() => {
    IDataRegisterRef.current = createIDataRegister({
      callbacks: {
        onLoading: setLoading,
        onSuccess: (certData) => {
          setMessage('注册成功!');
          console.log('证书数据:', certData);
          // 处理注册成功逻辑 - 解析证书数据
          try {
            const { csrJson } = JSON.parse(certData);
            console.log('解析后的CSR:', csrJson);
            // 可以将CSR数据保存到本地存储或发送到后端
            localStorage.setItem('csrData', csrJson);
          } catch (error) {
            console.error('解析证书数据失败:', error);
          }
        },
        onError: (error) => {
          setMessage(`注册失败: ${error.message || error}`);
        },
        onMessage: (message) => {
          setMessage(message);
        },
      },
    });

    return () => {
      if (IDataRegisterRef.current) {
        IDataRegisterRef.current.destroy();
      }
    };
  }, []);

  const handleRegister = () => {
    // 凭证类型:1-接入主体身份凭证 2-接入连接器身份凭证 3-业务节点身份凭证
    const certType = '1';

    // 企业名称
    const orgName = '数据空间研究院';

    // 国家代码
    const countryCode = 'CN';

    // 所属主体的标识(可选,连接器身份和业务节点身份必选)
    // const ou = '研发部';

    const subject = [
      { name: 'O', value: orgName }, // 企业名称(Organization)
      { name: 'C', value: countryCode }, // 国家代码(Country)
      // { name: 'OU', value: ou }, // 所属主体的标识(Organizational Unit)
    ];
    IDataRegisterRef.current?.registerIdentity({
      certType, // 凭证类型,必选
      subject: JSON.stringify(subject), // 主体信息,必选
      // didMethod: 'ethr', // DID方法,可选
    });
  };

  return (
    <div>
      <button
        onClick={handleRegister}
        disabled={loading}
      >
        {loading ? '注册中...' : '注册数字身份'}
      </button>
      {message && <div>{message}</div>}
    </div>
  );
}

export default RegisterComponent;

配置选项

IDataLoginOptions

| 参数 | 类型 | 默认值 | 说明 | | ----------------- | -------- | ------------------------ | ------------------------------------- | | pollingTimeout | number | 600000 | 轮询超时时间(毫秒),默认10分钟 | | pollingInterval | number | 1000 | 轮询间隔(毫秒),默认1秒 | | IDataServerUrl | string | http://localhost:4521 | API基础URL | | certificate | string | '' | 证书 | | requestTimeout | number | 60000 | HTTP请求超时时间(毫秒),默认60秒 | | apiEndpoints | object | 见API端点配置 | API端点配置 | | baseUrl | string | window.location.origin | 域名配置(用于桌面应用调用) | | type | number | 1 | 类型参数 1-区域,2-业务 | | clientType | string | 自动检测 | 客户端类型(browser/electron/server) | | cn | string | '' | 主体标识 | | contractData | string | '' | 合约数据(JSON字符串) | | nodeId | string | '' | 区域节点标识(登录必需) |

API端点配置

| 参数 | 类型 | 默认值 | 说明 | | --------------- | -------- | -------------------- | --------------------- | | getAuthResult | string | /get/authResult | 登录-认证结果 | | getToken | string | /get/token | 登录-获取token | | getCert | string | /get/cert | 注册-获取证书 | | importCert | string | /import/cert | 获取身份凭证-导入证书 | | createCert | string | /create/cert | 创建证书 | | updateCert | string | /import/updateCert | 更新证书 | | getSign | string | /get/sign | 获取合约签署 |

IDataSignContractOptions

| 参数 | 类型 | 默认值 | 说明 | | -------------- | -------- | ------ | ----------------------- | | contractData | string | '' | 合约数据(JSON 字符串) | | cn | string | '' | 主体标识 | | type | number | 1 | 类型参数 1-区域,2-业务 |

IDataLoginCallbacks

| 参数 | 类型 | 说明 | | ----------- | ------------------------------------------ | -------------------------------------------------------------------- | | onLoading | (loading: boolean) => void | 加载状态变化回调 | | onSuccess | (data: any) => void | 成功回调,返回相应数据 | | onError | (error: IDataError) => void | 错误回调,返回错误对象 | | onMessage | (message: string, type?: string) => void | 消息回调,type 可选值: 'info', 'error', 'warning', 'success', 'CORS' |

IDataError 接口

interface IDataError {
  /** 错误消息 */
  message: string;
  /** 错误数据 */
  data?: {
    message?: string;
    data?: any;
  };
  /** 原始错误对象 */
  originalError?: Error;
  /** HTTP状态码 */
  status?: number;
  /** 错误类型 */
  type?: string;
  /** 请求URL */
  url?: string;
}

TypeScript 类型定义

import {
  createIDataLogin,
  createIDataRegister,
  createIDataImportVc,
  createIDataSignContract,
  IDataLoginConfig,
  IDataLoginOptions,
  IDataLoginCallbacks,
  RegisterIdentityParams,
  CertData,
  ErrorCode,
  IDataError,
  type IDATA,
} from 'idata-client';

身份凭证数据结构

const certData = {
  crt: 'string', // 证书内容-----BEGIN CERTIFICATE-----\nXXX-----END CERTIFICATE-----(必填)
  apiUrl: 'string', // 接口地址(必填)
  certType: '1', // 凭证类型 1-接入主体身份凭证 2-接入连接器身份凭证 3-业务节点身份凭证(可选)
};

合约数据结构

const contractData = {
  contractId: 'string',      // 合约ID
  title: 'string',           // 合约标题
  content: 'string',         // 合约内容
  parties: Array<{           // 合约参与方
    name: 'string',
    role: 'string'
  }>,
  effectiveDate: 'string',   // 生效日期(ISO格式)
  expirationDate: 'string',  // 到期日期(ISO格式)
  // 其他自定义字段...
};

TypeScript 使用示例

类型导入

import {
  createIDataLogin,
  createIDataRegister,
  createIDataImportVc,
  createIDataSignContract,
  IDataLoginConfig,
  IDataLoginOptions,
  IDataLoginCallbacks,
  RegisterIdentityParams,
  CertData,
  ErrorCode,
  IDataError,
  IDATA,
} from 'idata-client';

登录示例

import { createIDataLogin, ErrorCode } from 'idata-client';

const loginConfig: IDataLoginConfig = {
  options: {
    nodeId: 'example-node-id',
  },
  callbacks: {
    onLoading: (loading: boolean) => {
      console.log('登录状态:', loading);
    },
    onSuccess: (data: any) => {
      console.log('登录成功:', data);
    },
    onError: (error: IDataError) => {
      console.error('登录错误:', error);
      if (error.message.includes(ErrorCode.LOGIN_MISSING_NODE_ID)) {
        console.error('错误:缺少 nodeId 参数');
      }
    },
    onMessage: (message: string, type: string) => {
      console.log(`[${type}]`, message);
    },
  },
};

const IDataLogin = createIDataLogin(loginConfig);
IDataLogin.login();

注册示例

import { createIDataRegister } from 'idata-client';

const registerConfig: IDataLoginConfig = {
  callbacks: {
    onSuccess: (data: any) => {
      const certData = JSON.parse(data);
      console.log('注册成功:', certData);
    },
    onError: (error: IDataError) => {
      console.error('注册错误:', error);
    },
  },
};

const IDataRegister = createIDataRegister(registerConfig);

const registerParams: RegisterIdentityParams = {
  certType: '1',
  subject: JSON.stringify({
    O: '企业名称',
    C: 'CN',
    OU: '所属主体的标识',
  }),
};

IDataRegister.registerIdentity(registerParams);

导入凭证示例

import { createIDataImportVc, CertData } from 'idata-client';

const importConfig: IDataLoginConfig = {
  callbacks: {
    onSuccess: (data: any) => {
      console.log('导入成功:', data);
    },
    onError: (error: IDataError) => {
      console.error('导入错误:', error);
    },
  },
};

const IDataImportVc = createIDataImportVc(importConfig);

const certData: CertData = {
  crt: '-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----',
  apiUrl: 'https://api.example.com',
  certType: '1',
};

IDataImportVc.importCert(certData);

签署合约示例

import { createIDataSignContract } from 'idata-client';

const signConfig: IDataLoginConfig = {
  options: {
    contractData: JSON.stringify({
      contractId: 'CONTRACT-001',
      title: '采购合同',
      content: '...',
    }),
    cn: 'example-company',
    type: 1,
  },
  callbacks: {
    onSuccess: (signature: string) => {
      console.log('签署成功:', signature);
    },
    onError: (error: IDataError) => {
      console.error('签署错误:', error);
    },
  },
};

const IDataSignContract = createIDataSignContract(signConfig);
IDataSignContract.signContract();

API 方法

登录相关

login()

开始统一身份登录流程。

await IDataLogin.login();

合约签署相关

signContract()

开始合约签署流程。

await IDataSignContract.signContract();

注册相关

registerIdentity(params)

注册身份(1-接入主体身份凭证 2-接入连接器身份凭证 3-业务节点身份凭证)。

// 注册
await IDataRegister.registerIdentity({
  certType: '1', // 凭证类型,必选。可选值:"1"(主体身份)、"2"(连接器身份)、"3"(业务节点身份)
  subject: '{"O":"企业名称","C":"CN"}', // 主体信息,必选
});

参数说明:

  • certType (必填): 凭证类型,可选值:"1"(主体身份)、"2"(连接器身份)、"3"(业务节点身份)
  • subject (必填): 主体信息,支持三种格式:
    1. JSON 字符串格式:'{"O":"企业名称","C":"CN"}'
    2. 对象格式:{O:"企业名称",C:"CN"}
    3. 数组格式:[{name:"O",value:"企业名称"},{name:"C",value:"CN"}] 支持的字段:O(企业名称,必选)、OU(所属主体的标识,可选)、C(国家代码,必选)
  • didMethod (可选): DID方法

凭证导入相关

importCert(certData)

导入身份凭证。

const certData = {
  crt: '-----BEGIN CERTIFICATE-----\nXXX-----END CERTIFICATE-----', // 证书内容(必填)
  apiUrl: 'http://example.com/api', // 接口地址(必填)
  certType: '1', // 证书类型 1-主体身份 2-连接器身份 3-业务节点身份(可选)
};

await IDataImportVc.importCert(certData);

工厂函数

createIDataLogin(config)

创建登录实例。

import { createIDataLogin } from 'idata-client';
const IDataLogin = createIDataLogin(config);

createIDataSignContract(config)

创建合约签署实例。

import { createIDataSignContract } from 'idata-client';
const IDataSignContract = createIDataSignContract(config);

createIDataRegister(config)

创建注册实例。

import { createIDataRegister } from 'idata-client';
const IDataRegister = createIDataRegister(config);

createIDataImportVc(config)

创建凭证导入实例。

import { createIDataImportVc } from 'idata-client';
const IDataImportVc = createIDataImportVc(config);

注意事项

  1. 桌面应用依赖:此 SDK 依赖于桌面应用程序,需要确保目标设备已安装相应的桌面客户端
  2. 自定义协议:自定义协议 IData://idata:// 需要在桌面应用程序中正确注册
  3. 服务器配置:确保 IData 服务器正确配置并运行在指定端口(默认 4521)
  4. 资源清理:建议在组件销毁时调用 destroy() 方法以释放资源
  5. 错误处理:建议实现完整的错误处理机制,包括网络错误、超时等情况
  6. 合约数据:合约数据需要正确格式化,建议使用 JSON 字符串格式
  7. 签署上下文:合约签署需要在用户明确知晓合约内容的情况下进行

错误处理

错误码列表

登录相关错误码 (JS10xx)

| 错误码 | 描述 | 解决方案 | | -------- | --------------------- | ------------------------------------------ | | JS1001 | 缺少必需参数 nodeId | 请确保在配置中提供正确的 nodeId 参数 | | JS1002 | nodeId 参数值无效 | 请确保 nodeId 是有效的字符串格式 | | JS1004 | 请求失败/身份认证失败 | 请检查网络连接、服务器状态或联系平台方处理 |

身份注册错误码 (JS30xx)

| 错误码 | 描述 | 解决方案 | | -------- | ------------------------------------------------ | -------------------------------------------------------------------------- | | JS3001 | 缺少必需参数 certType | 请提供 certType 参数,可选值:"1"、"2"、"3" | | JS3002 | certType 参数值无效 | certType 必须是 "1"(主体身份)、"2"(连接器身份)或 "3"(业务节点身份) | | JS3003 | 缺少必需参数 subject | 请提供有效的 subject 主体信息 | | JS3004 | subject 参数格式无效 | subject 必须是有效的 JSON 字符串格式或数组格式 | | JS3005 | subject 缺少必填字段 O(企业名称) | 请在 subject 中添加 O 字段,如 {"O":"企业名称"} | | JS3006 | subject 缺少必填字段 C(国家代码) | 请在 subject 中添加 C 字段,如 {"C":"CN"} | | JS3007 | subject 缺少必填字段 OU(连接器/业务节点必需) | 当 certType 为 "2" 或 "3" 时,必须在 subject 中添加 OU 字段 |

身份凭证导入错误码 (JS40xx)

| 错误码 | 描述 | 解决方案 | | -------- | --------------------- | ------------------------------ | | JS4001 | 缺少必需参数 crt | 请提供有效的证书内容(crt) | | JS4002 | 缺少必需参数 apiUrl | 请提供有效的接口地址(apiUrl) | | JS4003 | 请求失败 | 请检查网络连接和服务器状态 |

常见错误情况

  • 网络连接失败
  • 服务器响应错误
  • 桌面应用程序未安装
  • 自定义协议未注册
  • 轮询超时
  • 合约数据格式错误
  • 用户拒绝签署
  • 跨域请求被阻止
  • 请求超时

错误处理建议

const IDataSignContract = createIDataSignContract({
  callbacks: {
    onError: (error) => {
      console.error('合约签署错误:', error);
      console.error('错误类型:', error.type);
      console.error('错误URL:', error.url);

      // 根据错误码进行处理
      if (error.message.includes('JS1001')) {
        // 缺少 nodeId 参数
        alert('登录参数不完整,请联系管理员');
      } else if (error.message.includes('JS3001')) {
        // 缺少 certType 参数
        alert('注册参数不完整,请选择凭证类型');
      } else if (error.message.includes('JS4001')) {
        // 缺少证书参数
        alert('请提供有效的证书内容');
      } else if (error.type === 'CORS') {
        // 跨域错误
        alert('跨域请求被阻止,请检查服务器CORS配置');
      } else if (error.type === 'error' && error.message.includes('timeout')) {
        // 超时错误
        alert('操作超时,请检查网络连接后重试');
      } else if (error.message.includes('refuse')) {
        // 用户拒绝或客户端繁忙
        alert('客户端正忙,请稍后再试');
      } else {
        // 其他错误
        alert('操作失败:' + error.message);
      }
    },
    onMessage: (message, type) => {
      if (type === 'error') {
        console.error('错误消息:', message);
      } else if (type === 'CORS') {
        console.warn('跨域警告:', message);
      }
    },
  },
});

完整错误处理示例

import { createIDataLogin } from 'idata-client';

const IDataLogin = createIDataLogin({
  options: {
    nodeId: 'example-node-id', // 必须提供
  },
  callbacks: {
    onLoading: (loading) => {
      console.log('加载状态:', loading);
    },
    onSuccess: (token) => {
      console.log('登录成功:', token);
    },
    onError: (error) => {
      // 检查错误消息中的错误码
      if (error.message.includes('JS1001')) {
        console.error('错误:缺少 nodeId 参数');
      } else if (error.message.includes('JS1002')) {
        console.error('错误:nodeId 无效');
      } else if (error.type === 'CORS') {
        console.error('跨域错误:', error.message);
      } else if (error.type === 'error' && error.message.includes('timeout')) {
        console.error('超时错误:', error.message);
      } else {
        console.error('未知错误:', error);
      }
    },
    onMessage: (message, type) => {
      console.log(`[${type}]`, message);
    },
  },
});

浏览器兼容性

  • Chrome 60+
  • Firefox 55+
  • Safari 12+
  • Edge 79+
  • 支持现代浏览器的自定义协议处理

许可证

MIT License

贡献

欢迎提交 Issue 和 Pull Request 来改进这个项目。