@geekmidas/cli 10.0.0-alpha.34 → 10.0.0-alpha.36

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 (29) hide show
  1. package/dist/{config-DMpzmsQL.cjs → config-DgAHy8nY.cjs} +9 -5
  2. package/dist/config-DgAHy8nY.cjs.map +1 -0
  3. package/dist/{config-Du8AQJRK.mjs → config-Dss1WwtH.mjs} +9 -5
  4. package/dist/config-Dss1WwtH.mjs.map +1 -0
  5. package/dist/config.cjs +1 -1
  6. package/dist/config.mjs +1 -1
  7. package/dist/{github-DN-92LWP.mjs → github-C99BMxf0.mjs} +2 -2
  8. package/dist/{github-DN-92LWP.mjs.map → github-C99BMxf0.mjs.map} +1 -1
  9. package/dist/{github-JhvrmjQp.cjs → github-tZmMZ7td.cjs} +2 -2
  10. package/dist/{github-JhvrmjQp.cjs.map → github-tZmMZ7td.cjs.map} +1 -1
  11. package/dist/index.cjs +209 -87
  12. package/dist/index.cjs.map +1 -1
  13. package/dist/index.mjs +210 -88
  14. package/dist/index.mjs.map +1 -1
  15. package/dist/{migrate-u1oRHz6K.cjs → migrate-BsW4xBEO.cjs} +3 -3
  16. package/dist/{migrate-u1oRHz6K.cjs.map → migrate-BsW4xBEO.cjs.map} +1 -1
  17. package/dist/{migrate-BbhXxQer.mjs → migrate-CT8b3nt_.mjs} +3 -3
  18. package/dist/{migrate-BbhXxQer.mjs.map → migrate-CT8b3nt_.mjs.map} +1 -1
  19. package/dist/openapi.cjs +1 -1
  20. package/dist/openapi.mjs +1 -1
  21. package/dist/{workspace-LKFnEBX8.cjs → workspace-C0X8xSXV.cjs} +89 -15
  22. package/dist/workspace-C0X8xSXV.cjs.map +1 -0
  23. package/dist/{workspace-DB2YHgOl.mjs → workspace-EmXwO2qM.mjs} +90 -16
  24. package/dist/workspace-EmXwO2qM.mjs.map +1 -0
  25. package/package.json +14 -14
  26. package/dist/config-DMpzmsQL.cjs.map +0 -1
  27. package/dist/config-Du8AQJRK.mjs.map +0 -1
  28. package/dist/workspace-DB2YHgOl.mjs.map +0 -1
  29. package/dist/workspace-LKFnEBX8.cjs.map +0 -1
@@ -1,10 +1,11 @@
1
1
  const require_rolldown_runtime = require("./rolldown-runtime-C0BPl7ul.cjs");
2
2
  const require_discover = require("./discover-CroTIm0K.cjs");
3
- const require_config = require("./config-DMpzmsQL.cjs");
3
+ const require_config = require("./config-DgAHy8nY.cjs");
4
4
  const require_workspace = require("./workspace-CAJk6UmC.cjs");
5
5
  const require_storage = require("./storage-C2JC31V9.cjs");
6
6
  let node_fs = require("node:fs");
7
7
  let node_fs_promises = require("node:fs/promises");
8
+ let node_os = require("node:os");
8
9
  let node_path = require("node:path");
9
10
  let node_child_process = require("node:child_process");
10
11
  let _geekmidas_manifest = require("@geekmidas/manifest");
