import { entityKind } from 'drizzle-orm'; import { PodDialect } from './pod-dialect'; import { PodAsyncSession, type SelectFieldMap, SelectQueryBuilder, InsertQueryBuilder, UpdateQueryBuilder, DeleteQueryBuilder } from './pod-session'; import { PodTable, SolidSchema, type InferTableData, PodColumnBase, type InstantiateTableOptions } from './schema'; import { type PublicQueryCondition, type PublicWhereObject } from './query-conditions'; import { type TableSubscribeOptions, type EntitySubscribeOptions, type Subscription } from './notifications'; import { type FederatedError } from './federated'; import type { DataDiscovery } from './discovery'; import type { OrderByExpression } from './order-by'; /** * 初始化表选项 */ export interface InitOptions { /** 是否生成 Shape 定义 */ generateShape?: boolean; /** 是否保存 Shape 到 Pod(需要 generateShape 为 true) */ saveShape?: boolean; /** Shape 保存位置(容器路径或完整 URL),默认为 Pod 根目录下的 /shapes/ */ shapeLocation?: string; } type GenericPodTable = PodTable; type GenericPodResource = GenericPodTable; type QueryOrderBy = { column: PodColumnBase | string | OrderByExpression; direction?: 'asc' | 'desc'; }; type QueryExactOptions = { columns?: SelectFieldMap; with?: Record; }; type EntityLocator = Record; type QueryFindManyOptions = { where?: PublicWhereObject | PublicQueryCondition; columns?: SelectFieldMap; limit?: number; offset?: number; orderBy?: QueryOrderBy[] | QueryOrderBy; with?: Record; }; export type ResourceFileInput = { path?: string; url?: string; content?: BodyInit | null; contentType?: string; relationField?: string; }; export type ResourceWithDocumentInput = { row: Record; document?: ResourceFileInput; source?: ResourceFileInput; files?: ResourceFileInput[]; }; export type PlannedResourceFileWrite = { relationField: string; url: string; contentType?: string; }; export type ResourceWithDocumentPlan = { ok: true; resourceId: string; resourceIri: string; row: Record; rdfMutations: Array<{ type: 'upsert'; subject: string; fields: string[]; }>; fileWrites: PlannedResourceFileWrite[]; warnings: string[]; errors: string[]; }; export type MoveDocumentInput = { id: string; relationField?: string; fromPath?: string; fromUrl?: string; toPath?: string; toUrl?: string; content?: BodyInit | null; contentType?: string; }; export type DeleteResourceWithDocumentInput = { id: string; relationField?: string; path?: string; url?: string; }; type QueryResourceHelper = { findMany>(options?: QueryFindManyOptions): Promise; findFirst>(options?: QueryFindManyOptions): Promise; find>(target: string | EntityLocator, options?: QueryExactOptions): Promise; findById>(id: string, options?: QueryExactOptions): Promise; findByResource>(target: string | EntityLocator, options?: QueryExactOptions): Promise; findByLocator>(locator: EntityLocator, options?: QueryExactOptions): Promise; findByIri>(iri: string, options?: QueryExactOptions): Promise; count(options?: { where?: PublicWhereObject | PublicQueryCondition; }): Promise; }; type QueryProxy> = { [K in keyof TSchema as TSchema[K] extends GenericPodTable ? K : never]: TSchema[K] extends GenericPodTable ? QueryResourceHelper : never; }; export declare class PodDatabase = Record> { dialect: PodDialect; session: PodAsyncSession; schema?: TSchema | undefined; static readonly [entityKind] = "PodDatabase"; private static findByLocatorDeprecationWarned; private notificationsClient; private federatedExecutor; /** 最近一次联邦查询的错误(如果有) */ private lastFederatedErrors; constructor(dialect: PodDialect, session: PodAsyncSession, schema?: TSchema | undefined); /** * 获取最近一次联邦查询的错误 * 在调用 findMany 等方法后可以检查此值 */ getLastFederatedErrors(): FederatedError[]; /** * 清除联邦查询错误 */ clearFederatedErrors(): void; /** * 获取或创建 FederatedQueryExecutor */ private getFederatedExecutor; /** * Create a table from a schema with hooks * * This is the recommended way to create tables when you need hooks that * can access the database instance (e.g., for cross-table operations). * * @param schema - The schema definition (created via solidSchema()) * @param options - Table options including base path and hooks * @returns A PodTable instance with hooks bound to this database * * @example * ```typescript * const userSchema = solidSchema('users', { * id: id(), * name: text('name').predicate(SCHEMA.name), * email: text('email').predicate(SCHEMA.email), * }, { * type: SCHEMA.Person, * }); * * const userTable = db.createTable(userSchema, { * base: '/data/users/', * hooks: { * afterInsert: async (ctx, record) => { * // ctx.db is available - can query/insert other tables * await ctx.db.insert(auditTable).values({ * action: 'user_created', * userId: record['@id'], * }); * }, * afterUpdate: async (ctx, record, changes) => { * if ('email' in changes) { * // Send verification email via another service * await ctx.db.insert(notificationTable).values({ * type: 'email_changed', * userId: record['@id'], * }); * } * }, * }, * }); * ``` */ createTable>(schema: SolidSchema, options: InstantiateTableOptions): PodTable; select(fields?: SelectFieldMap): SelectQueryBuilder; insert(table: TTable): InsertQueryBuilder; update(table: TTable): UpdateQueryBuilder; delete(table: TTable): DeleteQueryBuilder; private resolvePodFileUrl; private getResourceFileInputs; private validateNoDuplicatePathComposition; dryRunResourceWithDocument(resource: TTable, input: ResourceWithDocumentInput): Promise; upsertResourceWithDocument(resource: TTable, input: ResourceWithDocumentInput): Promise; moveDocumentAndRelink(resource: TTable, input: MoveDocumentInput): Promise<{ fromUrl?: string; toUrl: string; resource: unknown | null; }>; deleteResourceWithDocument(resource: TTable, input: DeleteResourceWithDocumentInput): Promise<{ deleted: boolean; fileUrl?: string; }>; execute(query: string): Promise; executeSPARQL(query: string): Promise; batch(operations: TOperations): Promise<{ [K in keyof TOperations]: Awaited; }>; findFirst(table: TTable, where?: PublicWhereObject): Promise | null>; private getLocatorTemplate; private getRequiredLocatorKeys; private resolveResourceTargetIri; private resolveLocatorSubject; private resolveIdSubject; private warnFindByLocatorDeprecation; private getIriAlternative; resolveLocatorIri(resource: TTable, locator: EntityLocator): string; resolveLocatorId(resource: TTable, locator: EntityLocator): string; resolveResourceIri(resource: TTable, target: string | EntityLocator): string; resolveResourceId(resource: TTable, target: string | EntityLocator): string; resolveRelationIri(resource: TTable, target: string | EntityLocator): string; resolveRowIri(resource: TTable, row: EntityLocator): string; resolveRowId(resource: TTable, row: EntityLocator): string; private resolveBaseRelativeResourceId; private needsShortIdSubjectLookup; private buildShortIdSubjectSuffix; private resolveShortIdLookupSource; private extractSubjectFromLookupRow; private subjectMatchesShortId; private getIndexedSubject; private lookupSubjectIriByShortId; private resolveIdSubjectForExactOperation; findById(resource: TTable, id: string): Promise | null>; findById(resource: GenericPodResource, id: string): Promise; /** * @deprecated Use findById(resource, id), findByResource(resource, target), or findByIri(resource, iri). * This locator-shaped exact lookup will be removed in a future release. */ findByLocator(resource: TTable, locator: EntityLocator): Promise | null>; findByLocator(resource: GenericPodResource, locator: EntityLocator): Promise; findByResource(resource: TTable, target: string | EntityLocator): Promise | null>; findByResource(resource: GenericPodResource, target: string | EntityLocator): Promise; /** * 通过完整 IRI 查询单个实体 * * @param resource - Resource definition used to decode schema and subject shape. * @param iri - 完整 IRI,本地或远程 * @returns 实体数据,如果不存在则返回 null * * @example * ```typescript * // 本地 Agent * db.findByIri(agentTable, 'https://my.pod/agents/translator') * * // 远程 Profile * db.findByIri(solidProfileTable, 'https://alice.pod/profile/card#me') * * // 业务层不区分本地远程 * db.findByIri(agentTable, contact.entityUri) * ``` */ findByIri(resource: TTable, iri: string): Promise | null>; findByIri(resource: GenericPodResource, iri: string): Promise; private getColumnPredicate; private findByIriViaExactResource; private readPredicateObjectRowsFromDocument; private convertRdfTermToValue; private isMissingExactResourceError; private mapPredicateObjectRows; private extractIdFromIri; /** * 解析 IRI 为文档 URL 和 fragment */ private parseIri; /** * 通过完整 IRI 订阅单个实体的变更 * * @param resource - Resource definition used to decode schema and subject shape. * @param iri - 完整 IRI,本地或远程 * @param options - 订阅选项 * @returns 取消订阅函数 * * @example * ```typescript * const unsubscribe = await db.subscribeByIri( * solidProfileTable, * 'https://alice.pod/profile/card#me', * { * onUpdate: (data) => { * console.log('Profile updated:', data.name) * }, * onDelete: () => { * console.log('Profile deleted') * }, * onError: (error) => { * console.log('Subscription error:', error) * } * } * ) * * // 离开时取消订阅 * unsubscribe() * ``` */ subscribeByIri(resource: TTable, iri: string, options: EntitySubscribeOptions>): Promise<() => void>; /** * 通过完整 IRI 更新单个实体 * * @param resource - Resource definition used to decode schema and subject shape. * @param iri - 完整 IRI * @param data - 要更新的数据 * @returns 更新后的实体数据 * * @example * ```typescript * const updated = await db.updateByIri( * agentTable, * 'https://my.pod/agents/translator#agent', * { name: 'New Name', description: 'Updated description' } * ) * ``` */ updateByIri(resource: TTable, iri: string, data: Partial, '@id' | 'id'>>): Promise | null>; updateByIri(resource: GenericPodResource, iri: string, data: Record): Promise; updateById(resource: TTable, id: string, data: Partial, '@id' | 'id'>>): Promise | null>; updateById(resource: GenericPodResource, id: string, data: Record): Promise; /** * @deprecated Use updateById(resource, id, data), updateByResource(resource, target, data), or updateByIri(resource, iri, data). * This locator-shaped exact update will be removed in a future release. */ updateByLocator(resource: TTable, locator: EntityLocator, data: Partial, '@id' | 'id'>>): Promise | null>; updateByLocator(resource: GenericPodResource, locator: EntityLocator, data: Record): Promise; updateByResource(resource: TTable, target: string | EntityLocator, data: Partial, '@id' | 'id'>>): Promise | null>; updateByResource(resource: GenericPodResource, target: string | EntityLocator, data: Record): Promise; /** * 通过完整 IRI 删除单个实体 * * @param resource - Resource definition used to decode schema and subject shape. * @param iri - 完整 IRI * @returns 是否删除成功 * * @example * ```typescript * const deleted = await db.deleteByIri( * agentTable, * 'https://my.pod/agents/translator#agent' * ) * ``` */ deleteByIri(resource: TTable, iri: string): Promise; deleteById(resource: TTable, id: string): Promise; /** * @deprecated Use deleteById(resource, id), deleteByResource(resource, target), or deleteByIri(resource, iri). * This locator-shaped exact delete will be removed in a future release. */ deleteByLocator(resource: TTable, locator: EntityLocator): Promise; deleteByResource(resource: TTable, target: string | EntityLocator): Promise; transaction(transaction: (tx: PodDatabase) => Promise): Promise; getDialect(): PodDialect; getSession(): PodAsyncSession; getSchema(): TSchema | undefined; /** * 数据发现入口(TypeIndex + SAI 组合策略) */ get discovery(): DataDiscovery; connect(): Promise; disconnect(): Promise; /** * 订阅资源的变化通知 * * @param resource - 要订阅的 resource(会订阅其容器或文档) * @param options - 订阅选项,支持按类型分开的回调 * @returns 订阅句柄,可用于取消订阅 * * @example * ```typescript * const subscription = await db.subscribe(posts, { * onCreate: async (activity) => { * console.log('Created:', activity.object); * const latest = await db.select().from(posts); * }, * onUpdate: async (activity) => { * console.log('Updated:', activity.object); * const latest = await db.select().from(posts); * }, * onDelete: (activity) => { * console.log('Deleted:', activity.object); * }, * onError: (error) => { * console.error('Subscription error:', error); * } * }); * * // 取消订阅 * subscription.unsubscribe(); * ``` */ subscribe(resource: TTable, options: TableSubscribeOptions): Promise; /** * 解析 resource 对应的订阅主题 URL * @param resource Resource definition * @param iriOverride 可选的 IRI 覆盖(用于订阅其他 Pod 的资源) */ private resolveResourceTopic; getConfig(): { podUrl: string; webId: string; connected: boolean; }; /** * 通过 RDF 类型自动发现并生成表定义 * * 流程: * 1. 通过 TypeIndex/Interop 发现数据位置 * 2. 如果有 ShapeTree/Shape,加载并解析 * 3. 从 Shape 生成 PodTable 定义 * * @param rdfClass RDF 类型 URI (如 'http://schema.org/Person') * @returns 生成的 PodTable,如果未发现则返回 null * * @example * ```typescript * // 自动发现 Person 类型的表 * const persons = await db.discoverTable('http://schema.org/Person'); * if (persons) { * const data = await db.select().from(persons); * } * ``` */ discoverTable(rdfClass: string): Promise; /** * 发现多个类型的表 */ discoverTables(rdfClasses: string[]): Promise; /** * 发现某类型数据的所有位置 * @param rdfClass RDF 类型 URI * @param options 可选的过滤选项 * @returns 数据位置列表 * * @example * ```typescript * // 发现所有 Person 类型的数据位置 * const locations = await db.discover('https://schema.org/Person'); * * // 按 appId 过滤 * const acmeLocations = await db.discover('https://schema.org/Person', { * appId: 'https://acme.com/app#id' * }); * ``` */ discover(rdfClass: string, options?: import('./discovery').DiscoverOptions): Promise; /** * 获取所有数据注册信息 * @returns 所有注册的数据信息列表 * * @example * ```typescript * const allRegistrations = await db.discoverAll(); * for (const reg of allRegistrations) { * console.log(`${reg.rdfClass} at ${reg.container}`); * console.log(` Shape: ${reg.shape}`); * console.log(` Registered by: ${reg.registeredBy}`); * } * ``` */ discoverAll(): Promise; /** * 按应用 ID 发现数据位置 * @param appId 应用标识符 * @returns 该应用注册的数据位置列表 * * @example * ```typescript * const acmeData = await db.discoverByApp('https://acme.com/app#id'); * ``` */ discoverByApp(appId: string): Promise; /** * 从 DataLocation 创建 PodTable * 如果 location 有 shape,会加载 Shape 并生成完整的表定义 * * @param location 数据位置信息 * @param options 转换选项,可指定使用哪个 Shape * @returns PodTable 实例 * * @example * ```typescript * // 使用第一个可用的 Shape * const table = await db.locationToTable(location); * * // 指定使用某个 app 注册的 Shape * const table = await db.locationToTable(location, { * appId: 'https://acme.com/app#id' * }); * * // 直接传入 ShapeInfo 对象 * const table = await db.locationToTable(location, { * shape: location.shapes[0] * }); * * // 或者传入 Shape URL * const table = await db.locationToTable(location, { * shape: 'https://shapes.example/Person.shacl' * }); * ``` */ locationToTable(location: import('./discovery').DataLocation, options?: import('./discovery').LocationToTableOptions): Promise; /** * 从容器 URL 提取表名 */ private extractContainerName; /** * 发现并转换为表 - 支持按 appId 过滤 * * @param rdfClass RDF 类型 URI * @param options 发现选项 * @param tableOptions 转表选项(指定使用哪个 Shape) * * @example * ```typescript * // 发现所有 Person 表 * const allPersonTables = await db.discoverTablesFor('schema:Person'); * * // 只要 Acme app 的 Person 表,并使用 Acme 的 Shape * const acmePersonTable = await db.discoverTablesFor('schema:Person', * { appId: 'https://acme.com/app#id' }, * { appId: 'https://acme.com/app#id' } * ); * ``` */ discoverTablesFor(rdfClass: string, options?: import('./discovery').DiscoverOptions, tableOptions?: import('./discovery').LocationToTableOptions): Promise; /** * 从 RDF 类型 URI 提取类名 */ private extractClassName; addSource(source: string): void; removeSource(source: string): void; getSources(): string[]; addSourcesFromTypeIndex(): Promise; init(tables: TTable | TTable[], options?: InitOptions): Promise; init(...tables: Array): Promise; private queryProxy?; get query(): QueryProxy; private buildQueryProxy; private eagerLoadWith; private dedupeBySubject; private resolveIdFromRow; private resolveParentKey; private resolveIriFromRow; private collectLinkLookupKeys; private normalizeLinkValue; private normalizeLiteralValue; private collectInverseLinkValues; private groupRowsByIri; private extractFragment; } export {}; //# sourceMappingURL=pod-database.d.ts.map