@geekmidas/cli 10.0.0-alpha.21 → 10.0.0-alpha.22
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/{credentials-BfmmB4WD.cjs → credentials-COrSpZfF.cjs} +7 -1
- package/dist/{credentials-BfmmB4WD.cjs.map → credentials-COrSpZfF.cjs.map} +1 -1
- package/dist/{credentials-CD8ftUS6.mjs → credentials-D9TKJJZc.mjs} +2 -2
- package/dist/{credentials-CD8ftUS6.mjs.map → credentials-D9TKJJZc.mjs.map} +1 -1
- package/dist/{github-De-yMwVt.mjs → github-B4OQQZBV.mjs} +2 -2
- package/dist/{github-De-yMwVt.mjs.map → github-B4OQQZBV.mjs.map} +1 -1
- package/dist/{github-Bho-C6Zg.cjs → github-BpsnSDw2.cjs} +2 -2
- package/dist/{github-Bho-C6Zg.cjs.map → github-BpsnSDw2.cjs.map} +1 -1
- package/dist/index.cjs +263 -30
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +266 -33
- package/dist/index.mjs.map +1 -1
- package/dist/{openapi-DfjbhrsM.mjs → openapi-B5PSuLym.mjs} +5 -2
- package/dist/{openapi-DfjbhrsM.mjs.map → openapi-B5PSuLym.mjs.map} +1 -1
- package/dist/{openapi-B8bu_OXN.cjs → openapi-CN69hrf0.cjs} +10 -1
- package/dist/{openapi-B8bu_OXN.cjs.map → openapi-CN69hrf0.cjs.map} +1 -1
- package/dist/openapi.cjs +1 -1
- package/dist/openapi.mjs +1 -1
- package/dist/{transfer-CXoyQZbB.cjs → transfer-BZbyaV3I.cjs} +2 -2
- package/dist/{transfer-CXoyQZbB.cjs.map → transfer-BZbyaV3I.cjs.map} +1 -1
- package/dist/{transfer-Gsxfy6m-.mjs → transfer-Bk5YlRyB.mjs} +2 -2
- package/dist/{transfer-Gsxfy6m-.mjs.map → transfer-Bk5YlRyB.mjs.map} +1 -1
- package/package.json +13 -11
package/dist/index.mjs
CHANGED
|
@@ -2,24 +2,24 @@
|
|
|
2
2
|
import { n as __require, t as __exportAll } from "./rolldown-runtime-BhDjJH2R.mjs";
|
|
3
3
|
import { i as DEFAULT_CACHE, r as ConstructGenerator, s as isPartitionedRoutes, t as discover } from "./discover-CK-n3w6b.mjs";
|
|
4
4
|
import { i as loadConfig, l as derivedApps, n as getAppNameFromCwd, o as loadWorkspaceConfig, r as loadAppConfig, s as parseModuleConfig } from "./config-DdKkoUE1.mjs";
|
|
5
|
-
import { A as stripPublicPrefix, C as InvalidStages, D as stageProblems, E as deployedProblems, T as assertDeployedStage, a as getAppBuildOrder, k as getPublicEnvPrefix, o as getAppGkmConfig, s as getDependencyEnvVars, t as allConstructGlobs, v as getDeployTargetError, y as isDeployTargetSupported } from "./workspace-Bu3xNCej.mjs";
|
|
6
|
-
import { C as
|
|
5
|
+
import { A as stripPublicPrefix, C as InvalidStages, D as stageProblems, E as deployedProblems, T as assertDeployedStage, a as getAppBuildOrder, k as getPublicEnvPrefix, o as getAppGkmConfig, s as getDependencyEnvVars, t as allConstructGlobs, v as getDeployTargetError, w as TEST_STAGE, y as isDeployTargetSupported } from "./workspace-Bu3xNCej.mjs";
|
|
6
|
+
import { C as providerOf, S as eventsBackendFor, _ as COMPOSE_PATH, a as loadEnvFiles, b as resourceName, c as resolveServicePorts, d as backendsOf, f as constructGlobs, h as usesConstructs, i as isPortAvailable, l as rewriteUrlsWithPorts, m as reconcileWorkspace, n as createEntryWrapper, o as loadSecretsForApp, p as derivedContainers, r as findAvailablePort, s as prepareEntryCredentials, t as createCredentialsPreload, u as startWorkspaceServices, v as LOCAL_CA_PATH, w as storageBackendFor, x as cacheBackendFor, y as applyPostgres } from "./credentials-D9TKJJZc.mjs";
|
|
7
7
|
import { a as generateServiceCredentials, i as generateSecurePassword, n as generateConnectionUrls, o as rotateServicePassword, t as createStageSecrets } from "./generator-LfZU7NlU.mjs";
|
|
8
8
|
import { c as getKeyPath, i as setCustomSecret, n as readStageSecrets, o as toEmbeddableSecrets, r as secretsExist, s as writeStageSecrets, t as maskPassword } from "./storage-TP0x118M.mjs";
|
|
9
|
-
import { a as openapiCommand, c as runtimeFor, n as generateOpenApi, o as resolveOpenApiConfig, r as generateOpenApiFrom, s as
|
|
9
|
+
import { a as openapiCommand, c as EndpointGenerator, l as runtimeFor, n as generateOpenApi, o as resolveOpenApiConfig, r as generateOpenApiFrom, s as OpenApiTsGenerator, t as OPENAPI_OUTPUT_PATH$1 } from "./openapi-B5PSuLym.mjs";
|
|
10
10
|
import { i as withRoutes, r as withCompute } from "./emit-Bzq7JsMP.mjs";
|
|
11
11
|
import { i as writeDockerEnvFromSecrets, r as generateFullstackCustomSecrets } from "./reconcile-gb1lz1Dk.mjs";
|
|
12
|
-
import { a as isRemoteStore, n as pullStageSecrets, r as pushStageSecrets, t as NoStoredSecrets } from "./transfer-
|
|
12
|
+
import { a as isRemoteStore, n as pullStageSecrets, r as pushStageSecrets, t as NoStoredSecrets } from "./transfer-Bk5YlRyB.mjs";
|
|
13
13
|
import { createRequire } from "node:module";
|
|
14
14
|
import { Command } from "commander";
|
|
15
15
|
import { stdin, stdout } from "node:process";
|
|
16
16
|
import * as readline from "node:readline/promises";
|
|
17
17
|
import { copyFileSync, existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
18
|
-
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
18
|
+
import { mkdir, readFile, rm, 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, provideKey, provisionOrder, scopedName, serviceKey } from "@geekmidas/manifest";
|
|
22
|
+
import { cacheTable, canonicalId, cloudName, cookieDomain, dependentsOf, kebabCase, provideKey, provisionOrder, scopedName, serviceKey } from "@geekmidas/manifest";
|
|
23
23
|
import fg from "fast-glob";
|
|
24
24
|
import chokidar from "chokidar";
|
|
25
25
|
import { parse as parse$1, parseDocument } from "yaml";
|
|
@@ -36,8 +36,10 @@ import { Topic } from "@geekmidas/constructs/topic";
|
|
|
36
36
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
37
37
|
import { lookup } from "node:dns/promises";
|
|
38
38
|
import prompts from "prompts";
|
|
39
|
+
import { EnvironmentParser } from "@geekmidas/envkit";
|
|
40
|
+
import { serviceContext } from "@geekmidas/services";
|
|
39
41
|
//#region package.json
|
|
40
|
-
var version = "10.0.0-alpha.
|
|
42
|
+
var version = "10.0.0-alpha.22";
|
|
41
43
|
//#endregion
|
|
42
44
|
//#region src/auth/credentials.ts
|
|
43
45
|
/**
|
|
@@ -8407,26 +8409,26 @@ function loadPackageJson() {
|
|
|
8407
8409
|
* Run: pnpm --filter @geekmidas/cli sync-versions
|
|
8408
8410
|
*/
|
|
8409
8411
|
const GEEKMIDAS_VERSIONS = {
|
|
8410
|
-
"@geekmidas/audit": "~10.0.0-alpha.
|
|
8411
|
-
"@geekmidas/auth": "~10.0.0-alpha.
|
|
8412
|
-
"@geekmidas/cache": "~10.0.0-alpha.
|
|
8413
|
-
"@geekmidas/client": "~10.0.0-alpha.
|
|
8414
|
-
"@geekmidas/cloud": "~10.0.0-alpha.
|
|
8415
|
-
"@geekmidas/constructs": "~10.0.0-alpha.
|
|
8416
|
-
"@geekmidas/db": "~10.0.0-alpha.
|
|
8417
|
-
"@geekmidas/emailkit": "~10.0.0-alpha.
|
|
8418
|
-
"@geekmidas/envkit": "~10.0.0-alpha.
|
|
8419
|
-
"@geekmidas/errors": "~10.0.0-alpha.
|
|
8420
|
-
"@geekmidas/events": "~10.0.0-alpha.
|
|
8421
|
-
"@geekmidas/logger": "~10.0.0-alpha.
|
|
8422
|
-
"@geekmidas/manifest": "~10.0.0-alpha.
|
|
8423
|
-
"@geekmidas/rate-limit": "~10.0.0-alpha.
|
|
8424
|
-
"@geekmidas/schema": "~10.0.0-alpha.
|
|
8425
|
-
"@geekmidas/services": "~10.0.0-alpha.
|
|
8426
|
-
"@geekmidas/storage": "~10.0.0-alpha.
|
|
8427
|
-
"@geekmidas/studio": "~10.0.0-alpha.
|
|
8428
|
-
"@geekmidas/telescope": "~10.0.0-alpha.
|
|
8429
|
-
"@geekmidas/testkit": "~10.0.0-alpha.
|
|
8412
|
+
"@geekmidas/audit": "~10.0.0-alpha.22",
|
|
8413
|
+
"@geekmidas/auth": "~10.0.0-alpha.22",
|
|
8414
|
+
"@geekmidas/cache": "~10.0.0-alpha.22",
|
|
8415
|
+
"@geekmidas/client": "~10.0.0-alpha.22",
|
|
8416
|
+
"@geekmidas/cloud": "~10.0.0-alpha.22",
|
|
8417
|
+
"@geekmidas/constructs": "~10.0.0-alpha.22",
|
|
8418
|
+
"@geekmidas/db": "~10.0.0-alpha.22",
|
|
8419
|
+
"@geekmidas/emailkit": "~10.0.0-alpha.22",
|
|
8420
|
+
"@geekmidas/envkit": "~10.0.0-alpha.22",
|
|
8421
|
+
"@geekmidas/errors": "~10.0.0-alpha.22",
|
|
8422
|
+
"@geekmidas/events": "~10.0.0-alpha.22",
|
|
8423
|
+
"@geekmidas/logger": "~10.0.0-alpha.22",
|
|
8424
|
+
"@geekmidas/manifest": "~10.0.0-alpha.22",
|
|
8425
|
+
"@geekmidas/rate-limit": "~10.0.0-alpha.22",
|
|
8426
|
+
"@geekmidas/schema": "~10.0.0-alpha.22",
|
|
8427
|
+
"@geekmidas/services": "~10.0.0-alpha.22",
|
|
8428
|
+
"@geekmidas/storage": "~10.0.0-alpha.22",
|
|
8429
|
+
"@geekmidas/studio": "~10.0.0-alpha.22",
|
|
8430
|
+
"@geekmidas/telescope": "~10.0.0-alpha.22",
|
|
8431
|
+
"@geekmidas/testkit": "~10.0.0-alpha.22",
|
|
8430
8432
|
"@geekmidas/cli": `~${loadPackageJson().version}`
|
|
8431
8433
|
};
|
|
8432
8434
|
//#endregion
|
|
@@ -15127,6 +15129,223 @@ function printSummary(workspace, stage) {
|
|
|
15127
15129
|
logger$1.log("");
|
|
15128
15130
|
}
|
|
15129
15131
|
//#endregion
|
|
15132
|
+
//#region src/test/harness.ts
|
|
15133
|
+
/**
|
|
15134
|
+
* What `gkm test` writes for a feature test to be built from.
|
|
15135
|
+
*
|
|
15136
|
+
* `gkm test` already discovers an app's constructs and resolves the test
|
|
15137
|
+
* stage's environment; this keeps both, under `.gkm/test/`:
|
|
15138
|
+
*
|
|
15139
|
+
* - `manifest.json` — which construct and endpoint is exported where, and the
|
|
15140
|
+
* environment the test stage resolved, keyed as the constructs derive keys.
|
|
15141
|
+
* - `clients/<surface>.ts` — each surface's typed client, from the same
|
|
15142
|
+
* generator `gkm build` uses for the app's own.
|
|
15143
|
+
* - `index.ts` — the configured `it`, and a `Browser` holding one client per
|
|
15144
|
+
* surface and one per auth server, each on the browser's `fetch`.
|
|
15145
|
+
*
|
|
15146
|
+
* A test imports `it` from there and declares nothing — not a construct, not an
|
|
15147
|
+
* environment key, not a client.
|
|
15148
|
+
*/
|
|
15149
|
+
/**
|
|
15150
|
+
* The variable the manifest's path travels in — what
|
|
15151
|
+
* `@geekmidas/constructs/testing` reads. Its own constant rather than an import:
|
|
15152
|
+
* that entry carries the whole harness (MSW, Hono, Vitest), and importing it
|
|
15153
|
+
* here put all of it on the start-up path of every `gkm` command.
|
|
15154
|
+
*/
|
|
15155
|
+
const TEST_MANIFEST_ENV = "GKM_TEST_MANIFEST";
|
|
15156
|
+
/** Where the harness lives, relative to the workspace root. */
|
|
15157
|
+
const TEST_HARNESS_DIR = ".gkm/test";
|
|
15158
|
+
/**
|
|
15159
|
+
* Better-auth's client plugin for each server plugin id.
|
|
15160
|
+
*
|
|
15161
|
+
* Written down rather than derived: the ids and the client names do not follow
|
|
15162
|
+
* one rule (`admin` pairs with `adminClient`, `email-otp` with
|
|
15163
|
+
* `emailOTPClient`), and a guess that missed would hand a test a client
|
|
15164
|
+
* silently lacking every method of that plugin. A server plugin not listed has
|
|
15165
|
+
* no client counterpart — `bearer`, `open-api` — or needs one from another
|
|
15166
|
+
* package, and is left out.
|
|
15167
|
+
*/
|
|
15168
|
+
const CLIENT_PLUGINS = {
|
|
15169
|
+
admin: "adminClient",
|
|
15170
|
+
anonymous: "anonymousClient",
|
|
15171
|
+
"device-authorization": "deviceAuthorizationClient",
|
|
15172
|
+
"email-otp": "emailOTPClient",
|
|
15173
|
+
jwt: "jwtClient",
|
|
15174
|
+
"last-login-method": "lastLoginMethodClient",
|
|
15175
|
+
"magic-link": "magicLinkClient",
|
|
15176
|
+
"multi-session": "multiSessionClient",
|
|
15177
|
+
"oauth-popup": "oauthPopupClient",
|
|
15178
|
+
"one-tap": "oneTapClient",
|
|
15179
|
+
"one-time-token": "oneTimeTokenClient",
|
|
15180
|
+
organization: "organizationClient",
|
|
15181
|
+
"phone-number": "phoneNumberClient",
|
|
15182
|
+
siwe: "siweClient",
|
|
15183
|
+
"two-factor": "twoFactorClient",
|
|
15184
|
+
username: "usernameClient"
|
|
15185
|
+
};
|
|
15186
|
+
/** Write the manifest, the clients and the harness; return the manifest path. */
|
|
15187
|
+
async function writeTestHarness(options) {
|
|
15188
|
+
const sources = {};
|
|
15189
|
+
const declared = await discover({
|
|
15190
|
+
patterns: options.patterns,
|
|
15191
|
+
cwd: options.root,
|
|
15192
|
+
sources
|
|
15193
|
+
});
|
|
15194
|
+
const endpoints = (await new EndpointGenerator().load(options.patterns, options.root)).map(({ key, construct, path }) => ({
|
|
15195
|
+
surface: construct.owner ?? construct.surface?.id,
|
|
15196
|
+
construct,
|
|
15197
|
+
source: {
|
|
15198
|
+
file: path.absolute,
|
|
15199
|
+
export: key
|
|
15200
|
+
}
|
|
15201
|
+
})).filter((endpoint) => endpoint.surface !== void 0);
|
|
15202
|
+
const manifest = {
|
|
15203
|
+
stage: options.stage,
|
|
15204
|
+
constructs: Object.fromEntries(Object.entries(declared).flatMap(([id, declaration]) => {
|
|
15205
|
+
const source = sources[id];
|
|
15206
|
+
return source ? [[id, {
|
|
15207
|
+
kind: declaration.kind,
|
|
15208
|
+
source: {
|
|
15209
|
+
file: source.file,
|
|
15210
|
+
export: source.exportName
|
|
15211
|
+
}
|
|
15212
|
+
}]] : [];
|
|
15213
|
+
})),
|
|
15214
|
+
endpoints: endpoints.map(({ surface, source }) => ({
|
|
15215
|
+
surface,
|
|
15216
|
+
source
|
|
15217
|
+
})),
|
|
15218
|
+
env: options.env
|
|
15219
|
+
};
|
|
15220
|
+
const surfaces = [...new Set(endpoints.map(({ surface }) => surface))];
|
|
15221
|
+
const generator = new OpenApiTsGenerator();
|
|
15222
|
+
const clients = await Promise.all(surfaces.map(async (surface) => ({
|
|
15223
|
+
file: `${kebabCase(surface)}.ts`,
|
|
15224
|
+
content: await generator.generate(endpoints.filter((endpoint) => endpoint.surface === surface).map(({ construct }) => construct), {
|
|
15225
|
+
title: surface,
|
|
15226
|
+
version: "1.0.0"
|
|
15227
|
+
})
|
|
15228
|
+
})));
|
|
15229
|
+
const auths = await authClients(declared, sources, options.env);
|
|
15230
|
+
const drivers = driversFor({
|
|
15231
|
+
appRoot: options.root,
|
|
15232
|
+
cache: cacheBackendsIn(declared, options.cacheBackend)
|
|
15233
|
+
});
|
|
15234
|
+
const database = await databaseSource(declared, sources, endpoints.map(({ construct }) => construct));
|
|
15235
|
+
const harnessFor = (dir) => harnessModule({
|
|
15236
|
+
dir,
|
|
15237
|
+
database,
|
|
15238
|
+
surfaces: surfaces.map((id, index) => ({
|
|
15239
|
+
id,
|
|
15240
|
+
secured: clients[index].content.includes("authStrategies:")
|
|
15241
|
+
})),
|
|
15242
|
+
auths,
|
|
15243
|
+
drivers
|
|
15244
|
+
});
|
|
15245
|
+
const json = `${JSON.stringify(manifest, null, 2)}\n`;
|
|
15246
|
+
return (await Promise.all([...new Set(options.targets)].map(async (target) => {
|
|
15247
|
+
const dir = join(target, TEST_HARNESS_DIR);
|
|
15248
|
+
await rm(dir, {
|
|
15249
|
+
recursive: true,
|
|
15250
|
+
force: true
|
|
15251
|
+
});
|
|
15252
|
+
await mkdir(join(dir, "clients"), { recursive: true });
|
|
15253
|
+
await writeFile(join(dir, "manifest.json"), json);
|
|
15254
|
+
await writeFile(join(dir, "index.ts"), harnessFor(dir));
|
|
15255
|
+
for (const client of clients) await writeFile(join(dir, "clients", client.file), client.content);
|
|
15256
|
+
return join(dir, "manifest.json");
|
|
15257
|
+
})))[0];
|
|
15258
|
+
}
|
|
15259
|
+
/** Each auth server, with the client plugins its server plugins pair with. */
|
|
15260
|
+
async function authClients(declared, sources, env) {
|
|
15261
|
+
const clients = [];
|
|
15262
|
+
const envParser = new EnvironmentParser({ ...env });
|
|
15263
|
+
for (const [id, declaration] of Object.entries(declared)) {
|
|
15264
|
+
const source = sources[id];
|
|
15265
|
+
if (declaration.kind !== "rest-api" || !source) continue;
|
|
15266
|
+
const exported = (await import(source.file))[source.exportName];
|
|
15267
|
+
if (!isBetterAuth(exported) || exported.id !== id) continue;
|
|
15268
|
+
const ids = await exported.pluginIds({
|
|
15269
|
+
envParser,
|
|
15270
|
+
context: serviceContext
|
|
15271
|
+
});
|
|
15272
|
+
clients.push({
|
|
15273
|
+
id,
|
|
15274
|
+
basePath: exported.basePath,
|
|
15275
|
+
plugins: ids.flatMap((plugin) => CLIENT_PLUGINS[plugin] ?? [])
|
|
15276
|
+
});
|
|
15277
|
+
}
|
|
15278
|
+
return clients;
|
|
15279
|
+
}
|
|
15280
|
+
function isBetterAuth(value) {
|
|
15281
|
+
return typeof value === "object" && value !== null && "pluginIds" in value && "basePath" in value;
|
|
15282
|
+
}
|
|
15283
|
+
/** `Api` → `api`, `AdminApi` → `adminApi`: a construct id as a property name. */
|
|
15284
|
+
function propertyOf(id) {
|
|
15285
|
+
return id.charAt(0).toLowerCase() + id.slice(1);
|
|
15286
|
+
}
|
|
15287
|
+
/**
|
|
15288
|
+
* The database a test's `db` is — the one the endpoints were given with
|
|
15289
|
+
* `.database()`, found by the service they hold, which is the construct's own.
|
|
15290
|
+
* The same rule `featureTest` applies at runtime, applied here so `db` can be
|
|
15291
|
+
* typed by the construct's schema.
|
|
15292
|
+
*/
|
|
15293
|
+
async function databaseSource(declared, sources, endpoints) {
|
|
15294
|
+
const services = new Set(endpoints.map((endpoint) => endpoint.databaseService).filter(Boolean));
|
|
15295
|
+
if (services.size === 0) return void 0;
|
|
15296
|
+
for (const [id, declaration] of Object.entries(declared)) {
|
|
15297
|
+
const source = sources[id];
|
|
15298
|
+
if (!source || !declaration.kind.startsWith("database")) continue;
|
|
15299
|
+
const exported = (await import(source.file))[source.exportName];
|
|
15300
|
+
if (services.has(exported?.service)) return source;
|
|
15301
|
+
}
|
|
15302
|
+
}
|
|
15303
|
+
/** An import specifier for `file`, from the harness written into `dir`. */
|
|
15304
|
+
function specifierFrom(dir, file) {
|
|
15305
|
+
const path = relative(dir, file).replace(/\.tsx?$/, ".js");
|
|
15306
|
+
return path.startsWith(".") ? path : `./${path}`;
|
|
15307
|
+
}
|
|
15308
|
+
function harnessModule(options) {
|
|
15309
|
+
const { surfaces, auths, drivers, database, dir } = options;
|
|
15310
|
+
const plugins = [...new Set(auths.flatMap(({ plugins }) => plugins))].sort();
|
|
15311
|
+
const imports = [
|
|
15312
|
+
`import {${database ? " type DatabaseOf," : ""} featureTest, loadTestManifest } from '@geekmidas/constructs/testing';`,
|
|
15313
|
+
...database ? [`import type { ${database.exportName} as __database } from '${specifierFrom(dir, database.file)}';`] : [],
|
|
15314
|
+
`import { Browser as TestBrowser } from '@geekmidas/testkit/browser';`,
|
|
15315
|
+
...surfaces.map(({ id }) => `import { createApi as create${id} } from './clients/${kebabCase(id)}.js';`),
|
|
15316
|
+
...auths.length ? [`import { createAuthClient } from 'better-auth/client';`] : [],
|
|
15317
|
+
...plugins.length ? [`import { ${plugins.join(", ")} } from 'better-auth/client/plugins';`] : [],
|
|
15318
|
+
...drivers.imports ? [drivers.imports] : []
|
|
15319
|
+
];
|
|
15320
|
+
const url = (id) => `manifest.env[${JSON.stringify(provideKey(id, "url"))}]!`;
|
|
15321
|
+
const members = [...surfaces.map(({ id, secured }) => ` /** \`${id}\`, at ${provideKey(id, "url")}. */
|
|
15322
|
+
readonly ${propertyOf(id)} = create${id}({
|
|
15323
|
+
baseURL: ${url(id)},
|
|
15324
|
+
fetch: this.fetch,${secured ? `
|
|
15325
|
+
// Served in-process: no authorizer runs, so nothing is signed. A scheme
|
|
15326
|
+
// with no strategy sends its request without credentials.
|
|
15327
|
+
authStrategies: {} as never,` : ""}
|
|
15328
|
+
});`), ...auths.map((auth) => ` /** \`${auth.id}\`, at ${provideKey(auth.id, "url")}. */
|
|
15329
|
+
readonly ${propertyOf(auth.id)} = createAuthClient({
|
|
15330
|
+
baseURL: \`\${${url(auth.id)}}${auth.basePath}\`,
|
|
15331
|
+
plugins: [${auth.plugins.map((plugin) => `${plugin}()`).join(", ")}],
|
|
15332
|
+
fetchOptions: { customFetchImpl: this.fetch },
|
|
15333
|
+
});`)];
|
|
15334
|
+
return `// Generated by \`gkm test\` from the app's constructs — do not edit.
|
|
15335
|
+
${imports.join("\n")}
|
|
15336
|
+
|
|
15337
|
+
${drivers.setup ? `// The drivers the app's server entry registers, registered the same way.\n${drivers.setup}\n\n` : ""}const manifest = loadTestManifest(new URL('./manifest.json', import.meta.url));
|
|
15338
|
+
|
|
15339
|
+
/** A browser with a client for every surface the app declares. */
|
|
15340
|
+
export class Browser extends TestBrowser {
|
|
15341
|
+
${members.join("\n\n")}
|
|
15342
|
+
}
|
|
15343
|
+
|
|
15344
|
+
/** \`it\`, for a test that drives the app the way it runs deployed. */
|
|
15345
|
+
export const it = featureTest${database ? "<Browser, DatabaseOf<typeof __database>>" : ""}({ manifest, browser: Browser });
|
|
15346
|
+
`;
|
|
15347
|
+
}
|
|
15348
|
+
//#endregion
|
|
15130
15349
|
//#region src/test/index.ts
|
|
15131
15350
|
/**
|
|
15132
15351
|
* Run tests with secrets, dependency URLs, and .env files loaded.
|
|
@@ -15145,7 +15364,7 @@ async function testCommand(options = {}) {
|
|
|
15145
15364
|
if (defaultEnv.loaded.length > 0) console.log(` 📦 Loaded env: ${defaultEnv.loaded.join(", ")}`);
|
|
15146
15365
|
const result = await prepareEntryCredentials({
|
|
15147
15366
|
stage,
|
|
15148
|
-
startDocker:
|
|
15367
|
+
startDocker: !options.prepare,
|
|
15149
15368
|
secretsFileName: "test-secrets.json",
|
|
15150
15369
|
resolveDockerPorts: "full",
|
|
15151
15370
|
reconcileStage: "test"
|
|
@@ -15167,6 +15386,19 @@ async function testCommand(options = {}) {
|
|
|
15167
15386
|
const gkmDir = join(cwd, ".gkm");
|
|
15168
15387
|
const preloadPath = join(gkmDir, "test-credentials-preload.ts");
|
|
15169
15388
|
await createCredentialsPreload(preloadPath, result.secretsJsonPath);
|
|
15389
|
+
const workspace = await loadWorkspaceConfig(cwd).then((loaded) => loaded.workspace).catch(() => void 0);
|
|
15390
|
+
const manifestPath = workspace && usesConstructs(workspace) ? await writeTestHarness({
|
|
15391
|
+
root: workspace.root,
|
|
15392
|
+
targets: [workspace.root, ...Object.values(workspace.apps).map((app) => isAbsolute(app.path) ? app.path : join(workspace.root, app.path))],
|
|
15393
|
+
patterns: constructGlobs(workspace),
|
|
15394
|
+
cacheBackend: backendsOf(workspace).cache,
|
|
15395
|
+
stage: TEST_STAGE,
|
|
15396
|
+
env: finalCredentials
|
|
15397
|
+
}) : void 0;
|
|
15398
|
+
if (options.prepare) {
|
|
15399
|
+
console.log(manifestPath ? ` 🧪 Test harness written beside ${manifestPath}` : " 🧪 Nothing to prepare: this project declares no constructs");
|
|
15400
|
+
return;
|
|
15401
|
+
}
|
|
15170
15402
|
const nodeOptions = [
|
|
15171
15403
|
process.env.NODE_OPTIONS ?? "",
|
|
15172
15404
|
"--import=tsx",
|
|
@@ -15184,6 +15416,7 @@ async function testCommand(options = {}) {
|
|
|
15184
15416
|
env: {
|
|
15185
15417
|
...process.env,
|
|
15186
15418
|
...finalCredentials,
|
|
15419
|
+
...manifestPath ? { [TEST_MANIFEST_ENV]: manifestPath } : {},
|
|
15187
15420
|
NODE_ENV: "test",
|
|
15188
15421
|
NODE_OPTIONS: nodeOptions
|
|
15189
15422
|
}
|
|
@@ -15629,7 +15862,7 @@ program.command("exec").description("Run a command with secrets injected into Cr
|
|
|
15629
15862
|
process.exit(1);
|
|
15630
15863
|
}
|
|
15631
15864
|
});
|
|
15632
|
-
program.command("test").description("Run tests with secrets loaded from environment").option("--stage <stage>", "Stage to load secrets from (default: stages.local)").option("--run", "Run tests once without watch mode").option("--watch", "Enable watch mode").option("--coverage", "Generate coverage report").option("--ui", "Open Vitest UI").option("--auto-setup", "Generate a fresh stage (secrets + key) from the workspace config when none exists (for CI; also via GKM_AUTO_SETUP)").argument("[pattern]", "Pattern to filter tests").action(async (pattern, options) => {
|
|
15865
|
+
program.command("test").description("Run tests with secrets loaded from environment").option("--stage <stage>", "Stage to load secrets from (default: stages.local)").option("--run", "Run tests once without watch mode").option("--watch", "Enable watch mode").option("--coverage", "Generate coverage report").option("--ui", "Open Vitest UI").option("--auto-setup", "Generate a fresh stage (secrets + key) from the workspace config when none exists (for CI; also via GKM_AUTO_SETUP)").option("--prepare", "Write the test manifest and #test harness, then stop — for a typecheck that runs before the suite").argument("[pattern]", "Pattern to filter tests").action(async (pattern, options) => {
|
|
15633
15866
|
try {
|
|
15634
15867
|
const globalOptions = program.opts();
|
|
15635
15868
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
@@ -15761,7 +15994,7 @@ program.command("secrets:push").description("Send a deployed stage's secrets to
|
|
|
15761
15994
|
const globalOptions = program.opts();
|
|
15762
15995
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
15763
15996
|
const { loadWorkspaceConfig } = await import("./config.mjs");
|
|
15764
|
-
const { pushStageSecrets } = await import("./transfer-
|
|
15997
|
+
const { pushStageSecrets } = await import("./transfer-Bk5YlRyB.mjs").then((n) => n.i);
|
|
15765
15998
|
const { workspace } = await loadWorkspaceConfig();
|
|
15766
15999
|
const { addedKeys } = await pushStageSecrets(workspace, options.stage, { profile: options.profile });
|
|
15767
16000
|
for (const key of addedKeys) console.log(` + ${key} (reconciled)`);
|
|
@@ -15776,7 +16009,7 @@ program.command("secrets:pull").description("Bring a deployed stage's secrets fr
|
|
|
15776
16009
|
const globalOptions = program.opts();
|
|
15777
16010
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
15778
16011
|
const { loadWorkspaceConfig } = await import("./config.mjs");
|
|
15779
|
-
const { pullStageSecrets } = await import("./transfer-
|
|
16012
|
+
const { pullStageSecrets } = await import("./transfer-Bk5YlRyB.mjs").then((n) => n.i);
|
|
15780
16013
|
const { workspace } = await loadWorkspaceConfig();
|
|
15781
16014
|
const { addedKeys } = await pullStageSecrets(workspace, options.stage, { profile: options.profile });
|
|
15782
16015
|
for (const key of addedKeys) console.log(` + ${key} (reconciled)`);
|
|
@@ -15800,7 +16033,7 @@ program.command("secrets:reconcile").description("Backfill missing custom secret
|
|
|
15800
16033
|
console.error(`No secrets found for stage "${stage}". Run "gkm secrets:init --stage ${stage}" first.`);
|
|
15801
16034
|
process.exit(1);
|
|
15802
16035
|
}
|
|
15803
|
-
const { derivedContainers } = await import("./credentials-
|
|
16036
|
+
const { derivedContainers } = await import("./credentials-D9TKJJZc.mjs").then((n) => n.g);
|
|
15804
16037
|
const result = reconcileMissingSecrets(secrets, workspace, await derivedContainers(workspace, stage));
|
|
15805
16038
|
if (!result) {
|
|
15806
16039
|
console.log(`\n✓ Secrets for stage "${stage}" are up-to-date`);
|
|
@@ -15859,7 +16092,7 @@ program.command("deploy:github").description("Let GitHub Actions deploy a stage:
|
|
|
15859
16092
|
try {
|
|
15860
16093
|
const globalOptions = program.opts();
|
|
15861
16094
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
15862
|
-
const { deployGithubCommand } = await import("./github-
|
|
16095
|
+
const { deployGithubCommand } = await import("./github-B4OQQZBV.mjs");
|
|
15863
16096
|
await deployGithubCommand(options);
|
|
15864
16097
|
} catch (error) {
|
|
15865
16098
|
console.error(formatError(error));
|