@geekmidas/cli 10.0.0-alpha.34 → 10.0.0-alpha.35
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/{config-DMpzmsQL.cjs → config-DgAHy8nY.cjs} +9 -5
- package/dist/config-DgAHy8nY.cjs.map +1 -0
- package/dist/{config-Du8AQJRK.mjs → config-Dss1WwtH.mjs} +9 -5
- package/dist/config-Dss1WwtH.mjs.map +1 -0
- package/dist/config.cjs +1 -1
- package/dist/config.mjs +1 -1
- package/dist/{github-DN-92LWP.mjs → github-C99BMxf0.mjs} +2 -2
- package/dist/{github-DN-92LWP.mjs.map → github-C99BMxf0.mjs.map} +1 -1
- package/dist/{github-JhvrmjQp.cjs → github-tZmMZ7td.cjs} +2 -2
- package/dist/{github-JhvrmjQp.cjs.map → github-tZmMZ7td.cjs.map} +1 -1
- package/dist/index.cjs +205 -85
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +206 -86
- package/dist/index.mjs.map +1 -1
- package/dist/{migrate-BbhXxQer.mjs → migrate-C-D9xIbb.mjs} +3 -3
- package/dist/{migrate-BbhXxQer.mjs.map → migrate-C-D9xIbb.mjs.map} +1 -1
- package/dist/{migrate-u1oRHz6K.cjs → migrate-nlvT-GnY.cjs} +3 -3
- package/dist/{migrate-u1oRHz6K.cjs.map → migrate-nlvT-GnY.cjs.map} +1 -1
- package/dist/openapi.cjs +1 -1
- package/dist/openapi.mjs +1 -1
- package/dist/{workspace-DB2YHgOl.mjs → workspace-CIiGijNi.mjs} +90 -16
- package/dist/workspace-CIiGijNi.mjs.map +1 -0
- package/dist/{workspace-LKFnEBX8.cjs → workspace-CRjZkCTW.cjs} +89 -15
- package/dist/workspace-CRjZkCTW.cjs.map +1 -0
- package/package.json +14 -14
- package/dist/config-DMpzmsQL.cjs.map +0 -1
- package/dist/config-Du8AQJRK.mjs.map +0 -1
- package/dist/workspace-DB2YHgOl.mjs.map +0 -1
- package/dist/workspace-LKFnEBX8.cjs.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env -S npx tsx
|
|
2
2
|
import { n as __require, t as __exportAll } from "./rolldown-runtime-BhDjJH2R.mjs";
|
|
3
3
|
import { c as isPartitionedRoutes, i as ConstructGenerator, t as discover } from "./discover-CMxesskG.mjs";
|
|
4
|
-
import { a as loadConfig, c as parseModuleConfig, i as loadAppConfig, l as appKey, r as getAppNameFromCwd, s as loadWorkspaceConfig, u as derivedApps } from "./config-
|
|
4
|
+
import { a as loadConfig, c as parseModuleConfig, i as loadAppConfig, l as appKey, r as getAppNameFromCwd, s as loadWorkspaceConfig, u as derivedApps } from "./config-Dss1WwtH.mjs";
|
|
5
5
|
import { D as assertDeployedStage, E as TEST_STAGE, O as deployedProblems, T as InvalidStages, a as getAppBuildOrder, b as getDeployTargetError, k as stageProblems, m as stripPublicPrefix, o as getAppGkmConfig, p as getPublicEnvPrefix, t as allConstructGlobs, x as isDeployTargetSupported } from "./workspace-CsL76tpc.mjs";
|
|
6
6
|
import { c as getKeyPath, i as setCustomSecret, n as readStageSecrets, o as toEmbeddableSecrets, r as secretsExist, s as writeStageSecrets, t as maskPassword } from "./storage-BjTLjDDp.mjs";
|
|
7
|
-
import { _ as findAvailablePort, b as loadSecretsForApp, c as applyPostgres, d as cacheBackendFor, f as eventsBackendFor, g as createEntryWrapper, h as createCredentialsPreload, i as reconcileWorkspace, l as resourceName, m as storageBackendFor, n as constructGlobs, o as COMPOSE_PATH, p as providerOf, r as derivedContainers, s as LOCAL_CA_PATH, t as backendsOf, u as rootSite, v as isPortAvailable, x as prepareEntryCredentials, y as loadEnvFiles } from "./workspace-
|
|
7
|
+
import { _ as findAvailablePort, b as loadSecretsForApp, c as applyPostgres, d as cacheBackendFor, f as eventsBackendFor, g as createEntryWrapper, h as createCredentialsPreload, i as reconcileWorkspace, l as resourceName, m as storageBackendFor, n as constructGlobs, o as COMPOSE_PATH, p as providerOf, r as derivedContainers, s as LOCAL_CA_PATH, t as backendsOf, u as rootSite, v as isPortAvailable, x as prepareEntryCredentials, y as loadEnvFiles } from "./workspace-CIiGijNi.mjs";
|
|
8
8
|
import { n as EndpointGenerator, r as runtimeFor } from "./OpenApiTsGenerator-hDINScnz.mjs";
|
|
9
9
|
import { generateOpenApi, openapiCommand, resolveOpenApiConfig } from "./openapi.mjs";
|
|
10
10
|
import { a as writeTestHarness, i as TEST_MANIFEST_ENV, n as TEST_READY_FILE, o as cacheBackendsIn, s as driversFor, t as TEST_READY_ENV } from "./ready-D1BCiaYU.mjs";
|
|
@@ -19,7 +19,7 @@ import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
|
19
19
|
import { homedir } from "node:os";
|
|
20
20
|
import { basename, dirname, isAbsolute, join, parse, relative, resolve } from "node:path";
|
|
21
21
|
import { execFile, execSync, spawn } from "node:child_process";
|
|
22
|
-
import { cacheTable, canonicalId, cloudName, cookieDomain, dependentsOf, kebabCase, migrationFolder, provideKey, provisionOrder, scopedName, serviceKey } from "@geekmidas/manifest";
|
|
22
|
+
import { cacheTable, canonicalId, cloudName, cookieDomain, dependentsOf, kebabCase, migrationFolder, mobileOrigins, provideKey, provisionOrder, schemeBase, scopedName, serviceKey } from "@geekmidas/manifest";
|
|
23
23
|
import fg from "fast-glob";
|
|
24
24
|
import chokidar from "chokidar";
|
|
25
25
|
import { createCipheriv, createHash, randomBytes } from "node:crypto";
|
|
@@ -37,7 +37,7 @@ import { Subscriber } from "@geekmidas/constructs/subscribers";
|
|
|
37
37
|
import { Topic } from "@geekmidas/constructs/topic";
|
|
38
38
|
import { lookup } from "node:dns/promises";
|
|
39
39
|
//#region package.json
|
|
40
|
-
var version = "10.0.0-alpha.
|
|
40
|
+
var version = "10.0.0-alpha.35";
|
|
41
41
|
//#endregion
|
|
42
42
|
//#region src/auth/credentials.ts
|
|
43
43
|
/**
|
|
@@ -1087,9 +1087,9 @@ async function workspaceDevCommand(workspace, options) {
|
|
|
1087
1087
|
...reconciled.env
|
|
1088
1088
|
};
|
|
1089
1089
|
if (options.migrate || options.seed) {
|
|
1090
|
-
const { prepareDevDatabases } = await import("./migrate-
|
|
1090
|
+
const { prepareDevDatabases } = await import("./migrate-C-D9xIbb.mjs");
|
|
1091
1091
|
await prepareDevDatabases(workspace, reconciled.env, { seed: !!options.seed });
|
|
1092
|
-
} else await import("./migrate-
|
|
1092
|
+
} else await import("./migrate-C-D9xIbb.mjs").then(({ reportPendingMigrations }) => reportPendingMigrations(workspace, reconciled.env)).catch((error) => {
|
|
1093
1093
|
logger$10.log(`⚠️ Could not check for pending migrations: ${error instanceof Error ? error.message : String(error)}`);
|
|
1094
1094
|
});
|
|
1095
1095
|
const appUrls = {};
|
|
@@ -1119,6 +1119,13 @@ async function workspaceDevCommand(workspace, options) {
|
|
|
1119
1119
|
for (const appName of buildOrder) {
|
|
1120
1120
|
const app = workspace.apps[appName];
|
|
1121
1121
|
if (!app) continue;
|
|
1122
|
+
if (app.type === "mobile") {
|
|
1123
|
+
const own = reconciled.plan.resources.find((r) => r.kind === "mobile-app" && appKey(r.id) === appName);
|
|
1124
|
+
const scheme = own ? reconciled.env[own.envKey] : void 0;
|
|
1125
|
+
const device = Object.entries(reconciled.env).find(([key]) => key.endsWith("_DEVICE_URL"))?.[1];
|
|
1126
|
+
logger$10.log(` ${appName.padEnd(width)} ${scheme ? `${scheme}://` : "mobile"}${device ? ` — devices reach ${new URL(device).hostname}` : ""}`);
|
|
1127
|
+
continue;
|
|
1128
|
+
}
|
|
1122
1129
|
const local = `http://localhost:${app.port}`;
|
|
1123
1130
|
const url = Object.entries(appUrls).find(([id]) => appKey(id) === appName)?.[1];
|
|
1124
1131
|
logger$10.log(` ${appName.padEnd(width)} ${url ? `${url} -> ${local}` : local}`);
|
|
@@ -3313,9 +3320,13 @@ const PROVISIONERS = {
|
|
|
3313
3320
|
if (!url) throw new SurfaceHasNoAddress(declaration.id);
|
|
3314
3321
|
const origins = [...new Set(dependentsOf(context.manifest, declaration.id).map((caller) => context.addresses?.[caller]).filter((address) => Boolean(address)))].sort();
|
|
3315
3322
|
const domain = cookieDomain([url, ...origins]);
|
|
3323
|
+
const schemes = dependentsOf(context.manifest, declaration.id).flatMap((caller) => {
|
|
3324
|
+
const app = context.manifest[caller];
|
|
3325
|
+
return app?.kind === "mobile-app" ? mobileOrigins(schemeBase(context.project, app.scheme)) : [];
|
|
3326
|
+
});
|
|
3316
3327
|
return { provides: {
|
|
3317
3328
|
[provideKey(declaration.id, "url")]: url,
|
|
3318
|
-
[provideKey(declaration.id, "trustedOrigins")]: origins.join(","),
|
|
3329
|
+
[provideKey(declaration.id, "trustedOrigins")]: [...origins, ...schemes].join(","),
|
|
3319
3330
|
...domain ? { [provideKey(declaration.id, "cookieDomain")]: domain } : {}
|
|
3320
3331
|
} };
|
|
3321
3332
|
}
|
|
@@ -8000,11 +8011,11 @@ function routesGlob(structure) {
|
|
|
8000
8011
|
}
|
|
8001
8012
|
}
|
|
8002
8013
|
/**
|
|
8003
|
-
* Plain ids — `Database`, not `
|
|
8014
|
+
* Plain ids — `Database`, not `Shop`.
|
|
8004
8015
|
*
|
|
8005
8016
|
* The workspace `name` already scopes every physical name
|
|
8006
|
-
* (`production-
|
|
8007
|
-
* would say it twice: `production-
|
|
8017
|
+
* (`production-shop-database`), so an id that carried the project too
|
|
8018
|
+
* would say it twice: `production-shop-shop-cache`. Within a
|
|
8008
8019
|
* workspace the constructs are declared once, at its root, so there is no
|
|
8009
8020
|
* second app's `Database` to collide with.
|
|
8010
8021
|
*/
|
|
@@ -8730,30 +8741,32 @@ const TOOLCHAIN_VERSIONS = {
|
|
|
8730
8741
|
"@storybook/react-vite": "^10.6.0"
|
|
8731
8742
|
};
|
|
8732
8743
|
/**
|
|
8733
|
-
* The Expo app's packages, which track an Expo SDK release (
|
|
8734
|
-
* their own latest: SDK
|
|
8744
|
+
* The Expo app's packages, which track an Expo SDK release (57) rather than
|
|
8745
|
+
* their own latest: SDK 57 is tested against exactly these, React Native and
|
|
8735
8746
|
* React included, so they move when the SDK does.
|
|
8736
8747
|
*/
|
|
8737
8748
|
const EXPO_VERSIONS = {
|
|
8738
|
-
expo: "~
|
|
8739
|
-
"expo-constants": "~
|
|
8740
|
-
"expo-dev-client": "~
|
|
8741
|
-
"expo-
|
|
8742
|
-
"expo-
|
|
8743
|
-
"expo-
|
|
8744
|
-
"expo-
|
|
8745
|
-
"expo-
|
|
8746
|
-
|
|
8747
|
-
|
|
8748
|
-
"react-
|
|
8749
|
-
"react-native
|
|
8750
|
-
"react-native-
|
|
8751
|
-
"react-native-
|
|
8752
|
-
"react-native-
|
|
8749
|
+
expo: "~57.0.26",
|
|
8750
|
+
"expo-constants": "~57.0.20",
|
|
8751
|
+
"expo-dev-client": "~57.0.19",
|
|
8752
|
+
"expo-device": "~57.0.2",
|
|
8753
|
+
"expo-linking": "~57.0.11",
|
|
8754
|
+
"expo-router": "~57.0.24",
|
|
8755
|
+
"expo-splash-screen": "~57.0.9",
|
|
8756
|
+
"expo-status-bar": "~57.0.1",
|
|
8757
|
+
"expo-secure-store": "~57.0.4",
|
|
8758
|
+
react: "19.2.3",
|
|
8759
|
+
"react-dom": "19.2.3",
|
|
8760
|
+
"react-native": "0.86.3",
|
|
8761
|
+
"react-native-gesture-handler": "~2.32.0",
|
|
8762
|
+
"react-native-reanimated": "4.5.1",
|
|
8763
|
+
"react-native-worklets": "0.10.1",
|
|
8764
|
+
"react-native-safe-area-context": "~5.7.0",
|
|
8765
|
+
"react-native-screens": "~4.26.0",
|
|
8753
8766
|
"react-native-web": "~0.21.0",
|
|
8754
8767
|
"@react-navigation/native": "^7.1.0",
|
|
8755
8768
|
"@babel/core": "^7.25.0",
|
|
8756
|
-
"@types/react": "~19.
|
|
8769
|
+
"@types/react": "~19.2.2",
|
|
8757
8770
|
nativewind: "~4.2.0",
|
|
8758
8771
|
tailwindcss: "~3.4.0"
|
|
8759
8772
|
};
|
|
@@ -8785,26 +8798,26 @@ function loadPackageJson() {
|
|
|
8785
8798
|
* Run: pnpm --filter @geekmidas/cli sync-versions
|
|
8786
8799
|
*/
|
|
8787
8800
|
const GEEKMIDAS_VERSIONS = {
|
|
8788
|
-
"@geekmidas/audit": "~10.0.0-alpha.
|
|
8789
|
-
"@geekmidas/auth": "~10.0.0-alpha.
|
|
8790
|
-
"@geekmidas/cache": "~10.0.0-alpha.
|
|
8791
|
-
"@geekmidas/client": "~10.0.0-alpha.
|
|
8792
|
-
"@geekmidas/cloud": "~10.0.0-alpha.
|
|
8793
|
-
"@geekmidas/constructs": "~10.0.0-alpha.
|
|
8794
|
-
"@geekmidas/db": "~10.0.0-alpha.
|
|
8795
|
-
"@geekmidas/emailkit": "~10.0.0-alpha.
|
|
8796
|
-
"@geekmidas/envkit": "~10.0.0-alpha.
|
|
8797
|
-
"@geekmidas/errors": "~10.0.0-alpha.
|
|
8798
|
-
"@geekmidas/events": "~10.0.0-alpha.
|
|
8799
|
-
"@geekmidas/logger": "~10.0.0-alpha.
|
|
8800
|
-
"@geekmidas/manifest": "~10.0.0-alpha.
|
|
8801
|
-
"@geekmidas/rate-limit": "~10.0.0-alpha.
|
|
8802
|
-
"@geekmidas/schema": "~10.0.0-alpha.
|
|
8803
|
-
"@geekmidas/services": "~10.0.0-alpha.
|
|
8804
|
-
"@geekmidas/storage": "~10.0.0-alpha.
|
|
8805
|
-
"@geekmidas/studio": "~10.0.0-alpha.
|
|
8806
|
-
"@geekmidas/telescope": "~10.0.0-alpha.
|
|
8807
|
-
"@geekmidas/testkit": "~10.0.0-alpha.
|
|
8801
|
+
"@geekmidas/audit": "~10.0.0-alpha.35",
|
|
8802
|
+
"@geekmidas/auth": "~10.0.0-alpha.35",
|
|
8803
|
+
"@geekmidas/cache": "~10.0.0-alpha.35",
|
|
8804
|
+
"@geekmidas/client": "~10.0.0-alpha.35",
|
|
8805
|
+
"@geekmidas/cloud": "~10.0.0-alpha.35",
|
|
8806
|
+
"@geekmidas/constructs": "~10.0.0-alpha.35",
|
|
8807
|
+
"@geekmidas/db": "~10.0.0-alpha.35",
|
|
8808
|
+
"@geekmidas/emailkit": "~10.0.0-alpha.35",
|
|
8809
|
+
"@geekmidas/envkit": "~10.0.0-alpha.35",
|
|
8810
|
+
"@geekmidas/errors": "~10.0.0-alpha.35",
|
|
8811
|
+
"@geekmidas/events": "~10.0.0-alpha.35",
|
|
8812
|
+
"@geekmidas/logger": "~10.0.0-alpha.35",
|
|
8813
|
+
"@geekmidas/manifest": "~10.0.0-alpha.35",
|
|
8814
|
+
"@geekmidas/rate-limit": "~10.0.0-alpha.35",
|
|
8815
|
+
"@geekmidas/schema": "~10.0.0-alpha.35",
|
|
8816
|
+
"@geekmidas/services": "~10.0.0-alpha.35",
|
|
8817
|
+
"@geekmidas/storage": "~10.0.0-alpha.35",
|
|
8818
|
+
"@geekmidas/studio": "~10.0.0-alpha.35",
|
|
8819
|
+
"@geekmidas/telescope": "~10.0.0-alpha.35",
|
|
8820
|
+
"@geekmidas/testkit": "~10.0.0-alpha.35",
|
|
8808
8821
|
"@geekmidas/cli": `~${loadPackageJson().version}`
|
|
8809
8822
|
};
|
|
8810
8823
|
//#endregion
|
|
@@ -9526,8 +9539,7 @@ function generateExpoAppFiles(options) {
|
|
|
9526
9539
|
const apiPackage = `@${options.name}/api`;
|
|
9527
9540
|
const modelsPackage = `@${options.name}/models`;
|
|
9528
9541
|
const slug = options.name.toLowerCase().replace(/[^a-z0-9-]/g, "-");
|
|
9529
|
-
const scheme =
|
|
9530
|
-
const bundleId = `com.${scheme}.app`;
|
|
9542
|
+
const scheme = schemeBase(options.name);
|
|
9531
9543
|
const packageJson = {
|
|
9532
9544
|
name: packageName,
|
|
9533
9545
|
version: "0.0.1",
|
|
@@ -9552,6 +9564,7 @@ function generateExpoAppFiles(options) {
|
|
|
9552
9564
|
expo: EXPO_VERSIONS["expo"],
|
|
9553
9565
|
"expo-constants": EXPO_VERSIONS["expo-constants"],
|
|
9554
9566
|
"expo-dev-client": EXPO_VERSIONS["expo-dev-client"],
|
|
9567
|
+
"expo-device": EXPO_VERSIONS["expo-device"],
|
|
9555
9568
|
"expo-linking": EXPO_VERSIONS["expo-linking"],
|
|
9556
9569
|
"expo-router": EXPO_VERSIONS["expo-router"],
|
|
9557
9570
|
"expo-secure-store": EXPO_VERSIONS["expo-secure-store"],
|
|
@@ -9563,6 +9576,7 @@ function generateExpoAppFiles(options) {
|
|
|
9563
9576
|
"react-native": EXPO_VERSIONS["react-native"],
|
|
9564
9577
|
"react-native-gesture-handler": EXPO_VERSIONS["react-native-gesture-handler"],
|
|
9565
9578
|
"react-native-reanimated": EXPO_VERSIONS["react-native-reanimated"],
|
|
9579
|
+
"react-native-worklets": EXPO_VERSIONS["react-native-worklets"],
|
|
9566
9580
|
"react-native-safe-area-context": EXPO_VERSIONS["react-native-safe-area-context"],
|
|
9567
9581
|
"react-native-screens": EXPO_VERSIONS["react-native-screens"],
|
|
9568
9582
|
"react-native-web": EXPO_VERSIONS["react-native-web"],
|
|
@@ -9575,27 +9589,53 @@ function generateExpoAppFiles(options) {
|
|
|
9575
9589
|
}
|
|
9576
9590
|
};
|
|
9577
9591
|
const appConfig = `import type { ConfigContext, ExpoConfig } from '@expo/config';
|
|
9592
|
+
import { EnvironmentParser } from '@geekmidas/envkit';
|
|
9578
9593
|
|
|
9579
9594
|
/**
|
|
9580
|
-
*
|
|
9581
|
-
*
|
|
9595
|
+
* What the app is built with, from the environment \`gkm\` injects — the
|
|
9596
|
+
* \`MobileApp\` construct's \`.dependsOn([api, auth])\` is where every value
|
|
9597
|
+
* here comes from, so none of it is written down.
|
|
9598
|
+
*
|
|
9599
|
+
* - \`APP_SCHEME\`: the URL scheme for this stage — \`${scheme}\` deployed,
|
|
9600
|
+
* \`${scheme}-dev\` locally, so a development build and the store build on
|
|
9601
|
+
* one phone never answer each other's links.
|
|
9602
|
+
* - \`EXPO_PUBLIC_API_URL\` / \`EXPO_PUBLIC_AUTH_URL\`: where the servers
|
|
9603
|
+
* answer. Locally that is their own port on \`localhost\`, which
|
|
9604
|
+
* \`config.ts\` points at the machine Metro was served from.
|
|
9605
|
+
*
|
|
9606
|
+
* Read at runtime from \`Constants.expoConfig.extra.config\` — see \`config.ts\`.
|
|
9582
9607
|
*/
|
|
9583
|
-
|
|
9608
|
+
const config = new EnvironmentParser({ ...process.env })
|
|
9609
|
+
.create((get) => ({
|
|
9610
|
+
scheme: get('APP_SCHEME').string(),
|
|
9611
|
+
apiUrl: get('EXPO_PUBLIC_API_URL').string(),
|
|
9612
|
+
authUrl: get('EXPO_PUBLIC_AUTH_URL').string(),
|
|
9613
|
+
}))
|
|
9614
|
+
.parse();
|
|
9615
|
+
|
|
9616
|
+
export type Config = typeof config;
|
|
9617
|
+
|
|
9618
|
+
// A bundle id and an Android package allow no hyphens; one per scheme, so
|
|
9619
|
+
// each stage installs beside the others.
|
|
9620
|
+
const bundleId = \`com.\${config.scheme.replace(/[^a-z0-9]/g, '')}.app\`;
|
|
9621
|
+
|
|
9622
|
+
export default function expoConfig({ config: base }: ConfigContext): ExpoConfig {
|
|
9584
9623
|
return {
|
|
9624
|
+
...base,
|
|
9585
9625
|
name: '${options.name}',
|
|
9586
9626
|
slug: '${slug}',
|
|
9587
9627
|
version: '0.0.1',
|
|
9588
9628
|
orientation: 'portrait',
|
|
9589
9629
|
icon: './assets/icon.png',
|
|
9590
|
-
scheme:
|
|
9630
|
+
scheme: config.scheme,
|
|
9591
9631
|
userInterfaceStyle: 'automatic',
|
|
9592
9632
|
newArchEnabled: true,
|
|
9593
9633
|
ios: {
|
|
9594
9634
|
supportsTablet: true,
|
|
9595
|
-
bundleIdentifier:
|
|
9635
|
+
bundleIdentifier: bundleId,
|
|
9596
9636
|
},
|
|
9597
9637
|
android: {
|
|
9598
|
-
package:
|
|
9638
|
+
package: bundleId,
|
|
9599
9639
|
adaptiveIcon: {
|
|
9600
9640
|
foregroundImage: './assets/adaptive-icon.png',
|
|
9601
9641
|
backgroundColor: '#ffffff',
|
|
@@ -9621,6 +9661,7 @@ export default function config(_context: ConfigContext): ExpoConfig {
|
|
|
9621
9661
|
experiments: {
|
|
9622
9662
|
typedRoutes: true,
|
|
9623
9663
|
},
|
|
9664
|
+
extra: { ...base.extra, config },
|
|
9624
9665
|
};
|
|
9625
9666
|
}
|
|
9626
9667
|
`;
|
|
@@ -9633,16 +9674,13 @@ export default function config(_context: ConfigContext): ExpoConfig {
|
|
|
9633
9674
|
dev: {
|
|
9634
9675
|
developmentClient: true,
|
|
9635
9676
|
distribution: "internal",
|
|
9636
|
-
environment: "development"
|
|
9637
|
-
env: {
|
|
9638
|
-
EXPO_PUBLIC_API_URL: "http://localhost:3000",
|
|
9639
|
-
EXPO_PUBLIC_AUTH_URL: "http://localhost:3002"
|
|
9640
|
-
}
|
|
9677
|
+
environment: "development"
|
|
9641
9678
|
},
|
|
9642
9679
|
preview: {
|
|
9643
9680
|
distribution: "internal",
|
|
9644
9681
|
environment: "preview",
|
|
9645
9682
|
env: {
|
|
9683
|
+
APP_SCHEME: scheme,
|
|
9646
9684
|
EXPO_PUBLIC_API_URL: "https://api.example.com",
|
|
9647
9685
|
EXPO_PUBLIC_AUTH_URL: "https://auth.example.com"
|
|
9648
9686
|
}
|
|
@@ -9651,6 +9689,7 @@ export default function config(_context: ConfigContext): ExpoConfig {
|
|
|
9651
9689
|
autoIncrement: true,
|
|
9652
9690
|
environment: "production",
|
|
9653
9691
|
env: {
|
|
9692
|
+
APP_SCHEME: scheme,
|
|
9654
9693
|
EXPO_PUBLIC_API_URL: "https://api.example.com",
|
|
9655
9694
|
EXPO_PUBLIC_AUTH_URL: "https://auth.example.com"
|
|
9656
9695
|
}
|
|
@@ -9712,23 +9751,49 @@ export default {
|
|
|
9712
9751
|
`;
|
|
9713
9752
|
const expoEnvDts = `/// <reference types="expo/types" />\n`;
|
|
9714
9753
|
const nativewindEnvDts = `/// <reference types="nativewind/types" />\n`;
|
|
9715
|
-
const configTs = `import
|
|
9754
|
+
const configTs = `import Constants from 'expo-constants';
|
|
9755
|
+
import * as Device from 'expo-device';
|
|
9756
|
+
import { Platform } from 'react-native';
|
|
9757
|
+
|
|
9758
|
+
import type { Config } from './app.config.ts';
|
|
9759
|
+
|
|
9760
|
+
const built = Constants.expoConfig?.extra?.config as Config;
|
|
9761
|
+
|
|
9762
|
+
/** The host this device loaded the bundle from: Metro's, as the device sees it. */
|
|
9763
|
+
const metro = (Constants.expoConfig?.hostUri ?? '').split(':')[0];
|
|
9716
9764
|
|
|
9717
9765
|
/**
|
|
9718
|
-
*
|
|
9719
|
-
*
|
|
9766
|
+
* A private address on the local network — what a phone on the same Wi-Fi
|
|
9767
|
+
* reaches this machine on. A tunnel's \`*.exp.direct\` host is not one: a
|
|
9768
|
+
* tunnel forwards only Metro, so the servers are not behind it.
|
|
9720
9769
|
*/
|
|
9721
|
-
const
|
|
9722
|
-
|
|
9723
|
-
|
|
9724
|
-
|
|
9770
|
+
const isLan = (host: string) =>
|
|
9771
|
+
/^10\\./.test(host) ||
|
|
9772
|
+
/^192\\.168\\./.test(host) ||
|
|
9773
|
+
/^172\\.(1[6-9]|2\\d|3[01])\\./.test(host);
|
|
9725
9774
|
|
|
9726
|
-
|
|
9727
|
-
|
|
9728
|
-
|
|
9729
|
-
|
|
9730
|
-
|
|
9731
|
-
|
|
9775
|
+
/** \`localhost\` from inside the Android emulator is the emulator itself. */
|
|
9776
|
+
const isAndroidEmulator = Platform.OS === 'android' && !Device.isDevice;
|
|
9777
|
+
|
|
9778
|
+
/**
|
|
9779
|
+
* A server's URL as this device reaches it.
|
|
9780
|
+
*
|
|
9781
|
+
* Locally the servers are injected as \`http://localhost:<port>\`. That is this
|
|
9782
|
+
* device only on the iOS simulator. The Android emulator reaches this machine
|
|
9783
|
+
* at \`10.0.2.2\`, and a phone at the LAN address Metro was served from.
|
|
9784
|
+
* Deployed URLs have no \`localhost\` in them and pass through untouched.
|
|
9785
|
+
*/
|
|
9786
|
+
export function reachable(url: string): string {
|
|
9787
|
+
const host = isLan(metro) ? metro : isAndroidEmulator ? '10.0.2.2' : undefined;
|
|
9788
|
+
if (!host) return url;
|
|
9789
|
+
return url.replace(/\\/\\/(localhost|127\\.0\\.0\\.1)(?=[:/]|$)/, \`//\${host}\`);
|
|
9790
|
+
}
|
|
9791
|
+
|
|
9792
|
+
export const config = {
|
|
9793
|
+
scheme: built.scheme,
|
|
9794
|
+
apiUrl: reachable(built.apiUrl),
|
|
9795
|
+
authUrl: reachable(built.authUrl),
|
|
9796
|
+
};
|
|
9732
9797
|
`;
|
|
9733
9798
|
const queryClientTs = `import { QueryClient } from '@tanstack/react-query';
|
|
9734
9799
|
|
|
@@ -9740,21 +9805,23 @@ export const queryClient = new QueryClient({
|
|
|
9740
9805
|
},
|
|
9741
9806
|
});
|
|
9742
9807
|
`;
|
|
9743
|
-
const authClientTs = `import { expoClient } from '@better-auth/expo/client';
|
|
9808
|
+
const authClientTs = `import { expoClient, getSetCookie } from '@better-auth/expo/client';
|
|
9744
9809
|
import { magicLinkClient } from 'better-auth/client/plugins';
|
|
9745
9810
|
import { createAuthClient } from 'better-auth/react';
|
|
9811
|
+
import * as Linking from 'expo-linking';
|
|
9746
9812
|
import * as SecureStore from 'expo-secure-store';
|
|
9813
|
+
import { useEffect } from 'react';
|
|
9747
9814
|
|
|
9748
9815
|
import { config } from '@/config.ts';
|
|
9749
9816
|
|
|
9750
|
-
export const STORAGE_PREFIX =
|
|
9817
|
+
export const STORAGE_PREFIX = config.scheme;
|
|
9751
9818
|
export const COOKIE_STORE_KEY = \`\${STORAGE_PREFIX}_cookie\`;
|
|
9752
9819
|
|
|
9753
9820
|
export const authClient = createAuthClient({
|
|
9754
9821
|
baseURL: config.authUrl,
|
|
9755
9822
|
plugins: [
|
|
9756
9823
|
expoClient({
|
|
9757
|
-
scheme:
|
|
9824
|
+
scheme: config.scheme,
|
|
9758
9825
|
storagePrefix: STORAGE_PREFIX,
|
|
9759
9826
|
storage: SecureStore,
|
|
9760
9827
|
}),
|
|
@@ -9764,6 +9831,36 @@ export const authClient = createAuthClient({
|
|
|
9764
9831
|
|
|
9765
9832
|
export const { signIn, useSession } = authClient;
|
|
9766
9833
|
|
|
9834
|
+
/**
|
|
9835
|
+
* Sign in from the emailed link.
|
|
9836
|
+
*
|
|
9837
|
+
* The link opens in the phone's browser, which is what receives the session
|
|
9838
|
+
* cookie — not the app. The auth server's Expo plugin carries the cookie on the
|
|
9839
|
+
* redirect back into the app (\`?cookie=\`), and this stores it where the
|
|
9840
|
+
* Expo client keeps its own, merged with what is already there, then tells the
|
|
9841
|
+
* client the session changed. The Expo client does this itself only for a
|
|
9842
|
+
* sign-in it opened (social); a link opened from mail arrives as a deep link.
|
|
9843
|
+
*
|
|
9844
|
+
* Called once, from the root layout.
|
|
9845
|
+
*/
|
|
9846
|
+
export function useSessionFromLink() {
|
|
9847
|
+
const url = Linking.useLinkingURL();
|
|
9848
|
+
|
|
9849
|
+
useEffect(() => {
|
|
9850
|
+
const cookie = url ? Linking.parse(url).queryParams?.cookie : undefined;
|
|
9851
|
+
if (typeof cookie !== 'string') return;
|
|
9852
|
+
|
|
9853
|
+
void (async () => {
|
|
9854
|
+
const previous = await SecureStore.getItemAsync(COOKIE_STORE_KEY);
|
|
9855
|
+
await SecureStore.setItemAsync(
|
|
9856
|
+
COOKIE_STORE_KEY,
|
|
9857
|
+
getSetCookie(cookie, previous ?? undefined),
|
|
9858
|
+
);
|
|
9859
|
+
authClient.$store.notify('$sessionSignal');
|
|
9860
|
+
})();
|
|
9861
|
+
}, [url]);
|
|
9862
|
+
}
|
|
9863
|
+
|
|
9767
9864
|
export async function signOut() {
|
|
9768
9865
|
try {
|
|
9769
9866
|
await authClient.signOut();
|
|
@@ -9830,7 +9927,7 @@ import { StatusBar } from 'expo-status-bar';
|
|
|
9830
9927
|
import { useEffect, useMemo } from 'react';
|
|
9831
9928
|
import 'react-native-reanimated';
|
|
9832
9929
|
|
|
9833
|
-
import { authClient } from '@/lib/auth-client.ts';
|
|
9930
|
+
import { authClient, useSessionFromLink } from '@/lib/auth-client.ts';
|
|
9834
9931
|
import { ApiProvider } from '@/lib/api-context.tsx';
|
|
9835
9932
|
import { createAppApi } from '@/lib/api.ts';
|
|
9836
9933
|
import { queryClient } from '@/lib/query-client.ts';
|
|
@@ -9855,6 +9952,8 @@ function AuthGate({ children }: { children: React.ReactNode }) {
|
|
|
9855
9952
|
|
|
9856
9953
|
export default function RootLayout() {
|
|
9857
9954
|
const api = useMemo(() => createAppApi(), []);
|
|
9955
|
+
// The emailed sign-in link comes back as a deep link carrying the session.
|
|
9956
|
+
useSessionFromLink();
|
|
9858
9957
|
|
|
9859
9958
|
return (
|
|
9860
9959
|
<QueryClientProvider client={queryClient}>
|
|
@@ -10376,7 +10475,8 @@ function rootConstructDependencies(options) {
|
|
|
10376
10475
|
pg: DEPENDENCY_VERSIONS.pg,
|
|
10377
10476
|
...options.constructs.uploads ? { "@geekmidas/storage": v["@geekmidas/storage"] } : {},
|
|
10378
10477
|
...options.constructs.mail ? { "@geekmidas/emailkit": v["@geekmidas/emailkit"] } : {},
|
|
10379
|
-
...options.constructs.cache ? { "@geekmidas/cache": v["@geekmidas/cache"] } : {}
|
|
10478
|
+
...options.constructs.cache ? { "@geekmidas/cache": v["@geekmidas/cache"] } : {},
|
|
10479
|
+
...options.frontendFramework === "expo" ? { "@better-auth/expo": DEPENDENCY_VERSIONS["better-auth"] } : {}
|
|
10380
10480
|
};
|
|
10381
10481
|
}
|
|
10382
10482
|
/**
|
|
@@ -10853,6 +10953,26 @@ export const api = new RestApi('Api', {
|
|
|
10853
10953
|
`
|
|
10854
10954
|
});
|
|
10855
10955
|
const variant = frontendFramework === "tanstack-start" ? "{ path: 'apps/web', variant: 'tanstack' }" : frontendFramework === "expo" ? void 0 : "{ path: 'apps/web', variant: 'next' }";
|
|
10956
|
+
if (frontendFramework === "expo") files.push({
|
|
10957
|
+
path: "constructs/app.ts",
|
|
10958
|
+
content: `import { MobileApp } from '@geekmidas/constructs/mobile-app';
|
|
10959
|
+
import { api } from './api.ts';
|
|
10960
|
+
import { auth } from './auth.ts';
|
|
10961
|
+
|
|
10962
|
+
/**
|
|
10963
|
+
* The mobile app — a construct like a site, which is what makes it an app.
|
|
10964
|
+
*
|
|
10965
|
+
* \`.dependsOn()\` is the single fact behind what a mobile app otherwise has
|
|
10966
|
+
* written down by hand: its API and auth URLs, its URL scheme per stage, the
|
|
10967
|
+
* scheme in the auth server's trusted origins, Better Auth's Expo plugin, and
|
|
10968
|
+
* sign-in links a phone can open.
|
|
10969
|
+
*/
|
|
10970
|
+
export const app = new MobileApp('App', { path: 'apps/app' }).dependsOn([
|
|
10971
|
+
api,
|
|
10972
|
+
auth,
|
|
10973
|
+
]);
|
|
10974
|
+
`
|
|
10975
|
+
});
|
|
10856
10976
|
if (variant !== void 0) files.push({
|
|
10857
10977
|
path: "constructs/site.ts",
|
|
10858
10978
|
content: `import { StaticSite } from '@geekmidas/constructs/site';
|
|
@@ -15027,7 +15147,7 @@ var NoStoredSecrets = class extends Error {
|
|
|
15027
15147
|
}
|
|
15028
15148
|
};
|
|
15029
15149
|
async function reconcile(secrets, workspace, stage) {
|
|
15030
|
-
const { derivedContainers } = await import("./workspace-
|
|
15150
|
+
const { derivedContainers } = await import("./workspace-CIiGijNi.mjs").then((n) => n.a);
|
|
15031
15151
|
const result = reconcileMissingSecrets(secrets, workspace, await derivedContainers(workspace, stage));
|
|
15032
15152
|
return result ? {
|
|
15033
15153
|
secrets: result.secrets,
|
|
@@ -15426,7 +15546,7 @@ async function testCommand(options = {}) {
|
|
|
15426
15546
|
* starts without the reference data the app needs.
|
|
15427
15547
|
*/
|
|
15428
15548
|
async function migrateTestStage(workspace, env) {
|
|
15429
|
-
const { migrateAndSeed } = await import("./migrate-
|
|
15549
|
+
const { migrateAndSeed } = await import("./migrate-C-D9xIbb.mjs");
|
|
15430
15550
|
const { discover } = await import("./discover-CMxesskG.mjs").then((n) => n.n);
|
|
15431
15551
|
const sources = {};
|
|
15432
15552
|
const manifest = await discover({
|
|
@@ -15871,7 +15991,7 @@ program.command("migrate").description("Apply each database construct's migratio
|
|
|
15871
15991
|
try {
|
|
15872
15992
|
const globalOptions = program.opts();
|
|
15873
15993
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
15874
|
-
const { migrateCommand } = await import("./migrate-
|
|
15994
|
+
const { migrateCommand } = await import("./migrate-C-D9xIbb.mjs");
|
|
15875
15995
|
await migrateCommand({
|
|
15876
15996
|
...options,
|
|
15877
15997
|
...construct ? { construct } : {}
|
|
@@ -15885,7 +16005,7 @@ program.command("seed").description("Migrate, then run each database construct's
|
|
|
15885
16005
|
try {
|
|
15886
16006
|
const globalOptions = program.opts();
|
|
15887
16007
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
15888
|
-
const { seedCommand } = await import("./migrate-
|
|
16008
|
+
const { seedCommand } = await import("./migrate-C-D9xIbb.mjs");
|
|
15889
16009
|
await seedCommand({
|
|
15890
16010
|
...options,
|
|
15891
16011
|
...construct ? { construct } : {}
|
|
@@ -15899,7 +16019,7 @@ program.command("migration").description("Write a construct's next migration: an
|
|
|
15899
16019
|
try {
|
|
15900
16020
|
const globalOptions = program.opts();
|
|
15901
16021
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
15902
|
-
const { migrationCommand } = await import("./migrate-
|
|
16022
|
+
const { migrationCommand } = await import("./migrate-C-D9xIbb.mjs");
|
|
15903
16023
|
await migrationCommand({
|
|
15904
16024
|
construct,
|
|
15905
16025
|
...name ? { name } : {}
|
|
@@ -16067,7 +16187,7 @@ program.command("secrets:reconcile").description("Backfill missing custom secret
|
|
|
16067
16187
|
console.error(`No secrets found for stage "${stage}". Run "gkm secrets:init --stage ${stage}" first.`);
|
|
16068
16188
|
process.exit(1);
|
|
16069
16189
|
}
|
|
16070
|
-
const { derivedContainers } = await import("./workspace-
|
|
16190
|
+
const { derivedContainers } = await import("./workspace-CIiGijNi.mjs").then((n) => n.a);
|
|
16071
16191
|
const result = reconcileMissingSecrets(secrets, workspace, await derivedContainers(workspace, stage));
|
|
16072
16192
|
if (!result) {
|
|
16073
16193
|
console.log(`\n✓ Secrets for stage "${stage}" are up-to-date`);
|
|
@@ -16126,7 +16246,7 @@ program.command("deploy:github").description("Let GitHub Actions deploy a stage:
|
|
|
16126
16246
|
try {
|
|
16127
16247
|
const globalOptions = program.opts();
|
|
16128
16248
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
16129
|
-
const { deployGithubCommand } = await import("./github-
|
|
16249
|
+
const { deployGithubCommand } = await import("./github-C99BMxf0.mjs");
|
|
16130
16250
|
await deployGithubCommand(options);
|
|
16131
16251
|
} catch (error) {
|
|
16132
16252
|
console.error(formatError(error));
|