返回博客

TypeScript OSDK:为前端开发者打造类型安全的 Ontology SDK

TypeScript OSDK(Ontology Software Development Kit)是 coomia-dip 面向前端和全栈开发者的核心 SDK,它将 Ontology 的类型系统映射为 TypeScript 类型,通过代码生成实现编译期类型安全。开发者可以像操作本地对象一样操作 Ontology 中的 ObjectType、LinkType 和 Action,同时获得完整的 IDE 自动补全、类型推断和编译期错误检查。本文从架构设计、类型生成、运行时层、查询 DSL 到测试策略,全面解析 TypeScript OSDK 的设计与实现。

Coomia发布于 2025年9月29日16 分钟阅读
分享本文Twitter / X

系列:S6 平台工程 · 第 15 篇 | 难度:高级 | 阅读时间:18 分钟

TypeScript OSDK:为前端开发者打造类型安全的 Ontology SDK

#TL;DR

TypeScript OSDK(Ontology Software Development Kit)是 coomia-dip 面向前端和全栈开发者的核心 SDK,它将 Ontology 的类型系统映射为 TypeScript 类型,通过代码生成实现编译期类型安全。开发者可以像操作本地对象一样操作 Ontology 中的 ObjectType、LinkType 和 Action,同时获得完整的 IDE 自动补全、类型推断和编译期错误检查。本文从架构设计、类型生成、运行时层、查询 DSL 到测试策略,全面解析 TypeScript OSDK 的设计与实现。

#1. 为什么需要 TypeScript OSDK

#1.1 前端开发的 Ontology 挑战

coomia-dip 以 Ontology 为核心数据模型,前端应用需要频繁与 Ontology 交互。传统的 REST API 调用方式存在诸多痛点:

  • 类型不安全:API 返回 any 类型的 JSON,属性名拼写错误只能在运行时发现
  • 文档脱节:API 文档与实际接口容易不一致,开发者需要反复查阅文档
  • 模板代码多:每个 API 调用都需要手写请求构建、响应解析、错误处理
  • 缺乏领域语义:开发者看到的是 HTTP 方法和 URL,而非 Ontology 的领域概念

#1.2 对标 Palantir OSDK

Palantir 在 2023 年发布了 TypeScript OSDK,让开发者可以用类型安全的方式访问 Foundry Ontology。coomia-dip 的 TypeScript OSDK 实现了等价能力:

能力Palantir OSDKcoomia-dip OSDK
类型生成从 Ontology Schema 生成从 Ontology Schema 生成
ObjectType 操作CRUD + 搜索CRUD + 搜索 + 流式
LinkType 遍历支持支持 + 深度遍历
Action 执行类型安全的参数类型安全 + 乐观更新
查询 DSL链式 API链式 + 组合式
实时订阅ObjectSet 监听WebSocket + SSE
批量操作支持支持 + 事务
离线支持部分IndexedDB 本地缓存

#1.3 设计原则

TypeScript OSDK 遵循以下设计原则:

  1. 类型优先(Type-First):所有 API 在编译期完全类型安全
  2. 零配置(Zero Config):默认配置即可开箱使用
  3. 渐进式复杂度(Progressive Complexity):简单操作一行代码,复杂操作渐进暴露
  4. 框架无关(Framework Agnostic):不绑定 React/Vue/Angular,可在任何环境使用
  5. 可摇树(Tree-Shakeable):未使用的功能不会打入最终 bundle

#2. 整体架构

#2.1 分层架构

Code
┌─────────────────────────────────────────────────┐
│           Application Layer                      │
│  (React/Vue/Angular/Node.js)                    │
└─────────────┬───────────────────────────────────┘
              │
┌─────────────▼───────────────────────────────────┐
│        Generated Type Layer                      │
│  (ObjectTypes, LinkTypes, Actions — 编译期生成)   │
└─────────────┬───────────────────────────────────┘
              │
