@qvac/sdk 0.20.1 → 0.21.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/NOTICE +130 -62
  2. package/README.md +39 -5
  3. package/dist/examples/advisory-model-fit.d.ts +17 -16
  4. package/dist/examples/advisory-model-fit.js +18 -18
  5. package/dist/examples/asr/parakeet-indic-conformer-filesystem.js +3 -3
  6. package/dist/examples/asr/parakeet-unified-coreml.d.ts +2 -0
  7. package/dist/examples/asr/parakeet-unified-coreml.js +113 -0
  8. package/dist/examples/assess-model-fit.js +9 -4
  9. package/dist/examples/model-fit-ui/server.js +5 -2
  10. package/dist/examples/rpc-inference.d.ts +2 -0
  11. package/dist/examples/rpc-inference.js +51 -0
  12. package/dist/examples/rpc-server.d.ts +2 -0
  13. package/dist/examples/rpc-server.js +32 -0
  14. package/dist/examples/tools/llamacpp-deferred-tools.d.ts +2 -0
  15. package/dist/examples/tools/llamacpp-deferred-tools.js +77 -0
  16. package/dist/examples/tts/audio8.js +4 -0
  17. package/dist/examples/tts/moss-dialogue.d.ts +2 -0
  18. package/dist/examples/tts/moss-dialogue.js +63 -0
  19. package/dist/examples/tts/moss.d.ts +2 -0
  20. package/dist/examples/tts/moss.js +71 -0
  21. package/dist/scripts/contract/build-contract.d.ts +2 -1
  22. package/dist/scripts/contract/build-contract.js +4 -1
  23. package/dist/scripts/contract/build-numeric-constants.d.ts +15 -0
  24. package/dist/scripts/contract/build-numeric-constants.js +13 -0
  25. package/dist/scripts/contract/method-shapes.d.ts +3 -0
  26. package/dist/scripts/contract/method-shapes.js +3 -0
  27. package/dist/scripts/enforce-inference-versions.d.ts +18 -0
  28. package/dist/scripts/enforce-inference-versions.js +158 -0
  29. package/dist/scripts/generate-bare-kit-runtimes.d.ts +2 -0
  30. package/dist/scripts/generate-bare-kit-runtimes.js +106 -0
  31. package/dist/src/client/api/assess-model-fit.d.ts +5 -4
  32. package/dist/src/client/api/assess-model-fit.js +24 -7
  33. package/dist/src/client/api/bci-transcribe.d.ts +4 -1
  34. package/dist/src/client/api/get-system-resources.d.ts +8 -0
  35. package/dist/src/client/api/get-system-resources.js +8 -0
  36. package/dist/src/client/api/index.d.ts +1 -0
  37. package/dist/src/client/api/index.js +1 -0
  38. package/dist/src/client/api/rpc-server.d.ts +20 -0
  39. package/dist/src/client/api/rpc-server.js +45 -0
  40. package/dist/src/client/api/text-to-speech.d.ts +9 -0
  41. package/dist/src/client/api/text-to-speech.js +9 -0
  42. package/dist/src/client/api/transcribe.d.ts +21 -15
  43. package/dist/src/client/api/transcribe.js +65 -20
  44. package/dist/src/client/rpc/rpc-error.js +11 -7
  45. package/dist/src/commands/bundle/entry-gen.d.ts +2 -2
  46. package/dist/src/commands/bundle/entry-gen.js +8 -4
  47. package/dist/src/commands/bundle/index.d.ts +20 -0
  48. package/dist/src/commands/bundle/index.js +88 -12
  49. package/dist/src/commands/bundle/plugins.js +1 -1
  50. package/dist/src/commands/command-logger.d.ts +6 -0
  51. package/dist/src/commands/command-logger.js +12 -0
  52. package/dist/src/commands/host-prebuilds/index.d.ts +71 -0
  53. package/dist/src/commands/host-prebuilds/index.js +227 -0
  54. package/dist/src/commands/host-prebuilds/package-manager.d.ts +54 -0
  55. package/dist/src/commands/host-prebuilds/package-manager.js +278 -0
  56. package/dist/src/commands/index.d.ts +10 -1
  57. package/dist/src/commands/index.js +6 -0
  58. package/dist/src/commands/verify/abi.d.ts +22 -2
  59. package/dist/src/commands/verify/abi.js +49 -27
  60. package/dist/src/commands/verify/addon-source.d.ts +22 -0
  61. package/dist/src/commands/verify/addon-source.js +69 -3
  62. package/dist/src/commands/verify/bare-kit-runtime.d.ts +38 -0
  63. package/dist/src/commands/verify/bare-kit-runtime.js +201 -0
  64. package/dist/src/commands/verify/bare-kit-runtimes.d.ts +12 -0
  65. package/dist/src/commands/verify/bare-kit-runtimes.js +108 -0
  66. package/dist/src/commands/verify/bundle-source.js +16 -15
  67. package/dist/src/commands/verify/engines-advice.d.ts +73 -0
  68. package/dist/src/commands/verify/engines-advice.js +271 -0
  69. package/dist/src/commands/verify/index.d.ts +22 -0
  70. package/dist/src/commands/verify/index.js +151 -13
  71. package/dist/src/commands/verify/node-modules-source.js +43 -39
  72. package/dist/src/commands/verify/prebuilds.d.ts +19 -0
  73. package/dist/src/commands/verify/prebuilds.js +52 -13
  74. package/dist/src/expo/plugins/index.d.ts +2 -0
  75. package/dist/src/expo/plugins/withMobileBundle.d.ts +15 -5
  76. package/dist/src/expo/plugins/withMobileBundle.js +68 -42
  77. package/dist/src/expo/plugins/withQvacSDK.d.ts +7 -1
  78. package/dist/src/expo/plugins/withQvacSDK.js +6 -3
  79. package/dist/src/index.d.ts +5 -2
  80. package/dist/src/index.js +4 -2
  81. package/dist/src/schemas/sdk-errors-client.d.ts +2 -0
  82. package/dist/src/schemas/sdk-errors-client.js +16 -0
  83. package/dist/src/schemas/sdk-errors-server.d.ts +1 -0
  84. package/dist/src/schemas/sdk-errors-server.js +5 -0
  85. package/dist/src/utils/errors-client.d.ts +15 -0
  86. package/dist/src/utils/errors-client.js +26 -0
  87. package/dist/src/utils/errors-server.d.ts +1 -0
  88. package/dist/src/utils/errors-server.js +1 -0
  89. package/dist/src/utils/mcp-adapter.js +7 -1
  90. package/dist/src/utils/tool-helpers.d.ts +4 -0
  91. package/dist/src/utils/tool-helpers.js +2 -0
  92. package/dist/src/worker/ggml-rpc-server-provider.d.ts +2 -0
  93. package/dist/src/worker/ggml-rpc-server-provider.js +1 -0
  94. package/dist/src/worker/rpc-server-provider.d.ts +3 -0
  95. package/dist/src/worker/rpc-server-provider.js +1 -0
  96. package/package.json +30 -25
  97. package/dist/scripts/enforce-inference-peer-dependencies.d.ts +0 -2
  98. package/dist/scripts/enforce-inference-peer-dependencies.js +0 -39
  99. package/dist/src/deprecate.d.ts +0 -2
  100. package/dist/src/deprecate.js +0 -9
  101. package/dist/src/worker/legacy-worker-entry.d.ts +0 -2
  102. package/dist/src/worker/legacy-worker-entry.js +0 -3
  103. package/dist/src/worker/worker-core.d.ts +0 -7
  104. package/dist/src/worker/worker-core.js +0 -8
