New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@dangreaves/groq-query-builder

Package Overview
Dependencies
Maintainers
1
Versions
32
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@dangreaves/groq-query-builder - npm Package Compare versions

Comparing version 0.1.0 to 0.2.0

101

dist/index.d.ts

@@ -0,3 +1,4 @@

import { Logger } from 'pino';
import * as _sinclair_typebox from '@sinclair/typebox';
import { TProperties, TObject, TLiteral, TUnion, TArray, Static, TSchema } from '@sinclair/typebox';
import { TSchema, TProperties, TObject, TLiteral, TUnion, TArray, Static, TNull } from '@sinclair/typebox';
export { Array, Boolean, Literal, Number, String, Unknown } from '@sinclair/typebox';

@@ -9,2 +10,3 @@

declare const KindSymbol: unique symbol;
declare const AliasLiteralSymbol: unique symbol;
declare const NeedExpansionSymbol: unique symbol;

@@ -17,2 +19,3 @@ declare const SupportsExpansionSymbol: unique symbol;

declare enum Kind {
Alias = "Alias",
Projection = "Projection",

@@ -24,2 +27,22 @@ TypedProjection = "TypedProjection",

/**
* Alias to another attribute or literal.
* @example {foo:"literal"}
*/
type TAlias<T extends TSchema = TSchema> = T & Expandable & {
[KindSymbol]: Kind.Alias;
[AliasLiteralSymbol]: string;
};
/**
* Create an alias.
*/
declare function Alias<T extends TSchema>(schema: T, aliasLiteral: string): TAlias<T>;
/**
* Return true if this is an alias schema.
*/
declare function isAlias(schema: unknown): schema is TAlias;
/**
* Return the alias literal from the given alias.
*/
declare function getAliasLiteral(schema: TAlias): string;
/**
* Simple projection of attributes.

@@ -63,7 +86,14 @@ * @example foo{title,description}

/**
* Fallback schema for union projections.
*/
declare const unionFallbackProjection: TProjection<{
_type: TAlias<TLiteral<"unknown">>;
_rawType: TAlias<_sinclair_typebox.TString>;
}>;
/**
* Projection which uses the select operator to return a union of typed projections.
* @see https://www.sanity.io/docs/query-cheat-sheet#64a36d80be73
* @example "foo": select(_type == "person" => {name},_type == "company" => {companyName})
* @example foo{...select(_type == "person" => {name},_type == "company" => {companyName})}
*/
interface TUnionProjection<T extends TTypedProjection[] = TTypedProjection[]> extends TUnion<T>, Expandable {
interface TUnionProjection<T extends TTypedProjection[] = TTypedProjection[]> extends TUnion<[...T, typeof unionFallbackProjection]>, Expandable {
[KindSymbol]: Kind.UnionProjection;

@@ -83,3 +113,3 @@ }

*/
interface TCollection<T extends TProperties = TProperties> extends TArray<TProjection<T>>, Expandable {
interface TCollection<T extends TSchema = TSchema> extends TArray<T>, Expandable {
[KindSymbol]: Kind.Collection;

@@ -90,3 +120,3 @@ }

*/
declare function Collection<T extends TProperties>(properties: T): TCollection<T>;
declare function Collection<T extends TSchema>(projection: T): TCollection<T>;
/**

@@ -129,16 +159,2 @@ * Return true if this is a collection schema.

/**
* Infer result type from the given query.
*/
type InferFromQuery<Q extends BaseQuery<any>> = Q extends ArrayQuery<infer S> ? Static<TArray<S>> : Q extends BaseQuery<infer S> ? Static<S> : never;
/**
* Selection of fields to grab.
*/
type Selection = Record<string, TSchema> | TProjection | TTypedProjection | TUnionProjection;
/**
* Infer schema type from a given selection.
* If selection is a raw object it will be wrapped with a Projection.
*/
type InferSchemaFromSelection<T extends Selection> = T extends TSchema ? T : TProjection<T>;
type Slice = {

@@ -148,15 +164,6 @@ from: number;

};
type ConditionGroup = {
mode: "&&" | "||";
conditions: Condition[];
};
type Condition = {
key: string;
operator: "==" | "!=";
value: string;
};
type QueryPayload<T extends TSchema> = {
schema: T;
slice?: Slice;
conditionGroups?: ConditionGroup[];
filters: string[];
};

@@ -175,5 +182,5 @@ declare abstract class BaseQuery<T extends TSchema> {

/**
* Serialize the given projection schema to a GROQ string.
* Recursively serialize the given schema to a GROQ projection string.
*/
protected serializeProjection(schema: TObject): string;
protected serializeProjection(schema: TSchema): string;
/**

@@ -196,3 +203,7 @@ * Serialize the given union projection to a GROQ select conditions string.

*/
grab<S extends Selection>(selection: S): EntityQuery<InferSchemaFromSelection<S>>;
grab<T extends TSchema>(schema: T): EntityQuery<T>;
/**
* Add filter condition.
*/
filter(filterCondition: string): EntityQuery<T>;
}

@@ -207,4 +218,14 @@ declare class ArrayQuery<T extends TSchema> extends BaseQuery<T> {

*/
grab<S extends Selection>(selection: S): ArrayQuery<InferSchemaFromSelection<S>>;
grab<T extends TSchema>(schema: T): ArrayQuery<T>;
/**
* Add filter condition.
*/
filter(filterCondition: string): EntityQuery<T>;
/**
* Slice the collection.
*/
slice(from: Slice["from"], to?: Slice["to"]): EntityQuery<T>;
/**
* Return only the first result.
*/
first(): EntityQuery<T>;

@@ -215,6 +236,14 @@ }

*/
declare function filterByType(type: string): ArrayQuery<_sinclair_typebox.TUnknown>;
declare function filterByType(type: string, additionalFilter?: string): ArrayQuery<_sinclair_typebox.TUnknown>;
declare function makeSafeSanityFetch(fn: (query: string, params?: Record<string, string>) => Promise<any>): <T extends BaseQuery<any>>(query: T, params?: Record<string, string>) => Promise<InferFromQuery<T>>;
/**
* Infer result type from the given query.
*/
type InferFromQuery<Q extends BaseQuery<any>> = Q extends ArrayQuery<infer S> ? Static<TArray<S>> : Q extends BaseQuery<infer S> ? Static<TUnion<[S, TNull]>> : never;
export { ArrayQuery, BaseQuery, Collection, type Condition, type ConditionGroup, ConditionalExpand, EntityQuery, Expand, type InferFromQuery, type InferSchemaFromSelection, Projection, type QueryPayload, type Selection, type Slice, type TCollection, type TProjection, type TTypedProjection, type TUnionProjection, TypedProjection, UnionProjection, filterByType, getConditionalExpansionType, isCollection, isExpandable, isProjection, isTypedProjection, isUnionProjection, makeSafeSanityFetch, needsExpansion };
declare function makeSafeSanityFetch(fn: (query: string, params?: Record<string, string>) => Promise<any>, { logger, validationMode, }?: {
logger?: Logger;
validationMode?: "ERROR" | "WARN";
}): <T extends BaseQuery<any>>(query: T, params?: Record<string, string>) => Promise<InferFromQuery<T>>;
export { Alias, ArrayQuery, BaseQuery, Collection, ConditionalExpand, EntityQuery, Expand, type InferFromQuery, Projection, type QueryPayload, type Slice, type TAlias, type TCollection, type TProjection, type TTypedProjection, type TUnionProjection, TypedProjection, UnionProjection, filterByType, getAliasLiteral, getConditionalExpansionType, isAlias, isCollection, isExpandable, isProjection, isTypedProjection, isUnionProjection, makeSafeSanityFetch, needsExpansion };
// src/client.ts
import { Value, TransformDecodeCheckError } from "@sinclair/typebox/value";
function makeSafeSanityFetch(fn) {
import { pino } from "pino";
import { Type } from "@sinclair/typebox";
import { Value } from "@sinclair/typebox/value";
function makeSafeSanityFetch(fn, {
logger = pino(),
validationMode = "ERROR"
} = {}) {
return async function fetchSanity(query, params) {
const groq = query.serialize();
logger.debug({ query: groq }, "Sending GROQ query.");
const resultSchema = Type.Union([query.resolveSchema(), Type.Null()]);
const res = params ? await fn(groq, params) : await fn(groq);
try {
return Value.Decode(query.resolveSchema(), res);
} catch (e) {
if (e instanceof TransformDecodeCheckError) {
throw new GroqValidationError(e);
logger.debug({ res }, "Received GROQ response.");
const result = Value.Convert(resultSchema, res);
const isValid = Value.Check(resultSchema, result);
if (!isValid) {
const _errors = [...Value.Errors(resultSchema, result)];
const errors = _errors.map(({ message, path }) => ({
path,
message
}));
if ("ERROR" === validationMode) {
logger.error(
{ errors },
`GROQ response failed validation ("ERROR" mode).`
);
throw new GroqValidationError(errors);
}
throw e;
if ("WARN" === validationMode) {
logger.warn(
{ errors },
`GROQ response failed validation ("WARN" mode).`
);
}
}
return result;
};
}
var GroqValidationError = class extends Error {
constructor(validationError) {
constructor(errors) {
super("GROQ response did not match expected schema.");
this.validationError = validationError;
this.errors = errors;
}

@@ -25,6 +48,6 @@ };

// src/query.ts
import { Type as Type2, TypeGuard } from "@sinclair/typebox";
import { Type as Type3, TypeGuard } from "@sinclair/typebox";
// src/schemas.ts
import { Type } from "@sinclair/typebox";
import { Type as Type2 } from "@sinclair/typebox";
import {

@@ -39,7 +62,21 @@ Array,

var KindSymbol = Symbol("groq.Kind");
var AliasLiteralSymbol = Symbol("groq.AliasLiteral");
var NeedExpansionSymbol = Symbol("groq.NeedExpansion");
var SupportsExpansionSymbol = Symbol("groq.SupportsExpansion");
var ConditionalExpansionTypeSymbol = Symbol("groq.ConditionalExpansionType");
function Alias(schema, aliasLiteral) {
return {
...schema,
[KindSymbol]: "Alias" /* Alias */,
[AliasLiteralSymbol]: aliasLiteral
};
}
function isAlias(schema) {
return "Alias" /* Alias */ === schema[KindSymbol];
}
function getAliasLiteral(schema) {
return schema[AliasLiteralSymbol];
}
function Projection(properties) {
const schema = Type.Object(properties);
const schema = Type2.Object(properties);
schema[KindSymbol] = "Projection" /* Projection */;

@@ -61,4 +98,11 @@ schema[SupportsExpansionSymbol] = true;

}
var unionFallbackProjection = Projection({
_type: Alias(Type2.Literal("unknown"), `"unknown"`),
_rawType: Alias(Type2.String(), "_type")
});
function UnionProjection(projections) {
const schema = Type.Union(projections);
const schema = Type2.Union([
...projections,
unionFallbackProjection
]);
schema[KindSymbol] = "UnionProjection" /* UnionProjection */;

@@ -71,4 +115,4 @@ schema[SupportsExpansionSymbol] = true;

}
function Collection(properties) {
const schema = Type.Array(Projection(properties));
function Collection(projection) {
const schema = Type2.Array(projection);
schema[KindSymbol] = "Collection" /* Collection */;

@@ -110,8 +154,4 @@ schema[SupportsExpansionSymbol] = true;

let query = ["*"];
if (this.payload.conditionGroups && 0 < this.payload.conditionGroups.length) {
for (const conditionGroup of this.payload.conditionGroups) {
query.push(
`[${conditionGroup.conditions.map(({ key, operator, value }) => `${key} ${operator} ${JSON.stringify(value)}`).join(conditionGroup.mode)}]`
);
}
for (const filter of this.payload.filters) {
query.push(`[${filter}]`);
}

@@ -127,33 +167,35 @@ if (this.payload.slice) {

}
const projection = TypeGuard.IsObject(this.payload.schema) ? this.payload.schema : TypeGuard.IsArray(this.payload.schema) && TypeGuard.IsObject(this.payload.schema.items) ? this.payload.schema.items : null;
if (projection) {
query.push(this.serializeProjection(projection));
}
query.push(this.serializeProjection(this.payload.schema));
return query.join("");
}
/**
* Serialize the given projection schema to a GROQ string.
* Recursively serialize the given schema to a GROQ projection string.
*/
serializeProjection(schema) {
let attributes = [];
for (const [key, value] of Object.entries(schema.properties)) {
if (isProjection(value) || isTypedProjection(value)) {
attributes.push(`${key}${this.serializeProjection(value)}`);
continue;
if (TypeGuard.IsUnknown(schema))
return "";
const innerProjection = (() => {
if (isUnionProjection(schema)) {
return `{...select(${this.serializeUnionConditions(schema)})}`;
}
if (isUnionProjection(value)) {
attributes.push(
`${key}${this.wrapExpansionQuery(value, `{...select(${this.serializeUnionConditions(value)})}`)}`
);
continue;
if (isCollection(schema)) {
return `[]${this.serializeProjection(schema.items)}`;
}
if (isCollection(value)) {
attributes.push(
`${key}[]${this.wrapExpansionQuery(value, this.serializeProjection(value.items))}`
);
continue;
if (isAlias(schema)) {
return getAliasLiteral(schema);
}
attributes.push(key);
}
return this.wrapExpansionQuery(schema, `{${attributes.join(",")}}`);
if (TypeGuard.IsObject(schema)) {
let attributes = [];
for (const [key, value] of Object.entries(schema.properties)) {
const projection = this.serializeProjection(value);
attributes.push(
!projection ? key : /^\[?\]?-?>?{/.test(projection) ? `${key}${projection}` : `"${key}":${projection}`
// other types need quoted keys,
);
}
return `{${attributes.join(",")}}`;
}
return "";
})();
return this.wrapExpansionQuery(schema, innerProjection);
}

@@ -165,6 +207,10 @@ /**

let conditions = [];
for (const projection of schema.anyOf) {
conditions.push(
`_type == "${projection.properties._type.const}" => ${this.serializeProjection(projection)}`
);
for (const schemaVariant of schema.anyOf) {
if (isTypedProjection(schemaVariant)) {
conditions.push(
`_type == "${schemaVariant.properties._type.const}" => ${this.serializeProjection(schemaVariant)}`
);
} else {
conditions.push(this.serializeProjection(schemaVariant));
}
}

@@ -198,4 +244,3 @@ return conditions.join(",");

*/
grab(selection) {
const schema = TypeGuard.IsSchema(selection) ? selection : Projection(selection);
grab(schema) {
return new _EntityQuery({

@@ -206,2 +251,11 @@ ...this.payload,

}
/**
* Add filter condition.
*/
filter(filterCondition) {
return new _EntityQuery({
...this.payload,
filters: [...this.payload.filters, filterCondition]
});
}
};

@@ -213,3 +267,3 @@ var ArrayQuery = class _ArrayQuery extends BaseQuery {

resolveSchema() {
return Type2.Array(this.payload.schema);
return Type3.Array(this.payload.schema);
}

@@ -219,4 +273,3 @@ /**

*/
grab(selection) {
const schema = TypeGuard.IsSchema(selection) ? selection : Projection(selection);
grab(schema) {
return new _ArrayQuery({

@@ -227,2 +280,14 @@ ...this.payload,

}
/**
* Add filter condition.
*/
filter(filterCondition) {
return new EntityQuery({
...this.payload,
filters: [...this.payload.filters, filterCondition]
});
}
/**
* Slice the collection.
*/
slice(from, to) {

@@ -234,2 +299,5 @@ return new EntityQuery({

}
/**
* Return only the first result.
*/
first() {

@@ -239,10 +307,7 @@ return this.slice(0);

};
function filterByType(type) {
function filterByType(type, additionalFilter) {
return new ArrayQuery({
schema: Type2.Unknown(),
conditionGroups: [
{
mode: "&&",
conditions: [{ key: "_type", operator: "==", value: type }]
}
schema: Type3.Unknown(),
filters: [
`_type == "${type}"${additionalFilter ? ` && ${additionalFilter}` : ""}`
]

@@ -252,2 +317,3 @@ });

export {
Alias,
Array,

@@ -269,3 +335,5 @@ ArrayQuery,

filterByType,
getAliasLiteral,
getConditionalExpansionType,
isAlias,
isCollection,

@@ -272,0 +340,0 @@ isExpandable,

{
"name": "@dangreaves/groq-query-builder",
"version": "0.1.0",
"version": "0.2.0",
"license": "MIT",

@@ -27,2 +27,5 @@ "author": {

},
"peerDependencies": {
"pino": "^8.19.0"
},
"devDependencies": {

@@ -33,2 +36,3 @@ "@sanity/client": "^6.15.7",

"@types/node": "^20.11.30",
"pino": "^8.19.0",
"prettier": "^3.2.5",

@@ -35,0 +39,0 @@ "tsup": "^8.0.2",

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc