@loaders.gl/scan 5.0.0-alpha.3 → 5.0.0-alpha.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/delta.cjs +540 -54
- package/dist/delta.cjs.map +3 -3
- package/dist/dist.min.js +36 -7
- package/dist/hudi.cjs +559 -55
- package/dist/hudi.cjs.map +3 -3
- package/dist/hudi.d.ts +4 -0
- package/dist/hudi.d.ts.map +1 -1
- package/dist/hudi.js +24 -1
- package/dist/hudi.js.map +1 -1
- package/dist/iceberg.cjs +538 -52
- package/dist/iceberg.cjs.map +3 -3
- package/dist/index.cjs +36 -7
- package/dist/index.cjs.map +1 -1
- package/dist/scan-engine.d.ts.map +1 -1
- package/dist/scan-engine.js +9 -7
- package/dist/scan-engine.js.map +1 -1
- package/package.json +8 -8
- package/src/hudi.ts +36 -1
- package/src/scan-engine.ts +11 -7
package/src/hudi.ts
CHANGED
|
@@ -23,9 +23,13 @@ import type {
|
|
|
23
23
|
|
|
24
24
|
/** A physical Parquet base file recorded by a Hudi snapshot descriptor. */
|
|
25
25
|
export type HudiBaseFile = Readonly<{
|
|
26
|
+
/** Path or URL of the Parquet base file. */
|
|
26
27
|
path: string;
|
|
28
|
+
/** Compressed file size in bytes, when recorded by the descriptor. */
|
|
27
29
|
size?: number;
|
|
30
|
+
/** Number of records in the base file, when recorded by the descriptor. */
|
|
28
31
|
numRecords?: number;
|
|
32
|
+
/** Partition column values associated with the base file. */
|
|
29
33
|
partitionValues?: Readonly<Record<string, unknown>>;
|
|
30
34
|
}>;
|
|
31
35
|
|
|
@@ -139,7 +143,7 @@ export class HudiTableSource
|
|
|
139
143
|
private async loadDescriptor(signal?: AbortSignal): Promise<HudiSnapshotDescriptor> {
|
|
140
144
|
const text =
|
|
141
145
|
typeof this.data === 'string'
|
|
142
|
-
? await (await this.fetch(this.
|
|
146
|
+
? await (await this.fetch(this.url, {headers: this.options.hudi?.headers, signal})).text()
|
|
143
147
|
: await this.data.text();
|
|
144
148
|
let descriptor: unknown;
|
|
145
149
|
try {
|
|
@@ -154,6 +158,37 @@ export class HudiTableSource
|
|
|
154
158
|
if (!Array.isArray(files)) {
|
|
155
159
|
throw new Error('Hudi snapshot descriptor must contain a files array');
|
|
156
160
|
}
|
|
161
|
+
for (const [index, file] of files.entries()) {
|
|
162
|
+
if (!file || typeof file !== 'object' || Array.isArray(file)) {
|
|
163
|
+
throw new Error(`Hudi snapshot file at index ${index} must be an object`);
|
|
164
|
+
}
|
|
165
|
+
const value = file as Record<string, unknown>;
|
|
166
|
+
if (typeof value.path !== 'string' || !value.path) {
|
|
167
|
+
throw new Error(
|
|
168
|
+
`Hudi snapshot file at index ${index} must contain a non-empty string path`
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
if (
|
|
172
|
+
value.size !== undefined &&
|
|
173
|
+
(typeof value.size !== 'number' || !Number.isFinite(value.size))
|
|
174
|
+
) {
|
|
175
|
+
throw new Error(`Hudi snapshot file at index ${index} size must be a finite number`);
|
|
176
|
+
}
|
|
177
|
+
if (
|
|
178
|
+
value.numRecords !== undefined &&
|
|
179
|
+
(typeof value.numRecords !== 'number' || !Number.isFinite(value.numRecords))
|
|
180
|
+
) {
|
|
181
|
+
throw new Error(`Hudi snapshot file at index ${index} numRecords must be a finite number`);
|
|
182
|
+
}
|
|
183
|
+
if (
|
|
184
|
+
value.partitionValues !== undefined &&
|
|
185
|
+
(!value.partitionValues ||
|
|
186
|
+
typeof value.partitionValues !== 'object' ||
|
|
187
|
+
Array.isArray(value.partitionValues))
|
|
188
|
+
) {
|
|
189
|
+
throw new Error(`Hudi snapshot file at index ${index} partitionValues must be an object`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
157
192
|
const value = descriptor as HudiSnapshotDescriptor;
|
|
158
193
|
if (
|
|
159
194
|
value.tableType &&
|
package/src/scan-engine.ts
CHANGED
|
@@ -41,16 +41,19 @@ export type ScanEngine = ScanBackend & {
|
|
|
41
41
|
queryAsync: (sourceTable: ArrowTable, options?: ArrowQueryOptions) => Promise<ArrowTable>;
|
|
42
42
|
};
|
|
43
43
|
|
|
44
|
-
const
|
|
44
|
+
const scanBackends = new Map<ScanBackendName, ScanBackend | ScanBackendLoader>();
|
|
45
45
|
|
|
46
46
|
const arrowScanBackend: ScanBackend = Object.freeze({
|
|
47
47
|
name: 'arrow',
|
|
48
48
|
query: queryArrowTable,
|
|
49
|
-
queryAsync:
|
|
49
|
+
queryAsync: (sourceTable, options) =>
|
|
50
|
+
Promise.resolve().then(queryArrowTable.bind(null, sourceTable, options)),
|
|
50
51
|
explain: explainArrowTableQuery
|
|
51
52
|
});
|
|
52
53
|
|
|
53
|
-
|
|
54
|
+
// Store the built-in backend directly. Apart from avoiding needless indirection, this keeps
|
|
55
|
+
// coverage stable when browser shards transform this shared module independently.
|
|
56
|
+
scanBackends.set('arrow', arrowScanBackend);
|
|
54
57
|
|
|
55
58
|
/**
|
|
56
59
|
* Registers a lazy scan backend without adding it to the default bundle.
|
|
@@ -63,7 +66,7 @@ export function registerScanBackend(name: ScanBackendName, loader: ScanBackendLo
|
|
|
63
66
|
if (!name || !loader) {
|
|
64
67
|
throw new Error('A scan backend name and loader are required');
|
|
65
68
|
}
|
|
66
|
-
|
|
69
|
+
scanBackends.set(name, loader);
|
|
67
70
|
}
|
|
68
71
|
|
|
69
72
|
/**
|
|
@@ -74,13 +77,14 @@ export function registerScanBackend(name: ScanBackendName, loader: ScanBackendLo
|
|
|
74
77
|
*/
|
|
75
78
|
export async function createScanEngine(options: ScanEngineOptions = {}): Promise<ScanEngine> {
|
|
76
79
|
const backendName = options.backend || 'arrow';
|
|
77
|
-
const
|
|
78
|
-
if (!
|
|
80
|
+
const registeredBackend = scanBackends.get(backendName);
|
|
81
|
+
if (!registeredBackend) {
|
|
79
82
|
throw new Error(
|
|
80
83
|
`Scan backend "${backendName}" is not registered. The built-in backend is "arrow".`
|
|
81
84
|
);
|
|
82
85
|
}
|
|
83
|
-
const backend =
|
|
86
|
+
const backend =
|
|
87
|
+
typeof registeredBackend === 'function' ? await registeredBackend() : registeredBackend;
|
|
84
88
|
if (backend.name !== backendName) {
|
|
85
89
|
throw new Error(
|
|
86
90
|
`Scan backend loader returned "${backend.name}" while "${backendName}" was requested.`
|