┌─────────────▼───────────────────────────────────┐
│          Runtime Layer                           │
│  ┌──────────┐ ┌──────────┐ ┌──────────────┐    │
│  │ Client   │ │ Cache    │ │ Subscription │    │
│  │ Manager  │ │ Manager  │ │ Manager      │    │
│  └──────────┘ └──────────┘ └──────────────┘    │
│  ┌──────────┐ ┌──────────┐ ┌──────────────┐    │
│  │ Query    │ │ Batch    │ │ Auth         │    │
│  │ Builder  │ │ Executor │ │ Provider     │    │
│  └──────────┘ └──────────┘ └──────────────┘    │
└─────────────┬───────────────────────────────────┘
              │
┌─────────────▼───────────────────────────────────┐
│         Transport Layer                          │
│  ┌──────────┐ ┌──────────┐ ┌──────────────┐    │
│  │ HTTP/2   │ │ WebSocket│ │ gRPC-Web     │    │
│  │ Client   │ │ Client   │ │ Client       │    │
│  └──────────┘ └──────────┘ └──────────────┘    │
└─────────────────────────────────────────────────┘

#2.2 代码生成流程

Code
Ontology Schema ──▶ Schema Introspector ──▶ Type Generator ──▶ .ts files
    (Runtime)           (CLI Tool)           (Template Engine)

步骤:
1. CLI 连接 coomia-dip API,获取当前 Ontology Schema
2. Schema Introspector 解析 ObjectType、LinkType、ActionType 定义
3. Type Generator 使用 ts-morph 生成 TypeScript 类型和操作接口
4. 生成文件输出到 `src/generated/` 目录
5. 开发者直接 import 使用

#3. 类型生成系统

#3.1 ObjectType 类型生成

给定一个 Ontology ObjectType 定义:

JSON
{
  "apiName": "Employee",
  "displayName": "员工",
  "properties": {
    "employeeId": { "type": "string", "primaryKey": true },
    "name": { "type": "string" },
    "department": { "type": "string" },
    "salary": { "type": "double" },
    "hireDate": { "type": "timestamp" },
    "isActive": { "type": "boolean" },
    "tags": { "type": "array", "items": { "type": "string" } },
    "metadata": { "type": "object" }
  }
}

生成的 TypeScript 类型:

TypeScript
// src/generated/objects/Employee.ts

/** 员工 ObjectType */
export interface Employee {
  readonly employeeId: string;
  readonly name: string;
  readonly department: string;
  readonly salary: number;
  readonly hireDate: Date;
  readonly isActive: boolean;
  readonly tags: readonly string[];
  readonly metadata: Record<string, unknown>;
  readonly $primaryKey: string;
  readonly $objectType: "Employee";
}

/** Employee 的可写字段 */
export interface EmployeeWritable {
  name?: string;
  department?: string;
  salary?: number;
  hireDate?: Date;
  isActive?: boolean;
  tags?: string[];
  metadata?: Record<string, unknown>;
}

/** Employee 的过滤条件 */
export interface EmployeeFilter {
  employeeId?: StringFilter;
  name?: StringFilter;
  department?: StringFilter;
  salary?: NumericFilter;
  hireDate?: DateFilter;
  isActive?: BooleanFilter;
  tags?: ArrayFilter<string>;
  $and?: EmployeeFilter[];
  $or?: EmployeeFilter[];
  $not?: EmployeeFilter;
}

/** Employee 的排序字段 */
export type EmployeeOrderBy =
  | "employeeId"
  | "name"
  | "department"
  | "salary"
  | "hireDate"
  | "-employeeId"
  | "-name"
  | "-department"
  | "-salary"
  | "-hireDate";

/** Employee 的属性名集合 */
export type EmployeePropertyName = keyof Employee;

#3.2 LinkType 类型生成

TypeScript
// src/generated/links/EmployeeLinks.ts

