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

@autolabz/z-supabase-sdk

v0.1.2

Published

Client SDK for z-supabase-service shadow sessions

Downloads

9

Readme

@autolabz/z-supabase-sdk

面向前端应用的 Supabase 工具集,聚焦自托管 z-supabase-service 的三件事:会话托管、Shadow Session 同步、Storage 限流。

功能总览

  • Supabase ClientcreateSupabaseClient 默认关闭 autoRefreshToken / persistSession,将 session 生命周期交给业务决定。
  • Shadow Session 管理syncSupabaseSession / refreshShadowSession 通过服务端 API 下发 shadow 用户的 access/refresh token,并支持内存或 Web Storage 缓存。
  • Storage 工具createSignedUploadUrl 通过服务端统一校验配额;getStorageQuota 独立查询配额;listStorageObjects 复用 shadow session 查询 resources 表;deleteStorageResource 触发服务端删除并具备幂等自愈能力。所有方法在异常时都会抛出可枚举的 StorageUploadError

1. Supabase 客户端创建策略

SDK 会在创建客户端时合并默认 auth 选项,避免 Supabase JS 自动持久化 session,从而确保 shadow token 只来自受控流程。

import { createSupabaseClient } from '@autolabz/z-supabase-sdk';

const supabase = createSupabaseClient({
  supabaseUrl: import.meta.env.VITE_SUPABASE_URL!,
  supabaseAnonKey: import.meta.env.VITE_SUPABASE_ANON_KEY!,
  clientOptions: {
    global: { headers: { 'X-App-Version': '[email protected]' } },
  },
});

如需特殊场景(例如 SSR)可继续覆写 clientOptions.auth,SDK 仅负责提供安全的默认值。

2. Shadow Session 生命周期

syncSupabaseSession 调用 /auth/sync 生成 shadow 用户并向 Supabase client 写入 access / refresh token;refreshShadowSession 命中 /session/refresh,在 token 即将过期时续期。两个方法都会在失败时抛出 ShadowSessionError,并支持自定义 fetcher、clientId 以及各类回调。

import {
  syncSupabaseSession,
  refreshShadowSession,
  WebStorageSessionStorage,
} from '@autolabz/z-supabase-sdk';

const storage = new WebStorageSessionStorage('zsupa_session');

await syncSupabaseSession({
  serviceBaseUrl: import.meta.env.VITE_ZSUPA_SERVICE_URL!,
  authToken: oauthAccessToken,
  supabaseClient: supabase,
  storage,
  clientId: 'paper-admin',
  onBeforeSync: () => startLoading(),
  onSyncSuccess: (payload) => console.log('shadow user:', payload.shadowUserId),
});

await refreshShadowSession({
  serviceBaseUrl: import.meta.env.VITE_ZSUPA_SERVICE_URL!,
  supabaseClient: supabase,
  storage,
  clientId: 'paper-admin',
});

默认提供三种存储策略:

  • MemorySessionStorage:测试或短生命周期页面;
  • WebStorageSessionStorage:使用 localStorage,支持自定义 key;
  • createDefaultStorage():在浏览器中落地到 localStorage,否则回退内存。

3. Storage 工具

所有 Storage API 都会先进行参数校验(大小、路径、分页等)再请求服务端,当配额不足 / 参数非法时抛出带有 codemetaStorageUploadError,便于将 ZS501 之类的错误码直接映射到 UI。

上传流程(Signed URL 代上传)

  1. 前端调用 createSignedUploadUrl。SDK 会校验 authTokenfileName 与文件大小后,向 z-supabase-service/storage/uploads/sign 发起请求。
  2. 服务端收到请求后,会:
    • 根据 OAuth 用户 ID 找到对应 shadow user;
    • 创建一条 resources 记录并生成 resourceId,将真实存储文件名固定为 <resourceId>(.ext),并只允许写入 <shadowUserId>/<resourceId>.ext
    • 通过 Supabase Service Role 调用 createSignedUploadUrl,把存储配额(userQuotaBytesmaxFileBytes)和允许的 bucket 统一限制住。
  3. 服务端返回包含 resourceId 的 signed URL 响应;客户端可把 resourceId 用于后续轮询处理状态,并直接对该 URL 发起 PUT,整个过程无需接触 Service Role Key。

这样即使用户在浏览器 F12 中看到请求,也只能把 signedUrl 用于当前文件上传,无法在桶内任意写入其他路径,实现“系统代用户上传”的受控链路。

资源列表(Supabase resources 表)

listStorageObjects 需要一个 shadow session 已经连线的 supabaseClient,它会直接查询 resources 表并返回规范化字段(fileName, storagePath, size, status, createdAt 等),默认按 created_at desc 排序,同时返回 counthasMore 方便分页。如果当前 Supabase Client 里没有 session,方法会尝试从提供的 storage(或默认的 createDefaultStorage())恢复 shadow access/refresh token,确保真正发送的 AuthorizationBearer eyJ...;若缓存缺失则抛出 SHADOW_SESSION_MISSING 提醒先执行 syncSupabaseSession

const listed = await listStorageObjects({
  supabaseClient,
  shadowUserId: session.shadowUserId,
  storage, // 传入与 sync 阶段相同的 storage,便于页面刷新后自动恢复 JWT
  limit: 20,
  orderBy: { column: 'created_at', order: 'desc' },
});

listed.objects.forEach((resource) => {
  console.log(resource.fileName, resource.size, resource.storagePath);
});

配额查询(Quota API)

