@expofp/loader 1.0.60 → 1.0.63
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/bundle/bundle.js +134 -123
- package/dist/bundle/bundle.js.map +1 -1
- package/dist/bundle/{downloadZip-CohB2rQN.js → downloadOfflineZip-RIzZSzeG.js} +1001 -995
- package/dist/bundle/downloadOfflineZip-RIzZSzeG.js.map +1 -0
- package/dist/bundle/{makeOffline-DuRXI_Dd.js → makeOffline-Doxc2dX7.js} +35 -34
- package/dist/bundle/makeOffline-Doxc2dX7.js.map +1 -0
- package/dist/bundle/makeOfflineBundle-BoEPc05D.js +60 -0
- package/dist/bundle/makeOfflineBundle-BoEPc05D.js.map +1 -0
- package/dist/bundle/saveOfflineZip.browser-BTQeRUY_.js +7 -0
- package/dist/bundle/saveOfflineZip.browser-BTQeRUY_.js.map +1 -0
- package/dist/esm/importJson.js +4 -0
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/offline/downloadOfflineZip.d.ts +4 -0
- package/dist/esm/offline/downloadOfflineZip.js +15 -0
- package/dist/esm/offline/generateZip.d.ts +4 -0
- package/dist/esm/offline/{downloadZip.js → generateZip.js} +9 -8
- package/dist/esm/offline/index.d.ts +9 -1
- package/dist/esm/offline/index.js +12 -4
- package/dist/esm/offline/makeOffline.js +3 -2
- package/dist/esm/offline/makeOfflineBundle.js +14 -7
- package/dist/esm/offline/saveOfflineZip.browser.d.ts +1 -0
- package/dist/esm/offline/saveOfflineZip.browser.js +3 -0
- package/dist/esm/offline/saveOfflineZip.d.ts +1 -0
- package/dist/esm/offline/saveOfflineZip.js +16 -0
- package/dist/esm/resolve.d.ts +11 -4
- package/dist/esm/resolve.js +6 -34
- package/dist/esm/resolvers/expoRuntimeBranchResolver.js +1 -1
- package/dist/esm/resolvers/legacyAssetUrlsResolver.js +2 -1
- package/package.json +8 -2
- package/dist/bundle/downloadZip-CohB2rQN.js.map +0 -1
- package/dist/bundle/makeOffline-DuRXI_Dd.js.map +0 -1
- package/dist/bundle/makeOfflineBundle-XjvmvDQE.js +0 -58
- package/dist/bundle/makeOfflineBundle-XjvmvDQE.js.map +0 -1
- package/dist/esm/offline/downloadZip.d.ts +0 -1
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { d as x, r as v, b as O, e as R, f as w, h as d, p as S } from "./bundle.js";
|
|
2
2
|
function u(t, n) {
|
|
3
3
|
let r = n >>> 0;
|
|
4
4
|
for (let a = 0; a < t.length; a++)
|
|
5
5
|
r ^= t.charCodeAt(a), r = Math.imul(r, 16777619);
|
|
6
6
|
return (r >>> 0).toString(16).padStart(8, "0");
|
|
7
7
|
}
|
|
8
|
-
function
|
|
8
|
+
function E(t) {
|
|
9
9
|
return u(t, 2166136261) + // your original seed
|
|
10
10
|
u(t, 569420461) + u(t, 461845907) + u(t, 2246822507);
|
|
11
11
|
}
|
|
12
|
-
const m = /* @__PURE__ */ new Map(),
|
|
12
|
+
const m = /* @__PURE__ */ new Map(), $ = /* @__PURE__ */ new Map(), j = /* @__PURE__ */ new Set([
|
|
13
13
|
"con",
|
|
14
14
|
"prn",
|
|
15
15
|
"aux",
|
|
@@ -33,19 +33,19 @@ const m = /* @__PURE__ */ new Map(), p = /* @__PURE__ */ new Map(), E = /* @__PU
|
|
|
33
33
|
"lpt8",
|
|
34
34
|
"lpt9"
|
|
35
35
|
]);
|
|
36
|
-
function
|
|
36
|
+
function y(t) {
|
|
37
37
|
let n = t.normalize("NFKD").replace(/[^\p{Letter}\p{Number}]+/gu, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").toLowerCase();
|
|
38
|
-
return n = n.replace(/[<>:"/\\|?*]/g, ""), n = n.replace(/[. ]+$/g, ""), n || (n = "file"),
|
|
38
|
+
return n = n.replace(/[<>:"/\\|?*]/g, ""), n = n.replace(/[. ]+$/g, ""), n || (n = "file"), j.has(n) && (n += "-file"), n;
|
|
39
39
|
}
|
|
40
40
|
function h(t) {
|
|
41
41
|
const n = m.get(t);
|
|
42
42
|
if (n) return n;
|
|
43
|
-
const r =
|
|
43
|
+
const r = y(t);
|
|
44
44
|
let a = r, o = 2;
|
|
45
45
|
for (; ; ) {
|
|
46
|
-
const e =
|
|
46
|
+
const e = $.get(a);
|
|
47
47
|
if (!e)
|
|
48
|
-
return
|
|
48
|
+
return $.set(a, t), m.set(t, a), a;
|
|
49
49
|
if (e === t)
|
|
50
50
|
return m.set(t, a), a;
|
|
51
51
|
a = `${r}-${o++}`;
|
|
@@ -67,28 +67,29 @@ function M(t, n = "") {
|
|
|
67
67
|
throw new Error(`Cannot make target path from URL without file extension: ${t}`);
|
|
68
68
|
const s = e.substring(e.lastIndexOf("."));
|
|
69
69
|
let l = e.substring(0, e.lastIndexOf("."));
|
|
70
|
-
const
|
|
71
|
-
if (
|
|
72
|
-
const
|
|
70
|
+
const c = l.match(/[^a-zA-Z0-9\-._\/]/g);
|
|
71
|
+
if (c) {
|
|
72
|
+
const f = h(l);
|
|
73
73
|
console.warn(
|
|
74
74
|
`Warning: pathname contains invalid filesystem characters (${[
|
|
75
|
-
...new Set(
|
|
75
|
+
...new Set(c)
|
|
76
76
|
].join(
|
|
77
77
|
", "
|
|
78
|
-
)}), slugifying it: ${l}${s} => ${
|
|
79
|
-
), l =
|
|
78
|
+
)}), slugifying it: ${l}${s} => ${f}${s}`
|
|
79
|
+
), l = f;
|
|
80
80
|
}
|
|
81
81
|
if (e = l + s, e.length > 120 && (console.warn(
|
|
82
82
|
`Warning: pathname is too long (${e.length} characters), truncating to 150 characters: ${e}`
|
|
83
83
|
), e = e.substring(0, 120 - s.length) + s), i) {
|
|
84
|
-
const
|
|
85
|
-
g !== -1 ? e = `${e.slice(0, g)}.${
|
|
84
|
+
const f = E(i), g = e.lastIndexOf(".");
|
|
85
|
+
g !== -1 ? e = `${e.slice(0, g)}.${f}${e.slice(g)}` : e = `${e}${f}`;
|
|
86
86
|
}
|
|
87
87
|
return `./${n}${o}${e}`;
|
|
88
88
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
89
|
+
const b = x("efp:loader:makeOffline");
|
|
90
|
+
async function C(t) {
|
|
91
|
+
b("makeOffline", t);
|
|
92
|
+
const n = await p(t);
|
|
92
93
|
let r = !1, a;
|
|
93
94
|
async function* o() {
|
|
94
95
|
a = yield* n, r = !0;
|
|
@@ -101,8 +102,8 @@ async function F(t) {
|
|
|
101
102
|
files: o()
|
|
102
103
|
};
|
|
103
104
|
}
|
|
104
|
-
async function*
|
|
105
|
-
|
|
105
|
+
async function* p(t) {
|
|
106
|
+
b("makeOfflineInternal", t);
|
|
106
107
|
const n = {
|
|
107
108
|
refCache: /* @__PURE__ */ new Map(),
|
|
108
109
|
forceFetch: !0,
|
|
@@ -112,7 +113,7 @@ async function* $(t) {
|
|
|
112
113
|
async function* a(o, e) {
|
|
113
114
|
if (!(typeof o[e] != "object" || o[e] === null)) {
|
|
114
115
|
if ("$ref" in o[e]) {
|
|
115
|
-
const i =
|
|
116
|
+
const i = v.filter((f) => O(f, o[e].$ref));
|
|
116
117
|
if (i.length === 0)
|
|
117
118
|
throw new Error(`No resolver found for ref: ${o[e].$ref}`);
|
|
118
119
|
if (i.length > 1)
|
|
@@ -124,14 +125,14 @@ async function* $(t) {
|
|
|
124
125
|
l = W;
|
|
125
126
|
break;
|
|
126
127
|
case "resolveRef":
|
|
127
|
-
l =
|
|
128
|
+
l = U;
|
|
128
129
|
break;
|
|
129
130
|
default:
|
|
130
131
|
throw new Error(`Unknown offlineMethod: ${s.offlineMethod}`);
|
|
131
132
|
}
|
|
132
|
-
const
|
|
133
|
-
if (Object.isFrozen(o
|
|
134
|
-
o[e] = R({ $ref:
|
|
133
|
+
const c = yield* l(s, o[e].$ref, n);
|
|
134
|
+
if (Object.isFrozen(o)) throw new Error("Unexpected frozen node during makeOffline");
|
|
135
|
+
o[e] = R({ $ref: c }, o[e]);
|
|
135
136
|
}
|
|
136
137
|
if (Array.isArray(o[e]))
|
|
137
138
|
for (const [i] of o[e].entries())
|
|
@@ -143,16 +144,16 @@ async function* $(t) {
|
|
|
143
144
|
}
|
|
144
145
|
}
|
|
145
146
|
async function* W(t, n, r) {
|
|
146
|
-
const a =
|
|
147
|
-
yield*
|
|
148
|
-
const o =
|
|
147
|
+
const a = w(await d(t, n, r));
|
|
148
|
+
yield* p(a);
|
|
149
|
+
const o = S(n), e = M(o);
|
|
149
150
|
return yield { url: o, targetFilePath: e }, `${n.substring(0, n.length - o.length - 1)}:${e}`;
|
|
150
151
|
}
|
|
151
|
-
async function*
|
|
152
|
-
const a =
|
|
153
|
-
return yield { data: yield*
|
|
152
|
+
async function* U(t, n, r) {
|
|
153
|
+
const a = w(await d(t, n, r)), o = I(n);
|
|
154
|
+
return yield { data: yield* p(a), targetFilePath: o }, o;
|
|
154
155
|
}
|
|
155
156
|
export {
|
|
156
|
-
|
|
157
|
+
C as makeOffline
|
|
157
158
|
};
|
|
158
|
-
//# sourceMappingURL=makeOffline-
|
|
159
|
+
//# sourceMappingURL=makeOffline-Doxc2dX7.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"makeOffline-Doxc2dX7.js","sources":["../../src/offline/hashString.ts","../../src/offline/slugify.ts","../../src/offline/tools.ts","../../src/offline/makeOffline.ts"],"sourcesContent":["// 32-bit murmur-ish hash with configurable seed\nfunction murmur32WithSeed(str: string, seed: number): string {\n let h = seed >>> 0;\n\n for (let i = 0; i < str.length; i++) {\n h ^= str.charCodeAt(i);\n h = Math.imul(h, 0x01000193); // FNV-ish prime, good mixer\n }\n\n return (h >>> 0).toString(16).padStart(8, '0'); // 8 hex chars\n}\n\n// 128-bit (MD5-width) hash: 4 × 32-bit parts concatenated\nexport function hashString(str: string): string {\n return (\n murmur32WithSeed(str, 0x811c9dc5) + // your original seed\n murmur32WithSeed(str, 0x21f0aaad) +\n murmur32WithSeed(str, 0x1b873593) +\n murmur32WithSeed(str, 0x85ebca6b)\n );\n}\n","// Module-level caches\nconst inputToSlug = new Map<string, string>();\nconst slugToInput = new Map<string, string>();\n\nconst WINDOWS_RESERVED = new Set([\n \"con\", \"prn\", \"aux\", \"nul\",\n \"com1\", \"com2\", \"com3\", \"com4\", \"com5\", \"com6\", \"com7\", \"com8\", \"com9\",\n \"lpt1\", \"lpt2\", \"lpt3\", \"lpt4\", \"lpt5\", \"lpt6\", \"lpt7\", \"lpt8\", \"lpt9\",\n]);\n\nfunction makeBaseSlug(input: string): string {\n let slug = input\n .normalize(\"NFKD\")\n // All non letters/numbers → \"-\"\n .replace(/[^\\p{Letter}\\p{Number}]+/gu, \"-\")\n // Collapse multiple \"-\"\n .replace(/-+/g, \"-\")\n // Trim \"-\" from start/end\n .replace(/^-|-$/g, \"\")\n .toLowerCase();\n\n // Strip forbidden Windows characters just in case\n slug = slug.replace(/[<>:\"/\\\\|?*]/g, \"\");\n\n // Windows forbids trailing space/period\n slug = slug.replace(/[. ]+$/g, \"\");\n\n // Fallback if everything was stripped\n if (!slug) slug = \"file\";\n\n // Avoid bare reserved device names\n if (WINDOWS_RESERVED.has(slug)) {\n slug += \"-file\";\n }\n\n return slug;\n}\n\nexport function slugifyFsUnique(input: string): string {\n // If we've seen this exact input before, return the same slug\n const existing = inputToSlug.get(input);\n if (existing) return existing;\n\n const base = makeBaseSlug(input);\n let candidate = base;\n let counter = 2;\n\n while (true) {\n const existingInput = slugToInput.get(candidate);\n\n if (!existingInput) {\n // Free slug → claim it for this input\n slugToInput.set(candidate, input);\n inputToSlug.set(input, candidate);\n return candidate;\n }\n\n if (existingInput === input) {\n // Same input somehow (super defensive)\n inputToSlug.set(input, candidate);\n return candidate;\n }\n\n // Collision: same slug already used by different input → add suffix\n candidate = `${base}-${counter++}`;\n }\n}\n\n// // Optional: to reset between runs/tests\n// export function resetSlugCache() {\n// inputToSlug.clear();\n// slugToInput.clear();\n// }","import { hashString } from './hashString';\nimport { slugifyFsUnique } from './slugify';\n\nexport function makeUniqueJsonTargetPathFromString(str: string, namespace: string = ''): string {\n // const hash = hashString(str);\n let result = slugifyFsUnique(str); // + '-' + hash;\n if (namespace) {\n result = `${slugifyFsUnique(namespace)}$${result}`;\n }\n\n if (result.endsWith('/')) {\n result += 'index.json';\n } else if (!result.endsWith('.json')) {\n result += '.json';\n }\n return './' + result;\n // handle directory case\n}\n\nexport function makeTargetPathFromUrl(url: string, prefix: string = ''): string {\n // https://example.com/dir1/dir2/a.js => \"{prefix}{origin-slug}/dir1/dir2/a.js\";\n // https://example.com/dir1/dir2/a.js?params => \"{prefix}{origin-slug}/dir1/dir2/a{paramsmd5hash}.js\";\n // use slugify.ts\n\n try {\n new URL(url);\n } catch {\n debugger;\n }\n const urlObj = new URL(url);\n const origin = `${urlObj.protocol}//${urlObj.host}`;\n const originSlug = slugifyFsUnique(origin);\n\n let pathname = urlObj.pathname;\n let search = urlObj.search;\n\n // if path doesn't end with extension, throw\n if (!pathname.match(/\\.[^\\/]+$/)) {\n throw new Error(`Cannot make target path from URL without file extension: ${url}`);\n }\n\n const extension = pathname.substring(pathname.lastIndexOf('.'));\n let pathnameWithoutExtension = pathname.substring(0, pathname.lastIndexOf('.'));\n\n // check the pathname contains only valid fs characters\n const invalidPathnameChars = pathnameWithoutExtension.match(/[^a-zA-Z0-9\\-._\\/]/g);\n if (invalidPathnameChars) {\n const fixedPathnameWithoutExtension = slugifyFsUnique(pathnameWithoutExtension);\n console.warn(\n `Warning: pathname contains invalid filesystem characters (${[\n ...new Set(invalidPathnameChars),\n ].join(\n ', '\n )}), slugifying it: ${pathnameWithoutExtension}${extension} => ${fixedPathnameWithoutExtension}${extension}`\n );\n pathnameWithoutExtension = fixedPathnameWithoutExtension;\n }\n\n pathname = pathnameWithoutExtension + extension;\n\n if (pathname.length > 120) {\n console.warn(\n `Warning: pathname is too long (${pathname.length} characters), truncating to 150 characters: ${pathname}`\n );\n pathname = pathname.substring(0, 120 - extension.length) + extension;\n }\n\n if (search) {\n // create a hash from search params\n const hash = hashString(search);\n const dotIndex = pathname.lastIndexOf('.');\n if (dotIndex !== -1) {\n pathname = `${pathname.slice(0, dotIndex)}.${hash}${pathname.slice(dotIndex)}`;\n } else {\n pathname = `${pathname}${hash}`;\n }\n }\n\n // // handle directory case\n // if (pathname.endsWith('/')) {\n // pathname += '__index.json';\n // }\n\n return `./${prefix}${originSlug}${pathname}`;\n}\n","import debug from 'debug';\nimport {\n canResolve,\n parseRefValue,\n resolverResolve,\n type ResolveContextInternal,\n} from '../resolve';\nimport { resolvers } from '../resolvers';\nimport { createMergedObjectWithOverridenNonRefProps, deepClone } from '../shared';\nimport type { LocalFile, MakeOfflineResult, ManifestData, Resolver } from '../types';\nimport { makeTargetPathFromUrl, makeUniqueJsonTargetPathFromString } from './tools';\n\nconst log = debug('efp:loader:makeOffline');\n\nexport async function makeOffline(manifest: any): Promise<MakeOfflineResult> {\n log('makeOffline', manifest);\n // if (typeof manifest !== 'object' || manifest === null) {\n // throw new Error('Manifest must be an object');\n // }\n\n const res = await makeOfflineInternal(manifest);\n let done = false;\n let resultManifest: any;\n async function* files() {\n resultManifest = yield* res;\n done = true;\n }\n\n return {\n get manifest(): ManifestData {\n if (!done) throw new Error('Iterate over files before getting manifest');\n return resultManifest;\n },\n files: files() as AsyncGenerator<LocalFile, void, void>,\n };\n}\n\nasync function* makeOfflineInternal(manifest: any): AsyncGenerator<LocalFile, ManifestData, void> {\n log('makeOfflineInternal', manifest);\n const resolveContext: ResolveContextInternal = {\n refCache: new Map<string, Promise<any>>(),\n forceFetch: true,\n signal: null,\n };\n const parent = { manifest };\n yield* walk(parent, 'manifest');\n return parent.manifest;\n\n async function* walk(node: any, key: string | number): AsyncGenerator<LocalFile> {\n // console.log('walk', node, key, node[key]);\n // let node[key] = node[key];\n if (typeof node[key] !== 'object' || node[key] === null) {\n return;\n }\n // let resolversToUse1 = resolvers.filter((r) => r.canResolve(node[key].$ref));\n // const usedResolvers = new Set<Resolver>();\n // do {\n if ('$ref' in node[key]) {\n const resolversToUse = resolvers.filter((r) => canResolve(r, node[key].$ref));\n if (resolversToUse.length === 0) {\n throw new Error(`No resolver found for ref: ${node[key].$ref}`);\n }\n if (resolversToUse.length > 1) {\n throw new Error(`Multiple resolvers can make offline ref: ${node[key].$ref}`);\n }\n\n const resolver = resolversToUse[0];\n\n let func: typeof offlineLocalizeRef;\n switch (resolver.offlineMethod) {\n case 'localizeRef':\n func = offlineLocalizeRef;\n break;\n case 'resolveRef':\n func = offlineResolveRef;\n break;\n default:\n throw new Error(`Unknown offlineMethod: ${resolver.offlineMethod}`);\n }\n\n const mergeRef = yield* func(resolver, node[key].$ref, resolveContext);\n\n if (Object.isFrozen(node)) throw new Error('Unexpected frozen node during makeOffline');\n\n node[key] = createMergedObjectWithOverridenNonRefProps({ $ref: mergeRef }, node[key]);\n }\n\n // recurse\n if (Array.isArray(node[key])) {\n for (const [index] of node[key].entries()) {\n yield* walk(node[key], index);\n }\n } else if (typeof node[key] === 'object' && node[key] !== null) {\n for (const key1 of Object.keys(node[key])) {\n // debugger;\n yield* walk(node[key], key1);\n }\n }\n }\n}\n\n// function canResolve(resolver: Resolver, ref: string): boolean {\n// if (resolver.canResolve) {\n// return resolver.canResolve(ref);\n// }\n// if (resolver.schema) {\n// return canResolveRefSchema(ref, resolver.schema);\n// }\n// throw new Error('Resolver is missing canResolve method and schema property');\n// }\n\nasync function* offlineLocalizeRef(\n resolver: Resolver,\n ref: string,\n context: ResolveContextInternal\n): AsyncGenerator<LocalFile, string, void> {\n const refData = deepClone(await resolverResolve(resolver, ref, context));\n // emit assets\n yield* makeOfflineInternal(refData);\n const url = parseRefValue(ref);\n const targetFilePath = makeTargetPathFromUrl(url);\n // TODO: handle CSS and other text files that may reference other assets\n yield { url, targetFilePath };\n const schema = ref.substring(0, ref.length - url.length - 1);\n return `${schema}:${targetFilePath}`;\n}\n\nasync function* offlineResolveRef(\n resolver: Resolver,\n ref: string,\n context: ResolveContextInternal\n): AsyncGenerator<LocalFile, string, void> {\n const refData = deepClone(await resolverResolve(resolver, ref, context));\n const targetFilePath = makeUniqueJsonTargetPathFromString(ref);\n const data = yield* makeOfflineInternal(refData);\n yield { data, targetFilePath };\n return targetFilePath;\n}\n\n// export function createMakeOfflineResult(\n// manifest: any,\n// walk: (node: any, key: string | number) => AsyncGenerator<LocalFile>\n// ): MakeOfflineResult {\n// log('createMakeOfflineResult', manifest);\n// if (typeof manifest !== 'object' || manifest === null) {\n// throw new Error('Manifest must be an object');\n// }\n// const parent = { manifest };\n// let done = false;\n// async function* files() {\n// yield* walk(parent, 'manifest');\n// done = true;\n// }\n\n// return {\n// get manifest() {\n// if (!done) {\n// throw new Error('Cannot access manifest before all files are generated');\n// }\n// return parent.manifest;\n// },\n// files: files(),\n// };\n// }\n"],"names":["murmur32WithSeed","str","seed","h","i","hashString","inputToSlug","slugToInput","WINDOWS_RESERVED","makeBaseSlug","input","slug","slugifyFsUnique","existing","base","candidate","counter","existingInput","makeUniqueJsonTargetPathFromString","namespace","result","makeTargetPathFromUrl","url","prefix","urlObj","origin","originSlug","pathname","search","extension","pathnameWithoutExtension","invalidPathnameChars","fixedPathnameWithoutExtension","hash","dotIndex","log","debug","makeOffline","manifest","res","makeOfflineInternal","done","resultManifest","files","resolveContext","parent","walk","node","key","resolversToUse","resolvers","r","canResolve","resolver","func","offlineLocalizeRef","offlineResolveRef","mergeRef","createMergedObjectWithOverridenNonRefProps","index","key1","ref","context","refData","deepClone","resolverResolve","parseRefValue","targetFilePath"],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { d, a as f, resolve as s } from "./bundle.js";
|
|
2
|
+
import { makeOffline as c } from "./makeOffline-Doxc2dX7.js";
|
|
3
|
+
const l = d("efp:loader:makeOfflineBundle");
|
|
4
|
+
async function* p(t) {
|
|
5
|
+
l("Bundling", t), f(t);
|
|
6
|
+
const n = await c(t), a = /* @__PURE__ */ new Map();
|
|
7
|
+
for await (const e of n.files)
|
|
8
|
+
if ("data" in e) {
|
|
9
|
+
a.set(e.targetFilePath, e.data);
|
|
10
|
+
const i = JSON.stringify(e.data, null, 2);
|
|
11
|
+
l("Bundling file with data", e.targetFilePath, i), yield {
|
|
12
|
+
path: e.targetFilePath,
|
|
13
|
+
data: new TextEncoder().encode(i).buffer
|
|
14
|
+
};
|
|
15
|
+
} else if ("url" in e) {
|
|
16
|
+
const i = await w(e.url);
|
|
17
|
+
l("Bundling file from url", e.targetFilePath), yield {
|
|
18
|
+
path: e.targetFilePath,
|
|
19
|
+
data: await i.arrayBuffer()
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
const r = await h(n.manifest, a);
|
|
23
|
+
yield {
|
|
24
|
+
path: "./index.html",
|
|
25
|
+
data: new TextEncoder().encode(r).buffer
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
const u = 10, o = /* @__PURE__ */ new Set();
|
|
29
|
+
async function w(t) {
|
|
30
|
+
for (; o.size >= u; )
|
|
31
|
+
await Promise.race(o);
|
|
32
|
+
const n = (async () => {
|
|
33
|
+
l("Fetching file for zip:", t);
|
|
34
|
+
const a = await fetch(t);
|
|
35
|
+
if (!a.ok)
|
|
36
|
+
throw new Error(`Failed to fetch ${t}: ${a.status} ${a.statusText}`);
|
|
37
|
+
return await a.blob();
|
|
38
|
+
})();
|
|
39
|
+
o.add(n);
|
|
40
|
+
try {
|
|
41
|
+
return await n;
|
|
42
|
+
} finally {
|
|
43
|
+
o.delete(n);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
async function h(t, n) {
|
|
47
|
+
const a = await s(t, "/runtime/entry", { preResolvedRefs: n });
|
|
48
|
+
return `
|
|
49
|
+
<!DOCTYPE html>
|
|
50
|
+
<script type="module">
|
|
51
|
+
import { load } from ${JSON.stringify(a)};
|
|
52
|
+
await load(${JSON.stringify(t)});
|
|
53
|
+
console.info('🚀 loaded');
|
|
54
|
+
<\/script>
|
|
55
|
+
`;
|
|
56
|
+
}
|
|
57
|
+
export {
|
|
58
|
+
p as makeOfflineBundle
|
|
59
|
+
};
|
|
60
|
+
//# sourceMappingURL=makeOfflineBundle-BoEPc05D.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"makeOfflineBundle-BoEPc05D.js","sources":["../../src/offline/makeOfflineBundle.ts"],"sourcesContent":["import debug from 'debug';\nimport { resolve } from '..';\nimport { makeOffline } from './makeOffline';\nimport { deepFreeze } from '../shared';\n\nconst log = debug('efp:loader:makeOfflineBundle');\n\nexport async function* makeOfflineBundle(\n manifest: unknown\n): AsyncGenerator<{ path: string; data: ArrayBuffer }> {\n log('Bundling', manifest);\n deepFreeze(manifest);\n const offlineData = await makeOffline(manifest);\n const preResolvedRefs = new Map<string, any>();\n\n for await (const file of offlineData.files) {\n if ('data' in file) {\n preResolvedRefs.set(file.targetFilePath, file.data);\n const jsonString = JSON.stringify(file.data, null, 2);\n log('Bundling file with data', file.targetFilePath, jsonString);\n yield {\n path: file.targetFilePath,\n data: new TextEncoder().encode(jsonString).buffer,\n };\n } else if ('url' in file) {\n const blob = await downloadFile(file.url);\n log('Bundling file from url', file.targetFilePath);\n yield {\n path: file.targetFilePath,\n data: await blob.arrayBuffer(),\n };\n }\n }\n // yield* generateJsLoaderFiles();\n const html = await getIndexHtml(offlineData.manifest, preResolvedRefs);\n yield {\n path: './index.html',\n data: new TextEncoder().encode(html).buffer,\n };\n}\n\nconst MAX_CONCURRENT_DOWNLOADS = 10;\nconst queue = new Set<Promise<Blob>>();\n// have a queue to limit concurrent downloads\nasync function downloadFile(url: string): Promise<Blob> {\n while (queue.size >= MAX_CONCURRENT_DOWNLOADS) {\n await Promise.race(queue);\n }\n const downloadPromise = (async () => {\n log('Fetching file for zip:', url);\n const response = await fetch(url);\n if (!response.ok) {\n throw new Error(`Failed to fetch ${url}: ${response.status} ${response.statusText}`);\n }\n return await response.blob();\n })();\n queue.add(downloadPromise);\n try {\n return await downloadPromise;\n } finally {\n queue.delete(downloadPromise);\n }\n}\n\nasync function getIndexHtml(manifest: unknown, preResolvedRefs: Map<string, any>): Promise<string> {\n const entryPoint = await resolve(manifest, '/runtime/entry', { preResolvedRefs });\n const html = `\n<!DOCTYPE html>\n<script type=\"module\">\n import { load } from ${JSON.stringify(entryPoint)};\n await load(${JSON.stringify(manifest)});\n console.info('🚀 loaded');\n</script> \n`;\n return html;\n}\n"],"names":["log","debug","makeOfflineBundle","manifest","deepFreeze","offlineData","makeOffline","preResolvedRefs","file","jsonString","blob","downloadFile","html","getIndexHtml","MAX_CONCURRENT_DOWNLOADS","queue","url","downloadPromise","response","entryPoint","resolve"],"mappings":";;AAKA,MAAMA,IAAMC,EAAM,8BAA8B;AAEhD,gBAAuBC,EACrBC,GACqD;AACrD,EAAAH,EAAI,YAAYG,CAAQ,GACxBC,EAAWD,CAAQ;AACnB,QAAME,IAAc,MAAMC,EAAYH,CAAQ,GACxCI,wBAAsB,IAAA;AAE5B,mBAAiBC,KAAQH,EAAY;AACnC,QAAI,UAAUG,GAAM;AAClB,MAAAD,EAAgB,IAAIC,EAAK,gBAAgBA,EAAK,IAAI;AAClD,YAAMC,IAAa,KAAK,UAAUD,EAAK,MAAM,MAAM,CAAC;AACpD,MAAAR,EAAI,2BAA2BQ,EAAK,gBAAgBC,CAAU,GAC9D,MAAM;AAAA,QACJ,MAAMD,EAAK;AAAA,QACX,MAAM,IAAI,YAAA,EAAc,OAAOC,CAAU,EAAE;AAAA,MAAA;AAAA,IAE/C,WAAW,SAASD,GAAM;AACxB,YAAME,IAAO,MAAMC,EAAaH,EAAK,GAAG;AACxC,MAAAR,EAAI,0BAA0BQ,EAAK,cAAc,GACjD,MAAM;AAAA,QACJ,MAAMA,EAAK;AAAA,QACX,MAAM,MAAME,EAAK,YAAA;AAAA,MAAY;AAAA,IAEjC;AAGF,QAAME,IAAO,MAAMC,EAAaR,EAAY,UAAUE,CAAe;AACrE,QAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM,IAAI,YAAA,EAAc,OAAOK,CAAI,EAAE;AAAA,EAAA;AAEzC;AAEA,MAAME,IAA2B,IAC3BC,wBAAY,IAAA;AAElB,eAAeJ,EAAaK,GAA4B;AACtD,SAAOD,EAAM,QAAQD;AACnB,UAAM,QAAQ,KAAKC,CAAK;AAE1B,QAAME,KAAmB,YAAY;AACnC,IAAAjB,EAAI,0BAA0BgB,CAAG;AACjC,UAAME,IAAW,MAAM,MAAMF,CAAG;AAChC,QAAI,CAACE,EAAS;AACZ,YAAM,IAAI,MAAM,mBAAmBF,CAAG,KAAKE,EAAS,MAAM,IAAIA,EAAS,UAAU,EAAE;AAErF,WAAO,MAAMA,EAAS,KAAA;AAAA,EACxB,GAAA;AACA,EAAAH,EAAM,IAAIE,CAAe;AACzB,MAAI;AACF,WAAO,MAAMA;AAAA,EACf,UAAA;AACE,IAAAF,EAAM,OAAOE,CAAe;AAAA,EAC9B;AACF;AAEA,eAAeJ,EAAaV,GAAmBI,GAAoD;AACjG,QAAMY,IAAa,MAAMC,EAAQjB,GAAU,kBAAkB,EAAE,iBAAAI,GAAiB;AAShF,SARa;AAAA;AAAA;AAAA,yBAGU,KAAK,UAAUY,CAAU,CAAC;AAAA,eACpC,KAAK,UAAUhB,CAAQ,CAAC;AAAA;AAAA;AAAA;AAKvC;"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"saveOfflineZip.browser-BTQeRUY_.js","sources":["../../src/offline/saveOfflineZip.browser.ts"],"sourcesContent":["export function saveOfflineZip() {\n throw new Error('saveOfflineZip is not available in the browser');\n}\n"],"names":["saveOfflineZip"],"mappings":"AAAO,SAASA,IAAiB;AAC/B,QAAM,IAAI,MAAM,gDAAgD;AAClE;"}
|
package/dist/esm/importJson.js
CHANGED
|
@@ -7,6 +7,10 @@ const jsonFrozen = new WeakSet();
|
|
|
7
7
|
// Cons: can have CSP issues in some environments
|
|
8
8
|
const importJsonNative = new Function('url', 'return import(url, { with: { type: "json" } });');
|
|
9
9
|
export async function importJson(url, resolveContext) {
|
|
10
|
+
if (resolveContext.preResolvedRefs?.has(url)) {
|
|
11
|
+
log('importJson: using preResolvedRefs for', url);
|
|
12
|
+
return resolveContext.preResolvedRefs.get(url);
|
|
13
|
+
}
|
|
10
14
|
// console.warn('importJson:', resolveContext.forceFetch);
|
|
11
15
|
if (importJsonNotAvailable === undefined && !resolveContext.forceFetch) {
|
|
12
16
|
try {
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -3,5 +3,5 @@ export declare const load: (manifest: unknown, ...args: IArguments[]) => Promise
|
|
|
3
3
|
export declare const initialize: (manifest: unknown, ...args: IArguments[]) => Promise<any>;
|
|
4
4
|
export declare function callFunction_Experimental(name: string, manifest: unknown, ...args: IArguments[]): Promise<any>;
|
|
5
5
|
export { mutateManifest } from './mutateManifest';
|
|
6
|
-
export {
|
|
6
|
+
export { downloadOfflineZip, makeOfflineBundle, saveOfflineZip } from './offline';
|
|
7
7
|
export { resolve } from './resolve';
|
package/dist/esm/index.js
CHANGED
|
@@ -18,7 +18,7 @@ export async function callFunction_Experimental(name, manifest, ...args) {
|
|
|
18
18
|
return await fn(manifest, ...args);
|
|
19
19
|
}
|
|
20
20
|
export { mutateManifest } from './mutateManifest';
|
|
21
|
-
export {
|
|
21
|
+
export { downloadOfflineZip, makeOfflineBundle, saveOfflineZip } from './offline';
|
|
22
22
|
export { resolve } from './resolve';
|
|
23
23
|
// preloadJson('https://efp-runtime.expofp.com/branches/main.json');
|
|
24
24
|
// preconnectUrl('https://efp-runtime.expofp.com/');
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { generateZip } from './generateZip';
|
|
2
|
+
export async function downloadOfflineZip(manifest, extraFiles) {
|
|
3
|
+
// check browser only
|
|
4
|
+
if (typeof window === 'undefined' || typeof document === 'undefined') {
|
|
5
|
+
throw new Error('downloadOfflineZip can only be used in a browser environment');
|
|
6
|
+
}
|
|
7
|
+
// Generate archive
|
|
8
|
+
const blob = await generateZip(manifest, extraFiles);
|
|
9
|
+
// Create link & trigger download
|
|
10
|
+
const a = document.createElement('a');
|
|
11
|
+
a.href = URL.createObjectURL(blob);
|
|
12
|
+
a.download = 'offline.zip';
|
|
13
|
+
a.click();
|
|
14
|
+
URL.revokeObjectURL(a.href);
|
|
15
|
+
}
|
|
@@ -1,20 +1,21 @@
|
|
|
1
|
+
import debug from 'debug';
|
|
1
2
|
import JSZip from 'jszip';
|
|
2
|
-
import { log } from '../logger';
|
|
3
3
|
import { makeOfflineBundle } from './makeOfflineBundle';
|
|
4
|
-
|
|
4
|
+
const log = debug('efp:loader:generateZip');
|
|
5
|
+
export async function generateZip(manifest, extraFiles) {
|
|
5
6
|
const zip = new JSZip();
|
|
6
7
|
const bundle = await makeOfflineBundle(manifest);
|
|
7
8
|
for await (const file of bundle) {
|
|
9
|
+
log('Adding file to zip:', file.path, 'size:', file.data.byteLength);
|
|
8
10
|
zip.file(file.path, file.data);
|
|
9
11
|
}
|
|
12
|
+
for (const extraFile of extraFiles ?? []) {
|
|
13
|
+
log('Adding extra file to zip:', extraFile.path, 'size:', extraFile.data.byteLength);
|
|
14
|
+
zip.file(extraFile.path, extraFile.data);
|
|
15
|
+
}
|
|
10
16
|
// Generate archive
|
|
11
17
|
const blob = await zip.generateAsync({ type: 'blob' });
|
|
12
18
|
// console.info('Manifest for HTML:', offlineData.manifest);
|
|
13
19
|
log('Generated offline ZIP bundle, size:', blob.size);
|
|
14
|
-
|
|
15
|
-
const a = document.createElement('a');
|
|
16
|
-
a.href = URL.createObjectURL(blob);
|
|
17
|
-
a.download = 'offline.zip';
|
|
18
|
-
a.click();
|
|
19
|
-
URL.revokeObjectURL(a.href);
|
|
20
|
+
return blob;
|
|
20
21
|
}
|
|
@@ -2,4 +2,12 @@ export declare function makeOfflineBundle(manifest: unknown): Promise<AsyncGener
|
|
|
2
2
|
path: string;
|
|
3
3
|
data: ArrayBuffer;
|
|
4
4
|
}, any, any>>;
|
|
5
|
-
export declare function
|
|
5
|
+
export declare function generateZip(manifest: unknown, extraFiles?: Array<{
|
|
6
|
+
path: string;
|
|
7
|
+
data: Uint8Array;
|
|
8
|
+
}>): Promise<Blob>;
|
|
9
|
+
export declare function downloadOfflineZip(manifest: unknown, extraFiles?: Array<{
|
|
10
|
+
path: string;
|
|
11
|
+
data: Uint8Array;
|
|
12
|
+
}>): Promise<void>;
|
|
13
|
+
export declare function saveOfflineZip(manifest: unknown, path: string): Promise<void>;
|
|
@@ -2,9 +2,17 @@ export async function makeOfflineBundle(manifest) {
|
|
|
2
2
|
const { makeOfflineBundle } = await import('./makeOfflineBundle');
|
|
3
3
|
return await makeOfflineBundle(manifest);
|
|
4
4
|
}
|
|
5
|
-
export async function
|
|
6
|
-
const {
|
|
7
|
-
return await
|
|
5
|
+
export async function generateZip(manifest, extraFiles) {
|
|
6
|
+
const { generateZip } = await import('./generateZip');
|
|
7
|
+
return await generateZip(manifest, extraFiles);
|
|
8
|
+
}
|
|
9
|
+
export async function downloadOfflineZip(manifest, extraFiles) {
|
|
10
|
+
const { downloadOfflineZip } = await import('./downloadOfflineZip');
|
|
11
|
+
return await downloadOfflineZip(manifest, extraFiles);
|
|
12
|
+
}
|
|
13
|
+
export async function saveOfflineZip(manifest, path) {
|
|
14
|
+
const { saveOfflineZip } = await import('./saveOfflineZip');
|
|
15
|
+
return await saveOfflineZip(manifest, path);
|
|
8
16
|
}
|
|
9
17
|
async function debugLogOfflineManifestFiles(manifest) {
|
|
10
18
|
// console.log('Offline files:');
|
|
@@ -23,7 +31,7 @@ async function debugLogOfflineManifestFiles(manifest) {
|
|
|
23
31
|
console.info('Offline data: ', result.manifest);
|
|
24
32
|
}
|
|
25
33
|
if (typeof window !== 'undefined') {
|
|
26
|
-
window['
|
|
34
|
+
window['__debugDownloadOfflineZip'] = downloadOfflineZip;
|
|
27
35
|
window['__debugMakeOfflineBundle'] = makeOfflineBundle;
|
|
28
36
|
window['__debugLogOfflineManifestFiles'] = debugLogOfflineManifestFiles;
|
|
29
37
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import
|
|
1
|
+
import debug from 'debug';
|
|
2
2
|
import { canResolve, parseRefValue, resolverResolve, } from '../resolve';
|
|
3
3
|
import { resolvers } from '../resolvers';
|
|
4
4
|
import { createMergedObjectWithOverridenNonRefProps, deepClone } from '../shared';
|
|
5
5
|
import { makeTargetPathFromUrl, makeUniqueJsonTargetPathFromString } from './tools';
|
|
6
|
+
const log = debug('efp:loader:makeOffline');
|
|
6
7
|
export async function makeOffline(manifest) {
|
|
7
8
|
log('makeOffline', manifest);
|
|
8
9
|
// if (typeof manifest !== 'object' || manifest === null) {
|
|
@@ -64,7 +65,7 @@ async function* makeOfflineInternal(manifest) {
|
|
|
64
65
|
throw new Error(`Unknown offlineMethod: ${resolver.offlineMethod}`);
|
|
65
66
|
}
|
|
66
67
|
const mergeRef = yield* func(resolver, node[key].$ref, resolveContext);
|
|
67
|
-
if (Object.isFrozen(node
|
|
68
|
+
if (Object.isFrozen(node))
|
|
68
69
|
throw new Error('Unexpected frozen node during makeOffline');
|
|
69
70
|
node[key] = createMergedObjectWithOverridenNonRefProps({ $ref: mergeRef }, node[key]);
|
|
70
71
|
}
|
|
@@ -1,12 +1,18 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { makeOffline } from './makeOffline';
|
|
1
|
+
import debug from 'debug';
|
|
3
2
|
import { resolve } from '..';
|
|
3
|
+
import { makeOffline } from './makeOffline';
|
|
4
|
+
import { deepFreeze } from '../shared';
|
|
5
|
+
const log = debug('efp:loader:makeOfflineBundle');
|
|
4
6
|
export async function* makeOfflineBundle(manifest) {
|
|
5
|
-
log('
|
|
7
|
+
log('Bundling', manifest);
|
|
8
|
+
deepFreeze(manifest);
|
|
6
9
|
const offlineData = await makeOffline(manifest);
|
|
10
|
+
const preResolvedRefs = new Map();
|
|
7
11
|
for await (const file of offlineData.files) {
|
|
8
12
|
if ('data' in file) {
|
|
13
|
+
preResolvedRefs.set(file.targetFilePath, file.data);
|
|
9
14
|
const jsonString = JSON.stringify(file.data, null, 2);
|
|
15
|
+
log('Bundling file with data', file.targetFilePath, jsonString);
|
|
10
16
|
yield {
|
|
11
17
|
path: file.targetFilePath,
|
|
12
18
|
data: new TextEncoder().encode(jsonString).buffer,
|
|
@@ -14,6 +20,7 @@ export async function* makeOfflineBundle(manifest) {
|
|
|
14
20
|
}
|
|
15
21
|
else if ('url' in file) {
|
|
16
22
|
const blob = await downloadFile(file.url);
|
|
23
|
+
log('Bundling file from url', file.targetFilePath);
|
|
17
24
|
yield {
|
|
18
25
|
path: file.targetFilePath,
|
|
19
26
|
data: await blob.arrayBuffer(),
|
|
@@ -21,9 +28,9 @@ export async function* makeOfflineBundle(manifest) {
|
|
|
21
28
|
}
|
|
22
29
|
}
|
|
23
30
|
// yield* generateJsLoaderFiles();
|
|
24
|
-
const html = await getIndexHtml(offlineData.manifest);
|
|
31
|
+
const html = await getIndexHtml(offlineData.manifest, preResolvedRefs);
|
|
25
32
|
yield {
|
|
26
|
-
path: 'index.html',
|
|
33
|
+
path: './index.html',
|
|
27
34
|
data: new TextEncoder().encode(html).buffer,
|
|
28
35
|
};
|
|
29
36
|
}
|
|
@@ -50,8 +57,8 @@ async function downloadFile(url) {
|
|
|
50
57
|
queue.delete(downloadPromise);
|
|
51
58
|
}
|
|
52
59
|
}
|
|
53
|
-
async function getIndexHtml(manifest) {
|
|
54
|
-
const entryPoint = await resolve(manifest, '/runtime/entry');
|
|
60
|
+
async function getIndexHtml(manifest, preResolvedRefs) {
|
|
61
|
+
const entryPoint = await resolve(manifest, '/runtime/entry', { preResolvedRefs });
|
|
55
62
|
const html = `
|
|
56
63
|
<!DOCTYPE html>
|
|
57
64
|
<script type="module">
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function saveOfflineZip(): void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function saveOfflineZip(manifest: unknown, path: string): Promise<void>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { generateZip } from './generateZip';
|
|
2
|
+
import fs from 'fs/promises';
|
|
3
|
+
export async function saveOfflineZip(manifest, path) {
|
|
4
|
+
if (!path) {
|
|
5
|
+
throw new Error('Path is required to save the ZIP file');
|
|
6
|
+
}
|
|
7
|
+
// should be zip path
|
|
8
|
+
if (!path.endsWith('.zip')) {
|
|
9
|
+
throw new Error('The specified path must end with .zip');
|
|
10
|
+
}
|
|
11
|
+
// Generate archive
|
|
12
|
+
const blob = await generateZip(manifest);
|
|
13
|
+
const arrayBuffer = await blob.arrayBuffer();
|
|
14
|
+
await fs.writeFile(path, Buffer.from(arrayBuffer));
|
|
15
|
+
console.log('Generated offline ZIP bundle at', path);
|
|
16
|
+
}
|
package/dist/esm/resolve.d.ts
CHANGED
|
@@ -7,14 +7,19 @@ export interface ResolveContextInternal {
|
|
|
7
7
|
*/
|
|
8
8
|
importCallback?: (url: string, type: 'json' | 'script') => void;
|
|
9
9
|
signal: AbortSignal | null;
|
|
10
|
+
preResolvedRefs?: Map<string, any>;
|
|
10
11
|
}
|
|
11
|
-
|
|
12
|
+
interface ResolveOptionsNotMutate extends Partial<ResolveContextInternal> {
|
|
13
|
+
mutate?: false;
|
|
14
|
+
signal?: AbortSignal;
|
|
15
|
+
}
|
|
16
|
+
interface ResolveOptionMutate extends Omit<ResolveOptionsNotMutate, 'mutate'> {
|
|
12
17
|
/**
|
|
13
18
|
* Mutate original object during resolution.
|
|
14
19
|
*/
|
|
15
|
-
mutate
|
|
16
|
-
signal?: AbortSignal;
|
|
20
|
+
mutate: true;
|
|
17
21
|
}
|
|
22
|
+
export type ResolveOptions = ResolveOptionsNotMutate | ResolveOptionMutate;
|
|
18
23
|
/**
|
|
19
24
|
* Resolves a value from an object using a JSON Pointer RFC 6901.
|
|
20
25
|
*
|
|
@@ -31,8 +36,10 @@ export interface ResolveOptions extends Partial<ResolveContextInternal> {
|
|
|
31
36
|
* // result === 'baz'
|
|
32
37
|
* ```
|
|
33
38
|
*/
|
|
34
|
-
export declare function resolve(object: any, jsonPointer: string, options
|
|
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>;
|
|
35
41
|
export declare function resolverResolve(resolver: Resolver, ref: string, context: ResolveContextInternal): Promise<any>;
|
|
36
42
|
export declare function canResolve(resolver: Resolver, ref: string): boolean;
|
|
37
43
|
export declare function canResolveRefSchema(ref: string, prefixBase: string): boolean;
|
|
38
44
|
export declare function parseRefValue(ref: string): string;
|
|
45
|
+
export {};
|
package/dist/esm/resolve.js
CHANGED
|
@@ -2,32 +2,17 @@ import { log } from './logger';
|
|
|
2
2
|
import { resolvers } from './resolvers';
|
|
3
3
|
import { createMergedObjectWithOverridenNonRefProps, deepFreeze, replaceObjectFields, } from './shared';
|
|
4
4
|
const globalRefCache = new Map();
|
|
5
|
-
/**
|
|
6
|
-
* Resolves a value from an object using a JSON Pointer RFC 6901.
|
|
7
|
-
*
|
|
8
|
-
* @param object - The object to resolve the value from
|
|
9
|
-
* @param jsonPointer - A JSON Pointer string that must start with '/'
|
|
10
|
-
* @param options - Optional context for resolution, including cache and fetch callback
|
|
11
|
-
* @returns A promise that resolves to the value at the specified JSON Pointer path
|
|
12
|
-
* @throws {Error} If the JSON Pointer is not valid or if a $ref cannot be resolved
|
|
13
|
-
*
|
|
14
|
-
* @example
|
|
15
|
-
* ```typescript
|
|
16
|
-
* const obj = { foo: { bar: 'baz' } };
|
|
17
|
-
* const result = await resolve(obj, '/foo/bar');
|
|
18
|
-
* // result === 'baz'
|
|
19
|
-
* ```
|
|
20
|
-
*/
|
|
21
5
|
export async function resolve(object, jsonPointer, options) {
|
|
22
6
|
log('Resolving:', jsonPointer, 'of', object, options);
|
|
23
|
-
if (typeof window !== 'undefined') {
|
|
24
|
-
|
|
25
|
-
}
|
|
7
|
+
// if (typeof window !== 'undefined') {
|
|
8
|
+
// (window as any)['__lastResolveObject'] = object;
|
|
9
|
+
// }
|
|
26
10
|
const resolveContext = {
|
|
27
11
|
importCallback: options?.importCallback,
|
|
28
12
|
refCache: options?.refCache || globalRefCache,
|
|
29
13
|
forceFetch: !!options?.forceFetch,
|
|
30
14
|
signal: options?.signal || null,
|
|
15
|
+
preResolvedRefs: options?.preResolvedRefs,
|
|
31
16
|
};
|
|
32
17
|
if (typeof jsonPointer !== 'string') {
|
|
33
18
|
throw new Error(`Invalid JSON Pointer (not a string): ${jsonPointer}`);
|
|
@@ -260,21 +245,8 @@ export function parseRefValue(ref) {
|
|
|
260
245
|
// url: string
|
|
261
246
|
// ) => Promise<any>;
|
|
262
247
|
if (typeof window !== 'undefined') {
|
|
263
|
-
window['__debugResolve'] = async function debugResolve(
|
|
264
|
-
|
|
265
|
-
let ref;
|
|
266
|
-
let resolveContext;
|
|
267
|
-
if (typeof objectOrRef === 'string') {
|
|
268
|
-
object = window['__lastResolveObject'];
|
|
269
|
-
ref = objectOrRef;
|
|
270
|
-
resolveContext = refOrContext;
|
|
271
|
-
}
|
|
272
|
-
else {
|
|
273
|
-
object = objectOrRef;
|
|
274
|
-
ref = refOrContext;
|
|
275
|
-
resolveContext = context;
|
|
276
|
-
}
|
|
277
|
-
return await resolve(object, ref, resolveContext);
|
|
248
|
+
window['__debugResolve'] = async function debugResolve(object, jsonPointer, options) {
|
|
249
|
+
return await resolve(object, jsonPointer, options);
|
|
278
250
|
};
|
|
279
251
|
// (window as any)['__dd'] = async function dd() {
|
|
280
252
|
// const url = 'https://demo.expofp.com/manifest.json';
|
|
@@ -10,7 +10,7 @@ export const expoRuntimeBranchResolver = {
|
|
|
10
10
|
const branch = parseRefValue(ref);
|
|
11
11
|
const branchUrl = `${BASE_URL}branches/${branch}.json`;
|
|
12
12
|
return returnCachedRef(ref, context.refCache, async () => {
|
|
13
|
-
const release = await importJson(branchUrl, context);
|
|
13
|
+
const { release } = await importJson(branchUrl, context);
|
|
14
14
|
return {
|
|
15
15
|
$ref: `expo-runtime+https://efp-runtime.expofp.com/releases/${release}/`,
|
|
16
16
|
};
|
|
@@ -62,13 +62,14 @@ export const legacyAssetUrlsResolver = {
|
|
|
62
62
|
const layerUrl = `${dataUrlBase}${layerFile}?v=${version}`;
|
|
63
63
|
context.importCallback?.(layerUrl, 'script');
|
|
64
64
|
}
|
|
65
|
-
// const files = await filesPromise;...files,
|
|
65
|
+
// const files = await filesPromise;...files,
|
|
66
66
|
const data = globalThis.__data || {};
|
|
67
67
|
const dataFiles = ['data.js', 'wf.data.js', 'fp.svg.js', ...fpLayerFiles];
|
|
68
68
|
const dataFilesAssets = dataFiles.reduce((map, file) => {
|
|
69
69
|
map[file] = { $ref: `asset+${dataUrlBase}${file}?v=${version}` };
|
|
70
70
|
return map;
|
|
71
71
|
}, {});
|
|
72
|
+
// return {};
|
|
72
73
|
return { ...dataFilesAssets, ...populateDataJsAssetsMap(dataUrlBase, data) };
|
|
73
74
|
});
|
|
74
75
|
},
|