@expofp/utils 3.1.14 → 3.1.16

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.
@@ -3,62 +3,67 @@ import { deepFreeze } from './deep-freeze.js';
3
3
  import { fetchWithRetry } from './fetch-with-retry.js';
4
4
  import { importFsPromises } from './import-fs-promises.js';
5
5
  const log = debug('efp:utils:importJson');
6
- let importJsonNotAvailable;
7
6
  const jsonFrozen = new WeakSet();
7
+ // undefined = untried, function = works, null = known broken (Safari 14, CSP, etc.)
8
+ let importJsonNative;
8
9
  // ET: this is a workaround for Vite that analyzes dynamic imports and removes 'with' option
9
10
  // to be removed when Vite supports it properly
10
- // Cons: can have CSP issues in some environments
11
- const importJsonNative = new Function('url', 'return import(url, { with: { type: "json" } });');
12
- export async function importJson(url, options) {
13
- const opts = {
14
- forceFetch: options?.forceFetch || false,
15
- fetchCache: options?.fetchCache || new Map(),
16
- importCallback: options?.importCallback,
17
- signal: options?.signal || null,
18
- };
19
- // console.warn('importJson:', resolveContext.forceFetch);
20
- if (importJsonNotAvailable === undefined && !opts.forceFetch) {
11
+ // Cons: can have CSP issues in some environments.
12
+ // Lazy + try/caught: Safari 14's parser rejects the two-argument import() form at
13
+ // new Function() construction time, before any feature-detect call site can run.
14
+ function getImportJsonNative() {
15
+ if (importJsonNative === undefined) {
21
16
  try {
22
- await importJsonNative(url);
23
- importJsonNotAvailable = false;
17
+ importJsonNative = new Function('url', 'return import(url, { with: { type: "json" } });');
24
18
  }
25
19
  catch {
26
20
  log('Dynamic import not available');
27
- importJsonNotAvailable = true;
21
+ importJsonNative = null;
28
22
  }
29
23
  }
30
- let result = undefined;
31
- if (!importJsonNotAvailable && !opts.forceFetch) {
32
- log('Dynamic import', url);
33
- const module = await importJsonNative(url);
34
- result = module.default;
35
- }
36
- else if (url.startsWith('file:') && !opts.forceFetch) {
37
- log('Read from file system', url);
38
- result = await readJsonFromFs(new URL(url));
24
+ return importJsonNative ?? undefined;
25
+ }
26
+ export async function importJson(url, options) {
27
+ const forceFetch = options?.forceFetch ?? false;
28
+ const fetchCache = options?.fetchCache ?? new Map();
29
+ const signal = options?.signal ?? null;
30
+ let result;
31
+ const native = !forceFetch ? getImportJsonNative() : undefined;
32
+ if (native) {
33
+ try {
34
+ log('Dynamic import', url);
35
+ const module = await native(url);
36
+ result = module.default;
37
+ }
38
+ catch {
39
+ log('Dynamic import not available');
40
+ importJsonNative = null;
41
+ }
39
42
  }
40
- else {
41
- log('Fetch', url);
42
- result = await loadJson(url, opts.fetchCache, opts.signal || null);
43
+ if (result === undefined) {
44
+ if (url.startsWith('file:') && !forceFetch) {
45
+ log('Read from file system', url);
46
+ result = await readJsonFromFs(new URL(url));
47
+ }
48
+ else {
49
+ log('Fetch', url);
50
+ result = await loadJson(url, fetchCache, signal);
51
+ }
43
52
  }
44
- // log('Callback for imported JSON URL:', url, !!opts.importCallback);
45
- opts.importCallback?.(url);
46
- // Freeze the imported JSON to make it immutable
47
- // This is rather a guard against accidental mutations
53
+ options?.importCallback?.(url);
54
+ // Guard against accidental mutations of the imported JSON
48
55
  if (typeof result === 'object' && result !== null && !jsonFrozen.has(result)) {
49
56
  deepFreeze(result);
50
57
  jsonFrozen.add(result);
51
58
  }
52
59
  return result;
53
60
  }
54
- // const fetchCache = new Map<string, Promise<any>>();
55
61
  async function loadJson(url, fetchCache, signal) {
56
62
  const key = '__loadJson__' + url;
57
63
  if (fetchCache.has(key)) {
58
64
  return fetchCache.get(key);
59
65
  }
60
- const dataPromise = (async function loadJsonInner() {
61
- // in browser just use fetch, for node - fetch-retry
66
+ const dataPromise = (async () => {
62
67
  const response = await fetchWithRetry(url, { signal });
63
68
  if (!response.ok) {
64
69
  throw new Error(`Failed to fetch JSON from ${url}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@expofp/utils",
3
- "version": "3.1.14",
3
+ "version": "3.1.16",
4
4
  "type": "module",
5
5
  "description": "ExpoFP SDK internal: shared utilities",
6
6
  "homepage": "https://developer.expofp.com/",