Spaces:
Running
Running
Download source/teaql-ts/src/core/ast.ts from teaql/browser-sqlite-playground: direct link, hf CLI and curl.
- Browser
- Download file 10.7 kB
-
https://huggingface.co/spaces/teaql/browser-sqlite-playground/resolve/main/source/teaql-ts/src/core/ast.ts
- Command line
-
hf download hf://spaces/teaql/browser-sqlite-playground/source/teaql-ts/src/core/ast.ts
-
curl -L -o ast.ts https://huggingface.co/spaces/teaql/browser-sqlite-playground/resolve/main/source/teaql-ts/src/core/ast.ts
10.7 kB
| import { Value } from './value'; | |
| import { SmartList } from './smart-list'; | |
| import { retainQueryDiagnosticOrigin, snapshotQuery } from './query-snapshot'; | |
| import type { QueryOperation } from './request-intent'; | |
| export enum SortDirection { | |
| Asc = 'Asc', | |
| Desc = 'Desc' | |
| } | |
| export class OrderBy { | |
| constructor( | |
| public field: string, | |
| public expr: any | null, | |
| public direction: SortDirection | |
| ) {} | |
| static new(field: string, direction: SortDirection): OrderBy { | |
| return new OrderBy(field, null, direction); | |
| } | |
| static expr(expr: any, direction: SortDirection): OrderBy { | |
| return new OrderBy('', expr, direction); | |
| } | |
| static asc(field: string): OrderBy { return OrderBy.new(field, SortDirection.Asc); } | |
| static desc(field: string): OrderBy { return OrderBy.new(field, SortDirection.Desc); } | |
| } | |
| export class AggregationCacheOptions { | |
| constructor( | |
| public enabledValue: boolean, | |
| public cacheExpiredMillis: number, | |
| public propagateValue: boolean, | |
| public propagateCacheExpiredMillis: number | |
| ) {} | |
| static enabled(cacheExpiredMillis: number): AggregationCacheOptions { | |
| return new AggregationCacheOptions(true, cacheExpiredMillis, false, 0); | |
| } | |
| propagate(cacheExpiredMillis: number): AggregationCacheOptions { | |
| this.propagateValue = true; | |
| this.propagateCacheExpiredMillis = cacheExpiredMillis; | |
| return this; | |
| } | |
| } | |
| export interface TeaQLPage<T> { | |
| data: SmartList<T>; | |
| totalCount: number; | |
| offset: number; | |
| limit: number; | |
| } | |
| export interface QuerySelection { | |
| toQuery(): SelectQuery; | |
| } | |
| export interface FacetRequest { | |
| facetName: string; | |
| relationName: string; | |
| query: SelectQuery; | |
| includeAllFacets: boolean; | |
| operation?: QueryOperation; | |
| } | |
| export interface RelationAggregate { | |
| relationName: string; | |
| alias: string; | |
| query: SelectQuery; | |
| singleResult: boolean; | |
| operation?: QueryOperation; | |
| } | |
| export class SelectQuery { | |
| private hardLimitValue: number = 10_000; | |
| private continuousPageFetchOptions?: { namespace: string; ttlSeconds: number }; | |
| private continuousPageRuntimeContext?: any; | |
| private idSetPaginationOptions?: { namespace: string; ttlSeconds: number; maxIds: number }; | |
| private idSetPaginationRuntimeContext?: any; | |
| private topNProbeThreshold?: number; | |
| public entity: string; | |
| public filterCondition: any | null = null; | |
| public limitValue: number = 0; | |
| public offsetValue: number = 0; | |
| public orderItems: OrderBy[] = []; | |
| public selectItems: string[] = []; | |
| public properties: string[] = []; | |
| public joins: any[] = []; | |
| public groupByItems: string[] = []; | |
| public aggregateItems: any[] = []; | |
| public aggregationCache?: AggregationCacheOptions; | |
| public facets: FacetRequest[] = []; | |
| public relations: Array<{ | |
| name: string; | |
| query?: SelectQuery; | |
| localKey?: string; | |
| foreignKey?: string; | |
| many?: boolean; | |
| operation?: QueryOperation; | |
| }> = []; | |
| public relationAggregates: RelationAggregate[] = []; | |
| public commentText?: string; | |
| public purposeText?: string; | |
| constructor(entity: string) { | |
| this.entity = entity; | |
| // Local runtime policy must never cross the federation JSON boundary. | |
| Object.defineProperty(this, 'hardLimitValue', { enumerable: false, writable: true, value: 10_000 }); | |
| Object.defineProperty(this, 'continuousPageFetchOptions', { enumerable: false, writable: true, value: undefined }); | |
| Object.defineProperty(this, 'continuousPageRuntimeContext', { enumerable: false, writable: true, value: undefined }); | |
| Object.defineProperty(this, 'idSetPaginationOptions', { enumerable: false, writable: true, value: undefined }); | |
| Object.defineProperty(this, 'idSetPaginationRuntimeContext', { enumerable: false, writable: true, value: undefined }); | |
| Object.defineProperty(this, 'topNProbeThreshold', { enumerable: false, writable: true, value: undefined }); | |
| } | |
| comment(text: string): this { | |
| this.commentText = text; | |
| return this; | |
| } | |
| purpose(text: string): this { | |
| this.purposeText = text; | |
| return this; | |
| } | |
| facetBy( | |
| facetName: string, | |
| relationName: string, | |
| request: QuerySelection, | |
| includeAllFacets = true, | |
| operation?: QueryOperation, | |
| ): this { | |
| this.facets.push({ | |
| facetName, | |
| relationName, | |
| query: request.toQuery(), | |
| includeAllFacets, | |
| operation, | |
| }); | |
| return this; | |
| } | |
| clone(): SelectQuery { | |
| const copy = snapshotQuery(this); | |
| // Cloning an execution snapshot produces a mutable builder. The request | |
| // envelope still owns immutable intent and reapplies it on derivation. | |
| for (const field of ['commentText', 'purposeText', '_comment', '_purpose']) { | |
| const descriptor = Object.getOwnPropertyDescriptor(copy, field); | |
| if (descriptor && 'value' in descriptor && descriptor.configurable) { | |
| Object.defineProperty(copy, field, { ...descriptor, writable: true }); | |
| } | |
| } | |
| return copy; | |
| } | |
| filter(condition: any): this { | |
| this.filterCondition = condition; | |
| return this; | |
| } | |
| limit(limit: number): this { | |
| if (!Number.isSafeInteger(limit) || limit < 1) { | |
| throw new Error('QUERY_INVALID_LIMIT: limit must be a positive safe integer'); | |
| } | |
| this.limitValue = limit; | |
| return this; | |
| } | |
| optimizeForContinuousPageFetch(): this { | |
| return this.optimizeForContinuousPageFetchWith('default', 600); | |
| } | |
| optimizeForContinuousPageFetchWith(namespace: string, ttlSeconds: number): this { | |
| if (!namespace?.trim()) throw new Error('continuous page namespace must not be empty'); | |
| if (!Number.isSafeInteger(ttlSeconds) || ttlSeconds <= 0) throw new Error('continuous page ttlSeconds must be a positive integer'); | |
| this.continuousPageFetchOptions = { namespace, ttlSeconds }; | |
| return this; | |
| } | |
| bindContinuousPageRuntime(runtime: any): this { this.continuousPageRuntimeContext = runtime; return this; } | |
| localContinuousPageOptions(): { namespace: string; ttlSeconds: number } | undefined { return this.continuousPageFetchOptions; } | |
| localContinuousPageRuntime(): any { return this.continuousPageRuntimeContext; } | |
| clearContinuousPageRuntime(): this { this.continuousPageRuntimeContext = undefined; return this; } | |
| optimizePaginationWithIdSet(): this { | |
| return this.optimizePaginationWithIdSetConfig('default', 600, 3_000_000); | |
| } | |
| optimizePaginationWithIdSetConfig(namespace: string, ttlSeconds: number, maxIds: number): this { | |
| if (!namespace?.trim()) throw new Error('ID set namespace must not be empty'); | |
| if (!Number.isSafeInteger(ttlSeconds) || ttlSeconds <= 0) throw new Error('ID set ttlSeconds must be a positive integer'); | |
| if (!Number.isSafeInteger(maxIds) || maxIds <= 0) throw new Error('ID set maxIds must be a positive integer'); | |
| this.idSetPaginationOptions = { namespace, ttlSeconds, maxIds }; | |
| return this; | |
| } | |
| bindIdSetPaginationRuntime(runtime: any): this { this.idSetPaginationRuntimeContext = runtime; return this; } | |
| localIdSetPaginationOptions(): { namespace: string; ttlSeconds: number; maxIds: number } | undefined { return this.idSetPaginationOptions; } | |
| localIdSetPaginationRuntime(): any { return this.idSetPaginationRuntimeContext; } | |
| clearIdSetPaginationRuntime(): this { this.idSetPaginationRuntimeContext = undefined; return this; } | |
| topNProbeParentThreshold(threshold: number): this { | |
| if (!Number.isSafeInteger(threshold) || threshold < 0) { | |
| throw new Error('topNProbeParentThreshold must be a non-negative safe integer'); | |
| } | |
| this.topNProbeThreshold = threshold; | |
| return this; | |
| } | |
| localTopNProbeParentThreshold(): number | undefined { | |
| return this.topNProbeThreshold; | |
| } | |
| prepareForList(): this { | |
| if (!Number.isSafeInteger(this.offsetValue) || this.offsetValue < 0) { | |
| throw new Error('QUERY_INVALID_OFFSET: offset must be a non-negative safe integer'); | |
| } | |
| if (this.limitValue && (!Number.isSafeInteger(this.limitValue) || this.limitValue < 1)) { | |
| throw new Error('QUERY_INVALID_LIMIT: limit must be a positive safe integer'); | |
| } | |
| this.applyListLimit(this.hardLimitValue); | |
| return this; | |
| } | |
| forExactCount(alias = '__teaql_total'): SelectQuery { | |
| const count = new SelectQuery(this.entity); | |
| count.filterCondition = snapshotQuery(this.filterCondition); | |
| count.commentText = this.commentText; | |
| count.purposeText = this.purposeText; | |
| count.aggregate('Count', 'id', alias); | |
| retainQueryDiagnosticOrigin(this, count); | |
| return count; | |
| } | |
| private applyListLimit(ceiling: number): void { | |
| if (!this.limitValue) this.limitValue = ceiling; | |
| if (this.limitValue > ceiling) { | |
| throw new Error(`QUERY_HARD_LIMIT_EXCEEDED: requested limit ${this.limitValue} exceeds hard limit ${ceiling}`); | |
| } | |
| for (const relation of this.relations) { | |
| relation.query?.applyListLimit(10_000); | |
| } | |
| } | |
| offset(offset: number): this { | |
| if (!Number.isSafeInteger(offset) || offset < 0) { | |
| throw new Error('QUERY_INVALID_OFFSET: offset must be a non-negative safe integer'); | |
| } | |
| this.offsetValue = offset; | |
| return this; | |
| } | |
| relation(name: string): this { | |
| this.relations.push({ name }); | |
| return this; | |
| } | |
| relationQuery( | |
| name: string, | |
| query: SelectQuery, | |
| localKey = 'id', | |
| foreignKey = 'id', | |
| many = true, | |
| operation?: QueryOperation, | |
| ): this { | |
| this.relations.push({ name, query, localKey, foreignKey, many, operation }); | |
| return this; | |
| } | |
| relationAggregate( | |
| relationName: string, | |
| alias: string, | |
| query: SelectQuery, | |
| singleResult = true, | |
| operation?: QueryOperation, | |
| ): this { | |
| this.relationAggregates.push({ relationName, alias, query, singleResult, operation }); | |
| return this; | |
| } | |
| order(orderBy: OrderBy): this { | |
| this.orderItems.push(orderBy); | |
| return this; | |
| } | |
| select(items: string[]): this { | |
| this.selectItems.push(...items); | |
| return this; | |
| } | |
| enableAggregationCacheFor(cacheExpiredMillis: number): this { | |
| this.aggregationCache = AggregationCacheOptions.enabled(cacheExpiredMillis); | |
| return this; | |
| } | |
| propagateAggregationCache(cacheExpiredMillis: number): this { | |
| if (!this.aggregationCache) { | |
| this.aggregationCache = AggregationCacheOptions.enabled(0); | |
| } | |
| this.aggregationCache.propagate(cacheExpiredMillis); | |
| return this; | |
| } | |
| aggregate(functionName: string, field: string, alias: string): this { | |
| this.aggregateItems.push({ function: functionName, field, alias }); | |
| return this; | |
| } | |
| groupBy(item: string): this { | |
| this.groupByItems.push(item); | |
| return this; | |
| } | |
| } | |
| export class MutationQuery { | |
| constructor( | |
| public entity: string, | |
| public action: "Create" | "Update" | "Delete", | |
| public payload: any, | |
| public id?: any, | |
| public comment?: string, | |
| public expectedVersion?: number, | |
| ) {} | |
| } | |