Comparing version 1.7.0 to 1.8.0
{ | ||
"name": "groq-js", | ||
"version": "1.7.0", | ||
"version": "1.8.0", | ||
"keywords": [ | ||
@@ -35,3 +35,2 @@ "sanity", | ||
"module": "./dist/index.mjs", | ||
"source": "./src/1.ts", | ||
"types": "./dist/index.d.ts", | ||
@@ -53,4 +52,3 @@ "typesVersions": { | ||
"scripts": { | ||
"prebuild": "npx rimraf dist", | ||
"build": "pkg build --strict && pkg --strict", | ||
"build": "pkg build --strict --check --clean", | ||
"prepublishOnly": "npm run build", | ||
@@ -71,8 +69,8 @@ "prettify": "prettier --write .", | ||
"devDependencies": { | ||
"@sanity/pkg-utils": "^5.1.5", | ||
"@sanity/pkg-utils": "6.7.1", | ||
"@sanity/semantic-release-preset": "^4.1.7", | ||
"@types/debug": "^4.1.12", | ||
"@types/tap": "^15.0.11", | ||
"@typescript-eslint/eslint-plugin": "^7.5.0", | ||
"@typescript-eslint/parser": "^7.5.0", | ||
"@typescript-eslint/eslint-plugin": "^7.7.0", | ||
"@typescript-eslint/parser": "^7.7.0", | ||
"eslint": "^8.57.0", | ||
@@ -82,11 +80,11 @@ "eslint-config-prettier": "^9.1.0", | ||
"eslint-plugin-prettier": "^5.1.3", | ||
"eslint-plugin-simple-import-sort": "^12.0.0", | ||
"eslint-plugin-simple-import-sort": "^12.1.0", | ||
"ndjson": "^2.0.0", | ||
"prettier": "^3.2.5", | ||
"prettier-plugin-packagejson": "^2.4.14", | ||
"prettier-plugin-packagejson": "^2.5.0", | ||
"rimraf": "^5.0.0", | ||
"semver": "^7.5.4", | ||
"tap": "^16.3.10", | ||
"tsx": "^4.7.1", | ||
"typescript": "^5.4.2" | ||
"tsx": "^4.7.2", | ||
"typescript": "5.4.5" | ||
}, | ||
@@ -96,5 +94,2 @@ "engines": { | ||
}, | ||
"publishConfig": { | ||
"provenance": true | ||
}, | ||
"dependencies": { | ||
@@ -101,0 +96,0 @@ "debug": "^4.3.4" |
@@ -1,3 +0,3 @@ | ||
import {ExprNode} from '../nodeTypes' | ||
import {NULL_VALUE, Value} from '../values' | ||
import type {ExprNode} from '../nodeTypes' | ||
import {NULL_VALUE, type Value} from '../values' | ||
import {evaluate} from './evaluate' | ||
@@ -4,0 +4,0 @@ import {Scope} from './scope' |
@@ -1,2 +0,2 @@ | ||
import {Value} from '../values' | ||
import type {Value} from '../values' | ||
@@ -3,0 +3,0 @@ export function isEqual(a: Value, b: Value): boolean { |
@@ -1,2 +0,2 @@ | ||
import {ExprNode, FuncCallNode, PipeFuncCallNode} from '../nodeTypes' | ||
import type {ExprNode, FuncCallNode, PipeFuncCallNode} from '../nodeTypes' | ||
import { | ||
@@ -9,3 +9,3 @@ FALSE_VALUE, | ||
TRUE_VALUE, | ||
Value, | ||
type Value, | ||
} from '../values' | ||
@@ -15,3 +15,3 @@ import {operators} from './operators' | ||
import {Scope} from './scope' | ||
import {EvaluateOptions, Executor} from './types' | ||
import type {EvaluateOptions, Executor} from './types' | ||
@@ -253,3 +253,3 @@ export function evaluate( | ||
const id = value.data._ref | ||
const id = value.data['_ref'] | ||
if (typeof id !== 'string') { | ||
@@ -264,3 +264,3 @@ return NULL_VALUE | ||
for await (const doc of scope.source) { | ||
if (doc.type === 'object' && id === doc.data._id) { | ||
if (doc.type === 'object' && id === doc.data['_id']) { | ||
return doc | ||
@@ -267,0 +267,0 @@ } |
@@ -15,3 +15,3 @@ import type {ExprNode} from '../nodeTypes' | ||
TRUE_VALUE, | ||
Value, | ||
type Value, | ||
} from '../values' | ||
@@ -22,3 +22,3 @@ import {totalCompare} from './ordering' | ||
import {evaluateScore} from './scoring' | ||
import {Executor} from './types' | ||
import type {Executor} from './types' | ||
@@ -88,9 +88,10 @@ function hasReference(value: any, pathSet: Set<string>): boolean { | ||
// eslint-disable-next-line require-await | ||
_global.anywhere = async function anywhere() { | ||
// eslint-disable-next-line require-await | ||
_global['anywhere'] = async function anywhere() { | ||
throw new Error('not implemented') | ||
} | ||
_global.anywhere.arity = 1 | ||
_global['anywhere'].arity = 1 | ||
_global.coalesce = async function coalesce(args, scope, execute) { | ||
_global['coalesce'] = async function coalesce(args, scope, execute) { | ||
for (const arg of args) { | ||
@@ -105,3 +106,3 @@ const value = await execute(arg, scope) | ||
_global.count = async function count(args, scope, execute) { | ||
_global['count'] = async function count(args, scope, execute) { | ||
const inner = await execute(args[0], scope) | ||
@@ -119,5 +120,5 @@ if (!inner.isArray()) { | ||
} | ||
_global.count.arity = 1 | ||
_global['count'].arity = 1 | ||
_global.dateTime = async function dateTime(args, scope, execute) { | ||
_global['dateTime'] = async function dateTime(args, scope, execute) { | ||
const val = await execute(args[0], scope) | ||
@@ -132,17 +133,18 @@ if (val.type === 'datetime') { | ||
} | ||
_global.dateTime.arity = 1 | ||
_global['dateTime'].arity = 1 | ||
_global.defined = async function defined(args, scope, execute) { | ||
_global['defined'] = async function defined(args, scope, execute) { | ||
const inner = await execute(args[0], scope) | ||
return inner.type === 'null' ? FALSE_VALUE : TRUE_VALUE | ||
} | ||
_global.defined.arity = 1 | ||
_global['defined'].arity = 1 | ||
// eslint-disable-next-line require-await | ||
_global.identity = async function identity(args, scope) { | ||
// eslint-disable-next-line require-await | ||
_global['identity'] = async function identity(_args, scope) { | ||
return fromString(scope.context.identity) | ||
} | ||
_global.identity.arity = 0 | ||
_global['identity'].arity = 0 | ||
_global.length = async function length(args, scope, execute) { | ||
_global['length'] = async function length(args, scope, execute) { | ||
const inner = await execute(args[0], scope) | ||
@@ -165,5 +167,5 @@ | ||
} | ||
_global.length.arity = 1 | ||
_global['length'].arity = 1 | ||
_global.path = async function path(args, scope, execute) { | ||
_global['path'] = async function path(args, scope, execute) { | ||
const inner = await execute(args[0], scope) | ||
@@ -176,5 +178,5 @@ if (inner.type !== 'string') { | ||
} | ||
_global.path.arity = 1 | ||
_global['path'].arity = 1 | ||
_global.string = async function string(args, scope, execute) { | ||
_global['string'] = async function string(args, scope, execute) { | ||
const value = await execute(args[0], scope) | ||
@@ -191,5 +193,5 @@ switch (value.type) { | ||
} | ||
_global.string.arity = 1 | ||
_global['string'].arity = 1 | ||
_global.references = async function references(args, scope, execute) { | ||
_global['references'] = async function references(args, scope, execute) { | ||
const pathSet = new Set<string>() | ||
@@ -216,5 +218,5 @@ for (const arg of args) { | ||
} | ||
_global.references.arity = (c) => c >= 1 | ||
_global['references'].arity = (c) => c >= 1 | ||
_global.round = async function round(args, scope, execute) { | ||
_global['round'] = async function round(args, scope, execute) { | ||
const value = await execute(args[0], scope) | ||
@@ -246,12 +248,14 @@ if (value.type !== 'number') { | ||
} | ||
_global.round.arity = (count) => count >= 1 && count <= 2 | ||
_global['round'].arity = (count) => count >= 1 && count <= 2 | ||
// eslint-disable-next-line require-await | ||
_global.now = async function now(args, scope) { | ||
// eslint-disable-next-line require-await | ||
_global['now'] = async function now(_args, scope) { | ||
return fromString(scope.context.timestamp.toISOString()) | ||
} | ||
_global.now.arity = 0 | ||
_global['now'].arity = 0 | ||
// eslint-disable-next-line require-await | ||
_global.boost = async function boost() { | ||
// eslint-disable-next-line require-await | ||
_global['boost'] = async function boost() { | ||
// This should be handled by the scoring function. | ||
@@ -261,7 +265,7 @@ throw new Error('unexpected boost call') | ||
_global.boost.arity = 2 | ||
_global['boost'].arity = 2 | ||
const string: FunctionSet = {} | ||
string.lower = async function (args, scope, execute) { | ||
string['lower'] = async function (args, scope, execute) { | ||
const value = await execute(args[0], scope) | ||
@@ -275,5 +279,5 @@ | ||
} | ||
string.lower.arity = 1 | ||
string['lower'].arity = 1 | ||
string.upper = async function (args, scope, execute) { | ||
string['upper'] = async function (args, scope, execute) { | ||
const value = await execute(args[0], scope) | ||
@@ -287,5 +291,5 @@ | ||
} | ||
string.upper.arity = 1 | ||
string['upper'].arity = 1 | ||
string.split = async function (args, scope, execute) { | ||
string['split'] = async function (args, scope, execute) { | ||
const str = await execute(args[0], scope) | ||
@@ -309,8 +313,8 @@ if (str.type !== 'string') { | ||
} | ||
string.split.arity = 2 | ||
string['split'].arity = 2 | ||
_global.lower = string.lower | ||
_global.upper = string.upper | ||
_global['lower'] = string['lower'] | ||
_global['upper'] = string['upper'] | ||
string.startsWith = async function (args, scope, execute) { | ||
string['startsWith'] = async function (args, scope, execute) { | ||
const str = await execute(args[0], scope) | ||
@@ -328,7 +332,7 @@ if (str.type !== 'string') { | ||
} | ||
string.startsWith.arity = 2 | ||
string['startsWith'].arity = 2 | ||
const array: FunctionSet = {} | ||
array.join = async function (args, scope, execute) { | ||
array['join'] = async function (args, scope, execute) { | ||
const arr = await execute(args[0], scope) | ||
@@ -362,5 +366,5 @@ if (!arr.isArray()) { | ||
} | ||
array.join.arity = 2 | ||
array['join'].arity = 2 | ||
array.compact = async function (args, scope, execute) { | ||
array['compact'] = async function (args, scope, execute) { | ||
const arr = await execute(args[0], scope) | ||
@@ -379,5 +383,5 @@ if (!arr.isArray()) { | ||
} | ||
array.compact.arity = 1 | ||
array['compact'].arity = 1 | ||
array.unique = async function (args, scope, execute) { | ||
array['unique'] = async function (args, scope, execute) { | ||
const value = await execute(args[0], scope) | ||
@@ -407,6 +411,6 @@ if (!value.isArray()) { | ||
} | ||
array.unique.arity = 1 | ||
array['unique'].arity = 1 | ||
const pt: FunctionSet = {} | ||
pt.text = async function (args, scope, execute) { | ||
pt['text'] = async function (args, scope, execute) { | ||
const value = await execute(args[0], scope) | ||
@@ -422,7 +426,8 @@ const text = await portableTextContent(value) | ||
pt.text.arity = 1 | ||
pt['text'].arity = 1 | ||
const sanity: FunctionSet = {} | ||
// eslint-disable-next-line require-await | ||
sanity.projectId = async function (args, scope) { | ||
// eslint-disable-next-line require-await | ||
sanity['projectId'] = async function (_args, scope) { | ||
if (scope.context.sanity) { | ||
@@ -435,3 +440,4 @@ return fromString(scope.context.sanity.projectId) | ||
// eslint-disable-next-line require-await | ||
sanity.dataset = async function (args, scope) { | ||
// eslint-disable-next-line require-await | ||
sanity['dataset'] = async function (_args, scope) { | ||
if (scope.context.sanity) { | ||
@@ -453,3 +459,3 @@ return fromString(scope.context.sanity.dataset) | ||
pipeFunctions.order = async function order(base, args, scope, execute) { | ||
pipeFunctions['order'] = async function order(base, args, scope, execute) { | ||
// eslint-disable-next-line max-len | ||
@@ -512,6 +518,7 @@ // This is a workaround for https://github.com/rpetrich/babel-plugin-transform-async-to-promises/issues/59 | ||
} | ||
pipeFunctions.order.arity = (count) => count >= 1 | ||
pipeFunctions['order'].arity = (count) => count >= 1 | ||
// eslint-disable-next-line require-await | ||
pipeFunctions.score = async function score(base, args, scope, execute) { | ||
// eslint-disable-next-line require-await | ||
pipeFunctions['score'] = async function score(base, args, scope, execute) { | ||
if (!base.isArray()) return NULL_VALUE | ||
@@ -530,3 +537,3 @@ | ||
const newScope = scope.createNested(value) | ||
let valueScore = typeof value.data._score === 'number' ? value.data._score : 0 | ||
let valueScore = typeof value.data['_score'] === 'number' ? value.data['_score'] : 0 | ||
@@ -545,3 +552,3 @@ for (const arg of args) { | ||
pipeFunctions.score.arity = (count) => count >= 1 | ||
pipeFunctions['score'].arity = (count) => count >= 1 | ||
@@ -552,3 +559,4 @@ type ObjectWithScore = Record<string, unknown> & {_score: number} | ||
// eslint-disable-next-line require-await | ||
delta.operation = async function (args, scope) { | ||
// eslint-disable-next-line require-await | ||
delta['operation'] = async function (_args, scope) { | ||
const hasBefore = scope.context.before !== null | ||
@@ -572,27 +580,27 @@ const hasAfter = scope.context.after !== null | ||
delta.changedAny = () => { | ||
delta['changedAny'] = () => { | ||
throw new Error('not implemented') | ||
} | ||
delta.changedAny.arity = 1 | ||
delta.changedAny.mode = 'delta' | ||
delta['changedAny'].arity = 1 | ||
delta['changedAny'].mode = 'delta' | ||
delta.changedOnly = () => { | ||
delta['changedOnly'] = () => { | ||
throw new Error('not implemented') | ||
} | ||
delta.changedOnly.arity = 1 | ||
delta.changedOnly.mode = 'delta' | ||
delta['changedOnly'].arity = 1 | ||
delta['changedOnly'].mode = 'delta' | ||
const diff: FunctionSet = {} | ||
diff.changedAny = () => { | ||
diff['changedAny'] = () => { | ||
throw new Error('not implemented') | ||
} | ||
diff.changedAny.arity = 3 | ||
diff['changedAny'].arity = 3 | ||
diff.changedOnly = () => { | ||
diff['changedOnly'] = () => { | ||
throw new Error('not implemented') | ||
} | ||
diff.changedOnly.arity = 3 | ||
diff['changedOnly'].arity = 3 | ||
const math: FunctionSet = {} | ||
math.min = async function (args, scope, execute) { | ||
math['min'] = async function (args, scope, execute) { | ||
const arr = await execute(args[0], scope) | ||
@@ -615,5 +623,5 @@ if (!arr.isArray()) { | ||
} | ||
math.min.arity = 1 | ||
math['min'].arity = 1 | ||
math.max = async function (args, scope, execute) { | ||
math['max'] = async function (args, scope, execute) { | ||
const arr = await execute(args[0], scope) | ||
@@ -636,5 +644,5 @@ if (!arr.isArray()) { | ||
} | ||
math.max.arity = 1 | ||
math['max'].arity = 1 | ||
math.sum = async function (args, scope, execute) { | ||
math['sum'] = async function (args, scope, execute) { | ||
const arr = await execute(args[0], scope) | ||
@@ -655,5 +663,5 @@ if (!arr.isArray()) { | ||
} | ||
math.sum.arity = 1 | ||
math['sum'].arity = 1 | ||
math.avg = async function (args, scope, execute) { | ||
math['avg'] = async function (args, scope, execute) { | ||
const arr = await execute(args[0], scope) | ||
@@ -679,9 +687,9 @@ if (!arr.isArray()) { | ||
} | ||
math.avg.arity = 1 | ||
math['avg'].arity = 1 | ||
const dateTime: FunctionSet = {} | ||
dateTime.now = async function now(args, scope, execute) { | ||
dateTime['now'] = async function now(_args, scope) { | ||
return fromDateTime(new DateTime(scope.context.timestamp)) | ||
} | ||
dateTime.now.arity = 0 | ||
dateTime['now'].arity = 0 | ||
@@ -688,0 +696,0 @@ export const namespaces: NamespaceSet = { |
@@ -1,2 +0,2 @@ | ||
import {Value} from '../values' | ||
import type {Value} from '../values' | ||
@@ -3,0 +3,0 @@ const CHARS = /([^!@#$%^&*(),\\/?";:{}|[\]+<>\s-])+/g |
@@ -1,2 +0,2 @@ | ||
import {OpCall} from '../nodeTypes' | ||
import type {OpCall} from '../nodeTypes' | ||
import { | ||
@@ -11,6 +11,13 @@ FALSE_VALUE, | ||
TRUE_VALUE, | ||
Value, | ||
type Value, | ||
} from '../values' | ||
import {isEqual} from './equality' | ||
import {gatherText, matchAnalyzePattern, matchText, matchTokenize, Pattern, Token} from './matching' | ||
import { | ||
gatherText, | ||
matchAnalyzePattern, | ||
matchText, | ||
matchTokenize, | ||
type Pattern, | ||
type Token, | ||
} from './matching' | ||
import {partialCompare} from './ordering' | ||
@@ -17,0 +24,0 @@ |
@@ -1,2 +0,2 @@ | ||
import {getType, GroqType} from '../values' | ||
import {getType, type GroqType} from '../values' | ||
@@ -3,0 +3,0 @@ const TYPE_ORDER: {[key in GroqType]?: number} = { |
@@ -1,2 +0,2 @@ | ||
import {Value} from '../values' | ||
import type {Value} from '../values' | ||
@@ -30,4 +30,4 @@ export async function portableTextContent(value: Value): Promise<string | null> { | ||
function blockText(obj: Record<string, unknown>): string | null { | ||
if (typeof obj._type !== 'string') return null | ||
const children = obj.children | ||
if (typeof obj['_type'] !== 'string') return null | ||
const children = obj['children'] | ||
if (!Array.isArray(children)) return null | ||
@@ -34,0 +34,0 @@ |
@@ -1,3 +0,3 @@ | ||
import {Value} from '../values' | ||
import {Context} from './types' | ||
import type {Value} from '../values' | ||
import type {Context} from './types' | ||
@@ -4,0 +4,0 @@ export class Scope { |
@@ -1,5 +0,5 @@ | ||
import {ExprNode} from '../nodeTypes' | ||
import {gatherText, matchPatternRegex, matchTokenize, Token} from './matching' | ||
import type {ExprNode} from '../nodeTypes' | ||
import {gatherText, matchPatternRegex, matchTokenize, type Token} from './matching' | ||
import {Scope} from './scope' | ||
import {Executor} from './types' | ||
import type {Executor} from './types' | ||
@@ -6,0 +6,0 @@ // BM25 similarity constants |
@@ -1,3 +0,3 @@ | ||
import {ExprNode} from '../nodeTypes' | ||
import {Value} from '../values' | ||
import type {ExprNode} from '../nodeTypes' | ||
import type {Value} from '../values' | ||
import {Scope} from './scope' | ||
@@ -4,0 +4,0 @@ |
@@ -1,2 +0,2 @@ | ||
import {ParseOptions} from './types' | ||
import type {ParseOptions} from './types' | ||
@@ -3,0 +3,0 @@ export type MarkName = |
/* eslint-disable camelcase */ | ||
import {tryConstantEvaluate} from './evaluator' | ||
import {GroqFunctionArity, namespaces, pipeFunctions} from './evaluator/functions' | ||
import {Mark, MarkProcessor, MarkVisitor} from './markProcessor' | ||
import { | ||
import {type GroqFunctionArity, namespaces, pipeFunctions} from './evaluator/functions' | ||
import {type Mark, MarkProcessor, type MarkVisitor} from './markProcessor' | ||
import type { | ||
ArrayElementNode, | ||
@@ -17,3 +17,3 @@ ExprNode, | ||
import { | ||
TraversalResult, | ||
type TraversalResult, | ||
traverseArray, | ||
@@ -24,3 +24,3 @@ traverseElement, | ||
} from './traversal' | ||
import {ParseOptions} from './types' | ||
import type {ParseOptions} from './types' | ||
@@ -47,3 +47,3 @@ type EscapeSequences = "'" | '"' | '\\' | '/' | 'b' | 'f' | 'n' | 'r' | 't' | ||
class GroqQueryError extends Error { | ||
public name = 'GroqQueryError' | ||
public override name = 'GroqQueryError' | ||
} | ||
@@ -486,3 +486,3 @@ | ||
pair(p) { | ||
pair() { | ||
throw new GroqQueryError(`unexpected =>`) | ||
@@ -519,7 +519,7 @@ }, | ||
asc(p) { | ||
asc() { | ||
throw new GroqQueryError('unexpected asc') | ||
}, | ||
desc(p) { | ||
desc() { | ||
throw new GroqQueryError('unexpected desc') | ||
@@ -684,3 +684,3 @@ }, | ||
array_postfix(p) { | ||
array_postfix() { | ||
return (right) => traverseArray((base) => ({type: 'ArrayCoerce', base}), right) | ||
@@ -708,3 +708,3 @@ }, | ||
dblparent(p) { | ||
dblparent() { | ||
throw new Error('Invalid selector syntax') | ||
@@ -728,67 +728,67 @@ }, | ||
neg(p) { | ||
neg() { | ||
throw new Error('Invalid selector syntax') | ||
}, | ||
pos(p) { | ||
pos() { | ||
throw new Error('Invalid selector syntax') | ||
}, | ||
add(p) { | ||
add() { | ||
throw new Error('Invalid selector syntax') | ||
}, | ||
sub(p) { | ||
sub() { | ||
throw new Error('Invalid selector syntax') | ||
}, | ||
mul(p) { | ||
mul() { | ||
throw new Error('Invalid selector syntax') | ||
}, | ||
div(p) { | ||
div() { | ||
throw new Error('Invalid selector syntax') | ||
}, | ||
mod(p) { | ||
mod() { | ||
throw new Error('Invalid selector syntax') | ||
}, | ||
pow(p) { | ||
pow() { | ||
throw new Error('Invalid selector syntax') | ||
}, | ||
comp(p) { | ||
comp() { | ||
throw new Error('Invalid selector syntax') | ||
}, | ||
in_range(p) { | ||
in_range() { | ||
throw new Error('Invalid selector syntax') | ||
}, | ||
str(p) { | ||
str() { | ||
throw new Error('Invalid selector syntax') | ||
}, | ||
integer(p) { | ||
integer() { | ||
throw new Error('Invalid selector syntax') | ||
}, | ||
float(p) { | ||
float() { | ||
throw new Error('Invalid selector syntax') | ||
}, | ||
sci(p) { | ||
sci() { | ||
throw new Error('Invalid selector syntax') | ||
}, | ||
object(p) { | ||
object() { | ||
throw new Error('Invalid selector syntax') | ||
}, | ||
array(p) { | ||
array() { | ||
throw new Error('Invalid selector syntax') | ||
}, | ||
tuple(p) { | ||
tuple() { | ||
// This should only throw an error until we add support for tuples in selectors. | ||
@@ -799,3 +799,3 @@ throw new Error('Invalid selector syntax') | ||
func_call(p, mark) { | ||
const func = EXPR_BUILDER.func_call(p, mark) as FuncCallNode | ||
const func = EXPR_BUILDER['func_call'](p, mark) as FuncCallNode | ||
if (func.name === 'anywhere' && func.args.length === 1) return null | ||
@@ -806,31 +806,31 @@ | ||
pipecall(p) { | ||
pipecall() { | ||
throw new Error('Invalid selector syntax') | ||
}, | ||
pair(p) { | ||
pair() { | ||
throw new Error('Invalid selector syntax') | ||
}, | ||
and(p) { | ||
and() { | ||
throw new Error('Invalid selector syntax') | ||
}, | ||
or(p) { | ||
or() { | ||
throw new Error('Invalid selector syntax') | ||
}, | ||
not(p) { | ||
not() { | ||
throw new Error('Invalid selector syntax') | ||
}, | ||
asc(p) { | ||
asc() { | ||
throw new Error('Invalid selector syntax') | ||
}, | ||
desc(p) { | ||
desc() { | ||
throw new Error('Invalid selector syntax') | ||
}, | ||
param(p) { | ||
param() { | ||
throw new Error('Invalid selector syntax') | ||
@@ -882,3 +882,3 @@ }, | ||
public position: number | ||
public name = 'GroqSyntaxError' | ||
public override name = 'GroqSyntaxError' | ||
@@ -885,0 +885,0 @@ constructor(position: number) { |
/* eslint-disable camelcase */ | ||
import {ExprNode} from './nodeTypes' | ||
import type {ExprNode} from './nodeTypes' | ||
@@ -4,0 +4,0 @@ export type Traversal = (base: ExprNode) => ExprNode |
@@ -1,5 +0,6 @@ | ||
import {FuncCallNode} from '../nodeTypes' | ||
import type {FuncCallNode} from '../nodeTypes' | ||
import {Scope} from './scope' | ||
import {walk} from './typeEvaluate' | ||
import {NullTypeNode, TypeNode} from './types' | ||
import {mapConcrete} from './typeHelpers' | ||
import type {NullTypeNode, TypeNode} from './types' | ||
@@ -43,2 +44,34 @@ function unionWithoutNull(unionTypeNode: TypeNode): TypeNode { | ||
} | ||
case 'global.count': { | ||
const arg = walk({node: node.args[0], scope}) | ||
return mapConcrete(arg, scope, (arg) => { | ||
if (arg.type === 'array') { | ||
return {type: 'number'} | ||
} | ||
return {type: 'null'} satisfies NullTypeNode | ||
}) | ||
} | ||
case 'global.string': { | ||
const arg = walk({node: node.args[0], scope}) | ||
return mapConcrete(arg, scope, (node) => { | ||
if (node.type === 'string' || node.type === 'number' || node.type === 'boolean') { | ||
if (node.value) { | ||
return { | ||
type: 'string', | ||
value: node.value.toString(), | ||
} | ||
} | ||
return { | ||
type: 'string', | ||
} | ||
} | ||
return {type: 'null'} | ||
}) | ||
} | ||
case 'pt.text': { | ||
@@ -45,0 +78,0 @@ if (node.args.length === 0) { |
@@ -1,2 +0,2 @@ | ||
import {TypeNode} from './types' | ||
import type {TypeNode} from './types' | ||
@@ -3,0 +3,0 @@ export function hashField(field: TypeNode): string { |
@@ -1,2 +0,2 @@ | ||
import {TypeNode} from './types' | ||
import type {TypeNode} from './types' | ||
@@ -3,0 +3,0 @@ /** |
import debug from 'debug' | ||
import {InlineTypeNode, NullTypeNode, Schema, TypeNode, UnionTypeNode} from './types' | ||
import type {InlineTypeNode, NullTypeNode, Schema, TypeNode, UnionTypeNode} from './types' | ||
@@ -5,0 +5,0 @@ const $trace = debug('typeEvaluator:scope:trace') |
@@ -50,3 +50,3 @@ import debug from 'debug' | ||
} from './types' | ||
import {nullUnion} from './typeHelpers' | ||
import {mapConcrete, nullUnion} from './typeHelpers' | ||
@@ -455,10 +455,16 @@ const $trace = debug('typeEvaluator:evaluate:trace') | ||
$trace('filter.resolving %O', base) | ||
const resolved = resolveFilter(node.expr, createFilterScope(base, scope)) | ||
$trace('filter.resolved %O', resolved) | ||
return mapConcrete(base, scope, (base) => { | ||
$trace('filter.resolving %O', base) | ||
if (base.type === 'null') { | ||
return base | ||
} | ||
return { | ||
type: 'array', | ||
of: resolved, | ||
} | ||
const resolved = resolveFilter(node.expr, createFilterScope(base, scope)) | ||
$trace('filter.resolved %O', resolved) | ||
return { | ||
type: 'array', | ||
of: resolved, | ||
} | ||
}) | ||
} | ||
@@ -791,8 +797,13 @@ | ||
if (left.type === 'union' && isPrimitiveTypeNode(right)) { | ||
return left.of.some((node) => { | ||
if (isPrimitiveTypeNode(node)) { | ||
return node.value === undefined || right.value === undefined || node.value === right.value | ||
for (const node of left.of) { | ||
// both are primitive types, and their values are equal, we can return true | ||
if (isPrimitiveTypeNode(node) && node.value === right.value) { | ||
return true | ||
} | ||
return false | ||
}) | ||
// both are the same type, but the value is undefined, we can't determine the result | ||
if (isPrimitiveTypeNode(node) && node.value === undefined) { | ||
return undefined | ||
} | ||
} | ||
} | ||
@@ -1056,3 +1067,3 @@ if (left.type !== right.type) { | ||
// eslint-disable-next-line complexity, max-statements | ||
function resolveFilter(expr: ExprNode, scope: Scope): TypeNode { | ||
function resolveFilter(expr: ExprNode, scope: Scope): UnionTypeNode { | ||
$trace('resolveFilter.expr %O', expr) | ||
@@ -1082,50 +1093,2 @@ const filtered = scope.value.of.filter( | ||
type ConcreteTypeNode = | ||
| BooleanTypeNode | ||
| NullTypeNode | ||
| NumberTypeNode | ||
| StringTypeNode | ||
| ArrayTypeNode | ||
| ObjectTypeNode | ||
/** | ||
* mapConcrete extracts a _concrete type_ from a type node, applies the mapping | ||
* function to it and returns. Most notably, this will work through unions | ||
* (applying the mapping function for each variant) and inline (resolving the | ||
* reference). | ||
* | ||
* An `unknown` input type causes it to return `unknown` as well. | ||
* | ||
* After encountering unions the resulting types gets passed into `mergeUnions`. | ||
* By default this will just union them together again. | ||
*/ | ||
function mapConcrete( | ||
node: TypeNode, | ||
scope: Scope, | ||
mapper: (node: ConcreteTypeNode) => TypeNode, | ||
mergeUnions: (nodes: TypeNode[]) => TypeNode = (nodes) => | ||
optimizeUnions({type: 'union', of: nodes}), | ||
): TypeNode { | ||
switch (node.type) { | ||
case 'boolean': | ||
case 'array': | ||
case 'null': | ||
case 'object': | ||
case 'string': | ||
case 'number': | ||
return mapper(node) | ||
case 'unknown': | ||
return node | ||
case 'union': | ||
return mergeUnions(node.of.map((inner) => mapConcrete(inner, scope, mapper), mergeUnions)) | ||
case 'inline': { | ||
const resolvedInline = scope.context.lookupTypeDeclaration(node) | ||
return mapConcrete(resolvedInline, scope, mapper, mergeUnions) | ||
} | ||
default: | ||
// @ts-expect-error | ||
throw new Error(`Unknown type: ${node.type}`) | ||
} | ||
} | ||
function mapArray( | ||
@@ -1132,0 +1095,0 @@ node: TypeNode, |
@@ -1,2 +0,14 @@ | ||
import type {ObjectAttribute, ObjectTypeNode, TypeNode, UnionTypeNode} from './types' | ||
import {optimizeUnions} from './optimizations' | ||
import type {Scope} from './scope' | ||
import type { | ||
ArrayTypeNode, | ||
BooleanTypeNode, | ||
NullTypeNode, | ||
NumberTypeNode, | ||
ObjectAttribute, | ||
ObjectTypeNode, | ||
StringTypeNode, | ||
TypeNode, | ||
UnionTypeNode, | ||
} from './types' | ||
@@ -36,3 +48,3 @@ /** | ||
if (inArray) { | ||
attributes._key = { | ||
attributes['_key'] = { | ||
type: 'objectAttribute', | ||
@@ -65,1 +77,49 @@ value: { | ||
} | ||
type ConcreteTypeNode = | ||
| BooleanTypeNode | ||
| NullTypeNode | ||
| NumberTypeNode | ||
| StringTypeNode | ||
| ArrayTypeNode | ||
| ObjectTypeNode | ||
/** | ||
* mapConcrete extracts a _concrete type_ from a type node, applies the mapping | ||
* function to it and returns. Most notably, this will work through unions | ||
* (applying the mapping function for each variant) and inline (resolving the | ||
* reference). | ||
* | ||
* An `unknown` input type causes it to return `unknown` as well. | ||
* | ||
* After encountering unions the resulting types gets passed into `mergeUnions`. | ||
* By default this will just union them together again. | ||
*/ | ||
export function mapConcrete( | ||
node: TypeNode, | ||
scope: Scope, | ||
mapper: (node: ConcreteTypeNode) => TypeNode, | ||
mergeUnions: (nodes: TypeNode[]) => TypeNode = (nodes) => | ||
optimizeUnions({type: 'union', of: nodes}), | ||
): TypeNode { | ||
switch (node.type) { | ||
case 'boolean': | ||
case 'array': | ||
case 'null': | ||
case 'object': | ||
case 'string': | ||
case 'number': | ||
return mapper(node) | ||
case 'unknown': | ||
return node | ||
case 'union': | ||
return mergeUnions(node.of.map((inner) => mapConcrete(inner, scope, mapper), mergeUnions)) | ||
case 'inline': { | ||
const resolvedInline = scope.context.lookupTypeDeclaration(node) | ||
return mapConcrete(resolvedInline, scope, mapper, mergeUnions) | ||
} | ||
default: | ||
// @ts-expect-error - all types should be handled | ||
throw new Error(`Unknown type: ${node.type}`) | ||
} | ||
} |
import {formatRFC3339, parseRFC3339} from './dateHelpers' | ||
import {Path} from './Path' | ||
import {StreamValue} from './StreamValue' | ||
import {BooleanValue, GroqType, NullValue, Value} from './types' | ||
import type {BooleanValue, GroqType, NullValue, Value} from './types' | ||
@@ -6,0 +6,0 @@ export class StaticValue<P, T extends GroqType> { |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
887391
12572