@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
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* @qvac/sdk/electron-forge
|
|
3
3
|
*
|
|
4
4
|
* Electron Forge plugin: bundles the QVAC worker, verifies its native addons,
|
|
5
|
-
* then configures Electron Packager to tree-shake unused @qvac/* addons
|
|
6
|
-
* non-target prebuilds.
|
|
5
|
+
* then configures Electron Packager to tree-shake unused @qvac/* addons (along
|
|
6
|
+
* with their per-platform prebuild packages) and non-target prebuilds.
|
|
7
7
|
*
|
|
8
8
|
* macOS universal (`arch: "universal"`) is not supported — native addon
|
|
9
9
|
* prebuilds are arch-specific. Build darwin-arm64 and darwin-x64 separately.
|
|
@@ -146,16 +146,45 @@ function findQvacScopeDir(startDir) {
|
|
|
146
146
|
}
|
|
147
147
|
|
|
148
148
|
/**
|
|
149
|
-
*
|
|
150
|
-
*
|
|
149
|
+
* Reads the addon a per-platform prebuild package ships binaries for.
|
|
150
|
+
*
|
|
151
|
+
* Addons that split their prebuilds (`@qvac/tts-ggml` since 0.9.0, along with
|
|
152
|
+
* `@qvac/asr-ggml` 0.5.0 and `@qvac/audiogen-ggml` 0.4.0) publish the host's
|
|
153
|
+
* `.bare` in `@qvac/<addon>-<host>` (iOS flavours grouped as
|
|
154
|
+
* `@qvac/<addon>-ios`). Those packages carry no top-level `addon: true`;
|
|
155
|
+
* instead they embed an inner `addon/` package named after the meta addon.
|
|
156
|
+
* Returns that addon name, or null when `pkgDir` is not a platform package.
|
|
157
|
+
* Exposed for unit testing.
|
|
158
|
+
*/
|
|
159
|
+
function readPlatformPackageAddon(pkgDir) {
|
|
160
|
+
const addonPkgJsonPath = path.join(pkgDir, 'addon', 'package.json')
|
|
161
|
+
if (!fs.existsSync(addonPkgJsonPath)) return null
|
|
162
|
+
|
|
163
|
+
try {
|
|
164
|
+
const pkg = JSON.parse(fs.readFileSync(addonPkgJsonPath, 'utf8'))
|
|
165
|
+
if (pkg.addon === true && typeof pkg.name === 'string' && pkg.name.length > 0) {
|
|
166
|
+
return pkg.name
|
|
167
|
+
}
|
|
168
|
+
} catch (err) {
|
|
169
|
+
logger.warn(`Failed to parse ${addonPkgJsonPath}: ${err.message}`)
|
|
170
|
+
}
|
|
171
|
+
return null
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Discovers installed @qvac packages by scanning node_modules/@qvac: the
|
|
176
|
+
* addons (`addon: true` in package.json) and the per-platform prebuild
|
|
177
|
+
* packages, each mapped to the addon it belongs to.
|
|
151
178
|
*/
|
|
152
179
|
function discoverQvacAddonPackages(projectDir) {
|
|
180
|
+
const nothing = { addons: [], platformPackages: [] }
|
|
181
|
+
|
|
153
182
|
let scopeDir
|
|
154
183
|
try {
|
|
155
184
|
scopeDir = findQvacScopeDir(projectDir)
|
|
156
185
|
} catch (err) {
|
|
157
186
|
logger.warn(err.message)
|
|
158
|
-
return
|
|
187
|
+
return nothing
|
|
159
188
|
}
|
|
160
189
|
|
|
161
190
|
let entries
|
|
@@ -163,10 +192,11 @@ function discoverQvacAddonPackages(projectDir) {
|
|
|
163
192
|
entries = fs.readdirSync(scopeDir)
|
|
164
193
|
} catch (err) {
|
|
165
194
|
logger.fsError('discoverQvacAddonPackages', err)
|
|
166
|
-
return
|
|
195
|
+
return nothing
|
|
167
196
|
}
|
|
168
197
|
|
|
169
|
-
const
|
|
198
|
+
const addons = []
|
|
199
|
+
const platformPackages = []
|
|
170
200
|
|
|
171
201
|
for (const name of entries) {
|
|
172
202
|
const pkgDir = path.join(scopeDir, name)
|
|
@@ -175,18 +205,25 @@ function discoverQvacAddonPackages(projectDir) {
|
|
|
175
205
|
const pkgJsonPath = path.join(pkgDir, 'package.json')
|
|
176
206
|
if (!fs.existsSync(pkgJsonPath)) continue
|
|
177
207
|
|
|
208
|
+
const packageName = `@qvac/${name}`
|
|
178
209
|
try {
|
|
179
210
|
const pkg = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf8'))
|
|
180
211
|
if (pkg.addon === true) {
|
|
181
|
-
|
|
212
|
+
addons.push(packageName)
|
|
213
|
+
continue
|
|
182
214
|
}
|
|
183
215
|
} catch (err) {
|
|
184
216
|
logger.warn(`Failed to parse ${pkgJsonPath}: ${err.message}`)
|
|
217
|
+
continue
|
|
185
218
|
}
|
|
219
|
+
|
|
220
|
+
const addon = readPlatformPackageAddon(pkgDir)
|
|
221
|
+
if (addon !== null) platformPackages.push({ name: packageName, addon })
|
|
186
222
|
}
|
|
187
223
|
|
|
188
|
-
|
|
189
|
-
|
|
224
|
+
addons.sort()
|
|
225
|
+
platformPackages.sort((a, b) => a.name.localeCompare(b.name))
|
|
226
|
+
return { addons, platformPackages }
|
|
190
227
|
}
|
|
191
228
|
|
|
192
229
|
/**
|
|
@@ -203,20 +240,36 @@ function diffAddons(installed, required) {
|
|
|
203
240
|
}
|
|
204
241
|
|
|
205
242
|
/**
|
|
206
|
-
*
|
|
243
|
+
* Pure diff: per-platform prebuild packages whose addon is not in the
|
|
244
|
+
* required set. A platform package ships only its addon's binaries, so it
|
|
245
|
+
* follows that addon's include/exclude decision.
|
|
246
|
+
* Exposed for unit testing.
|
|
247
|
+
*/
|
|
248
|
+
function diffPlatformPackages(platformPackages, required) {
|
|
249
|
+
const requiredSet = new Set(required)
|
|
250
|
+
const exclusions = []
|
|
251
|
+
for (const { name, addon } of platformPackages) {
|
|
252
|
+
if (!requiredSet.has(addon)) exclusions.push(name)
|
|
253
|
+
}
|
|
254
|
+
return exclusions
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Computes the list of installed @qvac addons that aren't in `required`,
|
|
259
|
+
* plus the per-platform prebuild packages of those excluded addons.
|
|
207
260
|
* Logs include/exclude decisions for each discovered package.
|
|
208
261
|
*/
|
|
209
262
|
function computeExclusions(required, projectDir) {
|
|
210
|
-
const
|
|
263
|
+
const { addons, platformPackages } = discoverQvacAddonPackages(projectDir)
|
|
211
264
|
|
|
212
|
-
if (
|
|
265
|
+
if (addons.length === 0) {
|
|
213
266
|
logger.warn('No @qvac addon packages discovered. Skipping addon exclusions.')
|
|
214
267
|
return []
|
|
215
268
|
}
|
|
216
269
|
|
|
217
270
|
const requiredSet = new Set(required)
|
|
218
271
|
const exclusions = []
|
|
219
|
-
for (const pkg of
|
|
272
|
+
for (const pkg of addons) {
|
|
220
273
|
if (requiredSet.has(pkg)) {
|
|
221
274
|
logger.info(`Including required addon: ${pkg}`)
|
|
222
275
|
} else {
|
|
@@ -224,6 +277,14 @@ function computeExclusions(required, projectDir) {
|
|
|
224
277
|
exclusions.push(pkg)
|
|
225
278
|
}
|
|
226
279
|
}
|
|
280
|
+
for (const { name, addon } of platformPackages) {
|
|
281
|
+
if (requiredSet.has(addon)) {
|
|
282
|
+
logger.info(`Including platform package ${name} (prebuilds for ${addon})`)
|
|
283
|
+
} else {
|
|
284
|
+
logger.info(`Excluding platform package ${name} (prebuilds for unused addon ${addon})`)
|
|
285
|
+
exclusions.push(name)
|
|
286
|
+
}
|
|
287
|
+
}
|
|
227
288
|
return exclusions
|
|
228
289
|
}
|
|
229
290
|
|
|
@@ -594,6 +655,15 @@ async function runBundleAndVerify(commands, projectDir, options) {
|
|
|
594
655
|
)
|
|
595
656
|
}
|
|
596
657
|
|
|
658
|
+
// Narrowed prebuild checks are otherwise invisible in the log.
|
|
659
|
+
const narrowed = verifyResult.addons.filter(
|
|
660
|
+
(addon) => Array.isArray(addon.linkedHosts) && addon.linkedHosts.length < hosts.length
|
|
661
|
+
)
|
|
662
|
+
for (const addon of narrowed) {
|
|
663
|
+
const where = addon.linkedHosts.length === 0 ? 'no host' : addon.linkedHosts.join(', ')
|
|
664
|
+
logger.info(`verifyBundle: ${addon.name}@${addon.version} is linked on ${where}`)
|
|
665
|
+
}
|
|
666
|
+
|
|
597
667
|
return bundleResult
|
|
598
668
|
}
|
|
599
669
|
|
|
@@ -772,6 +842,8 @@ module.exports.QvacForgePluginError = QvacForgePluginError
|
|
|
772
842
|
// Internal helpers exposed for unit tests. Not part of the stable API.
|
|
773
843
|
module.exports.createIgnore = createIgnore
|
|
774
844
|
module.exports.diffAddons = diffAddons
|
|
845
|
+
module.exports.diffPlatformPackages = diffPlatformPackages
|
|
846
|
+
module.exports.readPlatformPackageAddon = readPlatformPackageAddon
|
|
775
847
|
module.exports.detectTargetHosts = detectTargetHosts
|
|
776
848
|
module.exports.resolveHosts = resolveHosts
|
|
777
849
|
module.exports.runBundleAndVerify = runBundleAndVerify
|
|
@@ -5,12 +5,15 @@
|
|
|
5
5
|
* Otherwise, falls back to linking all installed addons.
|
|
6
6
|
*
|
|
7
7
|
* This file is copied over react-native-bare-kit/android/link.mjs
|
|
8
|
-
* by withMobileBundle.ts during expo prebuild.
|
|
8
|
+
* by withMobileBundle.ts during expo prebuild. The resolver it imports is
|
|
9
|
+
* compiled from qvac-platform-addons.ts and installed beside this file as
|
|
10
|
+
* qvac-platform-addons.mjs.
|
|
9
11
|
*/
|
|
10
12
|
import fs from 'fs'
|
|
11
13
|
import path from 'path'
|
|
12
14
|
import { fileURLToPath } from 'url'
|
|
13
15
|
import link from 'bare-link'
|
|
16
|
+
import { resolvePlatformAddonRoots } from './qvac-platform-addons.mjs'
|
|
14
17
|
|
|
15
18
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
16
19
|
const projectRoot = path.join(__dirname, '..', '..', '..')
|
|
@@ -24,10 +27,12 @@ if (fs.existsSync(addonsDir)) {
|
|
|
24
27
|
const manifestPath = path.join(projectRoot, 'qvac', 'addons.manifest.json')
|
|
25
28
|
|
|
26
29
|
let pkg = null
|
|
30
|
+
let addonNames = []
|
|
27
31
|
if (fs.existsSync(manifestPath)) {
|
|
28
32
|
try {
|
|
29
33
|
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'))
|
|
30
34
|
const addons = Array.isArray(manifest.addons) ? manifest.addons : []
|
|
35
|
+
addonNames = addons
|
|
31
36
|
|
|
32
37
|
if (addons.length > 0) {
|
|
33
38
|
console.log(`[QVAC] Using addons manifest (${addons.length} addons): ${addons.join(', ')}`)
|
|
@@ -46,13 +51,17 @@ if (fs.existsSync(manifestPath)) {
|
|
|
46
51
|
console.log('[QVAC] No addons manifest found, linking all addons')
|
|
47
52
|
}
|
|
48
53
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
hosts: ['android-arm64', 'android-arm', 'android-ia32', 'android-x64'],
|
|
53
|
-
out: addonsDir
|
|
54
|
-
},
|
|
55
|
-
pkg
|
|
56
|
-
)) {
|
|
54
|
+
const hosts = ['android-arm64', 'android-arm', 'android-ia32', 'android-x64']
|
|
55
|
+
|
|
56
|
+
for await (const resource of link(projectRoot, { hosts, out: addonsDir }, pkg)) {
|
|
57
57
|
console.log('Wrote', resource)
|
|
58
58
|
}
|
|
59
|
+
|
|
60
|
+
// Split addons keep their binaries in a per-platform package; bare-link only
|
|
61
|
+
// reaches the meta, which has shipped no prebuilds/ since the split.
|
|
62
|
+
for (const addon of resolvePlatformAddonRoots(projectRoot, addonNames, 'android')) {
|
|
63
|
+
console.log(`[QVAC] Linking split addon ${addon.pkg.name} from its platform package`)
|
|
64
|
+
for await (const resource of link(addon.dir, { hosts, out: addonsDir }, addon.pkg)) {
|
|
65
|
+
console.log('Wrote', resource)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -5,12 +5,15 @@
|
|
|
5
5
|
* Otherwise, falls back to linking all installed addons.
|
|
6
6
|
*
|
|
7
7
|
* This file is copied over react-native-bare-kit/ios/link.mjs
|
|
8
|
-
* by withMobileBundle.ts during expo prebuild.
|
|
8
|
+
* by withMobileBundle.ts during expo prebuild. The resolver it imports is
|
|
9
|
+
* compiled from qvac-platform-addons.ts and installed beside this file as
|
|
10
|
+
* qvac-platform-addons.mjs.
|
|
9
11
|
*/
|
|
10
12
|
import fs from 'fs'
|
|
11
13
|
import path from 'path'
|
|
12
14
|
import { fileURLToPath } from 'url'
|
|
13
15
|
import link from 'bare-link'
|
|
16
|
+
import { resolvePlatformAddonRoots } from './qvac-platform-addons.mjs'
|
|
14
17
|
|
|
15
18
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
16
19
|
const projectRoot = path.join(__dirname, '..', '..', '..')
|
|
@@ -24,10 +27,12 @@ if (fs.existsSync(addonsDir)) {
|
|
|
24
27
|
const manifestPath = path.join(projectRoot, 'qvac', 'addons.manifest.json')
|
|
25
28
|
|
|
26
29
|
let pkg = null
|
|
30
|
+
let addonNames = []
|
|
27
31
|
if (fs.existsSync(manifestPath)) {
|
|
28
32
|
try {
|
|
29
33
|
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'))
|
|
30
34
|
const addons = Array.isArray(manifest.addons) ? manifest.addons : []
|
|
35
|
+
addonNames = addons
|
|
31
36
|
|
|
32
37
|
if (addons.length > 0) {
|
|
33
38
|
console.log(`[QVAC] Using addons manifest (${addons.length} addons): ${addons.join(', ')}`)
|
|
@@ -46,13 +51,17 @@ if (fs.existsSync(manifestPath)) {
|
|
|
46
51
|
console.log('[QVAC] No addons manifest found, linking all addons')
|
|
47
52
|
}
|
|
48
53
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
hosts: ['ios-arm64', 'ios-arm64-simulator', 'ios-x64-simulator'],
|
|
53
|
-
out: addonsDir
|
|
54
|
-
},
|
|
55
|
-
pkg
|
|
56
|
-
)) {
|
|
54
|
+
const hosts = ['ios-arm64', 'ios-arm64-simulator', 'ios-x64-simulator']
|
|
55
|
+
|
|
56
|
+
for await (const resource of link(projectRoot, { hosts, out: addonsDir }, pkg)) {
|
|
57
57
|
console.log('Wrote', resource)
|
|
58
58
|
}
|
|
59
|
+
|
|
60
|
+
// Split addons keep their binaries in a per-platform package; bare-link only
|
|
61
|
+
// reaches the meta, which has shipped no prebuilds/ since the split.
|
|
62
|
+
for (const addon of resolvePlatformAddonRoots(projectRoot, addonNames, 'ios')) {
|
|
63
|
+
console.log(`[QVAC] Linking split addon ${addon.pkg.name} from its platform package`)
|
|
64
|
+
for await (const resource of link(addon.dir, { hosts, out: addonsDir }, addon.pkg)) {
|
|
65
|
+
console.log('Wrote', resource)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
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
|
+
|
|
12
|
+
export const HOST_ADDON_IMPORT = '#host-addon'
|
|
13
|
+
|
|
14
|
+
const DEFAULT_ANDROID_CPU = 'arm64'
|
|
15
|
+
|
|
16
|
+
export interface PlatformAddonRoot {
|
|
17
|
+
dir: string
|
|
18
|
+
pkg: { name?: string; version?: string; addon?: boolean }
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function resolvePlatformAddonRoots(
|
|
22
|
+
projectRoot: string,
|
|
23
|
+
addonNames: string[],
|
|
24
|
+
platform: string
|
|
25
|
+
): PlatformAddonRoot[] {
|
|
26
|
+
const roots: PlatformAddonRoot[] = []
|
|
27
|
+
for (const name of addonNames) {
|
|
28
|
+
const root = resolvePlatformAddonRoot(projectRoot, name, platform)
|
|
29
|
+
if (root !== null) roots.push(root)
|
|
30
|
+
}
|
|
31
|
+
return roots
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function resolvePlatformAddonRoot(
|
|
35
|
+
projectRoot: string,
|
|
36
|
+
metaName: string,
|
|
37
|
+
platform: string
|
|
38
|
+
): PlatformAddonRoot | null {
|
|
39
|
+
const metaManifest = readManifest(packageDir(projectRoot, metaName))
|
|
40
|
+
if (metaManifest === null) return null
|
|
41
|
+
|
|
42
|
+
const platformPackage = resolveAddonPlatformPackage(
|
|
43
|
+
metaName,
|
|
44
|
+
metaManifest.imports?.[HOST_ADDON_IMPORT],
|
|
45
|
+
platform
|
|
46
|
+
)
|
|
47
|
+
if (platformPackage === null) return null
|
|
48
|
+
|
|
49
|
+
const addonDir = path.join(packageDir(projectRoot, platformPackage), 'addon')
|
|
50
|
+
const addonManifest = readManifest(addonDir)
|
|
51
|
+
if (addonManifest === null || addonManifest.addon !== true) return null
|
|
52
|
+
if (!fs.existsSync(path.join(addonDir, 'prebuilds'))) return null
|
|
53
|
+
|
|
54
|
+
return { dir: addonDir, pkg: addonManifest }
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function packageDir(projectRoot: string, packageName: string): string {
|
|
58
|
+
return path.join(projectRoot, 'node_modules', ...packageName.split('/'))
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
interface AddonManifest {
|
|
62
|
+
name?: string
|
|
63
|
+
version?: string
|
|
64
|
+
addon?: boolean
|
|
65
|
+
imports?: Record<string, unknown>
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function readManifest(dir: string): AddonManifest | null {
|
|
69
|
+
try {
|
|
70
|
+
const parsed: unknown = JSON.parse(fs.readFileSync(path.join(dir, 'package.json'), 'utf8'))
|
|
71
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) return null
|
|
72
|
+
return parsed as AddonManifest
|
|
73
|
+
} catch {
|
|
74
|
+
return null
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Package `#host-addon` names for `host`, if that name belongs to `metaName`.
|
|
80
|
+
* `host` is a Bare host (`android-arm64`, `ios-arm64-simulator`, `darwin-arm64`)
|
|
81
|
+
* or a mobile build target (`android`, `ios`).
|
|
82
|
+
*/
|
|
83
|
+
export function resolveAddonPlatformPackage(
|
|
84
|
+
metaName: string,
|
|
85
|
+
hostAddon: unknown,
|
|
86
|
+
host: string
|
|
87
|
+
): string | null {
|
|
88
|
+
const name = resolvePlatformPackageName(hostAddon, host)
|
|
89
|
+
if (name === null) return null
|
|
90
|
+
if (!name.startsWith(`${metaName}-`)) return null
|
|
91
|
+
return name
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* First package name a `#host-addon` map points at for `host`.
|
|
96
|
+
* Android nests the package under its architecture; iOS is a flat list.
|
|
97
|
+
* A platform-only `android` target uses arm64, matching the current mobile host.
|
|
98
|
+
*/
|
|
99
|
+
export function resolvePlatformPackageName(hostAddon: unknown, host: string): string | null {
|
|
100
|
+
if (typeof host !== 'string' || host.length === 0) return null
|
|
101
|
+
|
|
102
|
+
const separator = host.indexOf('-')
|
|
103
|
+
const platform = separator === -1 ? host : host.slice(0, separator)
|
|
104
|
+
const cpu = separator === -1 ? '' : host.slice(separator + 1)
|
|
105
|
+
|
|
106
|
+
const branch = readBranch(hostAddon, platform)
|
|
107
|
+
const direct = packageName(branch)
|
|
108
|
+
if (direct !== null) return direct
|
|
109
|
+
|
|
110
|
+
const cpuKey = cpu.length > 0 ? cpu : DEFAULT_ANDROID_CPU
|
|
111
|
+
return packageName(readBranch(branch, cpuKey))
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function packageName(candidate: unknown): string | null {
|
|
115
|
+
const name = Array.isArray(candidate) ? candidate[0] : candidate
|
|
116
|
+
if (typeof name !== 'string' || name.startsWith('.')) return null
|
|
117
|
+
return name
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function readBranch(value: unknown, key: string): unknown {
|
|
121
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value)) return undefined
|
|
122
|
+
if (!Object.hasOwn(value, key)) return undefined
|
|
123
|
+
return (value as Record<string, unknown>)[key]
|
|
124
|
+
}
|