@loaders.gl/core 5.0.0-alpha.7 → 5.0.0-alpha.9
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/dist.dev.js +308 -52
- package/dist/dist.min.js +3 -3
- package/dist/index.cjs +293 -60
- package/dist/index.cjs.map +3 -3
- package/dist/lib/api/create-data-source.d.ts +1 -1
- package/dist/lib/api/create-data-source.d.ts.map +1 -1
- package/dist/lib/api/create-data-source.js +23 -2
- package/dist/lib/api/create-data-source.js.map +1 -1
- package/dist/lib/api/load-in-batches.d.ts.map +1 -1
- package/dist/lib/api/load-in-batches.js +17 -6
- package/dist/lib/api/load-in-batches.js.map +1 -1
- package/dist/lib/api/load.d.ts +3 -3
- package/dist/lib/api/load.d.ts.map +1 -1
- package/dist/lib/api/load.js +14 -5
- package/dist/lib/api/load.js.map +1 -1
- package/dist/lib/api/parse-file.d.ts.map +1 -1
- package/dist/lib/api/parse-file.js +6 -2
- package/dist/lib/api/parse-file.js.map +1 -1
- package/dist/lib/api/parse-in-batches.d.ts.map +1 -1
- package/dist/lib/api/parse-in-batches.js +6 -1
- package/dist/lib/api/parse-in-batches.js.map +1 -1
- package/dist/lib/api/parse.d.ts.map +1 -1
- package/dist/lib/api/parse.js +6 -1
- package/dist/lib/api/parse.js.map +1 -1
- package/dist/lib/init.js +1 -1
- package/dist/lib/loader-utils/get-fetch-function.js +4 -13
- package/dist/lib/loader-utils/get-fetch-function.js.map +1 -1
- package/dist/lib/loader-utils/option-defaults.d.ts.map +1 -1
- package/dist/lib/loader-utils/option-defaults.js +1 -0
- package/dist/lib/loader-utils/option-defaults.js.map +1 -1
- package/dist/lib/loader-utils/option-utils.d.ts +7 -0
- package/dist/lib/loader-utils/option-utils.d.ts.map +1 -1
- package/dist/lib/loader-utils/option-utils.js +29 -0
- package/dist/lib/loader-utils/option-utils.js.map +1 -1
- package/dist/lib/utils/response-utils.js +2 -11
- package/dist/lib/utils/response-utils.js.map +1 -1
- package/dist/null-loader.js +1 -1
- package/dist/null-worker-node.js +1 -1
- package/dist/null-worker.js +1 -1
- package/package.json +6 -6
- package/src/lib/api/create-data-source.ts +31 -2
- package/src/lib/api/load-in-batches.ts +18 -11
- package/src/lib/api/load.ts +44 -6
- package/src/lib/api/parse-file.ts +15 -2
- package/src/lib/api/parse-in-batches.ts +11 -1
- package/src/lib/api/parse.ts +11 -1
- package/src/lib/loader-utils/get-fetch-function.ts +4 -15
- package/src/lib/loader-utils/option-defaults.ts +1 -0
- package/src/lib/loader-utils/option-utils.ts +34 -0
- package/src/lib/utils/response-utils.ts +2 -12
|
@@ -6,17 +6,20 @@ import type {
|
|
|
6
6
|
Loader,
|
|
7
7
|
LoaderOptions,
|
|
8
8
|
LoaderContext,
|
|
9
|
-
FetchLike,
|
|
10
9
|
BatchableDataType,
|
|
11
10
|
LoaderBatchType,
|
|
12
11
|
LoaderOptionsType,
|
|
13
12
|
LoaderOptionsWithShape,
|
|
14
13
|
LoaderShapeType
|
|
15
14
|
} from '@loaders.gl/loader-utils';
|
|
15
|
+
import {resolveLoaderAuthenticationOptions} from '@loaders.gl/loader-utils';
|
|
16
16
|
import {isLoaderObject} from '../loader-utils/normalize-loader';
|
|
17
17
|
import {getFetchFunction} from '../loader-utils/get-fetch-function';
|
|
18
|
+
import {applySearchParamsToUrl} from '../loader-utils/option-utils';
|
|
18
19
|
|
|
19
20
|
import {parseInBatches} from './parse-in-batches';
|
|
21
|
+
import {selectLoader} from './select-loader';
|
|
22
|
+
import {getLoaderImplementation} from './load-loader';
|
|
20
23
|
|
|
21
24
|
type FileType = string | File | Blob | Response | (string | File | Blob | Response)[] | FileList;
|
|
22
25
|
|
|
@@ -73,18 +76,13 @@ export function loadInBatches(
|
|
|
73
76
|
loadersArray = loaders as Loader | Loader[] | undefined;
|
|
74
77
|
}
|
|
75
78
|
|
|
76
|
-
// Select fetch function
|
|
77
|
-
const fetch = getFetchFunction(options || {});
|
|
78
|
-
|
|
79
79
|
// Single url/file
|
|
80
80
|
if (!Array.isArray(files)) {
|
|
81
|
-
return loadOneFileInBatches(files, loadersArray!, options || {}
|
|
81
|
+
return loadOneFileInBatches(files, loadersArray!, options || {});
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
// Multiple URLs / files
|
|
85
|
-
const promises = files.map(file =>
|
|
86
|
-
loadOneFileInBatches(file, loadersArray!, options || {}, fetch)
|
|
87
|
-
);
|
|
85
|
+
const promises = files.map(file => loadOneFileInBatches(file, loadersArray!, options || {}));
|
|
88
86
|
|
|
89
87
|
// No point in waiting here for all responses before starting to stream individual streams?
|
|
90
88
|
return promises;
|
|
@@ -93,12 +91,21 @@ export function loadInBatches(
|
|
|
93
91
|
async function loadOneFileInBatches(
|
|
94
92
|
file: FileType,
|
|
95
93
|
loaders: Loader | Loader[],
|
|
96
|
-
options: LoaderOptions
|
|
97
|
-
fetch: FetchLike
|
|
94
|
+
options: LoaderOptions
|
|
98
95
|
): Promise<AsyncIterable<unknown>> {
|
|
99
96
|
if (typeof file === 'string') {
|
|
100
97
|
const url = file;
|
|
101
|
-
const
|
|
98
|
+
const loader = await selectLoader(url, loaders, {
|
|
99
|
+
...options,
|
|
100
|
+
core: {...options.core, nothrow: true}
|
|
101
|
+
});
|
|
102
|
+
const authenticationLoader =
|
|
103
|
+
loader && !loader.getAuthentications && options.core?.credentials?.length
|
|
104
|
+
? await getLoaderImplementation(loader, options, url)
|
|
105
|
+
: loader;
|
|
106
|
+
options = await resolveLoaderAuthenticationOptions(authenticationLoader, url, options);
|
|
107
|
+
const fetch = getFetchFunction(options);
|
|
108
|
+
const response = await fetch(applySearchParamsToUrl(url, options));
|
|
102
109
|
// pick right overload
|
|
103
110
|
return Array.isArray(loaders)
|
|
104
111
|
? await parseInBatches(response, loaders, options)
|
package/src/lib/api/load.ts
CHANGED
|
@@ -14,12 +14,24 @@ import type {
|
|
|
14
14
|
LoaderArrayOptionsType,
|
|
15
15
|
LoaderArrayReturnType,
|
|
16
16
|
SourceLoader,
|
|
17
|
+
SourcePropsType,
|
|
18
|
+
SourceArrayOptionsType,
|
|
19
|
+
DataSourceOptions,
|
|
17
20
|
LoaderWithParser
|
|
18
21
|
} from '@loaders.gl/loader-utils';
|
|
19
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
isBlob,
|
|
24
|
+
isSourceLoader,
|
|
25
|
+
resolveAuthenticationOptions,
|
|
26
|
+
resolveLoaderAuthenticationOptions
|
|
27
|
+
} from '@loaders.gl/loader-utils';
|
|
20
28
|
import {isLoaderObject} from '../loader-utils/normalize-loader';
|
|
21
29
|
import {getFetchFunction} from '../loader-utils/get-fetch-function';
|
|
22
|
-
import {
|
|
30
|
+
import {
|
|
31
|
+
applySearchParamsToUrl,
|
|
32
|
+
normalizeLoaderOptions,
|
|
33
|
+
getGlobalLoaderOptions
|
|
34
|
+
} from '../loader-utils/option-utils';
|
|
23
35
|
import {fetchFile} from '../fetch/fetch-file';
|
|
24
36
|
|
|
25
37
|
import {parse} from './parse';
|
|
@@ -49,7 +61,7 @@ export async function load<
|
|
|
49
61
|
>(
|
|
50
62
|
url: string | DataType,
|
|
51
63
|
loader: LoaderT,
|
|
52
|
-
options?: OptionsT,
|
|
64
|
+
options?: LoaderT extends SourceLoader ? SourcePropsType<LoaderT> & DataSourceOptions : OptionsT,
|
|
53
65
|
context?: LoaderContext
|
|
54
66
|
): Promise<LoaderReturnType<LoaderT>>;
|
|
55
67
|
|
|
@@ -59,7 +71,7 @@ export async function load<
|
|
|
59
71
|
>(
|
|
60
72
|
url: string | DataType,
|
|
61
73
|
loaders: LoaderArrayT,
|
|
62
|
-
options?: OptionsT,
|
|
74
|
+
options?: LoaderArrayT extends SourceLoader[] ? SourceArrayOptionsType<LoaderArrayT> : OptionsT,
|
|
63
75
|
context?: LoaderContext
|
|
64
76
|
): Promise<LoaderArrayReturnType<LoaderArrayT>>;
|
|
65
77
|
|
|
@@ -95,8 +107,15 @@ export async function load(
|
|
|
95
107
|
resolvedOptions = options as LoaderOptions;
|
|
96
108
|
}
|
|
97
109
|
|
|
110
|
+
resolvedOptions ||= getGlobalLoaderOptions();
|
|
111
|
+
|
|
98
112
|
if (!Array.isArray(resolvedLoaders) && isSourceLoader(resolvedLoaders)) {
|
|
99
113
|
const sourceLoader = await resolveSourceLoader(resolvedLoaders, url, resolvedOptions);
|
|
114
|
+
resolvedOptions = await resolveLoaderAuthenticationOptions(
|
|
115
|
+
resolvedLoaders.getAuthentications ? resolvedLoaders : sourceLoader,
|
|
116
|
+
typeof url === 'string' ? url : '',
|
|
117
|
+
resolvedOptions || {}
|
|
118
|
+
);
|
|
100
119
|
const runtimeCoreApi = {
|
|
101
120
|
fetchFile,
|
|
102
121
|
parse,
|
|
@@ -119,6 +138,11 @@ export async function load(
|
|
|
119
138
|
isSourceLoader(resolvedLoaders[0])
|
|
120
139
|
) {
|
|
121
140
|
const sourceLoader = await resolveSourceLoader(resolvedLoaders[0], url, resolvedOptions);
|
|
141
|
+
resolvedOptions = await resolveLoaderAuthenticationOptions(
|
|
142
|
+
resolvedLoaders[0].getAuthentications ? resolvedLoaders[0] : sourceLoader,
|
|
143
|
+
typeof url === 'string' ? url : '',
|
|
144
|
+
resolvedOptions || {}
|
|
145
|
+
);
|
|
122
146
|
const runtimeCoreApi = {
|
|
123
147
|
fetchFile,
|
|
124
148
|
parse,
|
|
@@ -143,6 +167,11 @@ export async function load(
|
|
|
143
167
|
|
|
144
168
|
if (selectedLoader && isSourceLoader(selectedLoader)) {
|
|
145
169
|
const sourceLoader = await resolveSourceLoader(selectedLoader, url, resolvedOptions);
|
|
170
|
+
resolvedOptions = await resolveLoaderAuthenticationOptions(
|
|
171
|
+
selectedLoader.getAuthentications ? selectedLoader : sourceLoader,
|
|
172
|
+
typeof url === 'string' ? url : '',
|
|
173
|
+
resolvedOptions || {}
|
|
174
|
+
);
|
|
146
175
|
return sourceLoader.createDataSource(
|
|
147
176
|
url,
|
|
148
177
|
(resolvedOptions || {}) as LoaderOptionsType<SourceLoader>,
|
|
@@ -164,6 +193,11 @@ export async function load(
|
|
|
164
193
|
resolvedOptions,
|
|
165
194
|
url
|
|
166
195
|
);
|
|
196
|
+
resolvedOptions = await resolveLoaderAuthenticationOptions(
|
|
197
|
+
selectedLoader.getAuthentications ? selectedLoader : loaderImplementation,
|
|
198
|
+
url,
|
|
199
|
+
resolvedOptions || {}
|
|
200
|
+
);
|
|
167
201
|
const parseUrl = (
|
|
168
202
|
loaderImplementation as LoaderWithParser & {
|
|
169
203
|
parseUrl?: (
|
|
@@ -173,18 +207,22 @@ export async function load(
|
|
|
173
207
|
) => Promise<unknown>;
|
|
174
208
|
}
|
|
175
209
|
).parseUrl;
|
|
176
|
-
if (parseUrl)
|
|
210
|
+
if (parseUrl) {
|
|
211
|
+
const requestUrl = applySearchParamsToUrl(url, resolvedOptions);
|
|
212
|
+
return await parseUrl(requestUrl, resolvedOptions, context);
|
|
213
|
+
}
|
|
177
214
|
}
|
|
178
215
|
}
|
|
179
216
|
|
|
180
217
|
// Select fetch function
|
|
218
|
+
if (resolvedOptions) resolvedOptions = resolveAuthenticationOptions(resolvedOptions);
|
|
181
219
|
const fetch = getFetchFunction(resolvedOptions);
|
|
182
220
|
|
|
183
221
|
// at this point, `url` could be already loaded binary data
|
|
184
222
|
let data = url;
|
|
185
223
|
// url is a string, fetch the url
|
|
186
224
|
if (typeof url === 'string') {
|
|
187
|
-
data = await fetch(url);
|
|
225
|
+
data = await fetch(applySearchParamsToUrl(url, resolvedOptions));
|
|
188
226
|
// URL is Blob or File, fetchFile handles it (alt: we could generate ObjectURL here)
|
|
189
227
|
}
|
|
190
228
|
|
|
@@ -16,7 +16,11 @@ import type {
|
|
|
16
16
|
ReadableFile,
|
|
17
17
|
StrictLoaderOptions
|
|
18
18
|
} from '@loaders.gl/loader-utils';
|
|
19
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
isSourceLoader,
|
|
21
|
+
mergeOptions,
|
|
22
|
+
resolveLoaderAuthenticationOptions
|
|
23
|
+
} from '@loaders.gl/loader-utils';
|
|
20
24
|
import {validateWorkerVersion} from '@loaders.gl/worker-utils';
|
|
21
25
|
import {isLoaderObject} from '../loader-utils/normalize-loader';
|
|
22
26
|
import {normalizeOptions} from '../loader-utils/option-utils';
|
|
@@ -96,7 +100,16 @@ export async function parseFile(
|
|
|
96
100
|
}
|
|
97
101
|
|
|
98
102
|
// @ts-expect-error candidateLoaders may be a single forced loader
|
|
99
|
-
const
|
|
103
|
+
const normalizedOptions = normalizeOptions(options, loader, candidateLoaders, url);
|
|
104
|
+
const authenticationLoader =
|
|
105
|
+
!loader.getAuthentications && normalizedOptions.core?.credentials?.length
|
|
106
|
+
? await getLoaderImplementation(loader, normalizedOptions, url || context?.url)
|
|
107
|
+
: loader;
|
|
108
|
+
const strictOptions = await resolveLoaderAuthenticationOptions(
|
|
109
|
+
authenticationLoader,
|
|
110
|
+
url,
|
|
111
|
+
normalizedOptions
|
|
112
|
+
);
|
|
100
113
|
context = getLoaderContext(
|
|
101
114
|
// @ts-expect-error candidateLoaders may be a single forced loader
|
|
102
115
|
{url, _parse: parse, loaders: candidateLoaders},
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import type {Batch} from '@loaders.gl/schema';
|
|
6
6
|
import {isTable, makeBatchFromTable} from '@loaders.gl/schema-utils';
|
|
7
|
+
import {resolveLoaderAuthenticationOptions} from '@loaders.gl/loader-utils';
|
|
7
8
|
import type {
|
|
8
9
|
Loader,
|
|
9
10
|
LoaderWithParser,
|
|
@@ -112,7 +113,16 @@ export async function parseInBatches(
|
|
|
112
113
|
}
|
|
113
114
|
|
|
114
115
|
// Normalize options
|
|
115
|
-
const
|
|
116
|
+
const normalizedOptions = normalizeOptions(options, loader, loaderArray, url);
|
|
117
|
+
const authenticationLoader =
|
|
118
|
+
!loader.getAuthentications && normalizedOptions.core?.credentials?.length
|
|
119
|
+
? await getLoaderImplementation(loader, normalizedOptions, url || context?.url)
|
|
120
|
+
: loader;
|
|
121
|
+
const strictOptions = await resolveLoaderAuthenticationOptions(
|
|
122
|
+
authenticationLoader,
|
|
123
|
+
url || '',
|
|
124
|
+
normalizedOptions
|
|
125
|
+
);
|
|
116
126
|
context = getLoaderContext(
|
|
117
127
|
{url, _parseInBatches: parseInBatches, _parse: parse, loaders: loaderArray},
|
|
118
128
|
strictOptions,
|
package/src/lib/api/parse.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
// Copyright (c) vis.gl contributors
|
|
4
4
|
|
|
5
|
+
import {resolveLoaderAuthenticationOptions} from '@loaders.gl/loader-utils';
|
|
5
6
|
import type {
|
|
6
7
|
Loader,
|
|
7
8
|
LoaderContext,
|
|
@@ -121,7 +122,16 @@ export async function parse(
|
|
|
121
122
|
|
|
122
123
|
// Normalize options
|
|
123
124
|
// @ts-expect-error candidateLoaders
|
|
124
|
-
const
|
|
125
|
+
const normalizedOptions = normalizeOptions(options, loader, candidateLoaders, url); // Could be invalid...
|
|
126
|
+
const authenticationLoader =
|
|
127
|
+
!loader.getAuthentications && normalizedOptions.core?.credentials?.length
|
|
128
|
+
? await getLoaderImplementation(loader, normalizedOptions, url || context?.url)
|
|
129
|
+
: loader;
|
|
130
|
+
const strictOptions = await resolveLoaderAuthenticationOptions(
|
|
131
|
+
authenticationLoader,
|
|
132
|
+
url || '',
|
|
133
|
+
normalizedOptions
|
|
134
|
+
);
|
|
125
135
|
|
|
126
136
|
// Get a context (if already present, will be unchanged)
|
|
127
137
|
context = getLoaderContext(
|
|
@@ -27,9 +27,7 @@ export function getFetchFunction(
|
|
|
27
27
|
if (typeof fetchOption === 'function') {
|
|
28
28
|
fetchFunction = fetchOption;
|
|
29
29
|
} else if (isObject(fetchOption)) {
|
|
30
|
-
|
|
31
|
-
fetchFunction = (url, requestOptions) =>
|
|
32
|
-
baseFetch(url, mergeFetchOptions(fetchOption as RequestInit, requestOptions));
|
|
30
|
+
fetchFunction = context?.fetch || fetchFile;
|
|
33
31
|
} else if (context?.fetch) {
|
|
34
32
|
fetchFunction = context.fetch;
|
|
35
33
|
} else {
|
|
@@ -38,17 +36,8 @@ export function getFetchFunction(
|
|
|
38
36
|
|
|
39
37
|
return createAuthenticatedFetch({
|
|
40
38
|
fetch: fetchFunction,
|
|
41
|
-
credentials: loaderOptions.core?.credentials || []
|
|
39
|
+
credentials: loaderOptions.core?.credentials || [],
|
|
40
|
+
authentications: loaderOptions.core?.authentications,
|
|
41
|
+
fetchOptions: isObject(fetchOption) ? (fetchOption as RequestInit) : undefined
|
|
42
42
|
});
|
|
43
43
|
}
|
|
44
|
-
|
|
45
|
-
/** Combines static and per-request fetch options without replacing either header collection. */
|
|
46
|
-
function mergeFetchOptions(defaultOptions: RequestInit, requestOptions?: RequestInit): RequestInit {
|
|
47
|
-
const options = {...defaultOptions, ...requestOptions};
|
|
48
|
-
if (defaultOptions.headers || requestOptions?.headers) {
|
|
49
|
-
const headers = new Headers(defaultOptions.headers);
|
|
50
|
-
new Headers(requestOptions?.headers).forEach((value, key) => headers.set(key, value));
|
|
51
|
-
options.headers = headers;
|
|
52
|
-
}
|
|
53
|
-
return options;
|
|
54
|
-
}
|
|
@@ -42,6 +42,40 @@ const CORE_LOADER_OPTION_KEYS = [
|
|
|
42
42
|
'transforms'
|
|
43
43
|
] as const;
|
|
44
44
|
|
|
45
|
+
/**
|
|
46
|
+
* Appends loader search parameters to a URL while preserving existing parameters and fragments.
|
|
47
|
+
* @param url URL to update
|
|
48
|
+
* @param options Loader options containing optional search parameters
|
|
49
|
+
* @returns URL with the configured parameters appended
|
|
50
|
+
*/
|
|
51
|
+
export function applySearchParamsToUrl(url: string, options?: LoaderOptions): string {
|
|
52
|
+
const searchParams = options?.searchParams;
|
|
53
|
+
if (!searchParams || Object.keys(searchParams).length === 0 || /^(?:data|blob):/i.test(url)) {
|
|
54
|
+
return url;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const hashIndex = url.indexOf('#');
|
|
58
|
+
const hash = hashIndex >= 0 ? url.slice(hashIndex) : '';
|
|
59
|
+
const urlWithoutHash = hashIndex >= 0 ? url.slice(0, hashIndex) : url;
|
|
60
|
+
const queryIndex = urlWithoutHash.indexOf('?');
|
|
61
|
+
const path = queryIndex >= 0 ? urlWithoutHash.slice(0, queryIndex) : urlWithoutHash;
|
|
62
|
+
const query = queryIndex >= 0 ? urlWithoutHash.slice(queryIndex + 1) : '';
|
|
63
|
+
const existingSearchParams = new URLSearchParams(query);
|
|
64
|
+
const appendedSearchParams = new URLSearchParams();
|
|
65
|
+
for (const [key, value] of Object.entries(searchParams)) {
|
|
66
|
+
if (!existingSearchParams.has(key)) {
|
|
67
|
+
appendedSearchParams.set(key, String(value));
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const serializedAppendedSearchParams = appendedSearchParams.toString();
|
|
72
|
+
return `${path}${queryIndex >= 0 ? `?${query}` : ''}${
|
|
73
|
+
serializedAppendedSearchParams
|
|
74
|
+
? `${queryIndex >= 0 ? '&' : '?'}${serializedAppendedSearchParams}`
|
|
75
|
+
: ''
|
|
76
|
+
}${hash}`;
|
|
77
|
+
}
|
|
78
|
+
|
|
45
79
|
/**
|
|
46
80
|
* Global state for loaders.gl. Stored on `globalThis.loaders._state`
|
|
47
81
|
*/
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
// Copyright (c) vis.gl contributors
|
|
4
4
|
|
|
5
|
-
import {isResponse} from '@loaders.gl/loader-utils';
|
|
5
|
+
import {encodeArrayBufferToBase64, isResponse} from '@loaders.gl/loader-utils';
|
|
6
6
|
import {FetchError} from '../fetch/fetch-error';
|
|
7
7
|
import {getResourceContentLength, getResourceUrl, getResourceMIMEType} from './resource-utils';
|
|
8
8
|
import {shortenUrlForDisplay} from './url-utils';
|
|
@@ -120,18 +120,8 @@ async function getInitialDataUrl(
|
|
|
120
120
|
}
|
|
121
121
|
if (resource instanceof ArrayBuffer) {
|
|
122
122
|
const slice = resource.slice(0, INITIAL_DATA_LENGTH);
|
|
123
|
-
const base64 =
|
|
123
|
+
const base64 = encodeArrayBufferToBase64(slice);
|
|
124
124
|
return `data:base64,${base64}`;
|
|
125
125
|
}
|
|
126
126
|
return null;
|
|
127
127
|
}
|
|
128
|
-
|
|
129
|
-
// https://stackoverflow.com/questions/9267899/arraybuffer-to-base64-encoded-string
|
|
130
|
-
function arrayBufferToBase64(buffer: ArrayBuffer): string {
|
|
131
|
-
let binary = '';
|
|
132
|
-
const bytes = new Uint8Array(buffer);
|
|
133
|
-
for (let i = 0; i < bytes.byteLength; i++) {
|
|
134
|
-
binary += String.fromCharCode(bytes[i]);
|
|
135
|
-
}
|
|
136
|
-
return btoa(binary);
|
|
137
|
-
}
|