/** Employee 的关联关系 */
export interface EmployeeLinks {
  /** 员工所属部门 */
  department: SingleLink<Department>;
  /** 员工管理的项目 */
  managedProjects: MultiLink<Project>;
  /** 员工的直属上级 */
  manager: SingleLink<Employee>;
  /** 员工的直属下属 */
  directReports: MultiLink<Employee>;
}

/** 单关联:恰好一个关联对象 */
export interface SingleLink<T> {
  get(): Promise<T>;
  getRid(): string;
}

/** 多关联:零到多个关联对象 */
export interface MultiLink<T> {
  list(options?: PaginationOptions): Promise<Page<T>>;
  count(): Promise<number>;
  getRids(): Promise<string[]>;
  where(filter: FilterOf<T>): MultiLink<T>;
  orderBy(order: OrderByOf<T>): MultiLink<T>;
}

#3.3 ActionType 类型生成

TypeScript
// src/generated/actions/PromoteEmployee.ts

/** 晋升员工 Action */
export interface PromoteEmployeeParams {
  /** 员工 ID */
  employeeId: string;
  /** 新职级 */
  newLevel: number;
  /** 薪资涨幅百分比 */
  salaryIncreasePercent: number;
  /** 生效日期 */
  effectiveDate?: Date;
  /** 备注 */
  notes?: string;
}

export interface PromoteEmployeeResult {
  /** 更新后的员工对象 */
  employee: Employee;
  /** 审批状态 */
  approvalStatus: "approved" | "pending" | "rejected";
}

export type PromoteEmployeeAction = Action<
  PromoteEmployeeParams,
  PromoteEmployeeResult
>;

#4. 运行时 Client

#4.1 Client 初始化

TypeScript
import { OntoPlatformClient } from "@onto/osdk";

// 最简初始化
const client = new OntoPlatformClient({
  baseUrl: "https://coomia-dip.example.com",
  token: "your-api-token",
});

// 完整配置
const client = new OntoPlatformClient({
  baseUrl: "https://coomia-dip.example.com",
  auth: {
    type: "oauth2",
    clientId: "my-app",
    clientSecret: process.env.CLIENT_SECRET,
    scopes: ["ontology:read", "ontology:write"],
  },
  cache: {
    enabled: true,
    ttl: 60_000,        // 缓存 60 秒
    maxSize: 10_000,     // 最多缓存 10000 个对象
    strategy: "lru",
  },
  retry: {
    maxRetries: 3,
    backoffMs: 1000,
  },
  timeout: 30_000,
  interceptors: [loggingInterceptor, metricsInterceptor],
});

#4.2 ObjectType 操作

TypeScript
// 获取单个对象
const employee = await client.objects.Employee.get("emp-001");
console.log(employee.name);      // 类型安全:string
console.log(employee.salary);    // 类型安全:number

// 搜索对象
const results = await client.objects.Employee
  .where({
    department: { eq: "Engineering" },
    salary: { gte: 100000 },
    isActive: { eq: true },
  })
  .orderBy("-salary")
  .select("name", "salary", "department")
  .limit(20)
  .list();

for (const emp of results.data) {
  console.log(`${emp.name}: ${emp.salary}`);
}

// 创建对象
const newEmployee = await client.objects.Employee.create({
  name: "张三",
  department: "Engineering",
  salary: 120000,
  hireDate: new Date(),
  isActive: true,
  tags: ["senior", "fullstack"],
});

// 更新对象
await client.objects.Employee.update("emp-001", {
  salary: 150000,
  tags: ["senior", "fullstack", "lead"],
});

// 删除对象
await client.objects.Employee.delete("emp-001");

#4.3 LinkType 遍历

TypeScript
// 获取员工的部门
const dept = await client.objects.Employee
  .get("emp-001")
  .then(emp => emp.$links.department.get());

// 获取部门下的所有员工
const employees = await client.objects.Department
  .get("dept-eng")
  .then(dept => dept.$links.employees
    .where({ isActive: { eq: true } })
    .orderBy("name")
    .list()
  );

