lecodes-cli 2.2.6 → 2.2.8

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/dist/index.js CHANGED
@@ -52,7 +52,7 @@ var package_default;
52
52
  var init_package = __esm(() => {
53
53
  package_default = {
54
54
  name: "lecodes-cli",
55
- version: "2.2.6",
55
+ version: "2.2.8",
56
56
  dependencies: {
57
57
  "@letary/chisel": "^0.14.0",
58
58
  jimp: "^1.6.1",
@@ -76,7 +76,7 @@ var init_package = __esm(() => {
76
76
  },
77
77
  peerDependencies: {
78
78
  "lecodes-design": "^0.11.0",
79
- "lecodes-headless": "^2.2.6",
79
+ "lecodes-headless": "^2.2.8",
80
80
  "lecodes-3d-editor": "^0.9.0",
81
81
  "lecodes-assets": "^0.2.1",
82
82
  "heic-decode": "^2.1.0",
@@ -146,7 +146,7 @@ var init_package = __esm(() => {
146
146
  });
147
147
 
148
148
  // ../../sdk/src/version.ts
149
- var SDK_VERSION = "2.2.6", sdkMajor = (version) => {
149
+ var SDK_VERSION = "2.2.8", sdkMajor = (version) => {
150
150
  const m = version?.match(/^(\d+)\.\d+\.\d+/);
151
151
  return m ? Number(m[1]) : null;
152
152
  }, bundleSdkVersion = (head) => head.slice(0, 2000).match(/^\/\/ sdk: (\d+\.\d+\.\d+\S*)/m)?.[1] ?? null, bundleRunsOn = (bundleVersion, hostVersion) => sdkMajor(bundleVersion) !== null && sdkMajor(bundleVersion) === sdkMajor(hostVersion);
@@ -1703,7 +1703,9 @@ var init_chisel = __esm(() => {
1703
1703
  "NativeView",
1704
1704
  "CameraView",
1705
1705
  "MapView",
1706
- "QRScanner"
1706
+ "QRScanner",
1707
+ "WebView",
1708
+ "RTCVideoView"
1707
1709
  ],
1708
1710
  containers: ["UIColumn", "UIRow", "UIBox", "UIButton", "UIScrollable", "UIScreen", "UIWidget"],
1709
1711
  text: "UIText",
@@ -217963,7 +217965,19 @@ var FIRST_PARTY_PLUGINS = {
217963
217965
  "qr-scanner": "letary/lecodes-plugins",
217964
217966
  map: "letary/lecodes-plugins",
217965
217967
  geolocation: "letary/lecodes-plugins",
217966
- push: "letary/lecodes-plugins"
217968
+ push: "letary/lecodes-plugins",
217969
+ browser: "letary/lecodes-plugins"
217970
+ };
217971
+ var urlSchemeOf = (config) => {
217972
+ const scheme = config?.urlScheme;
217973
+ if (scheme === undefined)
217974
+ return;
217975
+ if (typeof scheme !== "string" || !/^[a-z][a-z0-9+.-]*$/.test(scheme)) {
217976
+ throw new CliError(`app.json "urlScheme" must be lowercase letters, digits, "+", "-" or ".", starting with a letter — got ${JSON.stringify(scheme)}.`);
217977
+ }
217978
+ if (scheme === "http" || scheme === "https")
217979
+ throw new CliError(`app.json "urlScheme" is the app's OWN scheme ("myshop" → myshop://…), not ${scheme}.`);
217980
+ return scheme;
217967
217981
  };
217968
217982
  var readAppConfigAt = (root) => readJson2(join18(root, "app.json"));
217969
217983
  var DESKTOP_DEFAULT_WINDOW = { width: 1280, height: 720 };
@@ -218887,11 +218901,30 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
218887
218901
  LeCodesAppHooks.coldStartNotification(response)
218888
218902
  }
218889
218903
 
218904
+ let controller = ViewController()
218905
+ // Cold-started by a link (app.json "urlScheme", or a universal link): the engine reads it as
218906
+ // app.launchUrl — set BEFORE the view loads, which boots the JS world.
218907
+ if let url = connectionOptions.urlContexts.first?.url
218908
+ ?? connectionOptions.userActivities.first(where: { $0.activityType == NSUserActivityTypeBrowsingWeb })?.webpageURL {
218909
+ controller.engine.setLaunchUrl(url.absoluteString)
218910
+ }
218890
218911
  let window = UIWindow(windowScene: windowScene)
218891
- window.rootViewController = ViewController()
218912
+ window.rootViewController = controller
218892
218913
  self.window = window
218893
218914
  window.makeKeyAndVisible()
218894
218915
  }
218916
+
218917
+ // A link while the app runs: the "url" app event (an OAuth redirect back into the app, a
218918
+ // checkout's return — what Browser.open(url, { returnUrl }) resolves on).
218919
+ func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
218920
+ guard let url = URLContexts.first?.url else { return }
218921
+ (window?.rootViewController as? ViewController)?.engine.emitUrl(url.absoluteString)
218922
+ }
218923
+
218924
+ func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
218925
+ guard userActivity.activityType == NSUserActivityTypeBrowsingWeb, let url = userActivity.webpageURL else { return }
218926
+ (window?.rootViewController as? ViewController)?.engine.emitUrl(url.absoluteString)
218927
+ }
218895
218928
  }
218896
218929
  `;
218897
218930
  var VIEW_CONTROLLER = `//
@@ -218952,9 +218985,22 @@ ${body}</dict>
218952
218985
  </plist>
218953
218986
  `;
218954
218987
  };
218955
- var infoPlistTemplate = (pluginKeys, updateUrl) => {
218988
+ var infoPlistTemplate = (pluginKeys, updateUrl, urlScheme) => {
218956
218989
  const xml = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
218957
218990
  const keys = Object.keys(pluginKeys).sort();
218991
+ const schemeBlock = !urlScheme ? "" : `
218992
+ <!-- lecodes:urlScheme — app.json "urlScheme": links the app opens on (the "url" app event) -->
218993
+ <key>CFBundleURLTypes</key>
218994
+ <array>
218995
+ <dict>
218996
+ <key>CFBundleURLName</key>
218997
+ <string>${xml(urlScheme)}</string>
218998
+ <key>CFBundleURLSchemes</key>
218999
+ <array>
219000
+ <string>${xml(urlScheme)}</string>
219001
+ </array>
219002
+ </dict>
219003
+ </array>`;
218958
219004
  const pluginBlock = keys.length === 0 ? "" : `
218959
219005
  <!-- lecodes:plugins — merged from installed plugin manifests by \`lecodes app sync\` -->
218960
219006
  ${keys.map((k) => ` <key>${k}</key>
@@ -218972,7 +219018,7 @@ ${keys.map((k) => ` <key>${k}</key>
218972
219018
  so App Store Connect skips the "Missing Compliance" prompt on every build.
218973
219019
  Flip to <true/> only if the app adds its own non-exempt cryptography. -->
218974
219020
  <key>ITSAppUsesNonExemptEncryption</key>
218975
- <false/>
219021
+ <false/>${schemeBlock}
218976
219022
  <key>UIApplicationSceneManifest</key>
218977
219023
  <dict>
218978
219024
  <key>UIApplicationSupportsMultipleScenes</key>
@@ -220359,7 +220405,16 @@ var APP_PROGUARD_RULES = `# R8 rules for app-local code (yours — \`lecodes app
220359
220405
  # reflection, e.g.
220360
220406
  # -keep class com.example.myapp.MyBridge { *; }
220361
220407
  `;
220362
- var appManifestTemplate = () => `<?xml version="1.0" encoding="utf-8"?>
220408
+ var urlSchemeFilter = (urlScheme) => !urlScheme ? "" : `
220409
+ <!-- lecodes:urlScheme — app.json "urlScheme": links the app opens on (the "url" app event) -->
220410
+ <intent-filter>
220411
+ <action android:name="android.intent.action.VIEW" />
220412
+ <category android:name="android.intent.category.DEFAULT" />
220413
+ <category android:name="android.intent.category.BROWSABLE" />
220414
+ <data android:scheme="${urlScheme}" />
220415
+ </intent-filter>
220416
+ <!-- /lecodes:urlScheme -->`;
220417
+ var appManifestTemplate = (urlScheme) => `<?xml version="1.0" encoding="utf-8"?>
220363
220418
  <!-- Generated by \`lecodes app init\` — yours to edit. Sync leaves it alone, except that with
220364
220419
  app.json "icon" set it restamps the android:icon VALUE to the generated @mipmap set.
220365
220420
  Plugin permissions/services arrive through the plugin modules' own manifests via the AGP
@@ -220384,7 +220439,7 @@ var appManifestTemplate = () => `<?xml version="1.0" encoding="utf-8"?>
220384
220439
  <intent-filter>
220385
220440
  <action android:name="android.intent.action.MAIN" />
220386
220441
  <category android:name="android.intent.category.LAUNCHER" />
220387
- </intent-filter>
220442
+ </intent-filter>${urlSchemeFilter(urlScheme)}
220388
220443
  </activity>
220389
220444
  </application>
220390
220445
 
@@ -221010,7 +221065,7 @@ var initAndroid = async (opts) => {
221010
221065
  write("gradle/wrapper/gradle-wrapper.jar", wrapperJar);
221011
221066
  write("app/build.gradle.kts", appBuildGradleTemplate(identity, config.android.minify ?? true));
221012
221067
  write("app/proguard-rules.pro", APP_PROGUARD_RULES);
221013
- write("app/src/main/AndroidManifest.xml", appManifestTemplate());
221068
+ write("app/src/main/AndroidManifest.xml", appManifestTemplate(urlSchemeOf(config)));
221014
221069
  write(`app/src/main/java/${pkgPath}/MainActivity.kt`, mainActivityTemplate(identity.applicationId));
221015
221070
  write(`app/src/main/java/${pkgPath}/AppViewModel.kt`, appViewModelTemplate(identity.applicationId));
221016
221071
  write("app/src/main/res/values/themes.xml", themesXmlTemplate());
@@ -221188,10 +221243,36 @@ var syncAndroid = async (root, config, prior) => {
221188
221243
  await writeAndroidIcons(androidDir, await loadIconMaster(root, config));
221189
221244
  restampManifestIcon(androidDir);
221190
221245
  }
221246
+ restampManifestUrlScheme(androidDir, urlSchemeOf(config));
221191
221247
  log("");
221192
221248
  success(`Android shell synced: app.js + ${runtimePlugins.length} plugin(s), SDK ${sdkVersion}, v${config.version ?? "1.0.0"} (${config.android?.versionCode ?? 1}).`);
221193
221249
  note("Build & run from Android Studio (`lecodes app open android`) — user files in android/app/ were not touched.");
221194
221250
  };
221251
+ var restampManifestUrlScheme = (androidDir, urlScheme) => {
221252
+ const path2 = join21(androidDir, "app", "src", "main", "AndroidManifest.xml");
221253
+ if (!existsSync19(path2))
221254
+ return;
221255
+ const content = readFileSync18(path2, "utf8");
221256
+ const tagged = /\n[ \t]*<!-- lecodes:urlScheme[\s\S]*?<!-- \/lecodes:urlScheme -->/;
221257
+ const block = urlSchemeFilter(urlScheme);
221258
+ let next;
221259
+ if (tagged.test(content))
221260
+ next = content.replace(tagged, block);
221261
+ else if (!urlScheme)
221262
+ return;
221263
+ else {
221264
+ const launcher = /(<category android:name="android\.intent\.category\.LAUNCHER" \/>\s*<\/intent-filter>)/;
221265
+ if (!launcher.test(content)) {
221266
+ warn(`couldn't find the launcher activity in app/src/main/AndroidManifest.xml — add an intent-filter for the "${urlScheme}" scheme yourself.`);
221267
+ return;
221268
+ }
221269
+ next = content.replace(launcher, `$1${block}`);
221270
+ }
221271
+ if (next === content)
221272
+ return;
221273
+ writeFileSync14(path2, next);
221274
+ log(`${c.green("updated")} android/app/src/main/AndroidManifest.xml: urlScheme ${urlScheme ? `→ ${urlScheme}://` : "removed"}`);
221275
+ };
221195
221276
  var restampAppBuildGradle = (androidDir, config, gradlePlugins) => {
221196
221277
  const path2 = join21(androidDir, "app", "build.gradle.kts");
221197
221278
  if (!existsSync19(path2)) {
@@ -222692,7 +222773,7 @@ var buildDesktop = async (flags) => {
222692
222773
  localShaders: true,
222693
222774
  desktopRenderer: renderer
222694
222775
  });
222695
- const wanted = new Set([...js.matchAll(/fetchLocal\("([^"]+)"\)/g)].map((m) => m[1]));
222776
+ const wanted = new Set([...js.matchAll(/(?:fetchLocal|_creatorFetch\.local)\("([^"]+)"\)/g)].map((m) => m[1]));
222696
222777
  const shipped = resources.filter((r2) => !r2.remote && (r2.shader || wanted.has(r2.name)));
222697
222778
  const byName = new Map;
222698
222779
  for (const r2 of resources) {
@@ -222991,7 +223072,7 @@ var initIos = async (opts) => {
222991
223072
  write("App/AppDelegate.swift", APP_DELEGATE);
222992
223073
  write("App/SceneDelegate.swift", SCENE_DELEGATE);
222993
223074
  write("App/ViewController.swift", VIEW_CONTROLLER);
222994
- write("App/Info.plist", infoPlistTemplate({}));
223075
+ write("App/Info.plist", infoPlistTemplate({}, undefined, urlSchemeOf(config)));
222995
223076
  write("App/App.entitlements", entitlementsTemplate({}));
222996
223077
  write("App/Base.lproj/LaunchScreen.storyboard", LAUNCH_STORYBOARD);
222997
223078
  write("App/Assets.xcassets/Contents.json", ASSETS_ROOT_JSON);
@@ -223178,7 +223259,7 @@ var syncIos = async (root, config) => {
223178
223259
  for (const [key, values] of Object.entries(infoValues))
223179
223260
  mergedKeys[key] = [...new Set(values)].join(" ");
223180
223261
  const updateUrl = resolveUpdateUrl(config.update, root, sdkVersion);
223181
- writeFileSync18(join25(iosDir, "App", "Info.plist"), infoPlistTemplate(mergedKeys, updateUrl));
223262
+ writeFileSync18(join25(iosDir, "App", "Info.plist"), infoPlistTemplate(mergedKeys, updateUrl, urlSchemeOf(config)));
223182
223263
  if (updateUrl)
223183
223264
  log(`${c.green("update")} OTA source ${updateUrl} — checked natively at launch, new code applies next launch${config.update === undefined ? c.dim(' ("update": false in app.json turns it off)') : ""}`);
223184
223265
  else if (config.update === false)
@@ -223240,7 +223321,7 @@ var sync2 = defineCommand({
223240
223321
  usage: "[ios|android]",
223241
223322
  description: `Compiles the bundle and embeds the assets it references (ios/App/Resources/ resp. the android assets/; offline by default, also for server projects — app.json "resources": {"remote": [globs]} keeps heavy assets streaming), picks the SDK variant from the bundle's engine use, vendors the app.json plugins (pinned in plugins.lock.json) and regenerates the CLI-owned shell files. User files (ios/App/*.swift, android/app/) are never touched.
223242
223323
 
223243
- Native OTA JS updates at launch (both shells; the current launch runs the newest LOCAL bundle, a download applies on the next): on by default for a pushed project (its bundle on the platform, the launcher's own URL), app.json "update": {"url": …} for custom hosting, "update": false turns it off. A download runs only when it is of the shell's SDK major and not older than the bundle running. app.json "icon" (a square ≥1024px image) renders the app icons every sync ("iconBackground": the adaptive / flatten backdrop). No platform = every shell that exists; a dual-shell project compiles once.`,
223324
+ Native OTA JS updates at launch (both shells; the current launch runs the newest LOCAL bundle, a download applies on the next): on by default for a pushed project (its bundle on the platform, the launcher's own URL), app.json "update": {"url": …} for custom hosting, "update": false turns it off. A download runs only when it is of the shell's SDK major and not older than the bundle running. app.json "icon" (a square ≥1024px image) renders the app icons every sync ("iconBackground": the adaptive / flatten backdrop). app.json "urlScheme" ("myshop") is the scheme the shells open on: iOS CFBundleURLTypes, Android a BROWSABLE intent-filter — a link arrives as app.launchUrl / the "url" event (the return of Browser.open with a returnUrl). No platform = every shell that exists; a dual-shell project compiles once.`,
223244
223325
  examples: ["lecodes app sync", "lecodes app sync android"],
223245
223326
  run: ({ args }) => runSync(args[0])
223246
223327
  });
package/package.json CHANGED
@@ -1,22 +1,22 @@
1
1
  {
2
2
  "name": "lecodes-cli",
3
- "version": "2.2.6",
3
+ "version": "2.2.8",
4
4
  "dependencies": {
5
5
  "@letary/chisel": "^0.14.0",
6
6
  "jimp": "^1.6.1",
7
- "lecodes-sdk": "2.2.6"
7
+ "lecodes-sdk": "2.2.8"
8
8
  },
9
9
  "devDependencies": {
10
10
  "lecodes-design": "https://storage.yandexcloud.net/le-codes/releases/packages/lecodes-design/lecodes-design-0.11.0.tgz",
11
- "lecodes-headless": "2.2.6",
11
+ "lecodes-headless": "2.2.8",
12
12
  "qrcode-terminal": "^0.12.0",
13
13
  "lecodes-3d-editor": "https://storage.yandexcloud.net/le-codes/releases/packages/lecodes-3d-editor/lecodes-3d-editor-0.9.0.tgz",
14
14
  "lecodes-assets": "0.2.1",
15
15
  "lecodes-creator-nav": "0.19.0",
16
- "lecodes-gl-lite": "2.2.6",
17
- "lecodes-web-frame": "2.2.6",
18
- "lecodes-web-player": "2.2.6",
19
- "lecodes-web-shared": "2.2.6",
16
+ "lecodes-gl-lite": "2.2.8",
17
+ "lecodes-web-frame": "2.2.8",
18
+ "lecodes-web-player": "2.2.8",
19
+ "lecodes-web-shared": "2.2.8",
20
20
  "lecodes-web-lite": "2.0.2",
21
21
  "heic-decode": "^2.1.0",
22
22
  "marcidb-embedded": "^0.15.0",
@@ -24,7 +24,7 @@
24
24
  },
25
25
  "peerDependencies": {
26
26
  "lecodes-design": "^0.11.0",
27
- "lecodes-headless": "^2.2.6",
27
+ "lecodes-headless": "^2.2.8",
28
28
  "lecodes-3d-editor": "^0.9.0",
29
29
  "lecodes-assets": "^0.2.1",
30
30
  "heic-decode": "^2.1.0",