chore(8.0): final pre-RC1 sweep — API consistency, named errors, orphans, zero-cast codebase
This commit is contained in:
parent
970e08c466
commit
1f7e365a4e
237 changed files with 1951 additions and 49413 deletions
|
|
@ -265,7 +265,7 @@ export abstract class IntegrationBase {
|
|||
}
|
||||
|
||||
/**
|
||||
* Query relations using Brainy getRelations()
|
||||
* Query relations using Brainy related()
|
||||
*/
|
||||
protected async queryRelations(params?: {
|
||||
from?: string
|
||||
|
|
@ -278,7 +278,7 @@ export abstract class IntegrationBase {
|
|||
throw new Error('Integration not initialized')
|
||||
}
|
||||
|
||||
return this.context.brain.getRelations(params)
|
||||
return this.context.brain.related(params)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -82,7 +82,12 @@ export interface IntegrationResponse {
|
|||
export class IntegrationHub {
|
||||
private integrations: Map<IntegrationType, IntegrationBase> = new Map()
|
||||
private config: Required<IntegrationHubConfig>
|
||||
private _endpoints: Record<IntegrationType, string> = {} as any
|
||||
/**
|
||||
* Endpoint paths keyed by integration type. Starts empty and gains one key
|
||||
* per integration enabled in initialize() — disabled integrations have no
|
||||
* entry, hence the sparse-record assertion on the initializer.
|
||||
*/
|
||||
private _endpoints: Record<IntegrationType, string> = {} as Record<IntegrationType, string>
|
||||
|
||||
/**
|
||||
* Create and initialize the Integration Hub
|
||||
|
|
@ -168,7 +173,7 @@ export class IntegrationHub {
|
|||
|
||||
private getBasePath(type: IntegrationType): string {
|
||||
const basePath = this.config.basePath
|
||||
const cfg = this.config.config?.[type] as any
|
||||
const cfg = this.config.config?.[type]
|
||||
|
||||
switch (type) {
|
||||
case 'odata':
|
||||
|
|
@ -222,7 +227,13 @@ export class IntegrationHub {
|
|||
if (path.startsWith(basePath) || path === basePath) {
|
||||
const integration = this.integrations.get(type as IntegrationType)
|
||||
if (integration && 'handleRequest' in integration) {
|
||||
const handler = integration as any
|
||||
// The `in` guard proved the integration exposes an HTTP handler.
|
||||
// Each integration declares its own structurally-compatible
|
||||
// request/response shapes (e.g. the OData request/response pair),
|
||||
// so the hub dispatches through this minimal common signature.
|
||||
const handler = integration as IntegrationBase & {
|
||||
handleRequest(request: IntegrationRequest): Promise<IntegrationResponse>
|
||||
}
|
||||
const relativePath = path.slice(basePath.length) || '/'
|
||||
|
||||
return handler.handleRequest({
|
||||
|
|
|
|||
|
|
@ -76,20 +76,29 @@ export const INTEGRATION_CATALOG: Record<IntegrationType, IntegrationInfo> = {
|
|||
* Detect current runtime environment
|
||||
*/
|
||||
export function detectEnvironment(): RuntimeEnvironment {
|
||||
// Runtime-detection boundary: these globals exist only in specific runtimes
|
||||
// (Deno, Bun, Cloudflare Workers), so they aren't on TypeScript's view of
|
||||
// globalThis — probe them through an optional-keyed view.
|
||||
const runtimeGlobals = globalThis as typeof globalThis & {
|
||||
Deno?: unknown
|
||||
Bun?: unknown
|
||||
HTMLRewriter?: unknown
|
||||
}
|
||||
|
||||
// Deno
|
||||
if (typeof (globalThis as any).Deno !== 'undefined') {
|
||||
if (typeof runtimeGlobals.Deno !== 'undefined') {
|
||||
return 'deno'
|
||||
}
|
||||
|
||||
// Bun
|
||||
if (typeof (globalThis as any).Bun !== 'undefined') {
|
||||
if (typeof runtimeGlobals.Bun !== 'undefined') {
|
||||
return 'bun'
|
||||
}
|
||||
|
||||
// Cloudflare Workers
|
||||
if (
|
||||
typeof (globalThis as any).caches !== 'undefined' &&
|
||||
typeof (globalThis as any).HTMLRewriter !== 'undefined'
|
||||
typeof runtimeGlobals.caches !== 'undefined' &&
|
||||
typeof runtimeGlobals.HTMLRewriter !== 'undefined'
|
||||
) {
|
||||
return 'cloudflare'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
import { Entity, Relation } from '../../types/brainy.types.js'
|
||||
import { NounType } from '../../types/graphTypes.js'
|
||||
import {
|
||||
TabularRow,
|
||||
RelationTabularRow,
|
||||
|
|
@ -462,9 +463,11 @@ export class TabularExporter {
|
|||
private rowToEntity(row: Record<string, any>): Partial<Entity> {
|
||||
const entity: Partial<Entity> = {}
|
||||
|
||||
// Map standard columns
|
||||
// Map standard columns. Imported rows are an untrusted boundary — the
|
||||
// Type cell is taken at its word here; Brainy validates the NounType
|
||||
// vocabulary when the partial entity is written.
|
||||
if (row.Id) entity.id = row.Id
|
||||
if (row.Type) entity.type = row.Type as any
|
||||
if (row.Type) entity.type = row.Type as NounType
|
||||
if (row.Service) entity.service = row.Service
|
||||
if (row.Confidence) entity.confidence = parseFloat(row.Confidence)
|
||||
if (row.Weight) entity.weight = parseFloat(row.Weight)
|
||||
|
|
|
|||
|
|
@ -14,7 +14,12 @@ import {
|
|||
IntegrationBase,
|
||||
HTTPIntegration
|
||||
} from '../core/IntegrationBase.js'
|
||||
import { IntegrationConfig, ODataQueryOptions } from '../core/types.js'
|
||||
import {
|
||||
IntegrationConfig,
|
||||
ODataQueryOptions,
|
||||
RelationTabularRow,
|
||||
TabularRow
|
||||
} from '../core/types.js'
|
||||
import { Entity, Relation, FindParams } from '../../types/brainy.types.js'
|
||||
import { NounType } from '../../types/graphTypes.js'
|
||||
import {
|
||||
|
|
@ -387,12 +392,13 @@ export class ODataIntegration extends IntegrationBase implements HTTPIntegration
|
|||
entities = entities.slice(0, pageSize)
|
||||
}
|
||||
|
||||
// Convert to tabular format
|
||||
let rows = this.exporter.entitiesToRows(entities)
|
||||
// Convert to tabular format. $select projects rows down to a subset of
|
||||
// columns, so the working type is Partial<TabularRow> from the start.
|
||||
let rows: Array<Partial<TabularRow>> = this.exporter.entitiesToRows(entities)
|
||||
|
||||
// Apply $select
|
||||
if (options.select && options.select.length > 0) {
|
||||
rows = applySelect(rows, options.select) as any
|
||||
rows = applySelect(rows, options.select)
|
||||
}
|
||||
|
||||
// Apply $orderby (if not handled by storage)
|
||||
|
|
@ -528,7 +534,7 @@ export class ODataIntegration extends IntegrationBase implements HTTPIntegration
|
|||
return this.errorResponse(400, 'Invalid entity ID')
|
||||
}
|
||||
|
||||
await this.context.brain.delete(idMatch[1])
|
||||
await this.context.brain.remove(idMatch[1])
|
||||
|
||||
return {
|
||||
status: 204,
|
||||
|
|
@ -566,16 +572,18 @@ export class ODataIntegration extends IntegrationBase implements HTTPIntegration
|
|||
offset: options.skip
|
||||
})
|
||||
|
||||
let rows = this.exporter.relationsToRows(relations)
|
||||
// $select projects rows down to a subset of columns, so the working type
|
||||
// is Partial<RelationTabularRow> from the start.
|
||||
let rows: Array<Partial<RelationTabularRow>> = this.exporter.relationsToRows(relations)
|
||||
|
||||
// Apply $filter
|
||||
if (options.filter) {
|
||||
rows = applyFilter(rows, options.filter) as any
|
||||
rows = applyFilter(rows, options.filter)
|
||||
}
|
||||
|
||||
// Apply $select
|
||||
if (options.select && options.select.length > 0) {
|
||||
rows = applySelect(rows, options.select) as any
|
||||
rows = applySelect(rows, options.select)
|
||||
}
|
||||
|
||||
// Apply $orderby
|
||||
|
|
|
|||
|
|
@ -552,7 +552,7 @@ export class GoogleSheetsIntegration
|
|||
return this.errorResponse(400, 'Missing required field: id')
|
||||
}
|
||||
|
||||
await this.context.brain.delete(body.id)
|
||||
await this.context.brain.remove(body.id)
|
||||
|
||||
return this.jsonResponse({ success: true, deleted: body.id })
|
||||
}
|
||||
|
|
@ -595,7 +595,7 @@ export class GoogleSheetsIntegration
|
|||
break
|
||||
|
||||
case 'delete':
|
||||
await this.context.brain.delete(op.id)
|
||||
await this.context.brain.remove(op.id)
|
||||
results.push({ success: true, id: op.id, action: 'delete' })
|
||||
break
|
||||
|
||||
|
|
|
|||
|
|
@ -338,20 +338,23 @@ export class SSEIntegration
|
|||
): EventFilter {
|
||||
const filter: EventFilter = {}
|
||||
|
||||
// HTTP query strings are an untrusted boundary: narrow them to the typed
|
||||
// filter fields directly. Unknown values are harmless — matchesFilter()
|
||||
// compares with includes(), so they simply never match an event.
|
||||
if (query.types) {
|
||||
filter.entityTypes = query.types.split(',') as any[]
|
||||
filter.entityTypes = query.types.split(',') as EventFilter['entityTypes']
|
||||
}
|
||||
|
||||
if (query.operations) {
|
||||
filter.operations = query.operations.split(',') as any[]
|
||||
filter.operations = query.operations.split(',') as EventFilter['operations']
|
||||
}
|
||||
|
||||
if (query.nounTypes) {
|
||||
filter.nounTypes = query.nounTypes.split(',') as any[]
|
||||
filter.nounTypes = query.nounTypes.split(',') as EventFilter['nounTypes']
|
||||
}
|
||||
|
||||
if (query.verbTypes) {
|
||||
filter.verbTypes = query.verbTypes.split(',') as any[]
|
||||
filter.verbTypes = query.verbTypes.split(',') as EventFilter['verbTypes']
|
||||
}
|
||||
|
||||
if (query.service) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue