@lynxship/cli 0.1.14 → 0.1.16

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 CHANGED
@@ -14,13 +14,13 @@ expiring download URL with a compact terminal QR code.
14
14
  ## Install
15
15
 
16
16
  ```bash
17
- npm install --global @lynxship/cli
17
+ npm install --global @lynxship/cli@latest
18
18
  ```
19
19
 
20
20
  Or run it without a global install:
21
21
 
22
22
  ```bash
23
- npx @lynxship/cli doctor --project-dir ./my-lynx-app
23
+ npx @lynxship/cli@latest doctor --project-dir ./my-lynx-app
24
24
  ```
25
25
 
26
26
  The package exposes the `lynxship` executable.
@@ -52,6 +52,34 @@ The CLI generator itself creates ReactLynx templates by default. Use the
52
52
  official `create-vue-lynx` scaffold when Vue is required, then add LynxShip to
53
53
  that existing project with `init`.
54
54
 
55
+ ## Octane and Miso
56
+
57
+ LynxShip detects Octane and Miso projects without replacing their
58
+ project-owned configuration.
59
+
60
+ Octane uses the official `@octanejs/rspeedy-plugin`. The Lynx integration is
61
+ still early access and its Lynx packages are not currently published on npm,
62
+ so development validation must use the official Octane repository and its
63
+ pnpm workspace:
64
+
65
+ ```bash
66
+ git clone https://github.com/octanejs/octane.git
67
+ cd octane
68
+ pnpm install --filter @octanejs/rspeedy-plugin... --frozen-lockfile
69
+ pnpm --filter @octanejs/rspeedy-plugin exec rspeedy dev \
70
+ --root examples/gallery --environment lynx
71
+ ```
72
+
73
+ Miso uses the official Haskell/Nix flake rather than an npm build script.
74
+ Install Nix and the project's Haskell toolchain, then let LynxShip use the
75
+ flake's `default`/`bundle` output or configure a project-specific output in
76
+ `lynxship.json`. Miso remains experimental; a successful bundle does not
77
+ prove a signed Android or iOS production build.
78
+
79
+ See the source-verified fixture notes in the repository's
80
+ `examples/lynx-octane-fixture` and `examples/miso-lynx-fixture` directories,
81
+ plus `docs/compatibility.md`.
82
+
55
83
  ## Requirements
56
84
 
57
85
  - Node.js 22 or 24 LTS, or Node.js 26 Current; Node.js 24 LTS is the
@@ -486,9 +514,10 @@ actual recording and analysis.
486
514
 
487
515
  ## Package layout
488
516
 
489
- The CLI is backed by the public `@lynxship/*` runtime packages in this
490
- workspace. They are published with the same version and must be available in
491
- the configured npm scope before installing the CLI package.
517
+ The CLI is backed by public `@lynxship/*` runtime packages in this workspace.
518
+ Their published versions may differ from the CLI version; workspace ranges are
519
+ rewritten to released versions when the package is packed. Publish a changed
520
+ runtime package before publishing the CLI when its source or version changed.
492
521
 
493
522
  ## License
494
523
 
