@expofp/config 3.28.3 → 3.30.0
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/README.md +6 -2
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/lib/load-config.js +52 -6
- package/dist/lib/serialize-config-resources.d.ts +24 -0
- package/dist/lib/serialize-config-resources.js +30 -0
- package/dist/lib/store-ref.d.ts +40 -0
- package/dist/lib/store-ref.js +56 -0
- package/dist/lib/visit-resources.d.ts +18 -4
- package/dist/lib/visit-resources.js +15 -4
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -32,5 +32,9 @@ been retired (`?hide=`, `?centerxy=`, `?lang=`, …) are listed in `retiredKeys`
|
|
|
32
32
|
old link opens a clean plan instead of replaying its query as search text.
|
|
33
33
|
|
|
34
34
|
Also: `applyIntents` (dispatch `selectBooth` / `changeLanguage` / … to the floor plan),
|
|
35
|
-
`serializeConfigResources` (collect schema-tagged assets + payloads for offline copies
|
|
36
|
-
`
|
|
35
|
+
`serializeConfigResources` (collect schema-tagged assets + payloads for offline copies and normalized
|
|
36
|
+
expo folders), `resolveUrl` / `compactUrl` (the `$/<expo>/<hash>/<name>` ⇄
|
|
37
|
+
`https://<expo>.expofp.com/immutable/<hash>/<name>` codec of a normalized folder's store refs —
|
|
38
|
+
`loadConfig` follows the folder's pointer `manifest.json` to its immutable snapshot and expands the
|
|
39
|
+
refs, so an effective config only ever carries full URLs), and `toDebugSettings` (config schema →
|
|
40
|
+
`@expofp/debug` panel). Types re-export from `@expofp/schema`.
|
package/dist/index.d.ts
CHANGED
|
@@ -5,7 +5,8 @@ export { parseDeprecatedUrlParams } from './lib/deprecated-url-params.js';
|
|
|
5
5
|
export { type LegacyOneShot, type LegacyPrimary, type LegacyUrlQuery, parseLegacyQuery, serializeSelection, splitPlatformParams, type UrlSelection, } from './lib/legacy-url.js';
|
|
6
6
|
export { loadConfig } from './lib/load-config.js';
|
|
7
7
|
export { type StorageLike } from './lib/local-storage-codec.js';
|
|
8
|
-
export { type ConfigResourceStore, serializeConfigResources, } from './lib/serialize-config-resources.js';
|
|
8
|
+
export { type ConfigResourceStore, serializationFormat, serializeConfigResources, SERIALIZER_VERSION, } from './lib/serialize-config-resources.js';
|
|
9
|
+
export { compactUrl, resolveUrl } from './lib/store-ref.js';
|
|
9
10
|
export { parseFromUrlTolerant } from './lib/url-codec.js';
|
|
10
11
|
export { parseIntentsFromUrl } from './lib/url-intents.js';
|
|
11
12
|
export { type Config, type Consent, type Manifest, type Options } from '@expofp/schema';
|
package/dist/index.js
CHANGED
|
@@ -6,6 +6,7 @@ export { toDebugSettings } from './lib/debug-settings.js';
|
|
|
6
6
|
export { parseDeprecatedUrlParams } from './lib/deprecated-url-params.js';
|
|
7
7
|
export { parseLegacyQuery, serializeSelection, splitPlatformParams, } from './lib/legacy-url.js';
|
|
8
8
|
export { loadConfig } from './lib/load-config.js';
|
|
9
|
-
export { serializeConfigResources, } from './lib/serialize-config-resources.js';
|
|
9
|
+
export { serializationFormat, serializeConfigResources, SERIALIZER_VERSION, } from './lib/serialize-config-resources.js';
|
|
10
|
+
export { compactUrl, resolveUrl } from './lib/store-ref.js';
|
|
10
11
|
export { parseFromUrlTolerant } from './lib/url-codec.js';
|
|
11
12
|
export { parseIntentsFromUrl } from './lib/url-intents.js';
|
package/dist/lib/load-config.js
CHANGED
|
@@ -8,10 +8,11 @@ import { parseFromStorage } from './local-storage-codec.js';
|
|
|
8
8
|
import { normalizeFpSvgLayerAliases } from './normalize-fp-svg.js';
|
|
9
9
|
import { normalizeLegacyData } from './normalize-legacy-data.js';
|
|
10
10
|
import { applyRebookingData } from './rebooking.js';
|
|
11
|
+
import { resolveUrl } from './store-ref.js';
|
|
11
12
|
import { parseFromUrlTolerant } from './url-codec.js';
|
|
12
13
|
import { parseIntentsFromUrl } from './url-intents.js';
|
|
13
14
|
import { readValidateFlag } from './validate-flag.js';
|
|
14
|
-
import { documentRef, setValidateManifest, validateOrClone } from './visit-resources.js';
|
|
15
|
+
import { documentRef, isRefShaped, setValidateManifest, validateOrClone, visitResources, } from './visit-resources.js';
|
|
15
16
|
const log = debug('efp:config');
|
|
16
17
|
/**
|
|
17
18
|
* Assemble the effective config — the runtime's single, complete copy of the
|
|
@@ -42,11 +43,17 @@ export async function loadConfig(manifest, options, url, storage) {
|
|
|
42
43
|
// layers merge, so it can't come off the effective config. See readValidateFlag.
|
|
43
44
|
const validateSchema = readValidateFlag(url, storage);
|
|
44
45
|
setValidateManifest(validateSchema); // gates the referenced-document parses (visit-resources)
|
|
45
|
-
//
|
|
46
|
-
//
|
|
47
|
-
//
|
|
48
|
-
//
|
|
49
|
-
|
|
46
|
+
// A normalized expo folder's mutable `manifest.json` is a tiny pointer
|
|
47
|
+
// (`{"$ref": "$/<expo>/<hash>/manifest.json"}`) — follow the chain down to
|
|
48
|
+
// the snapshot first. Then take an OWN copy of the settled manifest
|
|
49
|
+
// (validated when the flag is on — see validateOrClone): `resolve`
|
|
50
|
+
// deep-freezes what it settles, an offline or normalized manifest carries
|
|
51
|
+
// the full event data (booths, exhibitors, …), and `normalizeLegacyData`
|
|
52
|
+
// below mutates those nested objects in place. Store refs (`$/…`) expand
|
|
53
|
+
// into their canonical immutable URLs right here — the effective config
|
|
54
|
+
// only ever carries full URLs, so no consumer sees the notation.
|
|
55
|
+
manifest = validateOrClone(ManifestSchema, await resolveManifestChain(manifest));
|
|
56
|
+
await expandManifestStoreRefs(manifest);
|
|
50
57
|
// The legacy event-data directory and the data-revision pin are INPUTS,
|
|
51
58
|
// consumed right here — the effective config carries only the refs derived
|
|
52
59
|
// from them, so neither is merged.
|
|
@@ -195,6 +202,45 @@ export async function loadConfig(manifest, options, url, storage) {
|
|
|
195
202
|
deepFreeze(config);
|
|
196
203
|
return config;
|
|
197
204
|
}
|
|
205
|
+
/**
|
|
206
|
+
* Hops a manifest pointer chain may take before it must have settled. The
|
|
207
|
+
* normalized form needs exactly one (mutable pointer → immutable snapshot);
|
|
208
|
+
* the ceiling only guards against a cyclic or runaway chain.
|
|
209
|
+
*/
|
|
210
|
+
const MANIFEST_POINTER_HOPS_MAX = 3;
|
|
211
|
+
/**
|
|
212
|
+
* Follow the «pointer → snapshot» chain: a settled manifest that is itself
|
|
213
|
+
* ref-shaped is a pointer — resolve what it points at, until a document
|
|
214
|
+
* settles. A store ref (`$/…`) expands by the fixed convention before its
|
|
215
|
+
* fetch, the input ref included; a rooted local pointer resolves against
|
|
216
|
+
* the page like any rooted URL. Chain-walking lives here, not in
|
|
217
|
+
* `@expofp/resolve` — the resolver stays one-layered and domain-free.
|
|
218
|
+
*/
|
|
219
|
+
async function resolveManifestChain(input) {
|
|
220
|
+
let current = input;
|
|
221
|
+
for (let hops = 0;; hops++) {
|
|
222
|
+
if (isRefShaped(current))
|
|
223
|
+
current = { ...current, $ref: resolveUrl(current.$ref) };
|
|
224
|
+
const settled = await resolve(current);
|
|
225
|
+
if (!isRefShaped(settled))
|
|
226
|
+
return settled;
|
|
227
|
+
if (hops >= MANIFEST_POINTER_HOPS_MAX) {
|
|
228
|
+
throw new Error(`loadConfig: manifest pointer chain deeper than ${MANIFEST_POINTER_HOPS_MAX} at ${settled.$ref}`);
|
|
229
|
+
}
|
|
230
|
+
log('loadConfig', 'manifest is a pointer, following it to', settled.$ref);
|
|
231
|
+
current = { $ref: settled.$ref };
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Expand every store ref (`$/…`) of a normalized manifest — asset URLs and
|
|
236
|
+
* document `$ref`s alike — into its canonical URL, via the one schema-guided
|
|
237
|
+
* walk. Passthrough for other values, so compat and offline manifests come
|
|
238
|
+
* out untouched.
|
|
239
|
+
*/
|
|
240
|
+
async function expandManifestStoreRefs(manifest) {
|
|
241
|
+
const expand = (url) => resolveUrl(url);
|
|
242
|
+
await visitResources(ManifestSchema, manifest, { asset: expand, ref: expand });
|
|
243
|
+
}
|
|
198
244
|
async function loadLegacyDataJs(baseUrl, version) {
|
|
199
245
|
// data.js is a window-assigning script like fp.svg.js — resolve() evaluates
|
|
200
246
|
// it to its globals and the expo data document rides on `__data`. The
|
|
@@ -19,4 +19,28 @@ export type ConfigResourceStore = ResourceHandler;
|
|
|
19
19
|
export declare function serializeConfigResources(config: Config, store: ConfigResourceStore, options?: {
|
|
20
20
|
signal?: AbortSignal | null;
|
|
21
21
|
}): Promise<Manifest>;
|
|
22
|
+
/**
|
|
23
|
+
* Version of the serialization POLICY — what {@link serializeConfigResources}
|
|
24
|
+
* does to a config beyond what the schema says: the walk's rewrite rules
|
|
25
|
+
* (`visit-resources`), the CSS `@import` splicing, the legacy normalization.
|
|
26
|
+
* Bump it by hand when that behavior changes without a schema change; it is
|
|
27
|
+
* one half of {@link serializationFormat}.
|
|
28
|
+
*/
|
|
29
|
+
export declare const SERIALIZER_VERSION = 1;
|
|
30
|
+
/**
|
|
31
|
+
* The identity of the form a serialized manifest takes — the text a generator
|
|
32
|
+
* hashes into its format stamp. Two halves: the JSON Schema of `ConfigSchema`
|
|
33
|
+
* (the shape every snapshot follows; the referenced document schemas ride
|
|
34
|
+
* inside it, so a drawing- or wayfinding-schema change counts too — a schema
|
|
35
|
+
* change is a format change by construction, no hand bump needed) and
|
|
36
|
+
* {@link SERIALIZER_VERSION}. Deterministic across processes and platforms
|
|
37
|
+
* for one build of the packages.
|
|
38
|
+
*
|
|
39
|
+
* A snapshot built under one stamp must not be served under a runtime that
|
|
40
|
+
* expects another: the cloud generator (plan G § 2, wave G2) keys its
|
|
41
|
+
* level-0 "nothing changed" skip on this stamp next to the data revision, so
|
|
42
|
+
* a deploy that changes the format rebuilds every snapshot and a deploy that
|
|
43
|
+
* does not leaves them alone.
|
|
44
|
+
*/
|
|
45
|
+
export declare function serializationFormat(): string;
|
|
22
46
|
//# sourceMappingURL=serialize-config-resources.d.ts.map
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ConfigSchema } from '@expofp/schema';
|
|
2
|
+
import * as z from 'zod';
|
|
2
3
|
import { visitResources } from './visit-resources.js';
|
|
3
4
|
/**
|
|
4
5
|
* Serialize a loaded config into a portable manifest: every schema-tagged
|
|
@@ -18,3 +19,32 @@ export async function serializeConfigResources(config, store, options) {
|
|
|
18
19
|
await visitResources(ConfigSchema, manifest, store, { signal: options?.signal });
|
|
19
20
|
return manifest;
|
|
20
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* Version of the serialization POLICY — what {@link serializeConfigResources}
|
|
24
|
+
* does to a config beyond what the schema says: the walk's rewrite rules
|
|
25
|
+
* (`visit-resources`), the CSS `@import` splicing, the legacy normalization.
|
|
26
|
+
* Bump it by hand when that behavior changes without a schema change; it is
|
|
27
|
+
* one half of {@link serializationFormat}.
|
|
28
|
+
*/
|
|
29
|
+
export const SERIALIZER_VERSION = 1;
|
|
30
|
+
/**
|
|
31
|
+
* The identity of the form a serialized manifest takes — the text a generator
|
|
32
|
+
* hashes into its format stamp. Two halves: the JSON Schema of `ConfigSchema`
|
|
33
|
+
* (the shape every snapshot follows; the referenced document schemas ride
|
|
34
|
+
* inside it, so a drawing- or wayfinding-schema change counts too — a schema
|
|
35
|
+
* change is a format change by construction, no hand bump needed) and
|
|
36
|
+
* {@link SERIALIZER_VERSION}. Deterministic across processes and platforms
|
|
37
|
+
* for one build of the packages.
|
|
38
|
+
*
|
|
39
|
+
* A snapshot built under one stamp must not be served under a runtime that
|
|
40
|
+
* expects another: the cloud generator (plan G § 2, wave G2) keys its
|
|
41
|
+
* level-0 "nothing changed" skip on this stamp next to the data revision, so
|
|
42
|
+
* a deploy that changes the format rebuilds every snapshot and a deploy that
|
|
43
|
+
* does not leaves them alone.
|
|
44
|
+
*/
|
|
45
|
+
export function serializationFormat() {
|
|
46
|
+
return JSON.stringify({
|
|
47
|
+
serializer: SERIALIZER_VERSION,
|
|
48
|
+
schema: z.toJSONSchema(ConfigSchema, { io: 'input' }),
|
|
49
|
+
});
|
|
50
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `$/` notation of the normalized expo form (plan G § 3) — the codec
|
|
3
|
+
* between a store ref and the canonical URL it aliases.
|
|
4
|
+
*
|
|
5
|
+
* Every object of an expo's content-addressed store lives at one URL fixed
|
|
6
|
+
* forever — `https://<expo>.expofp.com/immutable/<hash>/<name>`, the expo's
|
|
7
|
+
* production host whichever contour generated or serves the folder — and
|
|
8
|
+
* the store ref `$/<expo>/<hash>/<name>` is that URL's alias by pure prefix
|
|
9
|
+
* substitution. No document, page or base URL takes part, so a snapshot
|
|
10
|
+
* reads identically from a contour page, an integrator's host, a local
|
|
11
|
+
* `floorplan.js`, an inline copy or a Node tool. The two forms are
|
|
12
|
+
* equivalent and a creator compacts at will: shorter bytes, an explicit
|
|
13
|
+
* "store content" marker, and an alias that survives a domain move.
|
|
14
|
+
*
|
|
15
|
+
* The marker is unambiguous by generation invariant, not by URL illegality:
|
|
16
|
+
* a store only ever returns canonical URLs, and no passthrough value ever
|
|
17
|
+
* starts with `$/`. A ref left unexpanded by mistake breaks loudly — an
|
|
18
|
+
* honest 404 on `<base>/$/…` — where a `#/…` fragment would fail silently.
|
|
19
|
+
*
|
|
20
|
+
* The schema `.meta` walker (`visit-resources`) calls the codec at every
|
|
21
|
+
* URL-carrying place — manifest fields, document `$ref`s, CSS `url(…)`
|
|
22
|
+
* tokens, `<image>` hrefs of a drawing — so prose like `US$/day` is never
|
|
23
|
+
* touched, and the effective config only ever carries full URLs.
|
|
24
|
+
*/
|
|
25
|
+
/** Whether a URL-carrying value is a store ref (`$/…`). */
|
|
26
|
+
export declare function isStoreRef(value: string): boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Expand a store ref into the canonical URL it aliases:
|
|
29
|
+
* `$/demo/ZL-InrvJgYpz1-k2/logo.png` →
|
|
30
|
+
* `https://demo.expofp.com/immutable/ZL-InrvJgYpz1-k2/logo.png`. Any other
|
|
31
|
+
* value — a full URL, a rooted or relative path, a `data:` URI — passes
|
|
32
|
+
* through untouched. A malformed `$/…` is a generation bug and throws.
|
|
33
|
+
*/
|
|
34
|
+
export declare function resolveUrl(ref: string): string;
|
|
35
|
+
/**
|
|
36
|
+
* Compact a canonical store URL into its `$/` alias — the inverse of
|
|
37
|
+
* {@link resolveUrl}; any other URL passes through untouched.
|
|
38
|
+
*/
|
|
39
|
+
export declare function compactUrl(url: string): string;
|
|
40
|
+
//# sourceMappingURL=store-ref.d.ts.map
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `$/` notation of the normalized expo form (plan G § 3) — the codec
|
|
3
|
+
* between a store ref and the canonical URL it aliases.
|
|
4
|
+
*
|
|
5
|
+
* Every object of an expo's content-addressed store lives at one URL fixed
|
|
6
|
+
* forever — `https://<expo>.expofp.com/immutable/<hash>/<name>`, the expo's
|
|
7
|
+
* production host whichever contour generated or serves the folder — and
|
|
8
|
+
* the store ref `$/<expo>/<hash>/<name>` is that URL's alias by pure prefix
|
|
9
|
+
* substitution. No document, page or base URL takes part, so a snapshot
|
|
10
|
+
* reads identically from a contour page, an integrator's host, a local
|
|
11
|
+
* `floorplan.js`, an inline copy or a Node tool. The two forms are
|
|
12
|
+
* equivalent and a creator compacts at will: shorter bytes, an explicit
|
|
13
|
+
* "store content" marker, and an alias that survives a domain move.
|
|
14
|
+
*
|
|
15
|
+
* The marker is unambiguous by generation invariant, not by URL illegality:
|
|
16
|
+
* a store only ever returns canonical URLs, and no passthrough value ever
|
|
17
|
+
* starts with `$/`. A ref left unexpanded by mistake breaks loudly — an
|
|
18
|
+
* honest 404 on `<base>/$/…` — where a `#/…` fragment would fail silently.
|
|
19
|
+
*
|
|
20
|
+
* The schema `.meta` walker (`visit-resources`) calls the codec at every
|
|
21
|
+
* URL-carrying place — manifest fields, document `$ref`s, CSS `url(…)`
|
|
22
|
+
* tokens, `<image>` hrefs of a drawing — so prose like `US$/day` is never
|
|
23
|
+
* touched, and the effective config only ever carries full URLs.
|
|
24
|
+
*/
|
|
25
|
+
const STORE_REF_PREFIX = '$/';
|
|
26
|
+
/** `$/<expo>/<key>` — the expo is one lowercase DNS label, the key is the rest verbatim. */
|
|
27
|
+
const STORE_REF = /^\$\/([a-z0-9-]+)\/(.+)$/;
|
|
28
|
+
/** The canonical store URL: the expo's production host, `/immutable/<key>`. */
|
|
29
|
+
const CANONICAL_URL = /^https:\/\/([a-z0-9-]+)\.expofp\.com\/immutable\/(.+)$/;
|
|
30
|
+
/** Whether a URL-carrying value is a store ref (`$/…`). */
|
|
31
|
+
export function isStoreRef(value) {
|
|
32
|
+
return value.startsWith(STORE_REF_PREFIX);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Expand a store ref into the canonical URL it aliases:
|
|
36
|
+
* `$/demo/ZL-InrvJgYpz1-k2/logo.png` →
|
|
37
|
+
* `https://demo.expofp.com/immutable/ZL-InrvJgYpz1-k2/logo.png`. Any other
|
|
38
|
+
* value — a full URL, a rooted or relative path, a `data:` URI — passes
|
|
39
|
+
* through untouched. A malformed `$/…` is a generation bug and throws.
|
|
40
|
+
*/
|
|
41
|
+
export function resolveUrl(ref) {
|
|
42
|
+
if (!isStoreRef(ref))
|
|
43
|
+
return ref;
|
|
44
|
+
const match = STORE_REF.exec(ref);
|
|
45
|
+
if (!match)
|
|
46
|
+
throw new Error(`resolveUrl: malformed store ref ${JSON.stringify(ref)}`);
|
|
47
|
+
return `https://${match[1]}.expofp.com/immutable/${match[2]}`;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Compact a canonical store URL into its `$/` alias — the inverse of
|
|
51
|
+
* {@link resolveUrl}; any other URL passes through untouched.
|
|
52
|
+
*/
|
|
53
|
+
export function compactUrl(url) {
|
|
54
|
+
const match = CANONICAL_URL.exec(url);
|
|
55
|
+
return match ? `${STORE_REF_PREFIX}${match[1]}/${match[2]}` : url;
|
|
56
|
+
}
|
|
@@ -21,13 +21,23 @@ import { type Ref } from '@expofp/resolve';
|
|
|
21
21
|
import type * as z from 'zod';
|
|
22
22
|
export interface ResourceHandler {
|
|
23
23
|
/**
|
|
24
|
-
* One asset URL; return the URL to write in its place
|
|
25
|
-
*
|
|
26
|
-
*
|
|
24
|
+
* One asset URL; return the URL to write in its place, or `undefined` to
|
|
25
|
+
* drop the field. Dropping is only meaningful for `optional` leaves — an
|
|
26
|
+
* `optionalAsset` tag marks a URL minted without an existence check, so the
|
|
27
|
+
* resource may not exist and storing it is best-effort (a store that finds
|
|
28
|
+
* it missing drops the field rather than write a dead URL; today's optional
|
|
29
|
+
* assets are all object fields, where a drop deletes the key).
|
|
27
30
|
*/
|
|
28
|
-
asset(url: string, optional?: boolean): string | Promise<string>;
|
|
31
|
+
asset(url: string, optional?: boolean): string | undefined | Promise<string | undefined>;
|
|
29
32
|
/** A ref's resolved — and internally rewritten — document; return the new ref URL. */
|
|
30
33
|
document?(refUrl: string, document: unknown): string | Promise<string>;
|
|
34
|
+
/**
|
|
35
|
+
* Rewrite a ref's URL in place WITHOUT resolving its document — the
|
|
36
|
+
* expansion hook for store refs (`$/…`) in a normalized manifest, where
|
|
37
|
+
* the document must stay lazy. When present it takes the whole ref
|
|
38
|
+
* branch: `document`/`documentError` do not fire on the same walk.
|
|
39
|
+
*/
|
|
40
|
+
ref?(refUrl: string): string | Promise<string>;
|
|
31
41
|
/**
|
|
32
42
|
* Tolerate a ref that failed to resolve — the field is dropped. Omitted,
|
|
33
43
|
* the failure propagates and aborts the walk; throw from here to abort
|
|
@@ -82,4 +92,8 @@ export declare function validateOrClone<T>(schema: z.ZodType, value: T): T;
|
|
|
82
92
|
export declare function documentRef<R extends Ref<unknown>>(ref: R, schema: z.ZodType, normalize?: (document: object) => void): R;
|
|
83
93
|
/** Rewrite every schema-tagged resource of `value` through `handler`, in place. */
|
|
84
94
|
export declare function visitResources(schema: z.ZodType, value: object, handler: ResourceHandler, options?: VisitResourcesOptions): Promise<void>;
|
|
95
|
+
/** Package-internal: `loadConfig`'s pointer-chain loop probes with it too. */
|
|
96
|
+
export declare function isRefShaped(value: unknown): value is {
|
|
97
|
+
$ref: string;
|
|
98
|
+
};
|
|
85
99
|
//# sourceMappingURL=visit-resources.d.ts.map
|
|
@@ -21,6 +21,7 @@ import { getResolved, isResolved, resolve } from '@expofp/resolve';
|
|
|
21
21
|
import { deepClone } from '@expofp/utils';
|
|
22
22
|
import { refSchemaOf, toJsonSchema, typeMatches } from './json-schema.js';
|
|
23
23
|
import { collectCssUrls, collectSvgImageAssets, inlineCssImports, isAbsoluteUrl, refDirectory, rewriteCssUrls, rewriteSvgImageAssets, toAbsoluteUrl, } from './resource-urls.js';
|
|
24
|
+
import { isStoreRef, resolveUrl } from './store-ref.js';
|
|
24
25
|
import { timeValidation } from './validate-timing.js';
|
|
25
26
|
/**
|
|
26
27
|
* Gates every schema validation (`parse`) of the large expo documents —
|
|
@@ -82,7 +83,10 @@ export function documentRef(ref, schema, normalize) {
|
|
|
82
83
|
const base = refDirectory(refUrl);
|
|
83
84
|
const rebase = isAbsoluteUrl(base);
|
|
84
85
|
await visitResources(schema, document, {
|
|
85
|
-
|
|
86
|
+
// A store ref (`$/…` — normalized snapshots) expands by the fixed
|
|
87
|
+
// convention, wherever the document came from (store-ref.ts); other
|
|
88
|
+
// paths get the standard web rebase.
|
|
89
|
+
asset: (url) => (isStoreRef(url) ? resolveUrl(url) : rebase ? toAbsoluteUrl(url, base) : url),
|
|
86
90
|
});
|
|
87
91
|
return document;
|
|
88
92
|
};
|
|
@@ -105,6 +109,10 @@ async function walk(node, value, assign, handler, options) {
|
|
|
105
109
|
// Rooted refs are already local — honored verbatim, like every other
|
|
106
110
|
// rooted resource URL.
|
|
107
111
|
if (isRefShaped(value)) {
|
|
112
|
+
if (handler.ref) {
|
|
113
|
+
assign({ $ref: await handler.ref(value.$ref) });
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
108
116
|
if (!handler.document || value.$ref.startsWith('/'))
|
|
109
117
|
return;
|
|
110
118
|
const refUrl = value.$ref;
|
|
@@ -155,8 +163,10 @@ async function walk(node, value, assign, handler, options) {
|
|
|
155
163
|
for (const url of collectCssUrls(css)) {
|
|
156
164
|
if (external.has(url))
|
|
157
165
|
continue; // a kept @import's URL stays remote
|
|
158
|
-
//
|
|
159
|
-
|
|
166
|
+
// Only scheme-qualified CSS urls are fetchable resources (legacy
|
|
167
|
+
// parity) — plus store refs (`$/…`), which a normalized snapshot
|
|
168
|
+
// carries there and the load must expand back symmetrically.
|
|
169
|
+
if (isStoreRef(url) || /^[a-z][a-z0-9+.-]*:/i.test(url)) {
|
|
160
170
|
rewrites.set(url, await handler.asset(url));
|
|
161
171
|
}
|
|
162
172
|
}
|
|
@@ -189,7 +199,8 @@ async function walk(node, value, assign, handler, options) {
|
|
|
189
199
|
}
|
|
190
200
|
}
|
|
191
201
|
}
|
|
192
|
-
|
|
202
|
+
/** Package-internal: `loadConfig`'s pointer-chain loop probes with it too. */
|
|
203
|
+
export function isRefShaped(value) {
|
|
193
204
|
return (typeof value === 'object' && value !== null && '$ref' in value && typeof value.$ref === 'string');
|
|
194
205
|
}
|
|
195
206
|
/** The root is an object/array container — leaves rewrite inside it. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@expofp/config",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.30.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "ExpoFP SDK internal: config layer and schemas",
|
|
6
6
|
"homepage": "https://developer.expofp.com/",
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
"debug": "^4.4.3",
|
|
30
30
|
"tslib": "^2.3.0",
|
|
31
31
|
"zod": "4.4.3",
|
|
32
|
-
"@expofp/resolve": "3.
|
|
33
|
-
"@expofp/schema": "3.
|
|
34
|
-
"@expofp/utils": "3.
|
|
32
|
+
"@expofp/resolve": "3.30.0",
|
|
33
|
+
"@expofp/schema": "3.30.0",
|
|
34
|
+
"@expofp/utils": "3.30.0"
|
|
35
35
|
}
|
|
36
36
|
}
|