@file-viewer/renderer-data 2.1.2 → 2.1.4
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/data.js +55 -54
- package/dist/psd.d.ts +2 -2
- package/dist/psd.js +10 -9
- package/package.json +2 -2
package/dist/data.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { resolveFileViewerDataSqlWasmUrl } from '@file-viewer/core/assets';
|
|
2
|
+
import { createFileViewerTranslator } from '@file-viewer/core';
|
|
2
3
|
const dataStyle = `
|
|
3
4
|
.data-viewer{min-height:100%;padding:28px;background:#eef1f4;color:#132235}
|
|
4
5
|
.data-card{max-width:1080px;margin:0 auto;overflow:hidden;border:1px solid rgba(15,23,42,.08);border-radius:8px;background:#fff;box-shadow:0 18px 48px rgba(15,23,42,.12)}
|
|
@@ -24,7 +25,6 @@ const fontMimeMap = {
|
|
|
24
25
|
woff: 'font/woff',
|
|
25
26
|
woff2: 'font/woff2',
|
|
26
27
|
};
|
|
27
|
-
const sampleText = 'Flyfish Viewer 轻量预览 AaBbCc 1234567890';
|
|
28
28
|
const createStyle = (documentRef) => {
|
|
29
29
|
const style = documentRef.createElement('style');
|
|
30
30
|
style.textContent = dataStyle;
|
|
@@ -86,27 +86,27 @@ const getWindowSqlWasmOverride = (documentRef) => {
|
|
|
86
86
|
return documentRef.defaultView?.__FLYFISH_DATA_SQL_WASM_URL__ ||
|
|
87
87
|
(typeof window !== 'undefined' ? window.__FLYFISH_DATA_SQL_WASM_URL__ : undefined);
|
|
88
88
|
};
|
|
89
|
-
const renderFont = async (documentRef, buffer, type) => {
|
|
89
|
+
const renderFont = async (documentRef, buffer, type, t) => {
|
|
90
90
|
const family = `FlyfishPreviewFont-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
91
91
|
const ownerWindow = documentRef.defaultView || (typeof window !== 'undefined' ? window : undefined);
|
|
92
92
|
const FontFaceConstructor = ownerWindow?.FontFace || (typeof FontFace !== 'undefined' ? FontFace : undefined);
|
|
93
93
|
if (!FontFaceConstructor) {
|
|
94
|
-
throw new Error('
|
|
94
|
+
throw new Error(t('data.error.fontFaceUnsupported'));
|
|
95
95
|
}
|
|
96
96
|
const face = new FontFaceConstructor(family, buffer);
|
|
97
97
|
await face.load();
|
|
98
98
|
documentRef.fonts?.add(face);
|
|
99
99
|
return {
|
|
100
|
-
title: '
|
|
100
|
+
title: t('data.title.font'),
|
|
101
101
|
fontFamily: family,
|
|
102
102
|
summary: [
|
|
103
|
-
{ label: '
|
|
104
|
-
{ label: '
|
|
105
|
-
{ label: '
|
|
103
|
+
{ label: t('data.label.format'), value: type.toUpperCase() },
|
|
104
|
+
{ label: t('data.label.size'), value: formatBytes(buffer.byteLength) },
|
|
105
|
+
{ label: t('data.label.rendering'), value: 'Browser FontFace API' },
|
|
106
106
|
],
|
|
107
107
|
};
|
|
108
108
|
};
|
|
109
|
-
const renderSqlite = async (documentRef, buffer, context) => {
|
|
109
|
+
const renderSqlite = async (documentRef, buffer, context, t) => {
|
|
110
110
|
const { default: initSqlJs } = await import('sql.js');
|
|
111
111
|
const sqlWasmUrl = resolveFileViewerDataSqlWasmUrl(context?.options?.data, [
|
|
112
112
|
getWindowSqlWasmOverride(documentRef),
|
|
@@ -121,11 +121,11 @@ const renderSqlite = async (documentRef, buffer, context) => {
|
|
|
121
121
|
? db.exec(`select * from "${firstTable.replace(/"/g, '""')}" limit 30`)[0]
|
|
122
122
|
: null;
|
|
123
123
|
return {
|
|
124
|
-
title: '
|
|
124
|
+
title: t('data.title.sqlite'),
|
|
125
125
|
summary: [
|
|
126
|
-
{ label: '
|
|
127
|
-
{ label: '
|
|
128
|
-
{ label: '
|
|
126
|
+
{ label: t('data.label.objects'), value: String(tables.length) },
|
|
127
|
+
{ label: t('data.label.sampleTable'), value: firstTable || '-' },
|
|
128
|
+
{ label: t('data.label.rendering'), value: 'sql.js WASM' },
|
|
129
129
|
],
|
|
130
130
|
rows: rows
|
|
131
131
|
? makeRows(rows.values.map((values) => Object.fromEntries(rows.columns.map((column, index) => [column, values[index]]))))
|
|
@@ -136,7 +136,7 @@ const renderSqlite = async (documentRef, buffer, context) => {
|
|
|
136
136
|
db.close();
|
|
137
137
|
}
|
|
138
138
|
};
|
|
139
|
-
const renderParquet = async (buffer) => {
|
|
139
|
+
const renderParquet = async (buffer, t) => {
|
|
140
140
|
const { parquetMetadataAsync, parquetReadObjects } = await import('hyparquet');
|
|
141
141
|
const file = {
|
|
142
142
|
byteLength: buffer.byteLength,
|
|
@@ -145,16 +145,16 @@ const renderParquet = async (buffer) => {
|
|
|
145
145
|
const metadata = await parquetMetadataAsync(file);
|
|
146
146
|
const rows = await parquetReadObjects({ file, rowFormat: 'object', rowEnd: 30 });
|
|
147
147
|
return {
|
|
148
|
-
title: '
|
|
148
|
+
title: t('data.title.parquet'),
|
|
149
149
|
summary: [
|
|
150
|
-
{ label: '
|
|
151
|
-
{ label: '
|
|
152
|
-
{ label: '
|
|
150
|
+
{ label: t('data.label.rows'), value: metadata.num_rows?.toString?.() || '-' },
|
|
151
|
+
{ label: t('data.label.columns'), value: String(metadata.schema?.filter(item => item.name).length || 0) },
|
|
152
|
+
{ label: t('data.label.rendering'), value: 'hyparquet' },
|
|
153
153
|
],
|
|
154
154
|
rows: makeRows(rows),
|
|
155
155
|
};
|
|
156
156
|
};
|
|
157
|
-
const renderAvro = async (buffer) => {
|
|
157
|
+
const renderAvro = async (buffer, t) => {
|
|
158
158
|
const avro = await import('avsc/etc/browser/avsc.js');
|
|
159
159
|
const decoder = avro.createBlobDecoder(new Blob([buffer]));
|
|
160
160
|
const rows = [];
|
|
@@ -172,26 +172,26 @@ const renderAvro = async (buffer) => {
|
|
|
172
172
|
decoder.on('error', reject);
|
|
173
173
|
});
|
|
174
174
|
return {
|
|
175
|
-
title: '
|
|
175
|
+
title: t('data.title.avro'),
|
|
176
176
|
summary: [
|
|
177
|
-
{ label: '
|
|
178
|
-
{ label: 'Schema', value: schema ? '
|
|
179
|
-
{ label: '
|
|
177
|
+
{ label: t('data.label.sampleRows'), value: String(rows.length) },
|
|
178
|
+
{ label: 'Schema', value: schema ? t('data.value.schemaRead') : t('data.value.schemaUnread') },
|
|
179
|
+
{ label: t('data.label.rendering'), value: 'avsc' },
|
|
180
180
|
],
|
|
181
181
|
rows: makeRows(rows),
|
|
182
182
|
text: schema.slice(0, 6000),
|
|
183
183
|
};
|
|
184
184
|
};
|
|
185
|
-
const renderWasm = async (buffer) => {
|
|
185
|
+
const renderWasm = async (buffer, t) => {
|
|
186
186
|
const module = await WebAssembly.compile(buffer.slice(0));
|
|
187
187
|
const imports = WebAssembly.Module.imports(module);
|
|
188
188
|
const exports = WebAssembly.Module.exports(module);
|
|
189
189
|
return {
|
|
190
|
-
title: '
|
|
190
|
+
title: t('data.title.wasm'),
|
|
191
191
|
summary: [
|
|
192
|
-
{ label: '
|
|
193
|
-
{ label: '
|
|
194
|
-
{ label: '
|
|
192
|
+
{ label: t('data.label.imports'), value: String(imports.length) },
|
|
193
|
+
{ label: t('data.label.exports'), value: String(exports.length) },
|
|
194
|
+
{ label: t('data.label.rendering'), value: 'WebAssembly.Module' },
|
|
195
195
|
],
|
|
196
196
|
rows: makeRows([
|
|
197
197
|
...imports.map(item => ({ kind: 'import', module: item.module, name: item.name, type: item.kind })),
|
|
@@ -199,51 +199,51 @@ const renderWasm = async (buffer) => {
|
|
|
199
199
|
]),
|
|
200
200
|
};
|
|
201
201
|
};
|
|
202
|
-
const renderPostScriptLike = async (buffer, type) => ({
|
|
203
|
-
title: type === 'eps' ? '
|
|
202
|
+
const renderPostScriptLike = async (buffer, type, t) => ({
|
|
203
|
+
title: type === 'eps' ? t('data.title.eps') : t('data.title.ai'),
|
|
204
204
|
summary: [
|
|
205
|
-
{ label: '
|
|
206
|
-
{ label: '
|
|
207
|
-
{ label: '
|
|
205
|
+
{ label: t('data.label.magic'), value: readMagic(buffer).replace(/\s/g, ' ') },
|
|
206
|
+
{ label: t('data.label.size'), value: formatBytes(buffer.byteLength) },
|
|
207
|
+
{ label: t('data.label.note'), value: type === 'ai' ? t('data.note.aiSummary') : t('data.note.postscriptSummary') },
|
|
208
208
|
],
|
|
209
209
|
text: extractReadableText(buffer),
|
|
210
210
|
});
|
|
211
|
-
const renderWebArchive = async (buffer) => ({
|
|
212
|
-
title: '
|
|
211
|
+
const renderWebArchive = async (buffer, t) => ({
|
|
212
|
+
title: t('data.title.webarchive'),
|
|
213
213
|
summary: [
|
|
214
|
-
{ label: '
|
|
215
|
-
{ label: '
|
|
216
|
-
{ label: '
|
|
214
|
+
{ label: t('data.label.container'), value: readMagic(buffer).startsWith('bplist') ? 'Binary plist' : 'WebArchive' },
|
|
215
|
+
{ label: t('data.label.size'), value: formatBytes(buffer.byteLength) },
|
|
216
|
+
{ label: t('data.label.note'), value: t('data.note.webarchive') },
|
|
217
217
|
],
|
|
218
218
|
text: extractReadableText(buffer),
|
|
219
219
|
});
|
|
220
|
-
const buildPreview = async (documentRef, buffer, type, context) => {
|
|
220
|
+
const buildPreview = async (documentRef, buffer, type, context, t) => {
|
|
221
221
|
if (type in fontMimeMap) {
|
|
222
|
-
return renderFont(documentRef, buffer, type);
|
|
222
|
+
return renderFont(documentRef, buffer, type, t);
|
|
223
223
|
}
|
|
224
224
|
if (type === 'sqlite') {
|
|
225
|
-
return renderSqlite(documentRef, buffer, context);
|
|
225
|
+
return renderSqlite(documentRef, buffer, context, t);
|
|
226
226
|
}
|
|
227
227
|
if (type === 'parquet') {
|
|
228
|
-
return renderParquet(buffer);
|
|
228
|
+
return renderParquet(buffer, t);
|
|
229
229
|
}
|
|
230
230
|
if (type === 'avro') {
|
|
231
|
-
return renderAvro(buffer);
|
|
231
|
+
return renderAvro(buffer, t);
|
|
232
232
|
}
|
|
233
233
|
if (type === 'wasm') {
|
|
234
|
-
return renderWasm(buffer);
|
|
234
|
+
return renderWasm(buffer, t);
|
|
235
235
|
}
|
|
236
236
|
if (type === 'ai' || type === 'eps') {
|
|
237
|
-
return renderPostScriptLike(buffer, type);
|
|
237
|
+
return renderPostScriptLike(buffer, type, t);
|
|
238
238
|
}
|
|
239
239
|
if (type === 'webarchive') {
|
|
240
|
-
return renderWebArchive(buffer);
|
|
240
|
+
return renderWebArchive(buffer, t);
|
|
241
241
|
}
|
|
242
242
|
return {
|
|
243
|
-
title: '
|
|
243
|
+
title: t('data.title.summary'),
|
|
244
244
|
summary: [
|
|
245
|
-
{ label: '
|
|
246
|
-
{ label: '
|
|
245
|
+
{ label: t('data.label.format'), value: type.toUpperCase() },
|
|
246
|
+
{ label: t('data.label.size'), value: formatBytes(buffer.byteLength) },
|
|
247
247
|
],
|
|
248
248
|
text: extractReadableText(buffer),
|
|
249
249
|
};
|
|
@@ -282,7 +282,7 @@ const appendRows = (documentRef, parent, rows) => {
|
|
|
282
282
|
wrap.appendChild(table);
|
|
283
283
|
parent.appendChild(wrap);
|
|
284
284
|
};
|
|
285
|
-
const renderPreviewDom = (documentRef, preview, type) => {
|
|
285
|
+
const renderPreviewDom = (documentRef, preview, type, t) => {
|
|
286
286
|
const root = createElement(documentRef, 'div', 'data-viewer');
|
|
287
287
|
const card = createElement(documentRef, 'section', 'data-card');
|
|
288
288
|
const header = createElement(documentRef, 'header', 'data-header');
|
|
@@ -290,7 +290,7 @@ const renderPreviewDom = (documentRef, preview, type) => {
|
|
|
290
290
|
card.appendChild(header);
|
|
291
291
|
appendSummary(documentRef, card, preview.summary);
|
|
292
292
|
if (preview.fontFamily) {
|
|
293
|
-
const fontPreview = createElement(documentRef, 'div', 'font-preview',
|
|
293
|
+
const fontPreview = createElement(documentRef, 'div', 'font-preview', t('data.font.sample'));
|
|
294
294
|
fontPreview.style.fontFamily = preview.fontFamily;
|
|
295
295
|
card.appendChild(fontPreview);
|
|
296
296
|
}
|
|
@@ -298,7 +298,7 @@ const renderPreviewDom = (documentRef, preview, type) => {
|
|
|
298
298
|
const imageWrap = createElement(documentRef, 'div', 'asset-image');
|
|
299
299
|
const image = createElement(documentRef, 'img');
|
|
300
300
|
image.src = preview.image;
|
|
301
|
-
image.alt = '
|
|
301
|
+
image.alt = t('data.image.alt');
|
|
302
302
|
imageWrap.appendChild(image);
|
|
303
303
|
card.appendChild(imageWrap);
|
|
304
304
|
}
|
|
@@ -312,6 +312,7 @@ const renderPreviewDom = (documentRef, preview, type) => {
|
|
|
312
312
|
export default async function renderDataAsset(buffer, target, type = 'bin', context) {
|
|
313
313
|
const documentRef = target.ownerDocument || document;
|
|
314
314
|
const normalizedType = type.toLowerCase();
|
|
315
|
+
const t = createFileViewerTranslator(context?.options);
|
|
315
316
|
if (normalizedType === 'ai' && readMagic(buffer, 5) === '%PDF-' && context?.renderNestedBuffer) {
|
|
316
317
|
const rendered = await context.renderNestedBuffer(buffer, 'pdf', target, context);
|
|
317
318
|
if (rendered) {
|
|
@@ -320,10 +321,10 @@ export default async function renderDataAsset(buffer, target, type = 'bin', cont
|
|
|
320
321
|
}
|
|
321
322
|
if (normalizedType === 'psd') {
|
|
322
323
|
const { default: renderPsdAsset } = await import('./psd.js');
|
|
323
|
-
return renderPsdAsset(buffer, target);
|
|
324
|
+
return renderPsdAsset(buffer, target, context);
|
|
324
325
|
}
|
|
325
|
-
const preview = await buildPreview(documentRef, buffer, normalizedType, context);
|
|
326
|
-
const root = renderPreviewDom(documentRef, preview, normalizedType);
|
|
326
|
+
const preview = await buildPreview(documentRef, buffer, normalizedType, context, t);
|
|
327
|
+
const root = renderPreviewDom(documentRef, preview, normalizedType, t);
|
|
327
328
|
target.replaceChildren(createStyle(documentRef), root);
|
|
328
329
|
return {
|
|
329
330
|
$el: root,
|
package/dist/psd.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { type FileViewerRenderedInstance } from '@file-viewer/core';
|
|
2
|
-
export default function renderPsdAsset(buffer: ArrayBuffer, target: HTMLDivElement): Promise<FileViewerRenderedInstance>;
|
|
1
|
+
import { type FileRenderContext, type FileViewerRenderedInstance } from '@file-viewer/core';
|
|
2
|
+
export default function renderPsdAsset(buffer: ArrayBuffer, target: HTMLDivElement, context?: FileRenderContext): Promise<FileViewerRenderedInstance>;
|
package/dist/psd.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createFileViewerZoomChangeEmitter, registerFileViewerZoomProvider, unregisterFileViewerZoomProvider } from '@file-viewer/core';
|
|
1
|
+
import { createFileViewerTranslator, createFileViewerZoomChangeEmitter, registerFileViewerZoomProvider, unregisterFileViewerZoomProvider } from '@file-viewer/core';
|
|
2
2
|
const psdStyle = `
|
|
3
3
|
.psd-viewer{display:grid;height:100%;min-height:460px;grid-template-rows:auto minmax(0,1fr);--psd-bg:#eef1f4;--psd-surface:#fff;--psd-border:rgba(15,23,42,.1);--psd-text:#132235;--psd-muted:#64748b;--psd-accent:#0f766e;background:var(--psd-bg);color:var(--psd-text);box-sizing:border-box}
|
|
4
4
|
.psd-toolbar{position:sticky;top:0;z-index:2;display:flex;min-height:52px;align-items:center;justify-content:space-between;gap:14px;padding:10px 16px;border-bottom:1px solid var(--psd-border);background:rgba(255,255,255,.92);backdrop-filter:blur(12px);box-sizing:border-box}
|
|
@@ -106,8 +106,9 @@ const flattenLayers = (documentRef, layers, depth = 0, path = '') => {
|
|
|
106
106
|
const clampZoom = (value) => {
|
|
107
107
|
return Math.min(4, Math.max(0.1, Number(value.toFixed(2))));
|
|
108
108
|
};
|
|
109
|
-
export default async function renderPsdAsset(buffer, target) {
|
|
109
|
+
export default async function renderPsdAsset(buffer, target, context) {
|
|
110
110
|
const documentRef = target.ownerDocument || document;
|
|
111
|
+
const t = createFileViewerTranslator(context?.options);
|
|
111
112
|
const { readPsd } = await import('ag-psd');
|
|
112
113
|
const psd = readPsd(buffer, { useImageData: true });
|
|
113
114
|
const compositeCanvas = toCanvas(documentRef, psd.canvas || psd.imageData);
|
|
@@ -120,14 +121,14 @@ export default async function renderPsdAsset(buffer, target) {
|
|
|
120
121
|
root.dataset.viewerZoomProvider = 'psd';
|
|
121
122
|
const toolbar = createElement(documentRef, 'div', 'psd-toolbar');
|
|
122
123
|
const title = createElement(documentRef, 'div', 'psd-title');
|
|
123
|
-
title.append(createElement(documentRef, 'strong', undefined, '
|
|
124
|
+
title.append(createElement(documentRef, 'strong', undefined, t('psd.title')), createElement(documentRef, 'span', undefined, `${psd.width || 0} x ${psd.height || 0} · ${layers.length} layers · ag-psd`));
|
|
124
125
|
const actions = createElement(documentRef, 'div', 'psd-actions');
|
|
125
126
|
const zoomOut = createElement(documentRef, 'button', undefined, '-');
|
|
126
127
|
const zoomLabel = createElement(documentRef, 'span', undefined, '100%');
|
|
127
128
|
const zoomIn = createElement(documentRef, 'button', undefined, '+');
|
|
128
|
-
const reset = createElement(documentRef, 'button', undefined, '
|
|
129
|
-
const showAll = createElement(documentRef, 'button', undefined, '
|
|
130
|
-
const hideAll = createElement(documentRef, 'button', undefined, '
|
|
129
|
+
const reset = createElement(documentRef, 'button', undefined, t('psd.action.fit'));
|
|
130
|
+
const showAll = createElement(documentRef, 'button', undefined, t('psd.action.showAll'));
|
|
131
|
+
const hideAll = createElement(documentRef, 'button', undefined, t('psd.action.hideAll'));
|
|
131
132
|
[zoomOut, zoomIn, reset, showAll, hideAll].forEach(button => {
|
|
132
133
|
button.type = 'button';
|
|
133
134
|
});
|
|
@@ -136,7 +137,7 @@ export default async function renderPsdAsset(buffer, target) {
|
|
|
136
137
|
const layout = createElement(documentRef, 'div', 'psd-layout');
|
|
137
138
|
const sidebar = createElement(documentRef, 'aside', 'psd-sidebar');
|
|
138
139
|
const sidebarHeader = createElement(documentRef, 'div', 'psd-sidebar-header');
|
|
139
|
-
sidebarHeader.append(createElement(documentRef, 'span', undefined, '
|
|
140
|
+
sidebarHeader.append(createElement(documentRef, 'span', undefined, t('psd.layers.title')), createElement(documentRef, 'span', undefined, t('psd.layers.redrawable', { count: drawableLayers.length })));
|
|
140
141
|
const list = createElement(documentRef, 'ul', 'psd-layer-list');
|
|
141
142
|
sidebar.append(sidebarHeader, list);
|
|
142
143
|
const stage = createElement(documentRef, 'main', 'psd-stage');
|
|
@@ -169,7 +170,7 @@ export default async function renderPsdAsset(buffer, target) {
|
|
|
169
170
|
});
|
|
170
171
|
};
|
|
171
172
|
if (!layers.length) {
|
|
172
|
-
list.appendChild(createElement(documentRef, 'li', 'psd-empty', '
|
|
173
|
+
list.appendChild(createElement(documentRef, 'li', 'psd-empty', t('psd.layers.empty')));
|
|
173
174
|
}
|
|
174
175
|
layers.forEach(layer => {
|
|
175
176
|
const item = createElement(documentRef, 'li', 'psd-layer');
|
|
@@ -188,7 +189,7 @@ export default async function renderPsdAsset(buffer, target) {
|
|
|
188
189
|
redraw();
|
|
189
190
|
});
|
|
190
191
|
const copy = createElement(documentRef, 'div');
|
|
191
|
-
copy.append(createElement(documentRef, 'strong', undefined, layer.name), createElement(documentRef, 'span', undefined, `${layer.left},${layer.top} · ${layer.width} x ${layer.height}${layer.hidden ?
|
|
192
|
+
copy.append(createElement(documentRef, 'strong', undefined, layer.name), createElement(documentRef, 'span', undefined, `${layer.left},${layer.top} · ${layer.width} x ${layer.height}${layer.hidden ? ` · ${t('psd.layers.hidden')}` : ''}`));
|
|
192
193
|
item.append(checkbox, copy);
|
|
193
194
|
list.appendChild(item);
|
|
194
195
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@file-viewer/renderer-data",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.4",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Standalone data asset renderer plugin for Flyfish File Viewer with PSD, SQLite, Parquet, Avro, WASM, font, AI/EPS, and WebArchive previews.",
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"LICENSE"
|
|
58
58
|
],
|
|
59
59
|
"dependencies": {
|
|
60
|
-
"@file-viewer/core": "^2.1.
|
|
60
|
+
"@file-viewer/core": "^2.1.4",
|
|
61
61
|
"ag-psd": "^30.1.1",
|
|
62
62
|
"avsc": "^5.7.9",
|
|
63
63
|
"hyparquet": "^1.26.0",
|