@@ -1 +1 @@
1
- {"version":3,"file":"android-build.d.ts","sourceRoot":"","sources":["../src/android-build.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAEpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAWhD,UAAU,mBAAmB;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,YAAY,CAAC;IACtB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,UAAU,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CACvD;AA8KD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAa7D;AAED,wBAAgB,0BAA0B,IAAI,OAAO,CAEpD;AAED;;;;;;GAMG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAsCpE;AAED,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,QAAQ,EACb,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,QAAQ,CAAC,CA+JnB"}
1
+ {"version":3,"file":"android-build.d.ts","sourceRoot":"","sources":["../src/android-build.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAEpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAWhD,UAAU,mBAAmB;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,YAAY,CAAC;IACtB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,UAAU,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CACvD;AA8KD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAa7D;AAED,wBAAgB,0BAA0B,IAAI,OAAO,CAEpD;AAED;;;;;;GAMG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAsCpE;AAED,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,QAAQ,EACb,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,QAAQ,CAAC,CAgKnB"}
@@ -218,6 +218,7 @@ export async function runRealAndroidBuild(job, options) {
218
218
  await buildLynxBundle(options.root, {
219
219
  env: environment,
220
220
  ...processOptions,
221
+ miso: options.profile.miso,
221
222
  });
222
223
  }
223
224
  step("Rspeedy bundle ready", 20);
@@ -4,6 +4,11 @@ export interface BundleBuildOptions {
4
4
  onOutput?: (line: string) => void;
5
5
  script?: string;
6
6
  rspeedyArgs?: string[];
7
+ miso?: {
8
+ attribute?: string;
9
+ artifact?: string;
10
+ };
7
11
  }
12
+ export declare function resolveMisoBuildTarget(flake: string, configuredAttribute?: string): string | undefined;
8
13
  export declare function buildLynxBundle(root: string, options?: BundleBuildOptions): Promise<void>;
9
14
  //# sourceMappingURL=bundle-build.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"bundle-build.d.ts","sourceRoot":"","sources":["../src/bundle-build.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,kBAAkB;IACjC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;AAcD,wBAAsB,eAAe,CACnC,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,IAAI,CAAC,CAqBf"}
1
+ {"version":3,"file":"bundle-build.d.ts","sourceRoot":"","sources":["../src/bundle-build.ts"],"names":[],"mappings":"AAgBA,MAAM,WAAW,kBAAkB;IACjC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,IAAI,CAAC,EAAE;QACL,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;CACH;AAED,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,MAAM,EACb,mBAAmB,CAAC,EAAE,MAAM,GAC3B,MAAM,GAAG,SAAS,CAWpB;AAcD,wBAAsB,eAAe,CACnC,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,IAAI,CAAC,CA8Df"}
@@ -1,6 +1,23 @@
1
- import { readFile } from "node:fs/promises";
2
- import { join } from "node:path";
3
- import { packageManagerScriptCommand, runProcess, runRspeedy, } from "./process-runner.js";
1
+ import { copyFile, mkdir, readFile } from "node:fs/promises";
2
+ import { existsSync } from "node:fs";
3
+ import { basename, join, resolve } from "node:path";
4
+ import { assert } from "@lynxship/contracts";
5
+ import { commandExists, packageManagerScriptCommand, runProcess, runRspeedy, } from "./process-runner.js";
6
+ import { detectLynxFramework } from "./frameworks.js";
7
+ export function resolveMisoBuildTarget(flake, configuredAttribute) {
8
+ const explicitAttribute = configuredAttribute?.trim();
9
+ if (explicitAttribute)
10
+ return ".#" + explicitAttribute;
11
+ if (flake.includes("counter-bundle"))
12
+ return ".#counter-bundle";
13
+ if (/\binherit\s+bundle\b|\bdefault\s*=\s*bundle\b/.test(flake)) {
14
+ return ".#bundle";
15
+ }
16
+ if (/\bdefault\s*=/.test(flake) && flake.includes("mkLynxBundle")) {
17
+ return ".";
18
+ }
19
+ return undefined;
20
+ }
4
21
  async function resolveBundleScript(root) {
5
22
  try {
6
23
  const manifest = JSON.parse(await readFile(join(root, "package.json"), "utf8"));
@@ -13,6 +30,28 @@ async function resolveBundleScript(root) {
13
30
  return "build";
14
31
  }
15
32
  export async function buildLynxBundle(root, options = {}) {
33
+ const framework = await detectLynxFramework(root);
34
+ if (framework.buildSystem === "miso-nix" &&
35
+ !options.script &&
36
+ !options.rspeedyArgs) {
37
+ assert(commandExists("nix"), "BUILD_MISO_NIX_REQUIRED", "Miso requires Nix. Install Nix, then rerun lynxship doctor or provide a project build script.");
38
+ const flake = await readFile(join(root, "flake.nix"), "utf8").catch(() => "");
39
+ const target = resolveMisoBuildTarget(flake, options.miso?.attribute);
40
+ assert(target, "BUILD_MISO_ATTRIBUTE_REQUIRED", "Miso bundle output is not discoverable. Set build.<profile>.miso.attribute in lynxship.json, or expose a default/mkLynxBundle output in flake.nix.");
41
+ await runProcess("nix", ["build", target], {
42
+ cwd: root,
43
+ env: options.env,
44
+ quiet: options.quiet,
45
+ onOutput: options.onOutput,
46
+ });
47
+ const artifact = resolve(root, options.miso?.artifact ?? "result/main.lynx.bundle");
48
+ assert(existsSync(artifact), "BUILD_MISO_BUNDLE_MISSING", "Miso did not produce the configured Lynx bundle: " + artifact);
49
+ const output = join(root, "dist", basename(artifact));
50
+ await mkdir(join(root, "dist"), { recursive: true });
51
+ await copyFile(artifact, output);
52
+ options.onOutput?.("Miso bundle ready: " + output);
53
+ return;
54
+ }
16
55
  if (!options.script && options.rspeedyArgs) {
17
56
  await runRspeedy(root, "build", options.rspeedyArgs, {
18
57
  env: options.env,
package/dist/config.d.ts CHANGED
@@ -3,6 +3,10 @@ export interface BuildProfile {
3
3
  distribution?: string;
4
4
  channel?: string;
5
5
  environment?: string;
6
+ miso?: {
7
+ attribute?: string;
8
+ artifact?: string;
9
+ };
6
10
  android?: {
7
11
  artifact?: string;
8
12
  };
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"desktop-build.d.ts","sourceRoot":"","sources":["../src/desktop-build.ts"],"names":[],"mappings":"AAGA,OAAO,EAAU,KAAK,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAE5D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAahD,UAAU,mBAAmB;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,YAAY,CAAC;IACtB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,UAAU,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CACvD;AAED,UAAU,WAAW;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,WAAW,EACrB,OAAO,CAAC,EAAE,YAAY,GACrB,MAAM,GAAG,SAAS,CAMpB;AAYD,wBAAsB,cAAc,CAClC,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,YAAY,GACrB,OAAO,CAAC,OAAO,CAAC,CAmBlB;AA+DD,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,QAAQ,EACb,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,QAAQ,CAAC,CA0GnB"}
1
+ {"version":3,"file":"desktop-build.d.ts","sourceRoot":"","sources":["../src/desktop-build.ts"],"names":[],"mappings":"AAGA,OAAO,EAAU,KAAK,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAE5D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAahD,UAAU,mBAAmB;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,YAAY,CAAC;IACtB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,UAAU,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CACvD;AAED,UAAU,WAAW;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,WAAW,EACrB,OAAO,CAAC,EAAE,YAAY,GACrB,MAAM,GAAG,SAAS,CAMpB;AAYD,wBAAsB,cAAc,CAClC,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,YAAY,GACrB,OAAO,CAAC,OAAO,CAAC,CAmBlB;AA+DD,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,QAAQ,EACb,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,QAAQ,CAAC,CA2GnB"}
@@ -99,6 +99,7 @@ export async function runRealDesktopBuild(job, options) {
99
99
  await buildLynxBundle(options.root, {
100
100
  quiet: options.quiet,
101
101
  onOutput: options.onEvent,
102
+ miso: options.profile.miso,
102
103
  });
103
104
  }
104
105
  transitionBuild(job, "queued", "Desktop build queued locally");
@@ -0,0 +1,10 @@
1
+ export type LynxFramework = "react" | "vue" | "octane" | "miso" | "vanilla" | "unknown";
2
+ export interface FrameworkDetection {
3
+ framework: LynxFramework;
4
+ label: string;
5
+ evidence: string;
6
+ buildSystem: "rspeedy" | "miso-nix" | "unknown";
7
+ experimental: boolean;
8
+ }
9
+ export declare function detectLynxFramework(root: string): Promise<FrameworkDetection>;
10
+ //# sourceMappingURL=frameworks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frameworks.d.ts","sourceRoot":"","sources":["../src/frameworks.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,aAAa,GACrB,OAAO,GACP,KAAK,GACL,QAAQ,GACR,MAAM,GACN,SAAS,GACT,SAAS,CAAC;AAEd,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,aAAa,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,SAAS,GAAG,UAAU,GAAG,SAAS,CAAC;IAChD,YAAY,EAAE,OAAO,CAAC;CACvB;AAiCD,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,kBAAkB,CAAC,CA+F7B"}
@@ -0,0 +1,104 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { existsSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ async function readText(file) {
5
+ return readFile(file, "utf8").catch(() => "");
6
+ }
7
+ async function readManifest(root) {
8
+ try {
9
+ return JSON.parse(await readText(join(root, "package.json")));
10
+ }
11
+ catch {
12
+ return {};
13
+ }
14
+ }
15
+ function dependencyNames(manifest) {
16
+ return new Set([
17
+ ...Object.keys(manifest.dependencies ?? {}),
18
+ ...Object.keys(manifest.devDependencies ?? {}),
19
+ ]);
20
+ }
21
+ function hasAny(names, candidates) {
22
+ return candidates.find((name) => names.has(name));
23
+ }
24
+ export async function detectLynxFramework(root) {
25
+ const manifest = await readManifest(root);
26
+ const names = dependencyNames(manifest);
27
+ const scripts = Object.values(manifest.scripts ?? {}).join(" ");
28
+ const configFiles = [
29
+ "lynx.config.ts",
30
+ "lynx.config.js",
31
+ "lynx.config.mjs",
32
+ "lynx.config.cjs",
33
+ "octane.config.ts",
34
+ "octane.config.js",
35
+ ];
36
+ const config = (await Promise.all(configFiles
37
+ .filter((file) => existsSync(join(root, file)))
38
+ .map((file) => readText(join(root, file))))).join("\n");
39
+ const flake = await readText(join(root, "flake.nix"));
40
+ const cabal = await readText(join(root, "cabal.project"));
41
+ const hasCabalFile = existsSync(join(root, "cabal.project"));
42
+ const hasMiso = hasAny(names, ["miso", "miso-lynx"]) ||
43
+ (existsSync(join(root, "flake.nix")) &&
44
+ /miso|mkLynxBundle|Miso\\.Native|lynx/i.test(flake + "\n" + cabal)) ||
45
+ (hasCabalFile && /miso|lynx/i.test(cabal));
46
+ if (hasMiso) {
47
+ return {
48
+ framework: "miso",
49
+ label: "Miso (Haskell)",
50
+ evidence: "Miso.Native/Haskell project detected",
51
+ buildSystem: "miso-nix",
52
+ experimental: true,
53
+ };
54
+ }
55
+ const hasOctane = hasAny(names, ["octane", "@octanejs/lynx", "@octanejs/rspeedy-plugin"]) ||
56
+ existsSync(join(root, "octane.config.ts")) ||
57
+ existsSync(join(root, "octane.config.js")) ||
58
+ /pluginOctane|@jsxImportSource\\s+octane|\\.tsrx\\b/.test(config + scripts);
59
+ if (hasOctane) {
60
+ return {
61
+ framework: "octane",
62
+ label: "Octane",
63
+ evidence: "Octane compiler/Rspeedy integration detected",
64
+ buildSystem: "rspeedy",
65
+ experimental: true,
66
+ };
67
+ }
68
+ const vueDependency = hasAny(names, ["vue-lynx", "@lynx-js/vue"]);
69
+ if (vueDependency || /pluginVueLynx|vue-lynx/i.test(config)) {
70
+ return {
71
+ framework: "vue",
72
+ label: "Vue Lynx",
73
+ evidence: "Vue Lynx integration detected",
74
+ buildSystem: "rspeedy",
75
+ experimental: false,
76
+ };
77
+ }
78
+ if (hasAny(names, ["@lynx-js/react", "@lynx-js/react-rsbuild-plugin"])) {
79
+ return {
80
+ framework: "react",
81
+ label: "ReactLynx",
82
+ evidence: "official ReactLynx package detected",
83
+ buildSystem: "rspeedy",
84
+ experimental: false,
85
+ };
86
+ }
87
+ if (names.has("@lynx-js/rspeedy") ||
88
+ /rspeedy|environments\\s*:/i.test(config + scripts)) {
89
+ return {
90
+ framework: "vanilla",
91
+ label: "Vanilla Lynx",
92
+ evidence: "Rspeedy/Lynx project detected",
93
+ buildSystem: "rspeedy",
94
+ experimental: false,
95
+ };
96
+ }
97
+ return {
98
+ framework: "unknown",
99
+ label: "Unknown",
100
+ evidence: "No supported Lynx framework marker found",
101
+ buildSystem: "unknown",
102
+ experimental: false,
103
+ };
104
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"guidance.d.ts","sourceRoot":"","sources":["../src/guidance.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;CAChC;AA6mBD,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,OAAO,EACd,OAAO,GAAE,eAAoB,GAC5B,WAAW,CAEb"}
1
+ {"version":3,"file":"guidance.d.ts","sourceRoot":"","sources":["../src/guidance.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;CAChC;AAmoBD,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,OAAO,EACd,OAAO,GAAE,eAAoB,GAC5B,WAAW,CAEb"}
package/dist/guidance.js CHANGED
@@ -105,6 +105,28 @@ const guidance = {
105
105
  ],
106
106
  note: "Configure environments.web in lynx.config.* or provide the project's build:web script.",
107
107
  },
108
+ BUILD_MISO_NIX_REQUIRED: {
109
+ commands: [
110
+ "lynxship doctor --platform android",
111
+ "nix --version",
112
+ "lynxship build --platform android --profile production",
113
+ ],
114
+ note: "Install Nix and use the project's flake to build the Miso Lynx bundle.",
115
+ },
116
+ BUILD_MISO_ATTRIBUTE_REQUIRED: {
117
+ commands: [
118
+ "lynxship init",
119
+ "lynxship build --platform android --profile production",
120
+ ],
121
+ note: "Expose a default/mkLynxBundle output or set build.production.miso.attribute to the Nix flake output that creates main.lynx.bundle.",
122
+ },
123
+ BUILD_MISO_BUNDLE_MISSING: {
124
+ commands: [
125
+ "nix build",
126
+ "lynxship build --platform android --profile production",
127
+ ],
128
+ note: "Check build.production.miso.artifact and the output produced by the Miso flake.",
129
+ },
108
130
  WEB_BUNDLE_MISSING: {
109
131
  commands: [
110
132
  "lynxship doctor --platform web",
@@ -1 +1 @@
1
- {"version":3,"file":"harmony-build.d.ts","sourceRoot":"","sources":["../src/harmony-build.ts"],"names":[],"mappings":"AAIA,OAAO,EAAU,KAAK,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAE5D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAKhD,UAAU,mBAAmB;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,YAAY,CAAC;IACtB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,UAAU,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CACvD;AAeD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAOpD;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG;IAC9C,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,OAAO,CAAC;IACd,GAAG,EAAE,OAAO,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB,CAcA;AAED,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC,MAAM,EAAE,CAAC,CA4BnB;AA2GD,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,QAAQ,EACb,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,QAAQ,CAAC,CAoGnB"}
1
+ {"version":3,"file":"harmony-build.d.ts","sourceRoot":"","sources":["../src/harmony-build.ts"],"names":[],"mappings":"AAIA,OAAO,EAAU,KAAK,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAE5D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAKhD,UAAU,mBAAmB;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,YAAY,CAAC;IACtB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,UAAU,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CACvD;AAeD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAOpD;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG;IAC9C,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,OAAO,CAAC;IACd,GAAG,EAAE,OAAO,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB,CAcA;AAED,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC,MAAM,EAAE,CAAC,CA4BnB;AA2GD,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,QAAQ,EACb,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,QAAQ,CAAC,CAqGnB"}
@@ -141,6 +141,7 @@ export async function runRealHarmonyBuild(job, options) {
141
141
  await buildLynxBundle(options.root, {
142
142
  quiet: options.quiet,
143
143
  onOutput: options.onEvent,
144
+ miso: options.profile.miso,
144
145
  });
145
146
  }
146
147
  step("Syncing bundle into the HarmonyOS HAP resources…", 20);
package/dist/index.js CHANGED
@@ -3,6 +3,7 @@ import { access, mkdir, readFile, writeFile } from "node:fs/promises";
3
3
  import { existsSync } from "node:fs";
4
4
  import { createHash, randomBytes, randomUUID } from "node:crypto";
5
5
  import { basename, dirname, join, resolve } from "node:path";
6
+ import { detectLynxFramework } from "./frameworks.js";
6
7
  import { BuildOrchestrator } from "@lynxship/build-orchestrator";
7
8
  import { JsonRepository } from "@lynxship/db";
8
9
  import { assert, createId, sha256, } from "@lynxship/contracts";
@@ -364,13 +365,14 @@ async function executeBuild(options, showResult = true) {
364
365
  }
365
366
  return result;
366
367
  }
367
- async function buildSharedLynxBundle() {
368
+ async function buildSharedLynxBundle(miso) {
368
369
  const progress = ui.progress("Shared Lynx bundle");
369
370
  try {
370
371
  progress.update(undefined, "Building the shared Lynx bundle once for native targets…");
371
372
  await buildLynxBundle(root, {
372
373
  quiet: json,
373
374
  onOutput: (message) => progress.event(message),
375
+ miso,
374
376
  });
375
377
  progress.update(100, "Shared native Lynx bundle ready");
376
378
  }
@@ -515,6 +517,9 @@ Global options:
515
517
  Node support: Node 22/24 LTS or Node 26 Current. Use Node 24 LTS for production.`;
516
518
  }
517
519
  async function looksLikeLynxProject() {
520
+ const framework = await detectLynxFramework(root);
521
+ if (framework.framework !== "unknown")
522
+ return true;
518
523
  const configFiles = [
519
524
  "lynx.config.ts",
520
525
  "lynx.config.js",
@@ -644,13 +649,13 @@ async function runRspeedyCommand(subcommand, environment) {
644
649
  let devUrl;
645
650
  let devQrPrinted = false;
646
651
  let devQrTimer;
647
- const printDevQr = () => {
652
+ const printDevQr = async () => {
648
653
  if (!devUrl || devQrPrinted || json || ui.options.quiet)
649
654
  return;
650
655
  devQrPrinted = true;
651
656
  if (devQrTimer)
652
657
  clearTimeout(devQrTimer);
653
- ui.devServerQr(devUrl);
658
+ await ui.devServerQr(devUrl);
654
659
  };
655
660
  await runRspeedy(root, subcommand, forwarded, {
656
661
  env: environment,
@@ -664,10 +669,12 @@ async function runRspeedyCommand(subcommand, environment) {
664
669
  devUrl = url;
665
670
  if (devQrTimer)
666
671
  clearTimeout(devQrTimer);
667
- devQrTimer = setTimeout(printDevQr, 500);
672
+ devQrTimer = setTimeout(() => {
673
+ void printDevQr();
674
+ }, 500);
668
675
  }
669
676
  if (devUrl && shouldPrintDevServerQr(line))
670
- printDevQr();
677
+ void printDevQr();
671
678
  },
672
679
  });
673
680
  printValue({ status: "success", command: `rspeedy ${subcommand}` }, {
@@ -938,7 +945,37 @@ async function main() {
938
945
  const nodeMajor = Number(process.versions.node.split(".")[0]);
939
946
  const nodeSupported = nodeMajor >= 22;
940
947
  const nodeRecommended = nodeMajor % 2 === 0;
948
+ const framework = await detectLynxFramework(root);
941
949
  const checks = [
950
+ {
951
+ name: "lynx-framework",
952
+ ok: framework.framework !== "unknown",
953
+ status: framework.framework === "unknown"
954
+ ? "warn"
955
+ : framework.experimental
956
+ ? "warn"
957
+ : "pass",
958
+ value: framework.framework === "unknown"
959
+ ? "unknown · verify the official Lynx/Rspeedy integration"
960
+ : framework.label +
961
+ " · " +
962
+ framework.evidence +
963
+ (framework.experimental ? " · early access" : ""),
964
+ },
965
+ ...(framework.framework === "miso"
966
+ ? [
967
+ {
968
+ name: "miso-nix",
969
+ ok: commandExists("nix"),
970
+ status: commandExists("nix")
971
+ ? "pass"
972
+ : "fail",
973
+ value: commandExists("nix")
974
+ ? "Nix detected"
975
+ : "missing · fix: install Nix and rerun doctor",
976
+ },
977
+ ]
978
+ : []),
942
979
  {
943
980
  name: "node",
944
981
  ok: nodeSupported,
@@ -1717,7 +1754,7 @@ async function main() {
1717
1754
  return;
1718
1755
  }
1719
1756
  if (buildAll && wait && !local)
1720
- await buildSharedLynxBundle();
1757
+ await buildSharedLynxBundle(resolveProfile(config, profile).miso);
1721
1758
  const progress = ui.progress("All Lynx targets build");
1722
1759
  const progressValues = {};
1723
1760
  const progressLabels = {};
@@ -1 +1 @@
1
- {"version":3,"file":"ios-build.d.ts","sourceRoot":"","sources":["../src/ios-build.ts"],"names":[],"mappings":"AAYA,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAEpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAMhD,UAAU,eAAe;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,YAAY,CAAC;IACtB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,UAAU,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CACvD;AAyHD;;;;;;GAMG;AACH,wBAAsB,uBAAuB,CAC3C,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,EAAE,CAAC,CAoBnB;AAoED;;;;GAIG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,UAAU,CAAC,EAAE,MAAM,EACnB,OAAO,GAAE;IAAE,aAAa,CAAC,EAAE,OAAO,CAAA;CAAO,GACxC,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAuD7B;AAgCD,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE;IACP,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CAChC,GACL,OAAO,CAAC,IAAI,CAAC,CAcf;AAkND,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,OAAO,CAcxE;AA+DD,wBAAsB,eAAe,CACnC,GAAG,EAAE,QAAQ,EACb,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,QAAQ,CAAC,CAqNnB"}
1
+ {"version":3,"file":"ios-build.d.ts","sourceRoot":"","sources":["../src/ios-build.ts"],"names":[],"mappings":"AAYA,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAEpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAMhD,UAAU,eAAe;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,YAAY,CAAC;IACtB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,UAAU,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CACvD;AAyHD;;;;;;GAMG;AACH,wBAAsB,uBAAuB,CAC3C,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,EAAE,CAAC,CAoBnB;AAoED;;;;GAIG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,UAAU,CAAC,EAAE,MAAM,EACnB,OAAO,GAAE;IAAE,aAAa,CAAC,EAAE,OAAO,CAAA;CAAO,GACxC,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAuD7B;AAgCD,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE;IACP,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CAChC,GACL,OAAO,CAAC,IAAI,CAAC,CAcf;AAmND,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,OAAO,CAcxE;AA+DD,wBAAsB,eAAe,CACnC,GAAG,EAAE,QAAQ,EACb,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,QAAQ,CAAC,CAsNnB"}
package/dist/ios-build.js CHANGED
@@ -264,6 +264,7 @@ async function runRealIosSimulatorBuild(job, options) {
264
264
  await buildLynxBundle(options.root, {
265
265
  quiet: options.quiet,
266
266
  onOutput: options.onEvent,
267
+ miso: options.profile.miso,
267
268
  });
268
269
  }
269
270
  if (ios.bundleScript) {
@@ -454,6 +455,7 @@ export async function runRealIosBuild(job, options) {
454
455
  await buildLynxBundle(options.root, {
455
456
  quiet: options.quiet,
456
457
  onOutput: options.onEvent,
458
+ miso: options.profile.miso,
457
459
  });
458
460
  }
459
461
  if (ios.bundleScript) {
@@ -35,5 +35,5 @@ export declare const pill: {
35
35
  export declare function finalLine(message: string, success?: boolean): void;
36
36
  export declare function nextSteps(commands: string[], note?: string, environment?: string): void;
37
37
  export declare function downloadArtifact(url: string, expiresAt?: string, enabled?: boolean): void;
38
- export declare function devServerQr(url: string, enabled?: boolean): void;
38
+ export declare function devServerQr(url: string, enabled?: boolean): Promise<void>;
39
39
  //# sourceMappingURL=components.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"components.d.ts","sourceRoot":"","sources":["../../src/ui/components.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAK7C,MAAM,WAAW,MAAM;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,SAAS,CAAC;CACxB;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAEjD;AAED,eAAO,MAAM,GAAG;qBACG,MAAM,GAAG,IAAI;kBAGhB,MAAM,GAAG,IAAI;mBAGZ,MAAM,GAAG,IAAI;kBAGd,MAAM,GAAG,IAAI;mBAGZ,MAAM,GAAG,IAAI;CAG7B,CAAC;AAgBF,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CA6C9D;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,MAAM,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7C,IAAI,IAAI,IAAI,CAAC;CACd;AA6CD,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAoBnD;AA6BD,wBAAgB,cAAc,CAC5B,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,OAAO,GACf,cAAc,CAuEhB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,IAAI,IAAI,IAAI,CAAC;CACd;AAED,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,aAAa,CA2BlE;AAYD,eAAO,MAAM,IAAI;oBACC,MAAM;iBACT,MAAM;iBACN,MAAM;kBACL,MAAM;CACrB,CAAC;AAEF,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,UAAO,GAAG,IAAI,CAE/D;AAED,wBAAgB,SAAS,CACvB,QAAQ,EAAE,MAAM,EAAE,EAClB,IAAI,CAAC,EAAE,MAAM,EACb,WAAW,CAAC,EAAE,MAAM,GACnB,IAAI,CAQN;AAED,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,MAAM,EACX,SAAS,CAAC,EAAE,MAAM,EAClB,OAAO,UAAO,GACb,IAAI,CAQN;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,UAAO,GAAG,IAAI,CAS7D"}
1
+ {"version":3,"file":"components.d.ts","sourceRoot":"","sources":["../../src/ui/components.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAK7C,MAAM,WAAW,MAAM;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,SAAS,CAAC;CACxB;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAEjD;AAED,eAAO,MAAM,GAAG;qBACG,MAAM,GAAG,IAAI;kBAGhB,MAAM,GAAG,IAAI;mBAGZ,MAAM,GAAG,IAAI;kBAGd,MAAM,GAAG,IAAI;mBAGZ,MAAM,GAAG,IAAI;CAG7B,CAAC;AAgBF,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CA6C9D;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,MAAM,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7C,IAAI,IAAI,IAAI,CAAC;CACd;AA6CD,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAoBnD;AA6BD,wBAAgB,cAAc,CAC5B,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,OAAO,GACf,cAAc,CAuEhB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,IAAI,IAAI,IAAI,CAAC;CACd;AAED,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,aAAa,CA2BlE;AAYD,eAAO,MAAM,IAAI;oBACC,MAAM;iBACT,MAAM;iBACN,MAAM;kBACL,MAAM;CACrB,CAAC;AAEF,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,UAAO,GAAG,IAAI,CAE/D;AAED,wBAAgB,SAAS,CACvB,QAAQ,EAAE,MAAM,EAAE,EAClB,IAAI,CAAC,EAAE,MAAM,EACb,WAAW,CAAC,EAAE,MAAM,GACnB,IAAI,CAQN;AAED,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,MAAM,EACX,SAAS,CAAC,EAAE,MAAM,EAClB,OAAO,UAAO,GACb,IAAI,CAQN;AAED,wBAAsB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,UAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAmB5E"}
@@ -289,15 +289,25 @@ export function downloadArtifact(url, expiresAt, enabled = true) {
289
289
  if (expiresAt)
290
290
  console.log(` ${c.muted(`Link expires at ${expiresAt}`)}`);
291
291
  }
292
- export function devServerQr(url, enabled = true) {
292
+ export async function devServerQr(url, enabled = true) {
293
293
  if (!enabled)
294
294
  return;
295
- console.log(`\n${c.brandBold("Lynx Explorer")}`);
295
+ const rail = c.teal("");
296
+ const printRail = (value = "") => console.log(` ${rail}${value}`);
297
+ printRail();
298
+ printRail(` ${c.brandBold("Lynx Explorer")}`);
296
299
  if (colorsEnabled && supportsUnicode()) {
297
- console.log(renderTerminalQr(url, true));
300
+ (await renderTerminalQr(url))
301
+ .split("\n")
302
+ .forEach((line) => printRail(` ${line}`));
298
303
  }
299
304
  else {
300
- qrcode.generate(url, { small: true }, (code) => console.log(code));
305
+ qrcode.generate(url, { small: true }, (code) => {
306
+ code.split("\n").forEach((line) => printRail(` ${line}`));
307
+ });
301
308
  }
302
- console.log(` ${c.teal("URL")} ${url}\n`);
309
+ printRail();
310
+ printRail(` ${c.teal("URL")} ${url}`);
311
+ printRail();
312
+ console.log();
303
313
  }
@@ -19,7 +19,7 @@ export declare class CliUi {
19
19
  spinner(text: string): SpinnerHandle;
20
20
  done(message: string, success?: boolean): void;
21
21
  downloadArtifact(url: string, expiresAt?: string): void;
22
- devServerQr(url: string): void;
22
+ devServerQr(url: string): Promise<void>;
23
23
  }
24
24
  export declare function createCliUi(args: string[]): CliUi;
25
25
  export { terminalOptions };
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ui/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAUL,KAAK,MAAM,EACX,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAEL,eAAe,EACf,KAAK,eAAe,EACrB,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAElD,qBAAa,KAAK;IAChB,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;IAElC,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;gBAElB,IAAI,EAAE,MAAM,EAAE;IAU1B,MAAM,IAAI,IAAI;IAId,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAI3B,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAI9B,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAI3B,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAI3B,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAK5B,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAI5B,SAAS,CAAC,QAAQ,EAAE,WAAW,GAAG,IAAI;IAKtC,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI;IAI5C,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI;IAKzC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc;IAIvC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa;IAIpC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,UAAO,GAAG,IAAI;IAI3C,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI;IAIvD,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;CAS/B;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,KAAK,CAEjD;AAED,OAAO,EAAE,eAAe,EAAE,CAAC;AAE3B,YAAY,EAAE,MAAM,EAAE,cAAc,EAAE,eAAe,EAAE,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ui/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAUL,KAAK,MAAM,EACX,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAEL,eAAe,EACf,KAAK,eAAe,EACrB,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAElD,qBAAa,KAAK;IAChB,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;IAElC,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;gBAElB,IAAI,EAAE,MAAM,EAAE;IAU1B,MAAM,IAAI,IAAI;IAId,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAI3B,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAI9B,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAI3B,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAI3B,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAK5B,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAI5B,SAAS,CAAC,QAAQ,EAAE,WAAW,GAAG,IAAI;IAKtC,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI;IAI5C,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI;IAKzC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc;IAIvC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa;IAIpC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,UAAO,GAAG,IAAI;IAI3C,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI;IAIjD,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAS9C;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,KAAK,CAEjD;AAED,OAAO,EAAE,eAAe,EAAE,CAAC;AAE3B,YAAY,EAAE,MAAM,EAAE,cAAc,EAAE,eAAe,EAAE,CAAC"}
package/dist/ui/index.js CHANGED
@@ -66,11 +66,11 @@ export class CliUi {
66
66
  downloadArtifact(url, expiresAt) {
67
67
  downloadArtifact(url, expiresAt, this.interactive);
68
68
  }
69
- devServerQr(url) {
69
+ async devServerQr(url) {
70
70
  // Rspeedy suppresses its own QR when its stdout is piped. LynxShip
71
71
  // captures that output, so render the fallback unless the caller asked
72
72
  // for machine JSON, quiet output, or an explicitly non-interactive run.
73
- devServerQr(url, !this.options.json && !this.options.quiet && !this.options.nonInteractive);
73
+ await devServerQr(url, !this.options.json && !this.options.quiet && !this.options.nonInteractive);
74
74
  }
75
75
  }
76
76
  export function createCliUi(args) {
package/dist/ui/qr.d.ts CHANGED
@@ -53,11 +53,9 @@ export declare const LYNXSHIP_QR_STYLE: {
53
53
  };
54
54
  };
55
55
  /**
56
- * Render the same QR styling tokens as qr-code-styling in a terminal.
57
- *
58
- * qr-code-styling produces SVG/canvas output, which a normal terminal cannot
59
- * display. The terminal adapter therefore uses its public QR matrix engine
60
- * and applies the exact shared ECC, gradient, rotation and quiet-zone values.
56
+ * Generate the QR with qr-code-styling itself, exactly like the WISA renderer.
57
+ * The SVG is then rasterized only to make the library's real rounded shapes
58
+ * displayable in a normal terminal; no QR modules are recreated by LynxShip.
61
59
  */
62
- export declare function renderTerminalQr(data: string, colored: boolean): string;
60
+ export declare function renderTerminalQr(data: string): Promise<string>;
63
61
  //# sourceMappingURL=qr.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"qr.d.ts","sourceRoot":"","sources":["../../src/ui/qr.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwCpB,CAAC;AAoEX;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,MAAM,CAkCvE"}
1
+ {"version":3,"file":"qr.d.ts","sourceRoot":"","sources":["../../src/ui/qr.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwCpB,CAAC;AAyLX;;;;GAIG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAmBpE"}
package/dist/ui/qr.js CHANGED
@@ -1,5 +1,6 @@
1
- import qrcodeGenerator from "qrcode-generator";
2
1
  import { LYNX_BRAND } from "./colors.js";
2
+ import { terminalWidth } from "./terminal.js";
3
+ import { createRequire } from "node:module";
3
4
  export const LYNXSHIP_QR_STYLE = {
4
5
  type: "svg",
5
6
  width: 300,
@@ -41,84 +42,139 @@ export const LYNXSHIP_QR_STYLE = {
41
42
  },
42
43
  backgroundOptions: { color: "#FFFFFF" },
43
44
  };
44
- const QUIET_ZONE = 2;
45
- const RESET = "\u001b[0m";
46
- const WHITE_BACKGROUND = "\u001b[48;2;255;255;255m";
47
- function hexToRgb(hex) {
48
- const value = hex.replace("#", "");
49
- return [
50
- Number.parseInt(value.slice(0, 2), 16),
51
- Number.parseInt(value.slice(2, 4), 16),
52
- Number.parseInt(value.slice(4, 6), 16),
53
- ];
45
+ async function loadDependencies() {
46
+ const require = createRequire(import.meta.url);
47
+ const qrModule = require("qr-code-styling");
48
+ const jsdomModule = require("jsdom");
49
+ const resvgModule = require("@resvg/resvg-js");
50
+ const pngModule = require("pngjs");
51
+ return {
52
+ QRCodeStyling: "default" in qrModule && qrModule.default
53
+ ? qrModule.default
54
+ : qrModule,
55
+ JSDOM: jsdomModule.JSDOM,
56
+ Resvg: resvgModule.Resvg,
57
+ PNG: pngModule.PNG,
58
+ };
54
59
  }
55
- function colorAt(row, column, size) {
56
- const angle = LYNXSHIP_QR_STYLE.dotsOptions.gradient.rotation;
57
- const x = size <= 1 ? 0 : column / (size - 1);
58
- const y = size <= 1 ? 0 : row / (size - 1);
59
- const projection = x * Math.cos(angle) + y * Math.sin(angle);
60
- const minimum = Math.min(0, Math.cos(angle), Math.sin(angle));
61
- const maximum = Math.max(0, Math.cos(angle), Math.sin(angle));
62
- const ratio = Math.max(0, Math.min(1, (projection - minimum) / (maximum - minimum)));
63
- const start = hexToRgb(LYNXSHIP_QR_STYLE.dotsOptions.gradient.colorStops[0].color);
64
- const end = hexToRgb(LYNXSHIP_QR_STYLE.dotsOptions.gradient.colorStops[1].color);
60
+ function ansiColor(channel, red, green, blue) {
61
+ return "\u001b[" + channel + ";2;" + red + ";" + green + ";" + blue + "m";
62
+ }
63
+ function pixelAt(image, x, y) {
64
+ const offset = (y * image.width + x) * 4;
65
65
  return [
66
- Math.round(start[0] + (end[0] - start[0]) * ratio),
67
- Math.round(start[1] + (end[1] - start[1]) * ratio),
68
- Math.round(start[2] + (end[2] - start[2]) * ratio),
66
+ image.data[offset] ?? 255,
67
+ image.data[offset + 1] ?? 255,
68
+ image.data[offset + 2] ?? 255,
69
+ image.data[offset + 3] ?? 255,
69
70
  ];
70
71
  }
71
- function ansiForeground([red, green, blue]) {
72
- return `\u001b[38;2;${red};${green};${blue}m`;
72
+ function isBackground([red, green, blue, alpha]) {
73
+ return alpha < 16 || (red > 248 && green > 248 && blue > 248);
73
74
  }
74
- function moduleAt(matrix, row, column, size) {
75
- if (row < QUIET_ZONE ||
76
- column < QUIET_ZONE ||
77
- row >= size + QUIET_ZONE ||
78
- column >= size + QUIET_ZONE)
79
- return false;
80
- return matrix.isDark(row - QUIET_ZONE, column - QUIET_ZONE);
75
+ function cropToQrContent(image) {
76
+ let left = image.width;
77
+ let top = image.height;
78
+ let right = -1;
79
+ let bottom = -1;
80
+ for (let y = 0; y < image.height; y += 1) {
81
+ for (let x = 0; x < image.width; x += 1) {
82
+ if (!isBackground(pixelAt(image, x, y))) {
83
+ left = Math.min(left, x);
84
+ top = Math.min(top, y);
85
+ right = Math.max(right, x);
86
+ bottom = Math.max(bottom, y);
87
+ }
88
+ }
89
+ }
90
+ if (right < left || bottom < top)
91
+ return image;
92
+ const padding = Math.max(3, Math.round(Math.min(image.width, image.height) * 0.02));
93
+ const cropLeft = Math.max(0, left - padding);
94
+ const cropTop = Math.max(0, top - padding);
95
+ const cropRight = Math.min(image.width - 1, right + padding);
96
+ const cropBottom = Math.min(image.height - 1, bottom + padding);
97
+ const width = cropRight - cropLeft + 1;
98
+ const height = cropBottom - cropTop + 1;
99
+ const data = new Uint8Array(width * height * 4);
100
+ for (let y = 0; y < height; y += 1) {
101
+ for (let x = 0; x < width; x += 1) {
102
+ const source = ((cropTop + y) * image.width + cropLeft + x) * 4;
103
+ const target = (y * width + x) * 4;
104
+ data[target] = image.data[source] ?? 255;
105
+ data[target + 1] = image.data[source + 1] ?? 255;
106
+ data[target + 2] = image.data[source + 2] ?? 255;
107
+ data[target + 3] = image.data[source + 3] ?? 255;
108
+ }
109
+ }
110
+ return { width, height, data };
81
111
  }
82
- /**
83
- * Render the same QR styling tokens as qr-code-styling in a terminal.
84
- *
85
- * qr-code-styling produces SVG/canvas output, which a normal terminal cannot
86
- * display. The terminal adapter therefore uses its public QR matrix engine
87
- * and applies the exact shared ECC, gradient, rotation and quiet-zone values.
88
- */
89
- export function renderTerminalQr(data, colored) {
90
- const matrix = qrcodeGenerator(0, "H");
91
- matrix.addData(data, "Byte");
92
- matrix.make();
93
- const size = matrix.getModuleCount();
94
- const fullSize = size + QUIET_ZONE * 2;
112
+ function renderRasterInTerminal(image) {
113
+ const columns = Math.max(35, Math.min(72, terminalWidth() - 8));
114
+ const scale = image.width / columns;
115
+ const rows = Math.ceil(image.height / scale / 2);
95
116
  const lines = [];
96
- for (let row = 0; row < fullSize; row += 2) {
97
- let line = colored ? WHITE_BACKGROUND : "";
98
- for (let column = 0; column < fullSize; column += 1) {
99
- const top = moduleAt(matrix, row, column, size);
100
- const bottom = moduleAt(matrix, row + 1, column, size);
101
- if (!top && !bottom) {
117
+ const whiteBackground = ansiColor("48", 255, 255, 255);
118
+ const reset = "\u001b[0m";
119
+ for (let row = 0; row < rows; row += 1) {
120
+ const topY = Math.min(image.height - 1, Math.floor(row * 2 * scale));
121
+ const bottomY = Math.min(image.height - 1, Math.floor((row * 2 + 1) * scale));
122
+ let line = whiteBackground;
123
+ for (let column = 0; column < columns; column += 1) {
124
+ const x = Math.min(image.width - 1, Math.floor(column * scale));
125
+ const top = pixelAt(image, x, topY);
126
+ const bottom = pixelAt(image, x, bottomY);
127
+ const topBackground = isBackground(top);
128
+ const bottomBackground = isBackground(bottom);
129
+ if (topBackground && bottomBackground) {
102
130
  line += " ";
103
- continue;
104
- }
105
- if (!colored) {
106
- line += top && bottom ? "█" : top ? "▀" : "▄";
107
- continue;
108
131
  }
109
- const topColor = colorAt(row, column, fullSize);
110
- const bottomColor = colorAt(row + 1, column, fullSize);
111
- if (top && bottom) {
112
- line += `${ansiForeground(topColor)}█`;
132
+ else if (!topBackground && bottomBackground) {
133
+ line +=
134
+ ansiColor("38", top[0], top[1], top[2]) +
135
+ ansiColor("48", 255, 255, 255) +
136
+ "▀";
113
137
  }
114
- else if (top) {
115
- line += `${ansiForeground(topColor)}▀`;
138
+ else if (topBackground && !bottomBackground) {
139
+ line +=
140
+ ansiColor("38", bottom[0], bottom[1], bottom[2]) +
141
+ ansiColor("48", 255, 255, 255) +
142
+ "▄";
116
143
  }
117
144
  else {
118
- line += `${ansiForeground(bottomColor)}▄`;
145
+ line +=
146
+ ansiColor("38", top[0], top[1], top[2]) +
147
+ ansiColor("48", bottom[0], bottom[1], bottom[2]) +
148
+ "▀";
119
149
  }
120
150
  }
121
- lines.push(`${line}${colored ? RESET : ""}`);
151
+ lines.push(line + reset);
122
152
  }
123
153
  return lines.join("\n");
124
154
  }
155
+ /**
156
+ * Generate the QR with qr-code-styling itself, exactly like the WISA renderer.
157
+ * The SVG is then rasterized only to make the library's real rounded shapes
158
+ * displayable in a normal terminal; no QR modules are recreated by LynxShip.
159
+ */
160
+ export async function renderTerminalQr(data) {
161
+ const { QRCodeStyling, JSDOM, Resvg, PNG } = await loadDependencies();
162
+ const qrCode = new QRCodeStyling({
163
+ jsdom: JSDOM,
164
+ data,
165
+ ...LYNXSHIP_QR_STYLE,
166
+ });
167
+ const svg = await qrCode.getRawData("svg");
168
+ if (!svg)
169
+ throw new Error("qr-code-styling returned no SVG data");
170
+ const svgText = Buffer.isBuffer(svg)
171
+ ? svg.toString("utf8")
172
+ : new TextDecoder().decode(svg);
173
+ // jsdom serializes SVG references as url('#id'). Normalize only those
174
+ // references for resvg; the SVG generated by qr-code-styling is unchanged
175
+ // for browsers and for the file returned by the library.
176
+ const rasterSvg = svgText.replaceAll("url('#", "url(#").replaceAll("')", ")");
177
+ const png = new Resvg(rasterSvg).render().asPng();
178
+ const image = cropToQrContent(PNG.sync.read(Buffer.from(png)));
179
+ return renderRasterInTerminal(image);
180
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"web-build.d.ts","sourceRoot":"","sources":["../src/web-build.ts"],"names":[],"mappings":"AAGA,OAAO,EAAU,KAAK,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAE5D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAIhD,UAAU,eAAe;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,YAAY,CAAC;IACtB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,UAAU,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CACvD;AAgBD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAOrE;AAiCD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAczD;AAED,wBAAsB,eAAe,CACnC,GAAG,EAAE,QAAQ,EACb,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,QAAQ,CAAC,CAsDnB"}
1
+ {"version":3,"file":"web-build.d.ts","sourceRoot":"","sources":["../src/web-build.ts"],"names":[],"mappings":"AAGA,OAAO,EAAU,KAAK,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAE5D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAIhD,UAAU,eAAe;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,YAAY,CAAC;IACtB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,UAAU,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CACvD;AAgBD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAOrE;AAiCD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAczD;AAED,wBAAsB,eAAe,CACnC,GAAG,EAAE,QAAQ,EACb,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,QAAQ,CAAC,CAuDnB"}
package/dist/web-build.js CHANGED
@@ -76,6 +76,7 @@ export async function runRealWebBuild(job, options) {
76
76
  quiet: options.quiet,
77
77
  onOutput: options.onEvent,
78
78
  script,
79
+ miso: options.profile.miso,
79
80
  rspeedyArgs: script
80
81
  ? undefined
81
82
  : ["--environment", options.profile.web?.environment ?? "web"],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lynxship/cli",
3
- "version": "0.1.14",
3
+ "version": "0.1.16",
4
4
  "description": "Build, sign, store, submit and update LynxJS applications from the terminal.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -54,6 +54,10 @@
54
54
  "types": "./dist/autolink.d.ts",
55
55
  "import": "./dist/autolink.js"
56
56
  },
57
+ "./frameworks": {
58
+ "types": "./dist/frameworks.d.ts",
59
+ "import": "./dist/frameworks.js"
60
+ },
57
61
  "./ui/*": {
58
62
  "types": "./dist/ui/*.d.ts",
59
63
  "import": "./dist/ui/*.js"
@@ -67,17 +71,20 @@
67
71
  "chalk": "5.4.1",
68
72
  "cli-progress": "3.12.0",
69
73
  "ora": "8.2.0",
70
- "qrcode-generator": "1.4.4",
71
74
  "qrcode-terminal": "0.12.0",
72
75
  "qr-code-styling": "1.9.2",
76
+ "@resvg/resvg-js": "2.6.2",
77
+ "jsdom": "29.1.1",
78
+ "pngjs": "7.0.0",
73
79
  "@lynxship/contracts": "0.1.0",
74
80
  "@lynxship/build-orchestrator": "0.1.0",
75
- "@lynxship/db": "0.1.0",
76
81
  "@lynxship/submit": "0.1.0",
77
- "@lynxship/signing": "0.1.0"
82
+ "@lynxship/signing": "0.1.0",
83
+ "@lynxship/db": "0.1.0"
78
84
  },
79
85
  "devDependencies": {
80
86
  "@types/cli-progress": "3.11.6",
87
+ "@types/pngjs": "6.0.4",
81
88
  "@types/qrcode-terminal": "0.12.2"
82
89
  },
83
90
  "scripts": {