@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.
- 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 +209 -87
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +210 -88
- package/dist/index.mjs.map +1 -1
- package/dist/{migrate-u1oRHz6K.cjs → migrate-BsW4xBEO.cjs} +3 -3
- package/dist/{migrate-u1oRHz6K.cjs.map → migrate-BsW4xBEO.cjs.map} +1 -1
- package/dist/{migrate-BbhXxQer.mjs → migrate-CT8b3nt_.mjs} +3 -3
- package/dist/{migrate-BbhXxQer.mjs.map → migrate-CT8b3nt_.mjs.map} +1 -1
- package/dist/openapi.cjs +1 -1
- package/dist/openapi.mjs +1 -1
- package/dist/{workspace-LKFnEBX8.cjs → workspace-C0X8xSXV.cjs} +89 -15
- package/dist/workspace-C0X8xSXV.cjs.map +1 -0
- package/dist/{workspace-DB2YHgOl.mjs → workspace-EmXwO2qM.mjs} +90 -16
- package/dist/workspace-EmXwO2qM.mjs.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.cjs
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
3
|
const require_rolldown_runtime = require("./rolldown-runtime-C0BPl7ul.cjs");
|
|
4
4
|
const require_discover = require("./discover-CroTIm0K.cjs");
|
|
5
|
-
const require_config = require("./config-
|
|
5
|
+
const require_config = require("./config-DgAHy8nY.cjs");
|
|
6
6
|
const require_workspace = require("./workspace-CAJk6UmC.cjs");
|
|
7
7
|
const require_storage = require("./storage-C2JC31V9.cjs");
|
|
8
|
-
const require_workspace$1 = require("./workspace-
|
|
8
|
+
const require_workspace$1 = require("./workspace-C0X8xSXV.cjs");
|
|
9
9
|
const require_OpenApiTsGenerator = require("./OpenApiTsGenerator-D1nhN98m.cjs");
|
|
10
10
|
const require_openapi = require("./openapi.cjs");
|
|
11
11
|
const require_ready = require("./ready-CXhoutix.cjs");
|
|
@@ -42,7 +42,7 @@ let _geekmidas_constructs_subscribers = require("@geekmidas/constructs/subscribe
|
|
|
42
42
|
let _geekmidas_constructs_topic = require("@geekmidas/constructs/topic");
|
|
43
43
|
let node_dns_promises = require("node:dns/promises");
|
|
44
44
|
//#region package.json
|
|
45
|
-
var version = "10.0.0-alpha.
|
|
45
|
+
var version = "10.0.0-alpha.36";
|
|
46
46
|
//#endregion
|
|
47
47
|
//#region src/auth/credentials.ts
|
|
48
48
|
/**
|
|
@@ -1092,9 +1092,9 @@ async function workspaceDevCommand(workspace, options) {
|
|
|
1092
1092
|
...reconciled.env
|
|
1093
1093
|
};
|
|
1094
1094
|
if (options.migrate || options.seed) {
|
|
1095
|
-
const { prepareDevDatabases } = await Promise.resolve().then(() => require("./migrate-
|
|
1095
|
+
const { prepareDevDatabases } = await Promise.resolve().then(() => require("./migrate-BsW4xBEO.cjs"));
|
|
1096
1096
|
await prepareDevDatabases(workspace, reconciled.env, { seed: !!options.seed });
|
|
1097
|
-
} else await Promise.resolve().then(() => require("./migrate-
|
|
1097
|
+
} else await Promise.resolve().then(() => require("./migrate-BsW4xBEO.cjs")).then(({ reportPendingMigrations }) => reportPendingMigrations(workspace, reconciled.env)).catch((error) => {
|
|
1098
1098
|
logger$10.log(`⚠️ Could not check for pending migrations: ${error instanceof Error ? error.message : String(error)}`);
|
|
1099
1099
|
});
|
|
1100
1100
|
const appUrls = {};
|
|
@@ -1124,6 +1124,13 @@ async function workspaceDevCommand(workspace, options) {
|
|
|
1124
1124
|
for (const appName of buildOrder) {
|
|
1125
1125
|
const app = workspace.apps[appName];
|
|
1126
1126
|
if (!app) continue;
|
|
1127
|
+
if (app.type === "mobile") {
|
|
1128
|
+
const own = reconciled.plan.resources.find((r) => r.kind === "mobile-app" && require_config.appKey(r.id) === appName);
|
|
1129
|
+
const scheme = own ? reconciled.env[own.envKey] : void 0;
|
|
1130
|
+
const device = Object.entries(reconciled.env).find(([key]) => key.endsWith("_DEVICE_URL"))?.[1];
|
|
1131
|
+
logger$10.log(` ${appName.padEnd(width)} ${scheme ? `${scheme}://` : "mobile"}${device ? ` — devices reach ${new URL(device).hostname}` : ""}`);
|
|
1132
|
+
continue;
|
|
1133
|
+
}
|
|
1127
1134
|
const local = `http://localhost:${app.port}`;
|
|
1128
1135
|
const url = Object.entries(appUrls).find(([id]) => require_config.appKey(id) === appName)?.[1];
|
|
1129
1136
|
logger$10.log(` ${appName.padEnd(width)} ${url ? `${url} -> ${local}` : local}`);
|
|
@@ -3318,9 +3325,13 @@ const PROVISIONERS = {
|
|
|
3318
3325
|
if (!url) throw new SurfaceHasNoAddress(declaration.id);
|
|
3319
3326
|
const origins = [...new Set((0, _geekmidas_manifest.dependentsOf)(context.manifest, declaration.id).map((caller) => context.addresses?.[caller]).filter((address) => Boolean(address)))].sort();
|
|
3320
3327
|
const domain = (0, _geekmidas_manifest.cookieDomain)([url, ...origins]);
|
|
3328
|
+
const schemes = (0, _geekmidas_manifest.dependentsOf)(context.manifest, declaration.id).flatMap((caller) => {
|
|
3329
|
+
const app = context.manifest[caller];
|
|
3330
|
+
return app?.kind === "mobile-app" ? (0, _geekmidas_manifest.mobileOrigins)((0, _geekmidas_manifest.schemeBase)(context.project, app.scheme)) : [];
|
|
3331
|
+
});
|
|
3321
3332
|
return { provides: {
|
|
3322
3333
|
[(0, _geekmidas_manifest.provideKey)(declaration.id, "url")]: url,
|
|
3323
|
-
[(0, _geekmidas_manifest.provideKey)(declaration.id, "trustedOrigins")]: origins.join(","),
|
|
3334
|
+
[(0, _geekmidas_manifest.provideKey)(declaration.id, "trustedOrigins")]: [...origins, ...schemes].join(","),
|
|
3324
3335
|
...domain ? { [(0, _geekmidas_manifest.provideKey)(declaration.id, "cookieDomain")]: domain } : {}
|
|
3325
3336
|
} };
|
|
3326
3337
|
}
|
|
@@ -8011,11 +8022,11 @@ function routesGlob(structure) {
|
|
|
8011
8022
|
}
|
|
8012
8023
|
}
|
|
8013
8024
|
/**
|
|
8014
|
-
* Plain ids — `Database`, not `
|
|
8025
|
+
* Plain ids — `Database`, not `Shop`.
|
|
8015
8026
|
*
|
|
8016
8027
|
* The workspace `name` already scopes every physical name
|
|
8017
|
-
* (`production-
|
|
8018
|
-
* would say it twice: `production-
|
|
8028
|
+
* (`production-shop-database`), so an id that carried the project too
|
|
8029
|
+
* would say it twice: `production-shop-shop-cache`. Within a
|
|
8019
8030
|
* workspace the constructs are declared once, at its root, so there is no
|
|
8020
8031
|
* second app's `Database` to collide with.
|
|
8021
8032
|
*/
|
|
@@ -8741,30 +8752,32 @@ const TOOLCHAIN_VERSIONS = {
|
|
|
8741
8752
|
"@storybook/react-vite": "^10.6.0"
|
|
8742
8753
|
};
|
|
8743
8754
|
/**
|
|
8744
|
-
* The Expo app's packages, which track an Expo SDK release (
|
|
8745
|
-
* their own latest: SDK
|
|
8755
|
+
* The Expo app's packages, which track an Expo SDK release (57) rather than
|
|
8756
|
+
* their own latest: SDK 57 is tested against exactly these, React Native and
|
|
8746
8757
|
* React included, so they move when the SDK does.
|
|
8747
8758
|
*/
|
|
8748
8759
|
const EXPO_VERSIONS = {
|
|
8749
|
-
expo: "~
|
|
8750
|
-
"expo-constants": "~
|
|
8751
|
-
"expo-dev-client": "~
|
|
8752
|
-
"expo-
|
|
8753
|
-
"expo-
|
|
8754
|
-
"expo-
|
|
8755
|
-
"expo-
|
|
8756
|
-
"expo-
|
|
8757
|
-
|
|
8758
|
-
|
|
8759
|
-
"react-
|
|
8760
|
-
"react-native
|
|
8761
|
-
"react-native-
|
|
8762
|
-
"react-native-
|
|
8763
|
-
"react-native-
|
|
8760
|
+
expo: "~57.0.26",
|
|
8761
|
+
"expo-constants": "~57.0.20",
|
|
8762
|
+
"expo-dev-client": "~57.0.19",
|
|
8763
|
+
"expo-device": "~57.0.2",
|
|
8764
|
+
"expo-linking": "~57.0.11",
|
|
8765
|
+
"expo-router": "~57.0.24",
|
|
8766
|
+
"expo-splash-screen": "~57.0.9",
|
|
8767
|
+
"expo-status-bar": "~57.0.1",
|
|
8768
|
+
"expo-secure-store": "~57.0.4",
|
|
8769
|
+
react: "19.2.3",
|
|
8770
|
+
"react-dom": "19.2.3",
|
|
8771
|
+
"react-native": "0.86.3",
|
|
8772
|
+
"react-native-gesture-handler": "~2.32.0",
|
|
8773
|
+
"react-native-reanimated": "4.5.1",
|
|
8774
|
+
"react-native-worklets": "0.10.1",
|
|
8775
|
+
"react-native-safe-area-context": "~5.7.0",
|
|
8776
|
+
"react-native-screens": "~4.26.0",
|
|
8764
8777
|
"react-native-web": "~0.21.0",
|
|
8765
8778
|
"@react-navigation/native": "^7.1.0",
|
|
8766
8779
|
"@babel/core": "^7.25.0",
|
|
8767
|
-
"@types/react": "~19.
|
|
8780
|
+
"@types/react": "~19.2.2",
|
|
8768
8781
|
nativewind: "~4.2.0",
|
|
8769
8782
|
tailwindcss: "~3.4.0"
|
|
8770
8783
|
};
|
|
@@ -8796,26 +8809,26 @@ function loadPackageJson() {
|
|
|
8796
8809
|
* Run: pnpm --filter @geekmidas/cli sync-versions
|
|
8797
8810
|
*/
|
|
8798
8811
|
const GEEKMIDAS_VERSIONS = {
|
|
8799
|
-
"@geekmidas/audit": "~10.0.0-alpha.
|
|
8800
|
-
"@geekmidas/auth": "~10.0.0-alpha.
|
|
8801
|
-
"@geekmidas/cache": "~10.0.0-alpha.
|
|
8802
|
-
"@geekmidas/client": "~10.0.0-alpha.
|
|
8803
|
-
"@geekmidas/cloud": "~10.0.0-alpha.
|
|
8804
|
-
"@geekmidas/constructs": "~10.0.0-alpha.
|
|
8805
|
-
"@geekmidas/db": "~10.0.0-alpha.
|
|
8806
|
-
"@geekmidas/emailkit": "~10.0.0-alpha.
|
|
8807
|
-
"@geekmidas/envkit": "~10.0.0-alpha.
|
|
8808
|
-
"@geekmidas/errors": "~10.0.0-alpha.
|
|
8809
|
-
"@geekmidas/events": "~10.0.0-alpha.
|
|
8810
|
-
"@geekmidas/logger": "~10.0.0-alpha.
|
|
8811
|
-
"@geekmidas/manifest": "~10.0.0-alpha.
|
|
8812
|
-
"@geekmidas/rate-limit": "~10.0.0-alpha.
|
|
8813
|
-
"@geekmidas/schema": "~10.0.0-alpha.
|
|
8814
|
-
"@geekmidas/services": "~10.0.0-alpha.
|
|
8815
|
-
"@geekmidas/storage": "~10.0.0-alpha.
|
|
8816
|
-
"@geekmidas/studio": "~10.0.0-alpha.
|
|
8817
|
-
"@geekmidas/telescope": "~10.0.0-alpha.
|
|
8818
|
-
"@geekmidas/testkit": "~10.0.0-alpha.
|
|
8812
|
+
"@geekmidas/audit": "~10.0.0-alpha.36",
|
|
8813
|
+
"@geekmidas/auth": "~10.0.0-alpha.36",
|
|
8814
|
+
"@geekmidas/cache": "~10.0.0-alpha.36",
|
|
8815
|
+
"@geekmidas/client": "~10.0.0-alpha.36",
|
|
8816
|
+
"@geekmidas/cloud": "~10.0.0-alpha.36",
|
|
8817
|
+
"@geekmidas/constructs": "~10.0.0-alpha.36",
|
|
8818
|
+
"@geekmidas/db": "~10.0.0-alpha.36",
|
|
8819
|
+
"@geekmidas/emailkit": "~10.0.0-alpha.36",
|
|
8820
|
+
"@geekmidas/envkit": "~10.0.0-alpha.36",
|
|
8821
|
+
"@geekmidas/errors": "~10.0.0-alpha.36",
|
|
8822
|
+
"@geekmidas/events": "~10.0.0-alpha.36",
|
|
8823
|
+
"@geekmidas/logger": "~10.0.0-alpha.36",
|
|
8824
|
+
"@geekmidas/manifest": "~10.0.0-alpha.36",
|
|
8825
|
+
"@geekmidas/rate-limit": "~10.0.0-alpha.36",
|
|
8826
|
+
"@geekmidas/schema": "~10.0.0-alpha.36",
|
|
8827
|
+
"@geekmidas/services": "~10.0.0-alpha.36",
|
|
8828
|
+
"@geekmidas/storage": "~10.0.0-alpha.36",
|
|
8829
|
+
"@geekmidas/studio": "~10.0.0-alpha.36",
|
|
8830
|
+
"@geekmidas/telescope": "~10.0.0-alpha.36",
|
|
8831
|
+
"@geekmidas/testkit": "~10.0.0-alpha.36",
|
|
8819
8832
|
"@geekmidas/cli": `~${loadPackageJson().version}`
|
|
8820
8833
|
};
|
|
8821
8834
|
//#endregion
|
|
@@ -9537,8 +9550,7 @@ function generateExpoAppFiles(options) {
|
|
|
9537
9550
|
const apiPackage = `@${options.name}/api`;
|
|
9538
9551
|
const modelsPackage = `@${options.name}/models`;
|
|
9539
9552
|
const slug = options.name.toLowerCase().replace(/[^a-z0-9-]/g, "-");
|
|
9540
|
-
const scheme =
|
|
9541
|
-
const bundleId = `com.${scheme}.app`;
|
|
9553
|
+
const scheme = (0, _geekmidas_manifest.schemeBase)(options.name);
|
|
9542
9554
|
const packageJson = {
|
|
9543
9555
|
name: packageName,
|
|
9544
9556
|
version: "0.0.1",
|
|
@@ -9563,6 +9575,7 @@ function generateExpoAppFiles(options) {
|
|
|
9563
9575
|
expo: EXPO_VERSIONS["expo"],
|
|
9564
9576
|
"expo-constants": EXPO_VERSIONS["expo-constants"],
|
|
9565
9577
|
"expo-dev-client": EXPO_VERSIONS["expo-dev-client"],
|
|
9578
|
+
"expo-device": EXPO_VERSIONS["expo-device"],
|
|
9566
9579
|
"expo-linking": EXPO_VERSIONS["expo-linking"],
|
|
9567
9580
|
"expo-router": EXPO_VERSIONS["expo-router"],
|
|
9568
9581
|
"expo-secure-store": EXPO_VERSIONS["expo-secure-store"],
|
|
@@ -9574,6 +9587,7 @@ function generateExpoAppFiles(options) {
|
|
|
9574
9587
|
"react-native": EXPO_VERSIONS["react-native"],
|
|
9575
9588
|
"react-native-gesture-handler": EXPO_VERSIONS["react-native-gesture-handler"],
|
|
9576
9589
|
"react-native-reanimated": EXPO_VERSIONS["react-native-reanimated"],
|
|
9590
|
+
"react-native-worklets": EXPO_VERSIONS["react-native-worklets"],
|
|
9577
9591
|
"react-native-safe-area-context": EXPO_VERSIONS["react-native-safe-area-context"],
|
|
9578
9592
|
"react-native-screens": EXPO_VERSIONS["react-native-screens"],
|
|
9579
9593
|
"react-native-web": EXPO_VERSIONS["react-native-web"],
|
|
@@ -9586,27 +9600,51 @@ function generateExpoAppFiles(options) {
|
|
|
9586
9600
|
}
|
|
9587
9601
|
};
|
|
9588
9602
|
const appConfig = `import type { ConfigContext, ExpoConfig } from '@expo/config';
|
|
9603
|
+
import { EnvironmentParser } from '@geekmidas/envkit';
|
|
9589
9604
|
|
|
9590
9605
|
/**
|
|
9591
|
-
*
|
|
9592
|
-
*
|
|
9606
|
+
* What the app is built with, from the environment \`gkm\` injects — the
|
|
9607
|
+
* \`MobileApp\` construct's \`.dependsOn([api, auth])\` is where every value
|
|
9608
|
+
* here comes from, so none of it is written down.
|
|
9609
|
+
*
|
|
9610
|
+
* - \`APP_SCHEME\`: the URL scheme — \`${scheme}\`, the same on every stage.
|
|
9611
|
+
* - \`EXPO_PUBLIC_API_URL\` / \`EXPO_PUBLIC_AUTH_URL\`: where the servers
|
|
9612
|
+
* answer. Locally that is their own port on \`localhost\`, which
|
|
9613
|
+
* \`config.ts\` points at the machine Metro was served from.
|
|
9614
|
+
*
|
|
9615
|
+
* Read at runtime from \`Constants.expoConfig.extra.config\` — see \`config.ts\`.
|
|
9593
9616
|
*/
|
|
9594
|
-
|
|
9617
|
+
const config = new EnvironmentParser({ ...process.env })
|
|
9618
|
+
.create((get) => ({
|
|
9619
|
+
scheme: get('APP_SCHEME').string(),
|
|
9620
|
+
apiUrl: get('EXPO_PUBLIC_API_URL').string(),
|
|
9621
|
+
authUrl: get('EXPO_PUBLIC_AUTH_URL').string(),
|
|
9622
|
+
}))
|
|
9623
|
+
.parse();
|
|
9624
|
+
|
|
9625
|
+
export type Config = typeof config;
|
|
9626
|
+
|
|
9627
|
+
// A bundle id and an Android package allow no hyphens; one per scheme, so
|
|
9628
|
+
// each stage installs beside the others.
|
|
9629
|
+
const bundleId = \`com.\${config.scheme.replace(/[^a-z0-9]/g, '')}.app\`;
|
|
9630
|
+
|
|
9631
|
+
export default function expoConfig({ config: base }: ConfigContext): ExpoConfig {
|
|
9595
9632
|
return {
|
|
9633
|
+
...base,
|
|
9596
9634
|
name: '${options.name}',
|
|
9597
9635
|
slug: '${slug}',
|
|
9598
9636
|
version: '0.0.1',
|
|
9599
9637
|
orientation: 'portrait',
|
|
9600
9638
|
icon: './assets/icon.png',
|
|
9601
|
-
scheme:
|
|
9639
|
+
scheme: config.scheme,
|
|
9602
9640
|
userInterfaceStyle: 'automatic',
|
|
9603
9641
|
newArchEnabled: true,
|
|
9604
9642
|
ios: {
|
|
9605
9643
|
supportsTablet: true,
|
|
9606
|
-
bundleIdentifier:
|
|
9644
|
+
bundleIdentifier: bundleId,
|
|
9607
9645
|
},
|
|
9608
9646
|
android: {
|
|
9609
|
-
package:
|
|
9647
|
+
package: bundleId,
|
|
9610
9648
|
adaptiveIcon: {
|
|
9611
9649
|
foregroundImage: './assets/adaptive-icon.png',
|
|
9612
9650
|
backgroundColor: '#ffffff',
|
|
@@ -9632,6 +9670,7 @@ export default function config(_context: ConfigContext): ExpoConfig {
|
|
|
9632
9670
|
experiments: {
|
|
9633
9671
|
typedRoutes: true,
|
|
9634
9672
|
},
|
|
9673
|
+
extra: { ...base.extra, config },
|
|
9635
9674
|
};
|
|
9636
9675
|
}
|
|
9637
9676
|
`;
|
|
@@ -9644,16 +9683,13 @@ export default function config(_context: ConfigContext): ExpoConfig {
|
|
|
9644
9683
|
dev: {
|
|
9645
9684
|
developmentClient: true,
|
|
9646
9685
|
distribution: "internal",
|
|
9647
|
-
environment: "development"
|
|
9648
|
-
env: {
|
|
9649
|
-
EXPO_PUBLIC_API_URL: "http://localhost:3000",
|
|
9650
|
-
EXPO_PUBLIC_AUTH_URL: "http://localhost:3002"
|
|
9651
|
-
}
|
|
9686
|
+
environment: "development"
|
|
9652
9687
|
},
|
|
9653
9688
|
preview: {
|
|
9654
9689
|
distribution: "internal",
|
|
9655
9690
|
environment: "preview",
|
|
9656
9691
|
env: {
|
|
9692
|
+
APP_SCHEME: scheme,
|
|
9657
9693
|
EXPO_PUBLIC_API_URL: "https://api.example.com",
|
|
9658
9694
|
EXPO_PUBLIC_AUTH_URL: "https://auth.example.com"
|
|
9659
9695
|
}
|
|
@@ -9662,6 +9698,7 @@ export default function config(_context: ConfigContext): ExpoConfig {
|
|
|
9662
9698
|
autoIncrement: true,
|
|
9663
9699
|
environment: "production",
|
|
9664
9700
|
env: {
|
|
9701
|
+
APP_SCHEME: scheme,
|
|
9665
9702
|
EXPO_PUBLIC_API_URL: "https://api.example.com",
|
|
9666
9703
|
EXPO_PUBLIC_AUTH_URL: "https://auth.example.com"
|
|
9667
9704
|
}
|
|
@@ -9723,23 +9760,49 @@ export default {
|
|
|
9723
9760
|
`;
|
|
9724
9761
|
const expoEnvDts = `/// <reference types="expo/types" />\n`;
|
|
9725
9762
|
const nativewindEnvDts = `/// <reference types="nativewind/types" />\n`;
|
|
9726
|
-
const configTs = `import
|
|
9763
|
+
const configTs = `import Constants from 'expo-constants';
|
|
9764
|
+
import * as Device from 'expo-device';
|
|
9765
|
+
import { Platform } from 'react-native';
|
|
9766
|
+
|
|
9767
|
+
import type { Config } from './app.config.ts';
|
|
9768
|
+
|
|
9769
|
+
const built = Constants.expoConfig?.extra?.config as Config;
|
|
9770
|
+
|
|
9771
|
+
/** The host this device loaded the bundle from: Metro's, as the device sees it. */
|
|
9772
|
+
const metro = (Constants.expoConfig?.hostUri ?? '').split(':')[0];
|
|
9727
9773
|
|
|
9728
9774
|
/**
|
|
9729
|
-
*
|
|
9730
|
-
*
|
|
9775
|
+
* A private address on the local network — what a phone on the same Wi-Fi
|
|
9776
|
+
* reaches this machine on. A tunnel's \`*.exp.direct\` host is not one: a
|
|
9777
|
+
* tunnel forwards only Metro, so the servers are not behind it.
|
|
9731
9778
|
*/
|
|
9732
|
-
const
|
|
9733
|
-
|
|
9734
|
-
|
|
9735
|
-
|
|
9779
|
+
const isLan = (host: string) =>
|
|
9780
|
+
/^10\\./.test(host) ||
|
|
9781
|
+
/^192\\.168\\./.test(host) ||
|
|
9782
|
+
/^172\\.(1[6-9]|2\\d|3[01])\\./.test(host);
|
|
9736
9783
|
|
|
9737
|
-
|
|
9738
|
-
|
|
9739
|
-
|
|
9740
|
-
|
|
9741
|
-
|
|
9742
|
-
|
|
9784
|
+
/** \`localhost\` from inside the Android emulator is the emulator itself. */
|
|
9785
|
+
const isAndroidEmulator = Platform.OS === 'android' && !Device.isDevice;
|
|
9786
|
+
|
|
9787
|
+
/**
|
|
9788
|
+
* A server's URL as this device reaches it.
|
|
9789
|
+
*
|
|
9790
|
+
* Locally the servers are injected as \`http://localhost:<port>\`. That is this
|
|
9791
|
+
* device only on the iOS simulator. The Android emulator reaches this machine
|
|
9792
|
+
* at \`10.0.2.2\`, and a phone at the LAN address Metro was served from.
|
|
9793
|
+
* Deployed URLs have no \`localhost\` in them and pass through untouched.
|
|
9794
|
+
*/
|
|
9795
|
+
export function reachable(url: string): string {
|
|
9796
|
+
const host = isLan(metro) ? metro : isAndroidEmulator ? '10.0.2.2' : undefined;
|
|
9797
|
+
if (!host) return url;
|
|
9798
|
+
return url.replace(/\\/\\/(localhost|127\\.0\\.0\\.1)(?=[:/]|$)/, \`//\${host}\`);
|
|
9799
|
+
}
|
|
9800
|
+
|
|
9801
|
+
export const config = {
|
|
9802
|
+
scheme: built.scheme,
|
|
9803
|
+
apiUrl: reachable(built.apiUrl),
|
|
9804
|
+
authUrl: reachable(built.authUrl),
|
|
9805
|
+
};
|
|
9743
9806
|
`;
|
|
9744
9807
|
const queryClientTs = `import { QueryClient } from '@tanstack/react-query';
|
|
9745
9808
|
|
|
@@ -9751,21 +9814,23 @@ export const queryClient = new QueryClient({
|
|
|
9751
9814
|
},
|
|
9752
9815
|
});
|
|
9753
9816
|
`;
|
|
9754
|
-
const authClientTs = `import { expoClient } from '@better-auth/expo/client';
|
|
9817
|
+
const authClientTs = `import { expoClient, getSetCookie } from '@better-auth/expo/client';
|
|
9755
9818
|
import { magicLinkClient } from 'better-auth/client/plugins';
|
|
9756
9819
|
import { createAuthClient } from 'better-auth/react';
|
|
9820
|
+
import * as Linking from 'expo-linking';
|
|
9757
9821
|
import * as SecureStore from 'expo-secure-store';
|
|
9822
|
+
import { useEffect } from 'react';
|
|
9758
9823
|
|
|
9759
9824
|
import { config } from '@/config.ts';
|
|
9760
9825
|
|
|
9761
|
-
export const STORAGE_PREFIX =
|
|
9826
|
+
export const STORAGE_PREFIX = config.scheme;
|
|
9762
9827
|
export const COOKIE_STORE_KEY = \`\${STORAGE_PREFIX}_cookie\`;
|
|
9763
9828
|
|
|
9764
9829
|
export const authClient = createAuthClient({
|
|
9765
9830
|
baseURL: config.authUrl,
|
|
9766
9831
|
plugins: [
|
|
9767
9832
|
expoClient({
|
|
9768
|
-
scheme:
|
|
9833
|
+
scheme: config.scheme,
|
|
9769
9834
|
storagePrefix: STORAGE_PREFIX,
|
|
9770
9835
|
storage: SecureStore,
|
|
9771
9836
|
}),
|
|
@@ -9775,6 +9840,36 @@ export const authClient = createAuthClient({
|
|
|
9775
9840
|
|
|
9776
9841
|
export const { signIn, useSession } = authClient;
|
|
9777
9842
|
|
|
9843
|
+
/**
|
|
9844
|
+
* Sign in from the emailed link.
|
|
9845
|
+
*
|
|
9846
|
+
* The link opens in the phone's browser, which is what receives the session
|
|
9847
|
+
* cookie — not the app. The auth server's Expo plugin carries the cookie on the
|
|
9848
|
+
* redirect back into the app (\`?cookie=\`), and this stores it where the
|
|
9849
|
+
* Expo client keeps its own, merged with what is already there, then tells the
|
|
9850
|
+
* client the session changed. The Expo client does this itself only for a
|
|
9851
|
+
* sign-in it opened (social); a link opened from mail arrives as a deep link.
|
|
9852
|
+
*
|
|
9853
|
+
* Called once, from the root layout.
|
|
9854
|
+
*/
|
|
9855
|
+
export function useSessionFromLink() {
|
|
9856
|
+
const url = Linking.useLinkingURL();
|
|
9857
|
+
|
|
9858
|
+
useEffect(() => {
|
|
9859
|
+
const cookie = url ? Linking.parse(url).queryParams?.cookie : undefined;
|
|
9860
|
+
if (typeof cookie !== 'string') return;
|
|
9861
|
+
|
|
9862
|
+
void (async () => {
|
|
9863
|
+
const previous = await SecureStore.getItemAsync(COOKIE_STORE_KEY);
|
|
9864
|
+
await SecureStore.setItemAsync(
|
|
9865
|
+
COOKIE_STORE_KEY,
|
|
9866
|
+
getSetCookie(cookie, previous ?? undefined),
|
|
9867
|
+
);
|
|
9868
|
+
authClient.$store.notify('$sessionSignal');
|
|
9869
|
+
})();
|
|
9870
|
+
}, [url]);
|
|
9871
|
+
}
|
|
9872
|
+
|
|
9778
9873
|
export async function signOut() {
|
|
9779
9874
|
try {
|
|
9780
9875
|
await authClient.signOut();
|
|
@@ -9841,7 +9936,7 @@ import { StatusBar } from 'expo-status-bar';
|
|
|
9841
9936
|
import { useEffect, useMemo } from 'react';
|
|
9842
9937
|
import 'react-native-reanimated';
|
|
9843
9938
|
|
|
9844
|
-
import { authClient } from '@/lib/auth-client.ts';
|
|
9939
|
+
import { authClient, useSessionFromLink } from '@/lib/auth-client.ts';
|
|
9845
9940
|
import { ApiProvider } from '@/lib/api-context.tsx';
|
|
9846
9941
|
import { createAppApi } from '@/lib/api.ts';
|
|
9847
9942
|
import { queryClient } from '@/lib/query-client.ts';
|
|
@@ -9866,6 +9961,8 @@ function AuthGate({ children }: { children: React.ReactNode }) {
|
|
|
9866
9961
|
|
|
9867
9962
|
export default function RootLayout() {
|
|
9868
9963
|
const api = useMemo(() => createAppApi(), []);
|
|
9964
|
+
// The emailed sign-in link comes back as a deep link carrying the session.
|
|
9965
|
+
useSessionFromLink();
|
|
9869
9966
|
|
|
9870
9967
|
return (
|
|
9871
9968
|
<QueryClientProvider client={queryClient}>
|
|
@@ -10387,7 +10484,8 @@ function rootConstructDependencies(options) {
|
|
|
10387
10484
|
pg: DEPENDENCY_VERSIONS.pg,
|
|
10388
10485
|
...options.constructs.uploads ? { "@geekmidas/storage": v["@geekmidas/storage"] } : {},
|
|
10389
10486
|
...options.constructs.mail ? { "@geekmidas/emailkit": v["@geekmidas/emailkit"] } : {},
|
|
10390
|
-
...options.constructs.cache ? { "@geekmidas/cache": v["@geekmidas/cache"] } : {}
|
|
10487
|
+
...options.constructs.cache ? { "@geekmidas/cache": v["@geekmidas/cache"] } : {},
|
|
10488
|
+
...options.frontendFramework === "expo" ? { "@better-auth/expo": DEPENDENCY_VERSIONS["better-auth"] } : {}
|
|
10391
10489
|
};
|
|
10392
10490
|
}
|
|
10393
10491
|
/**
|
|
@@ -10820,7 +10918,7 @@ export const authDb = database.schema<Record<string, never>, 'AuthDb'>('AuthDb')
|
|
|
10820
10918
|
files.push(usersMigration());
|
|
10821
10919
|
files.push({
|
|
10822
10920
|
path: "constructs/auth.ts",
|
|
10823
|
-
content:
|
|
10921
|
+
content: `${frontendFramework === "expo" ? "import { expo } from '@better-auth/expo';\n" : ""}import { BetterAuth } from '@geekmidas/constructs/auth';
|
|
10824
10922
|
import { authDb } from './database.ts';
|
|
10825
10923
|
|
|
10826
10924
|
/**
|
|
@@ -10835,7 +10933,11 @@ import { authDb } from './database.ts';
|
|
|
10835
10933
|
export const auth = new BetterAuth('Auth', {
|
|
10836
10934
|
path: 'apps/auth',
|
|
10837
10935
|
database: authDb,
|
|
10838
|
-
basePath: '/api/auth'
|
|
10936
|
+
basePath: '/api/auth',${frontendFramework === "expo" ? `
|
|
10937
|
+
// The mobile app signs in through Better Auth's Expo plugin: it sends its
|
|
10938
|
+
// scheme as the origin, and this is what reads it. The construct refuses to
|
|
10939
|
+
// start without it once a MobileApp depends on this server.
|
|
10940
|
+
options: { plugins: [expo()] },` : ""}
|
|
10839
10941
|
});
|
|
10840
10942
|
`
|
|
10841
10943
|
});
|
|
@@ -10864,6 +10966,26 @@ export const api = new RestApi('Api', {
|
|
|
10864
10966
|
`
|
|
10865
10967
|
});
|
|
10866
10968
|
const variant = frontendFramework === "tanstack-start" ? "{ path: 'apps/web', variant: 'tanstack' }" : frontendFramework === "expo" ? void 0 : "{ path: 'apps/web', variant: 'next' }";
|
|
10969
|
+
if (frontendFramework === "expo") files.push({
|
|
10970
|
+
path: "constructs/app.ts",
|
|
10971
|
+
content: `import { MobileApp } from '@geekmidas/constructs/mobile-app';
|
|
10972
|
+
import { api } from './api.ts';
|
|
10973
|
+
import { auth } from './auth.ts';
|
|
10974
|
+
|
|
10975
|
+
/**
|
|
10976
|
+
* The mobile app — a construct like a site, which is what makes it an app.
|
|
10977
|
+
*
|
|
10978
|
+
* \`.dependsOn()\` is the single fact behind what a mobile app otherwise has
|
|
10979
|
+
* written down by hand: its API and auth URLs, its URL scheme, the
|
|
10980
|
+
* scheme in the auth server's trusted origins, Better Auth's Expo plugin, and
|
|
10981
|
+
* sign-in links a phone can open.
|
|
10982
|
+
*/
|
|
10983
|
+
export const app = new MobileApp('App', { path: 'apps/app' }).dependsOn([
|
|
10984
|
+
api,
|
|
10985
|
+
auth,
|
|
10986
|
+
]);
|
|
10987
|
+
`
|
|
10988
|
+
});
|
|
10867
10989
|
if (variant !== void 0) files.push({
|
|
10868
10990
|
path: "constructs/site.ts",
|
|
10869
10991
|
content: `import { StaticSite } from '@geekmidas/constructs/site';
|
|
@@ -15038,7 +15160,7 @@ var NoStoredSecrets = class extends Error {
|
|
|
15038
15160
|
}
|
|
15039
15161
|
};
|
|
15040
15162
|
async function reconcile(secrets, workspace, stage) {
|
|
15041
|
-
const { derivedContainers } = await Promise.resolve().then(() => require("./workspace-
|
|
15163
|
+
const { derivedContainers } = await Promise.resolve().then(() => require("./workspace-C0X8xSXV.cjs")).then((n) => n.workspace_exports);
|
|
15042
15164
|
const result = require_reconcile.reconcileMissingSecrets(secrets, workspace, await derivedContainers(workspace, stage));
|
|
15043
15165
|
return result ? {
|
|
15044
15166
|
secrets: result.secrets,
|
|
@@ -15437,7 +15559,7 @@ async function testCommand(options = {}) {
|
|
|
15437
15559
|
* starts without the reference data the app needs.
|
|
15438
15560
|
*/
|
|
15439
15561
|
async function migrateTestStage(workspace, env) {
|
|
15440
|
-
const { migrateAndSeed } = await Promise.resolve().then(() => require("./migrate-
|
|
15562
|
+
const { migrateAndSeed } = await Promise.resolve().then(() => require("./migrate-BsW4xBEO.cjs"));
|
|
15441
15563
|
const { discover } = await Promise.resolve().then(() => require("./discover-CroTIm0K.cjs")).then((n) => n.discover_exports);
|
|
15442
15564
|
const sources = {};
|
|
15443
15565
|
const manifest = await discover({
|
|
@@ -15882,7 +16004,7 @@ program.command("migrate").description("Apply each database construct's migratio
|
|
|
15882
16004
|
try {
|
|
15883
16005
|
const globalOptions = program.opts();
|
|
15884
16006
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
15885
|
-
const { migrateCommand } = await Promise.resolve().then(() => require("./migrate-
|
|
16007
|
+
const { migrateCommand } = await Promise.resolve().then(() => require("./migrate-BsW4xBEO.cjs"));
|
|
15886
16008
|
await migrateCommand({
|
|
15887
16009
|
...options,
|
|
15888
16010
|
...construct ? { construct } : {}
|
|
@@ -15896,7 +16018,7 @@ program.command("seed").description("Migrate, then run each database construct's
|
|
|
15896
16018
|
try {
|
|
15897
16019
|
const globalOptions = program.opts();
|
|
15898
16020
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
15899
|
-
const { seedCommand } = await Promise.resolve().then(() => require("./migrate-
|
|
16021
|
+
const { seedCommand } = await Promise.resolve().then(() => require("./migrate-BsW4xBEO.cjs"));
|
|
15900
16022
|
await seedCommand({
|
|
15901
16023
|
...options,
|
|
15902
16024
|
...construct ? { construct } : {}
|
|
@@ -15910,7 +16032,7 @@ program.command("migration").description("Write a construct's next migration: an
|
|
|
15910
16032
|
try {
|
|
15911
16033
|
const globalOptions = program.opts();
|
|
15912
16034
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
15913
|
-
const { migrationCommand } = await Promise.resolve().then(() => require("./migrate-
|
|
16035
|
+
const { migrationCommand } = await Promise.resolve().then(() => require("./migrate-BsW4xBEO.cjs"));
|
|
15914
16036
|
await migrationCommand({
|
|
15915
16037
|
construct,
|
|
15916
16038
|
...name ? { name } : {}
|
|
@@ -16078,7 +16200,7 @@ program.command("secrets:reconcile").description("Backfill missing custom secret
|
|
|
16078
16200
|
console.error(`No secrets found for stage "${stage}". Run "gkm secrets:init --stage ${stage}" first.`);
|
|
16079
16201
|
process.exit(1);
|
|
16080
16202
|
}
|
|
16081
|
-
const { derivedContainers } = await Promise.resolve().then(() => require("./workspace-
|
|
16203
|
+
const { derivedContainers } = await Promise.resolve().then(() => require("./workspace-C0X8xSXV.cjs")).then((n) => n.workspace_exports);
|
|
16082
16204
|
const result = reconcileMissingSecrets(secrets, workspace, await derivedContainers(workspace, stage));
|
|
16083
16205
|
if (!result) {
|
|
16084
16206
|
console.log(`\n✓ Secrets for stage "${stage}" are up-to-date`);
|
|
@@ -16137,7 +16259,7 @@ program.command("deploy:github").description("Let GitHub Actions deploy a stage:
|
|
|
16137
16259
|
try {
|
|
16138
16260
|
const globalOptions = program.opts();
|
|
16139
16261
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
16140
|
-
const { deployGithubCommand } = await Promise.resolve().then(() => require("./github-
|
|
16262
|
+
const { deployGithubCommand } = await Promise.resolve().then(() => require("./github-tZmMZ7td.cjs"));
|
|
16141
16263
|
await deployGithubCommand(options);
|
|
16142
16264
|
} catch (error) {
|
|
16143
16265
|
console.error(formatError(error));
|