unbrowse 1.1.4 → 1.1.5
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/dist/cli.js +20 -16
- package/package.json +1 -1
- package/runtime-src/cli.ts +28 -16
package/dist/cli.js
CHANGED
|
@@ -615,24 +615,28 @@ function buildEntityIndex(items) {
|
|
|
615
615
|
function detectEntityIndex(data) {
|
|
616
616
|
if (data == null || typeof data !== "object")
|
|
617
617
|
return null;
|
|
618
|
-
|
|
619
|
-
const
|
|
620
|
-
if (Array.isArray(obj.included))
|
|
621
|
-
candidates.push(obj.included);
|
|
622
|
-
if (obj.data && typeof obj.data === "object") {
|
|
623
|
-
const d = obj.data;
|
|
624
|
-
if (Array.isArray(d.included))
|
|
625
|
-
candidates.push(d.included);
|
|
626
|
-
}
|
|
627
|
-
for (const arr of candidates) {
|
|
618
|
+
let best = null;
|
|
619
|
+
const check = (arr) => {
|
|
628
620
|
if (arr.length < 2)
|
|
629
|
-
|
|
630
|
-
const sample = arr.slice(0,
|
|
621
|
+
return;
|
|
622
|
+
const sample = arr.slice(0, 10);
|
|
631
623
|
const withUrn = sample.filter((i) => i != null && typeof i === "object" && typeof i.entityUrn === "string").length;
|
|
632
|
-
if (withUrn >= sample.length * 0.5)
|
|
633
|
-
|
|
624
|
+
if (withUrn >= sample.length * 0.5 && (!best || arr.length > best.length)) {
|
|
625
|
+
best = arr;
|
|
626
|
+
}
|
|
627
|
+
};
|
|
628
|
+
const obj = data;
|
|
629
|
+
for (const val of Object.values(obj)) {
|
|
630
|
+
if (Array.isArray(val)) {
|
|
631
|
+
check(val);
|
|
632
|
+
} else if (val != null && typeof val === "object" && !Array.isArray(val)) {
|
|
633
|
+
for (const nested of Object.values(val)) {
|
|
634
|
+
if (Array.isArray(nested))
|
|
635
|
+
check(nested);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
634
638
|
}
|
|
635
|
-
return null;
|
|
639
|
+
return best ? buildEntityIndex(best) : null;
|
|
636
640
|
}
|
|
637
641
|
function resolvePath(obj, path5, entityIndex) {
|
|
638
642
|
if (!path5 || obj == null)
|
|
@@ -658,7 +662,7 @@ function resolvePath(obj, path5, entityIndex) {
|
|
|
658
662
|
}
|
|
659
663
|
const rec = cur;
|
|
660
664
|
let val = rec[seg];
|
|
661
|
-
if (val
|
|
665
|
+
if (val == null && entityIndex) {
|
|
662
666
|
const ref = rec[`*${seg}`];
|
|
663
667
|
if (typeof ref === "string") {
|
|
664
668
|
val = entityIndex.get(ref);
|
package/package.json
CHANGED
package/runtime-src/cli.ts
CHANGED
|
@@ -118,28 +118,38 @@ function buildEntityIndex(items: unknown[]): Map<string, unknown> {
|
|
|
118
118
|
return index;
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
/** Detect if an object contains a normalized entity array and build the index.
|
|
121
|
+
/** Detect if an object contains a normalized entity array and build the index.
|
|
122
|
+
* Searches all top-level and one-level-nested arrays for entityUrn-keyed items,
|
|
123
|
+
* picking the largest qualifying array. Works for any normalized API shape. */
|
|
122
124
|
function detectEntityIndex(data: unknown): Map<string, unknown> | null {
|
|
123
125
|
if (data == null || typeof data !== "object") return null;
|
|
124
|
-
const obj = data as Record<string, unknown>;
|
|
125
126
|
|
|
126
|
-
|
|
127
|
-
const candidates: unknown[][] = [];
|
|
128
|
-
if (Array.isArray(obj.included)) candidates.push(obj.included);
|
|
129
|
-
if (obj.data && typeof obj.data === "object") {
|
|
130
|
-
const d = obj.data as Record<string, unknown>;
|
|
131
|
-
if (Array.isArray(d.included)) candidates.push(d.included);
|
|
132
|
-
}
|
|
127
|
+
let best: unknown[] | null = null;
|
|
133
128
|
|
|
134
|
-
|
|
135
|
-
if (arr.length < 2)
|
|
136
|
-
const sample = arr.slice(0,
|
|
129
|
+
const check = (arr: unknown[]) => {
|
|
130
|
+
if (arr.length < 2) return;
|
|
131
|
+
const sample = arr.slice(0, 10);
|
|
137
132
|
const withUrn = sample.filter(
|
|
138
133
|
(i) => i != null && typeof i === "object" && typeof (i as Record<string, unknown>).entityUrn === "string"
|
|
139
134
|
).length;
|
|
140
|
-
if (withUrn >= sample.length * 0.5
|
|
135
|
+
if (withUrn >= sample.length * 0.5 && (!best || arr.length > best.length)) {
|
|
136
|
+
best = arr;
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
const obj = data as Record<string, unknown>;
|
|
141
|
+
for (const val of Object.values(obj)) {
|
|
142
|
+
if (Array.isArray(val)) {
|
|
143
|
+
check(val);
|
|
144
|
+
} else if (val != null && typeof val === "object" && !Array.isArray(val)) {
|
|
145
|
+
// One level deep: { data: { included: [...] } }, { response: { entities: [...] } }, etc.
|
|
146
|
+
for (const nested of Object.values(val as Record<string, unknown>)) {
|
|
147
|
+
if (Array.isArray(nested)) check(nested);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
141
150
|
}
|
|
142
|
-
|
|
151
|
+
|
|
152
|
+
return best ? buildEntityIndex(best) : null;
|
|
143
153
|
}
|
|
144
154
|
|
|
145
155
|
/** Resolve a dot-path like "data.items[].name" against an object.
|
|
@@ -165,8 +175,10 @@ function resolvePath(obj: unknown, path: string, entityIndex?: Map<string, unkno
|
|
|
165
175
|
const rec = cur as Record<string, unknown>;
|
|
166
176
|
let val = rec[seg];
|
|
167
177
|
|
|
168
|
-
// URN reference resolution: if direct lookup fails, check for "*key" reference
|
|
169
|
-
|
|
178
|
+
// URN reference resolution: if direct lookup fails (or is null), check for "*key" reference.
|
|
179
|
+
// Normalized APIs (LinkedIn Voyager, Facebook Graph) set inline fields to null when
|
|
180
|
+
// the value is stored as a URN reference: e.g. socialDetail: null + *socialDetail: "urn:li:..."
|
|
181
|
+
if (val == null && entityIndex) {
|
|
170
182
|
const ref = rec[`*${seg}`];
|
|
171
183
|
if (typeof ref === "string") {
|
|
172
184
|
val = entityIndex.get(ref);
|