// 深度遍历:获取经理的所有下属的项目
const projects = await client.objects.Employee
  .get("emp-manager-001")
  .then(mgr => mgr.$links.directReports.list())
  .then(reports =>
    Promise.all(
      reports.data.map(r => r.$links.managedProjects.list())
    )
  );

#4.4 Action 执行

TypeScript
// 执行 Action
const result = await client.actions.PromoteEmployee.execute({
  employeeId: "emp-001",
  newLevel: 5,
  salaryIncreasePercent: 15,
  effectiveDate: new Date("2026-04-01"),
  notes: "优秀绩效晋升",
});

if (result.approvalStatus === "approved") {
  console.log(`晋升成功,新薪资: ${result.employee.salary}`);
} else {
  console.log(`晋升待审批`);
}

// Action 参数验证(编译期)
await client.actions.PromoteEmployee.execute({
  employeeId: "emp-001",
  newLevel: "five",  // TS Error: Type 'string' is not assignable to type 'number'
  salaryIncreasePercent: 15,
});

// 批量 Action
const results = await client.actions.PromoteEmployee.executeBatch([
  { employeeId: "emp-001", newLevel: 5, salaryIncreasePercent: 15 },
  { employeeId: "emp-002", newLevel: 4, salaryIncreasePercent: 10 },
  { employeeId: "emp-003", newLevel: 6, salaryIncreasePercent: 20 },
]);

#5. 查询 DSL 设计

#5.1 链式查询构建

TypeScript
// 复杂查询示例
const seniorEngineers = await client.objects.Employee
  .where({
    $and: [
      { department: { eq: "Engineering" } },
      { salary: { gte: 150000 } },
      {
        $or: [
          { tags: { contains: "senior" } },
          { tags: { contains: "staff" } },
        ],
      },
    ],
  })
  .orderBy("-salary", "name")
  .select("employeeId", "name", "salary", "tags")
  .page({ size: 50, token: nextPageToken })
  .list();

#5.2 聚合查询

TypeScript
// 按部门统计薪资
const stats = await client.objects.Employee
  .where({ isActive: { eq: true } })
  .aggregate({
    groupBy: ["department"],
    metrics: {
      avgSalary: { field: "salary", op: "avg" },
      maxSalary: { field: "salary", op: "max" },
      headcount: { op: "count" },
    },
    orderBy: "-avgSalary",
  });

// 结果类型自动推断
for (const group of stats.groups) {
  console.log(
    `${group.key.department}: avg=${group.metrics.avgSalary}, count=${group.metrics.headcount}`
  );
}

#5.3 时间序列查询

TypeScript
// 时间序列数据查询
const metrics = await client.objects.ServerMetrics
  .timeSeries({
    property: "cpuUsage",
    range: {
      start: new Date("2026-03-01"),
      end: new Date("2026-03-24"),
    },
    granularity: "1h",
    aggregation: "avg",
  });

#6. 实时订阅

#6.1 ObjectSet 订阅

TypeScript
// 订阅对象变更
const subscription = client.objects.Employee
  .where({ department: { eq: "Engineering" } })
  .subscribe({
    onAdded(employee) {
      console.log(`新员工: ${employee.name}`);
    },
    onUpdated(employee, previousVersion) {
      console.log(`更新: ${employee.name}`);
      if (employee.salary !== previousVersion.salary) {
        console.log(
          `薪资变更: ${previousVersion.salary} -> ${employee.salary}`
        );
      }
    },
    onRemoved(employeeId) {
      console.log(`员工离职: ${employeeId}`);
    },
    onError(error) {
      console.error("订阅错误:", error);
    },
  });

// 取消订阅
subscription.unsubscribe();

#6.2 WebSocket 传输

TypeScript
// 底层 WebSocket 管理
class SubscriptionManager {
  private ws: WebSocket | null = null;
  private subscriptions = new Map<string, SubscriptionHandler>();
  private reconnectAttempts = 0;
  private maxReconnectAttempts = 10;

  connect(url: string): void {
    this.ws = new WebSocket(url);

    this.ws.onmessage = (event) => {
      const message = JSON.parse(event.data) as SubscriptionMessage;
      const handler = this.subscriptions.get(message.subscriptionId);
      if (handler) {
        switch (message.type) {
          case "OBJECT_ADDED":
            handler.onAdded?.(message.object);
            break;
          case "OBJECT_UPDATED":
            handler.onUpdated?.(message.object, message.previous);
            break;
          case "OBJECT_REMOVED":
            handler.onRemoved?.(message.objectId);
            break;
        }
      }
    };

    this.ws.onclose = () => {
      this.reconnect();
    };
  }

  private reconnect(): void {
    if (this.reconnectAttempts >= this.maxReconnectAttempts) return;

    const delay = Math.min(
      1000 * Math.pow(2, this.reconnectAttempts),
      30000
    );
    this.reconnectAttempts++;

    setTimeout(() => {
      this.connect(this.url);
      // 重新订阅所有活跃订阅
      for (const [id, handler] of this.subscriptions) {
        this.resubscribe(id, handler);
      }
    }, delay);
  }
}

#7. 缓存系统

#7.1 多级缓存

TypeScript
class CacheManager {
  private l1Cache: Map<string, CacheEntry>;      // 内存缓存
  private l2Cache: IDBDatabase | null;            // IndexedDB

  constructor(options: CacheOptions) {
    this.l1Cache = new Map();
    if (options.persistent) {
      this.initIndexedDB();
    }
  }

  async get<T>(key: string): Promise<T | undefined> {
    // L1: 内存
    const l1Entry = this.l1Cache.get(key);
    if (l1Entry && !this.isExpired(l1Entry)) {
      return l1Entry.value as T;
    }

    // L2: IndexedDB
    if (this.l2Cache) {
      const l2Entry = await this.getFromIDB(key);
      if (l2Entry && !this.isExpired(l2Entry)) {
        this.l1Cache.set(key, l2Entry);  // 回填 L1
        return l2Entry.value as T;
      }
    }

    return undefined;
  }

  async set<T>(key: string, value: T, ttl?: number): Promise<void> {
    const entry: CacheEntry = {
      value,
      createdAt: Date.now(),
      ttl: ttl ?? this.defaultTtl,
    };

    this.l1Cache.set(key, entry);
    this.evictIfNeeded();

    if (this.l2Cache) {
      await this.setToIDB(key, entry);
    }
  }

  invalidate(pattern: string): void {
    for (const key of this.l1Cache.keys()) {
      if (key.match(pattern)) {
        this.l1Cache.delete(key);
      }
    }
  }
}

#7.2 乐观更新

TypeScript
// Action 执行时的乐观更新
async function executeWithOptimisticUpdate<P, R>(
  action: Action<P, R>,
  params: P,
  optimisticUpdate: (cache: CacheManager) => void,
): Promise<R> {
  // 1. 立即应用乐观更新
  const rollback = cache.snapshot();
  optimisticUpdate(cache);

  try {
    // 2. 执行实际 Action
    const result = await action.execute(params);
    // 3. 用实际结果替换乐观值
    cache.applyServerResponse(result);
    return result;
  } catch (error) {
    // 4. 失败时回滚
    cache.restore(rollback);
    throw error;
  }
}

// 使用示例
await executeWithOptimisticUpdate(
  client.actions.PromoteEmployee,
  { employeeId: "emp-001", newLevel: 5, salaryIncreasePercent: 15 },
  (cache) => {
    cache.update("Employee:emp-001", (emp) => ({
      ...emp,
      salary: emp.salary * 1.15,
    }));
  },
);

#8. 错误处理

#8.1 类型安全的错误体系