@@ -0,0 +1,227 @@
1
+ import { promises as fsp } from 'node:fs';
2
+ import path from 'node:path';
3
+ import semver from 'semver';
4
+ import { createCommandLogger } from '../command-logger.js';
5
+ import { readAddonPackageJson } from '../verify/addon-source.js';
6
+ import { checkPrebuilds, isMobileHost } from '../verify/prebuilds.js';
7
+ import { checkPackageManagerSupport, detectPackageManager, installArgs, isPackageManagerName, isWorkspaceMember, runPackageManager } from './package-manager.js';
8
+ import { HostPrebuildsInstallFailedError, HostPrebuildsInstallRefusedError } from '../../utils/errors-client.js';
9
+ /** npm's package-name grammar. On Windows the names pass through a shell. */
10
+ const PACKAGE_NAME = /^(@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/;
11
+ /**
12
+ * Installs the per-platform prebuild packages that addons need for mobile
13
+ * hosts and that the project does not have yet.
14
+ *
15
+ * A split addon names one platform package per host in the `#host-addon` map
16
+ * of its package.json (`<addon>-ios` covers every iOS host). Desktop platform
17
+ * packages install as the addon's os/cpu-filtered optionalDependencies, but a
18
+ * build host never reports a mobile os, so mobile ones have to be declared.
19
+ * This declares the missing ones in package.json, pinned to each addon's
20
+ * exact version, using the project's own package manager.
21
+ *
22
+ * A host whose prebuild already resolves is skipped: a local
23
+ * `prebuilds/<host>` in the addon (source builds) or its platform package at
24
+ * the addon's version. Throws `HostPrebuildsInstallRefusedError` when no
25
+ * supported package manager (npm 7+, pnpm, bun, Yarn Berry) is found, and
26
+ * `HostPrebuildsInstallFailedError` when the install does not produce the
27
+ * packages.
28
+ */
29
+ export async function ensureHostPrebuilds(options) {
30
+ const logger = createCommandLogger(options);
31
+ const addons = await resolveAddons(options.projectRoot, options.addons, logger);
32
+ return installMissingHostPrebuilds({
33
+ projectRoot: options.projectRoot,
34
+ hosts: options.hosts,
35
+ addons,
36
+ packageManager: options.packageManager,
37
+ quiet: options.quiet === true,
38
+ logger
39
+ });
40
+ }
41
+ /** `ensureHostPrebuilds` for addons the caller has already located. */
42
+ export async function installMissingHostPrebuilds(options) {
43
+ const { projectRoot, hosts, addons, quiet, logger } = options;
44
+ const missing = await findMissingHostPrebuilds(addons, hosts);
45
+ if (missing.length === 0)
46
+ return { installed: [], packageManager: null };
47
+ const dependencies = toDependencies(missing);
48
+ const manager = await choosePackageManager(projectRoot, options.packageManager, dependencies);
49
+ const args = installArgs(manager, missing.map((pkg) => `${pkg.name}@${pkg.version}`));
50
+ const command = `${manager} ${args.join(' ')}`;
51
+ logger.info(`\n📦 Installing addon platform packages with ${manager}:`);
52
+ for (const pkg of missing) {
53
+ logger.info(` ${pkg.name}@${pkg.version} (${pkg.hosts.join(', ')})`);
54
+ }
55
+ const result = await runPackageManager(manager, args, { cwd: projectRoot, quiet });
56
+ if (result.error !== undefined || result.code !== 0) {
57
+ const outcome = result.error?.message ?? `exited with code ${result.code ?? 'unknown'}`;
58
+ const stderr = result.stderr.trim();
59
+ throw new HostPrebuildsInstallFailedError(`\`${command}\` ${outcome}${stderr.length > 0 ? `\n\n${stderr}` : ''}`, dependencies, result.error);
60
+ }
61
+ const stillMissing = await findMissingHostPrebuilds(addons, hosts);
62
+ if (stillMissing.length > 0) {
63
+ const names = stillMissing.map((pkg) => pkg.name).join(', ');
64
+ const pnpHint = manager === 'yarn'
65
+ ? "; Yarn Plug'n'Play installs no node_modules, so set `nodeLinker: node-modules`"
66
+ : '';
67
+ throw new HostPrebuildsInstallFailedError(`\`${command}\` succeeded, but ${names} is still not installed under node_modules${pnpHint}`, toDependencies(stillMissing));
68
+ }
69
+ logger.info(` Installed ${missing.length} platform package${missing.length === 1 ? '' : 's'}`);
70
+ return { installed: missing, packageManager: manager };
71
+ }
72
+ /**
73
+ * The platform packages `addons` still need for the mobile hosts in `hosts`,
74
+ * one entry per package. Throws when two installed versions of one addon need
75
+ * the same platform package, since only one version of it can be installed.
76
+ */
77
+ export async function findMissingHostPrebuilds(addons, hosts) {
78
+ const mobileHosts = hosts.filter(isMobileHost);
79
+ const packages = new Map();
80
+ if (mobileHosts.length === 0)
81
+ return [];
82
+ for (const addon of addons) {
83
+ for (const issue of await checkPrebuilds({ addon, hosts: mobileHosts })) {
84
+ const pin = issue.platformPackage;
85
+ if (pin === undefined)
86
+ continue;
87
+ const known = packages.get(pin.name);
88
+ if (known === undefined) {
89
+ packages.set(pin.name, { ...pin, addon: addon.name, hosts: [issue.host] });
90
+ continue;
91
+ }
92
+ if (known.version !== pin.version) {
93
+ throw new HostPrebuildsInstallRefusedError(`${addon.name} is installed at both ${known.version} and ${pin.version}, but ` +
94
+ `${pin.name} can be installed at only one version. ` +
95
+ `Deduplicate ${addon.name} to a single version and try again`, {});
96
+ }
97
+ if (!known.hosts.includes(issue.host))
98
+ known.hosts.push(issue.host);
99
+ }
100
+ }
101
+ const missing = [...packages.values()].sort((a, b) => a.name.localeCompare(b.name));
102
+ for (const pkg of missing) {
103
+ if (!PACKAGE_NAME.test(pkg.name) || semver.valid(pkg.version) === null) {
104
+ throw new HostPrebuildsInstallRefusedError(`${pkg.addon} names "${pkg.name}@${pkg.version}", which is not a valid package name and version`, {});
105
+ }
106
+ }
107
+ return missing;
108
+ }
109
+ async function choosePackageManager(projectRoot, requested, dependencies) {
110
+ let manager;
111
+ let source = '';
112
+ if (requested !== undefined) {
113
+ if (!isPackageManagerName(requested)) {
114
+ throw new HostPrebuildsInstallRefusedError(`packageManager "${String(requested)}" is not npm, pnpm, bun, or yarn`, dependencies);
115
+ }
116
+ manager = requested;
117
+ }
118
+ else {
119
+ const detected = await detectPackageManager(projectRoot);
120
+ if (detected.manager === null) {
121
+ throw new HostPrebuildsInstallRefusedError(`could not tell which package manager the project uses (${detected.reason})`, dependencies);
122
+ }
123
+ manager = detected.manager;
124
+ source = ` (detected from ${detected.source})`;
125
+ }
126
+ const unsupported = await checkPackageManagerSupport(manager, projectRoot);
127
+ if (unsupported !== null) {
128
+ throw new HostPrebuildsInstallRefusedError(`${unsupported}${source}`, dependencies);
129
+ }
130
+ return manager;
131
+ }
132
+ /**
133
+ * The installed addons named in `names`, or every installed addon when
134
+ * `names` is omitted. For each name the copy in the nearest modules directory
135
+ * wins, as it does for Node's resolver.
136
+ */
137
+ async function resolveAddons(projectRoot, names, logger) {
138
+ const moduleDirs = await reachableModuleDirs(projectRoot);
139
+ const wanted = names === undefined ? null : new Set(names);
140
+ const found = new Map();
141
+ for (const modulesDir of moduleDirs) {
142
+ const candidates = wanted === null ? await listPackageNames(modulesDir) : [...wanted];
143
+ for (const name of candidates) {
144
+ if (found.has(name))
145
+ continue;
146
+ const result = await readAddonPackageJson({
147
+ packageJsonPath: path.join(modulesDir, ...name.split('/'), 'package.json'),
148
+ expectedName: name
149
+ });
150
+ if (result.addon !== undefined)
151
+ found.set(name, result.addon);
152
+ }
153
+ }
154
+ for (const name of wanted ?? []) {
155
+ if (!found.has(name))
156
+ logger.warn(`${name} is not an installed native addon; skipping it`);
157
+ }
158
+ return [...found.values()];
159
+ }
160
+ /**
161
+ * Every modules directory a package installed for `projectRoot` can sit in,
162
+ * nearest first: the project's `node_modules` and that of each workspace root
163
+ * above it that lists the project (npm, Yarn, and bun hoist workspace
164
+ * dependencies to the root), plus the views pnpm (`.pnpm/node_modules`) and
165
+ * bun's isolated layout (`.bun/node_modules`) keep of the packages that are not
166
+ * direct dependencies. Any other enclosing project installed its node_modules
167
+ * for itself, so its addons do not belong to this one.
168
+ */
169
+ async function reachableModuleDirs(projectRoot) {
170
+ const start = path.resolve(projectRoot);
171
+ const dirs = [];
172
+ let dir = start;
173
+ for (;;) {
174
+ if (dir === start || (await isWorkspaceMember(dir, start))) {
175
+ const nodeModules = path.join(dir, 'node_modules');
176
+ for (const candidate of [
177
+ nodeModules,
178
+ path.join(nodeModules, '.pnpm', 'node_modules'),
179
+ path.join(nodeModules, '.bun', 'node_modules')
180
+ ]) {
181
+ if (await isDirectory(candidate))
182
+ dirs.push(candidate);
183
+ }
184
+ }
185
+ const parent = path.dirname(dir);
186
+ if (parent === dir)
187
+ return dirs;
188
+ dir = parent;
189
+ }
190
+ }
191
+ async function listPackageNames(modulesDir) {
192
+ const names = [];
193
+ for (const entry of await readDirNames(modulesDir)) {
194
+ if (entry.startsWith('.'))
195
+ continue;
196
+ if (!entry.startsWith('@')) {
197
+ names.push(entry);
198
+ continue;
199
+ }
200
+ for (const scoped of await readDirNames(path.join(modulesDir, entry))) {
201
+ if (!scoped.startsWith('.'))
202
+ names.push(`${entry}/${scoped}`);
203
+ }
204
+ }
205
+ return names;
206
+ }
207
+ async function readDirNames(dir) {
208
+ try {
209
+ return (await fsp.readdir(dir, { withFileTypes: true }))
210
+ .filter((entry) => entry.isDirectory() || entry.isSymbolicLink())
211
+ .map((entry) => entry.name);
212
+ }
213
+ catch {
214
+ return [];
215
+ }
216
+ }
217
+ async function isDirectory(dir) {
218
+ try {
219
+ return (await fsp.stat(dir)).isDirectory();
220
+ }
221
+ catch {
222
+ return false;
223
+ }
224
+ }
225
+ function toDependencies(packages) {
226
+ return Object.fromEntries(packages.map((pkg) => [pkg.name, pkg.version]));
227
+ }
@@ -0,0 +1,54 @@
1
+ export type PackageManagerName = 'npm' | 'pnpm' | 'bun' | 'yarn';
2
+ /**
3
+ * The package manager that owns `projectRoot`, from the project's own
4
+ * `packageManager` field, lockfile, or node_modules install files. A project
5
+ * with none of those is a workspace member only if the nearest directory above
6
+ * it that has them is a workspace root listing it; any other enclosing project
7
+ * belongs to someone else, and its package manager is not this one's.
8
+ */
9
+ export declare function detectPackageManager(projectRoot: string): Promise<{
10
+ manager: null;
11
+ reason: string;
12
+ source?: never;
13
+ } | {
14
+ manager: PackageManagerName;
15
+ source: string;
16
+ reason?: never;
17
+ }>;
18
+ export declare function isPackageManagerName(value: string): value is PackageManagerName;
19
+ /**
20
+ * Whether the workspace globs declared at `rootDir` (package.json `workspaces`
21
+ * for npm, Yarn, and bun; `pnpm-workspace.yaml` for pnpm) include `memberDir`.
22
+ */
23
+ export declare function isWorkspaceMember(rootDir: string, memberDir: string): Promise<boolean>;
24
+ /**
25
+ * The `packages` list of a pnpm-workspace.yaml, in block (`- 'apps/*'`) or
26
+ * flow (`packages: ['apps/*']`) form. The SDK has no YAML parser, and the
27
+ * file's other keys are irrelevant here.
28
+ */
29
+ export declare function parsePnpmWorkspacePackages(yaml: string): string[];
30
+ /**
31
+ * Why `manager` cannot install in `cwd`, or null when it can. The supported
32
+ * set matches the installers the split addons document: npm 7+, pnpm, bun,
33
+ * and Yarn Berry.
34
+ */
35
+ export declare function checkPackageManagerSupport(manager: PackageManagerName, cwd: string): Promise<string | null>;
36
+ /** Arguments that add `specs` as exact-version dependencies of the package in cwd. */
37
+ export declare function installArgs(manager: PackageManagerName, specs: string[]): string[];
38
+ export interface RunPackageManagerResult {
39
+ code: number | null;
40
+ stdout: string;
41
+ stderr: string;
42
+ error?: Error;
43
+ }
44
+ /**
45
+ * Quiet runs capture stdout and stderr; other runs stream them to this
46
+ * process. Windows resolves the package managers' `.cmd` shims through the
47
+ * shell, which does not escape arguments, so callers must pass only
48
+ * validated ones.
49
+ */
50
+ export declare function runPackageManager(manager: PackageManagerName, args: string[], options: {
51
+ cwd: string;
52
+ quiet: boolean;
53
+ }): Promise<RunPackageManagerResult>;
54
+ //# sourceMappingURL=package-manager.d.ts.map
@@ -0,0 +1,278 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { promises as fsp } from 'node:fs';
3
+ import path from 'node:path';
4
+ import semver from 'semver';
5
+ const PACKAGE_MANAGERS = ['npm', 'pnpm', 'bun', 'yarn'];
6
+ const LOCKFILES = [
7
+ ['package-lock.json', 'npm'],
8
+ ['npm-shrinkwrap.json', 'npm'],
9
+ ['pnpm-lock.yaml', 'pnpm'],
10
+ ['bun.lock', 'bun'],
11
+ ['bun.lockb', 'bun'],
12
+ ['yarn.lock', 'yarn']
13
+ ];
14
+ /**
15
+ * Files a package manager writes into the node_modules it installs. They name
16
+ * the tool that built the tree even where the project keeps no lockfile. Bun's
17
+ * default layout writes none.
18
+ */
19
+ const INSTALL_MARKERS = [
20
+ ['.package-lock.json', 'npm'],
21
+ ['.modules.yaml', 'pnpm'],
22
+ ['.yarn-state.yml', 'yarn'],
23
+ ['.yarn-integrity', 'yarn']
24
+ ];
25
+ /**
26
+ * The package manager that owns `projectRoot`, from the project's own
27
+ * `packageManager` field, lockfile, or node_modules install files. A project
28
+ * with none of those is a workspace member only if the nearest directory above
29
+ * it that has them is a workspace root listing it; any other enclosing project
30
+ * belongs to someone else, and its package manager is not this one's.
31
+ */
32
+ export async function detectPackageManager(projectRoot) {
33
+ const start = path.resolve(projectRoot);
34
+ const own = await readInstallEvidence(start);
35
+ if (hasEvidence(own))
36
+ return decidePackageManager(start, own);
37
+ let dir = start;
38
+ for (;;) {
39
+ const parent = path.dirname(dir);
40
+ if (parent === dir) {
41
+ return {
42
+ manager: null,
43
+ reason: `no lockfile or packageManager field found in ${start} or its parent directories`
44
+ };
45
+ }
46
+ dir = parent;
47
+ const evidence = await readInstallEvidence(dir);
48
+ if (!hasEvidence(evidence))
49
+ continue;
50
+ if (!(await isWorkspaceMember(dir, start))) {
51
+ return {
52
+ manager: null,
53
+ reason: `${start} has no lockfile of its own, and ${dir}, which has one, ` +
54
+ 'is not a workspace root that lists it'
55
+ };
56
+ }
57
+ return decidePackageManager(dir, evidence);
58
+ }
59
+ }
60
+ export function isPackageManagerName(value) {
61
+ return PACKAGE_MANAGERS.includes(value);
62
+ }
63
+ function hasEvidence(evidence) {
64
+ return evidence.declared !== null || evidence.found.length > 0;
65
+ }
66
+ /** A declared `packageManager` decides; otherwise every lockfile and install file must agree. */
67
+ function decidePackageManager(dir, evidence) {
68
+ const { declared, found } = evidence;
69
+ if (declared !== null) {
70
+ const name = declared.split('@')[0] ?? '';
71
+ if (!isPackageManagerName(name)) {
72
+ return {
73
+ manager: null,
74
+ reason: `package.json in ${dir} declares packageManager "${declared}", ` +
75
+ 'which is not npm, pnpm, bun, or Yarn'
76
+ };
77
+ }
78
+ return {
79
+ manager: name,
80
+ source: `packageManager "${declared}" in ${path.join(dir, 'package.json')}`
81
+ };
82
+ }
83
+ const managers = [...new Set(found.map((entry) => entry.manager))];
84
+ const [first] = found;
85
+ if (managers.length === 1 && first !== undefined) {
86
+ return { manager: first.manager, source: first.source };
87
+ }
88
+ return {
89
+ manager: null,
90
+ reason: `${dir} has install files from ${managers.join(', ')} ` +
91
+ `(${found.map((entry) => path.relative(dir, entry.source)).join(', ')}); ` +
92
+ 'set the packageManager field in package.json to pick one'
93
+ };
94
+ }
95
+ async function readInstallEvidence(dir) {
96
+ const found = [];
97
+ for (const [file, manager] of LOCKFILES) {
98
+ const source = path.join(dir, file);
99
+ if (await isFile(source))
100
+ found.push({ manager, source });
101
+ }
102
+ for (const [file, manager] of INSTALL_MARKERS) {
103
+ const source = path.join(dir, 'node_modules', file);
104
+ if (await isFile(source))
105
+ found.push({ manager, source });
106
+ }
107
+ const manifest = await readPackageJson(dir);
108
+ const field = manifest?.['packageManager'];
109
+ return { declared: typeof field === 'string' && field.length > 0 ? field : null, found };
110
+ }
111
+ async function isFile(filePath) {
112
+ try {
113
+ return (await fsp.stat(filePath)).isFile();
114
+ }
115
+ catch {
116
+ return false;
117
+ }
118
+ }
119
+ async function readPackageJson(dir) {
120
+ try {
121
+ const parsed = JSON.parse(await fsp.readFile(path.join(dir, 'package.json'), 'utf8'));
122
+ if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed))
123
+ return null;
124
+ return parsed;
125
+ }
126
+ catch {
127
+ return null;
128
+ }
129
+ }
130
+ /**
131
+ * Whether the workspace globs declared at `rootDir` (package.json `workspaces`
132
+ * for npm, Yarn, and bun; `pnpm-workspace.yaml` for pnpm) include `memberDir`.
133
+ */
134
+ export async function isWorkspaceMember(rootDir, memberDir) {
135
+ const relative = path.relative(rootDir, memberDir).split(path.sep).join('/');
136
+ let included = false;
137
+ for (const pattern of await readWorkspacePatterns(rootDir)) {
138
+ const negated = pattern.startsWith('!');
139
+ const glob = (negated ? pattern.slice(1) : pattern).replace(/^\.\//, '').replace(/\/+$/, '');
140
+ if (globToRegExp(glob).test(relative))
141
+ included = !negated;
142
+ }
143
+ return included;
144
+ }
145
+ async function readWorkspacePatterns(dir) {
146
+ const patterns = [];
147
+ const workspaces = (await readPackageJson(dir))?.['workspaces'];
148
+ const list = typeof workspaces === 'object' && workspaces !== null && !Array.isArray(workspaces)
149
+ ? workspaces.packages
150
+ : workspaces;
151
+ if (Array.isArray(list)) {
152
+ patterns.push(...list.filter((entry) => typeof entry === 'string'));
153
+ }
154
+ try {
155
+ const yaml = await fsp.readFile(path.join(dir, 'pnpm-workspace.yaml'), 'utf8');
156
+ patterns.push(...parsePnpmWorkspacePackages(yaml));
157
+ }
158
+ catch {
159
+ // Not a pnpm workspace.
160
+ }
161
+ return patterns;
162
+ }
163
+ /**
164
+ * The `packages` list of a pnpm-workspace.yaml, in block (`- 'apps/*'`) or
165
+ * flow (`packages: ['apps/*']`) form. The SDK has no YAML parser, and the
166
+ * file's other keys are irrelevant here.
167
+ */
168
+ export function parsePnpmWorkspacePackages(yaml) {
169
+ const patterns = [];
170
+ let inPackages = false;
171
+ for (const rawLine of yaml.split(/\r?\n/)) {
172
+ const line = rawLine.replace(/\s+#.*$/, '');
173
+ const flow = /^packages\s*:\s*\[(.*)\]\s*$/.exec(line);
174
+ if (flow !== null) {
175
+ patterns.push(...(flow[1] ?? '').split(',').map(unquote).filter(Boolean));
176
+ break;
177
+ }
178
+ if (/^packages\s*:\s*$/.test(line)) {
179
+ inPackages = true;
180
+ continue;
181
+ }
182
+ if (!inPackages)
183
+ continue;
184
+ if (/^\S/.test(line))
185
+ break;
186
+ const item = /^\s*-\s*(.+?)\s*$/.exec(line);
187
+ if (item !== null)
188
+ patterns.push(unquote(item[1] ?? ''));
189
+ }
190
+ return patterns;
191
+ }
192
+ function unquote(value) {
193
+ return value.trim().replace(/^(['"])(.*)\1$/, '$2');
194
+ }
195
+ /** `*` matches within one path segment, `**` across segments. */
196
+ function globToRegExp(glob) {
197
+ let source = '';
198
+ for (let i = 0; i < glob.length; i++) {
199
+ const char = glob[i] ?? '';
200
+ if (char === '*' && glob[i + 1] === '*') {
201
+ const slash = glob[i + 2] === '/';
202
+ source += slash ? '(?:.*/)?' : '.*';
203
+ i += slash ? 2 : 1;
204
+ }
205
+ else if (char === '*') {
206
+ source += '[^/]*';
207
+ }
208
+ else if (char === '?') {
209
+ source += '[^/]';
210
+ }
211
+ else {
212
+ source += char.replace(/[.+^${}()|[\]\\]/g, '\\$&');
213
+ }
214
+ }
215
+ return new RegExp(`^${source}$`);
216
+ }
217
+ /**
218
+ * Why `manager` cannot install in `cwd`, or null when it can. The supported
219
+ * set matches the installers the split addons document: npm 7+, pnpm, bun,
220
+ * and Yarn Berry.
221
+ */
222
+ export async function checkPackageManagerSupport(manager, cwd) {
223
+ const result = await runPackageManager(manager, ['--version'], { cwd, quiet: true });
224
+ if (result.error !== undefined || result.code !== 0) {
225
+ return `\`${manager} --version\` failed; is ${manager} installed and on PATH?`;
226
+ }
227
+ const version = semver.coerce(result.stdout.trim())?.version;
228
+ if (version === undefined)
229
+ return null;
230
+ if (manager === 'npm' && semver.lt(version, '7.0.0')) {
231
+ return `npm ${version} is too old; npm 7 or later is required`;
232
+ }
233
+ if (manager === 'yarn' && semver.lt(version, '2.0.0')) {
234
+ return `Yarn ${version} (Yarn Classic) is not supported; use Yarn Berry (2 or later)`;
235
+ }
236
+ return null;
237
+ }
238
+ /** Arguments that add `specs` as exact-version dependencies of the package in cwd. */
239
+ export function installArgs(manager, specs) {
240
+ switch (manager) {
241
+ case 'npm':
242
+ return ['install', '--save-exact', ...specs];
243
+ case 'pnpm':
244
+ return ['add', '--save-exact', ...specs];
245
+ case 'bun':
246
+ case 'yarn':
247
+ return ['add', '--exact', ...specs];
248
+ }
249
+ }
250
+ /**
251
+ * Quiet runs capture stdout and stderr; other runs stream them to this
252
+ * process. Windows resolves the package managers' `.cmd` shims through the
253
+ * shell, which does not escape arguments, so callers must pass only
254
+ * validated ones.
255
+ */
256
+ export function runPackageManager(manager, args, options) {
257
+ return new Promise((resolve) => {
258
+ let stdout = '';
259
+ let stderr = '';
260
+ const spawnOptions = {
261
+ cwd: options.cwd,
262
+ stdio: options.quiet ? ['ignore', 'pipe', 'pipe'] : 'inherit'
263
+ };
264
+ const proc = process.platform === 'win32'
265
+ ? spawn([manager, ...args].join(' '), { ...spawnOptions, shell: true })
266
+ : spawn(manager, args, spawnOptions);
267
+ proc.stdout?.setEncoding('utf8');
268
+ proc.stdout?.on('data', (chunk) => {
269
+ stdout += chunk;
270
+ });
271
+ proc.stderr?.setEncoding('utf8');
272
+ proc.stderr?.on('data', (chunk) => {
273
+ stderr += chunk;
274
+ });
275
+ proc.on('error', (error) => resolve({ code: null, stdout, stderr, error }));
276
+ proc.on('close', (code) => resolve({ code, stdout, stderr }));
277
+ });
278
+ }
@@ -1,5 +1,14 @@
1
1
  export { bundleSdk } from './bundle/index.js';
2
2
  export type { BundleSdkOptions, BundleSdkResult } from './bundle/index.js';
3
+ export { ensureHostPrebuilds } from './host-prebuilds/index.js';
4
+ export type { EnsureHostPrebuildsOptions, EnsureHostPrebuildsResult, HostPrebuildPackage, PackageManagerName } from './host-prebuilds/index.js';
3
5
  export { verifyBundle, hasErrors, hasWarnings, formatVerifyBundleResult } from './verify/index.js';
4
- export type { VerifyBundleOptions, VerifyBundleResult, VerifyBundleIssue } from './verify/index.js';
6
+ export type { VerifyBundleOptions, VerifyBundleResult, VerifyBundleIssue, RuntimeGroup } from './verify/index.js';
7
+ export { HostPrebuildsInstallFailedError, HostPrebuildsInstallRefusedError } from '../utils/errors-client.js';
8
+ export { formatRuntimeSource } from './verify/abi.js';
9
+ export type { BareRuntime, BareRuntimeResolution } from './verify/abi.js';
10
+ export { isReactNativeBareKitInstalled } from './verify/bare-kit-runtime.js';
11
+ export { isMobileHost } from './verify/prebuilds.js';
12
+ export { formatEnginesAdvice } from './verify/engines-advice.js';
13
+ export type { EnginesAdvice, EnginesOverride, EnginesUpgrade } from './verify/engines-advice.js';
5
14
  //# sourceMappingURL=index.d.ts.map
@@ -1,2 +1,8 @@
1
1
  export { bundleSdk } from './bundle/index.js';
2
+ export { ensureHostPrebuilds } from './host-prebuilds/index.js';
2
3
  export { verifyBundle, hasErrors, hasWarnings, formatVerifyBundleResult } from './verify/index.js';
4
+ export { HostPrebuildsInstallFailedError, HostPrebuildsInstallRefusedError } from '../utils/errors-client.js';
5
+ export { formatRuntimeSource } from './verify/abi.js';
6
+ export { isReactNativeBareKitInstalled } from './verify/bare-kit-runtime.js';
7
+ export { isMobileHost } from './verify/prebuilds.js';
8
+ export { formatEnginesAdvice } from './verify/engines-advice.js';
@@ -1,7 +1,10 @@
1
1
  import { type NativeAddon } from './addon-source.js';
2
2
  export interface BareRuntime {
3
3
  version: string;
4
- source: 'flag' | 'config' | 'bare-runtime' | 'bare';
4
+ source: 'flag' | 'config' | 'bare-runtime' | 'bare' | 'react-native-bare-kit';
5
+ /** Installed version of the package named by `source`, when it differs from `version`. */
6
+ packageVersion?: string;
7
+ detail?: string;
5
8
  }
6
9
  export interface UnresolvedBareRuntime {
7
10
  reason: string;
@@ -27,6 +30,15 @@ export interface AbiMismatchIssue {
27
30
  enginesBare: string;
28
31
  runtimeVersion: string;
29
32
  }
33
+ /** A bundled package that is not an addon but still declares engines.bare. */
34
+ export interface EnginesMismatchIssue {
35
+ code: 'engines-mismatch';
36
+ level: 'error';
37
+ package: string;
38
+ message: string;
39
+ enginesBare: string;
40
+ runtimeVersion: string;
41
+ }
30
42
  export interface UnknownRuntimeIssue {
31
43
  code: 'unknown-runtime-version';
32
44
  level: 'warning';
@@ -40,13 +52,21 @@ export interface MalformedEnginesBareIssue {
40
52
  enginesBare: string;
41
53
  message: string;
42
54
  }
43
- export type AbiIssue = AbiMismatchIssue | UnknownRuntimeIssue | MalformedEnginesBareIssue;
55
+ export type AbiIssue = AbiMismatchIssue | EnginesMismatchIssue | UnknownRuntimeIssue | MalformedEnginesBareIssue;
44
56
  export interface CheckAbiOptions {
45
57
  addons: NativeAddon[];
46
58
  runtime: BareRuntimeResolution;
59
+ /** Non-addon packages that declare engines.bare. */
60
+ packages?: EnginesPackage[] | undefined;
61
+ }
62
+ export interface EnginesPackage {
63
+ name: string;
64
+ version?: string;
65
+ enginesBare: string;
47
66
  }
48
67
  export declare function resolveBareRuntime(options: ResolveBareRuntimeOptions): Promise<BareRuntimeResolution>;
49
68
  export declare function normalizeVersion(value: string): string | null;
50
69
  export declare function formatConfigLabel(projectRoot: string, configPath?: string): string;
70
+ export declare function formatRuntimeSource(runtime: BareRuntime): string;
51
71
  export declare function checkAbi(options: CheckAbiOptions): AbiIssue[];
52
72
  //# sourceMappingURL=abi.d.ts.map