@@ -210,6 +211,7 @@ async function prepareEntryCredentials(options) {
210
211
  const stage = options.stage ?? workspace?.stages.local;
211
212
  const credentials = stage ? await loadSecretsForApp(secretsRoot, stage, appName) : {};
212
213
  credentials.PORT = String(resolvedPort);
214
+ if (appInfo?.app.type === "mobile") credentials.RCT_METRO_PORT = String(resolvedPort);
213
215
  const declaredKeys = [];
214
216
  if (workspace) {
215
217
  const { reconcileWorkspace } = await Promise.resolve().then(() => workspace_exports);
@@ -689,7 +691,8 @@ const CONTAINERLESS = {
689
691
  secret: true,
690
692
  credential: true,
691
693
  "rest-api": true,
692
- site: true
694
+ site: true,
695
+ "mobile-app": true
693
696
  };
694
697
  /**
695
698
  * Containers that cannot run alone.
@@ -709,7 +712,8 @@ const REQUIRES = { "redis-http": ["redis"] };
709
712
  const ROLES = {
710
713
  queue: "publisherConnectionString",
711
714
  topic: "publisherConnectionString",
712
- credential: "credential"
715
+ credential: "credential",
716
+ "mobile-app": "scheme"
713
717
  };
714
718
  /** The kinds whose container is the events backend's rather than their own. */
715
719
  const EVENT_KINDS = {
@@ -820,6 +824,10 @@ function planFor(manifest, stage, order, options = {}) {
820
824
  provisions: PROVISIONS[declaration.kind] === true,
821
825
  ...declaration.kind === "rest-api" ? { callers: (0, _geekmidas_manifest.dependentsOf)(manifest, id) } : {},
822
826
  ...declaration.kind === "site" ? { publicEnv: (0, _geekmidas_manifest.publicEnvFor)(declaration, manifest) } : {},
827
+ ...declaration.kind === "mobile-app" ? {
828
+ publicEnv: (0, _geekmidas_manifest.publicEnvFor)(declaration, manifest),
829
+ ...declaration.scheme ? { scheme: declaration.scheme } : {}
830
+ } : {},
823
831
  ...declaration.kind === "file-server" && declaration.open?.length ? { open: declaration.open } : {},
824
832
  ..."of" in declaration ? { of: declaration.of } : {},
825
833
  ..."schema" in declaration && declaration.schema ? { schema: declaration.schema } : {},
@@ -969,7 +977,12 @@ function envFor(plan, options) {
969
977
  }
970
978
  for (const resource of plan.resources) {
971
979
  const url = resolved[resource.id];
972
- if (resource.kind === "rest-api") Object.assign(env, surfaceEnv(resource, url, resolved));
980
+ if (resource.kind === "rest-api") Object.assign(env, surfaceEnv(resource, url, resolved, {
981
+ plan,
982
+ ...options.addresses ? { addresses: options.addresses } : {},
983
+ ...options.lanAddress ? { lanAddress: options.lanAddress } : {},
984
+ ...options.metroPorts ? { metroPorts: options.metroPorts } : {}
985
+ }));
973
986
  if (url) env[resource.envKey] = url;
974
987
  if (isDatabase(resource.kind) && resource.roles !== false) {
975
988
  const owner = ownerUrl(resource, plan, options.ports, options.project ?? "");
@@ -981,7 +994,7 @@ function envFor(plan, options) {
981
994
  if (inbox !== void 0) env[(0, _geekmidas_manifest.provideKey)(resource.id, "inboxUrl")] = `http://${LOCAL_HOST}:${inbox}`;
982
995
  }
983
996
  }
984
- Object.assign(env, publicEnv(plan, env));
997
+ Object.assign(env, publicEnv(plan, env, options.addresses));
985
998
  Object.assign(env, brokerEnv(plan, options.ports));
986
999
  if (plan.resources.some((r) => r.kind === "objects" && env[r.envKey])) Object.assign(env, STORAGE_CREDENTIALS);
987
1000
  return env;
@@ -999,10 +1012,11 @@ function envFor(plan, options) {
999
1012
  * against `http:///`, where a missing variable fails at build with the name of
1000
1013
  * the thing that is missing.
1001
1014
  */
1002
- function publicEnv(plan, resolved) {
1015
+ function publicEnv(plan, resolved, addresses = {}) {
1003
1016
  const env = {};
1017
+ const owners = new Map(plan.resources.map((r) => [r.envKey, r.id]));
1004
1018
  for (const resource of plan.resources) for (const [key, source] of Object.entries(resource.publicEnv ?? {})) {
1005
- const value = resolved[source];
1019
+ const value = (resource.kind === "mobile-app" ? addresses[owners.get(source) ?? ""] : void 0) ?? resolved[source];
1006
1020
  if (value) env[key] = value;
1007
1021
  }
1008
1022
  return env;
@@ -1022,13 +1036,26 @@ function publicEnv(plan, resolved) {
1022
1036
  * means it is not running in this stage, and keys belong with the address they
1023
1037
  * describe.
1024
1038
  */
1025
- function surfaceEnv(resource, url, resolved = {}) {
1039
+ function surfaceEnv(resource, url, resolved = {}, local = {}) {
1026
1040
  if (!url) return {};
1027
1041
  const origins = [...new Set((resource.callers ?? []).map((caller) => resolved[caller]).filter((address) => Boolean(address)).map(originOf).filter((origin) => Boolean(origin)))].sort();
1028
1042
  const domain = (0, _geekmidas_manifest.cookieDomain)([url, ...origins]);
1043
+ const mobile = (resource.callers ?? []).map((caller) => local.plan?.resources.find((r) => r.id === caller && r.kind === "mobile-app")).filter((caller) => Boolean(caller));
1044
+ const hosts = [...local.lanAddress ? [local.lanAddress] : [], LOCAL_HOST];
1045
+ const schemes = mobile.flatMap((caller) => {
1046
+ const scheme = resolved[caller.id];
1047
+ const port = local.metroPorts?.[caller.id];
1048
+ return scheme ? (0, _geekmidas_manifest.mobileOrigins)(scheme, {
1049
+ hosts,
1050
+ ...port ? { port } : {}
1051
+ }) : [];
1052
+ });
1053
+ const own = local.addresses?.[resource.id];
1054
+ const device = mobile.length > 0 && local.lanAddress && own ? own.replace(/\/\/(localhost|127\.0\.0\.1)(?=[:/]|$)/, `//${local.lanAddress}`) : void 0;
1029
1055
  return {
1030
- [(0, _geekmidas_manifest.provideKey)(resource.id, "trustedOrigins")]: origins.join(","),
1031
- ...domain ? { [(0, _geekmidas_manifest.provideKey)(resource.id, "cookieDomain")]: domain } : {}
1056
+ [(0, _geekmidas_manifest.provideKey)(resource.id, "trustedOrigins")]: [...origins, ...new Set(schemes)].join(","),
1057
+ ...domain ? { [(0, _geekmidas_manifest.provideKey)(resource.id, "cookieDomain")]: domain } : {},
1058
+ ...device ? { [(0, _geekmidas_manifest.provideKey)(resource.id, "deviceUrl")]: device } : {}
1032
1059
  };
1033
1060
  }
1034
1061
  /** An address reduced to the origin a browser compares against. */
@@ -1108,6 +1135,7 @@ function workerBroker(plan, ports) {
1108
1135
  */
1109
1136
  function urlFor(resource, plan, ports, project, addresses = {}) {
1110
1137
  if (resource.kind === "secret") return localSecret(project, plan, resource);
1138
+ if (resource.kind === "mobile-app") return (0, _geekmidas_manifest.schemeBase)(project, resource.scheme);
1111
1139
  if (resource.kind === "credential") return void 0;
1112
1140
  if (resource.kind === "rest-api" || resource.kind === "site") {
1113
1141
  const address = addresses[resource.id];
@@ -1352,7 +1380,7 @@ function inNetworkEnv(workspace, manifest) {
1352
1380
  * port.
1353
1381
  */
1354
1382
  function appEnvKeys(manifest, appName, runnables = {}) {
1355
- const entry = Object.entries(manifest).find(([id, d]) => (d.kind === "rest-api" || d.kind === "site") && require_config.appKey(id) === appName);
1383
+ const entry = Object.entries(manifest).find(([id, d]) => (d.kind === "rest-api" || d.kind === "site" || d.kind === "mobile-app") && require_config.appKey(id) === appName);
1356
1384
  if (!entry) return void 0;
1357
1385
  const [id, declaration] = entry;
1358
1386
  const keys = /* @__PURE__ */ new Set([
@@ -1381,7 +1409,7 @@ function appEnvKeys(manifest, appName, runnables = {}) {
1381
1409
  keys.add("AWS_REGION");
1382
1410
  }
1383
1411
  }
1384
- if (declaration.kind === "site") for (const key of Object.keys((0, _geekmidas_manifest.publicEnvFor)(declaration, manifest))) keys.add(key);
1412
+ if (declaration.kind === "site" || declaration.kind === "mobile-app") for (const key of Object.keys((0, _geekmidas_manifest.publicEnvFor)(declaration, manifest))) keys.add(key);
1385
1413
  return keys;
1386
1414
  }
1387
1415
  /** One compose service per app the workspace runs, behind the apps profile. */
@@ -1881,6 +1909,30 @@ const dockerCli = {
1881
1909
  }
1882
1910
  };
1883
1911
  //#endregion
1912
+ //#region src/reconcile/lan.ts
1913
+ /**
1914
+ * This machine's address on the local network — what a phone running the app
1915
+ * reaches it on.
1916
+ */
1917
+ /**
1918
+ * The first private IPv4 address of an interface that is up and not loopback.
1919
+ *
1920
+ * Private ranges only (`10/8`, `172.16/12`, `192.168/16`): a public address
1921
+ * would mean listening for a phone on the open internet, and a link-local one
1922
+ * (`169.254/16`) is what an interface has when it found no network at all.
1923
+ * Undefined when there is none — a laptop on no network still runs, its mobile
1924
+ * app reaching only a simulator.
1925
+ */
1926
+ function lanAddress(interfaces = (0, node_os.networkInterfaces)()) {
1927
+ for (const entries of Object.values(interfaces)) for (const entry of entries ?? []) {
1928
+ if (entry.family !== "IPv4" || entry.internal) continue;
1929
+ if (isPrivate(entry.address)) return entry.address;
1930
+ }
1931
+ }
1932
+ function isPrivate(address) {
1933
+ return /^10\./.test(address) || /^192\.168\./.test(address) || /^172\.(1[6-9]|2\d|3[01])\./.test(address);
1934
+ }
1935
+ //#endregion
1884
1936
  //#region src/reconcile/ports.ts
1885
1937
  /**
1886
1938
  * Port allocation — why two projects can run at once.
@@ -2431,13 +2483,19 @@ async function reconcile(options) {
2431
2483
  ...options.apps ? { apps: options.apps(plan.containers) } : {}
2432
2484
  });
2433
2485
  const caddyfile = toCaddyfile(sitesFor(plan, project, options.addresses));
2434
- const hash = planHash(plan, compose, { caddyfile });
2486
+ const hash = planHash(plan, compose, {
2487
+ caddyfile,
2488
+ postgres: postgresStatements(plan, project).map((statement) => statement.create).join(";\n")
2489
+ });
2435
2490
  const addresses = addressesFor(plan.containers, ports);
2491
+ const lan = plan.resources.some((r) => r.kind === "mobile-app") ? options.lanAddress === void 0 ? lanAddress() : options.lanAddress ?? void 0 : void 0;
2436
2492
  const env = envFor(plan, {
2437
2493
  ports,
2438
2494
  project,
2439
2495
  ...options.mailFrom ? { mailFrom: options.mailFrom } : {},
2440
- ...options.addresses ? { addresses: options.addresses } : {}
2496
+ ...options.addresses ? { addresses: options.addresses } : {},
2497
+ ...lan ? { lanAddress: lan } : {},
2498
+ ...options.metroPorts ? { metroPorts: options.metroPorts } : {}
2441
2499
  });
2442
2500
  if (plan.containers.includes("caddy")) env.NODE_EXTRA_CA_CERTS = (0, node_path.join)(root, LOCAL_CA_PATH);
2443
2501
  const result = {
@@ -2564,6 +2622,7 @@ var workspace_exports = /* @__PURE__ */ require_rolldown_runtime.__exportAll({
2564
2622
  backendsOf: () => backendsOf,
2565
2623
  constructGlobs: () => constructGlobs,
2566
2624
  derivedContainers: () => derivedContainers,
2625
+ metroPorts: () => metroPorts,
2567
2626
  reconcileWorkspace: () => reconcileWorkspace,
2568
2627
  surfaceAddresses: () => surfaceAddresses
2569
2628
  });
@@ -2627,6 +2686,7 @@ async function reconcileWorkspace(workspace, options) {
2627
2686
  ...backendsOf(workspace),
2628
2687
  saved: await loadPortState(workspace.root),
2629
2688
  addresses: surfaceAddresses(workspace, manifest),
2689
+ metroPorts: metroPorts(workspace, manifest),
2630
2690
  apps: (containers) => appServices(workspace, manifest, containers, runnables),
2631
2691
  ...options.start === void 0 ? {} : { start: options.start }
2632
2692
  });
@@ -2634,6 +2694,20 @@ async function reconcileWorkspace(workspace, options) {
2634
2694
  return result;
2635
2695
  }
2636
2696
  /**
2697
+ * Where each mobile app's Metro runs: its app's port, which `gkm exec` hands
2698
+ * Expo as `RCT_METRO_PORT`. Kept apart from the surfaces' addresses — a phone
2699
+ * reaches Metro by `exp://`, not through the edge.
2700
+ */
2701
+ function metroPorts(workspace, manifest) {
2702
+ const ports = {};
2703
+ for (const [id, declaration] of Object.entries(manifest)) {
2704
+ if (declaration.kind !== "mobile-app") continue;
2705
+ const port = workspace.apps[require_config.appKey(id)]?.port;
2706
+ if (port) ports[id] = port;
2707
+ }
2708
+ return ports;
2709
+ }
2710
+ /**
2637
2711
  * Where each declared surface and site answers locally.
2638
2712
  *
2639
2713
  * The addresses come from the ports the workspace already assigns; which
@@ -2791,4 +2865,4 @@ Object.defineProperty(exports, "workspace_exports", {
2791
2865
  }
2792
2866
  });
2793
2867
 
2794
- //# sourceMappingURL=workspace-LKFnEBX8.cjs.map
2868
+ //# sourceMappingURL=workspace-C0X8xSXV.cjs.map