From 51241865f8c77827e8c88b88cd11082c743a7c88 Mon Sep 17 00:00:00 2001 From: Alex Yang <himself65@outlook.com> Date: Thu, 16 May 2024 16:29:16 -0700 Subject: [PATCH] feat: improve BaseNode (#848) --- .github/workflows/test.yml | 3 +- package.json | 1 + packages/core/.madgerc | 7 + packages/core/package.json | 1 - packages/core/src/Node.ts | 148 +++++++++++------- packages/core/src/Settings.ts | 17 +- .../core/src/indices/vectorStore/index.ts | 2 +- packages/core/src/internal/decorator/node.ts | 60 +++++++ .../core/src/internal/settings/chunk-size.ts | 19 +++ .../src/storage/docStore/KVDocumentStore.ts | 14 +- packages/core/src/storage/docStore/utils.ts | 22 ++- packages/core/tests/Embedding.test.ts | 6 +- packages/core/tests/Node.test.ts | 57 ++++++- .../tests/indices/VectorStoreIndex.test.ts | 9 +- packages/env/.madgerc | 7 + packages/env/jsr.json | 2 +- packages/env/package.json | 1 + pnpm-lock.yaml | 6 +- turbo.json | 1 + 19 files changed, 293 insertions(+), 90 deletions(-) create mode 100644 packages/core/.madgerc create mode 100644 packages/core/src/internal/decorator/node.ts create mode 100644 packages/core/src/internal/settings/chunk-size.ts create mode 100644 packages/env/.madgerc diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e75558cc1..c068bf97a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -76,8 +76,7 @@ jobs: - name: Run Type Check run: pnpm run type-check - name: Run Circular Dependency Check - run: pnpm run circular-check - working-directory: ./packages/core + run: pnpm dlx turbo run circular-check - uses: actions/upload-artifact@v3 if: failure() with: diff --git a/package.json b/package.json index 96fe34ef8..5185efc45 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "eslint-plugin-react": "7.34.1", "husky": "^9.0.11", "lint-staged": "^15.2.2", + "madge": "^7.0.0", "prettier": "^3.2.5", "prettier-plugin-organize-imports": "^3.2.4", "turbo": "^1.13.3", diff --git a/packages/core/.madgerc b/packages/core/.madgerc new file mode 100644 index 000000000..66f0c66c6 --- /dev/null +++ b/packages/core/.madgerc @@ -0,0 +1,7 @@ +{ + "detectiveOptions": { + "ts": { + "skipTypeImports": true + } + } +} diff --git a/packages/core/package.json b/packages/core/package.json index e53b2fe0b..1e6d3a39d 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -69,7 +69,6 @@ "@swc/core": "^1.5.5", "concurrently": "^8.2.2", "glob": "^10.3.12", - "madge": "^7.0.0", "typescript": "^5.4.5" }, "engines": { diff --git a/packages/core/src/Node.ts b/packages/core/src/Node.ts index 8d8d045ab..820f464df 100644 --- a/packages/core/src/Node.ts +++ b/packages/core/src/Node.ts @@ -1,5 +1,5 @@ import { createSHA256, path, randomUUID } from "@llamaindex/env"; -import _ from "lodash"; +import { chunkSizeCheck, lazyInitHash } from "./internal/decorator/node.js"; export enum NodeRelationship { SOURCE = "SOURCE", @@ -37,6 +37,16 @@ export type RelatedNodeType<T extends Metadata = Metadata> = | RelatedNodeInfo<T> | RelatedNodeInfo<T>[]; +export type BaseNodeParams<T extends Metadata = Metadata> = { + id_?: string; + metadata?: T; + excludedEmbedMetadataKeys?: string[]; + excludedLlmMetadataKeys?: string[]; + relationships?: Partial<Record<NodeRelationship, RelatedNodeType<T>>>; + hash?: string; + embedding?: number[]; +}; + /** * Generic abstract class for retrievable nodes */ @@ -47,21 +57,37 @@ export abstract class BaseNode<T extends Metadata = Metadata> { * * Set to a UUID by default. */ - id_: string = randomUUID(); + id_: string; embedding?: number[]; // Metadata fields - metadata: T = {} as T; - excludedEmbedMetadataKeys: string[] = []; - excludedLlmMetadataKeys: string[] = []; - relationships: Partial<Record<NodeRelationship, RelatedNodeType<T>>> = {}; - hash: string = ""; - - constructor(init?: Partial<BaseNode<T>>) { - Object.assign(this, init); - } - - abstract getType(): ObjectType; + metadata: T; + excludedEmbedMetadataKeys: string[]; + excludedLlmMetadataKeys: string[]; + relationships: Partial<Record<NodeRelationship, RelatedNodeType<T>>>; + + @lazyInitHash + accessor hash: string = ""; + + protected constructor(init?: BaseNodeParams<T>) { + const { + id_, + metadata, + excludedEmbedMetadataKeys, + excludedLlmMetadataKeys, + relationships, + hash, + embedding, + } = init || {}; + this.id_ = id_ ?? randomUUID(); + this.metadata = metadata ?? ({} as T); + this.excludedEmbedMetadataKeys = excludedEmbedMetadataKeys ?? []; + this.excludedLlmMetadataKeys = excludedLlmMetadataKeys ?? []; + this.relationships = relationships ?? {}; + this.embedding = embedding; + } + + abstract get type(): ObjectType; abstract getContent(metadataMode: MetadataMode): string; abstract getMetadataStr(metadataMode: MetadataMode): string; @@ -146,7 +172,12 @@ export abstract class BaseNode<T extends Metadata = Metadata> { * @see toMutableJSON - use to return a mutable JSON instead */ toJSON(): Record<string, any> { - return { ...this, type: this.getType() }; + return { + ...this, + type: this.type, + // hash is an accessor property, so it's not included in the rest operator + hash: this.hash, + }; } clone(): BaseNode { @@ -159,32 +190,43 @@ export abstract class BaseNode<T extends Metadata = Metadata> { * @return {Record<string, any>} - The JSON representation of the object. */ toMutableJSON(): Record<string, any> { - return _.cloneDeep(this.toJSON()); + return structuredClone(this.toJSON()); } } +export type TextNodeParams<T extends Metadata = Metadata> = + BaseNodeParams<T> & { + text?: string; + textTemplate?: string; + startCharIdx?: number; + endCharIdx?: number; + metadataSeparator?: string; + }; + /** * TextNode is the default node type for text. Most common node type in LlamaIndex.TS */ export class TextNode<T extends Metadata = Metadata> extends BaseNode<T> { - text: string = ""; - textTemplate: string = ""; + text: string; + textTemplate: string; startCharIdx?: number; endCharIdx?: number; // textTemplate: NOTE write your own formatter if needed // metadataTemplate: NOTE write your own formatter if needed - metadataSeparator: string = "\n"; + metadataSeparator: string; - constructor(init?: Partial<TextNode<T>>) { + constructor(init: TextNodeParams<T> = {}) { super(init); - Object.assign(this, init); - - if (new.target === TextNode) { - // Don't generate the hash repeatedly so only do it if this is - // constructing the derived class - this.hash = init?.hash ?? this.generateHash(); + const { text, textTemplate, startCharIdx, endCharIdx, metadataSeparator } = + init; + this.text = text ?? ""; + this.textTemplate = textTemplate ?? ""; + if (startCharIdx) { + this.startCharIdx = startCharIdx; } + this.endCharIdx = endCharIdx; + this.metadataSeparator = metadataSeparator ?? "\n"; } /** @@ -194,7 +236,7 @@ export class TextNode<T extends Metadata = Metadata> extends BaseNode<T> { */ generateHash() { const hashFunction = createSHA256(); - hashFunction.update(`type=${this.getType()}`); + hashFunction.update(`type=${this.type}`); hashFunction.update( `startCharIdx=${this.startCharIdx} endCharIdx=${this.endCharIdx}`, ); @@ -202,10 +244,11 @@ export class TextNode<T extends Metadata = Metadata> extends BaseNode<T> { return hashFunction.digest(); } - getType(): ObjectType { + get type() { return ObjectType.TEXT; } + @chunkSizeCheck getContent(metadataMode: MetadataMode = MetadataMode.NONE): string { const metadataStr = this.getMetadataStr(metadataMode).trim(); return `${metadataStr}\n\n${this.text}`.trim(); @@ -246,19 +289,21 @@ export class TextNode<T extends Metadata = Metadata> extends BaseNode<T> { } } +export type IndexNodeParams<T extends Metadata = Metadata> = + TextNodeParams<T> & { + indexId: string; + }; + export class IndexNode<T extends Metadata = Metadata> extends TextNode<T> { - indexId: string = ""; + indexId: string; - constructor(init?: Partial<IndexNode<T>>) { + constructor(init?: IndexNodeParams<T>) { super(init); - Object.assign(this, init); - - if (new.target === IndexNode) { - this.hash = init?.hash ?? this.generateHash(); - } + const { indexId } = init || {}; + this.indexId = indexId ?? ""; } - getType(): ObjectType { + get type() { return ObjectType.INDEX; } } @@ -267,16 +312,11 @@ export class IndexNode<T extends Metadata = Metadata> extends TextNode<T> { * A document is just a special text node with a docId. */ export class Document<T extends Metadata = Metadata> extends TextNode<T> { - constructor(init?: Partial<Document<T>>) { + constructor(init?: TextNodeParams<T>) { super(init); - Object.assign(this, init); - - if (new.target === Document) { - this.hash = init?.hash ?? this.generateHash(); - } } - getType() { + get type() { return ObjectType.DOCUMENT; } } @@ -303,21 +343,21 @@ export function jsonToNode(json: any, type?: ObjectType) { export type ImageType = string | Blob | URL; -export type ImageNodeConstructorProps<T extends Metadata> = Pick< - ImageNode<T>, - "image" | "id_" -> & - Partial<ImageNode<T>>; +export type ImageNodeParams<T extends Metadata = Metadata> = + TextNodeParams<T> & { + image: ImageType; + }; export class ImageNode<T extends Metadata = Metadata> extends TextNode<T> { image: ImageType; // image as blob - constructor(init: ImageNodeConstructorProps<T>) { + constructor(init: ImageNodeParams<T>) { super(init); - this.image = init.image; + const { image } = init; + this.image = image; } - getType(): ObjectType { + get type() { return ObjectType.IMAGE; } @@ -360,15 +400,11 @@ export class ImageNode<T extends Metadata = Metadata> extends TextNode<T> { } export class ImageDocument<T extends Metadata = Metadata> extends ImageNode<T> { - constructor(init: ImageNodeConstructorProps<T>) { + constructor(init: ImageNodeParams<T>) { super(init); - - if (new.target === ImageDocument) { - this.hash = init?.hash ?? this.generateHash(); - } } - getType() { + get type() { return ObjectType.IMAGE_DOCUMENT; } } diff --git a/packages/core/src/Settings.ts b/packages/core/src/Settings.ts index 6852fffcc..7b2d751cc 100644 --- a/packages/core/src/Settings.ts +++ b/packages/core/src/Settings.ts @@ -13,6 +13,11 @@ import { setCallbackManager, withCallbackManager, } from "./internal/settings/CallbackManager.js"; +import { + getChunkSize, + setChunkSize, + withChunkSize, +} from "./internal/settings/chunk-size.js"; import type { LLM } from "./llm/types.js"; import type { NodeParser } from "./nodeParsers/types.js"; @@ -41,14 +46,12 @@ class GlobalSettings implements Config { #promptHelper: PromptHelper | null = null; #embedModel: BaseEmbedding | null = null; #nodeParser: NodeParser | null = null; - #chunkSize?: number; #chunkOverlap?: number; #llmAsyncLocalStorage = new AsyncLocalStorage<LLM>(); #promptHelperAsyncLocalStorage = new AsyncLocalStorage<PromptHelper>(); #embedModelAsyncLocalStorage = new AsyncLocalStorage<BaseEmbedding>(); #nodeParserAsyncLocalStorage = new AsyncLocalStorage<NodeParser>(); - #chunkSizeAsyncLocalStorage = new AsyncLocalStorage<number>(); #chunkOverlapAsyncLocalStorage = new AsyncLocalStorage<number>(); #promptAsyncLocalStorage = new AsyncLocalStorage<PromptConfig>(); @@ -115,8 +118,8 @@ class GlobalSettings implements Config { get nodeParser(): NodeParser { if (this.#nodeParser === null) { this.#nodeParser = new SimpleNodeParser({ - chunkSize: this.#chunkSize, - chunkOverlap: this.#chunkOverlap, + chunkSize: this.chunkSize, + chunkOverlap: this.chunkOverlap, }); } @@ -147,15 +150,15 @@ class GlobalSettings implements Config { } set chunkSize(chunkSize: number | undefined) { - this.#chunkSize = chunkSize; + setChunkSize(chunkSize); } get chunkSize(): number | undefined { - return this.#chunkSizeAsyncLocalStorage.getStore() ?? this.#chunkSize; + return getChunkSize(); } withChunkSize<Result>(chunkSize: number, fn: () => Result): Result { - return this.#chunkSizeAsyncLocalStorage.run(chunkSize, fn); + return withChunkSize(chunkSize, fn); } get chunkOverlap(): number | undefined { diff --git a/packages/core/src/indices/vectorStore/index.ts b/packages/core/src/indices/vectorStore/index.ts index 2dc5922c8..d96cb3a05 100644 --- a/packages/core/src/indices/vectorStore/index.ts +++ b/packages/core/src/indices/vectorStore/index.ts @@ -311,7 +311,7 @@ export class VectorStoreIndex extends BaseIndex<IndexDict> { // NOTE: if the vector store keeps text, // we only need to add image and index nodes for (let i = 0; i < nodes.length; ++i) { - const type = nodes[i].getType(); + const { type } = nodes[i]; if ( !vectorStore.storesText || type === ObjectType.INDEX || diff --git a/packages/core/src/internal/decorator/node.ts b/packages/core/src/internal/decorator/node.ts new file mode 100644 index 000000000..be52eac9f --- /dev/null +++ b/packages/core/src/internal/decorator/node.ts @@ -0,0 +1,60 @@ +import { getEnv } from "@llamaindex/env"; +import type { BaseNode } from "../../Node.js"; +import { getChunkSize } from "../settings/chunk-size.js"; + +const emitOnce = false; + +export function chunkSizeCheck( + contentGetter: () => string, + _context: ClassMethodDecoratorContext | ClassGetterDecoratorContext, +) { + return function <Node extends BaseNode>(this: Node) { + const content = contentGetter.call(this); + const chunkSize = getChunkSize(); + const enableChunkSizeCheck = getEnv("ENABLE_CHUNK_SIZE_CHECK") === "true"; + if ( + enableChunkSizeCheck && + chunkSize !== undefined && + content.length > chunkSize + ) { + console.warn( + `Node (${this.id_}) is larger than chunk size: ${content.length}`, + ); + if (!emitOnce) { + console.warn( + "Will truncate the content if it is larger than chunk size", + ); + console.warn("If you want to disable this behavior:"); + console.warn(" 1. Set Settings.chunkSize = undefined"); + console.warn(" 2. Set Settings.chunkSize to a larger value"); + console.warn( + " 3. Change the way of splitting content into smaller chunks", + ); + } + return content.slice(0, chunkSize); + } + return content; + }; +} + +export function lazyInitHash( + value: ClassAccessorDecoratorTarget<BaseNode, string>, + _context: ClassAccessorDecoratorContext, +): ClassAccessorDecoratorResult<BaseNode, string> { + return { + get() { + const oldValue = value.get.call(this); + if (oldValue === "") { + const hash = this.generateHash(); + value.set.call(this, hash); + } + return value.get.call(this); + }, + set(newValue: string) { + value.set.call(this, newValue); + }, + init(value: string): string { + return value; + }, + }; +} diff --git a/packages/core/src/internal/settings/chunk-size.ts b/packages/core/src/internal/settings/chunk-size.ts new file mode 100644 index 000000000..88d984907 --- /dev/null +++ b/packages/core/src/internal/settings/chunk-size.ts @@ -0,0 +1,19 @@ +import { AsyncLocalStorage } from "@llamaindex/env"; + +const chunkSizeAsyncLocalStorage = new AsyncLocalStorage<number | undefined>(); +const globalChunkSize: number | null = null; + +export function getChunkSize(): number | undefined { + return globalChunkSize ?? chunkSizeAsyncLocalStorage.getStore(); +} + +export function setChunkSize(chunkSize: number | undefined) { + chunkSizeAsyncLocalStorage.enterWith(chunkSize); +} + +export function withChunkSize<Result>( + embeddedModel: number, + fn: () => Result, +): Result { + return chunkSizeAsyncLocalStorage.run(embeddedModel, fn); +} diff --git a/packages/core/src/storage/docStore/KVDocumentStore.ts b/packages/core/src/storage/docStore/KVDocumentStore.ts index ac644faba..42c5be8f3 100644 --- a/packages/core/src/storage/docStore/KVDocumentStore.ts +++ b/packages/core/src/storage/docStore/KVDocumentStore.ts @@ -5,7 +5,7 @@ import { DEFAULT_NAMESPACE } from "../constants.js"; import type { BaseKVStore } from "../kvStore/types.js"; import type { RefDocInfo } from "./types.js"; import { BaseDocumentStore } from "./types.js"; -import { docToJson, jsonToDoc } from "./utils.js"; +import { docToJson, isValidDocJson, jsonToDoc } from "./utils.js"; type DocMetaData = { docHash: string; refDocId?: string }; @@ -27,7 +27,12 @@ export class KVDocumentStore extends BaseDocumentStore { const jsonDict = await this.kvstore.getAll(this.nodeCollection); const docs: Record<string, BaseNode> = {}; for (const key in jsonDict) { - docs[key] = jsonToDoc(jsonDict[key] as Record<string, any>); + const value = jsonDict[key]; + if (isValidDocJson(value)) { + docs[key] = jsonToDoc(value); + } else { + console.warn(`Invalid JSON for docId ${key}`); + } } return docs; } @@ -51,7 +56,7 @@ export class KVDocumentStore extends BaseDocumentStore { await this.kvstore.put(nodeKey, data, this.nodeCollection); const metadata: DocMetaData = { docHash: doc.hash }; - if (doc.getType() === ObjectType.TEXT && doc.sourceNode !== undefined) { + if (doc.type === ObjectType.TEXT && doc.sourceNode !== undefined) { const refDocInfo = (await this.getRefDocInfo( doc.sourceNode.nodeId, )) || { @@ -86,6 +91,9 @@ export class KVDocumentStore extends BaseDocumentStore { return; } } + if (!isValidDocJson(json)) { + throw new Error(`Invalid JSON for docId ${docId}`); + } return jsonToDoc(json); } diff --git a/packages/core/src/storage/docStore/utils.ts b/packages/core/src/storage/docStore/utils.ts index 6eccaaebf..ca1290768 100644 --- a/packages/core/src/storage/docStore/utils.ts +++ b/packages/core/src/storage/docStore/utils.ts @@ -4,14 +4,28 @@ import { Document, ObjectType, TextNode } from "../../Node.js"; const TYPE_KEY = "__type__"; const DATA_KEY = "__data__"; -export function docToJson(doc: BaseNode): Record<string, any> { +type DocJson = { + [TYPE_KEY]: ObjectType; + [DATA_KEY]: string; +}; + +export function isValidDocJson(docJson: any): docJson is DocJson { + return ( + typeof docJson === "object" && + docJson !== null && + docJson[TYPE_KEY] !== undefined && + docJson[DATA_KEY] !== undefined + ); +} + +export function docToJson(doc: BaseNode): DocJson { return { - [DATA_KEY]: JSON.stringify(doc), - [TYPE_KEY]: doc.getType(), + [DATA_KEY]: JSON.stringify(doc.toJSON()), + [TYPE_KEY]: doc.type, }; } -export function jsonToDoc(docDict: Record<string, any>): BaseNode { +export function jsonToDoc(docDict: DocJson): BaseNode { const docType = docDict[TYPE_KEY]; const dataDict = JSON.parse(docDict[DATA_KEY]); let doc: BaseNode; diff --git a/packages/core/tests/Embedding.test.ts b/packages/core/tests/Embedding.test.ts index ab863ead1..a70297c97 100644 --- a/packages/core/tests/Embedding.test.ts +++ b/packages/core/tests/Embedding.test.ts @@ -1,8 +1,4 @@ -import { - OpenAIEmbedding, - SimilarityType, - similarity, -} from "llamaindex/embeddings/index"; +import { OpenAIEmbedding, SimilarityType, similarity } from "llamaindex"; import { beforeAll, describe, expect, test } from "vitest"; import { mockEmbeddingModel } from "./utility/mockOpenAI.js"; diff --git a/packages/core/tests/Node.test.ts b/packages/core/tests/Node.test.ts index 34a65a2c2..0bbfab42a 100644 --- a/packages/core/tests/Node.test.ts +++ b/packages/core/tests/Node.test.ts @@ -1,6 +1,26 @@ -import { TextNode } from "llamaindex/Node"; +import { Document, TextNode } from "llamaindex/Node"; import { beforeEach, describe, expect, test } from "vitest"; +describe("Document", () => { + let document: Document; + + beforeEach(() => { + document = new Document({ text: "Hello World" }); + }); + + test("should generate a hash", () => { + expect(document.hash).toMatchInlineSnapshot( + `"1mkNkQC30mZlBBG48DNuG2WSKcTQ32DImC+4JUoVijg="`, + ); + }); + + test("clone should have the same hash", () => { + const hash = document.hash; + const clone = document.clone(); + expect(clone.hash).toBe(hash); + }); +}); + describe("TextNode", () => { let node: TextNode; @@ -9,7 +29,9 @@ describe("TextNode", () => { }); test("should generate a hash", () => { - expect(node.hash).toBe("nTSKdUTYqR52MPv/brvb4RTGeqedTEqG9QN8KSAj2Do="); + expect(node.hash).toMatchInlineSnapshot( + `"nTSKdUTYqR52MPv/brvb4RTGeqedTEqG9QN8KSAj2Do="`, + ); }); test("clone should have the same hash", () => { @@ -17,4 +39,35 @@ describe("TextNode", () => { const clone = node.clone(); expect(clone.hash).toBe(hash); }); + + test("node toJSON should keep the same", () => { + node.metadata.something = 1; + node.metadata.somethingElse = "2"; + expect(node.toJSON()).toMatchInlineSnapshot( + { + id_: expect.any(String), + }, + ` + { + "embedding": undefined, + "endCharIdx": undefined, + "excludedEmbedMetadataKeys": [], + "excludedLlmMetadataKeys": [], + "hash": "nTSKdUTYqR52MPv/brvb4RTGeqedTEqG9QN8KSAj2Do=", + "id_": Any<String>, + "metadata": { + "something": 1, + "somethingElse": "2", + }, + "metadataSeparator": " + ", + "relationships": {}, + "startCharIdx": undefined, + "text": "Hello World", + "textTemplate": "", + "type": "TEXT", + } + `, + ); + }); }); diff --git a/packages/core/tests/indices/VectorStoreIndex.test.ts b/packages/core/tests/indices/VectorStoreIndex.test.ts index 1537eba40..e2578adde 100644 --- a/packages/core/tests/indices/VectorStoreIndex.test.ts +++ b/packages/core/tests/indices/VectorStoreIndex.test.ts @@ -5,8 +5,7 @@ import { storageContextFromDefaults, } from "llamaindex"; import { DocStoreStrategy } from "llamaindex/ingestion/strategies/index"; -import { rmSync } from "node:fs"; -import { mkdtemp } from "node:fs/promises"; +import { mkdtemp, rm } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { afterAll, beforeAll, describe, expect, test } from "vitest"; @@ -15,7 +14,7 @@ const testDir = await mkdtemp(join(tmpdir(), "test-")); import { mockServiceContext } from "../utility/mockServiceContext.js"; -describe.sequential("VectorStoreIndex", () => { +describe("VectorStoreIndex", () => { let serviceContext: ServiceContext; let storageContext: StorageContext; let testStrategy: ( @@ -57,7 +56,7 @@ describe.sequential("VectorStoreIndex", () => { expect(entries[0]).toBe(entries[1]); }); - afterAll(() => { - rmSync(testDir, { recursive: true }); + afterAll(async () => { + await rm(testDir, { recursive: true }); }); }); diff --git a/packages/env/.madgerc b/packages/env/.madgerc new file mode 100644 index 000000000..66f0c66c6 --- /dev/null +++ b/packages/env/.madgerc @@ -0,0 +1,7 @@ +{ + "detectiveOptions": { + "ts": { + "skipTypeImports": true + } + } +} diff --git a/packages/env/jsr.json b/packages/env/jsr.json index e9233e0d8..f2406b997 100644 --- a/packages/env/jsr.json +++ b/packages/env/jsr.json @@ -5,6 +5,6 @@ ".": "./src/index.ts" }, "publish": { - "include": ["LICENSE", "README.md", "src/**/*.ts", "jsr.json"] + "include": ["LICENSE", "README.md", "src/**/*", "jsr.json"] } } diff --git a/packages/env/package.json b/packages/env/package.json index 5bcaf7dce..5ceb7fa83 100644 --- a/packages/env/package.json +++ b/packages/env/package.json @@ -62,6 +62,7 @@ "build:type": "tsc -p tsconfig.json", "postbuild": "node -e \"require('fs').writeFileSync('./dist/cjs/package.json', JSON.stringify({ type: 'commonjs' }))\"", "dev": "concurrently \"pnpm run build:esm --watch\" \"pnpm run build:cjs --watch\" \"pnpm run build:type --watch\"", + "circular-check": "madge -c ./src/index.ts", "test": "vitest" }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8dae7907e..f197b38fc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -40,6 +40,9 @@ importers: lint-staged: specifier: ^15.2.2 version: 15.2.2 + madge: + specifier: ^7.0.0 + version: 7.0.0(typescript@5.4.5) prettier: specifier: ^3.2.5 version: 3.2.5 @@ -461,9 +464,6 @@ importers: glob: specifier: ^10.3.12 version: 10.3.12 - madge: - specifier: ^7.0.0 - version: 7.0.0(typescript@5.4.5) typescript: specifier: ^5.4.5 version: 5.4.5 diff --git a/turbo.json b/turbo.json index 85e908e14..f8ee85347 100644 --- a/turbo.json +++ b/turbo.json @@ -12,6 +12,7 @@ "test": { "dependsOn": ["^build"] }, + "circular-check": {}, "e2e": { "dependsOn": ["^build"] }, -- GitLab