TypeScript
// 错误类型层次
abstract class OsdkError extends Error {
  abstract readonly code: string;
  abstract readonly statusCode: number;
  readonly requestId: string;
  readonly timestamp: Date;
}

class ObjectNotFoundError extends OsdkError {
  readonly code = "OBJECT_NOT_FOUND";
  readonly statusCode = 404;
  readonly objectType: string;
  readonly objectId: string;
}

class PermissionDeniedError extends OsdkError {
  readonly code = "PERMISSION_DENIED";
  readonly statusCode = 403;
  readonly requiredPermission: string;
}

class ValidationError extends OsdkError {
  readonly code = "VALIDATION_ERROR";
  readonly statusCode = 400;
  readonly violations: ValidationViolation[];
}

class ActionExecutionError extends OsdkError {
  readonly code = "ACTION_EXECUTION_FAILED";
  readonly statusCode = 500;
  readonly actionType: string;
  readonly failureReason: string;
}

// 使用
try {
  const emp = await client.objects.Employee.get("emp-999");
} catch (error) {
  if (error instanceof ObjectNotFoundError) {
    console.log(`对象不存在: ${error.objectType}/${error.objectId}`);
  } else if (error instanceof PermissionDeniedError) {
    console.log(`权限不足: 需要 ${error.requiredPermission}`);
  }
}

#8.2 Result 模式

TypeScript
// 提供 Result 模式作为异常的替代
type Result<T, E = OsdkError> =
  | { ok: true; value: T }
  | { ok: false; error: E };

// 安全版本的 API
const result = await client.objects.Employee.getSafe("emp-001");
if (result.ok) {
  console.log(result.value.name);
} else {
  console.log(`错误: ${result.error.code}`);
}

#9. 代码生成 CLI

#9.1 CLI 命令

Bash
# 初始化 OSDK 项目
npx @onto/osdk init --base-url https://coomia-dip.example.com

# 生成类型
npx @onto/osdk generate \
  --output src/generated \
  --ontology my-ontology \
  --format esm \
  --strict

# 增量生成(仅更新变更的类型)
npx @onto/osdk generate --incremental

# 验证生成代码
npx @onto/osdk validate

# 查看 Ontology Schema
npx @onto/osdk schema --ontology my-ontology

#9.2 配置文件

JSON
{
  "$schema": "https://coomia-dip.dev/osdk.schema.json",
  "version": 1,
  "connection": {
    "baseUrl": "https://coomia-dip.example.com",
    "auth": {
      "type": "token",
      "envVar": "ONTO_API_TOKEN"
    }
  },
  "generate": {
    "output": "src/generated",
    "ontologies": ["employee-ontology", "project-ontology"],
    "format": "esm",
    "strict": true,
    "features": {
      "subscriptions": true,
      "aggregations": true,
      "timeSeries": true,
      "batchOperations": true
    }
  }
}

#10. 框架集成

#10.1 React 集成

TypeScript
// @onto/osdk-react
import { useObject, useObjectSet, useAction } from "@onto/osdk-react";

function EmployeeCard({ employeeId }: { employeeId: string }) {
  const { data: employee, isLoading, error } = useObject(
    client.objects.Employee,
    employeeId,
  );

  if (isLoading) return <Spinner />;
  if (error) return <ErrorBanner error={error} />;

  return (
    <div>
      <h2>{employee.name}</h2>
      <p>部门: {employee.department}</p>
      <p>薪资: {employee.salary.toLocaleString()}</p>
    </div>
  );
}

function EmployeeList() {
  const { data, isLoading, fetchNextPage, hasNextPage } = useObjectSet(
    client.objects.Employee
      .where({ isActive: { eq: true } })
      .orderBy("name")
      .limit(20),
  );

  return (
    <div>
      {data?.pages.map(page =>
        page.data.map(emp => (
          <EmployeeCard key={emp.employeeId} employeeId={emp.employeeId} />
        ))
      )}
      {hasNextPage && (
        <button onClick={() => fetchNextPage()}>加载更多</button>
      )}
    </div>
  );
}

