From 1049f8a1f5ec387901209703f7bfe9c57c794bcc Mon Sep 17 00:00:00 2001 From: Matt McCormick Date: Mon, 16 Feb 2026 21:29:27 -0500 Subject: [PATCH] fix(fizarrita): bundle codec-worker for Vite production builds Vite only recognises the combined `new Worker(new URL(..., import.meta.url))` pattern as a worker entry point. The previous approach stored the URL in a variable (`DEFAULT_WORKER_URL`) and passed it to `new Worker()` separately, causing Vite to copy codec-worker.js as a plain static asset without bundling its `./internals/*` dependencies. In production builds this left the worker's relative imports unresolved, crashing at runtime. Add `createDefaultWorker()` factory functions in get-worker.ts and set-worker.ts that use the combined expression. Deprecate `DEFAULT_WORKER_URL` (kept for backward compatibility) and export `createDefaultWorker` from the package. Bump version to 1.4.0. --- fizarrita/package.json | 2 +- fizarrita/src/get-worker.ts | 212 +++++++++++++++++++++++++----------- fizarrita/src/index.ts | 73 +++++++------ fizarrita/src/set-worker.ts | 141 +++++++++++++++++------- 4 files changed, 293 insertions(+), 135 deletions(-) diff --git a/fizarrita/package.json b/fizarrita/package.json index ae2a597..5078409 100644 --- a/fizarrita/package.json +++ b/fizarrita/package.json @@ -1,6 +1,6 @@ { "name": "@fideus-labs/fizarrita", - "version": "1.3.0", + "version": "1.4.0", "description": "Worker-pool-accelerated get/set for zarrita.js — offloads codec encode/decode to Web Workers.", "type": "module", "main": "./dist/index.js", diff --git a/fizarrita/src/get-worker.ts b/fizarrita/src/get-worker.ts index cd1684e..5fb6e3f 100644 --- a/fizarrita/src/get-worker.ts +++ b/fizarrita/src/get-worker.ts @@ -9,33 +9,55 @@ * Uses WorkerPool.runTasks() for bounded-concurrency scheduling. */ -import type { WorkerPool, WorkerPoolTask } from '@fideus-labs/worker-pool' +import type { WorkerPool, WorkerPoolTask } from "@fideus-labs/worker-pool" import type { - Array as ZarrArray, Chunk, DataType, Readable, Scalar, Slice, -} from 'zarrita' -import { BasicIndexer } from './internals/indexer.js' -import { create_codec_pipeline } from './internals/codec-pipeline.js' -import { setter } from './internals/setter.js' + Array as ZarrArray, +} from "zarrita" + +import { create_codec_pipeline } from "./internals/codec-pipeline.js" +import { BasicIndexer } from "./internals/indexer.js" +import { setter } from "./internals/setter.js" import { - get_ctr, - get_strides, - create_chunk_key_encoder, assertSharedArrayBufferAvailable, + create_chunk_key_encoder, createBuffer, -} from './internals/util.js' -import type { GetWorkerOptions, CodecChunkMeta, ChunkCache } from './types.js' -import { workerDecode, workerDecodeInto, getMetaId } from './worker-rpc.js' + get_ctr, + get_strides, +} from "./internals/util.js" +import type { ChunkCache, CodecChunkMeta, GetWorkerOptions } from "./types.js" +import { getMetaId, workerDecode, workerDecodeInto } from "./worker-rpc.js" /** * Default URL for the codec worker. Uses `import.meta.url` to resolve * relative to this module. + * + * @deprecated Use {@link createDefaultWorker} instead — it produces a + * `new Worker(new URL(..., import.meta.url))` expression that bundlers + * like Vite recognise as a worker entry point and bundle accordingly. */ -export const DEFAULT_WORKER_URL = new URL('./codec-worker.js', import.meta.url) +export const DEFAULT_WORKER_URL = new URL("./codec-worker.js", import.meta.url) + +/** + * Create a Worker using the default codec-worker script bundled with this + * package. + * + * Using `new Worker(new URL(..., import.meta.url))` in a single expression + * allows bundlers (Vite, Rollup, webpack 5) to detect the worker entry point + * and bundle its dependency graph into a self-contained asset. The previous + * approach — storing the URL in a variable and passing it to `new Worker()` + * separately — caused bundlers to treat the worker file as a plain static + * asset, leaving its relative `./internals/*` imports unresolved. + */ +export function createDefaultWorker(): Worker { + return new Worker(new URL("./codec-worker.js", import.meta.url), { + type: "module", + }) +} /** Shared TextDecoder instance. */ const decoder = new TextDecoder() @@ -81,13 +103,16 @@ export interface ArrayMetadata { fillValue: Scalar | null } -export async function readArrayMetadata( - arr: ZarrArray, -): Promise { +export async function readArrayMetadata< + D extends DataType, + Store extends Readable, +>(arr: ZarrArray): Promise { const store = arr.store // Try v3 first: read zarr.json - const v3Path = (arr.path === '/' ? '/zarr.json' : `${arr.path}/zarr.json`) as `/${string}` + const v3Path = ( + arr.path === "/" ? "/zarr.json" : `${arr.path}/zarr.json` + ) as `/${string}` const v3Bytes = await store.get(v3Path) if (v3Bytes) { const metadata = JSON.parse(decoder.decode(v3Bytes)) @@ -103,13 +128,18 @@ export async function readArrayMetadata }> = [] - if (metadata.order === 'F') { - codecs.push({ name: 'transpose', configuration: { order: 'F' } }) + const codecs: Array<{ + name: string + configuration: Record + }> = [] + if (metadata.order === "F") { + codecs.push({ name: "transpose", configuration: { order: "F" } }) } if (metadata.compressor) { const { id, ...configuration } = metadata.compressor @@ -122,11 +152,14 @@ export async function readArrayMetadata 0 ? codecs : [{ name: 'bytes', configuration: { endian: 'little' } }], + codecs: + codecs.length > 0 + ? codecs + : [{ name: "bytes", configuration: { endian: "little" } }], }, encodeChunkKey: create_chunk_key_encoder({ - name: 'v2', - configuration: { separator: metadata.dimension_separator ?? '.' }, + name: "v2", + configuration: { separator: metadata.dimension_separator ?? "." }, }), fillValue: metadata.fill_value ?? null, } @@ -137,9 +170,9 @@ export async function readArrayMetadata>> 0) !== 0xfd2fb528) return null + if (magic >>> 0 !== 0xfd2fb528) return null const fhd = compressed[4] const fcsFlag = (fhd >> 6) & 3 @@ -212,7 +247,9 @@ export function readZstdFrameContentSize(compressed: Uint8Array): number | null * * The nbytes field at offset 4 is the uncompressed data size in bytes. */ -export function readBloscFrameContentSize(compressed: Uint8Array): number | null { +export function readBloscFrameContentSize( + compressed: Uint8Array, +): number | null { if (compressed.length < 16) return null // Blosc version must be >= 1 (version byte at offset 0) @@ -284,14 +321,14 @@ async function probeDecompressedSize( // array_to_bytes codecs (bytes, etc.) don't compress // bytes_to_bytes codecs are the compressors return ( - name === 'gzip' || - name === 'zlib' || - name === 'blosc' || - name === 'zstd' || - name === 'lz4' || - name === 'bz2' || - name === 'lzma' || - name === 'snappy' + name === "gzip" || + name === "zlib" || + name === "blosc" || + name === "zstd" || + name === "lz4" || + name === "bz2" || + name === "lzma" || + name === "snappy" ) }) if (!hasCompression) { @@ -349,7 +386,8 @@ export function inferChunkShape( function scoreCandidate(shape: number[]): number { // L1 distance from metadata let l1 = 0 - for (let i = 0; i < shape.length; i++) l1 += Math.abs(shape[i] - metadataChunkShape[i]) + for (let i = 0; i < shape.length; i++) + l1 += Math.abs(shape[i] - metadataChunkShape[i]) // Penalty for non-power-of-2 dimensions let pow2Penalty = 0 @@ -381,7 +419,7 @@ export function inferChunkShape( } function addCandidate(shape: number[]): void { - const key = shape.join(',') + const key = shape.join(",") if (seen.has(key)) return seen.add(key) allCandidates.push({ shape, score: scoreCandidate(shape) }) @@ -395,7 +433,11 @@ export function inferChunkShape( ) if (fixedProduct === 0 || actualElements % fixedProduct !== 0) continue const candidate = actualElements / fixedProduct - if (candidate > 0 && candidate <= arrayShape[vary] && Number.isInteger(candidate)) { + if ( + candidate > 0 && + candidate <= arrayShape[vary] && + Number.isInteger(candidate) + ) { const result = [...metadataChunkShape] result[vary] = candidate addCandidate(result) @@ -463,11 +505,14 @@ export function inferChunkShape( * Returns true if the candidate is valid (probe returned 404/empty), * false if invalid (probe returned data, meaning chunks are too coarse). */ -async function validateCandidateChunkShape( +async function validateCandidateChunkShape< + D extends DataType, + Store extends Readable, +>( arr: ZarrArray, encodeChunkKey: (chunk_coords: number[]) => string, candidate: number[], - storeOpts?: Parameters[1], + storeOpts?: Parameters[1], ): Promise { const ndim = candidate.length @@ -526,12 +571,15 @@ async function validateCandidateChunkShape( +export async function probeActualChunkShape< + D extends DataType, + Store extends Readable, +>( arr: ZarrArray, encodeChunkKey: (chunk_coords: number[]) => string, codecMeta: CodecChunkMeta, bytesPerElement: number, - storeOpts?: Parameters[1], + storeOpts?: Parameters[1], ): Promise { const metadataChunkShape = codecMeta.chunk_shape const metaElements = metadataChunkShape.reduce((a, b) => a * b, 1) @@ -546,14 +594,22 @@ export async function probeActualChunkShape( arr: ZarrArray, selection: Sel | null = null, - opts: GetWorkerOptions[1]>, + opts: GetWorkerOptions[1]>, ): Promise< null extends Sel[number] ? Chunk @@ -632,7 +693,6 @@ export async function getWorker< : Scalar > { const { pool, workerUrl } = opts - const resolvedWorkerUrl = workerUrl ?? DEFAULT_WORKER_URL const useShared = !!opts.useSharedArrayBuffer const cache = opts.cache ?? NULL_CACHE @@ -644,15 +704,23 @@ export async function getWorker< const { codecMeta, encodeChunkKey, fillValue } = await readArrayMetadata(arr) const Ctr = get_ctr(arr.dtype) - const bytesPerElement = (Ctr as unknown as { BYTES_PER_ELEMENT: number }).BYTES_PER_ELEMENT + const bytesPerElement = (Ctr as unknown as { BYTES_PER_ELEMENT: number }) + .BYTES_PER_ELEMENT // Probe actual chunk shape — detects metadata vs data mismatch - const actualChunkShape = await probeActualChunkShape(arr, encodeChunkKey, codecMeta, bytesPerElement, opts.opts) + const actualChunkShape = await probeActualChunkShape( + arr, + encodeChunkKey, + codecMeta, + bytesPerElement, + opts.opts, + ) // Update codecMeta to use the actual chunk shape for codec pipeline - const correctedCodecMeta = actualChunkShape !== codecMeta.chunk_shape - ? { ...codecMeta, chunk_shape: actualChunkShape } - : codecMeta + const correctedCodecMeta = + actualChunkShape !== codecMeta.chunk_shape + ? { ...codecMeta, chunk_shape: actualChunkShape } + : codecMeta // Get stable metaId for the codec metadata (used by worker-rpc meta-init) const metaId = getMetaId(correctedCodecMeta) @@ -699,7 +767,11 @@ export async function getWorker< } tasks.push(async (workerSlot: Worker | null) => { - const worker = workerSlot ?? new Worker(resolvedWorkerUrl, { type: 'module' }) + const worker = + workerSlot ?? + (workerUrl + ? new Worker(workerUrl, { type: "module" }) + : createDefaultWorker()) // Fetch raw bytes from store on main thread const rawBytes = await arr.store.get(chunkPath, opts.opts) @@ -708,14 +780,17 @@ export async function getWorker< // Missing chunk — fill value, no worker needed const fillChunkShape = edgeChunkShape const fillChunkStrides = get_strides(fillChunkShape) - const fillChunkSize = fillChunkShape.reduce((a: number, b: number) => a * b, 1) + const fillChunkSize = fillChunkShape.reduce( + (a: number, b: number) => a * b, + 1, + ) const chunkData = new Ctr(fillChunkSize) if (fillValue != null) { // @ts-expect-error: fill_value type is union chunkData.fill(fillValue) } const chunk: Chunk = { - data: chunkData as Chunk['data'], + data: chunkData as Chunk["data"], shape: fillChunkShape, stride: fillChunkStrides, } @@ -750,7 +825,13 @@ export async function getWorker< // subsequent cache hits that skip the worker entirely. let chunk: Chunk try { - chunk = await workerDecode(worker, rawBytes, metaId, correctedCodecMeta, isEdgeChunk ? edgeChunkShape : undefined) + chunk = await workerDecode( + worker, + rawBytes, + metaId, + correctedCodecMeta, + isEdgeChunk ? edgeChunkShape : undefined, + ) } catch (error) { worker.terminate() throw error @@ -761,7 +842,13 @@ export async function getWorker< // Standard path: worker decodes, transfers back, main thread copies let chunk: Chunk try { - chunk = await workerDecode(worker, rawBytes, metaId, correctedCodecMeta, isEdgeChunk ? edgeChunkShape : undefined) + chunk = await workerDecode( + worker, + rawBytes, + metaId, + correctedCodecMeta, + isEdgeChunk ? edgeChunkShape : undefined, + ) } catch (error) { worker.terminate() throw error @@ -782,9 +869,10 @@ export async function getWorker< // If the final shape is empty (all integer selections), return a scalar if (indexer.shape.length === 0) { - const unwrap = 'get' in out.data - ? (out.data as unknown as { get(idx: number): Scalar }).get(0) - : (out.data as unknown as ArrayLike>)[0] + const unwrap = + "get" in out.data + ? (out.data as unknown as { get(idx: number): Scalar }).get(0) + : (out.data as unknown as ArrayLike>)[0] // @ts-expect-error: TS can't narrow conditional type return unwrap } diff --git a/fizarrita/src/index.ts b/fizarrita/src/index.ts index e563acd..7ad3e0c 100644 --- a/fizarrita/src/index.ts +++ b/fizarrita/src/index.ts @@ -6,46 +6,55 @@ * WorkerPool with bounded concurrency. */ +export type { ArrayMetadata } from "./get-worker.js" export { + createCacheKey, + createDefaultWorker, + /** @deprecated Use {@link createDefaultWorker} instead. */ + DEFAULT_WORKER_URL, + getStoreId, getWorker, - readZstdFrameContentSize, - readBloscFrameContentSize, inferChunkShape, - DEFAULT_WORKER_URL, - readArrayMetadata, probeActualChunkShape, - createCacheKey, - getStoreId, -} from './get-worker.js' -export type { ArrayMetadata } from './get-worker.js' -export { setWorker } from './set-worker.js' -export type { - GetWorkerOptions, - SetWorkerOptions, - CodecChunkMeta, - ChunkCache, - Projection, - Indices, -} from './types.js' - + readArrayMetadata, + readBloscFrameContentSize, + readZstdFrameContentSize, +} from "./get-worker.js" // Internals — exported for building custom workers that extend the codec worker -export { create_codec_pipeline } from './internals/codec-pipeline.js' -export { - get_ctr, - get_strides, - create_chunk_key_encoder, - createBuffer, - assertSharedArrayBufferAvailable, -} from './internals/util.js' -export type { ChunkKeyEncoding } from './internals/util.js' -export { setter, compat_chunk, set_from_chunk_binary } from './internals/setter.js' +export { create_codec_pipeline } from "./internals/codec-pipeline.js" +export type { ChunkProjection, IndexerProjection } from "./internals/indexer.js" export { BasicIndexer, normalize_selection, slice, slice_indices, -} from './internals/indexer.js' -export type { ChunkProjection, IndexerProjection } from './internals/indexer.js' - +} from "./internals/indexer.js" +export { + compat_chunk, + set_from_chunk_binary, + setter, +} from "./internals/setter.js" +export type { ChunkKeyEncoding } from "./internals/util.js" +export { + assertSharedArrayBufferAvailable, + create_chunk_key_encoder, + createBuffer, + get_ctr, + get_strides, +} from "./internals/util.js" +export { setWorker } from "./set-worker.js" +export type { + ChunkCache, + CodecChunkMeta, + GetWorkerOptions, + Indices, + Projection, + SetWorkerOptions, +} from "./types.js" // Worker RPC helpers — for composing custom workers -export { workerDecode, workerDecodeInto, workerEncode, getMetaId } from './worker-rpc.js' +export { + getMetaId, + workerDecode, + workerDecodeInto, + workerEncode, +} from "./worker-rpc.js" diff --git a/fizarrita/src/set-worker.ts b/fizarrita/src/set-worker.ts index 42f2962..89d8966 100644 --- a/fizarrita/src/set-worker.ts +++ b/fizarrita/src/set-worker.ts @@ -9,9 +9,8 @@ * Uses WorkerPool.runTasks() for bounded-concurrency scheduling. */ -import type { WorkerPool, WorkerPoolTask } from '@fideus-labs/worker-pool' +import type { WorkerPool, WorkerPoolTask } from "@fideus-labs/worker-pool" import type { - Array as ZarrArray, Chunk, DataType, Indices, @@ -20,23 +19,39 @@ import type { Scalar, Slice, TypedArray, -} from 'zarrita' -import { BasicIndexer, type IndexerProjection } from './internals/indexer.js' -import { setter } from './internals/setter.js' + Array as ZarrArray, +} from "zarrita" + +import { BasicIndexer, type IndexerProjection } from "./internals/indexer.js" +import { setter } from "./internals/setter.js" import { - get_ctr, - get_strides, - create_chunk_key_encoder, assertSharedArrayBufferAvailable, + create_chunk_key_encoder, createBuffer, -} from './internals/util.js' -import type { SetWorkerOptions, CodecChunkMeta } from './types.js' -import { workerDecode, workerEncode, workerEncodeShared, getMetaId } from './worker-rpc.js' + get_ctr, + get_strides, +} from "./internals/util.js" +import type { CodecChunkMeta, SetWorkerOptions } from "./types.js" +import { + getMetaId, + workerDecode, + workerEncode, + workerEncodeShared, +} from "./worker-rpc.js" /** - * Default URL for the codec worker. + * Create a Worker using the default codec-worker script bundled with this + * package. + * + * Using `new Worker(new URL(..., import.meta.url))` in a single expression + * allows bundlers (Vite, Rollup, webpack 5) to detect the worker entry point + * and bundle its dependency graph into a self-contained asset. */ -const DEFAULT_WORKER_URL = new URL('./codec-worker.js', import.meta.url) +function createDefaultWorker(): Worker { + return new Worker(new URL("./codec-worker.js", import.meta.url), { + type: "module", + }) +} /** Shared TextDecoder instance. */ const decoder = new TextDecoder() @@ -57,7 +72,9 @@ async function readArrayMetadata( const store = arr.store // Try v3 first: read zarr.json - const v3Path = (arr.path === '/' ? '/zarr.json' : `${arr.path}/zarr.json`) as `/${string}` + const v3Path = ( + arr.path === "/" ? "/zarr.json" : `${arr.path}/zarr.json` + ) as `/${string}` const v3Bytes = await store.get(v3Path) if (v3Bytes) { const metadata = JSON.parse(decoder.decode(v3Bytes)) @@ -73,13 +90,18 @@ async function readArrayMetadata( } // Try v2: read .zarray - const v2Path = (arr.path === '/' ? '/.zarray' : `${arr.path}/.zarray`) as `/${string}` + const v2Path = ( + arr.path === "/" ? "/.zarray" : `${arr.path}/.zarray` + ) as `/${string}` const v2Bytes = await store.get(v2Path) if (v2Bytes) { const metadata = JSON.parse(decoder.decode(v2Bytes)) - const codecs: Array<{ name: string; configuration: Record }> = [] - if (metadata.order === 'F') { - codecs.push({ name: 'transpose', configuration: { order: 'F' } }) + const codecs: Array<{ + name: string + configuration: Record + }> = [] + if (metadata.order === "F") { + codecs.push({ name: "transpose", configuration: { order: "F" } }) } if (metadata.compressor) { const { id, ...configuration } = metadata.compressor @@ -92,11 +114,14 @@ async function readArrayMetadata( codecMeta: { data_type: arr.dtype, chunk_shape: arr.chunks, - codecs: codecs.length > 0 ? codecs : [{ name: 'bytes', configuration: { endian: 'little' } }], + codecs: + codecs.length > 0 + ? codecs + : [{ name: "bytes", configuration: { endian: "little" } }], }, encodeChunkKey: create_chunk_key_encoder({ - name: 'v2', - configuration: { separator: metadata.dimension_separator ?? '.' }, + name: "v2", + configuration: { separator: metadata.dimension_separator ?? "." }, }), fillValue: metadata.fill_value ?? null, } @@ -107,9 +132,9 @@ async function readArrayMetadata( codecMeta: { data_type: arr.dtype, chunk_shape: arr.chunks, - codecs: [{ name: 'bytes', configuration: { endian: 'little' } }], + codecs: [{ name: "bytes", configuration: { endian: "little" } }], }, - encodeChunkKey: create_chunk_key_encoder({ name: 'default' }), + encodeChunkKey: create_chunk_key_encoder({ name: "default" }), fillValue: null, } } @@ -118,9 +143,10 @@ async function readArrayMetadata( // Helpers // --------------------------------------------------------------------------- -function flip_indexer_projection( - m: IndexerProjection, -): { from: number | Indices | null; to: number | Indices | null } { +function flip_indexer_projection(m: IndexerProjection): { + from: number | Indices | null + to: number | Indices | null +} { if (m.to == null) return { from: m.to, to: m.from } return { from: m.to, to: m.from } } @@ -130,7 +156,7 @@ function is_total_slice( shape: readonly number[], ): boolean { return selection.every((s, i) => { - if (typeof s === 'number') return false + if (typeof s === "number") return false const [start, stop, step] = s return stop - start === shape[i] && step === 1 }) @@ -177,7 +203,6 @@ export async function setWorker( opts: SetWorkerOptions, ): Promise { const { pool, workerUrl } = opts - const resolvedWorkerUrl = workerUrl ?? DEFAULT_WORKER_URL const useShared = !!opts.useSharedArrayBuffer if (useShared) { @@ -191,7 +216,8 @@ export async function setWorker( const metaId = getMetaId(codecMeta) const Ctr = get_ctr(arr.dtype) - const bytesPerElement = (Ctr as unknown as { BYTES_PER_ELEMENT: number }).BYTES_PER_ELEMENT + const bytesPerElement = (Ctr as unknown as { BYTES_PER_ELEMENT: number }) + .BYTES_PER_ELEMENT // Set up the indexer const indexer = new BasicIndexer({ @@ -215,7 +241,11 @@ export async function setWorker( const chunkPath = arr.resolve(chunkKey).path tasks.push(async (workerSlot: Worker | null) => { - const worker = workerSlot ?? new Worker(resolvedWorkerUrl, { type: 'module' }) + const worker = + workerSlot ?? + (workerUrl + ? new Worker(workerUrl, { type: "module" }) + : createDefaultWorker()) let chunkData: TypedArray @@ -223,9 +253,17 @@ export async function setWorker( // Totally replace this chunk — no need to fetch existing data // Use SAB when requested so the encode worker can read without transfer const buffer = createBuffer(chunkSize * bytesPerElement, useShared) - chunkData = new Ctr(buffer as ArrayBuffer, 0, chunkSize) as TypedArray - if (typeof value === 'object' && value !== null) { - const chunk = setter.prepare(chunkData, chunkShape.slice(), chunkStrides.slice()) as Chunk + chunkData = new Ctr( + buffer as ArrayBuffer, + 0, + chunkSize, + ) as TypedArray + if (typeof value === "object" && value !== null) { + const chunk = setter.prepare( + chunkData, + chunkShape.slice(), + chunkStrides.slice(), + ) as Chunk setter.set_from_chunk( chunk, value as Chunk, @@ -242,15 +280,26 @@ export async function setWorker( if (rawBytes) { // Decode existing chunk on worker try { - const decoded = await workerDecode(worker, rawBytes, metaId, codecMeta) + const decoded = await workerDecode( + worker, + rawBytes, + metaId, + codecMeta, + ) if (useShared) { // Copy decoded data into a SAB-backed buffer for zero-transfer encode const buffer = createBuffer( chunkSize * bytesPerElement, true, ) as SharedArrayBuffer - const sabData = new Ctr(buffer as unknown as ArrayBuffer, 0, chunkSize) as TypedArray - ;(sabData as unknown as { set(src: unknown): void }).set(decoded.data) + const sabData = new Ctr( + buffer as unknown as ArrayBuffer, + 0, + chunkSize, + ) as TypedArray + ;(sabData as unknown as { set(src: unknown): void }).set( + decoded.data, + ) chunkData = sabData } else { chunkData = decoded.data @@ -262,22 +311,34 @@ export async function setWorker( } else { // Missing chunk — start from fill value const buffer = createBuffer(chunkSize * bytesPerElement, useShared) - chunkData = new Ctr(buffer as ArrayBuffer, 0, chunkSize) as TypedArray + chunkData = new Ctr( + buffer as ArrayBuffer, + 0, + chunkSize, + ) as TypedArray if (fillValue != null) { // @ts-expect-error: fill_value union chunkData.fill(fillValue) } } - const chunk = setter.prepare(chunkData, chunkShape.slice(), chunkStrides.slice()) as Chunk - if (typeof value === 'object' && value !== null) { + const chunk = setter.prepare( + chunkData, + chunkShape.slice(), + chunkStrides.slice(), + ) as Chunk + if (typeof value === "object" && value !== null) { setter.set_from_chunk( chunk, value as Chunk, flipped as Projection[], ) } else { - setter.set_scalar(chunk, chunkSelection as (number | Indices)[], value as Scalar) + setter.set_scalar( + chunk, + chunkSelection as (number | Indices)[], + value as Scalar, + ) } }