@geekmidas/cli 10.0.0-alpha.33 → 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 +221 -86
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +222 -87
- package/dist/index.mjs.map +1 -1
- package/dist/{migrate-D2rtGKYi.mjs → migrate-C-D9xIbb.mjs} +3 -3
- package/dist/{migrate-D2rtGKYi.mjs.map → migrate-C-D9xIbb.mjs.map} +1 -1
- package/dist/{migrate-BkytlJlU.cjs → migrate-nlvT-GnY.cjs} +3 -3
- package/dist/{migrate-BkytlJlU.cjs.map → migrate-nlvT-GnY.cjs.map} +1 -1
- package/dist/openapi.cjs +1 -1
- package/dist/openapi.mjs +1 -1
- package/dist/{workspace-BNurJoFx.mjs → workspace-CIiGijNi.mjs} +91 -16
- package/dist/workspace-CIiGijNi.mjs.map +1 -0
- package/dist/{workspace-C8TbIWxO.cjs → workspace-CRjZkCTW.cjs} +90 -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-BNurJoFx.mjs.map +0 -1
- package/dist/workspace-C8TbIWxO.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-CRjZkCTW.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.35";
|
|
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-nlvT-GnY.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-nlvT-GnY.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}`);
|
|
@@ -3107,6 +3114,7 @@ const PROVISIONERS = {
|
|
|
3107
3114
|
context.deferred.push(...statementsFor({
|
|
3108
3115
|
id: declaration.id,
|
|
3109
3116
|
database: postgres.databaseName,
|
|
3117
|
+
ownsDatabase: true,
|
|
3110
3118
|
runtime,
|
|
3111
3119
|
owner,
|
|
3112
3120
|
...reads ? { reader: (0, _geekmidas_db_pg_roles.readerRole)(runtime) } : {},
|
|
@@ -3317,9 +3325,13 @@ const PROVISIONERS = {
|
|
|
3317
3325
|
if (!url) throw new SurfaceHasNoAddress(declaration.id);
|
|
3318
3326
|
const origins = [...new Set((0, _geekmidas_manifest.dependentsOf)(context.manifest, declaration.id).map((caller) => context.addresses?.[caller]).filter((address) => Boolean(address)))].sort();
|
|
3319
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
|
+
});
|
|
3320
3332
|
return { provides: {
|
|
3321
3333
|
[(0, _geekmidas_manifest.provideKey)(declaration.id, "url")]: url,
|
|
3322
|
-
[(0, _geekmidas_manifest.provideKey)(declaration.id, "trustedOrigins")]: origins.join(","),
|
|
3334
|
+
[(0, _geekmidas_manifest.provideKey)(declaration.id, "trustedOrigins")]: [...origins, ...schemes].join(","),
|
|
3323
3335
|
...domain ? { [(0, _geekmidas_manifest.provideKey)(declaration.id, "cookieDomain")]: domain } : {}
|
|
3324
3336
|
} };
|
|
3325
3337
|
}
|
|
@@ -3342,6 +3354,7 @@ function statementsFor(options) {
|
|
|
3342
3354
|
owner,
|
|
3343
3355
|
...reader ? { reader } : {},
|
|
3344
3356
|
schema,
|
|
3357
|
+
...options.ownsDatabase ? { database } : {},
|
|
3345
3358
|
passwords: {
|
|
3346
3359
|
runtime: derivedPassword(context, runtime),
|
|
3347
3360
|
owner: derivedPassword(context, owner),
|
|
@@ -8009,11 +8022,11 @@ function routesGlob(structure) {
|
|
|
8009
8022
|
}
|
|
8010
8023
|
}
|
|
8011
8024
|
/**
|
|
8012
|
-
* Plain ids — `Database`, not `
|
|
8025
|
+
* Plain ids — `Database`, not `Shop`.
|
|
8013
8026
|
*
|
|
8014
8027
|
* The workspace `name` already scopes every physical name
|
|
8015
|
-
* (`production-
|
|
8016
|
-
* 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
|
|
8017
8030
|
* workspace the constructs are declared once, at its root, so there is no
|
|
8018
8031
|
* second app's `Database` to collide with.
|
|
8019
8032
|
*/
|
|
@@ -8739,30 +8752,32 @@ const TOOLCHAIN_VERSIONS = {
|
|
|
8739
8752
|
"@storybook/react-vite": "^10.6.0"
|
|
8740
8753
|
};
|
|
8741
8754
|
/**
|
|
8742
|
-
* The Expo app's packages, which track an Expo SDK release (
|
|
8743
|
-
* 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
|
|
8744
8757
|
* React included, so they move when the SDK does.
|
|
8745
8758
|
*/
|
|
8746
8759
|
const EXPO_VERSIONS = {
|
|
8747
|
-
expo: "~
|
|
8748
|
-
"expo-constants": "~
|
|
8749
|
-
"expo-dev-client": "~
|
|
8750
|
-
"expo-
|
|
8751
|
-
"expo-
|
|
8752
|
-
"expo-
|
|
8753
|
-
"expo-
|
|
8754
|
-
"expo-
|
|
8755
|
-
|
|
8756
|
-
|
|
8757
|
-
"react-
|
|
8758
|
-
"react-native
|
|
8759
|
-
"react-native-
|
|
8760
|
-
"react-native-
|
|
8761
|
-
"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",
|
|
8762
8777
|
"react-native-web": "~0.21.0",
|
|
8763
8778
|
"@react-navigation/native": "^7.1.0",
|
|
8764
8779
|
"@babel/core": "^7.25.0",
|
|
8765
|
-
"@types/react": "~19.
|
|
8780
|
+
"@types/react": "~19.2.2",
|
|
8766
8781
|
nativewind: "~4.2.0",
|
|
8767
8782
|
tailwindcss: "~3.4.0"
|
|
8768
8783
|
};
|
|
@@ -8794,26 +8809,26 @@ function loadPackageJson() {
|
|
|
8794
8809
|
* Run: pnpm --filter @geekmidas/cli sync-versions
|
|
8795
8810
|
*/
|
|
8796
8811
|
const GEEKMIDAS_VERSIONS = {
|
|
8797
|
-
"@geekmidas/audit": "~10.0.0-alpha.
|
|
8798
|
-
"@geekmidas/auth": "~10.0.0-alpha.
|
|
8799
|
-
"@geekmidas/cache": "~10.0.0-alpha.
|
|
8800
|
-
"@geekmidas/client": "~10.0.0-alpha.
|
|
8801
|
-
"@geekmidas/cloud": "~10.0.0-alpha.
|
|
8802
|
-
"@geekmidas/constructs": "~10.0.0-alpha.
|
|
8803
|
-
"@geekmidas/db": "~10.0.0-alpha.
|
|
8804
|
-
"@geekmidas/emailkit": "~10.0.0-alpha.
|
|
8805
|
-
"@geekmidas/envkit": "~10.0.0-alpha.
|
|
8806
|
-
"@geekmidas/errors": "~10.0.0-alpha.
|
|
8807
|
-
"@geekmidas/events": "~10.0.0-alpha.
|
|
8808
|
-
"@geekmidas/logger": "~10.0.0-alpha.
|
|
8809
|
-
"@geekmidas/manifest": "~10.0.0-alpha.
|
|
8810
|
-
"@geekmidas/rate-limit": "~10.0.0-alpha.
|
|
8811
|
-
"@geekmidas/schema": "~10.0.0-alpha.
|
|
8812
|
-
"@geekmidas/services": "~10.0.0-alpha.
|
|
8813
|
-
"@geekmidas/storage": "~10.0.0-alpha.
|
|
8814
|
-
"@geekmidas/studio": "~10.0.0-alpha.
|
|
8815
|
-
"@geekmidas/telescope": "~10.0.0-alpha.
|
|
8816
|
-
"@geekmidas/testkit": "~10.0.0-alpha.
|
|
8812
|
+
"@geekmidas/audit": "~10.0.0-alpha.35",
|
|
8813
|
+
"@geekmidas/auth": "~10.0.0-alpha.35",
|
|
8814
|
+
"@geekmidas/cache": "~10.0.0-alpha.35",
|
|
8815
|
+
"@geekmidas/client": "~10.0.0-alpha.35",
|
|
8816
|
+
"@geekmidas/cloud": "~10.0.0-alpha.35",
|
|
8817
|
+
"@geekmidas/constructs": "~10.0.0-alpha.35",
|
|
8818
|
+
"@geekmidas/db": "~10.0.0-alpha.35",
|
|
8819
|
+
"@geekmidas/emailkit": "~10.0.0-alpha.35",
|
|
8820
|
+
"@geekmidas/envkit": "~10.0.0-alpha.35",
|
|
8821
|
+
"@geekmidas/errors": "~10.0.0-alpha.35",
|
|
8822
|
+
"@geekmidas/events": "~10.0.0-alpha.35",
|
|
8823
|
+
"@geekmidas/logger": "~10.0.0-alpha.35",
|
|
8824
|
+
"@geekmidas/manifest": "~10.0.0-alpha.35",
|
|
8825
|
+
"@geekmidas/rate-limit": "~10.0.0-alpha.35",
|
|
8826
|
+
"@geekmidas/schema": "~10.0.0-alpha.35",
|
|
8827
|
+
"@geekmidas/services": "~10.0.0-alpha.35",
|
|
8828
|
+
"@geekmidas/storage": "~10.0.0-alpha.35",
|
|
8829
|
+
"@geekmidas/studio": "~10.0.0-alpha.35",
|
|
8830
|
+
"@geekmidas/telescope": "~10.0.0-alpha.35",
|
|
8831
|
+
"@geekmidas/testkit": "~10.0.0-alpha.35",
|
|
8817
8832
|
"@geekmidas/cli": `~${loadPackageJson().version}`
|
|
8818
8833
|
};
|
|
8819
8834
|
//#endregion
|
|
@@ -9535,8 +9550,7 @@ function generateExpoAppFiles(options) {
|
|
|
9535
9550
|
const apiPackage = `@${options.name}/api`;
|
|
9536
9551
|
const modelsPackage = `@${options.name}/models`;
|
|
9537
9552
|
const slug = options.name.toLowerCase().replace(/[^a-z0-9-]/g, "-");
|
|
9538
|
-
const scheme =
|
|
9539
|
-
const bundleId = `com.${scheme}.app`;
|
|
9553
|
+
const scheme = (0, _geekmidas_manifest.schemeBase)(options.name);
|
|
9540
9554
|
const packageJson = {
|
|
9541
9555
|
name: packageName,
|
|
9542
9556
|
version: "0.0.1",
|
|
@@ -9561,6 +9575,7 @@ function generateExpoAppFiles(options) {
|
|
|
9561
9575
|
expo: EXPO_VERSIONS["expo"],
|
|
9562
9576
|
"expo-constants": EXPO_VERSIONS["expo-constants"],
|
|
9563
9577
|
"expo-dev-client": EXPO_VERSIONS["expo-dev-client"],
|
|
9578
|
+
"expo-device": EXPO_VERSIONS["expo-device"],
|
|
9564
9579
|
"expo-linking": EXPO_VERSIONS["expo-linking"],
|
|
9565
9580
|
"expo-router": EXPO_VERSIONS["expo-router"],
|
|
9566
9581
|
"expo-secure-store": EXPO_VERSIONS["expo-secure-store"],
|
|
@@ -9572,6 +9587,7 @@ function generateExpoAppFiles(options) {
|
|
|
9572
9587
|
"react-native": EXPO_VERSIONS["react-native"],
|
|
9573
9588
|
"react-native-gesture-handler": EXPO_VERSIONS["react-native-gesture-handler"],
|
|
9574
9589
|
"react-native-reanimated": EXPO_VERSIONS["react-native-reanimated"],
|
|
9590
|
+
"react-native-worklets": EXPO_VERSIONS["react-native-worklets"],
|
|
9575
9591
|
"react-native-safe-area-context": EXPO_VERSIONS["react-native-safe-area-context"],
|
|
9576
9592
|
"react-native-screens": EXPO_VERSIONS["react-native-screens"],
|
|
9577
9593
|
"react-native-web": EXPO_VERSIONS["react-native-web"],
|
|
@@ -9584,27 +9600,53 @@ function generateExpoAppFiles(options) {
|
|
|
9584
9600
|
}
|
|
9585
9601
|
};
|
|
9586
9602
|
const appConfig = `import type { ConfigContext, ExpoConfig } from '@expo/config';
|
|
9603
|
+
import { EnvironmentParser } from '@geekmidas/envkit';
|
|
9587
9604
|
|
|
9588
9605
|
/**
|
|
9589
|
-
*
|
|
9590
|
-
*
|
|
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 for this stage — \`${scheme}\` deployed,
|
|
9611
|
+
* \`${scheme}-dev\` locally, so a development build and the store build on
|
|
9612
|
+
* one phone never answer each other's links.
|
|
9613
|
+
* - \`EXPO_PUBLIC_API_URL\` / \`EXPO_PUBLIC_AUTH_URL\`: where the servers
|
|
9614
|
+
* answer. Locally that is their own port on \`localhost\`, which
|
|
9615
|
+
* \`config.ts\` points at the machine Metro was served from.
|
|
9616
|
+
*
|
|
9617
|
+
* Read at runtime from \`Constants.expoConfig.extra.config\` — see \`config.ts\`.
|
|
9591
9618
|
*/
|
|
9592
|
-
|
|
9619
|
+
const config = new EnvironmentParser({ ...process.env })
|
|
9620
|
+
.create((get) => ({
|
|
9621
|
+
scheme: get('APP_SCHEME').string(),
|
|
9622
|
+
apiUrl: get('EXPO_PUBLIC_API_URL').string(),
|
|
9623
|
+
authUrl: get('EXPO_PUBLIC_AUTH_URL').string(),
|
|
9624
|
+
}))
|
|
9625
|
+
.parse();
|
|
9626
|
+
|
|
9627
|
+
export type Config = typeof config;
|
|
9628
|
+
|
|
9629
|
+
// A bundle id and an Android package allow no hyphens; one per scheme, so
|
|
9630
|
+
// each stage installs beside the others.
|
|
9631
|
+
const bundleId = \`com.\${config.scheme.replace(/[^a-z0-9]/g, '')}.app\`;
|
|
9632
|
+
|
|
9633
|
+
export default function expoConfig({ config: base }: ConfigContext): ExpoConfig {
|
|
9593
9634
|
return {
|
|
9635
|
+
...base,
|
|
9594
9636
|
name: '${options.name}',
|
|
9595
9637
|
slug: '${slug}',
|
|
9596
9638
|
version: '0.0.1',
|
|
9597
9639
|
orientation: 'portrait',
|
|
9598
9640
|
icon: './assets/icon.png',
|
|
9599
|
-
scheme:
|
|
9641
|
+
scheme: config.scheme,
|
|
9600
9642
|
userInterfaceStyle: 'automatic',
|
|
9601
9643
|
newArchEnabled: true,
|
|
9602
9644
|
ios: {
|
|
9603
9645
|
supportsTablet: true,
|
|
9604
|
-
bundleIdentifier:
|
|
9646
|
+
bundleIdentifier: bundleId,
|
|
9605
9647
|
},
|
|
9606
9648
|
android: {
|
|
9607
|
-
package:
|
|
9649
|
+
package: bundleId,
|
|
9608
9650
|
adaptiveIcon: {
|
|
9609
9651
|
foregroundImage: './assets/adaptive-icon.png',
|
|
9610
9652
|
backgroundColor: '#ffffff',
|
|
@@ -9630,6 +9672,7 @@ export default function config(_context: ConfigContext): ExpoConfig {
|
|
|
9630
9672
|
experiments: {
|
|
9631
9673
|
typedRoutes: true,
|
|
9632
9674
|
},
|
|
9675
|
+
extra: { ...base.extra, config },
|
|
9633
9676
|
};
|
|
9634
9677
|
}
|
|
9635
9678
|
`;
|
|
@@ -9642,16 +9685,13 @@ export default function config(_context: ConfigContext): ExpoConfig {
|
|
|
9642
9685
|
dev: {
|
|
9643
9686
|
developmentClient: true,
|
|
9644
9687
|
distribution: "internal",
|
|
9645
|
-
environment: "development"
|
|
9646
|
-
env: {
|
|
9647
|
-
EXPO_PUBLIC_API_URL: "http://localhost:3000",
|
|
9648
|
-
EXPO_PUBLIC_AUTH_URL: "http://localhost:3002"
|
|
9649
|
-
}
|
|
9688
|
+
environment: "development"
|
|
9650
9689
|
},
|
|
9651
9690
|
preview: {
|
|
9652
9691
|
distribution: "internal",
|
|
9653
9692
|
environment: "preview",
|
|
9654
9693
|
env: {
|
|
9694
|
+
APP_SCHEME: scheme,
|
|
9655
9695
|
EXPO_PUBLIC_API_URL: "https://api.example.com",
|
|
9656
9696
|
EXPO_PUBLIC_AUTH_URL: "https://auth.example.com"
|
|
9657
9697
|
}
|
|
@@ -9660,6 +9700,7 @@ export default function config(_context: ConfigContext): ExpoConfig {
|
|
|
9660
9700
|
autoIncrement: true,
|
|
9661
9701
|
environment: "production",
|
|
9662
9702
|
env: {
|
|
9703
|
+
APP_SCHEME: scheme,
|
|
9663
9704
|
EXPO_PUBLIC_API_URL: "https://api.example.com",
|
|
9664
9705
|
EXPO_PUBLIC_AUTH_URL: "https://auth.example.com"
|
|
9665
9706
|
}
|
|
@@ -9721,23 +9762,49 @@ export default {
|
|
|
9721
9762
|
`;
|
|
9722
9763
|
const expoEnvDts = `/// <reference types="expo/types" />\n`;
|
|
9723
9764
|
const nativewindEnvDts = `/// <reference types="nativewind/types" />\n`;
|
|
9724
|
-
const configTs = `import
|
|
9765
|
+
const configTs = `import Constants from 'expo-constants';
|
|
9766
|
+
import * as Device from 'expo-device';
|
|
9767
|
+
import { Platform } from 'react-native';
|
|
9768
|
+
|
|
9769
|
+
import type { Config } from './app.config.ts';
|
|
9770
|
+
|
|
9771
|
+
const built = Constants.expoConfig?.extra?.config as Config;
|
|
9772
|
+
|
|
9773
|
+
/** The host this device loaded the bundle from: Metro's, as the device sees it. */
|
|
9774
|
+
const metro = (Constants.expoConfig?.hostUri ?? '').split(':')[0];
|
|
9725
9775
|
|
|
9726
9776
|
/**
|
|
9727
|
-
*
|
|
9728
|
-
*
|
|
9777
|
+
* A private address on the local network — what a phone on the same Wi-Fi
|
|
9778
|
+
* reaches this machine on. A tunnel's \`*.exp.direct\` host is not one: a
|
|
9779
|
+
* tunnel forwards only Metro, so the servers are not behind it.
|
|
9729
9780
|
*/
|
|
9730
|
-
const
|
|
9731
|
-
|
|
9732
|
-
|
|
9733
|
-
|
|
9781
|
+
const isLan = (host: string) =>
|
|
9782
|
+
/^10\\./.test(host) ||
|
|
9783
|
+
/^192\\.168\\./.test(host) ||
|
|
9784
|
+
/^172\\.(1[6-9]|2\\d|3[01])\\./.test(host);
|
|
9734
9785
|
|
|
9735
|
-
|
|
9736
|
-
|
|
9737
|
-
|
|
9738
|
-
|
|
9739
|
-
|
|
9740
|
-
|
|
9786
|
+
/** \`localhost\` from inside the Android emulator is the emulator itself. */
|
|
9787
|
+
const isAndroidEmulator = Platform.OS === 'android' && !Device.isDevice;
|
|
9788
|
+
|
|
9789
|
+
/**
|
|
9790
|
+
* A server's URL as this device reaches it.
|
|
9791
|
+
*
|
|
9792
|
+
* Locally the servers are injected as \`http://localhost:<port>\`. That is this
|
|
9793
|
+
* device only on the iOS simulator. The Android emulator reaches this machine
|
|
9794
|
+
* at \`10.0.2.2\`, and a phone at the LAN address Metro was served from.
|
|
9795
|
+
* Deployed URLs have no \`localhost\` in them and pass through untouched.
|
|
9796
|
+
*/
|
|
9797
|
+
export function reachable(url: string): string {
|
|
9798
|
+
const host = isLan(metro) ? metro : isAndroidEmulator ? '10.0.2.2' : undefined;
|
|
9799
|
+
if (!host) return url;
|
|
9800
|
+
return url.replace(/\\/\\/(localhost|127\\.0\\.0\\.1)(?=[:/]|$)/, \`//\${host}\`);
|
|
9801
|
+
}
|
|
9802
|
+
|
|
9803
|
+
export const config = {
|
|
9804
|
+
scheme: built.scheme,
|
|
9805
|
+
apiUrl: reachable(built.apiUrl),
|
|
9806
|
+
authUrl: reachable(built.authUrl),
|
|
9807
|
+
};
|
|
9741
9808
|
`;
|
|
9742
9809
|
const queryClientTs = `import { QueryClient } from '@tanstack/react-query';
|
|
9743
9810
|
|
|
@@ -9749,21 +9816,23 @@ export const queryClient = new QueryClient({
|
|
|
9749
9816
|
},
|
|
9750
9817
|
});
|
|
9751
9818
|
`;
|
|
9752
|
-
const authClientTs = `import { expoClient } from '@better-auth/expo/client';
|
|
9819
|
+
const authClientTs = `import { expoClient, getSetCookie } from '@better-auth/expo/client';
|
|
9753
9820
|
import { magicLinkClient } from 'better-auth/client/plugins';
|
|
9754
9821
|
import { createAuthClient } from 'better-auth/react';
|
|
9822
|
+
import * as Linking from 'expo-linking';
|
|
9755
9823
|
import * as SecureStore from 'expo-secure-store';
|
|
9824
|
+
import { useEffect } from 'react';
|
|
9756
9825
|
|
|
9757
9826
|
import { config } from '@/config.ts';
|
|
9758
9827
|
|
|
9759
|
-
export const STORAGE_PREFIX =
|
|
9828
|
+
export const STORAGE_PREFIX = config.scheme;
|
|
9760
9829
|
export const COOKIE_STORE_KEY = \`\${STORAGE_PREFIX}_cookie\`;
|
|
9761
9830
|
|
|
9762
9831
|
export const authClient = createAuthClient({
|
|
9763
9832
|
baseURL: config.authUrl,
|
|
9764
9833
|
plugins: [
|
|
9765
9834
|
expoClient({
|
|
9766
|
-
scheme:
|
|
9835
|
+
scheme: config.scheme,
|
|
9767
9836
|
storagePrefix: STORAGE_PREFIX,
|
|
9768
9837
|
storage: SecureStore,
|
|
9769
9838
|
}),
|
|
@@ -9773,6 +9842,36 @@ export const authClient = createAuthClient({
|
|
|
9773
9842
|
|
|
9774
9843
|
export const { signIn, useSession } = authClient;
|
|
9775
9844
|
|
|
9845
|
+
/**
|
|
9846
|
+
* Sign in from the emailed link.
|
|
9847
|
+
*
|
|
9848
|
+
* The link opens in the phone's browser, which is what receives the session
|
|
9849
|
+
* cookie — not the app. The auth server's Expo plugin carries the cookie on the
|
|
9850
|
+
* redirect back into the app (\`?cookie=\`), and this stores it where the
|
|
9851
|
+
* Expo client keeps its own, merged with what is already there, then tells the
|
|
9852
|
+
* client the session changed. The Expo client does this itself only for a
|
|
9853
|
+
* sign-in it opened (social); a link opened from mail arrives as a deep link.
|
|
9854
|
+
*
|
|
9855
|
+
* Called once, from the root layout.
|
|
9856
|
+
*/
|
|
9857
|
+
export function useSessionFromLink() {
|
|
9858
|
+
const url = Linking.useLinkingURL();
|
|
9859
|
+
|
|
9860
|
+
useEffect(() => {
|
|
9861
|
+
const cookie = url ? Linking.parse(url).queryParams?.cookie : undefined;
|
|
9862
|
+
if (typeof cookie !== 'string') return;
|
|
9863
|
+
|
|
9864
|
+
void (async () => {
|
|
9865
|
+
const previous = await SecureStore.getItemAsync(COOKIE_STORE_KEY);
|
|
9866
|
+
await SecureStore.setItemAsync(
|
|
9867
|
+
COOKIE_STORE_KEY,
|
|
9868
|
+
getSetCookie(cookie, previous ?? undefined),
|
|
9869
|
+
);
|
|
9870
|
+
authClient.$store.notify('$sessionSignal');
|
|
9871
|
+
})();
|
|
9872
|
+
}, [url]);
|
|
9873
|
+
}
|
|
9874
|
+
|
|
9776
9875
|
export async function signOut() {
|
|
9777
9876
|
try {
|
|
9778
9877
|
await authClient.signOut();
|
|
@@ -9839,7 +9938,7 @@ import { StatusBar } from 'expo-status-bar';
|
|
|
9839
9938
|
import { useEffect, useMemo } from 'react';
|
|
9840
9939
|
import 'react-native-reanimated';
|
|
9841
9940
|
|
|
9842
|
-
import { authClient } from '@/lib/auth-client.ts';
|
|
9941
|
+
import { authClient, useSessionFromLink } from '@/lib/auth-client.ts';
|
|
9843
9942
|
import { ApiProvider } from '@/lib/api-context.tsx';
|
|
9844
9943
|
import { createAppApi } from '@/lib/api.ts';
|
|
9845
9944
|
import { queryClient } from '@/lib/query-client.ts';
|
|
@@ -9864,6 +9963,8 @@ function AuthGate({ children }: { children: React.ReactNode }) {
|
|
|
9864
9963
|
|
|
9865
9964
|
export default function RootLayout() {
|
|
9866
9965
|
const api = useMemo(() => createAppApi(), []);
|
|
9966
|
+
// The emailed sign-in link comes back as a deep link carrying the session.
|
|
9967
|
+
useSessionFromLink();
|
|
9867
9968
|
|
|
9868
9969
|
return (
|
|
9869
9970
|
<QueryClientProvider client={queryClient}>
|
|
@@ -10385,7 +10486,20 @@ function rootConstructDependencies(options) {
|
|
|
10385
10486
|
pg: DEPENDENCY_VERSIONS.pg,
|
|
10386
10487
|
...options.constructs.uploads ? { "@geekmidas/storage": v["@geekmidas/storage"] } : {},
|
|
10387
10488
|
...options.constructs.mail ? { "@geekmidas/emailkit": v["@geekmidas/emailkit"] } : {},
|
|
10388
|
-
...options.constructs.cache ? { "@geekmidas/cache": v["@geekmidas/cache"] } : {}
|
|
10489
|
+
...options.constructs.cache ? { "@geekmidas/cache": v["@geekmidas/cache"] } : {},
|
|
10490
|
+
...options.frontendFramework === "expo" ? { "@better-auth/expo": DEPENDENCY_VERSIONS["better-auth"] } : {}
|
|
10491
|
+
};
|
|
10492
|
+
}
|
|
10493
|
+
/**
|
|
10494
|
+
* What `test/factories/` imports at the root: the factory and faker, and
|
|
10495
|
+
* Kysely's types where the root does not already install it for the
|
|
10496
|
+
* constructs.
|
|
10497
|
+
*/
|
|
10498
|
+
function rootFactoryDependencies(options) {
|
|
10499
|
+
return {
|
|
10500
|
+
"@geekmidas/testkit": GEEKMIDAS_VERSIONS["@geekmidas/testkit"],
|
|
10501
|
+
"@faker-js/faker": DEPENDENCY_VERSIONS["@faker-js/faker"],
|
|
10502
|
+
...options.template === "fullstack" ? {} : { kysely: DEPENDENCY_VERSIONS.kysely }
|
|
10389
10503
|
};
|
|
10390
10504
|
}
|
|
10391
10505
|
/**
|
|
@@ -10427,6 +10541,7 @@ function generateMonorepoFiles(options, _template) {
|
|
|
10427
10541
|
turbo: DEPENDENCY_VERSIONS["turbo"],
|
|
10428
10542
|
typescript: TOOLCHAIN_VERSIONS["typescript"],
|
|
10429
10543
|
vitest: TOOLCHAIN_VERSIONS["vitest"],
|
|
10544
|
+
...options.constructs.database ? rootFactoryDependencies(options) : {},
|
|
10430
10545
|
...deploy.devDependencies
|
|
10431
10546
|
}
|
|
10432
10547
|
};
|
|
@@ -10849,6 +10964,26 @@ export const api = new RestApi('Api', {
|
|
|
10849
10964
|
`
|
|
10850
10965
|
});
|
|
10851
10966
|
const variant = frontendFramework === "tanstack-start" ? "{ path: 'apps/web', variant: 'tanstack' }" : frontendFramework === "expo" ? void 0 : "{ path: 'apps/web', variant: 'next' }";
|
|
10967
|
+
if (frontendFramework === "expo") files.push({
|
|
10968
|
+
path: "constructs/app.ts",
|
|
10969
|
+
content: `import { MobileApp } from '@geekmidas/constructs/mobile-app';
|
|
10970
|
+
import { api } from './api.ts';
|
|
10971
|
+
import { auth } from './auth.ts';
|
|
10972
|
+
|
|
10973
|
+
/**
|
|
10974
|
+
* The mobile app — a construct like a site, which is what makes it an app.
|
|
10975
|
+
*
|
|
10976
|
+
* \`.dependsOn()\` is the single fact behind what a mobile app otherwise has
|
|
10977
|
+
* written down by hand: its API and auth URLs, its URL scheme per stage, the
|
|
10978
|
+
* scheme in the auth server's trusted origins, Better Auth's Expo plugin, and
|
|
10979
|
+
* sign-in links a phone can open.
|
|
10980
|
+
*/
|
|
10981
|
+
export const app = new MobileApp('App', { path: 'apps/app' }).dependsOn([
|
|
10982
|
+
api,
|
|
10983
|
+
auth,
|
|
10984
|
+
]);
|
|
10985
|
+
`
|
|
10986
|
+
});
|
|
10852
10987
|
if (variant !== void 0) files.push({
|
|
10853
10988
|
path: "constructs/site.ts",
|
|
10854
10989
|
content: `import { StaticSite } from '@geekmidas/constructs/site';
|
|
@@ -12055,7 +12190,7 @@ describe('example', () => {
|
|
|
12055
12190
|
function generateTestFactoryFiles(options) {
|
|
12056
12191
|
if (!options.constructs.database) return [];
|
|
12057
12192
|
const db = databaseFor();
|
|
12058
|
-
const schema = options.monorepo ? `@${options.name}/constructs/database.ts` : "../../src/constructs/database.ts";
|
|
12193
|
+
const schema = options.monorepo && options.template === "fullstack" ? `@${options.name}/constructs/database.ts` : options.monorepo ? `../../${options.apiPath}/src/constructs/database.ts` : "../../src/constructs/database.ts";
|
|
12059
12194
|
return [{
|
|
12060
12195
|
path: `test/factories/${(0, _geekmidas_manifest.kebabCase)(db.id)}.ts`,
|
|
12061
12196
|
content: `import type { Kysely } from 'kysely';
|
|
@@ -15023,7 +15158,7 @@ var NoStoredSecrets = class extends Error {
|
|
|
15023
15158
|
}
|
|
15024
15159
|
};
|
|
15025
15160
|
async function reconcile(secrets, workspace, stage) {
|
|
15026
|
-
const { derivedContainers } = await Promise.resolve().then(() => require("./workspace-
|
|
15161
|
+
const { derivedContainers } = await Promise.resolve().then(() => require("./workspace-CRjZkCTW.cjs")).then((n) => n.workspace_exports);
|
|
15027
15162
|
const result = require_reconcile.reconcileMissingSecrets(secrets, workspace, await derivedContainers(workspace, stage));
|
|
15028
15163
|
return result ? {
|
|
15029
15164
|
secrets: result.secrets,
|
|
@@ -15422,7 +15557,7 @@ async function testCommand(options = {}) {
|
|
|
15422
15557
|
* starts without the reference data the app needs.
|
|
15423
15558
|
*/
|
|
15424
15559
|
async function migrateTestStage(workspace, env) {
|
|
15425
|
-
const { migrateAndSeed } = await Promise.resolve().then(() => require("./migrate-
|
|
15560
|
+
const { migrateAndSeed } = await Promise.resolve().then(() => require("./migrate-nlvT-GnY.cjs"));
|
|
15426
15561
|
const { discover } = await Promise.resolve().then(() => require("./discover-CroTIm0K.cjs")).then((n) => n.discover_exports);
|
|
15427
15562
|
const sources = {};
|
|
15428
15563
|
const manifest = await discover({
|
|
@@ -15867,7 +16002,7 @@ program.command("migrate").description("Apply each database construct's migratio
|
|
|
15867
16002
|
try {
|
|
15868
16003
|
const globalOptions = program.opts();
|
|
15869
16004
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
15870
|
-
const { migrateCommand } = await Promise.resolve().then(() => require("./migrate-
|
|
16005
|
+
const { migrateCommand } = await Promise.resolve().then(() => require("./migrate-nlvT-GnY.cjs"));
|
|
15871
16006
|
await migrateCommand({
|
|
15872
16007
|
...options,
|
|
15873
16008
|
...construct ? { construct } : {}
|
|
@@ -15881,7 +16016,7 @@ program.command("seed").description("Migrate, then run each database construct's
|
|
|
15881
16016
|
try {
|
|
15882
16017
|
const globalOptions = program.opts();
|
|
15883
16018
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
15884
|
-
const { seedCommand } = await Promise.resolve().then(() => require("./migrate-
|
|
16019
|
+
const { seedCommand } = await Promise.resolve().then(() => require("./migrate-nlvT-GnY.cjs"));
|
|
15885
16020
|
await seedCommand({
|
|
15886
16021
|
...options,
|
|
15887
16022
|
...construct ? { construct } : {}
|
|
@@ -15895,7 +16030,7 @@ program.command("migration").description("Write a construct's next migration: an
|
|
|
15895
16030
|
try {
|
|
15896
16031
|
const globalOptions = program.opts();
|
|
15897
16032
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
15898
|
-
const { migrationCommand } = await Promise.resolve().then(() => require("./migrate-
|
|
16033
|
+
const { migrationCommand } = await Promise.resolve().then(() => require("./migrate-nlvT-GnY.cjs"));
|
|
15899
16034
|
await migrationCommand({
|
|
15900
16035
|
construct,
|
|
15901
16036
|
...name ? { name } : {}
|
|
@@ -16063,7 +16198,7 @@ program.command("secrets:reconcile").description("Backfill missing custom secret
|
|
|
16063
16198
|
console.error(`No secrets found for stage "${stage}". Run "gkm secrets:init --stage ${stage}" first.`);
|
|
16064
16199
|
process.exit(1);
|
|
16065
16200
|
}
|
|
16066
|
-
const { derivedContainers } = await Promise.resolve().then(() => require("./workspace-
|
|
16201
|
+
const { derivedContainers } = await Promise.resolve().then(() => require("./workspace-CRjZkCTW.cjs")).then((n) => n.workspace_exports);
|
|
16067
16202
|
const result = reconcileMissingSecrets(secrets, workspace, await derivedContainers(workspace, stage));
|
|
16068
16203
|
if (!result) {
|
|
16069
16204
|
console.log(`\n✓ Secrets for stage "${stage}" are up-to-date`);
|
|
@@ -16122,7 +16257,7 @@ program.command("deploy:github").description("Let GitHub Actions deploy a stage:
|
|
|
16122
16257
|
try {
|
|
16123
16258
|
const globalOptions = program.opts();
|
|
16124
16259
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
16125
|
-
const { deployGithubCommand } = await Promise.resolve().then(() => require("./github-
|
|
16260
|
+
const { deployGithubCommand } = await Promise.resolve().then(() => require("./github-tZmMZ7td.cjs"));
|
|
16126
16261
|
await deployGithubCommand(options);
|
|
16127
16262
|
} catch (error) {
|
|
16128
16263
|
console.error(formatError(error));
|