#10.2 Vue 集成

TypeScript
// @onto/osdk-vue
import { useObject, useObjectSet } from "@onto/osdk-vue";

const { data: employee, loading, error } = useObject(
  () => client.objects.Employee.get(props.employeeId),
  { watch: () => props.employeeId },
);

#11. 性能优化

#11.1 请求合并

TypeScript
class RequestBatcher {
  private pending = new Map<string, Promise<unknown>>();
  private batchWindow = 10; // ms

  async get<T>(objectType: string, id: string): Promise<T> {
    const key = `${objectType}:${id}`;

    if (this.pending.has(key)) {
      return this.pending.get(key) as Promise<T>;
    }

    const promise = this.scheduleBatch<T>(objectType, id);
    this.pending.set(key, promise);
    return promise;
  }

  private async scheduleBatch<T>(
    objectType: string,
    id: string,
  ): Promise<T> {
    await new Promise(r => setTimeout(r, this.batchWindow));

    const batchIds = [...this.pending.keys()]
      .filter(k => k.startsWith(`${objectType}:`))
      .map(k => k.split(":")[1]);

    const results = await this.batchFetch(objectType, batchIds);

    for (const [batchId, result] of Object.entries(results)) {
      this.pending.delete(`${objectType}:${batchId}`);
    }

    return results[id] as T;
  }
}

#11.2 Select 字段优化

TypeScript
// 只请求需要的字段,减少传输数据量
const employees = await client.objects.Employee
  .select("name", "salary")  // 只返回 name 和 salary
  .list();

// 类型自动收窄
employees.data[0].name;       // OK: string
employees.data[0].salary;     // OK: number
employees.data[0].department; // TS Error: Property does not exist

#12. 安全与认证

#12.1 Token 管理

TypeScript
class TokenManager {
  private accessToken: string | null = null;
  private refreshToken: string | null = null;
  private expiresAt: number = 0;

  async getToken(): Promise<string> {
    if (this.accessToken && Date.now() < this.expiresAt - 60_000) {
      return this.accessToken;
    }
    return this.refresh();
  }

  private async refresh(): Promise<string> {
    const response = await fetch(`${this.baseUrl}/oauth/token`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        grant_type: "refresh_token",
        refresh_token: this.refreshToken,
        client_id: this.clientId,
      }),
    });

    const data = await response.json();
    this.accessToken = data.access_token;
    this.refreshToken = data.refresh_token;
    this.expiresAt = Date.now() + data.expires_in * 1000;

    return this.accessToken!;
  }
}

#12.2 请求签名

TypeScript
// 敏感操作的请求签名
class RequestSigner {
  sign(request: RequestInit, timestamp: number): RequestInit {
    const payload = `${request.method}:${request.url}:${timestamp}`;
    const signature = hmacSHA256(payload, this.secretKey);

    return {
      ...request,
      headers: {
        ...request.headers,
        "X-Request-Timestamp": timestamp.toString(),
        "X-Request-Signature": signature,
      },
    };
  }
}

#Key Takeaways

  1. 类型生成是 TypeScript OSDK 的核心,从 Ontology Schema 自动生成完整的 TypeScript 类型系统,实现编译期类型安全
  2. 分层架构将生成层、运行时层和传输层分离,每层可独立演进
  3. 查询 DSL 提供链式和组合式两种风格,支持过滤、排序、分页、聚合和时间序列
  4. 实时订阅通过 WebSocket 实现 ObjectSet 变更推送,支持自动重连和重新订阅
  5. 框架集成提供 React/Vue 的 hooks 封装,实现声明式数据绑定

#Next Article

下一篇我们将探讨异步 SDK 设计,了解如何构建支持 async/await 的高性能 SDK。

Tags: TypeScript OSDK 代码生成 类型安全 Ontology SDK React 前端 平台工程