@expofp/loader 1.0.78 → 1.0.91

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.
Files changed (96) hide show
  1. package/dist/bundle/ImportHttpRuntime.node-B85BNHJG.js +92 -0
  2. package/dist/bundle/ImportHttpRuntime.node-B85BNHJG.js.map +1 -0
  3. package/dist/bundle/bundle.js +236 -768
  4. package/dist/bundle/bundle.js.map +1 -1
  5. package/dist/bundle/fetch-retry.umd-g1itNdNw.js +115 -0
  6. package/dist/bundle/fetch-retry.umd-g1itNdNw.js.map +1 -0
  7. package/dist/esm/ImportHttpRuntime.node.d.ts +28 -0
  8. package/dist/esm/ImportHttpRuntime.node.js +139 -0
  9. package/dist/esm/_OLD_resolveRuntimeUrl.d.ts +1 -0
  10. package/dist/esm/_OLD_resolveRuntimeUrl.js +10 -0
  11. package/dist/esm/fetch.d.ts +1 -0
  12. package/dist/esm/fetch.js +14 -0
  13. package/dist/esm/importHttpRuntime.d.ts +1 -0
  14. package/dist/esm/importHttpRuntime.js +12 -0
  15. package/dist/esm/index.d.ts +10 -7
  16. package/dist/esm/index.js +40 -19
  17. package/dist/esm/resolve.d.ts +10 -44
  18. package/dist/esm/resolve.js +91 -295
  19. package/dist/esm/types.d.ts +11 -26
  20. package/package.json +3 -1
  21. package/dist/bundle/cssTextAssetResolver.offlineFunc-CWvHnYni.js +0 -19
  22. package/dist/bundle/cssTextAssetResolver.offlineFunc-CWvHnYni.js.map +0 -1
  23. package/dist/bundle/downloadOfflineZip-CNz_lUGZ.js +0 -2344
  24. package/dist/bundle/downloadOfflineZip-CNz_lUGZ.js.map +0 -1
  25. package/dist/bundle/legacyDataUrlBaseResolver.offlineFunc-DPaSp_zV.js +0 -87
  26. package/dist/bundle/legacyDataUrlBaseResolver.offlineFunc-DPaSp_zV.js.map +0 -1
  27. package/dist/bundle/makeOffline-Dj-0o5_7.js +0 -76
  28. package/dist/bundle/makeOffline-Dj-0o5_7.js.map +0 -1
  29. package/dist/bundle/makeOfflineBundle-D8tePWGI.js +0 -70
  30. package/dist/bundle/makeOfflineBundle-D8tePWGI.js.map +0 -1
  31. package/dist/bundle/saveOfflineZip.browser-BTQeRUY_.js +0 -7
  32. package/dist/bundle/saveOfflineZip.browser-BTQeRUY_.js.map +0 -1
  33. package/dist/bundle/tools-D0u8lBvQ.js +0 -102
  34. package/dist/bundle/tools-D0u8lBvQ.js.map +0 -1
  35. package/dist/esm/_OLD_fetchWithRetry.d.ts +0 -1
  36. package/dist/esm/_OLD_fetchWithRetry.js +0 -101
  37. package/dist/esm/importJson.d.ts +0 -2
  38. package/dist/esm/importJson.js +0 -57
  39. package/dist/esm/loadScript.d.ts +0 -16
  40. package/dist/esm/loadScript.js +0 -167
  41. package/dist/esm/logger.d.ts +0 -1
  42. package/dist/esm/logger.js +0 -5
  43. package/dist/esm/mutateManifest.d.ts +0 -2
  44. package/dist/esm/mutateManifest.js +0 -10
  45. package/dist/esm/offline/downloadOfflineZip.d.ts +0 -4
  46. package/dist/esm/offline/downloadOfflineZip.js +0 -15
  47. package/dist/esm/offline/generateZip.d.ts +0 -4
  48. package/dist/esm/offline/generateZip.js +0 -41
  49. package/dist/esm/offline/hashString.d.ts +0 -1
  50. package/dist/esm/offline/hashString.js +0 -16
  51. package/dist/esm/offline/index.d.ts +0 -14
  52. package/dist/esm/offline/index.js +0 -41
  53. package/dist/esm/offline/makeOffline.d.ts +0 -2
  54. package/dist/esm/offline/makeOffline.js +0 -144
  55. package/dist/esm/offline/makeOfflineBundle.d.ts +0 -4
  56. package/dist/esm/offline/makeOfflineBundle.js +0 -92
  57. package/dist/esm/offline/saveOfflineZip.browser.d.ts +0 -1
  58. package/dist/esm/offline/saveOfflineZip.browser.js +0 -3
  59. package/dist/esm/offline/saveOfflineZip.d.ts +0 -1
  60. package/dist/esm/offline/saveOfflineZip.js +0 -16
  61. package/dist/esm/offline/slugify.d.ts +0 -1
  62. package/dist/esm/offline/slugify.js +0 -61
  63. package/dist/esm/offline/tools.d.ts +0 -3
  64. package/dist/esm/offline/tools.js +0 -85
  65. package/dist/esm/resolvers/_OLD_expoResolver.d.ts +0 -1
  66. package/dist/esm/resolvers/_OLD_expoResolver.js +0 -49
  67. package/dist/esm/resolvers/assetResolver.d.ts +0 -6
  68. package/dist/esm/resolvers/assetResolver.js +0 -26
  69. package/dist/esm/resolvers/bundleAssetsResolver.d.ts +0 -2
  70. package/dist/esm/resolvers/bundleAssetsResolver.js +0 -20
  71. package/dist/esm/resolvers/cssTextAssetResolver.d.ts +0 -8
  72. package/dist/esm/resolvers/cssTextAssetResolver.js +0 -15
  73. package/dist/esm/resolvers/cssTextAssetResolver.offlineFunc.d.ts +0 -2
  74. package/dist/esm/resolvers/cssTextAssetResolver.offlineFunc.js +0 -22
  75. package/dist/esm/resolvers/expoRuntimeBranchResolver.d.ts +0 -2
  76. package/dist/esm/resolvers/expoRuntimeBranchResolver.js +0 -20
  77. package/dist/esm/resolvers/expoRuntimeGetBranchResolver.d.ts +0 -2
  78. package/dist/esm/resolvers/expoRuntimeGetBranchResolver.js +0 -14
  79. package/dist/esm/resolvers/expoRuntimeResolver.d.ts +0 -2
  80. package/dist/esm/resolvers/expoRuntimeResolver.js +0 -39
  81. package/dist/esm/resolvers/httpResolver.d.ts +0 -5
  82. package/dist/esm/resolvers/httpResolver.js +0 -14
  83. package/dist/esm/resolvers/index.d.ts +0 -2
  84. package/dist/esm/resolvers/index.js +0 -22
  85. package/dist/esm/resolvers/legacyAssetUrlsResolver.d.ts +0 -9
  86. package/dist/esm/resolvers/legacyAssetUrlsResolver.js +0 -116
  87. package/dist/esm/resolvers/legacyDataResolver.d.ts +0 -8
  88. package/dist/esm/resolvers/legacyDataResolver.js +0 -20
  89. package/dist/esm/resolvers/legacyDataUrlBaseResolver.d.ts +0 -8
  90. package/dist/esm/resolvers/legacyDataUrlBaseResolver.js +0 -15
  91. package/dist/esm/resolvers/legacyDataUrlBaseResolver.offlineFunc.d.ts +0 -2
  92. package/dist/esm/resolvers/legacyDataUrlBaseResolver.offlineFunc.js +0 -129
  93. package/dist/esm/returnCachedRef.d.ts +0 -1
  94. package/dist/esm/returnCachedRef.js +0 -12
  95. package/dist/esm/shared.d.ts +0 -8
  96. package/dist/esm/shared.js +0 -273
