@ai- matrx/ 数据
评论家AI Matrx 数据层: /db 等级——来之不易的Supabase原始人
每个 Matrx 客户端在平台数据库中使用 – /net 一级——
恢复力三重奏,每一次外出呼叫和事件流的行驶——以及 /files 等级 -
马特克斯文件通信客户端。 每一个原始的存在,因为一个真正的生产
事件伪造了它;文件头带故事.
npm install @ai-matrx/data
| 子路径 | 它携带着什么 |
|---|---|
@ai-matrx/data | 核心:结构PostgREST类型, pgErrorToError外加 /net trio再出口(它骑着核心). |
@ai-matrx/data/db | Supabase道:乐观的通用语,完整列表读取,JSONB合并,会话-复读,auth cookie,实时卫生. |
@ai-matrx/api/net | 网-抗力三重奏:有弹性接取,再试/回放,溪流摊位监督,结构化的网易词汇. |
@ai-matrx/data/next | Next.js捆绑:每个Supabase客户端的一款下个应用程序构建的一款厂房——浏览器,服务器组件,路由处理器,代理/中件,OAuth调回,外加整个中件认证-cookie通过. |
@ai-matrx/data/react | 反应约束 : useAsyncData, (中文(简体) ), useRealtimeChannel 编号 : useRealtimeQuery, (中文(简体) ), useAsyncAction。盖章 "use client"。 。 。 . |
@ai-matrx/data/access | 出入口的纯节:"你无法打开这个"词汇,the access_denied_context 解析器客户端,请求 / 决定 / 撤回流量, 每个人隐藏, 诚实的复制帮助 。 未盖章 "use client"; 注入的应用程序输入 configureAccess。 。 。 . |
@ai-matrx/data/db
| 导出 | 它会做什么 |
|---|---|
guardedUpdate | 乐观的货币 比较和扫描的峡谷 version 栏。 诚实地分类"0行更新": conflict (使用当前行,以便用户界面提供合并/更新) vs not_found。召回形状的——它不需要知道您的计划或客户端. |
readAllRows 编号 : tryReadAllRows | 读取你打算视为完整的名单的方法 PostgREST悄悄地封顶了回复(HTTP 206看起来像是成功);一个用于存在/diff决定的截取列表是一个自信地错误的答案. 服务器宣布的总数和 掷出 (单位:千美元)IncompleteReadError而不是退回一份可以证明是部分清单. readAllRowsRest 编号 : tryReadAllRowsRest 是生的吗fetch 双胞胎是没有客户端的剧本. |
mergeJsonColumn | 合并到一行的 JSONB 列而不丢失并行的作者密钥: 作曲 guardedUpdate 与有界再读再出再试. 返回打入状态, 绝不为正常结果丢出 . |
createSessionRetry | 恢复一个值得重试的失败:一个请求到达了PostgREST,在实际存在会话时没有用户JWT(在飞行中重新出现, cookie switch). 在重新解决会议之后,确切地重复一次。 其他一切皆从所出而来. |
createOAuthRefreshFetch | 保留 Auth-js 的正常存储,锁定,刷新生命周期和事件,但路径已验证的允许列表 OAuth-客户端会话刷新到 /auth/v1/oauth/token 及其JWT client_id通过Supabase的普通刷新端点继续正常会话. |
createAuthCookie | 在您的顶端的每个子域上登录一次: 重新命名( O)sb-matrx-auth),),全域认证 cookie只在真实的顶级主机上发布——本地主机和预览只获得主机,因为浏览器默默拒绝不匹配的 Domain。 。 。 . |
createActiveOrgCookie | 一个浏览器内存 一个人正在行动, 由顶端的每个 Matrx 表面共享(matrx-active-org, (中文(简体) ), Domain=.aimatrx.com: 身份按键(<userId>:<orgId>),全域仅在上市顶上,只存在于别处的主机. 存储 Cononical 命令的存储选取行; 永远不会决定 . |
uniqueChannelTopic | 实时频道卫生:每个站台独有话题,因此回放19个双入声效果和快刷音永远无法与仍联通的频道相撞. |
门——与数据库交谈的一种方式
每个应用程序通过门对数据库进行比标 本地生成 从
一个数据库描述。 本包件内的描述船(Adescription/一个被咬了
,所以不需要数据库或数据库访问.
# in your app (package.json: "db:generate": "matrx-data generate --out src/db/generated --schemas workbench,chat")
npx matrx-data generate --out src/db/generated --schemas workbench # one file per schema
npx matrx-data generate --out src/db/generated --schemas workbench --check # CI: fail when stale
npx matrx-data generate --out src/db/generated --flags-index # + per-schema flags for tableByName
# fresher than this package version? With the direct-DB variables (SUPABASE_MATRIX_*):
npx matrx-data emit --out db-contract && npx matrx-data generate --from db-contract --out src/db/generated
import { createDb, listAll, read, insert, update, remove, call } from "@ai-matrx/data/db";
import { noteFolders, notes, noteFolderGetOrCreate } from "./db/generated/workbench";
const db = createDb({ client: supabase, organization: () => activeOrgId }); // wraps YOUR client
const pinned = await listAll(db, notes, (q) => q.eq("is_pinned", true)); // complete; typed filter; live rows
const row = await read(db, notes, id, { columns: ["id", "label"] }); // typed to those columns
await insert(db, notes, { folder_id, label }); // child: the folder's organization, never the active one
await update(db, notes, id, { label }, { expectedVersion: row.version }); // or { overwrite: true }
await remove(db, notes, id, { expectedVersion: v }); // soft delete; archive-only tables refuse
await call(db, noteFolderGetOrCreate, { p_name: "Clients" });
// @ai-matrx/api: pass db.credentials as its `credentials` option — one sign-in feeds both lanes
规则来自描述(活桌注册),从不使用呼叫网站:
访问阶梯; 读取从不由活动组织过滤并排除软删除行, 除非
includeDeleted· ; listAll 由完整的主密钥完成并订购; 版本上的每个字
表格是看守的; 门沿登记类型; createServiceDb 只有服务器.
@ai-matrx/api/net
| 导出 | 它会做什么 |
|---|---|
resilientFetch | fetch 有两个阶段的超时——连接(默认为15s,请求启动为-)和总计(默认为120s, null ——加一个编组 AbortController 回到呼叫者 取消身体读取。 永远不要预设障碍 navigator.onLine (这面旗帜是一种暗示;一个真正的停产告诉我们它说谎)——只有当一个真正的网络故障证实了这一点时,才报告下线. |
withRetry | 带有焦急状态的指向反转 专有 仅限操作。 默认的上游重试 准确的误差分类标记 retryable (连接超时,网络故障,HTTP 408/429/5xx). 荣誉 AbortSignal 中后卫; 绝不包装用户撰写的提交. |
monitorStream | 环绕一个可执行的同步事件,同时提供两个保证:心跳截止(事件之间的默认为30)和绝对寿命上限(默认为10min). 被卡住的溪流抛出 HeartbeatTimeoutError编号 :TotalTimeoutError - 并终止您的 AbortController - 而不是永远等待. |
NetError 家属 | ConnectTimeoutError, (中文(简体) ), TotalTimeoutError, (中文(简体) ), HeartbeatTimeoutError, (中文(简体) ), NetworkError, (中文(简体) ), HttpError, (中文(简体) ), AbortedError, (中文(简体) ), OfflineError - 每一个失败都会决定一个阶级 code 诚实的 retryable 旗,所以调用者在类型上的分支,从不进行字符串匹配. 帮助者 : isNetError, (中文(简体) ), toNetError, (中文(简体) ), isTransportFailure (这是运输器死亡,还是服务器回答?), extractErrorMessage。 。 。 . |
接线:没有注射. 该等级具有零依赖性和零接合性——
fetch 和计时器来自环境(节点20+,浏览器,React Industrial/Hermes),
它从不触及Supabase, org上下文, 或API道。 自己传 AbortSignal
改为 resilientFetch编号 :withRetry 还有你自己的 AbortController 改为 monitorStream 这样
监视超时也会取消基本请求 。 这里的一切也是
从根再出口 @ai-matrx/data 进入点.
@ai-matrx/data/next
Next.js的应用程序有五个地方必须构建一个Supabase客户端,每个地方 它们必须通过 SAME 认证- cookie 选项, 否则它不能看到其他的会话 写作。 一个工厂拥有所有五个。 宿主只注射身份识别.
// One module in your app — everything else imports from here.
import { createNextSupabase } from "@ai-matrx/data/next";
import type { Database } from "@/types/database.types";
export const supabaseNext = createNextSupabase<Database>({
// STATIC member accesses: Next only inlines NEXT_PUBLIC_* for those.
supabaseUrl: process.env.NEXT_PUBLIC_SUPABASE_URL,
publishableKey: process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY,
apexDomain: "example.com",
cookieName: "sb-example-auth-v2",
legacyCookieName: "sb-example-auth", // optional: migrate + clear an old key
});
| 门 | 打电话 |
|---|---|
| 客户部分 | supabaseNext.browserClient() - 一个单子在a globalThis 插槽 |
| 服务器组件/ 动作 | supabaseNext.serverClient({ cookieStore: await cookies(), host }) |
| 路由处理器 | supabaseNext.routeClient({ requestCookies: request.cookies, setCookie, host }) |
| 代理/ 中间软件 | await supabaseNext.middlewareSession({ host, requestCookies, createResponse, createRedirect }) |
middlewareSession 是整个认证通过在一个呼叫和返回中
{ user, client, response, redirect(url), splitCookieJar, ambiguousAuthCookies, authUnavailable }。它拥有: 遗产- 关键迁移( 仅缺席, 所以现场会议永远不会
覆盖),"没有什么可以运行在 createServerClient 和身份决心"
规则是结构化的,持续在当前键下迁移的会话,清除
此主机发布范围取代密钥, 无存储响应标题
带饼干的响应, 饼干结转到方向。 丢弃其中任何一种
随机对用户进行记录——每个用户都是这样找到的.
身份决定是 getClaims()永远 getUser(),它是被弹出.
getUser() 向 Auth 服务器发送每个 JWT 的请求; getClaims() 核查
使用 WebCrypto 本地的、通常没有网络的 JWKS 访问令牌
和信任一样,因为签名被检查。 它需要一个 不对称
在项目上签名密钥(ECC/RSA);带有对称密钥可降解为
getUser()- 每一个呼叫的形状请求,仍然正确,但是没有节省。 一个2.5s
身份预算——每个请求一个截止日期 /auth/v1/user,一个符号刷新和
JWKS 抓取, 之后 Auth- js 以不可重试的 408 来回答, 所以它的 30 s 刷新
备份不能运行 (0. 18. 0) —— 意味着一个停滞的认证服务器成本
在Vercel上
16个强制通过 进入一个有硬任务盖的节点Lambda, 无限制版本返回
504 MIDDLEWARE_INVOCATION_TIMEOUT 对真实的人.
🚨 authUnavailable 是消费者的义务. 意思是当局不可能
完成,未"签出": user 实值 null但是没有写 auth cookie,
过期或愈合,被替换的密钥没有清除. 在治疗前读一下
user === null 已签出 ——宿主回弹 /login 无效用户必须跳过
或一个2.5s的网络闪烁 显示人们离开会场.
一个呼叫站点需要 created_at, (中文(简体) ), identities, (中文(简体) ), last_sign_in_at, (中文(简体) ), factors 或者说
*_confirmed_at 必须保留 getUser() 其他地方:JWT没有携带任何武器.
全额合同: common-docs/systems/platform/auth/proxy-identity/FEATURE.md。 。 。 .
没有 next/* 导入软件包中的活性. cookies() 财务报告和审定财务报表 NextResponse 到达通过
结构接合createResponse: () => NextResponse.next({ request })),这样 /next
在"Next"之外建立和测试,从不将框架拖入另一个消费者的图表.
身份丢弃 MissingSupabaseIdentityError 命名变量,而不是
密码 undefined.trim() 类型错误 process.env.X! 产自Supabase内部.
@ai-matrx/data/react
三个钩子,每个钩子都是由MEASURED的重复消费者所建造的——而不是数据比对框架. 这里没有缓存、 商店和查询客户端 : 这些编曲 下级 您的 Redux 或 密码 重复查询,他们不会与之竞争。 他们拥有的是每份手卷拷贝 错了.
| 钩子 | 它拥有的 |
|---|---|
useAsyncData(load, deps, opts) | 取消( 实数) AbortSignal 到达您的加载器, stale- response 命令, 诚实的错误转换( PostgREST 错误是一个普通对象 ) - String(err) 使“[对象对象]”仅重试 Transport失败, keepPreviousData 因此密钥更改从不给屏幕空白, 和一个沉默的中止,因为它不是一个失败. |
useRealtimeChannel(client, topic, bindings, opts) | 独一无二的每架主题,与指数回放和稳定性重置重联,一个 onReconnect 追赶门(插座倒下时错过的事件是GONE),诚实 connecting/connected/reconnecting/disconnected 状态,最新召回的参考, 保证拆解. |
useRealtimeQuery({ client, topic, watch, load, deps }, opts) | 两者组成,有实时事件 COALESCED 所以一个批量的写法是一读,和一个再读的接线重新连接. |
useAsyncAction(action, opts) | 双投递 REFUSAL( 一个已禁用按钮不是警卫), 未挂载安全状态, 诚实错误以及歧视 { ok: true, value } | { ok: false, error } 结果是没有被吞噬。 写作永远不会被重审——重播的呈文就是如何获得两张发票. |
usePaginatedData({ queryKey, initialCursor, loadPage, getRowId }) | 一次用一个请求进行光标积累,身份遮掩,中止/存储-结果处理,明确重试,在位行标识更新,并大声重复拒绝. 它没有缓存和表依赖. |
const { data, loading, error, refresh, realtimeStatus } = useRealtimeQuery({
client: supabaseNext.browserClient(),
topic: `jobs:${fileId}`,
watch: [{ schema: "docproc", table: "jobs", filter: `file_id=eq.${fileId}` }],
load: ({ signal }) => listJobsForFile(fileId, signal),
deps: [fileId],
}, { enabled: fileId !== null });
Supabase客户端类型是结构化的——包从不导入
@supabase/supabase-js。一个真正的客户端通过形状满足它,有一个编译时间
在包件本身的测试中证明这一点(在测试过程中,包件的签名太窄)
软件包的构建,这正是一个主机否则会撞到的失败.
@ai-matrx/data/knobs, (中文(简体) ), /app-config, (中文(简体) ), /config-attestation, (中文(简体) ), /config-types
配置没有代码默认值, 每个读数都计算在内 :
import { createKnobStore, defineKnobFeature, knob, parse } from "@ai-matrx/data/knobs";
import { createAppConfigReader } from "@ai-matrx/data/app-config";
import { createConfigReadRecorder } from "@ai-matrx/data/config-attestation";
const CHAT = defineKnobFeature("desktop.confidential_chat", {
recentlyDeletedDays: knob("recently_deleted_days", parse.int(0, 3650)),
});
const chat = createKnobStore({ feature: CHAT, client, address, bundled, cache, log, name: "confidential_chat", recorder });
chat.load();
await chat.refresh();
chat.get("recentlyDeletedDays"); // live DB → last-seen cache → build snapshot; throws KnobUnavailableError if none
createAppConfigReader 读取应用程序 public.app_config 同样的行( PostgREST 为
Accept-Profile: public然后,与携带者一起做爱. forDisplay() 其中一个是冷冻的拷贝
从不计数. createConfigReadRecorder 批次读入间隔桶
platform.config_reads_report· ; buildConfigManifest 从输入的登记册中建立释放表.
@ai-matrx/data/access
由网站出入口功能移动. 纯( 无反应 ) : fetchAccessDeniedContext, (中文(简体) ), deriveStatus, (中文(简体) ),
classifyDataError, (中文(简体) ), readServerRefusal 编号 : readTypedRefusal, (中文(简体) ), createAccessRequest 编号 : createDeleteRequest 编号 :
createSettingAccessRequest 编号 : decideAccessRequest 编号 : withdrawAccessRequest 编号 : listAccessRequests, (中文(简体) ),
fetchRemovalRights 编号 : hideForMe 编号 : unhideForMe 编号 : listHiddenItems,则 requestAccess 路由助手,
actionAuthority, (中文(简体) ), archivedWords, (中文(简体) ), decideServerReadRecheck, (中文(简体) ), isGovernedActionDenial类型
(单位:千美元)AccessDeniedContext, (中文(简体) ), AccessGateStatus(中文(简体) ).
这个 configureAccess 合同. 软件包没有从应用程序中进口任何东西。 打电话
configureAccess({ getClient, ... }) 启动时一次; 之前使用的任何服务都会丢出一个错误命名
configureAccess域(除 getClient 具有可选性,并且只根据使用它们的特性需要:
| 外地 | 用于 |
|---|---|
getClient() | 主机Supabase客户端(需要). |
fetchWithOrganization | 矿石运输 fetch,用于 CMS 已解析的符号(/api/cms/access-context) (中文(简体) ). |
ensureOrgId | 解决org id或提高宿主的Org门(通过回放消费者). |
sendDirectActionMessage | 主机的DM原始;没有它的要求仍然文件, delivered 报告 0. |
entityTokens | CMS 访问门令牌 : isCmsAccessGateToken, (中文(简体) ), cmsAccessGateLabel,站点动作/关键帮助. |
platformOrgIds | { systemOrganizationId };默认为平台的系统 org. |
actionRegistries, (中文(简体) ), extraRequestHandlers | 反应部分读取的不透明主机注册/处理器 . |
resetAccessEnv() 清除注射(测试)。 反应屏幕在 @ai-matrx/sharing/access/react (数据没有UI).
@ai-matrx/data (核心数)
结构类型(PostgrestLikeError, (中文(简体) ), MaybeSingleResponse, (中文(简体) ), Json和
pgErrorToError - PostgREST 错误是普通对象,不是 Errors; 此折叠为
描述性真实错误。 这个 /net 三重奏也在这里再出口.
设计
- 零运行时间依赖关系. 所有Supabase打字都是结构性的——一个真正的supabase-js 客户端满足了形状;没有导入任何形状。 工作在节点20+,浏览器, 和反应土著/赫姆斯.
- 注射主机,有观点的行为. 价值和客户端通过参数输入
(单位:千美元)
apexDomain, (中文(简体) ),auth• 行为——CAS合同,拒绝部分归还 列表,单因重试——是故意的不可配置的. 意见是 产品. - 双载具. 无害环境管理和共同JS条件与相应的申报,证明: 每一次放出都装满了焦油丸.
麻省理工学院