@qvac/sdk 0.19.0 → 0.20.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/NOTICE +111 -82
- package/dist/examples/abot-world.js +3 -3
- package/dist/examples/advisory-model-fit.d.ts +59 -0
- package/dist/examples/advisory-model-fit.js +156 -0
- package/dist/examples/asr/parakeet-nemotron-filesystem.d.ts +2 -0
- package/dist/examples/asr/parakeet-nemotron-filesystem.js +170 -0
- package/dist/examples/assess-model-fit.js +6 -1
- package/dist/examples/audiogen/edit-music.d.ts +2 -0
- package/dist/examples/audiogen/edit-music.js +121 -0
- package/dist/examples/audiogen/understand-music.d.ts +2 -0
- package/dist/examples/audiogen/understand-music.js +73 -0
- package/dist/examples/diffusion-ic-lora-ltx.js +3 -3
- package/dist/examples/diffusion-img2vid.js +1 -1
- package/dist/examples/diffusion-txt2vid-minimax-h3.d.ts +2 -0
- package/dist/examples/diffusion-txt2vid-minimax-h3.js +64 -0
- package/dist/examples/diffusion-txt2vid.js +1 -1
- package/dist/examples/kv-cache-cleanup.js +5 -2
- package/dist/examples/model-fit-ui/server.js +33 -7
- package/dist/examples/rag/rag-turbovec.d.ts +2 -0
- package/dist/examples/rag/rag-turbovec.js +58 -0
- package/dist/examples/tts/audio8.js +5 -5
- package/dist/examples/tts/chatterbox-enhanced.js +4 -5
- package/dist/examples/tts/chatterbox-multilingual.js +5 -5
- package/dist/examples/tts/chatterbox.js +5 -5
- package/dist/examples/tts/cosyvoice3.js +5 -5
- package/dist/examples/tts/llm-to-tts-streaming.js +12 -2
- package/dist/examples/tts/parler.js +5 -5
- package/dist/examples/tts/supertonic-enhanced.js +4 -5
- package/dist/examples/tts/supertonic-multilingual.js +5 -5
- package/dist/examples/tts/supertonic.js +5 -5
- package/dist/scripts/contract/build-contract.d.ts +1 -1
- package/dist/scripts/contract/constants-registry.d.ts +31 -0
- package/dist/scripts/contract/constants-registry.js +13 -2
- package/dist/scripts/contract/method-shapes.d.ts +3 -0
- package/dist/scripts/contract/method-shapes.js +3 -0
- package/dist/src/client/api/assess-model-fit.js +2 -0
- package/dist/src/client/api/audio-edit.d.ts +36 -0
- package/dist/src/client/api/audio-edit.js +38 -0
- package/dist/src/client/api/audio-gen-result.d.ts +5 -1
- package/dist/src/client/api/audio-gen-result.js +105 -52
- package/dist/src/client/api/audio-gen.d.ts +38 -1
- package/dist/src/client/api/audio-gen.js +38 -1
- package/dist/src/client/api/audio-understand.d.ts +25 -0
- package/dist/src/client/api/audio-understand.js +27 -0
- package/dist/src/client/api/delete-cache.d.ts +7 -1
- package/dist/src/client/api/delete-cache.js +11 -1
- package/dist/src/client/api/index.d.ts +3 -0
- package/dist/src/client/api/index.js +3 -0
- package/dist/src/client/api/text-to-speech.d.ts +31 -4
- package/dist/src/client/api/text-to-speech.js +118 -21
- package/dist/src/client/api/vector-index.d.ts +143 -0
- package/dist/src/client/api/vector-index.js +168 -0
- package/dist/src/client/api/world.d.ts +2 -2
- package/dist/src/commands/bundle/manifest.d.ts +2 -0
- package/dist/src/commands/verify/addon-source.d.ts +6 -0
- package/dist/src/commands/verify/bundle-source.d.ts +2 -0
- package/dist/src/commands/verify/bundle-source.js +155 -1
- package/dist/src/commands/verify/index.js +1 -0
- package/dist/src/commands/verify/prebuilds.d.ts +23 -0
- package/dist/src/commands/verify/prebuilds.js +129 -13
- package/dist/src/commands/verify/resource-collectors.js +2 -1
- package/dist/src/expo/plugins/patches/qvac-platform-addons.d.ts +23 -0
- package/dist/src/expo/plugins/patches/qvac-platform-addons.js +94 -0
- package/dist/src/expo/plugins/withMobileBundle.d.ts +12 -2
- package/dist/src/expo/plugins/withMobileBundle.js +62 -31
- package/dist/src/index.d.ts +3 -3
- package/dist/src/index.js +7 -3
- package/dist/src/schemas/sdk-errors-server.d.ts +4 -0
- package/dist/src/schemas/sdk-errors-server.js +22 -0
- package/dist/src/utils/aggregate-events.d.ts +3 -1
- package/dist/src/utils/aggregate-events.js +7 -1
- package/dist/src/utils/errors-server.d.ts +12 -0
- package/dist/src/utils/errors-server.js +21 -0
- package/docs/system-resources-support-matrix.md +19 -19
- package/package.json +22 -15
- package/src/electron-forge/index.cjs +86 -14
- package/src/expo/plugins/patches/android-link.mjs +18 -9
- package/src/expo/plugins/patches/ios-link.mjs +18 -9
- package/src/expo/plugins/patches/qvac-platform-addons.ts +124 -0
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
import { promises as fsp } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { DEFAULT_HOSTS } from '../bundle/constants.js';
|
|
2
4
|
import { buildNestedPathIndex, extractBarePackHeader, extractPackedString } from '../bundle/manifest.js';
|
|
3
5
|
import { deduplicateAddons, readAddonPackageJson } from './addon-source.js';
|
|
6
|
+
/** bare-pack resolves a linked native addon to this prefix instead of a module path. */
|
|
7
|
+
const LINKED_PREFIX = 'linked:';
|
|
8
|
+
/** Reserved resolution key naming the package.json that owns a module. */
|
|
9
|
+
const PACKAGE_KEY = '#package';
|
|
10
|
+
/** Export conditions that name a platform. */
|
|
11
|
+
const PLATFORMS = new Set(DEFAULT_HOSTS.map(platformOf));
|
|
12
|
+
/** `win32-x64` -> `win32`, `ios-arm64-simulator` -> `ios`. */
|
|
13
|
+
function platformOf(host) {
|
|
14
|
+
return host.split('-')[0] ?? host;
|
|
15
|
+
}
|
|
4
16
|
export class InvalidBundleSourceError extends Error {
|
|
5
17
|
bundlePath;
|
|
6
18
|
constructor(bundlePath, cause) {
|
|
@@ -13,7 +25,7 @@ export class InvalidBundleSourceError extends Error {
|
|
|
13
25
|
}
|
|
14
26
|
}
|
|
15
27
|
export async function collectAddonsFromBundle(options) {
|
|
16
|
-
const { bundlePath, projectRoot, diagnostics } = options;
|
|
28
|
+
const { bundlePath, projectRoot, hosts, diagnostics } = options;
|
|
17
29
|
let bundleText;
|
|
18
30
|
try {
|
|
19
31
|
bundleText = await fsp.readFile(bundlePath, 'utf8');
|
|
@@ -22,10 +34,14 @@ export async function collectAddonsFromBundle(options) {
|
|
|
22
34
|
throw new InvalidBundleSourceError(bundlePath, error);
|
|
23
35
|
}
|
|
24
36
|
let resolutions;
|
|
37
|
+
let main;
|
|
38
|
+
let imports;
|
|
25
39
|
try {
|
|
26
40
|
const packed = extractPackedString(bundleText);
|
|
27
41
|
const header = extractBarePackHeader(packed);
|
|
28
42
|
resolutions = header.resolutions ?? {};
|
|
43
|
+
main = header.main;
|
|
44
|
+
imports = header.imports ?? {};
|
|
29
45
|
}
|
|
30
46
|
catch (error) {
|
|
31
47
|
throw new InvalidBundleSourceError(bundlePath, error);
|
|
@@ -34,6 +50,14 @@ export async function collectAddonsFromBundle(options) {
|
|
|
34
50
|
diagnostics.emptyResolutions = true;
|
|
35
51
|
}
|
|
36
52
|
const pathsByPackage = buildNestedPathIndex(resolutions, projectRoot);
|
|
53
|
+
const analysed = analyseLinkedHosts({
|
|
54
|
+
resolutions,
|
|
55
|
+
main,
|
|
56
|
+
imports,
|
|
57
|
+
hosts: hosts ?? [],
|
|
58
|
+
projectRoot,
|
|
59
|
+
pathsByPackage
|
|
60
|
+
});
|
|
37
61
|
const addons = [];
|
|
38
62
|
for (const [pkgName, candidates] of pathsByPackage) {
|
|
39
63
|
for (const candidate of candidates) {
|
|
@@ -42,6 +66,8 @@ export async function collectAddonsFromBundle(options) {
|
|
|
42
66
|
expectedName: pkgName
|
|
43
67
|
});
|
|
44
68
|
if (result.isAddon && result.addon) {
|
|
69
|
+
if (analysed !== null)
|
|
70
|
+
result.addon.linkedHosts = analysed.get(candidate) ?? [];
|
|
45
71
|
addons.push(result.addon);
|
|
46
72
|
}
|
|
47
73
|
else if (result.invalid !== undefined && diagnostics !== undefined) {
|
|
@@ -51,3 +77,131 @@ export async function collectAddonsFromBundle(options) {
|
|
|
51
77
|
}
|
|
52
78
|
return deduplicateAddons(addons);
|
|
53
79
|
}
|
|
80
|
+
/**
|
|
81
|
+
* `mapLinkedHosts`, rejected unless its keys line up with the paths the path
|
|
82
|
+
* index found. A disagreement would read as "nothing is linked" and drop every
|
|
83
|
+
* prebuild check, so require an overlap before trusting the map.
|
|
84
|
+
*/
|
|
85
|
+
function analyseLinkedHosts(options) {
|
|
86
|
+
const { pathsByPackage, ...mapOptions } = options;
|
|
87
|
+
const linkedHosts = mapLinkedHosts(mapOptions);
|
|
88
|
+
if (linkedHosts === null)
|
|
89
|
+
return null;
|
|
90
|
+
for (const candidates of pathsByPackage.values()) {
|
|
91
|
+
for (const candidate of candidates) {
|
|
92
|
+
if (linkedHosts.has(candidate))
|
|
93
|
+
return linkedHosts;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Which of `hosts` link each package's addon, keyed by the owning package.json.
|
|
100
|
+
* A host counts only when the bundle both reaches a module of that package on
|
|
101
|
+
* its platform *and* resolves a `linked:` addon there.
|
|
102
|
+
*
|
|
103
|
+
* Both halves are needed because bare-pack emits two graph shapes: for a single
|
|
104
|
+
* host it resolves conditions eagerly, dropping the binding module of a package
|
|
105
|
+
* whose platform export is a JS stub; for several hosts it keeps every branch
|
|
106
|
+
* and links a name per host, so only reachability separates the platforms.
|
|
107
|
+
*
|
|
108
|
+
* Null leaves every host checked, as if no analysis had run.
|
|
109
|
+
*/
|
|
110
|
+
function mapLinkedHosts(options) {
|
|
111
|
+
const { resolutions, main, imports, hosts, projectRoot } = options;
|
|
112
|
+
if (hosts.length === 0)
|
|
113
|
+
return null;
|
|
114
|
+
if (main === undefined || !Object.prototype.hasOwnProperty.call(resolutions, main))
|
|
115
|
+
return null;
|
|
116
|
+
// `main` is the only root we walk; an import map could reach past it.
|
|
117
|
+
if (Object.keys(imports).length > 0)
|
|
118
|
+
return null;
|
|
119
|
+
const linked = new Map();
|
|
120
|
+
const ownersByPlatform = new Map();
|
|
121
|
+
for (const host of hosts) {
|
|
122
|
+
const platform = platformOf(host);
|
|
123
|
+
let owners = ownersByPlatform.get(platform);
|
|
124
|
+
if (owners === undefined) {
|
|
125
|
+
owners = linkedOwnersOn(resolutions, main, platform, projectRoot);
|
|
126
|
+
ownersByPlatform.set(platform, owners);
|
|
127
|
+
}
|
|
128
|
+
if (owners === null)
|
|
129
|
+
return null;
|
|
130
|
+
for (const owner of owners) {
|
|
131
|
+
let forOwner = linked.get(owner);
|
|
132
|
+
if (forOwner === undefined) {
|
|
133
|
+
forOwner = new Set();
|
|
134
|
+
linked.set(owner, forOwner);
|
|
135
|
+
}
|
|
136
|
+
forOwner.add(host);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return new Map([...linked].map(([owner, forOwner]) => [owner, [...forOwner].sort()]));
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* package.json paths of the addons linked on `platform`, walking from `main`.
|
|
143
|
+
* Null if a linked module names no owning package.
|
|
144
|
+
*/
|
|
145
|
+
function linkedOwnersOn(resolutions, main, platform, projectRoot) {
|
|
146
|
+
const owners = new Set();
|
|
147
|
+
const seen = new Set();
|
|
148
|
+
const stack = [main];
|
|
149
|
+
while (stack.length > 0) {
|
|
150
|
+
const module = stack.pop();
|
|
151
|
+
if (seen.has(module))
|
|
152
|
+
continue;
|
|
153
|
+
seen.add(module);
|
|
154
|
+
const edges = asRecord(resolutions[module]);
|
|
155
|
+
if (edges === null)
|
|
156
|
+
continue;
|
|
157
|
+
let linksAddon = false;
|
|
158
|
+
for (const [specifier, target] of Object.entries(edges)) {
|
|
159
|
+
if (specifier === PACKAGE_KEY)
|
|
160
|
+
continue;
|
|
161
|
+
for (const resolved of targetsOn(target, platform)) {
|
|
162
|
+
if (resolved.startsWith(LINKED_PREFIX))
|
|
163
|
+
linksAddon = true;
|
|
164
|
+
else
|
|
165
|
+
stack.push(resolved);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
if (!linksAddon)
|
|
169
|
+
continue;
|
|
170
|
+
const owner = edges[PACKAGE_KEY];
|
|
171
|
+
if (typeof owner !== 'string')
|
|
172
|
+
return null;
|
|
173
|
+
owners.add(path.join(projectRoot, owner));
|
|
174
|
+
}
|
|
175
|
+
return owners;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Every target a specifier can resolve to on `platform`, first-match-wins: this
|
|
179
|
+
* platform or `default` settles it, another platform is ruled out. A condition
|
|
180
|
+
* the host cannot decide (`require`, `bare`, ...) is kept and the scan goes on,
|
|
181
|
+
* so reachability is over- rather than under-reported.
|
|
182
|
+
*/
|
|
183
|
+
function targetsOn(target, platform) {
|
|
184
|
+
if (typeof target === 'string')
|
|
185
|
+
return [target];
|
|
186
|
+
const conditions = asRecord(target);
|
|
187
|
+
if (conditions === null)
|
|
188
|
+
return [];
|
|
189
|
+
const targets = [];
|
|
190
|
+
for (const [condition, value] of Object.entries(conditions)) {
|
|
191
|
+
if (PLATFORMS.has(condition)) {
|
|
192
|
+
if (condition !== platform)
|
|
193
|
+
continue;
|
|
194
|
+
targets.push(...targetsOn(value, platform));
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
197
|
+
targets.push(...targetsOn(value, platform));
|
|
198
|
+
if (condition === 'default')
|
|
199
|
+
break;
|
|
200
|
+
}
|
|
201
|
+
return targets;
|
|
202
|
+
}
|
|
203
|
+
function asRecord(value) {
|
|
204
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value))
|
|
205
|
+
return null;
|
|
206
|
+
return value;
|
|
207
|
+
}
|
|
@@ -11,6 +11,29 @@ export interface CheckPrebuildsOptions {
|
|
|
11
11
|
addon: NativeAddon;
|
|
12
12
|
hosts: string[];
|
|
13
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* One place an addon's prebuild for a host may live. `platformPackage` is set
|
|
16
|
+
* when the directory belongs to a per-platform prebuild package rather than
|
|
17
|
+
* the addon package itself.
|
|
18
|
+
*/
|
|
19
|
+
export interface PrebuildLocation {
|
|
20
|
+
hostDir: string;
|
|
21
|
+
platformPackage?: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Directories to search for an addon's `<host>` prebuild, in precedence order:
|
|
25
|
+
*
|
|
26
|
+
* 1. `<packageRoot>/prebuilds/<host>` — the fat layout every addon used to
|
|
27
|
+
* publish, and where source builds and `linked:` checkouts still land.
|
|
28
|
+
* 2. `<platformRoot>/addon/prebuilds/<host>` — the per-platform package the
|
|
29
|
+
* addon's `#host-addon` map names for this host, when that package is
|
|
30
|
+
* installed. Each platform package embeds an inner `addon/` package named
|
|
31
|
+
* after the meta addon so the `.bare` file keeps its name.
|
|
32
|
+
*
|
|
33
|
+
* A local `prebuilds/` always wins, matching the precedence the addons apply
|
|
34
|
+
* in their own `binding.js` (`require.addon()` first, platform package second).
|
|
35
|
+
*/
|
|
36
|
+
export declare function resolvePrebuildLocations(addon: NativeAddon, host: string): Promise<PrebuildLocation[]>;
|
|
14
37
|
export declare function checkPrebuilds(options: CheckPrebuildsOptions): Promise<MissingPrebuildIssue[]>;
|
|
15
38
|
export declare function listBarePrebuildFiles(hostDir: string): Promise<string[]>;
|
|
16
39
|
//# sourceMappingURL=prebuilds.d.ts.map
|
|
@@ -1,26 +1,142 @@
|
|
|
1
1
|
import { promises as fsp } from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { formatAddonId } from './addon-source.js';
|
|
4
|
+
import { HOST_ADDON_IMPORT, resolveAddonPlatformPackage } from '../../expo/plugins/patches/qvac-platform-addons.js';
|
|
5
|
+
const PREBUILDS_DIR = 'prebuilds';
|
|
6
|
+
const PLATFORM_ADDON_DIR = 'addon';
|
|
7
|
+
/**
|
|
8
|
+
* Directories to search for an addon's `<host>` prebuild, in precedence order:
|
|
9
|
+
*
|
|
10
|
+
* 1. `<packageRoot>/prebuilds/<host>` — the fat layout every addon used to
|
|
11
|
+
* publish, and where source builds and `linked:` checkouts still land.
|
|
12
|
+
* 2. `<platformRoot>/addon/prebuilds/<host>` — the per-platform package the
|
|
13
|
+
* addon's `#host-addon` map names for this host, when that package is
|
|
14
|
+
* installed. Each platform package embeds an inner `addon/` package named
|
|
15
|
+
* after the meta addon so the `.bare` file keeps its name.
|
|
16
|
+
*
|
|
17
|
+
* A local `prebuilds/` always wins, matching the precedence the addons apply
|
|
18
|
+
* in their own `binding.js` (`require.addon()` first, platform package second).
|
|
19
|
+
*/
|
|
20
|
+
export async function resolvePrebuildLocations(addon, host) {
|
|
21
|
+
const locations = [
|
|
22
|
+
{ hostDir: path.join(addon.packageRoot, PREBUILDS_DIR, host) }
|
|
23
|
+
];
|
|
24
|
+
const platformPackage = await platformPackageForHost(addon, host);
|
|
25
|
+
if (platformPackage === null)
|
|
26
|
+
return locations;
|
|
27
|
+
const platformRoot = await findInstalledPackage(await realPackageRoot(addon.packageRoot), platformPackage);
|
|
28
|
+
if (platformRoot !== null) {
|
|
29
|
+
locations.push({
|
|
30
|
+
hostDir: path.join(platformRoot, PLATFORM_ADDON_DIR, PREBUILDS_DIR, host),
|
|
31
|
+
platformPackage
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
return locations;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* The directory the addon really lives in. Under pnpm's isolated layout
|
|
38
|
+
* `node_modules/@qvac/tts-ggml` is a symlink into the virtual store
|
|
39
|
+
* (`node_modules/.pnpm/<id>/node_modules/@qvac/tts-ggml`), and the addon's
|
|
40
|
+
* dependencies — its platform package included — are linked next to it in
|
|
41
|
+
* that store directory rather than at the project's top-level `node_modules`.
|
|
42
|
+
* Node resolves from the real path, so the platform-package search starts
|
|
43
|
+
* there too. An unresolvable path (a fixture, a bundle path that no longer
|
|
44
|
+
* exists) is searched as given.
|
|
45
|
+
*/
|
|
46
|
+
async function realPackageRoot(packageRoot) {
|
|
47
|
+
try {
|
|
48
|
+
return await fsp.realpath(packageRoot);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return path.resolve(packageRoot);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
4
54
|
export async function checkPrebuilds(options) {
|
|
5
55
|
const { addon, hosts } = options;
|
|
6
56
|
const issues = [];
|
|
7
57
|
for (const host of hosts) {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
58
|
+
if (addon.linkedHosts !== undefined && !addon.linkedHosts.includes(host))
|
|
59
|
+
continue;
|
|
60
|
+
const locations = await resolvePrebuildLocations(addon, host);
|
|
61
|
+
if (await anyLocationHasPrebuild(locations))
|
|
62
|
+
continue;
|
|
63
|
+
issues.push({
|
|
64
|
+
code: 'missing-prebuild',
|
|
65
|
+
level: 'error',
|
|
66
|
+
addon: formatAddonId(addon),
|
|
67
|
+
host,
|
|
68
|
+
packageRoot: addon.packageRoot,
|
|
69
|
+
message: await describeMissingPrebuild(addon, host, locations)
|
|
70
|
+
});
|
|
21
71
|
}
|
|
22
72
|
return issues;
|
|
23
73
|
}
|
|
74
|
+
async function anyLocationHasPrebuild(locations) {
|
|
75
|
+
for (const location of locations) {
|
|
76
|
+
if ((await listBarePrebuildFiles(location.hostDir)).length > 0)
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
async function describeMissingPrebuild(addon, host, locations) {
|
|
82
|
+
const expected = locations.map((location) => path.join(location.hostDir, '*.bare'));
|
|
83
|
+
const platformPackage = await platformPackageForHost(addon, host);
|
|
84
|
+
const searchedPlatformPackage = locations.some((location) => location.platformPackage === platformPackage);
|
|
85
|
+
return (`${formatAddonId(addon)} is missing a prebuild for ${host} ` +
|
|
86
|
+
`(expected ${expected.join(' or ')}).` +
|
|
87
|
+
missingPlatformPackageHint(addon, platformPackage, searchedPlatformPackage));
|
|
88
|
+
}
|
|
89
|
+
function missingPlatformPackageHint(addon, platformPackage, searchedPlatformPackage) {
|
|
90
|
+
if (platformPackage === null || searchedPlatformPackage)
|
|
91
|
+
return '';
|
|
92
|
+
const pin = addon.version === undefined ? platformPackage : `"${platformPackage}": "${addon.version}"`;
|
|
93
|
+
return (` Add this exact dependency to package.json (same version as ${addon.name})` +
|
|
94
|
+
` and reinstall: ${pin}`);
|
|
95
|
+
}
|
|
96
|
+
async function platformPackageForHost(addon, host) {
|
|
97
|
+
return resolveAddonPlatformPackage(addon.name, await readHostAddonMap(addon), host);
|
|
98
|
+
}
|
|
99
|
+
async function readHostAddonMap(addon) {
|
|
100
|
+
try {
|
|
101
|
+
const raw = await fsp.readFile(path.join(addon.packageRoot, 'package.json'), 'utf8');
|
|
102
|
+
const parsed = JSON.parse(raw);
|
|
103
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed))
|
|
104
|
+
return undefined;
|
|
105
|
+
const imports = parsed.imports;
|
|
106
|
+
return imports?.[HOST_ADDON_IMPORT];
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
return undefined;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Locates `packageName` the way Node's resolver would from `fromDir`: in
|
|
114
|
+
* `<dir>/node_modules/<packageName>` for `fromDir` and each of its ancestors.
|
|
115
|
+
* Covers a hoisted platform package next to the meta package, one nested
|
|
116
|
+
* under the meta package's own `node_modules`, and — given the meta package's
|
|
117
|
+
* real path — the sibling link pnpm places in its virtual-store directory.
|
|
118
|
+
*/
|
|
119
|
+
async function findInstalledPackage(fromDir, packageName) {
|
|
120
|
+
let dir = path.resolve(fromDir);
|
|
121
|
+
for (;;) {
|
|
122
|
+
const candidate = path.join(dir, 'node_modules', packageName);
|
|
123
|
+
if (await isPackageDir(candidate))
|
|
124
|
+
return candidate;
|
|
125
|
+
const parent = path.dirname(dir);
|
|
126
|
+
if (parent === dir)
|
|
127
|
+
return null;
|
|
128
|
+
dir = parent;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
async function isPackageDir(dir) {
|
|
132
|
+
try {
|
|
133
|
+
const stat = await fsp.stat(path.join(dir, 'package.json'));
|
|
134
|
+
return stat.isFile();
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
24
140
|
export async function listBarePrebuildFiles(hostDir) {
|
|
25
141
|
let entries;
|
|
26
142
|
try {
|
|
@@ -15,7 +15,8 @@ const DEFAULT_MEASUREMENT_DEPENDENCIES = {
|
|
|
15
15
|
export async function acceptResourceCollectorPackaging(options) {
|
|
16
16
|
const addons = await collectAddonsFromBundle({
|
|
17
17
|
bundlePath: options.bundlePath,
|
|
18
|
-
projectRoot: options.projectRoot
|
|
18
|
+
projectRoot: options.projectRoot,
|
|
19
|
+
hosts: options.hosts
|
|
19
20
|
});
|
|
20
21
|
const collectors = new Map(addons
|
|
21
22
|
.filter((addon) => isResourceCollectorPackage(addon.name))
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export declare const HOST_ADDON_IMPORT = "#host-addon";
|
|
2
|
+
export interface PlatformAddonRoot {
|
|
3
|
+
dir: string;
|
|
4
|
+
pkg: {
|
|
5
|
+
name?: string;
|
|
6
|
+
version?: string;
|
|
7
|
+
addon?: boolean;
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
export declare function resolvePlatformAddonRoots(projectRoot: string, addonNames: string[], platform: string): PlatformAddonRoot[];
|
|
11
|
+
/**
|
|
12
|
+
* Package `#host-addon` names for `host`, if that name belongs to `metaName`.
|
|
13
|
+
* `host` is a Bare host (`android-arm64`, `ios-arm64-simulator`, `darwin-arm64`)
|
|
14
|
+
* or a mobile build target (`android`, `ios`).
|
|
15
|
+
*/
|
|
16
|
+
export declare function resolveAddonPlatformPackage(metaName: string, hostAddon: unknown, host: string): string | null;
|
|
17
|
+
/**
|
|
18
|
+
* First package name a `#host-addon` map points at for `host`.
|
|
19
|
+
* Android nests the package under its architecture; iOS is a flat list.
|
|
20
|
+
* A platform-only `android` target uses arm64, matching the current mobile host.
|
|
21
|
+
*/
|
|
22
|
+
export declare function resolvePlatformPackageName(hostAddon: unknown, host: string): string | null;
|
|
23
|
+
//# sourceMappingURL=qvac-platform-addons.d.ts.map
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves split-addon platform packages from the meta package's `#host-addon`
|
|
3
|
+
* map. Linker, verify, and the missing-prebuild error all call this module so
|
|
4
|
+
* the slice name cannot drift from what the addon actually imports.
|
|
5
|
+
*
|
|
6
|
+
* Keep this file free of `@/` imports: the compiled JS is copied next to the
|
|
7
|
+
* patched `link.mjs` as `qvac-platform-addons.mjs`.
|
|
8
|
+
*/
|
|
9
|
+
import * as fs from 'fs';
|
|
10
|
+
import * as path from 'path';
|
|
11
|
+
export const HOST_ADDON_IMPORT = '#host-addon';
|
|
12
|
+
const DEFAULT_ANDROID_CPU = 'arm64';
|
|
13
|
+
export function resolvePlatformAddonRoots(projectRoot, addonNames, platform) {
|
|
14
|
+
const roots = [];
|
|
15
|
+
for (const name of addonNames) {
|
|
16
|
+
const root = resolvePlatformAddonRoot(projectRoot, name, platform);
|
|
17
|
+
if (root !== null)
|
|
18
|
+
roots.push(root);
|
|
19
|
+
}
|
|
20
|
+
return roots;
|
|
21
|
+
}
|
|
22
|
+
function resolvePlatformAddonRoot(projectRoot, metaName, platform) {
|
|
23
|
+
const metaManifest = readManifest(packageDir(projectRoot, metaName));
|
|
24
|
+
if (metaManifest === null)
|
|
25
|
+
return null;
|
|
26
|
+
const platformPackage = resolveAddonPlatformPackage(metaName, metaManifest.imports?.[HOST_ADDON_IMPORT], platform);
|
|
27
|
+
if (platformPackage === null)
|
|
28
|
+
return null;
|
|
29
|
+
const addonDir = path.join(packageDir(projectRoot, platformPackage), 'addon');
|
|
30
|
+
const addonManifest = readManifest(addonDir);
|
|
31
|
+
if (addonManifest === null || addonManifest.addon !== true)
|
|
32
|
+
return null;
|
|
33
|
+
if (!fs.existsSync(path.join(addonDir, 'prebuilds')))
|
|
34
|
+
return null;
|
|
35
|
+
return { dir: addonDir, pkg: addonManifest };
|
|
36
|
+
}
|
|
37
|
+
function packageDir(projectRoot, packageName) {
|
|
38
|
+
return path.join(projectRoot, 'node_modules', ...packageName.split('/'));
|
|
39
|
+
}
|
|
40
|
+
function readManifest(dir) {
|
|
41
|
+
try {
|
|
42
|
+
const parsed = JSON.parse(fs.readFileSync(path.join(dir, 'package.json'), 'utf8'));
|
|
43
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed))
|
|
44
|
+
return null;
|
|
45
|
+
return parsed;
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Package `#host-addon` names for `host`, if that name belongs to `metaName`.
|
|
53
|
+
* `host` is a Bare host (`android-arm64`, `ios-arm64-simulator`, `darwin-arm64`)
|
|
54
|
+
* or a mobile build target (`android`, `ios`).
|
|
55
|
+
*/
|
|
56
|
+
export function resolveAddonPlatformPackage(metaName, hostAddon, host) {
|
|
57
|
+
const name = resolvePlatformPackageName(hostAddon, host);
|
|
58
|
+
if (name === null)
|
|
59
|
+
return null;
|
|
60
|
+
if (!name.startsWith(`${metaName}-`))
|
|
61
|
+
return null;
|
|
62
|
+
return name;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* First package name a `#host-addon` map points at for `host`.
|
|
66
|
+
* Android nests the package under its architecture; iOS is a flat list.
|
|
67
|
+
* A platform-only `android` target uses arm64, matching the current mobile host.
|
|
68
|
+
*/
|
|
69
|
+
export function resolvePlatformPackageName(hostAddon, host) {
|
|
70
|
+
if (typeof host !== 'string' || host.length === 0)
|
|
71
|
+
return null;
|
|
72
|
+
const separator = host.indexOf('-');
|
|
73
|
+
const platform = separator === -1 ? host : host.slice(0, separator);
|
|
74
|
+
const cpu = separator === -1 ? '' : host.slice(separator + 1);
|
|
75
|
+
const branch = readBranch(hostAddon, platform);
|
|
76
|
+
const direct = packageName(branch);
|
|
77
|
+
if (direct !== null)
|
|
78
|
+
return direct;
|
|
79
|
+
const cpuKey = cpu.length > 0 ? cpu : DEFAULT_ANDROID_CPU;
|
|
80
|
+
return packageName(readBranch(branch, cpuKey));
|
|
81
|
+
}
|
|
82
|
+
function packageName(candidate) {
|
|
83
|
+
const name = Array.isArray(candidate) ? candidate[0] : candidate;
|
|
84
|
+
if (typeof name !== 'string' || name.startsWith('.'))
|
|
85
|
+
return null;
|
|
86
|
+
return name;
|
|
87
|
+
}
|
|
88
|
+
function readBranch(value, key) {
|
|
89
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value))
|
|
90
|
+
return undefined;
|
|
91
|
+
if (!Object.hasOwn(value, key))
|
|
92
|
+
return undefined;
|
|
93
|
+
return value[key];
|
|
94
|
+
}
|
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
import type { ExpoConfig } from 'expo/config';
|
|
2
|
+
/**
|
|
3
|
+
* Desktop-only spawn path, deferred so bare-pack does not walk `bare-process`
|
|
4
|
+
* -> `bare-posix` (no `android-arm64` prebuild). Mobile advisory uses
|
|
5
|
+
* in-process `@qvac/model-fit` (`fitParams`), not this subprocess.
|
|
6
|
+
*/
|
|
7
|
+
declare const MOBILE_UNSUPPORTED_MODULES: string[];
|
|
8
|
+
type MobilePlatform = 'android' | 'ios';
|
|
9
|
+
declare const MOBILE_HOSTS_BY_PLATFORM: Record<MobilePlatform, string[]>;
|
|
10
|
+
/** Every mobile host, in platform order. The bundle covers all of them. */
|
|
2
11
|
declare const MOBILE_HOSTS: string[];
|
|
3
12
|
type BareKitLinkerPaths = {
|
|
4
13
|
android: string | null;
|
|
@@ -11,6 +20,7 @@ type BareKitLinkerPaths = {
|
|
|
11
20
|
* Uses `qvac.config.*` if present.
|
|
12
21
|
*/
|
|
13
22
|
declare function withMobileBundle(config: ExpoConfig): ExpoConfig;
|
|
23
|
+
declare function mobileHostsForPlatform(platform: string): string[];
|
|
14
24
|
/** Runs the patched iOS linker after bundleSdk has written the current addons manifest. */
|
|
15
25
|
declare function runIOSAddonLinker(linkerPath: string): Promise<void>;
|
|
16
26
|
/**
|
|
@@ -18,7 +28,7 @@ declare function runIOSAddonLinker(linkerPath: string): Promise<void>;
|
|
|
18
28
|
* the paths for each platform that was successfully patched.
|
|
19
29
|
*/
|
|
20
30
|
declare function patchBareKitLinkers(projectRoot: string, qvacSdkPath: string): BareKitLinkerPaths;
|
|
21
|
-
export { MOBILE_HOSTS, patchBareKitLinkers, runIOSAddonLinker };
|
|
22
|
-
export type { BareKitLinkerPaths };
|
|
31
|
+
export { MOBILE_HOSTS, MOBILE_HOSTS_BY_PLATFORM, MOBILE_UNSUPPORTED_MODULES, mobileHostsForPlatform, patchBareKitLinkers, runIOSAddonLinker };
|
|
32
|
+
export type { BareKitLinkerPaths, MobilePlatform };
|
|
23
33
|
export default withMobileBundle;
|
|
24
34
|
//# sourceMappingURL=withMobileBundle.d.ts.map
|