@@ -0,0 +1,139 @@
1
+ import debug from 'debug';
2
+ import crypto from 'node:crypto';
3
+ import fs from 'node:fs/promises';
4
+ import os from 'node:os';
5
+ import path from 'node:path';
6
+ import url from 'node:url';
7
+ const log = debug('efp:loader:nodeImportHttpRuntime');
8
+ const runtimeDirCache = new Map();
9
+ function sha256Hex(input) {
10
+ return crypto.createHash('sha256').update(input).digest('hex');
11
+ }
12
+ function toRuntimeDirUrl(runtime) {
13
+ const u = new URL(runtime);
14
+ // Ensure we treat runtime as a file URL and strip the last segment
15
+ const pathname = u.pathname;
16
+ const idx = pathname.lastIndexOf('/');
17
+ u.pathname = idx >= 0 ? pathname.slice(0, idx + 1) : '/';
18
+ // Keep search/fragment off for directory identity
19
+ u.search = '';
20
+ u.hash = '';
21
+ return u;
22
+ }
23
+ function isSafeRelative(p) {
24
+ // Reject absolute paths, Windows drive paths, and parent traversal.
25
+ if (!p || p.startsWith('/') || p.startsWith('\\'))
26
+ return false;
27
+ if (/^[a-zA-Z]:[\\/]/.test(p))
28
+ return false;
29
+ const norm = path.posix.normalize(p.replace(/\\/g, '/'));
30
+ if (norm.startsWith('..') || norm.includes('/../'))
31
+ return false;
32
+ return true;
33
+ }
34
+ async function ensureDir(dir) {
35
+ await fs.mkdir(dir, { recursive: true });
36
+ }
37
+ async function fileExists(fp) {
38
+ try {
39
+ await fs.stat(fp);
40
+ return true;
41
+ }
42
+ catch {
43
+ return false;
44
+ }
45
+ }
46
+ async function fetchJson(fetchImpl, url) {
47
+ const res = await fetchImpl(url);
48
+ if (!res.ok)
49
+ throw new Error(`Failed to fetch JSON ${url}: ${res.status} ${res.statusText}`);
50
+ return await res.json();
51
+ }
52
+ async function fetchToFile(fetchImpl, url, outPath) {
53
+ const res = await fetchImpl(url);
54
+ if (!res.ok)
55
+ throw new Error(`Failed to fetch ${url}: ${res.status} ${res.statusText}`);
56
+ const ab = await res.arrayBuffer();
57
+ await ensureDir(path.dirname(outPath));
58
+ await fs.writeFile(outPath, Buffer.from(ab));
59
+ }
60
+ function extractFileList(bundle) {
61
+ return bundle.files.map((f) => f.file);
62
+ // if (Array.isArray(bundle)) return bundle.slice();
63
+ // const fromKnown = bundle.files ?? bundle.assets ?? bundle.entries ?? undefined;
64
+ // if (Array.isArray(fromKnown)) return fromKnown.slice();
65
+ // // As a last resort, look for the first string[] field
66
+ // for (const v of Object.values(bundle)) {
67
+ // if (Array.isArray(v) && v.every((x) => typeof x === 'string')) {
68
+ // return (v as string[]).slice();
69
+ // }
70
+ // }
71
+ // throw new Error(
72
+ // 'bundle.json has no recognizable file list (expected array or {files/assets/entries})'
73
+ // );
74
+ }
75
+ async function runWithConcurrency(items, concurrency, worker) {
76
+ const queue = items.slice();
77
+ const workers = Array.from({ length: Math.max(1, concurrency) }, async () => {
78
+ while (queue.length) {
79
+ const item = queue.shift();
80
+ await worker(item);
81
+ }
82
+ });
83
+ await Promise.all(workers);
84
+ }
85
+ /**
86
+ * Downloads an HTTP runtime bundle (bundle.json + all referenced files)
87
+ * into a stable temp folder hashed by the runtime directory URL,
88
+ * then imports expofp.js locally via file://.
89
+ */
90
+ export async function nodeImportHttpRuntime(runtime, opts = {}) {
91
+ const { cacheRootDir = path.join(os.tmpdir(), 'efp-loader'), concurrency = 8, forceRedownload = false, fetchImpl = globalThis.fetch, } = opts;
92
+ log("Cache root dir: '%s'", cacheRootDir);
93
+ if (typeof fetchImpl !== 'function') {
94
+ throw new Error('No fetch() available. Use Node 18+ or pass fetchImpl (e.g. undici fetch).');
95
+ }
96
+ const runtimeDir = toRuntimeDirUrl(runtime);
97
+ const runtimeDirKey = runtimeDir.toString();
98
+ // One in-flight download/import per runtime dir
99
+ if (!forceRedownload && runtimeDirCache.has(runtimeDirKey)) {
100
+ return runtimeDirCache.get(runtimeDirKey);
101
+ }
102
+ const task = (async () => {
103
+ const hash = sha256Hex(runtimeDirKey).slice(0, 16);
104
+ const localRoot = path.join(cacheRootDir, hash);
105
+ await ensureDir(localRoot);
106
+ const bundleUrl = new URL('bundle.json', runtimeDir).toString();
107
+ log('Fetching bundle.json:', bundleUrl);
108
+ const bundle = (await fetchJson(fetchImpl, bundleUrl));
109
+ const files = extractFileList(bundle);
110
+ // Always include the entry file (from runtime) even if bundle.json forgot it
111
+ const entryName = path.posix.basename(new URL(runtime).pathname);
112
+ if (!files.includes(entryName))
113
+ files.unshift(entryName);
114
+ // Sanitize + normalize
115
+ const safeFiles = files.map((f) => f.replace(/\\/g, '/')).filter((f) => isSafeRelative(f));
116
+ if (safeFiles.length === 0) {
117
+ throw new Error(`bundle.json from ${bundleUrl} produced 0 safe files to download`);
118
+ }
119
+ log('Downloading files:', safeFiles.length, '->', localRoot);
120
+ await runWithConcurrency(safeFiles, concurrency, async (rel) => {
121
+ const outPath = path.join(localRoot, rel.split('/').join(path.sep));
122
+ if (!forceRedownload && (await fileExists(outPath)))
123
+ return;
124
+ const fileUrl = new URL(rel, runtimeDir).toString();
125
+ await fetchToFile(fetchImpl, fileUrl, outPath);
126
+ });
127
+ const localEntryPath = path.join(localRoot, entryName.split('/').join(path.sep));
128
+ if (!(await fileExists(localEntryPath))) {
129
+ throw new Error(`Entry file missing after download: ${localEntryPath} (from ${runtime})`);
130
+ }
131
+ const entryFileUrl = url.pathToFileURL(localEntryPath).toString();
132
+ log('Importing local runtime entry:', entryFileUrl);
133
+ // @ts-ignore - dynamic import URL string is fine in Node
134
+ const mod = (await import(entryFileUrl));
135
+ return mod;
136
+ })();
137
+ runtimeDirCache.set(runtimeDirKey, task);
138
+ return task;
139
+ }
@@ -0,0 +1 @@
1
+ export declare function resolveRuntimeUrl(manifest: any): Promise<string>;
@@ -0,0 +1,10 @@
1
+ // import $RefParser from '@apidevtools/json-schema-ref-parser';
2
+ // import { log } from 'debug';
3
+ import { resolve } from './resolve';
4
+ export async function resolveRuntimeUrl(manifest) {
5
+ manifest = await resolve(manifest);
6
+ if (!manifest.runtime) {
7
+ throw new Error('Manifest is missing runtime');
8
+ }
9
+ return await resolve(manifest.runtime);
10
+ }
@@ -0,0 +1 @@
1
+ export declare const fetch: (...args: Parameters<typeof window.fetch>) => ReturnType<typeof window.fetch>;
@@ -0,0 +1,14 @@
1
+ let fn;
2
+ export const fetch = async function fetch(...args) {
3
+ if (!fn) {
4
+ if (typeof window !== 'undefined' && typeof document !== 'undefined') {
5
+ fn = window.fetch.bind(window);
6
+ }
7
+ else {
8
+ const fr = await import('fetch-retry');
9
+ const fetchWithRetry = fr.default(globalThis.fetch);
10
+ fn = fetchWithRetry;
11
+ }
12
+ }
13
+ return fn.apply(null, args);
14
+ };
@@ -0,0 +1 @@
1
+ export declare function importHttpRuntime<TModule = any>(runtime: string): Promise<TModule>;
@@ -0,0 +1,12 @@
1
+ import { fetch } from './fetch';
2
+ export async function importHttpRuntime(runtime) {
3
+ // if browser, use dynamic import
4
+ if (typeof window !== 'undefined' && typeof document !== 'undefined') {
5
+ const module = await import(/* @vite-ignore */ runtime);
6
+ return module;
7
+ }
8
+ else {
9
+ const { nodeImportHttpRuntime } = await import('./ImportHttpRuntime.node');
10
+ return nodeImportHttpRuntime(runtime, { fetchImpl: fetch });
11
+ }
12
+ }
@@ -1,7 +1,10 @@
1
- export declare const mount: (manifest: unknown, ...args: IArguments[]) => Promise<any>;
2
- export declare const load: (manifest: unknown, ...args: IArguments[]) => Promise<any>;
3
- export declare const initialize: (manifest: unknown, ...args: IArguments[]) => Promise<any>;
4
- export declare function callFunction_Experimental(name: string, manifest: unknown, ...args: IArguments[]): Promise<any>;
5
- export { mutateManifest } from './mutateManifest';
6
- export { downloadOfflineZip, makeOfflineBundle, saveOfflineZip, makeOffline } from './offline';
7
- export { resolve } from './resolve';
1
+ import { LoaderApi, Manifest, Ref } from './types';
2
+ declare global {
3
+ const __efpLoaderBuildInfo: [version: string, builtAt: string] | undefined;
4
+ }
5
+ export declare function loadApi(manifest: Manifest | Ref | string, options?: {
6
+ fetchCallback?: (ref: string) => void;
7
+ }): Promise<LoaderApi>;
8
+ declare const api: LoaderApi;
9
+ export declare function load(manifest: Manifest | Ref | string, ...args: unknown[]): Promise<unknown>;
10
+ export default api;
package/dist/esm/index.js CHANGED
@@ -1,24 +1,45 @@
1
- import { log } from './logger';
2
- import { createFunction } from './shared';
3
- const info = globalThis.__efpLoaderBuildInfo;
4
- if (info) {
5
- log('Initialized', { version: info[0], builtAt: info[1] });
1
+ import debug from 'debug';
2
+ import { importHttpRuntime } from './importHttpRuntime';
3
+ import { resolve } from './resolve';
4
+ const log = debug('efp:loader');
5
+ if (typeof __efpLoaderBuildInfo !== 'undefined') {
6
+ log('Initialized', { version: __efpLoaderBuildInfo[0], builtAt: __efpLoaderBuildInfo[1] });
6
7
  }
7
8
  else {
8
9
  log('Initialized');
9
10
  }
10
- export const mount = createFunction('mount');
11
- export const load = createFunction('load');
12
- export const initialize = createFunction('initialize');
13
- // extra
14
- // export const createFloorPlan = createFunction('createFloorPlan');
15
- // export const rewriteManifestToLocalFiles = createFunction('rewriteManifestToLocalFiles');
16
- export async function callFunction_Experimental(name, manifest, ...args) {
17
- const fn = createFunction(name);
18
- return await fn(manifest, ...args);
11
+ export async function loadApi(manifest, options) {
12
+ log('Loading manifest:', manifest);
13
+ if (typeof manifest === 'string') {
14
+ manifest = { $ref: manifest };
15
+ }
16
+ manifest = await resolve(manifest, { fetchCallback: options?.fetchCallback });
17
+ const runtime = await resolve(manifest.runtime, {
18
+ fetchCallback: options?.fetchCallback,
19
+ });
20
+ log('Resolved runtime URL:', runtime);
21
+ if (typeof runtime !== 'string') {
22
+ throw new Error('Resolved runtime is not a string ');
23
+ }
24
+ const api = await importHttpRuntime(runtime);
25
+ api.meta = { runtime };
26
+ return api; //{ ...api, runtime } as LoaderApi;
19
27
  }
20
- export { mutateManifest } from './mutateManifest';
21
- export { downloadOfflineZip, makeOfflineBundle, saveOfflineZip, makeOffline } from './offline';
22
- export { resolve } from './resolve';
23
- // preloadJson('https://efp-runtime.expofp.com/branches/main.json');
24
- // preconnectUrl('https://efp-runtime.expofp.com/');
28
+ // allow calling api functions directly from here
29
+ const api = new Proxy({}, {
30
+ get: (_, prop) => {
31
+ return async (...args) => {
32
+ const manifest = args[0];
33
+ const runtimeApi = await loadApi(manifest);
34
+ const fn = runtimeApi[prop];
35
+ if (typeof fn !== 'function') {
36
+ throw new Error(`API has no function named '${prop}'`);
37
+ }
38
+ return fn.apply(runtimeApi, args);
39
+ };
40
+ },
41
+ });
42
+ export async function load(manifest, ...args) {
43
+ return api.load(manifest, ...args);
44
+ }
45
+ export default api;
@@ -1,45 +1,11 @@
1
- import type { Resolver } from './types';
2
- export interface ResolveContextInternal {
3
- forceFetch: boolean;
4
- refCache: Map<string, Promise<any>>;
5
- /**
6
- * Callback invoked when a URL is fetched or imported.
7
- */
8
- importCallback?: (url: string, type: 'json' | 'script') => void;
9
- signal: AbortSignal | null;
10
- preResolvedRefs?: Map<string, any>;
11
- }
12
- interface ResolveOptionsNotMutate extends Partial<ResolveContextInternal> {
13
- mutate?: false;
1
+ export type ResolveOptions = {
2
+ maxHops?: number;
14
3
  signal?: AbortSignal;
15
- }
16
- interface ResolveOptionMutate extends Omit<ResolveOptionsNotMutate, 'mutate'> {
17
- /**
18
- * Mutate original object during resolution.
19
- */
20
- mutate: true;
21
- }
22
- export type ResolveOptions = ResolveOptionsNotMutate | ResolveOptionMutate;
23
- /**
24
- * Resolves a value from an object using a JSON Pointer RFC 6901.
25
- *
26
- * @param object - The object to resolve the value from
27
- * @param jsonPointer - A JSON Pointer string that must start with '/'
28
- * @param options - Optional context for resolution, including cache and fetch callback
29
- * @returns A promise that resolves to the value at the specified JSON Pointer path
30
- * @throws {Error} If the JSON Pointer is not valid or if a $ref cannot be resolved
31
- *
32
- * @example
33
- * ```typescript
34
- * const obj = { foo: { bar: 'baz' } };
35
- * const result = await resolve(obj, '/foo/bar');
36
- * // result === 'baz'
37
- * ```
38
- */
39
- export declare function resolve(object: any, jsonPointer: string, options: ResolveOptionMutate): Promise<void>;
40
- export declare function resolve<T = any>(object: any, jsonPointer: string, options?: ResolveOptionsNotMutate): Promise<T>;
41
- export declare function resolverResolve(resolver: Resolver, ref: string, context: ResolveContextInternal): Promise<any>;
42
- export declare function canResolve(resolver: Resolver, ref: string): boolean;
43
- export declare function canResolveRefSchema(ref: string, prefixBase: string): boolean;
44
- export declare function parseRefValue(ref: string): string;
45
- export {};
4
+ onHop?: (info: {
5
+ hop: number;
6
+ ref: string;
7
+ via: 'import' | 'fetch';
8
+ }) => void;
9
+ fetchCallback?: (ref: string) => void;
10
+ };
11
+ export declare function resolve<T>(input: unknown, options?: ResolveOptions): Promise<T>;