use record for headers

Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
Alexander Rose
2026-04-25 07:50:21 -07:00
parent 0937c84f47
commit c6874c922d
5 changed files with 31 additions and 30 deletions

View File

@@ -59,7 +59,7 @@ export async function getG3dDataBlock(ctx: PluginContext, header: G3dHeader, url
async function getRawData(ctx: PluginContext, urlOrData: string | Uint8Array, range: { offset: number, size: number }) {
if (typeof urlOrData === 'string') {
return await ctx.runTask(ctx.fetch({ url: urlOrData, headers: [['Range', `bytes=${range.offset}-${range.offset + range.size - 1}`]], type: 'binary' }));
return await ctx.runTask(ctx.fetch({ url: urlOrData, headers: { 'Range': `bytes=${range.offset}-${range.offset + range.size - 1}` }, type: 'binary' }));
} else {
return urlOrData.slice(range.offset, range.offset + range.size);
}

View File

@@ -1,7 +1,8 @@
/**
* Copyright (c) 2026 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author OpenAI Codex
* @author Ludovic Autin <ludovic.autin@gmail.com>
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { RuntimeContext } from '../../mol-task';
@@ -20,10 +21,10 @@ describe('graphql transport', () => {
if (!Asset.isUrl(asset)) throw new Error('expected URL asset');
expect(asset.url).toBe('https://example.org/graphql');
expect(asset.headers).toEqual([
['Content-Type', 'application/json; charset=utf-8'],
['Accept', 'application/json']
]);
expect(asset.headers).toEqual({
'Content-Type': 'application/json; charset=utf-8',
'Accept': 'application/json'
});
expect(asset.body).toContain('"query"');
expect(asset.body).toContain('"variables"');
@@ -55,10 +56,10 @@ describe('graphql transport', () => {
url: 'https://example.org/graphql',
type: 'json',
body: '{"query":"{ test }"}',
headers: [
['Content-Type', 'application/json'],
['Accept', 'application/json']
]
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
}).run();
expect(fetchSpy).toHaveBeenCalledWith('https://example.org/graphql', {

View File

@@ -17,10 +17,10 @@ type _File = File;
type Asset = Asset.Url | Asset.File
namespace Asset {
export type Url = { kind: 'url', id: UUID, url: string, title?: string, body?: string, headers?: [string, string][] }
export type Url = { kind: 'url', id: UUID, url: string, title?: string, body?: string, headers?: Record<string, string> }
export type File = { kind: 'file', id: UUID, name: string, file?: _File }
export function Url(url: string, options?: { body?: string, title?: string, headers?: [string, string][] }): Url {
export function Url(url: string, options?: { body?: string, title?: string, headers?: Record<string, string> }): Url {
return { kind: 'url', id: UUID.create22(), url, ...options };
}
@@ -54,7 +54,7 @@ namespace Asset {
return typeof url === 'string' ? url : url.url;
}
export function getUrlAsset(manager: AssetManager, url: string | Url, body?: string, headers?: [string, string][]) {
export function getUrlAsset(manager: AssetManager, url: string | Url, body?: string, headers?: Record<string, string>) {
if (typeof url === 'string') {
const asset = manager.tryFindUrl(url, body, headers);
return asset || Url(url, { body, headers });
@@ -63,13 +63,13 @@ namespace Asset {
}
}
function urlHeadersEqual(a?: [string, string][], b?: [string, string][]) {
const aLength = a?.length ?? 0;
const bLength = b?.length ?? 0;
if (aLength !== bLength) return false;
function urlHeadersEqual(a?: Record<string, string>, b?: Record<string, string>) {
const aKeys = a ? Object.keys(a) : [];
const bKeys = b ? Object.keys(b) : [];
if (aKeys.length !== bKeys.length) return false;
for (let i = 0; i < aLength; i++) {
if (a![i][0] !== b![i][0] || a![i][1] !== b![i][1]) return false;
for (const key of aKeys) {
if (a![key] !== b![key]) return false;
}
return true;
}
@@ -84,7 +84,7 @@ class AssetManager {
return iterableToArray(this._assets.values());
}
tryFindUrl(url: string, body?: string, headers?: [string, string][]): Asset.Url | undefined {
tryFindUrl(url: string, body?: string, headers?: Record<string, string>): Asset.Url | undefined {
const assets = this.assets.values();
while (true) {
const v = assets.next();

View File

@@ -34,7 +34,7 @@ export interface AjaxGetParams<T extends DataType = 'string'> {
url: string,
type?: T,
title?: string,
headers?: [string, string][],
headers?: Record<string, string>,
body?: string
}
@@ -250,7 +250,7 @@ function getRequestResponseType(type: DataType): XMLHttpRequestResponseType {
}
}
function ajaxGetInternal<T extends DataType>(title: string | undefined, url: string, type: T, body?: string, headers?: [string, string][]): Task<DataResponse<T>> {
function ajaxGetInternal<T extends DataType>(title: string | undefined, url: string, type: T, body?: string, headers?: Record<string, string>): Task<DataResponse<T>> {
if (RUNNING_IN_NODEJS) {
if (url.startsWith('file://')) {
return ajaxGetInternal_file_NodeJS(title, url, type, body, headers);
@@ -265,7 +265,7 @@ function ajaxGetInternal<T extends DataType>(title: string | undefined, url: str
xhttp.open(body ? 'post' : 'get', url, true);
if (headers) {
for (const [name, value] of headers) {
for (const [name, value] of Object.entries(headers)) {
xhttp.setRequestHeader(name, value);
}
}
@@ -311,7 +311,7 @@ function readFileAsync(filename: string): Promise<NonSharedBuffer> {
}
/** Alternative implementation of ajaxGetInternal for NodeJS for file:// protocol */
function ajaxGetInternal_file_NodeJS<T extends DataType>(title: string | undefined, url: string, type: T, body?: string, headers?: [string, string][]): Task<DataResponse<T>> {
function ajaxGetInternal_file_NodeJS<T extends DataType>(title: string | undefined, url: string, type: T, body?: string, headers?: Record<string, string>): Task<DataResponse<T>> {
if (!RUNNING_IN_NODEJS) throw new Error('This function should only be used when running in Node.js');
if (!url.startsWith('file://')) throw new Error('This function is only for URLs with protocol file://');
@@ -327,7 +327,7 @@ function ajaxGetInternal_file_NodeJS<T extends DataType>(title: string | undefin
}
/** Alternative implementation of ajaxGetInternal for NodeJS for http(s):// protocol */
function ajaxGetInternal_http_NodeJS<T extends DataType>(title: string | undefined, url: string, type: T, body?: string, headers?: [string, string][]): Task<DataResponse<T>> {
function ajaxGetInternal_http_NodeJS<T extends DataType>(title: string | undefined, url: string, type: T, body?: string, headers?: Record<string, string>): Task<DataResponse<T>> {
if (!RUNNING_IN_NODEJS) throw new Error('This function should only be used when running in Node.js');
const aborter = new AbortController();
@@ -337,7 +337,7 @@ function ajaxGetInternal_http_NodeJS<T extends DataType>(title: string | undefin
signal: aborter.signal,
method: body ? 'POST' : 'GET',
body,
headers: headers ? Object.fromEntries(headers) : void 0
headers
});
if (!(response.status >= 200 && response.status < 400)) {
throw new Error(`Download failed with status code ${response.status}`);

View File

@@ -57,10 +57,10 @@ export class GraphQLClient {
constructor(private url: string, private assetManager: AssetManager) { }
async request(ctx: RuntimeContext, query: string, variables?: Variables): Promise<Asset.Wrapper<'json'>> {
const headers: [string, string][] = [
['Content-Type', 'application/json; charset=utf-8'],
['Accept', 'application/json']
];
const headers: Record<string, string> = {
'Content-Type': 'application/json; charset=utf-8',
'Accept': 'application/json',
};
const body = JSON.stringify({ query, variables }, null, 2);
const url = Asset.getUrlAsset(this.assetManager, this.url, body, headers);
const result = await this.assetManager.resolve(url, 'json').runInContext(ctx);