create-svc 0.1.42 → 0.1.44
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/package.json
CHANGED
package/src/scaffold.test.ts
CHANGED
|
@@ -316,7 +316,7 @@ test("scaffolds the workers target with wrangler lifecycle commands", async () =
|
|
|
316
316
|
const wranglerConfig = await Bun.file(join(generatedRoot, "wrangler.toml")).text();
|
|
317
317
|
expect(wranglerConfig).toContain('name = "dns-api"');
|
|
318
318
|
expect(wranglerConfig).toContain('compatibility_flags = ["nodejs_compat"]');
|
|
319
|
-
expect(wranglerConfig).toContain('pattern = "api.dns-api.anmho.com
|
|
319
|
+
expect(wranglerConfig).toContain('pattern = "api.dns-api.anmho.com"');
|
|
320
320
|
expect(wranglerConfig).toContain('binding = "HYPERDRIVE"');
|
|
321
321
|
expect(wranglerConfig).toContain('AUTH_ENABLED = "true"');
|
|
322
322
|
expect(wranglerConfig).toContain('AUTH_AUDIENCE = "api://dns-api"');
|
|
@@ -7,7 +7,7 @@ import { manualGitHubDeleteCommand } from "../../git-bootstrap";
|
|
|
7
7
|
import { ensureAuthClient, ensureAuthResourceServer, runAuthCommand, runAuthDoctor } from "../authctl";
|
|
8
8
|
import { stopLocalDev } from "../local-dev";
|
|
9
9
|
import { serviceConfig } from "../runtime";
|
|
10
|
-
import { isLocalDatabaseUrl, resolveCommandPath } from "./lib";
|
|
10
|
+
import { isLocalDatabaseUrl, isMissingDatabaseError, resolveCommandPath } from "./lib";
|
|
11
11
|
|
|
12
12
|
const config = {
|
|
13
13
|
serviceName: serviceConfig.service_id,
|
|
@@ -37,7 +37,7 @@ export async function main(argv = Bun.argv.slice(2)) {
|
|
|
37
37
|
ensureAuthResourceServer();
|
|
38
38
|
ensureAuthClient();
|
|
39
39
|
const databaseUrl = await resolveDatabaseUrl({ preferRemote: true });
|
|
40
|
-
await
|
|
40
|
+
await applySchemaWithRetries(databaseUrl);
|
|
41
41
|
await ensureHyperdrive(databaseUrl);
|
|
42
42
|
run("wrangler", ["deploy"]);
|
|
43
43
|
return `Created https://${config.hostname}`;
|
|
@@ -259,8 +259,13 @@ async function deleteHyperdrive() {
|
|
|
259
259
|
async function resolveDatabaseUrl(options: { preferRemote?: boolean } = {}) {
|
|
260
260
|
const direct = Bun.env.DATABASE_URL?.trim();
|
|
261
261
|
const apiKey = resolveNeonApiKey();
|
|
262
|
-
if (direct
|
|
263
|
-
|
|
262
|
+
if (direct) {
|
|
263
|
+
if (!options.preferRemote || !isLocalDatabaseUrl(direct)) {
|
|
264
|
+
return direct;
|
|
265
|
+
}
|
|
266
|
+
if (!apiKey) {
|
|
267
|
+
throw new Error("NEON_API_KEY or readable Vault Neon provider path is required for Workers production create; ignoring local DATABASE_URL");
|
|
268
|
+
}
|
|
264
269
|
}
|
|
265
270
|
|
|
266
271
|
if (!apiKey) {
|
|
@@ -416,6 +421,23 @@ create index if not exists waitlist_triggers_status_created_idx
|
|
|
416
421
|
}
|
|
417
422
|
}
|
|
418
423
|
|
|
424
|
+
async function applySchemaWithRetries(databaseUrl: string) {
|
|
425
|
+
let lastError: unknown;
|
|
426
|
+
for (let attempt = 1; attempt <= 10; attempt += 1) {
|
|
427
|
+
try {
|
|
428
|
+
await applySchema(databaseUrl);
|
|
429
|
+
return;
|
|
430
|
+
} catch (error) {
|
|
431
|
+
lastError = error;
|
|
432
|
+
if (!isMissingDatabaseError(error) || attempt === 10) {
|
|
433
|
+
break;
|
|
434
|
+
}
|
|
435
|
+
await Bun.sleep(2_000);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
throw lastError;
|
|
439
|
+
}
|
|
440
|
+
|
|
419
441
|
async function runDoctor() {
|
|
420
442
|
const results: Array<{ name: string; status: DoctorStatus; detail: string }> = [];
|
|
421
443
|
|
|
@@ -430,7 +452,7 @@ async function runDoctor() {
|
|
|
430
452
|
if (!text.includes(`name = "${config.serviceName}"`)) {
|
|
431
453
|
throw new Error(`wrangler.toml does not name ${config.serviceName}`);
|
|
432
454
|
}
|
|
433
|
-
if (!text.includes(`pattern = "${config.hostname}
|
|
455
|
+
if (!text.includes(`pattern = "${config.hostname}"`)) {
|
|
434
456
|
throw new Error(`wrangler.toml does not route ${config.hostname}`);
|
|
435
457
|
}
|
|
436
458
|
return "name and custom domain route configured";
|
|
@@ -3,7 +3,7 @@ import { mkdir, rm, writeFile } from "node:fs/promises";
|
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { tmpdir } from "node:os";
|
|
5
5
|
import { mkdtempSync } from "node:fs";
|
|
6
|
-
import { isLocalDatabaseUrl, resolveCommandPath } from "./lib";
|
|
6
|
+
import { isLocalDatabaseUrl, isMissingDatabaseError, resolveCommandPath } from "./lib";
|
|
7
7
|
|
|
8
8
|
test("resolveCommandPath prefers repo-local bins", async () => {
|
|
9
9
|
const root = mkdtempSync(join(tmpdir(), "create-svc-workers-bin-"));
|
|
@@ -26,3 +26,7 @@ test("isLocalDatabaseUrl detects localhost database URLs", () => {
|
|
|
26
26
|
expect(isLocalDatabaseUrl("not a url")).toBe(false);
|
|
27
27
|
});
|
|
28
28
|
|
|
29
|
+
test("isMissingDatabaseError detects Neon database propagation failures", () => {
|
|
30
|
+
expect(isMissingDatabaseError(new Error('database "codex_workers" does not exist'))).toBe(true);
|
|
31
|
+
expect(isMissingDatabaseError(new Error("password authentication failed"))).toBe(false);
|
|
32
|
+
});
|