data-prism
Advanced tools
Comparing version 0.0.1 to 0.0.2
@@ -1,2 +0,5 @@ | ||
export { queryGraph } from "./query-graph.js"; | ||
export { linkInverses, emptyGraph, mergeGraphs } from "./graph.js"; | ||
export { flattenResource, normalizeResource, normalizeResources, } from "./mappers.js"; | ||
export { createQueryGraph, queryGraph } from "./graph/query.js"; | ||
export { forEachQuery, mapQuery, reduceQuery, forEachSchemalessQuery, mapSchemalessQuery, reduceSchemalessQuery, } from "./query.js"; | ||
//# sourceMappingURL=index.d.ts.map |
@@ -1,1 +0,4 @@ | ||
export { queryGraph } from "./query-graph.js"; | ||
export { linkInverses, emptyGraph, mergeGraphs } from "./graph.js"; | ||
export { flattenResource, normalizeResource, normalizeResources, } from "./mappers.js"; | ||
export { createQueryGraph, queryGraph } from "./graph/query.js"; | ||
export { forEachQuery, mapQuery, reduceQuery, forEachSchemalessQuery, mapSchemalessQuery, reduceSchemalessQuery, } from "./query.js"; |
@@ -0,3 +1,4 @@ | ||
import { Schema } from "./schema"; | ||
export type Expression = { | ||
[k: string]: any; | ||
[k: string]: unknown; | ||
}; | ||
@@ -20,3 +21,3 @@ export type Query = { | ||
where?: { | ||
[k: string]: any; | ||
[k: string]: unknown; | ||
}; | ||
@@ -27,9 +28,29 @@ }; | ||
}; | ||
export type CompiledQuery = Query & { | ||
export type NormalQuery = Query & { | ||
select: { | ||
[k: string]: string | CompiledQuery; | ||
[k: string]: string | NormalQuery | Expression; | ||
}; | ||
}; | ||
export type CompiledRootQuery = RootQuery & CompiledQuery; | ||
export declare function compileQuery(rootQuery: any): CompiledRootQuery; | ||
export type NormalRootQuery = RootQuery & NormalQuery; | ||
export type QueryInfo = { | ||
path: string[]; | ||
parent: Query | null; | ||
}; | ||
type ParentQueryInfo<S extends Schema> = QueryInfo & { | ||
type: string & keyof S["resources"]; | ||
}; | ||
export type SchemaQueryInfo<S extends Schema> = ParentQueryInfo<S> & { | ||
attributes: string[]; | ||
relationships: { | ||
[k: string]: Query; | ||
}; | ||
}; | ||
export declare function normalizeQuery(rootQuery: RootQuery): NormalRootQuery; | ||
export declare function forEachSchemalessQuery(query: any, fn: any): void; | ||
export declare function mapSchemalessQuery(query: any, fn: any): any; | ||
export declare function reduceSchemalessQuery(query: any, fn: any, init: any): any; | ||
export declare function forEachQuery<S extends Schema>(schema: S, query: RootQuery, fn: (subquery: Query, info: SchemaQueryInfo<S>) => unknown): void; | ||
export declare function mapQuery<S extends Schema>(schema: S, query: RootQuery, fn: (subquery: Query, info: SchemaQueryInfo<S>) => unknown): any; | ||
export declare function reduceQuery<S extends Schema, T>(schema: S, query: RootQuery, fn: (acc: T, subquery: Query, info: SchemaQueryInfo<S>) => T, init: T): any; | ||
export {}; | ||
//# sourceMappingURL=query.d.ts.map |
import { defaultExpressionEngine } from "@data-prism/expressions"; | ||
import { mapValues } from "lodash-es"; | ||
export function compileQuery(rootQuery) { | ||
import { mapValues, pick } from "lodash-es"; | ||
const { isExpression } = defaultExpressionEngine; | ||
export function normalizeQuery(rootQuery) { | ||
const stringToProp = (str) => ({ [str]: str }); | ||
@@ -13,5 +14,3 @@ const go = (query) => { | ||
: select; | ||
const subqueries = mapValues(selectObj, (sel) => typeof sel === "object" && !defaultExpressionEngine.isExpression(sel) | ||
? go(sel) | ||
: sel); | ||
const subqueries = mapValues(selectObj, (sel) => typeof sel === "object" && !isExpression(sel) ? go(sel) : sel); | ||
return { ...query, select: subqueries }; | ||
@@ -21,1 +20,144 @@ }; | ||
} | ||
export function forEachSchemalessQuery(query, fn) { | ||
const go = (subquery, info) => { | ||
fn(subquery, info); | ||
Object.entries(subquery.select).forEach(([prop, select]) => { | ||
if (typeof select === "object" && !isExpression(select)) { | ||
const nextInfo = { | ||
path: [...info.path, prop], | ||
parent: subquery, | ||
}; | ||
go(select, nextInfo); | ||
} | ||
}); | ||
}; | ||
const initInfo = { | ||
path: [], | ||
parent: null, | ||
}; | ||
go(normalizeQuery(query), initInfo); | ||
} | ||
export function mapSchemalessQuery(query, fn) { | ||
const go = (subquery, info) => { | ||
const mappedSelect = mapValues(subquery.select, (select, prop) => { | ||
if (typeof select !== "object" || isExpression(select)) | ||
return select; | ||
const nextInfo = { | ||
path: [...info.path, prop], | ||
parent: subquery, | ||
}; | ||
return go(select, nextInfo); | ||
}); | ||
return fn({ ...subquery, select: mappedSelect }, info); | ||
}; | ||
const initInfo = { | ||
path: [], | ||
parent: null, | ||
}; | ||
return go(normalizeQuery(query), initInfo); | ||
} | ||
export function reduceSchemalessQuery(query, fn, init) { | ||
const go = (subquery, info, accValue) => Object.entries(subquery.select).reduce((acc, [prop, select]) => { | ||
if (typeof select !== "object" || isExpression(select)) | ||
return acc; | ||
const nextInfo = { | ||
path: [...info.path, prop], | ||
parent: subquery, | ||
}; | ||
return go(select, nextInfo, acc); | ||
}, fn(accValue, subquery, info)); | ||
const initInfo = { | ||
path: [], | ||
parent: null, | ||
}; | ||
return go(normalizeQuery(query), initInfo, init); | ||
} | ||
export function forEachQuery(schema, query, fn) { | ||
const go = (subquery, info) => { | ||
const { path, type } = info; | ||
const resourceSchema = schema.resources[type]; | ||
const attributes = Object.keys(resourceSchema.attributes).filter((a) => a in subquery.select); | ||
const relationships = pick(subquery.select, Object.keys(resourceSchema.relationships)); | ||
const fullInfo = { | ||
...info, | ||
attributes, | ||
relationships, | ||
}; | ||
fn(subquery, fullInfo); | ||
Object.entries(subquery.select).forEach(([prop, select]) => { | ||
if (typeof select === "object" && !isExpression(select)) { | ||
const nextInfo = { | ||
path: [...path, prop], | ||
parent: subquery, | ||
type: resourceSchema.relationships[prop].type, | ||
}; | ||
go(select, nextInfo); | ||
} | ||
}); | ||
}; | ||
const initInfo = { | ||
path: [], | ||
parent: null, | ||
type: query.type, | ||
}; | ||
go(normalizeQuery(query), initInfo); | ||
} | ||
export function mapQuery(schema, query, fn) { | ||
const go = (subquery, info) => { | ||
const { path, type } = info; | ||
const resourceSchema = schema.resources[type]; | ||
const attributes = Object.keys(resourceSchema.attributes).filter((a) => a in subquery.select); | ||
const relationships = pick(subquery.select, Object.keys(resourceSchema.relationships)); | ||
const fullInfo = { | ||
...info, | ||
attributes, | ||
relationships, | ||
}; | ||
const mappedSelect = mapValues(subquery.select, (select, prop) => { | ||
if (typeof select !== "object" || isExpression(select)) | ||
return select; | ||
const nextInfo = { | ||
path: [...path, prop], | ||
parent: subquery, | ||
type: resourceSchema.relationships[prop].type, | ||
}; | ||
return go(select, nextInfo); | ||
}); | ||
return fn({ ...subquery, select: mappedSelect }, fullInfo); | ||
}; | ||
const initInfo = { | ||
path: [], | ||
parent: null, | ||
type: query.type, | ||
}; | ||
return go(normalizeQuery(query), initInfo); | ||
} | ||
export function reduceQuery(schema, query, fn, init) { | ||
const go = (subquery, info, accValue) => { | ||
const { path, type } = info; | ||
const resourceSchema = schema.resources[type]; | ||
const attributes = Object.keys(resourceSchema.attributes).filter((a) => a in subquery.select); | ||
const relationships = pick(subquery.select, Object.keys(resourceSchema.relationships)); | ||
const fullInfo = { | ||
...info, | ||
attributes, | ||
relationships, | ||
}; | ||
return Object.entries(subquery.select).reduce((acc, [prop, select]) => { | ||
if (typeof select !== "object" || isExpression(select)) | ||
return acc; | ||
const nextInfo = { | ||
path: [...path, prop], | ||
parent: subquery, | ||
type: resourceSchema.relationships[prop].type, | ||
}; | ||
return go(select, nextInfo, acc); | ||
}, fn(accValue, subquery, fullInfo)); | ||
}; | ||
const initInfo = { | ||
path: [], | ||
parent: null, | ||
type: query.type, | ||
}; | ||
return go(normalizeQuery(query), initInfo, init); | ||
} |
@@ -1,13 +0,1 @@ | ||
import { CanonicalResource } from "./query-graph"; | ||
export type Schema = { | ||
$schema?: string; | ||
$id?: string; | ||
title?: string; | ||
description?: string; | ||
meta?: any; | ||
version?: string; | ||
resources: { | ||
[k: string]: SchemaResource; | ||
}; | ||
}; | ||
type SchemaAttribute = { | ||
@@ -17,6 +5,6 @@ type: "object" | "array" | "boolean" | "string" | "number" | "integer" | "null"; | ||
description?: string; | ||
default?: any; | ||
default?: unknown; | ||
$comment?: string; | ||
deprecated?: boolean; | ||
meta?: any; | ||
meta?: unknown; | ||
}; | ||
@@ -37,6 +25,14 @@ type SchemaRelationship = { | ||
}; | ||
export declare function canonicalizeResource(resourceType: string, resource: { | ||
[k: string]: any; | ||
}, schema: Schema, mappers: any): CanonicalResource; | ||
export type Schema = { | ||
$schema?: string; | ||
$id?: string; | ||
title?: string; | ||
description?: string; | ||
meta?: unknown; | ||
version?: string; | ||
resources: { | ||
[k: string]: SchemaResource; | ||
}; | ||
}; | ||
export {}; | ||
//# sourceMappingURL=schema.d.ts.map |
@@ -1,30 +0,1 @@ | ||
import { mapValues } from "lodash-es"; | ||
export function canonicalizeResource(resourceType, resource, schema, mappers) { | ||
const resSchema = schema.resources[resourceType]; | ||
const attributes = mapValues(resSchema.attributes, (_, attr) => mappers[attr] && typeof mappers[attr] === "function" | ||
? mappers(resource) | ||
: mappers[attr] | ||
? resource[mappers[attr]] | ||
: resource[attr]); | ||
const relationships = mapValues(resSchema.relationships, (relSchema, rel) => { | ||
if (mappers[rel] && typeof mappers[rel] === "function") | ||
return mappers(resource); | ||
const emptyRel = relSchema.cardinality === "many" ? [] : null; | ||
const id = mappers.id ? resource[mappers.id] : resource.id; | ||
}); | ||
const idField = mappers.id ?? "id"; | ||
return { | ||
id: resource[idField], | ||
type: resourceType, | ||
attributes, | ||
relationships, | ||
}; | ||
} | ||
// export function canonicalizeResources( | ||
// resourceType: string, | ||
// resources: Resource[], | ||
// schema: Schema, | ||
// mappers = {}, | ||
// ): CanonicalResources { | ||
// const resSchema = schema.resources[resourceType]; | ||
// } | ||
export {}; |
{ | ||
"name": "data-prism", | ||
"version": "0.0.1", | ||
"version": "0.0.2", | ||
"type": "module", | ||
@@ -20,3 +20,3 @@ "main": "./dist/index.js", | ||
"eslint": "^8.41.0", | ||
"eslint-config-prettier": "^8.8.0", | ||
"eslint-config-prettier": "^8.10.0", | ||
"eslint-config-standard": "^17.1.0", | ||
@@ -23,0 +23,0 @@ "prettier": "^3.1.1", |
# Querable Graphs | ||
This library exposes the ability to query graphs to receive result trees using a robust query language. It requires two components. | ||
This library exposes the ability to query graphs to receive result trees using a robust query language. In addition it provides a suite of utility functions to help wrangle data into appropriate formats and interact with data structures effectively. See [helper functions](./helpers.md). | ||
This document focuses on constructing queries as this is the most common and use case for the library that requires a fair bit of explaination. | ||
## Resource Data | ||
Resource data is a representation of the graph of data to be queried on. It should be presented in _canonical form_, which looks like this: | ||
Resource data is a representation of the graph of data to be queried on. It should be presented in _normal form_, which looks like this: | ||
@@ -179,3 +181,3 @@ ```javascript | ||
This is one reason why the canonical form for resources is important: we can traverse the resource to elsewhere in the graph. | ||
This is one reason why the normal form for resources is important: we can traverse the resource to elsewhere in the graph. | ||
@@ -182,0 +184,0 @@ #### Conclusion |
@@ -1,1 +0,15 @@ | ||
export { queryGraph } from "./query-graph.js"; | ||
export { linkInverses, emptyGraph, mergeGraphs } from "./graph.js"; | ||
export { | ||
flattenResource, | ||
normalizeResource, | ||
normalizeResources, | ||
} from "./mappers.js"; | ||
export { createQueryGraph, queryGraph } from "./graph/query.js"; | ||
export { | ||
forEachQuery, | ||
mapQuery, | ||
reduceQuery, | ||
forEachSchemalessQuery, | ||
mapSchemalessQuery, | ||
reduceSchemalessQuery, | ||
} from "./query.js"; |
254
src/query.ts
import { defaultExpressionEngine } from "@data-prism/expressions"; | ||
import { mapValues } from "lodash-es"; | ||
import { mapValues, pick } from "lodash-es"; | ||
import { Schema } from "./schema"; | ||
export type Expression = { | ||
[k: string]: any; | ||
[k: string]: unknown; | ||
}; | ||
@@ -19,3 +20,3 @@ | ||
type?: string; | ||
where?: { [k: string]: any }; | ||
where?: { [k: string]: unknown }; | ||
}; | ||
@@ -27,14 +28,30 @@ | ||
export type CompiledQuery = Query & { | ||
export type NormalQuery = Query & { | ||
select: { | ||
[k: string]: string | CompiledQuery; | ||
[k: string]: string | NormalQuery | Expression; | ||
}; | ||
}; | ||
export type CompiledRootQuery = RootQuery & CompiledQuery; | ||
export type NormalRootQuery = RootQuery & NormalQuery; | ||
export function compileQuery(rootQuery): CompiledRootQuery { | ||
export type QueryInfo = { | ||
path: string[]; | ||
parent: Query | null; | ||
}; | ||
type ParentQueryInfo<S extends Schema> = QueryInfo & { | ||
type: string & keyof S["resources"]; | ||
}; | ||
export type SchemaQueryInfo<S extends Schema> = ParentQueryInfo<S> & { | ||
attributes: string[]; | ||
relationships: { [k: string]: Query }; | ||
}; | ||
const { isExpression } = defaultExpressionEngine; | ||
export function normalizeQuery(rootQuery: RootQuery): NormalRootQuery { | ||
const stringToProp = (str) => ({ [str]: str }); | ||
const go = (query: Query): CompiledQuery => { | ||
const go = (query: Query): NormalQuery => { | ||
const { select } = query; | ||
@@ -49,5 +66,3 @@ const selectObj = Array.isArray(select) | ||
const subqueries = mapValues(selectObj, (sel) => | ||
typeof sel === "object" && !defaultExpressionEngine.isExpression(sel) | ||
? go(sel) | ||
: sel, | ||
typeof sel === "object" && !isExpression(sel) ? go(sel) : sel, | ||
); | ||
@@ -58,3 +73,218 @@ | ||
return go(rootQuery) as CompiledRootQuery; | ||
return go(rootQuery) as NormalRootQuery; | ||
} | ||
export function forEachSchemalessQuery(query, fn) { | ||
const go = (subquery: Query, info: QueryInfo) => { | ||
fn(subquery, info); | ||
Object.entries(subquery.select).forEach(([prop, select]) => { | ||
if (typeof select === "object" && !isExpression(select)) { | ||
const nextInfo = { | ||
path: [...info.path, prop], | ||
parent: subquery, | ||
}; | ||
go(select as Query, nextInfo); | ||
} | ||
}); | ||
}; | ||
const initInfo = { | ||
path: [], | ||
parent: null, | ||
}; | ||
go(normalizeQuery(query), initInfo); | ||
} | ||
export function mapSchemalessQuery(query, fn) { | ||
const go = (subquery: Query, info: QueryInfo) => { | ||
const mappedSelect = mapValues(subquery.select, (select, prop) => { | ||
if (typeof select !== "object" || isExpression(select)) return select; | ||
const nextInfo = { | ||
path: [...info.path, prop], | ||
parent: subquery, | ||
}; | ||
return go(select as Query, nextInfo); | ||
}); | ||
return fn({ ...subquery, select: mappedSelect }, info); | ||
}; | ||
const initInfo = { | ||
path: [], | ||
parent: null, | ||
}; | ||
return go(normalizeQuery(query), initInfo); | ||
} | ||
export function reduceSchemalessQuery(query, fn, init) { | ||
const go = (subquery: Query, info: QueryInfo, accValue) => | ||
Object.entries(subquery.select).reduce( | ||
(acc, [prop, select]) => { | ||
if (typeof select !== "object" || isExpression(select)) return acc; | ||
const nextInfo = { | ||
path: [...info.path, prop], | ||
parent: subquery, | ||
}; | ||
return go(select as Query, nextInfo, acc); | ||
}, | ||
fn(accValue, subquery, info), | ||
); | ||
const initInfo = { | ||
path: [], | ||
parent: null, | ||
}; | ||
return go(normalizeQuery(query), initInfo, init); | ||
} | ||
export function forEachQuery<S extends Schema>( | ||
schema: S, | ||
query: RootQuery, | ||
fn: (subquery: Query, info: SchemaQueryInfo<S>) => unknown, | ||
) { | ||
const go = (subquery: Query, info: ParentQueryInfo<S>) => { | ||
const { path, type } = info; | ||
const resourceSchema = schema.resources[type]; | ||
const attributes = Object.keys(resourceSchema.attributes).filter( | ||
(a) => a in subquery.select, | ||
); | ||
const relationships = pick( | ||
subquery.select, | ||
Object.keys(resourceSchema.relationships), | ||
) as { [k: string]: Query }; | ||
const fullInfo = { | ||
...info, | ||
attributes, | ||
relationships, | ||
}; | ||
fn(subquery, fullInfo); | ||
Object.entries(subquery.select).forEach(([prop, select]) => { | ||
if (typeof select === "object" && !isExpression(select)) { | ||
const nextInfo = { | ||
path: [...path, prop], | ||
parent: subquery, | ||
type: resourceSchema.relationships[prop].type, | ||
}; | ||
go(select as Query, nextInfo); | ||
} | ||
}); | ||
}; | ||
const initInfo = { | ||
path: [], | ||
parent: null, | ||
type: query.type, | ||
}; | ||
go(normalizeQuery(query), initInfo); | ||
} | ||
export function mapQuery<S extends Schema>( | ||
schema: S, | ||
query: RootQuery, | ||
fn: (subquery: Query, info: SchemaQueryInfo<S>) => unknown, | ||
) { | ||
const go = (subquery: Query, info: ParentQueryInfo<S>) => { | ||
const { path, type } = info; | ||
const resourceSchema = schema.resources[type]; | ||
const attributes = Object.keys(resourceSchema.attributes).filter( | ||
(a) => a in subquery.select, | ||
); | ||
const relationships = pick( | ||
subquery.select, | ||
Object.keys(resourceSchema.relationships), | ||
) as { [k: string]: Query }; | ||
const fullInfo = { | ||
...info, | ||
attributes, | ||
relationships, | ||
}; | ||
const mappedSelect = mapValues(subquery.select, (select, prop) => { | ||
if (typeof select !== "object" || isExpression(select)) return select; | ||
const nextInfo = { | ||
path: [...path, prop], | ||
parent: subquery, | ||
type: resourceSchema.relationships[prop].type, | ||
}; | ||
return go(select as Query, nextInfo); | ||
}); | ||
return fn({ ...subquery, select: mappedSelect }, fullInfo); | ||
}; | ||
const initInfo = { | ||
path: [], | ||
parent: null, | ||
type: query.type, | ||
}; | ||
return go(normalizeQuery(query), initInfo); | ||
} | ||
export function reduceQuery<S extends Schema, T>( | ||
schema: S, | ||
query: RootQuery, | ||
fn: (acc: T, subquery: Query, info: SchemaQueryInfo<S>) => T, | ||
init: T, | ||
) { | ||
const go = (subquery: Query, info: ParentQueryInfo<S>, accValue) => { | ||
const { path, type } = info; | ||
const resourceSchema = schema.resources[type]; | ||
const attributes = Object.keys(resourceSchema.attributes).filter( | ||
(a) => a in subquery.select, | ||
); | ||
const relationships = pick( | ||
subquery.select, | ||
Object.keys(resourceSchema.relationships), | ||
) as { [k: string]: Query }; | ||
const fullInfo = { | ||
...info, | ||
attributes, | ||
relationships, | ||
}; | ||
return Object.entries(subquery.select).reduce( | ||
(acc, [prop, select]) => { | ||
if (typeof select !== "object" || isExpression(select)) return acc; | ||
const nextInfo = { | ||
path: [...path, prop], | ||
parent: subquery, | ||
type: resourceSchema.relationships[prop].type, | ||
}; | ||
return go(select as Query, nextInfo, acc); | ||
}, | ||
fn(accValue, subquery, fullInfo), | ||
); | ||
}; | ||
const initInfo = { | ||
path: [], | ||
parent: null, | ||
type: query.type, | ||
}; | ||
return go(normalizeQuery(query), initInfo, init); | ||
} |
@@ -1,14 +0,1 @@ | ||
import { mapValues } from "lodash-es"; | ||
import { CanonicalResource, CanonicalResources } from "./query-graph"; | ||
export type Schema = { | ||
$schema?: string; | ||
$id?: string; | ||
title?: string; | ||
description?: string; | ||
meta?: any; | ||
version?: string; | ||
resources: { [k: string]: SchemaResource }; | ||
}; | ||
type SchemaAttribute = { | ||
@@ -25,6 +12,6 @@ type: | ||
description?: string; | ||
default?: any; | ||
default?: unknown; | ||
$comment?: string; | ||
deprecated?: boolean; | ||
meta?: any; | ||
meta?: unknown; | ||
}; | ||
@@ -44,45 +31,10 @@ | ||
type Resource = { [k: string]: any }; | ||
export function canonicalizeResource( | ||
resourceType: string, | ||
resource: { [k: string]: any }, | ||
schema: Schema, | ||
mappers, | ||
): CanonicalResource { | ||
const resSchema = schema.resources[resourceType]; | ||
const attributes = mapValues(resSchema.attributes, (_, attr) => | ||
mappers[attr] && typeof mappers[attr] === "function" | ||
? mappers(resource) | ||
: mappers[attr] | ||
? resource[mappers[attr]] | ||
: resource[attr], | ||
); | ||
const relationships = mapValues(resSchema.relationships, (relSchema, rel) => { | ||
if (mappers[rel] && typeof mappers[rel] === "function") | ||
return mappers(resource); | ||
const emptyRel = relSchema.cardinality === "many" ? [] : null; | ||
const id = mappers.id ? resource[mappers.id] : resource.id; | ||
}); | ||
const idField = mappers.id ?? "id"; | ||
return { | ||
id: resource[idField], | ||
type: resourceType, | ||
attributes, | ||
relationships, | ||
}; | ||
} | ||
// export function canonicalizeResources( | ||
// resourceType: string, | ||
// resources: Resource[], | ||
// schema: Schema, | ||
// mappers = {}, | ||
// ): CanonicalResources { | ||
// const resSchema = schema.resources[resourceType]; | ||
// } | ||
export type Schema = { | ||
$schema?: string; | ||
$id?: string; | ||
title?: string; | ||
description?: string; | ||
meta?: unknown; | ||
version?: string; | ||
resources: { [k: string]: SchemaResource }; | ||
}; |
@@ -17,5 +17,3 @@ { | ||
], | ||
"include": [ | ||
"./src/**/*" | ||
] | ||
} | ||
"include": ["./src/**/*"] | ||
} |
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
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
232374
48
2788
253
1