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

sy-page-sdk

v0.1.3

Published

Custom code page SDK for sy lowcode platform

Downloads

524

Readme

sy-page-sdk

面向 custom_code_page 的前端 SDK。当前版本以“统一 transport + 业务模块 API”为中心,不再推荐页面代码直接拼平台 HTTP 路径、手写 query 字符串,或依赖旧的 form.queryList 心智。

规范源码以 src 为准,README 示例对应的可类型检查版本见 readme.examples.tsx

开发前先读

page-sdk 负责帮页面屏蔽平台 query 序列化和 envelope 差异,但它不是平台语义本身。

在写页面前,建议先按下面顺序阅读:

  1. ../../docs/platform-form-api.md
  2. ../../docs/field-value-shapes.md
  3. ../../docs/simple-search.md
  4. ../../docs/advanced-search.md
  5. 本 README

这样可以避免几个高频错误:

  • 猜错 SelectField / MultiSelectField / 人员字段的值形态
  • 手写 searchFieldJsonfiltersorder 字符串
  • result.datadata.dataresponse.data 混用
  • formInstId 错当成 id

模块地图

  • sdk.form
    • 表单详情、增删改、变更记录、simple search、advanced search、导入导出、数据管理页配置
  • sdk.user
    • 用户创建、更新、删除、查询、当前用户、按用户名读取、检索、按部门查询
  • sdk.role
    • 角色 CRUD、角色用户列表、角色分配、切换当前平台或应用角色
  • sdk.permission.formGroup
    • 表单权限组 CRUD、列表、查看态字段权限读取
  • sdk.permission.pageGroup
    • 页面权限组 CRUD、列表、当前用户菜单权限读取
  • sdk.permission.api
    • API 权限 CRUD、列表、分配、按角色读取、反查角色
  • sdk.permission.ui
    • UI 权限 CRUD、列表、分配、读取当前平台或应用权限
  • sdk.process
    • 流程实例读取、终止、审批、回调触发
  • sdk.dataSource.run
    • 页面便捷层。会读取 context.page.dataSources,再路由到 sdk.form.*
  • sdk.request
    • JSON 请求底层接口
  • sdk.download
    • 二进制下载底层接口

React 入口

保留的 React API:

  • createReactPage
  • PageProvider
  • usePageSdk
  • usePageContext
  • useNavigation
  • useMessage
  • useModal
  • usePageProps
  • usePageRoute
  • useDataSource
  • useCurrentUser
  • useFormViewPermissions

useDataSource 仍然可用,但现在只是 sdk.dataSource.run 的轻量封装,不再是 SDK 的核心模型。对于已经声明了 page.dataSources 的读页面,它仍然是默认首选,因为它天然带有 loading / error / refresh

最小示例

import type { PageListResult } from "sy-page-sdk"
import { createReactPage, useDataSource, usePageSdk } from "sy-page-sdk/react"

type CustomerRecord = {
  formInstanceId: string
  customerName: string
}

function CustomerPage() {
  const sdk = usePageSdk()
  const { data, loading, refresh } = useDataSource<
    PageListResult<CustomerRecord>
  >("customerList", {
    immediate: true,
  })

  const handleOpenDetail = async () => {
    const firstCustomerId = data?.data[0]?.formInstanceId
    if (!firstCustomerId) return

    await sdk.form.getDetail({
      formUuid: "FORM_CUSTOMER",
      formInstanceId: firstCustomerId,
    })
  }

  return (
    <section>
      <button type="button" onClick={() => refresh()}>
        刷新
      </button>
      <button type="button" onClick={() => void handleOpenDetail()}>
        读取详情
      </button>
      <div>{loading ? "loading" : `count: ${data?.totalCount || 0}`}</div>
    </section>
  )
}

export const { mount, unmount, update } = createReactPage(CustomerPage)

Transport 模型

页面正常情况下优先使用业务模块;只有在平台新增了 README 尚未封装的接口时,才直接用底层 transport。

const profile = await sdk.request<{ id: string; name: string }>({
  path: "/user/current",
  method: "get",
})

const exportFile = await sdk.download({
  path: `/${sdk.context.app.appType}/v1/form/advancedExport.xlsx`,
  method: "get",
  query: {
    formUuid: "FORM_CUSTOMER",
    exportAll: "y",
  },
})

sdk.request / sdk.download 都会:

  • 自动序列化 query
  • 统一兼容平台的 result 型和 data + message 型 envelope
  • 对下载接口补齐 blobfileNamecontentType
  • 复用宿主运行时的鉴权、报错和 request 客户端能力

表单查询

推荐默认使用 advancedSearch,页面代码传结构化对象,SDK 负责把 filtersordersearchFieldJson 等参数序列化为平台要求的格式。

如果你还不确定某个组件的真实查询值结构,先看:

import type { SearchGroup, SearchSortItem } from "sy-page-sdk"

const filters: SearchGroup = {
  logic: "AND",
  rules: [
    {
      key: "status",
      componentName: "SelectField",
      operator: "EQ",
      value: "active",
    },
    {
      key: "detailList",
      componentName: "SubFormField",
      operator: "EXISTS",
      value: {
        logic: "AND",
        rules: [
          {
            key: "sku",
            componentName: "TextField",
            operator: "CONTAINS",
            value: "license",
          },
        ],
      },
    },
  ],
  conditions: [
    {
      logic: "OR",
      rules: [
        {
          key: "level",
          componentName: "SelectField",
          operator: "EQ",
          value: "A",
        },
        {
          key: "owner",
          componentName: "TextField",
          operator: "EQ",
          value: "Alice",
        },
      ],
    },
  ],
}

const order: SearchSortItem[] = [
  { id: "modifiedTime", isAsc: "n" },
  { id: "customerName", isAsc: "y" },
]

const list = await sdk.form.advancedSearch({
  formUuid: "FORM_CUSTOMER",
  currentPage: 1,
  pageSize: 20,
  filters,
  order,
  instanceStatus: "running",
})

simple search 和 ID 搜索仍然保留:

const idList = await sdk.form.searchIds<string>({
  formUuid: "FORM_CUSTOMER",
  currentPage: 1,
  pageSize: 50,
  search: {
    logic: "AND",
    rules: [
      {
        key: "customerName",
        componentName: "TextField",
        operator: "CONTAINS",
        value: "星云",
      },
    ],
  },
  dynamicOrder: {
    id: "modifiedTime",
    isAsc: "n",
  },
})

列表查询成功后,页面通常这样读取结果:

const rows = list.result?.data || []
const totalCount = list.result?.totalCount || 0
const currentPage = list.result?.currentPage || 1

如果走 useDataSource("customerList"),则默认直接读取:

const { data } = useDataSource<PageListResult<CustomerRecord>>("customerList")

const rows = data?.data || []

用户、角色与权限

import { useCurrentUser, useFormViewPermissions } from "sy-page-sdk/react"

const { user, isGuest, isInternalUser } = useCurrentUser()
const viewPermissions = useFormViewPermissions("FORM_CUSTOMER")

const canEdit = viewPermissions.can("edit")
const canDelete = viewPermissions.can("delete")
const ownerPermission = viewPermissions.getFieldPermission("owner")

const users = await sdk.user.search("alice")

const role = await sdk.role.create({
  name: "客户经理",
  code: "customer-manager",
  scope: "app",
})

if (users.result?.[0]?.id && role.result?.id) {
  await sdk.role.assignRoles({
    userId: users.result[0].id,
    roleIds: [role.result.id],
  })

  await sdk.permission.pageGroup.getUserMenuPermissions(sdk.context.app.appType)
}

const formGroup = await sdk.permission.formGroup.create({
  appType: sdk.context.app.appType,
  formUuid: "FORM_CUSTOMER",
  name: "客户表单查看组",
  type: "view",
  roles: [role.result?.id || ""],
})

const pageGroup = await sdk.permission.pageGroup.list({
  appType: sdk.context.app.appType,
})

const viewFieldPermissions =
  await sdk.permission.formGroup.getViewFieldPermissions({
    formUuid: "FORM_CUSTOMER",
  })

const viewPermissionSummary =
  await sdk.permission.formGroup.getViewPermissionSummary({
    formUuid: "FORM_CUSTOMER",
  })

const myPagePermissions = await sdk.permission.pageGroup.getUserMenuPermissions(
  sdk.context.app.appType,
)

公开页会通过平台创建或复用游客账号。页面不要自行推导游客规则,优先读取 PageContext.user.isGuest / PageContext.user.userType,并通过表单查看态权限汇总控制编辑、删除、变更记录和流程入口。

消息通知

页面即时触发通知时,可以使用按通知类型发送的 API。审批节点、定时提醒、表单事件通知仍应优先由 workflow / automation 承接。

await sdk.notification.sendByType({
  notificationType: "customer_follow_up",
  recipientId: user.id,
  payload: {
    customerName: "杭州星云科技",
  },
  channels: ["inapp"],
})

await sdk.notification.batchSendByType({
  notificationType: "customer_follow_up",
  recipients: [
    {
      recipientId: "user-1",
      payload: {
        customerName: "杭州星云科技",
      },
    },
  ],
})

不要在页面里硬编码钉钉、第三方待办地址、密钥或模板编号;这些由平台消息配置决定。

流程能力

const instance = await sdk.process.getInstance({
  instanceId: "PROC-2026-0001",
})

await sdk.process.approveTask({
  instanceId: "PROC-2026-0001",
  formUuid: "FORM_CUSTOMER",
  action: "approved",
  comments: "审批通过",
})

await sdk.process.terminateInstance({
  processInstanceId: "PROC-2026-0001",
  reason: "测试结束",
})

await sdk.process.triggerCallbackTask({
  taskId: "TASK-CALLBACK-001",
  payload: {
    source: "custom-page",
  },
})

数据管理页配置

const current = await sdk.form.getDataManagementConfig({
  formUuid: "FORM_CUSTOMER",
})

await sdk.form.saveDataManagementConfig({
  formUuid: "FORM_CUSTOMER",
  config: {
    ...current.result,
    showFields: ["customerName", "owner", "status", "level"],
    sort: [{ id: "modifiedTime", isAsc: "n" }],
  },
})

文件流

SDK 不会自动触发浏览器下载,只会返回统一的二进制对象,由页面决定何时下载或预览。

const preview = await sdk.form.importPreview({
  formUuid: "FORM_CUSTOMER",
  fileBase64,
  fileName: "customers.xlsx",
})

const exportFile = await sdk.form.advancedExport({
  formUuid: "FORM_CUSTOMER",
  filters,
  order,
  exportAll: "y",
})

const failedFile = await sdk.form.downloadImportFailed({
  recordId: "IMP-2026-0001",
})

const exportUrl = URL.createObjectURL(exportFile.blob)

PageBinaryResponse 结构:

type PageBinaryResponse = {
  blob: Blob
  fileName?: string
  contentType?: string
  headers?: Record<string, string | undefined>
  raw?: unknown
}

什么时候用 useDataSource

适合:

  • 页面已经有 page.dataSources
  • 页面主要是列表、报表、dashboard 这类读场景
  • 只是想复用页面配置里的数据源 key
  • 想要拿到现成的 loading / error / refresh

不适合:

  • 用户、角色、权限、流程这类业务模块
  • 需要导入导出、数据管理配置、文件下载
  • 你已经明确知道要调哪个平台能力

这类场景直接调用 usePageSdk() 后使用 sdk.form / sdk.user / sdk.role / sdk.permission / sdk.process 会更清晰。

无论使用 useDataSource 还是 usePageSdk(),正式页面都应展示真实的 empty / error state,不要在运行时回退样例数据。