Loading...
Loading...
Compare original and translation side by side
anyany@typescript-eslint/no-explicit-anyany@typescript-eslint/no-explicit-anyany| Type | Use Case | Location |
|---|---|---|
| Local | Private modules within your app | |
| Packaged | Published NPM packages | |
| Hybrid | Shared internal packages | |
| 类型 | 适用场景 | 位置 |
|---|---|---|
| 本地组件 | 应用内部的私有模块 | |
| 打包组件 | 发布为NPM包的组件 | |
| 混合组件 | 内部共享的包 | |
my-component/
├── package.json # NPM package config
├── src/
│ └── component/
│ ├── convex.config.ts # Component definition
│ ├── schema.ts # Component schema
│ ├── public.ts # Public API functions
│ ├── internal.ts # Internal functions
│ └── _generated/ # Generated types (gitignored)
└── src/
└── client/
└── index.ts # Client-side wrapper classmy-component/
├── package.json # NPM包配置
├── src/
│ └── component/
│ ├── convex.config.ts # 组件定义文件
│ ├── schema.ts # 组件Schema
│ ├── public.ts # 公共API函数
│ ├── internal.ts # 内部函数
│ └── _generated/ # 生成的类型文件(已加入git忽略)
└── src/
└── client/
└── index.ts # 客户端封装类// src/component/convex.config.ts
import { defineComponent } from "convex/server";
export default defineComponent("myComponent");// src/component/convex.config.ts
import { defineComponent } from "convex/server";
export default defineComponent("myComponent");// src/component/schema.ts
import { defineSchema, defineTable } from "convex/server";
import { v } from "convex/values";
export default defineSchema({
items: defineTable({
key: v.string(),
value: v.string(),
expiresAt: v.optional(v.number()),
}).index("by_key", ["key"]),
});// src/component/schema.ts
import { defineSchema, defineTable } from "convex/server";
import { v } from "convex/values";
export default defineSchema({
items: defineTable({
key: v.string(),
value: v.string(),
expiresAt: v.optional(v.number()),
}).index("by_key", ["key"]),
});// src/component/public.ts
import { mutation, query } from "./_generated/server";
import { v } from "convex/values";
export const set = mutation({
args: {
key: v.string(),
value: v.string(),
ttl: v.optional(v.number()),
},
returns: v.null(),
handler: async (ctx, args) => {
const existing = await ctx.db
.query("items")
.withIndex("by_key", (q) => q.eq("key", args.key))
.unique();
const data = {
key: args.key,
value: args.value,
expiresAt: args.ttl ? Date.now() + args.ttl : undefined,
};
if (existing) {
await ctx.db.replace(existing._id, data);
} else {
await ctx.db.insert("items", data);
}
return null;
},
});
export const get = query({
args: { key: v.string() },
returns: v.union(v.string(), v.null()),
handler: async (ctx, args) => {
const item = await ctx.db
.query("items")
.withIndex("by_key", (q) => q.eq("key", args.key))
.unique();
if (!item) return null;
if (item.expiresAt && item.expiresAt < Date.now()) return null;
return item.value;
},
});// src/component/public.ts
import { mutation, query } from "./_generated/server";
import { v } from "convex/values";
export const set = mutation({
args: {
key: v.string(),
value: v.string(),
ttl: v.optional(v.number()),
},
returns: v.null(),
handler: async (ctx, args) => {
const existing = await ctx.db
.query("items")
.withIndex("by_key", (q) => q.eq("key", args.key))
.unique();
const data = {
key: args.key,
value: args.value,
expiresAt: args.ttl ? Date.now() + args.ttl : undefined,
};
if (existing) {
await ctx.db.replace(existing._id, data);
} else {
await ctx.db.insert("items", data);
}
return null;
},
});
export const get = query({
args: { key: v.string() },
returns: v.union(v.string(), v.null()),
handler: async (ctx, args) => {
const item = await ctx.db
.query("items")
.withIndex("by_key", (q) => q.eq("key", args.key))
.unique();
if (!item) return null;
if (item.expiresAt && item.expiresAt < Date.now()) return null;
return item.value;
},
});// convex/convex.config.ts
import { defineApp } from "convex/server";
import myComponent from "@org/my-component/convex.config";
const app = defineApp();
app.use(myComponent, { name: "cache" }); // Mount with a name
export default app;// convex/convex.config.ts
import { defineApp } from "convex/server";
import myComponent from "@org/my-component/convex.config";
const app = defineApp();
app.use(myComponent, { name: "cache" }); // 为组件指定挂载名称
export default app;// convex/myFunctions.ts
import { mutation } from "./_generated/server";
import { components } from "./_generated/api";
import { v } from "convex/values";
export const cacheValue = mutation({
args: { key: v.string(), value: v.string() },
returns: v.null(),
handler: async (ctx, args) => {
// Call component function via components.<name>.<module>.<function>
await ctx.runMutation(components.cache.public.set, {
key: args.key,
value: args.value,
ttl: 3600000, // 1 hour
});
return null;
},
});// convex/myFunctions.ts
import { mutation } from "./_generated/server";
import { components } from "./_generated/api";
import { v } from "convex/values";
export const cacheValue = mutation({
args: { key: v.string(), value: v.string() },
returns: v.null(),
handler: async (ctx, args) => {
// 通过components.<挂载名>.<模块>.<函数>调用组件函数
await ctx.runMutation(components.cache.public.set, {
key: args.key,
value: args.value,
ttl: 3600000, // 1小时
});
return null;
},
});Id<"table">// ❌ WRONG: Can't use Id<"items"> from component
import { Id } from "./_generated/dataModel";
const id: Id<"items"> = ...; // Error!
// ✅ CORRECT: Use string or branded type from component
export const processItem = mutation({
args: { itemId: v.string() }, // Accept as string
returns: v.null(),
handler: async (ctx, args) => {
// Pass to component functions as-is
await ctx.runMutation(components.myComponent.public.process, {
itemId: args.itemId,
});
return null;
},
});Id<"table">// ❌ 错误:无法使用组件的Id<"items">类型
import { Id } from "./_generated/dataModel";
const id: Id<"items"> = ...; // 报错!
// ✅ 正确:使用字符串或组件提供的带标识类型
export const processItem = mutation({
args: { itemId: v.string() }, // 以字符串形式接收
returns: v.null(),
handler: async (ctx, args) => {
// 直接传递给组件函数
await ctx.runMutation(components.myComponent.public.process, {
itemId: args.itemId,
});
return null;
},
});process.env// ✅ CORRECT: Component accepts config via function args
// src/component/public.ts
export const initialize = mutation({
args: {
apiKey: v.string(),
endpoint: v.string(),
},
returns: v.null(),
handler: async (ctx, args) => {
await ctx.db.insert("config", {
apiKey: args.apiKey,
endpoint: args.endpoint,
});
return null;
},
});
// Host app passes env vars
export const setup = mutation({
args: {},
returns: v.null(),
handler: async (ctx) => {
await ctx.runMutation(components.myComponent.public.initialize, {
apiKey: process.env.API_KEY!,
endpoint: process.env.API_ENDPOINT!,
});
return null;
},
});process.env// ✅ 正确:组件通过函数参数接收配置
// src/component/public.ts
export const initialize = mutation({
args: {
apiKey: v.string(),
endpoint: v.string(),
},
returns: v.null(),
handler: async (ctx, args) => {
await ctx.db.insert("config", {
apiKey: args.apiKey,
endpoint: args.endpoint,
});
return null;
},
});
// 宿主应用传递环境变量
export const setup = mutation({
args: {},
returns: v.null(),
handler: async (ctx) => {
await ctx.runMutation(components.myComponent.public.initialize, {
apiKey: process.env.API_KEY!,
endpoint: process.env.API_ENDPOINT!,
});
return null;
},
});// ❌ WRONG: Components can't access ctx.auth
export const createItem = mutation({
args: { data: v.string() },
returns: v.null(),
handler: async (ctx, args) => {
const identity = await ctx.auth.getUserIdentity(); // Error!
return null;
},
});
// ✅ CORRECT: Accept userId as argument
export const createItem = mutation({
args: {
userId: v.string(),
data: v.string(),
},
returns: v.id("items"),
handler: async (ctx, args) => {
return await ctx.db.insert("items", {
userId: args.userId,
data: args.data,
});
},
});
// Host app handles auth and passes user info
export const createItemWithAuth = mutation({
args: { data: v.string() },
returns: v.string(),
handler: async (ctx, args) => {
const identity = await ctx.auth.getUserIdentity();
if (!identity) throw new Error("Unauthorized");
const itemId = await ctx.runMutation(
components.myComponent.public.createItem,
{
userId: identity.subject,
data: args.data,
}
);
return itemId; // Return as string
},
});// ❌ 错误:组件无法访问ctx.auth
export const createItem = mutation({
args: { data: v.string() },
returns: v.null(),
handler: async (ctx, args) => {
const identity = await ctx.auth.getUserIdentity(); // 报错!
return null;
},
});
// ✅ 正确:接收userId作为参数
export const createItem = mutation({
args: {
userId: v.string(),
data: v.string(),
},
returns: v.id("items"),
handler: async (ctx, args) => {
return await ctx.db.insert("items", {
userId: args.userId,
data: args.data,
});
},
});
// 宿主应用处理认证并传递用户信息
export const createItemWithAuth = mutation({
args: { data: v.string() },
returns: v.string(),
handler: async (ctx, args) => {
const identity = await ctx.auth.getUserIdentity();
if (!identity) throw new Error("未授权");
const itemId = await ctx.runMutation(
components.myComponent.public.createItem,
{
userId: identity.subject,
data: args.data,
}
);
return itemId; // 以字符串形式返回
},
});// src/component/public.ts
export const list = query({
args: {
cursor: v.optional(v.string()),
limit: v.number(),
},
returns: v.object({
items: v.array(
v.object({
id: v.string(),
data: v.string(),
})
),
nextCursor: v.union(v.string(), v.null()),
}),
handler: async (ctx, args) => {
const results = await ctx.db
.query("items")
.paginate({ cursor: args.cursor ?? null, numItems: args.limit });
return {
items: results.page.map((item) => ({
id: item._id,
data: item.data,
})),
nextCursor: results.continueCursor,
};
},
});// src/component/public.ts
export const list = query({
args: {
cursor: v.optional(v.string()),
limit: v.number(),
},
returns: v.object({
items: v.array(
v.object({
id: v.string(),
data: v.string(),
})
),
nextCursor: v.union(v.string(), v.null()),
}),
handler: async (ctx, args) => {
const results = await ctx.db
.query("items")
.paginate({ cursor: args.cursor ?? null, numItems: args.limit });
return {
items: results.page.map((item) => ({
id: item._id,
data: item.data,
})),
nextCursor: results.continueCursor,
};
},
});// src/client/index.ts
import {
FunctionReference,
GenericMutationCtx,
GenericQueryCtx,
} from "convex/server";
import { api } from "../component/_generated/api";
// Re-export the component config
export { default } from "../component/convex.config";
// Type for the installed component
type ComponentApi = typeof api;
export class MyComponentClient {
private component: ComponentApi;
constructor(component: ComponentApi) {
this.component = component;
}
// Wrapper methods for cleaner API
async set(
ctx: GenericMutationCtx<Record<string, never>>,
key: string,
value: string,
ttl?: number
): Promise<void> {
await ctx.runMutation(this.component.public.set, { key, value, ttl });
}
async get(
ctx: GenericQueryCtx<Record<string, never>>,
key: string
): Promise<string | null> {
return await ctx.runQuery(this.component.public.get, { key });
}
}// src/client/index.ts
import {
FunctionReference,
GenericMutationCtx,
GenericQueryCtx,
} from "convex/server";
import { api } from "../component/_generated/api";
// 重新导出组件配置
export { default } from "../component/convex.config";
// 已安装组件的类型
type ComponentApi = typeof api;
export class MyComponentClient {
private component: ComponentApi;
constructor(component: ComponentApi) {
this.component = component;
}
// 封装方法以简化API调用
async set(
ctx: GenericMutationCtx<Record<string, never>>,
key: string,
value: string,
ttl?: number
): Promise<void> {
await ctx.runMutation(this.component.public.set, { key, value, ttl });
}
async get(
ctx: GenericQueryCtx<Record<string, never>>,
key: string
): Promise<string | null> {
return await ctx.runQuery(this.component.public.get, { key });
}
}// convex/cache.ts
import { MyComponentClient } from "@org/my-component";
import { components } from "./_generated/api";
import { mutation, query } from "./_generated/server";
import { v } from "convex/values";
const cache = new MyComponentClient(components.cache);
export const setValue = mutation({
args: { key: v.string(), value: v.string() },
returns: v.null(),
handler: async (ctx, args) => {
await cache.set(ctx, args.key, args.value, 3600000);
return null;
},
});
export const getValue = query({
args: { key: v.string() },
returns: v.union(v.string(), v.null()),
handler: async (ctx, args) => {
return await cache.get(ctx, args.key);
},
});// convex/cache.ts
import { MyComponentClient } from "@org/my-component";
import { components } from "./_generated/api";
import { mutation, query } from "./_generated/server";
import { v } from "convex/values";
const cache = new MyComponentClient(components.cache);
export const setValue = mutation({
args: { key: v.string(), value: v.string() },
returns: v.null(),
handler: async (ctx, args) => {
await cache.set(ctx, args.key, args.value, 3600000);
return null;
},
});
export const getValue = query({
args: { key: v.string() },
returns: v.union(v.string(), v.null()),
handler: async (ctx, args) => {
return await cache.get(ctx, args.key);
},
});{
"name": "@org/my-component",
"version": "1.0.0",
"type": "module",
"main": "./dist/client/index.js",
"types": "./dist/client/index.d.ts",
"exports": {
".": {
"types": "./dist/client/index.d.ts",
"import": "./dist/client/index.js"
},
"./convex.config": {
"types": "./dist/component/convex.config.d.ts",
"import": "./dist/component/convex.config.js"
}
},
"files": ["dist", "src"],
"scripts": {
"build": "npm run build:esm && npm run build:cjs",
"build:esm": "tsc --project tsconfig.json",
"typecheck": "tsc --noEmit",
"prepare": "npm run build"
},
"dependencies": {
"convex": "^1.17.0"
},
"devDependencies": {
"typescript": "^5.0.0"
},
"peerDependencies": {
"convex": "^1.17.0"
}
}{
"name": "@org/my-component",
"version": "1.0.0",
"type": "module",
"main": "./dist/client/index.js",
"types": "./dist/client/index.d.ts",
"exports": {
".": {
"types": "./dist/client/index.d.ts",
"import": "./dist/client/index.js"
},
"./convex.config": {
"types": "./dist/component/convex.config.d.ts",
"import": "./dist/component/convex.config.js"
}
},
"files": ["dist", "src"],
"scripts": {
"build": "npm run build:esm && npm run build:cjs",
"build:esm": "tsc --project tsconfig.json",
"typecheck": "tsc --noEmit",
"prepare": "npm run build"
},
"dependencies": {
"convex": "^1.17.0"
},
"devDependencies": {
"typescript": "^5.0.0"
},
"peerDependencies": {
"convex": "^1.17.0"
}
}{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "bundler",
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "bundler",
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}undefinedundefinedundefinedundefined// tests/component.test.ts
import { convexTest } from "convex-test";
import { describe, expect, it } from "vitest";
import schema from "../src/component/schema";
import { api } from "../src/component/_generated/api";
describe("MyComponent", () => {
it("should set and get values", async () => {
const t = convexTest(schema);
// Set a value
await t.mutation(api.public.set, {
key: "test-key",
value: "test-value",
});
// Get the value
const result = await t.query(api.public.get, {
key: "test-key",
});
expect(result).toBe("test-value");
});
it("should respect TTL expiration", async () => {
const t = convexTest(schema);
// Set with TTL of 0 (immediate expiry)
await t.mutation(api.public.set, {
key: "expiring-key",
value: "expiring-value",
ttl: -1000, // Already expired
});
// Should return null for expired item
const result = await t.query(api.public.get, {
key: "expiring-key",
});
expect(result).toBeNull();
});
});// tests/component.test.ts
import { convexTest } from "convex-test";
import { describe, expect, it } from "vitest";
import schema from "../src/component/schema";
import { api } from "../src/component/_generated/api";
describe("MyComponent", () => {
it("应该能设置并获取值", async () => {
const t = convexTest(schema);
// 设置值
await t.mutation(api.public.set, {
key: "test-key",
value: "test-value",
});
// 获取值
const result = await t.query(api.public.get, {
key: "test-key",
});
expect(result).toBe("test-value");
});
it("应该遵守TTL过期规则", async () => {
const t = convexTest(schema);
// 设置TTL为0(立即过期)
await t.mutation(api.public.set, {
key: "expiring-key",
value: "expiring-value",
ttl: -1000, // 已过期
});
// 过期项应返回null
const result = await t.query(api.public.get, {
key: "expiring-key",
});
expect(result).toBeNull();
});
});// tests/with-data.test.ts
import { convexTest } from "convex-test";
import { describe, expect, it } from "vitest";
import schema from "../src/component/schema";
import { api } from "../src/component/_generated/api";
describe("MyComponent with data", () => {
it("should handle existing data", async () => {
const t = convexTest(schema);
// Seed data directly
await t.run(async (ctx) => {
await ctx.db.insert("items", {
key: "seeded-key",
value: "seeded-value",
});
});
// Query should find seeded data
const result = await t.query(api.public.get, {
key: "seeded-key",
});
expect(result).toBe("seeded-value");
});
});// tests/with-data.test.ts
import { convexTest } from "convex-test";
import { describe, expect, it } from "vitest";
import schema from "../src/component/schema";
import { api } from "../src/component/_generated/api";
describe("带数据的MyComponent", () => {
it("应该能处理已有数据", async () => {
const t = convexTest(schema);
// 直接植入测试数据
await t.run(async (ctx) => {
await ctx.db.insert("items", {
key: "seeded-key",
value: "seeded-value",
});
});
// 查询应能找到植入的数据
const result = await t.query(api.public.get, {
key: "seeded-key",
});
expect(result).toBe("seeded-value");
});
});convex/
├── convex.config.ts # Main app config
├── schema.ts # Main app schema
├── myLocalComponent/ # Local component
│ ├── convex.config.ts # Component definition
│ ├── schema.ts # Component schema
│ └── functions.ts # Component functions
└── functions.ts # Main app functions// convex/convex.config.ts
import { defineApp } from "convex/server";
import myLocalComponent from "./myLocalComponent/convex.config";
const app = defineApp();
app.use(myLocalComponent, { name: "localCache" });
export default app;convex/
├── convex.config.ts # 主应用配置
├── schema.ts # 主应用Schema
├── myLocalComponent/ # 本地组件
│ ├── convex.config.ts # 组件定义
│ ├── schema.ts # 组件Schema
│ └── functions.ts # 组件函数
└── functions.ts # 主应用函数// convex/convex.config.ts
import { defineApp } from "convex/server";
import myLocalComponent from "./myLocalComponent/convex.config";
const app = defineApp();
app.use(myLocalComponent, { name: "localCache" });
export default app;import { Id } from "./_generated/dataModel";
export const getItem = query({
args: { itemId: v.id("items") }, // ❌ Not accessible!
handler: async (ctx, args) => {
return await ctx.db.get(args.itemId);
},
});export const getItem = query({
args: { itemId: v.string() }, // ✅ Accept as string
returns: v.union(
v.object({
id: v.string(),
data: v.string(),
}),
v.null()
),
handler: async (ctx, args) => {
// Look up by index or iterate
const item = await ctx.db
.query("items")
.filter((q) => q.eq(q.field("_id"), args.itemId))
.first();
if (!item) return null;
return { id: item._id, data: item.data };
},
});import { Id } from "./_generated/dataModel";
export const getItem = query({
args: { itemId: v.id("items") }, // ❌ 无法访问!
handler: async (ctx, args) => {
return await ctx.db.get(args.itemId);
},
});export const getItem = query({
args: { itemId: v.string() }, // ✅ 以字符串形式接收
returns: v.union(
v.object({
id: v.string(),
data: v.string(),
}),
v.null()
),
handler: async (ctx, args) => {
// 通过索引查询或遍历查找
const item = await ctx.db
.query("items")
.filter((q) => q.eq(q.field("_id"), args.itemId))
.first();
if (!item) return null;
return { id: item._id, data: item.data };
},
});// src/component/functions.ts
export const callApi = action({
args: {},
returns: v.null(),
handler: async () => {
// ❌ Components can't access process.env!
const apiKey = process.env.API_KEY;
await fetch("...", { headers: { Authorization: apiKey } });
return null;
},
});// src/component/functions.ts
export const callApi = action({
args: { apiKey: v.string() }, // ✅ Accept as argument
returns: v.null(),
handler: async (ctx, args) => {
await fetch("...", { headers: { Authorization: args.apiKey } });
return null;
},
});
// Host app passes env var
await ctx.runAction(components.myComponent.functions.callApi, {
apiKey: process.env.API_KEY!,
});// src/component/functions.ts
export const callApi = action({
args: {},
returns: v.null(),
handler: async () => {
// ❌ 组件无法访问process.env!
const apiKey = process.env.API_KEY;
await fetch("...", { headers: { Authorization: apiKey } });
return null;
},
});// src/component/functions.ts
export const callApi = action({
args: { apiKey: v.string() }, // ✅ 通过参数接收
returns: v.null(),
handler: async (ctx, args) => {
await fetch("...", { headers: { Authorization: args.apiKey } });
return null;
},
});
// 宿主应用传递环境变量
await ctx.runAction(components.myComponent.functions.callApi, {
apiKey: process.env.API_KEY!,
});// src/component/functions.ts
export const userAction = mutation({
args: { data: v.string() },
returns: v.null(),
handler: async (ctx, args) => {
// ❌ Components don't have ctx.auth!
const identity = await ctx.auth.getUserIdentity();
return null;
},
});// src/component/functions.ts
export const userAction = mutation({
args: {
userId: v.string(), // ✅ Accept user info as argument
data: v.string(),
},
returns: v.null(),
handler: async (ctx, args) => {
await ctx.db.insert("userActions", {
userId: args.userId,
data: args.data,
});
return null;
},
});
// Host app handles auth
export const doUserAction = mutation({
args: { data: v.string() },
returns: v.null(),
handler: async (ctx, args) => {
const identity = await ctx.auth.getUserIdentity();
if (!identity) throw new Error("Unauthorized");
await ctx.runMutation(components.myComponent.functions.userAction, {
userId: identity.subject,
data: args.data,
});
return null;
},
});// src/component/functions.ts
export const userAction = mutation({
args: { data: v.string() },
returns: v.null(),
handler: async (ctx, args) => {
// ❌ 组件没有ctx.auth!
const identity = await ctx.auth.getUserIdentity();
return null;
},
});// src/component/functions.ts
export const userAction = mutation({
args: {
userId: v.string(), // ✅ 通过参数接收用户信息
data: v.string(),
},
returns: v.null(),
handler: async (ctx, args) => {
await ctx.db.insert("userActions", {
userId: args.userId,
data: args.data,
});
return null;
},
});
// 宿主应用处理认证
export const doUserAction = mutation({
args: { data: v.string() },
returns: v.null(),
handler: async (ctx, args) => {
const identity = await ctx.auth.getUserIdentity();
if (!identity) throw new Error("未授权");
await ctx.runMutation(components.myComponent.functions.userAction, {
userId: identity.subject,
data: args.data,
});
return null;
},
});// convex.config.ts
import { defineComponent } from "convex/server";
export default defineComponent("componentName");// convex.config.ts
import { defineComponent } from "convex/server";
export default defineComponent("componentName");// convex/convex.config.ts
import { defineApp } from "convex/server";
import myComponent from "@org/my-component/convex.config";
const app = defineApp();
app.use(myComponent, { name: "mountName" });
export default app;// convex/convex.config.ts
import { defineApp } from "convex/server";
import myComponent from "@org/my-component/convex.config";
const app = defineApp();
app.use(myComponent, { name: "mountName" });
export default app;import { components } from "./_generated/api";
// In mutations/queries/actions
await ctx.runMutation(components.mountName.module.function, args);
await ctx.runQuery(components.mountName.module.function, args);
await ctx.runAction(components.mountName.module.function, args);import { components } from "./_generated/api";
// 在mutation/query/action中调用
await ctx.runMutation(components.mountName.module.function, args);
await ctx.runQuery(components.mountName.module.function, args);
await ctx.runAction(components.mountName.module.function, args);| Feature | Available in Components? |
|---|---|
| Yes (own schema only) |
| No |
| No |
| Yes |
| No (use strings) |
| Pagination cursors | String only |
| 功能特性 | 组件中是否可用? |
|---|---|
| 是(仅能访问自身Schema) |
| 否 |
| 否 |
| 是 |
| 否(使用字符串) |
| 分页游标 | 仅支持字符串类型 |