full-json-extractor 1.0.2 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/.vscode/launch.json +0 -21
- package/eslint.config.js +0 -39
- package/jest.config.js +0 -10
- package/src/__tests__/json-parser.test.ts +0 -171
- package/src/benchmark.ts +0 -60
- package/src/extractor.ts +0 -228
- package/src/index.ts +0 -1
- package/src/interfaces.ts +0 -37
- package/src/types/interval-tree-1d.d.ts +0 -39
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "full-json-extractor",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"clean": "rimraf dist build .cache",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"format:fix": "eslint . --fix",
|
|
9
9
|
"build": "tsc",
|
|
10
10
|
"test": "jest",
|
|
11
|
-
"release": "npm run test && npm run build"
|
|
11
|
+
"release": "npm run format && npm run test && npm run build"
|
|
12
12
|
},
|
|
13
13
|
"keywords": [
|
|
14
14
|
"json",
|
package/.vscode/launch.json
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
// Use IntelliSense to learn about possible attributes.
|
|
3
|
-
// Hover to view descriptions of existing attributes.
|
|
4
|
-
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
|
5
|
-
"version": "0.2.0",
|
|
6
|
-
"configurations": [
|
|
7
|
-
|
|
8
|
-
{
|
|
9
|
-
"type": "node",
|
|
10
|
-
"request": "launch",
|
|
11
|
-
"name": "Launch Program",
|
|
12
|
-
"skipFiles": [
|
|
13
|
-
"<node_internals>/**"
|
|
14
|
-
],
|
|
15
|
-
"program": "${workspaceFolder}\\dist\\index.js",
|
|
16
|
-
"outFiles": [
|
|
17
|
-
"${workspaceFolder}/**/*.js"
|
|
18
|
-
]
|
|
19
|
-
}
|
|
20
|
-
]
|
|
21
|
-
}
|
package/eslint.config.js
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
// eslint.config.mjs
|
|
2
|
-
import js from "@eslint/js";
|
|
3
|
-
import tseslint from "typescript-eslint";
|
|
4
|
-
import tsParser from "@typescript-eslint/parser";
|
|
5
|
-
import prettier from "eslint-plugin-prettier";
|
|
6
|
-
import { fileURLToPath } from "node:url";
|
|
7
|
-
import { dirname } from "node:path";
|
|
8
|
-
import prettierConfig from "eslint-config-prettier";
|
|
9
|
-
import { defineConfig } from "eslint/config";
|
|
10
|
-
|
|
11
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
12
|
-
|
|
13
|
-
export default defineConfig([
|
|
14
|
-
{ ignores: ["node_modules", "dist", "eslint.config.*", "jest.config.*"] },
|
|
15
|
-
|
|
16
|
-
{
|
|
17
|
-
files: ["**/*.{js,cjs,mjs}"],
|
|
18
|
-
extends: [js.configs.recommended],
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
files: ["**/*.{ts,tsx}"],
|
|
22
|
-
languageOptions: {
|
|
23
|
-
parser: tsParser,
|
|
24
|
-
parserOptions: {
|
|
25
|
-
projectService: true,
|
|
26
|
-
tsconfigRootDir: __dirname,
|
|
27
|
-
},
|
|
28
|
-
},
|
|
29
|
-
plugins: {
|
|
30
|
-
"@typescript-eslint": tseslint.plugin,
|
|
31
|
-
prettier,
|
|
32
|
-
},
|
|
33
|
-
rules: {
|
|
34
|
-
"prettier/prettier": ["error", { tabWidth: 4}],
|
|
35
|
-
}
|
|
36
|
-
},
|
|
37
|
-
prettierConfig,
|
|
38
|
-
tseslint.configs.stylisticTypeChecked,
|
|
39
|
-
]);
|
package/jest.config.js
DELETED
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
import { extractJsons } from "../extractor";
|
|
2
|
-
|
|
3
|
-
describe("extractJsonIntervals.test", () => {
|
|
4
|
-
it("only json, valid json, 1-depth", () => {
|
|
5
|
-
const testData = `{"sample_id": 1, "data": { "key": "xdsc" }}`;
|
|
6
|
-
const expectedData = [
|
|
7
|
-
{
|
|
8
|
-
sample_id: 1,
|
|
9
|
-
data: {
|
|
10
|
-
key: "xdsc",
|
|
11
|
-
},
|
|
12
|
-
},
|
|
13
|
-
];
|
|
14
|
-
|
|
15
|
-
expect(extractJsons(testData)[0]).toEqual(expectedData[0]);
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
it("only json, invalid json, 0-depth", () => {
|
|
19
|
-
const testData = "{key: 1}";
|
|
20
|
-
expect(extractJsons(testData)).toEqual([]);
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it("raw string, invalid json, 2-depth", () => {
|
|
24
|
-
const testData = '[hi] { "outer": { "inner": { key: 1 } } }';
|
|
25
|
-
expect(extractJsons(testData)).toEqual([]);
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it("raw string, valid json, 1-depth", () => {
|
|
29
|
-
const testData = `[hi] {"sample_id": 1, "data": { "key": "xdsc" }}`;
|
|
30
|
-
const expectedData = [
|
|
31
|
-
{
|
|
32
|
-
sample_id: 1,
|
|
33
|
-
data: {
|
|
34
|
-
key: "xdsc",
|
|
35
|
-
},
|
|
36
|
-
},
|
|
37
|
-
];
|
|
38
|
-
|
|
39
|
-
expect(extractJsons(testData)[0]).toEqual(expectedData[0]);
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it("raw string, valid escaped json, 1-depth", () => {
|
|
43
|
-
const testData = `[hi] "{\"sample_id\": 1, \"data\": { \"key\": \"xdsc\" }}"`;
|
|
44
|
-
const expectedData = [
|
|
45
|
-
{
|
|
46
|
-
sample_id: 1,
|
|
47
|
-
data: {
|
|
48
|
-
key: "xdsc",
|
|
49
|
-
},
|
|
50
|
-
},
|
|
51
|
-
];
|
|
52
|
-
|
|
53
|
-
expect(extractJsons(testData)[0]).toEqual(expectedData[0]);
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
it("raw string, valid json, 1-depth, 3 objects", () => {
|
|
57
|
-
const testData = `[hi] {"sample_id": 1, "data": { "key": "xdsc" }} {"sample_id": 2, "data": { "key": "xdsc" }} {"sample_id": 3, "data": { "key": "xdsc" }}`;
|
|
58
|
-
const expectedData = [
|
|
59
|
-
{
|
|
60
|
-
sample_id: 1,
|
|
61
|
-
data: {
|
|
62
|
-
key: "xdsc",
|
|
63
|
-
},
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
sample_id: 2,
|
|
67
|
-
data: {
|
|
68
|
-
key: "xdsc",
|
|
69
|
-
},
|
|
70
|
-
},
|
|
71
|
-
{
|
|
72
|
-
sample_id: 3,
|
|
73
|
-
data: {
|
|
74
|
-
key: "xdsc",
|
|
75
|
-
},
|
|
76
|
-
},
|
|
77
|
-
];
|
|
78
|
-
|
|
79
|
-
const result = extractJsons(testData);
|
|
80
|
-
for (const json of expectedData) {
|
|
81
|
-
expect(result).toContainEqual(json);
|
|
82
|
-
}
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
it("raw string, valid json, 1-depth, 3 objects, extra {", () => {
|
|
86
|
-
const testData = `[hi] {{"sample_id": 1, "data": { "key": "xdsc" }} {"sample_id": 2, "data": { "key": "xdsc" }} {"sample_id": 3, "data": { "key": "xdsc" }}`;
|
|
87
|
-
const expectedData = [
|
|
88
|
-
{
|
|
89
|
-
sample_id: 1,
|
|
90
|
-
data: {
|
|
91
|
-
key: "xdsc",
|
|
92
|
-
},
|
|
93
|
-
},
|
|
94
|
-
{
|
|
95
|
-
sample_id: 2,
|
|
96
|
-
data: {
|
|
97
|
-
key: "xdsc",
|
|
98
|
-
},
|
|
99
|
-
},
|
|
100
|
-
{
|
|
101
|
-
sample_id: 3,
|
|
102
|
-
data: {
|
|
103
|
-
key: "xdsc",
|
|
104
|
-
},
|
|
105
|
-
},
|
|
106
|
-
];
|
|
107
|
-
|
|
108
|
-
const result = extractJsons(testData);
|
|
109
|
-
for (const json of expectedData) {
|
|
110
|
-
expect(result).toContainEqual(json);
|
|
111
|
-
}
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
it("raw string, valid json, 1-depth, 3 objects, extra {, {} in string", () => {
|
|
115
|
-
const testData = `[hi] {{"sample_id": 1, "data": { "key": "x}dsc" }} {"sample_id": 2, "data": { "key": "xd{sc" }} {"sample_id": 3, "data": { "key": "xdsc" }}`;
|
|
116
|
-
const expectedData = [
|
|
117
|
-
{
|
|
118
|
-
sample_id: 1,
|
|
119
|
-
data: {
|
|
120
|
-
key: "x}dsc",
|
|
121
|
-
},
|
|
122
|
-
},
|
|
123
|
-
{
|
|
124
|
-
sample_id: 2,
|
|
125
|
-
data: {
|
|
126
|
-
key: "xd{sc",
|
|
127
|
-
},
|
|
128
|
-
},
|
|
129
|
-
{
|
|
130
|
-
sample_id: 3,
|
|
131
|
-
data: {
|
|
132
|
-
key: "xdsc",
|
|
133
|
-
},
|
|
134
|
-
},
|
|
135
|
-
];
|
|
136
|
-
|
|
137
|
-
const result = extractJsons(testData);
|
|
138
|
-
for (const json of expectedData) {
|
|
139
|
-
expect(result).toContainEqual(json);
|
|
140
|
-
}
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
it("raw string, valid json, 2-depth, 150 objects, extra {, {} in string", () => {
|
|
144
|
-
const testData: string[] = [];
|
|
145
|
-
for (let i = 0; i < 150; i++) {
|
|
146
|
-
testData.push(
|
|
147
|
-
`[hi] {{"sample_id": ${i}, "data": { "key": "x}ds{c", "subdata": { "key": 2 }}}`,
|
|
148
|
-
);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
const expectedData: object[] = [];
|
|
152
|
-
for (let i = 0; i < 150; i++) {
|
|
153
|
-
expectedData.push({
|
|
154
|
-
sample_id: i,
|
|
155
|
-
data: {
|
|
156
|
-
key: "x}ds{c",
|
|
157
|
-
subdata: {
|
|
158
|
-
key: 2,
|
|
159
|
-
},
|
|
160
|
-
},
|
|
161
|
-
});
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
const testDataString = testData.join("");
|
|
165
|
-
const result = extractJsons(testDataString);
|
|
166
|
-
expect(result.length).toEqual(150);
|
|
167
|
-
for (const json of expectedData) {
|
|
168
|
-
expect(result).toContainEqual(json);
|
|
169
|
-
}
|
|
170
|
-
});
|
|
171
|
-
});
|
package/src/benchmark.ts
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { performance } from "node:perf_hooks";
|
|
2
|
-
import process from "node:process";
|
|
3
|
-
|
|
4
|
-
import { extractJsons } from "./extractor.js";
|
|
5
|
-
|
|
6
|
-
function generateNestedJSON(depth: number): object {
|
|
7
|
-
let obj: any = { value: "test" };
|
|
8
|
-
for (let i = 0; i < depth; i++) {
|
|
9
|
-
obj = { nested: obj };
|
|
10
|
-
}
|
|
11
|
-
return obj;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function generateRawString(
|
|
15
|
-
depth: number,
|
|
16
|
-
count: number,
|
|
17
|
-
sizeFactor: number,
|
|
18
|
-
): string {
|
|
19
|
-
const jsons = [];
|
|
20
|
-
for (let i = 0; i < count; i++) {
|
|
21
|
-
const base = generateNestedJSON(depth);
|
|
22
|
-
(base as any).padding = "x".repeat(sizeFactor);
|
|
23
|
-
jsons.push(JSON.stringify(base));
|
|
24
|
-
}
|
|
25
|
-
return jsons.join(" some text in between ");
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function getMemoryUsageMB(): number {
|
|
29
|
-
const used = process.memoryUsage().heapUsed / 1024 / 1024;
|
|
30
|
-
return Math.round(used * 100) / 100;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
async function runBenchmark() {
|
|
34
|
-
const depths = [1, 3, 5, 10, 20];
|
|
35
|
-
const sizes = [0, 1000, 10000, 50_000]; // padding sizes
|
|
36
|
-
const counts = [1, 5, 10, 20]; // number of JSONs per string
|
|
37
|
-
|
|
38
|
-
for (const depth of depths) {
|
|
39
|
-
for (const size of sizes) {
|
|
40
|
-
for (const count of counts) {
|
|
41
|
-
const input = generateRawString(depth, count, size);
|
|
42
|
-
|
|
43
|
-
const memBefore = getMemoryUsageMB();
|
|
44
|
-
const start = performance.now();
|
|
45
|
-
|
|
46
|
-
const result = extractJsons(input);
|
|
47
|
-
|
|
48
|
-
const end = performance.now();
|
|
49
|
-
const memAfter = getMemoryUsageMB();
|
|
50
|
-
|
|
51
|
-
console.log(
|
|
52
|
-
`Depth=${depth}, Size=${size}, Count=${count} | Time=${(end - start).toFixed(3)} ms | ` +
|
|
53
|
-
`MemΔ=${(memAfter - memBefore).toFixed(3)} MB | OutputLen=${JSON.stringify(result).length}`,
|
|
54
|
-
);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
runBenchmark();
|
package/src/extractor.ts
DELETED
|
@@ -1,228 +0,0 @@
|
|
|
1
|
-
import IntervalTree from "interval-tree-1d";
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
BraceLocationInfo,
|
|
5
|
-
IntervalTreeType,
|
|
6
|
-
Limit,
|
|
7
|
-
MemoPosition,
|
|
8
|
-
} from "./interfaces";
|
|
9
|
-
import Denque from "denque";
|
|
10
|
-
|
|
11
|
-
const LBRACE = "{";
|
|
12
|
-
const RBRACE = "}";
|
|
13
|
-
|
|
14
|
-
class JsonExtractError extends Error {}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Extracts json objects from a given input string
|
|
18
|
-
* @param input input string
|
|
19
|
-
* @param limit Sets pre-check behavior. If set to 'log2', method will terminate pre-check after reaching log2(n) characters. Useful for extremely large malformed data i.e. many {} + non-json text
|
|
20
|
-
* Else, will do a O(n) pre-check scan to coarsely validate brace matches. Useful for many json objects (i.e. early termination)
|
|
21
|
-
* @returns array of JSON objects
|
|
22
|
-
*/
|
|
23
|
-
export function extractJsons<T = unknown>(
|
|
24
|
-
input: string,
|
|
25
|
-
limit: Limit = "none",
|
|
26
|
-
): T[] {
|
|
27
|
-
if (!input.length) {
|
|
28
|
-
return [];
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const locations = generateBracesPrefixAndSufix(input);
|
|
32
|
-
if (!locations.prefix.length || !locations.suffix.length) {
|
|
33
|
-
return [];
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
return findValidJsons<T>(locations, input, limit);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function setMapValue(
|
|
40
|
-
memo: Map<number, Set<number>>,
|
|
41
|
-
leftIndex: number,
|
|
42
|
-
rightIndex: number,
|
|
43
|
-
): boolean {
|
|
44
|
-
let set = memo.get(leftIndex);
|
|
45
|
-
if (!set) {
|
|
46
|
-
set = new Set<number>();
|
|
47
|
-
memo.set(leftIndex, set);
|
|
48
|
-
}
|
|
49
|
-
if (set.has(rightIndex)) {
|
|
50
|
-
return false;
|
|
51
|
-
}
|
|
52
|
-
set.add(rightIndex);
|
|
53
|
-
return true;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function findValidJsons<T = unknown>(
|
|
57
|
-
{ prefix, suffix }: BraceLocationInfo,
|
|
58
|
-
input: string,
|
|
59
|
-
limit: Limit,
|
|
60
|
-
): T[] {
|
|
61
|
-
const tree = IntervalTree();
|
|
62
|
-
const queue = new Denque<MemoPosition>([[0, suffix.length - 1]]);
|
|
63
|
-
const memo = new Map<number, Set<number>>([
|
|
64
|
-
[0, new Set([suffix.length - 1])],
|
|
65
|
-
]);
|
|
66
|
-
const jsons: T[] = [];
|
|
67
|
-
|
|
68
|
-
while (!queue.isEmpty()) {
|
|
69
|
-
const [leftIndex, rightIndex] = queue.shift()!;
|
|
70
|
-
const leftPosition: number = prefix[leftIndex]!;
|
|
71
|
-
const rightPosition: number = suffix[rightIndex]!;
|
|
72
|
-
|
|
73
|
-
if (
|
|
74
|
-
rightPosition < leftPosition ||
|
|
75
|
-
queryIntervalSync(tree, leftPosition, rightPosition)
|
|
76
|
-
) {
|
|
77
|
-
continue;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const set = memo.get(leftIndex)!;
|
|
81
|
-
set.delete(rightIndex);
|
|
82
|
-
|
|
83
|
-
try {
|
|
84
|
-
if (
|
|
85
|
-
isBalancedWithOneJson(input, leftPosition, rightPosition, limit)
|
|
86
|
-
) {
|
|
87
|
-
jsons.push(
|
|
88
|
-
JSON.parse(input.slice(leftPosition, rightPosition + 1)),
|
|
89
|
-
);
|
|
90
|
-
tree.insert([leftPosition, rightPosition]);
|
|
91
|
-
continue;
|
|
92
|
-
}
|
|
93
|
-
} catch (error) {
|
|
94
|
-
if (!(error instanceof SyntaxError)) {
|
|
95
|
-
throw error as JsonExtractError;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
if (
|
|
99
|
-
rightIndex - 1 >= 0 &&
|
|
100
|
-
setMapValue(memo, leftIndex, rightIndex - 1)
|
|
101
|
-
) {
|
|
102
|
-
queue.push([leftIndex, rightIndex - 1]);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
if (
|
|
106
|
-
leftIndex + 1 < prefix.length &&
|
|
107
|
-
setMapValue(memo, leftIndex + 1, rightIndex)
|
|
108
|
-
) {
|
|
109
|
-
queue.push([leftIndex + 1, rightIndex]);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
if (!set.size) {
|
|
113
|
-
memo.delete(leftIndex);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
return jsons;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
function generateBracesPrefixAndSufix(input: string): BraceLocationInfo {
|
|
120
|
-
const prefix: number[] = [];
|
|
121
|
-
const suffix: number[] = [];
|
|
122
|
-
|
|
123
|
-
for (let i = 0; i < input.length; i++) {
|
|
124
|
-
if (input[i] == LBRACE) {
|
|
125
|
-
prefix.push(i);
|
|
126
|
-
} else if (input[i] == RBRACE) {
|
|
127
|
-
suffix.push(i);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
return {
|
|
132
|
-
prefix,
|
|
133
|
-
suffix,
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
function generateLimit(input: string, left: number, limit: Limit) {
|
|
138
|
-
switch (limit) {
|
|
139
|
-
case "log2":
|
|
140
|
-
return (
|
|
141
|
-
left +
|
|
142
|
-
Math.min(input.length, Math.ceil(Math.log2(input.length)))
|
|
143
|
-
);
|
|
144
|
-
case "none":
|
|
145
|
-
return input.length;
|
|
146
|
-
default:
|
|
147
|
-
throw new JsonExtractError("unknown limit type provided");
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Coarse pre-check to filter out invalid json candidates. Short circuits if >1 json candidates exist in slice
|
|
153
|
-
* @param input
|
|
154
|
-
* @param left
|
|
155
|
-
* @param right
|
|
156
|
-
* @returns
|
|
157
|
-
*/
|
|
158
|
-
function isBalancedWithOneJson(
|
|
159
|
-
input: string,
|
|
160
|
-
left: number,
|
|
161
|
-
right: number,
|
|
162
|
-
limit: Limit,
|
|
163
|
-
): boolean {
|
|
164
|
-
const terminationThreshold = generateLimit(input, left, limit);
|
|
165
|
-
let braceCount = 0;
|
|
166
|
-
let inString = false;
|
|
167
|
-
let escapeNext = false;
|
|
168
|
-
let firstJsonObj = true;
|
|
169
|
-
|
|
170
|
-
for (let i = left; i <= right; i++) {
|
|
171
|
-
if (i >= terminationThreshold) {
|
|
172
|
-
return true;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
const char = input[i];
|
|
176
|
-
|
|
177
|
-
if (escapeNext) {
|
|
178
|
-
escapeNext = false;
|
|
179
|
-
continue;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
if (char === "\\") {
|
|
183
|
-
escapeNext = true;
|
|
184
|
-
continue;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
if (char === '"') {
|
|
188
|
-
inString = !inString;
|
|
189
|
-
continue;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
if (inString) continue;
|
|
193
|
-
|
|
194
|
-
if (char === LBRACE) {
|
|
195
|
-
braceCount++;
|
|
196
|
-
} else if (char === RBRACE) {
|
|
197
|
-
braceCount--;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
if (braceCount < 0) {
|
|
201
|
-
return false;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
if (braceCount === 0) {
|
|
205
|
-
if (!firstJsonObj) {
|
|
206
|
-
return false;
|
|
207
|
-
}
|
|
208
|
-
firstJsonObj = !firstJsonObj;
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
return braceCount === 0;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
function queryIntervalSync(
|
|
216
|
-
tree: IntervalTreeType,
|
|
217
|
-
low: number,
|
|
218
|
-
high: number,
|
|
219
|
-
): boolean {
|
|
220
|
-
let intervalExists = false;
|
|
221
|
-
tree.queryInterval(low, high, ([left, right]: [number, number]) => {
|
|
222
|
-
if (left < low && high < right) {
|
|
223
|
-
intervalExists = true;
|
|
224
|
-
return;
|
|
225
|
-
}
|
|
226
|
-
});
|
|
227
|
-
return intervalExists;
|
|
228
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./extractor";
|
package/src/interfaces.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
export interface BraceLocationInfo {
|
|
2
|
-
readonly prefix: number[];
|
|
3
|
-
readonly suffix: number[];
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
export type Interval = [number, number];
|
|
7
|
-
|
|
8
|
-
export interface IntervalTreeType {
|
|
9
|
-
readonly count: number;
|
|
10
|
-
insert(interval: Interval): void;
|
|
11
|
-
readonly intervals: Interval[];
|
|
12
|
-
queryInterval(lo: number, hi: number, cb: (interval: Interval) => any): any;
|
|
13
|
-
queryPoint(x: number, cb: (interval: Interval) => any): any;
|
|
14
|
-
remove(interval: Interval): boolean;
|
|
15
|
-
root: IntervalTreeNode | null;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export type Limit = "log2" | "none";
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* [left, right]: [number, number]
|
|
22
|
-
*/
|
|
23
|
-
export type MemoPosition = [number, number];
|
|
24
|
-
|
|
25
|
-
interface IntervalTreeNode {
|
|
26
|
-
count: number;
|
|
27
|
-
insert(interval: Interval): void;
|
|
28
|
-
intervals(result?: Interval[]): Interval[];
|
|
29
|
-
left: IntervalTreeNode | null;
|
|
30
|
-
leftPoints: Interval[];
|
|
31
|
-
mid: number;
|
|
32
|
-
queryInterval(lo: number, hi: number, cb: (interval: Interval) => any): any;
|
|
33
|
-
queryPoint(x: number, cb: (interval: Interval) => any): any;
|
|
34
|
-
remove(interval: Interval): number;
|
|
35
|
-
right: IntervalTreeNode | null;
|
|
36
|
-
rightPoints: Interval[];
|
|
37
|
-
}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
declare module "interval-tree-1d" {
|
|
2
|
-
type Interval = [number, number];
|
|
3
|
-
|
|
4
|
-
interface IntervalTree {
|
|
5
|
-
readonly count: number;
|
|
6
|
-
insert(interval: Interval): void;
|
|
7
|
-
readonly intervals: Interval[];
|
|
8
|
-
queryInterval(
|
|
9
|
-
lo: number,
|
|
10
|
-
hi: number,
|
|
11
|
-
cb: (interval: Interval) => any,
|
|
12
|
-
): any;
|
|
13
|
-
queryPoint(x: number, cb: (interval: Interval) => any): any;
|
|
14
|
-
remove(interval: Interval): boolean;
|
|
15
|
-
root: IntervalTreeNode | null;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
interface IntervalTreeNode {
|
|
19
|
-
count: number;
|
|
20
|
-
insert(interval: Interval): void;
|
|
21
|
-
intervals(result?: Interval[]): Interval[];
|
|
22
|
-
left: IntervalTreeNode | null;
|
|
23
|
-
leftPoints: Interval[];
|
|
24
|
-
mid: number;
|
|
25
|
-
queryInterval(
|
|
26
|
-
lo: number,
|
|
27
|
-
hi: number,
|
|
28
|
-
cb: (interval: Interval) => any,
|
|
29
|
-
): any;
|
|
30
|
-
queryPoint(x: number, cb: (interval: Interval) => any): any;
|
|
31
|
-
remove(interval: Interval): number;
|
|
32
|
-
right: IntervalTreeNode | null;
|
|
33
|
-
rightPoints: Interval[];
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function createWrapper(intervals?: Interval[]): IntervalTree;
|
|
37
|
-
|
|
38
|
-
export = createWrapper;
|
|
39
|
-
}
|