当页面需要展示“已用/剩余空间”而不想触发签名流程时,可直接调用 getStorageQuota。SDK 仅需 serviceBaseUrlauthToken,会向 /storage/quota 发送 GET 请求,并返回 shadowUserId, usedBytes, remainingBytes, quotaBytes, maxFileBytes

import { getStorageQuota } from '@autolabz/z-supabase-sdk';

const quota = await getStorageQuota({
  serviceBaseUrl: import.meta.env.VITE_ZSUPA_SERVICE_URL!,
  authToken: shadowAccessToken,
  clientId: 'paper-admin',
});

console.log(`已用 ${(quota.usedBytes / 1024 / 1024).toFixed(1)} MB / 总额度 ${(quota.quotaBytes / 1024 / 1024).toFixed(1)} MB`);

资源删除(Delete API)

deleteStorageResource 会向 /storage/resources/:resourceId 发送 DELETE 请求。服务端会验证影子账号归属、删除 Supabase Storage 对象,并在对象缺失时自动忽略错误、仅清理数据库记录,实现幂等、“自愈”的删除逻辑。SDK 只需提供 authToken, serviceBaseUrl, resourceId

import { deleteStorageResource } from '@autolabz/z-supabase-sdk';

await deleteStorageResource({
  serviceBaseUrl: import.meta.env.VITE_ZSUPA_SERVICE_URL!,
  authToken: shadowAccessToken,
  resourceId: selectedResourceId,
  clientId: 'paper-admin',
});

如返回 success: true 即表示顺利清理,无论 Supabase Storage 中对象是否已经不存在。

查看 / 下载方式

  • 生成可访问链接:拿到资源记录后,可用 shadow session 驱动的 Supabase JS 客户端直接调用 supabase.storage.from(bucket).createSignedUrl(resource.storagePath!, ttl)download(resource.storagePath!) 获取一次性访问链接 / Blob 数据。RLS 只允许访问 <shadowUserId>/**,因此天然满足“只看自己的文件”。
  • 对外分享(可选):需要向第三方暴露文件时,可在自建后端使用 Service Role Key 生成一次性签名,再下发给终端;SDK 专注在“用户查看自己的资源”场景,推荐优先走 shadow session。
import { createSignedUploadUrl, getStorageQuota, listStorageObjects } from '@autolabz/z-supabase-sdk';

const signed = await createSignedUploadUrl({
  serviceBaseUrl: import.meta.env.VITE_ZSUPA_SERVICE_URL!,
  authToken: supabase.auth.session()?.access_token ?? '',
  fileName: file.name,
  bytes: file.size,
  mimeType: file.type,
  clientId: 'paper-admin',
});

console.log('resourceId for follow-up polling:', signed.resourceId);

await fetch(signed.uploadUrl, { method: 'PUT', body: file, headers: signed.headers });

const quota = await getStorageQuota({
  serviceBaseUrl: import.meta.env.VITE_ZSUPA_SERVICE_URL!,
  authToken: supabase.auth.session()?.access_token ?? '',
});

const listed = await listStorageObjects({
  supabaseClient,
  shadowUserId: session.shadowUserId,
  storage,
  limit: 20,
});

安装

npm install @autolabz/z-supabase-sdk

快速上手

import { createSupabaseClient, syncSupabaseSession, createSignedUploadUrl } from '@autolabz/z-supabase-sdk';

const supabase = createSupabaseClient({
  supabaseUrl: import.meta.env.VITE_SUPABASE_URL!,
  supabaseAnonKey: import.meta.env.VITE_SUPABASE_ANON_KEY!,
});

await syncSupabaseSession({
  serviceBaseUrl: import.meta.env.VITE_ZSUPA_SERVICE_URL!,
  authToken: oauthAccessToken,
  supabaseClient: supabase,
  clientId: 'paper-admin',
});

const signed = await createSignedUploadUrl({
  serviceBaseUrl: import.meta.env.VITE_ZSUPA_SERVICE_URL!,
  authToken: supabase.auth.session()?.access_token ?? '',
  fileName: file.name,
  bytes: file.size,
  mimeType: file.type,
});

await fetch(signed.uploadUrl, {
  method: 'PUT',
  body: file,
  headers: signed.headers,
});

API 速览

| 方法 / 类型 | 描述 | | --- | --- | | createSupabaseClient | 生成禁用自动 refresh/persist 的 Supabase client。 | | syncSupabaseSession | 请求 /auth/sync,拉取 shadow session 并写入 Supabase client。 | | refreshShadowSession | 请求 /session/refresh,在 access token 即将过期时续期。 | | createSignedUploadUrl | 请求 /storage/uploads/sign,校验配额并拿到 PUT 签名链接。 | | getStorageQuota | 请求 /storage/quota,查询影子用户的已用/剩余额度。 | | deleteStorageResource | 请求 /storage/resources/:resourceId,触发服务端删除并自愈缺失对象。 | | listStorageObjects | 使用 shadow session 的 Supabase client 查询 resources 表并返回分页结果。 | | MemorySessionStorage / WebStorageSessionStorage | shadow session 的可插拔持久化策略。 | | ShadowSessionError / StorageUploadError | 携带 codemeta 的错误类型,便于 UI 映射。 |

createSignedUploadUrl 会在参数阶段阻止 authToken 缺失、bytes <= 0 等问题;服务端错误会透出 StorageUploadError,可直接针对 ZS501(容量用尽)、ZS401(鉴权失败)等错误码给出提示。