dineway 0.1.35 → 0.1.37
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/README.md +15 -3
- package/dist/api/route-utils.d.mts +1 -1
- package/dist/api/schemas/index.d.mts +1 -1
- package/dist/{api-Ow6RbraA.mjs → api-DpeH2EYq.mjs} +1 -1
- package/dist/astro/index.d.mts +1 -1
- package/dist/astro/index.mjs +1 -1
- package/dist/astro/middleware/auth.d.mts +1 -1
- package/dist/astro/middleware/seed.mjs +1 -1
- package/dist/astro/middleware.mjs +4 -4
- package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +1 -1
- package/dist/astro/routes/api/admin/plugins/index.mjs +1 -1
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +1 -1
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs +1 -1
- package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +1 -1
- package/dist/astro/routes/api/admin/plugins/updates.mjs +1 -1
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +1 -1
- package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +1 -1
- package/dist/astro/routes/api/auth/dev-bypass.mjs +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs +1 -1
- package/dist/astro/routes/api/health.mjs +1 -1
- package/dist/astro/routes/api/manifest.mjs +1 -1
- package/dist/astro/routes/api/mcp.mjs +1 -1
- package/dist/astro/routes/api/openapi.json.mjs +1 -1
- package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs +1 -1
- package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +1 -1
- package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs +1 -1
- package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +1 -1
- package/dist/astro/routes/api/schema/collections/index.mjs +1 -1
- package/dist/astro/routes/api/schema/orphans/_slug_.mjs +1 -1
- package/dist/astro/routes/api/schema/orphans/index.mjs +1 -1
- package/dist/astro/routes/api/setup/dev-bypass.mjs +1 -1
- package/dist/astro/routes/api/setup/index.mjs +1 -1
- package/dist/astro/routes/api/well-known/auth.mjs +1 -1
- package/dist/astro/types.d.mts +1 -1
- package/dist/{bylines-CtD_p_1z.d.mts → bylines-CNe_OWKA.d.mts} +16 -16
- package/dist/cli/index.mjs +302 -72
- package/dist/db/index.mjs +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +3 -3
- package/dist/media/local-runtime.d.mts +1 -1
- package/dist/plugins/adapt-sandbox-entry.d.mts +1 -1
- package/dist/{preview-5HuX6fjF.mjs → preview-BhgxNRWI.mjs} +1 -1
- package/dist/{runner-lqEiJbO-.mjs → runner-S3smkgdc.mjs} +15 -2
- package/dist/runtime.d.mts +1 -1
- package/dist/version-BWhBEejU.mjs +6 -0
- package/package.json +2 -2
- package/dist/version-q9Wd8cwL.mjs +0 -6
package/dist/cli/index.mjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { t as __exportAll } from "../chunk-ClPoSABd.mjs";
|
|
3
3
|
import { n as createDatabase } from "../connection-BCNICDWN.mjs";
|
|
4
4
|
import { c as listTablesLike } from "../dialect-helpers-TkdbkFad.mjs";
|
|
5
|
-
import {
|
|
5
|
+
import { i as runMigrations, n as getMigrationStatus, t as getMigrationFingerprint } from "../runner-S3smkgdc.mjs";
|
|
6
6
|
import { r as isI18nEnabled } from "../config-XW5tMrH8.mjs";
|
|
7
7
|
import { n as slugify } from "../slugify-BzGxlOFx.mjs";
|
|
8
8
|
import { t as ContentRepository } from "../content-DvpMad_N.mjs";
|
|
@@ -27,9 +27,9 @@ import { createHeaderAwareFetch, customHeadersInterceptor, isRedirectResponse, r
|
|
|
27
27
|
import { o as convertDataForRead } from "../transport-B7kO-4ee.mjs";
|
|
28
28
|
import { DinewayClient } from "../client/index.mjs";
|
|
29
29
|
import { LocalStorage } from "../storage/local.mjs";
|
|
30
|
+
import { createHash } from "node:crypto";
|
|
30
31
|
import { imageSize } from "image-size";
|
|
31
32
|
import { createGzipDecoder, unpackTar } from "modern-tar";
|
|
32
|
-
import { createHash } from "node:crypto";
|
|
33
33
|
import { createReadStream, createWriteStream, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
34
34
|
import { basename, dirname, extname, isAbsolute, join, relative, resolve } from "node:path";
|
|
35
35
|
import { defineCommand, runCommand, runMain } from "citty";
|
|
@@ -190,7 +190,15 @@ function removeMarketplaceCredential(registryUrl) {
|
|
|
190
190
|
//#region src/cli/project-env.ts
|
|
191
191
|
const DEFAULT_DINEWAY_URL = "http://localhost:4321";
|
|
192
192
|
const ENV_KEY_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
193
|
-
const LINE_SPLIT_PATTERN = /\r?\n/;
|
|
193
|
+
const LINE_SPLIT_PATTERN$1 = /\r?\n/;
|
|
194
|
+
const DINEWAY_LOCAL_STATE_GITIGNORE_COMMENT = "# Dineway local state";
|
|
195
|
+
const DINEWAY_LOCAL_STATE_GITIGNORE_ENTRIES = [
|
|
196
|
+
".dineway/deploy.json",
|
|
197
|
+
".dineway/forgeway.json",
|
|
198
|
+
".dineway/types.ts",
|
|
199
|
+
".dineway/schema.json",
|
|
200
|
+
".dineway/uploads/"
|
|
201
|
+
];
|
|
194
202
|
const NEWLINE_PATTERN = /\r?\n/g;
|
|
195
203
|
function parseDotenvValue(value) {
|
|
196
204
|
const trimmed = value.trim();
|
|
@@ -199,7 +207,7 @@ function parseDotenvValue(value) {
|
|
|
199
207
|
}
|
|
200
208
|
function parseDotenv(text) {
|
|
201
209
|
const env = {};
|
|
202
|
-
for (const line of text.split(LINE_SPLIT_PATTERN)) {
|
|
210
|
+
for (const line of text.split(LINE_SPLIT_PATTERN$1)) {
|
|
203
211
|
const trimmed = line.trim();
|
|
204
212
|
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
205
213
|
const normalized = trimmed.startsWith("export ") ? trimmed.slice(7).trim() : trimmed;
|
|
@@ -233,7 +241,7 @@ function formatEnvValue(value) {
|
|
|
233
241
|
async function upsertProjectEnv(cwd, values) {
|
|
234
242
|
const envPath = join(cwd, ".env");
|
|
235
243
|
const existing = await readFile(envPath, "utf-8").catch(() => "");
|
|
236
|
-
const lines = existing ? existing.split(LINE_SPLIT_PATTERN) : [];
|
|
244
|
+
const lines = existing ? existing.split(LINE_SPLIT_PATTERN$1) : [];
|
|
237
245
|
const seen = /* @__PURE__ */ new Set();
|
|
238
246
|
const nextLines = lines.map((line) => {
|
|
239
247
|
const trimmed = line.trim();
|
|
@@ -255,9 +263,18 @@ async function upsertProjectEnv(cwd, values) {
|
|
|
255
263
|
async function ensureProjectGitignoreEntry(cwd, entry) {
|
|
256
264
|
const gitignorePath = join(cwd, ".gitignore");
|
|
257
265
|
const existing = await readFile(gitignorePath, "utf-8").catch(() => "");
|
|
258
|
-
if (existing.split(LINE_SPLIT_PATTERN).map((line) => line.trim()).includes(entry)) return;
|
|
266
|
+
if (existing.split(LINE_SPLIT_PATTERN$1).map((line) => line.trim()).includes(entry)) return;
|
|
259
267
|
await writeFile(gitignorePath, `${existing}${existing && !existing.endsWith("\n") ? "\n" : ""}${entry}\n`, "utf-8");
|
|
260
268
|
}
|
|
269
|
+
async function ensureProjectGitignoreDinewayLocalState(cwd) {
|
|
270
|
+
const gitignorePath = join(cwd, ".gitignore");
|
|
271
|
+
const existing = await readFile(gitignorePath, "utf-8").catch(() => "");
|
|
272
|
+
const lines = new Set(existing.split(LINE_SPLIT_PATTERN$1).map((line) => line.trim()));
|
|
273
|
+
const missingEntries = DINEWAY_LOCAL_STATE_GITIGNORE_ENTRIES.filter((entry) => !lines.has(entry));
|
|
274
|
+
if (missingEntries.length === 0) return;
|
|
275
|
+
const block = [DINEWAY_LOCAL_STATE_GITIGNORE_COMMENT, ...missingEntries].join("\n");
|
|
276
|
+
await writeFile(gitignorePath, `${existing}${existing.trim() ? existing.endsWith("\n") ? "\n" : "\n\n" : ""}${block}\n`, "utf-8");
|
|
277
|
+
}
|
|
261
278
|
|
|
262
279
|
//#endregion
|
|
263
280
|
//#region src/cli/client-factory.ts
|
|
@@ -1553,45 +1570,67 @@ const flyTarget = {
|
|
|
1553
1570
|
|
|
1554
1571
|
//#endregion
|
|
1555
1572
|
//#region src/cli/commands/deploy/utils/config.ts
|
|
1573
|
+
const DINEWAY_DIR_NAME = ".dineway";
|
|
1574
|
+
const DEPLOY_STATE_FILE_NAME = "deploy.json";
|
|
1575
|
+
function deployStatePath(cwd) {
|
|
1576
|
+
return join(cwd, DINEWAY_DIR_NAME, DEPLOY_STATE_FILE_NAME);
|
|
1577
|
+
}
|
|
1578
|
+
function emptyDeployState() {
|
|
1579
|
+
return { version: 1 };
|
|
1580
|
+
}
|
|
1556
1581
|
async function readDeployPackageJson(cwd) {
|
|
1557
1582
|
const content = await readFile(join(cwd, "package.json"), "utf-8");
|
|
1558
1583
|
return JSON.parse(content);
|
|
1559
1584
|
}
|
|
1585
|
+
async function readDeployState(cwd) {
|
|
1586
|
+
try {
|
|
1587
|
+
const content = await readFile(deployStatePath(cwd), "utf-8");
|
|
1588
|
+
return {
|
|
1589
|
+
...emptyDeployState(),
|
|
1590
|
+
...JSON.parse(content),
|
|
1591
|
+
version: 1
|
|
1592
|
+
};
|
|
1593
|
+
} catch (error) {
|
|
1594
|
+
if (error && typeof error === "object" && error.code === "ENOENT") return emptyDeployState();
|
|
1595
|
+
throw error;
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
async function writeDeployState(cwd, state) {
|
|
1599
|
+
await mkdir(join(cwd, DINEWAY_DIR_NAME), { recursive: true });
|
|
1600
|
+
await writeFile(deployStatePath(cwd), JSON.stringify({
|
|
1601
|
+
...state,
|
|
1602
|
+
version: 1
|
|
1603
|
+
}, null, " ") + "\n", "utf-8");
|
|
1604
|
+
}
|
|
1560
1605
|
async function readSavedDeployTarget(cwd) {
|
|
1561
|
-
return (await
|
|
1606
|
+
return (await readDeployState(cwd)).target;
|
|
1562
1607
|
}
|
|
1563
1608
|
async function writeDeployTarget(cwd, target) {
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
}
|
|
1572
|
-
};
|
|
1573
|
-
await writeFile(pkgPath, JSON.stringify(pkg, null, " ") + "\n", "utf-8");
|
|
1609
|
+
await writeDeployState(cwd, {
|
|
1610
|
+
...await readDeployState(cwd),
|
|
1611
|
+
target
|
|
1612
|
+
});
|
|
1613
|
+
}
|
|
1614
|
+
async function readForgewayDeployMetadata(cwd) {
|
|
1615
|
+
return (await readDeployState(cwd)).targets?.forgeway ?? {};
|
|
1574
1616
|
}
|
|
1575
1617
|
async function writeForgewayDeployMetadata(cwd, metadata) {
|
|
1576
|
-
const
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
...pkg.dineway?.deploy,
|
|
1618
|
+
const state = await readDeployState(cwd);
|
|
1619
|
+
await writeDeployState(cwd, {
|
|
1620
|
+
...state,
|
|
1621
|
+
targets: {
|
|
1622
|
+
...state.targets,
|
|
1582
1623
|
forgeway: {
|
|
1583
|
-
...
|
|
1624
|
+
...state.targets?.forgeway,
|
|
1584
1625
|
...metadata
|
|
1585
1626
|
}
|
|
1586
1627
|
}
|
|
1587
|
-
};
|
|
1588
|
-
await writeFile(pkgPath, JSON.stringify(pkg, null, " ") + "\n", "utf-8");
|
|
1628
|
+
});
|
|
1589
1629
|
}
|
|
1590
1630
|
|
|
1591
1631
|
//#endregion
|
|
1592
1632
|
//#region src/cli/commands/deploy/utils/forgeway-secrets.ts
|
|
1593
1633
|
const DINEWAY_CONFIG_DIR_NAME = ".dineway";
|
|
1594
|
-
const DINEWAY_CONFIG_GITIGNORE = "*\n!.gitignore\n";
|
|
1595
1634
|
const TRAILING_SLASH_PATTERN$1 = /\/$/;
|
|
1596
1635
|
function getDinewayConfigDir(cwd = process.cwd()) {
|
|
1597
1636
|
return join(cwd, DINEWAY_CONFIG_DIR_NAME);
|
|
@@ -1608,19 +1647,10 @@ async function readStore(cwd = process.cwd()) {
|
|
|
1608
1647
|
}
|
|
1609
1648
|
}
|
|
1610
1649
|
async function writeStore(store, cwd = process.cwd()) {
|
|
1611
|
-
|
|
1612
|
-
await mkdir(dir, {
|
|
1650
|
+
await mkdir(getDinewayConfigDir(cwd), {
|
|
1613
1651
|
recursive: true,
|
|
1614
1652
|
mode: 448
|
|
1615
1653
|
});
|
|
1616
|
-
try {
|
|
1617
|
-
await writeFile(join(dir, ".gitignore"), DINEWAY_CONFIG_GITIGNORE, {
|
|
1618
|
-
encoding: "utf-8",
|
|
1619
|
-
flag: "wx"
|
|
1620
|
-
});
|
|
1621
|
-
} catch (error) {
|
|
1622
|
-
if (!error || typeof error !== "object" || error.code !== "EEXIST") throw error;
|
|
1623
|
-
}
|
|
1624
1654
|
const file = getForgewaySecretPath(cwd);
|
|
1625
1655
|
await writeFile(file, JSON.stringify(store, null, " ") + "\n", {
|
|
1626
1656
|
encoding: "utf-8",
|
|
@@ -1665,6 +1695,7 @@ const DATABASE_ENV_VAR_NAMES = ["DINEWAY_DATABASE_URL", "DINEWAY_DATABASE_AUTH_T
|
|
|
1665
1695
|
const POLL_INTERVAL_MS$1 = 5e3;
|
|
1666
1696
|
const POLL_TIMEOUT_MS$1 = 3e5;
|
|
1667
1697
|
const DIRECT_UPLOAD_CONCURRENCY = 8;
|
|
1698
|
+
const FILE_HASH_CONCURRENCY = 8;
|
|
1668
1699
|
const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
1669
1700
|
const PLACE_ID_PATTERN = /^[A-Za-z0-9_-]{10,}$/;
|
|
1670
1701
|
const SLUG_SEPARATOR_PATTERN = /[^a-z0-9]+/g;
|
|
@@ -1675,6 +1706,10 @@ const LOCATION_PARTS_PATTERN = /[,;|/]+|\s+-\s+/u;
|
|
|
1675
1706
|
const JWT_PATTERN = /[A-Za-z0-9_-]{32,}\.[A-Za-z0-9_-]{16,}\.[A-Za-z0-9_-]{16,}/g;
|
|
1676
1707
|
const TOKEN_ASSIGNMENT_PATTERN = /(DINEWAY_DATABASE_AUTH_TOKEN|DATABASE_AUTH_TOKEN|AUTH_TOKEN)=\S+/gi;
|
|
1677
1708
|
const TRAILING_SLASH_PATTERN = /\/$/;
|
|
1709
|
+
const LEADING_SLASHES_PATTERN = /^\/+/;
|
|
1710
|
+
const TRAILING_SLASHES_PATTERN = /\/+$/;
|
|
1711
|
+
const REGEXP_ESCAPE_PATTERN = /[|\\{}()[\]^$+?.]/g;
|
|
1712
|
+
const LINE_SPLIT_PATTERN = /\r?\n/;
|
|
1678
1713
|
const BACKSLASH_PATTERN = /\\/g;
|
|
1679
1714
|
const SHADOW_EMAIL_PATTERN = /^shadow_[a-f0-9]{16}@dineway\.ai$/i;
|
|
1680
1715
|
const FOOD_TYPES = new Set([
|
|
@@ -1689,6 +1724,8 @@ const EXCLUDE_PATTERNS = [
|
|
|
1689
1724
|
"node_modules",
|
|
1690
1725
|
".git",
|
|
1691
1726
|
".next",
|
|
1727
|
+
".astro",
|
|
1728
|
+
".plan",
|
|
1692
1729
|
".env",
|
|
1693
1730
|
".env.local",
|
|
1694
1731
|
"dist",
|
|
@@ -1710,7 +1747,11 @@ const EXCLUDE_PATTERNS = [
|
|
|
1710
1747
|
".turbo",
|
|
1711
1748
|
".cache",
|
|
1712
1749
|
"skills",
|
|
1713
|
-
"coverage"
|
|
1750
|
+
"coverage",
|
|
1751
|
+
"uploads",
|
|
1752
|
+
"data.db",
|
|
1753
|
+
"data.db-shm",
|
|
1754
|
+
"data.db-wal"
|
|
1714
1755
|
];
|
|
1715
1756
|
var ForgewayApiError = class extends Error {
|
|
1716
1757
|
constructor(message, status) {
|
|
@@ -1746,6 +1787,39 @@ function getEnv(name) {
|
|
|
1746
1787
|
const value = process.env[name];
|
|
1747
1788
|
return value && value.trim() ? value.trim() : void 0;
|
|
1748
1789
|
}
|
|
1790
|
+
function isVerboseDeploy(options) {
|
|
1791
|
+
return options?.verbose === true || getEnv("DINEWAY_DEPLOY_VERBOSE") === "1";
|
|
1792
|
+
}
|
|
1793
|
+
function formatDuration(ms) {
|
|
1794
|
+
if (ms < 1e3) return `${ms}ms`;
|
|
1795
|
+
return `${Math.round(ms / 100) / 10}s`;
|
|
1796
|
+
}
|
|
1797
|
+
function formatBytes$1(bytes) {
|
|
1798
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
1799
|
+
const units = [
|
|
1800
|
+
"KB",
|
|
1801
|
+
"MB",
|
|
1802
|
+
"GB"
|
|
1803
|
+
];
|
|
1804
|
+
let value = bytes / 1024;
|
|
1805
|
+
for (let index = 0; index < units.length; index++) {
|
|
1806
|
+
const unit = units[index];
|
|
1807
|
+
if (value < 1024 || index === units.length - 1) return `${value.toFixed(value >= 10 ? 0 : 1)} ${unit}`;
|
|
1808
|
+
value /= 1024;
|
|
1809
|
+
}
|
|
1810
|
+
return `${bytes} B`;
|
|
1811
|
+
}
|
|
1812
|
+
function deploymentFileSize(files) {
|
|
1813
|
+
return files.reduce((total, file) => total + file.size, 0);
|
|
1814
|
+
}
|
|
1815
|
+
function formatSeedSummary(seed) {
|
|
1816
|
+
if (!seed) return "no seed";
|
|
1817
|
+
return [
|
|
1818
|
+
`collections ${seed.collections.created}/${seed.collections.skipped}/${seed.collections.updated}`,
|
|
1819
|
+
`content ${seed.content.created}/${seed.content.skipped}/${seed.content.updated}`,
|
|
1820
|
+
`media ${seed.media.created}/${seed.media.skipped}`
|
|
1821
|
+
].join(", ");
|
|
1822
|
+
}
|
|
1749
1823
|
async function readJsonResponse(response) {
|
|
1750
1824
|
return await response.json().catch(() => null);
|
|
1751
1825
|
}
|
|
@@ -1841,8 +1915,8 @@ async function promptRequired(question, initialValue, deps, errorMessage) {
|
|
|
1841
1915
|
if (!answer) throw new Error(errorMessage);
|
|
1842
1916
|
return answer;
|
|
1843
1917
|
}
|
|
1844
|
-
function
|
|
1845
|
-
return
|
|
1918
|
+
async function readSavedForgewayMetadata(cwd) {
|
|
1919
|
+
return await readForgewayDeployMetadata(cwd).catch(() => ({}));
|
|
1846
1920
|
}
|
|
1847
1921
|
async function resolveForgewayAccountEmail(options, grant, cwd, deps) {
|
|
1848
1922
|
const stored = await (deps.readCredentials ?? readForgewayCredentials)(cwd);
|
|
@@ -1920,8 +1994,7 @@ async function resolveFormalAccountCredentials(context, grant, email, deps) {
|
|
|
1920
1994
|
return credentials;
|
|
1921
1995
|
}
|
|
1922
1996
|
async function resolveShadowProjectContext(cwd, options, deps) {
|
|
1923
|
-
const
|
|
1924
|
-
const saved = pkg ? getSavedForgewayMetadata(pkg) : {};
|
|
1997
|
+
const saved = await readSavedForgewayMetadata(cwd);
|
|
1925
1998
|
const platformApiUrl = normalizePlatformApiUrl(getEnv("DINEWAY_API_BASE_URL") ?? getEnv("FORGEWAY_API_URL") ?? (typeof saved.platformApiUrl === "string" ? saved.platformApiUrl : void 0));
|
|
1926
1999
|
const savedPlaceId = options.placeId ?? getEnv("DINEWAY_PLACE_ID") ?? (typeof saved.placeId === "string" ? saved.placeId : void 0);
|
|
1927
2000
|
let grant = await (deps.readShadowGrant ?? readForgewayShadowGrant)(platformApiUrl, savedPlaceId, cwd);
|
|
@@ -2010,8 +2083,12 @@ async function createShadowUser(platformApiUrl, placeId, deps) {
|
|
|
2010
2083
|
function isRecord(value) {
|
|
2011
2084
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2012
2085
|
}
|
|
2086
|
+
function scalarText(value) {
|
|
2087
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") return String(value);
|
|
2088
|
+
return "";
|
|
2089
|
+
}
|
|
2013
2090
|
function uniqueStrings(values) {
|
|
2014
|
-
return [...new Set(values.map((value) =>
|
|
2091
|
+
return [...new Set(values.map((value) => scalarText(value).trim()).filter(Boolean))];
|
|
2015
2092
|
}
|
|
2016
2093
|
function displayText(value) {
|
|
2017
2094
|
if (typeof value === "string" || typeof value === "number") return String(value);
|
|
@@ -2020,10 +2097,10 @@ function displayText(value) {
|
|
|
2020
2097
|
return "";
|
|
2021
2098
|
}
|
|
2022
2099
|
function normalizeCompare(value) {
|
|
2023
|
-
return
|
|
2100
|
+
return scalarText(value).toLowerCase().normalize("NFKD").replace(DIACRITICS_PATTERN, "").replace(NON_COMPARE_PATTERN, "");
|
|
2024
2101
|
}
|
|
2025
2102
|
function candidatePlaceId(candidate) {
|
|
2026
|
-
return
|
|
2103
|
+
return (scalarText(candidate.placeId) || scalarText(candidate.id)).trim();
|
|
2027
2104
|
}
|
|
2028
2105
|
function candidateNameTexts(candidate) {
|
|
2029
2106
|
return uniqueStrings([
|
|
@@ -2140,7 +2217,7 @@ function scorePlaceCandidate(candidate, context) {
|
|
|
2140
2217
|
score += 10;
|
|
2141
2218
|
reasons.push("food_type");
|
|
2142
2219
|
}
|
|
2143
|
-
const businessStatus =
|
|
2220
|
+
const businessStatus = (scalarText(candidate.businessStatus) || scalarText(candidate.business_status)).toUpperCase();
|
|
2144
2221
|
if (businessStatus === "OPERATIONAL") {
|
|
2145
2222
|
score += 4;
|
|
2146
2223
|
reasons.push("operational");
|
|
@@ -2391,8 +2468,7 @@ async function createDeploymentSite(context, input, deps) {
|
|
|
2391
2468
|
});
|
|
2392
2469
|
}
|
|
2393
2470
|
async function resolveDeploymentSite(cwd, context, options, seedPath, deps, preResolvedRestaurant) {
|
|
2394
|
-
const
|
|
2395
|
-
const saved = pkg ? getSavedForgewayMetadata(pkg) : {};
|
|
2471
|
+
const saved = await readSavedForgewayMetadata(cwd);
|
|
2396
2472
|
const explicitSite = options.site;
|
|
2397
2473
|
const savedSite = typeof saved.siteId === "string" ? saved.siteId : saved.siteSlug;
|
|
2398
2474
|
const siteRef = context.placeId ? explicitSite : explicitSite ?? savedSite;
|
|
@@ -2422,20 +2498,50 @@ function redactDatabaseOutput(value, secrets) {
|
|
|
2422
2498
|
for (const secret of secrets) if (secret.length > 0) redacted = redacted.split(secret).join("[redacted]");
|
|
2423
2499
|
return redacted.replace(JWT_PATTERN, "[redacted-token]").replace(TOKEN_ASSIGNMENT_PATTERN, "$1=[redacted]");
|
|
2424
2500
|
}
|
|
2501
|
+
async function hashFileSha256(filePath) {
|
|
2502
|
+
const hash = createHash("sha256");
|
|
2503
|
+
for await (const chunk of createReadStream(filePath)) hash.update(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
2504
|
+
return `sha256:${hash.digest("hex")}`;
|
|
2505
|
+
}
|
|
2506
|
+
async function buildDinewayInitializationFingerprint(seedPath) {
|
|
2507
|
+
const migration = getMigrationFingerprint();
|
|
2508
|
+
return {
|
|
2509
|
+
version: 1,
|
|
2510
|
+
migrationHash: migration.migrationHash,
|
|
2511
|
+
migrationCount: migration.migrationCount,
|
|
2512
|
+
seedHash: seedPath ? await hashFileSha256(seedPath) : null
|
|
2513
|
+
};
|
|
2514
|
+
}
|
|
2515
|
+
function initializationFingerprintsMatch(left, right) {
|
|
2516
|
+
return left?.version === right.version && left.migrationHash === right.migrationHash && left.migrationCount === right.migrationCount && left.seedHash === right.seedHash;
|
|
2517
|
+
}
|
|
2518
|
+
function localDatabaseInitializationMatches(metadata, siteId, databaseId, fingerprint) {
|
|
2519
|
+
return metadata?.status === "succeeded" && metadata.siteId === siteId && metadata.databaseId === databaseId && initializationFingerprintsMatch(metadata.fingerprint, fingerprint);
|
|
2520
|
+
}
|
|
2521
|
+
async function readLocalDatabaseInitializationMetadata(cwd) {
|
|
2522
|
+
return (await readSavedForgewayMetadata(cwd)).databaseInitialization;
|
|
2523
|
+
}
|
|
2524
|
+
async function writeLocalDatabaseInitializationMetadata(cwd, metadata) {
|
|
2525
|
+
await writeForgewayDeployMetadata(cwd, { databaseInitialization: {
|
|
2526
|
+
...metadata,
|
|
2527
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2528
|
+
} });
|
|
2529
|
+
}
|
|
2425
2530
|
async function runDinewayInitialization(options) {
|
|
2426
2531
|
const db = createDatabase({
|
|
2427
2532
|
url: options.credentials.databaseUrl,
|
|
2428
2533
|
authToken: options.credentials.authToken
|
|
2429
2534
|
});
|
|
2430
2535
|
try {
|
|
2431
|
-
await runMigrations(db);
|
|
2536
|
+
const migrations = await runMigrations(db);
|
|
2537
|
+
let seedResult = null;
|
|
2432
2538
|
if (options.seedPath) {
|
|
2433
2539
|
const seed = JSON.parse(await readFile(options.seedPath, "utf-8"));
|
|
2434
2540
|
const validation = validateSeed(seed);
|
|
2435
2541
|
if (!validation.valid) throw new Error(`Seed validation failed: ${validation.errors.join("; ")}`);
|
|
2436
2542
|
const uploadsDir = resolve(options.cwd, "uploads");
|
|
2437
2543
|
await mkdir(uploadsDir, { recursive: true });
|
|
2438
|
-
await applySeed(db, seed, {
|
|
2544
|
+
seedResult = await applySeed(db, seed, {
|
|
2439
2545
|
includeContent: true,
|
|
2440
2546
|
onConflict: "skip",
|
|
2441
2547
|
storage: new LocalStorage({
|
|
@@ -2444,6 +2550,10 @@ async function runDinewayInitialization(options) {
|
|
|
2444
2550
|
})
|
|
2445
2551
|
});
|
|
2446
2552
|
}
|
|
2553
|
+
return {
|
|
2554
|
+
migrations,
|
|
2555
|
+
seed: seedResult
|
|
2556
|
+
};
|
|
2447
2557
|
} catch (error) {
|
|
2448
2558
|
const message = error instanceof Error ? error.message : String(error);
|
|
2449
2559
|
throw new Error(redactDatabaseOutput(message, [options.credentials.authToken, options.credentials.databaseUrl]), { cause: error });
|
|
@@ -2474,25 +2584,75 @@ async function recordSiteDatabaseInitialization(context, siteId, databaseId, sta
|
|
|
2474
2584
|
});
|
|
2475
2585
|
}
|
|
2476
2586
|
async function initializeSiteDatabaseBeforeDeploy(options) {
|
|
2587
|
+
const fingerprint = await buildDinewayInitializationFingerprint(options.seedPath);
|
|
2588
|
+
const bindStart = Date.now();
|
|
2477
2589
|
const databaseId = (await bindSiteDatabase(options.context, options.siteId, options.database, options.deps)).binding.database.id;
|
|
2590
|
+
if (options.verbose) consola.info(`Database bound in ${formatDuration(Date.now() - bindStart)}`);
|
|
2591
|
+
const localInitialization = await readLocalDatabaseInitializationMetadata(options.cwd);
|
|
2592
|
+
if (!options.force && localDatabaseInitializationMatches(localInitialization, options.siteId, databaseId, fingerprint)) return {
|
|
2593
|
+
status: "skipped",
|
|
2594
|
+
databaseId,
|
|
2595
|
+
fingerprint
|
|
2596
|
+
};
|
|
2597
|
+
const revealStart = Date.now();
|
|
2478
2598
|
const credentials = await revealDatabaseRuntimeCredentials(options.context, databaseId, options.deps);
|
|
2599
|
+
if (options.verbose) consola.info(`Database credentials revealed in ${formatDuration(Date.now() - revealStart)}`);
|
|
2479
2600
|
try {
|
|
2480
|
-
await (options.deps.runDinewayInitialization ?? runDinewayInitialization)({
|
|
2601
|
+
const result = await (options.deps.runDinewayInitialization ?? runDinewayInitialization)({
|
|
2481
2602
|
cwd: options.cwd,
|
|
2482
2603
|
credentials,
|
|
2483
2604
|
seedPath: options.seedPath
|
|
2484
2605
|
});
|
|
2485
2606
|
await recordSiteDatabaseInitialization(options.context, options.siteId, credentials.databaseId || databaseId, "succeeded", void 0, options.deps);
|
|
2607
|
+
await writeLocalDatabaseInitializationMetadata(options.cwd, {
|
|
2608
|
+
siteId: options.siteId,
|
|
2609
|
+
databaseId: credentials.databaseId || databaseId,
|
|
2610
|
+
status: "succeeded",
|
|
2611
|
+
fingerprint
|
|
2612
|
+
});
|
|
2613
|
+
return {
|
|
2614
|
+
status: "initialized",
|
|
2615
|
+
databaseId: credentials.databaseId || databaseId,
|
|
2616
|
+
fingerprint,
|
|
2617
|
+
result
|
|
2618
|
+
};
|
|
2486
2619
|
} catch (error) {
|
|
2487
2620
|
const message = redactDatabaseOutput(error instanceof Error ? error.message : String(error), [credentials.authToken, credentials.databaseUrl]);
|
|
2488
2621
|
await recordSiteDatabaseInitialization(options.context, options.siteId, credentials.databaseId || databaseId, "failed", message, options.deps).catch(() => void 0);
|
|
2622
|
+
await writeLocalDatabaseInitializationMetadata(options.cwd, {
|
|
2623
|
+
siteId: options.siteId,
|
|
2624
|
+
databaseId: credentials.databaseId || databaseId,
|
|
2625
|
+
status: "failed",
|
|
2626
|
+
fingerprint,
|
|
2627
|
+
error: message.slice(0, 2e3)
|
|
2628
|
+
}).catch(() => void 0);
|
|
2489
2629
|
throw new Error(message, { cause: error });
|
|
2490
2630
|
}
|
|
2491
2631
|
}
|
|
2492
|
-
function
|
|
2632
|
+
function normalizeIgnorePattern(pattern) {
|
|
2633
|
+
return pattern.trim().replace(BACKSLASH_PATTERN, "/").replace(LEADING_SLASHES_PATTERN, "").replace(TRAILING_SLASHES_PATTERN, "");
|
|
2634
|
+
}
|
|
2635
|
+
function wildcardPatternToRegExp(pattern) {
|
|
2636
|
+
const source = pattern.split("**").map((part) => part.split("*").map((segment) => segment.replace(REGEXP_ESCAPE_PATTERN, "\\$&")).join("[^/]*")).join(".*");
|
|
2637
|
+
return new RegExp(`^${source}$`);
|
|
2638
|
+
}
|
|
2639
|
+
function matchesIgnorePattern(path, rawPattern) {
|
|
2640
|
+
const pattern = normalizeIgnorePattern(rawPattern);
|
|
2641
|
+
if (!pattern || pattern.startsWith("#")) return false;
|
|
2642
|
+
if (pattern.includes("*")) return wildcardPatternToRegExp(pattern).test(path);
|
|
2643
|
+
if (!pattern.includes("/")) return path === pattern || path.startsWith(`${pattern}/`) || basename(path) === pattern;
|
|
2644
|
+
return path === pattern || path.startsWith(`${pattern}/`);
|
|
2645
|
+
}
|
|
2646
|
+
async function readDeploymentIgnorePatterns(sourceDir) {
|
|
2647
|
+
const ignorePath = join(sourceDir, ".dinewayignore");
|
|
2648
|
+
if (!await fileExists$8(ignorePath)) return [];
|
|
2649
|
+
return (await readFile(ignorePath, "utf-8")).split(LINE_SPLIT_PATTERN).map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
|
|
2650
|
+
}
|
|
2651
|
+
function shouldExclude(name, customPatterns = []) {
|
|
2493
2652
|
const normalized = name.replace(BACKSLASH_PATTERN, "/");
|
|
2494
2653
|
for (const pattern of EXCLUDE_PATTERNS) if (normalized === pattern || normalized.startsWith(`${pattern}/`) || normalized.endsWith(`/${pattern}`) || normalized.includes(`/${pattern}/`)) return true;
|
|
2495
|
-
|
|
2654
|
+
if (normalized.endsWith(".log")) return true;
|
|
2655
|
+
return customPatterns.some((pattern) => matchesIgnorePattern(normalized, pattern));
|
|
2496
2656
|
}
|
|
2497
2657
|
function normalizeRelativePath(sourceDir, absolutePath) {
|
|
2498
2658
|
return relative(sourceDir, absolutePath).replace(BACKSLASH_PATTERN, "/");
|
|
@@ -2511,6 +2671,8 @@ async function hashFile(filePath) {
|
|
|
2511
2671
|
};
|
|
2512
2672
|
}
|
|
2513
2673
|
async function collectDeploymentFiles(sourceDir) {
|
|
2674
|
+
const customIgnorePatterns = await readDeploymentIgnorePatterns(sourceDir);
|
|
2675
|
+
const candidates = [];
|
|
2514
2676
|
const files = [];
|
|
2515
2677
|
async function walk(currentDir) {
|
|
2516
2678
|
const entries = await readdir(currentDir, { withFileTypes: true });
|
|
@@ -2518,22 +2680,29 @@ async function collectDeploymentFiles(sourceDir) {
|
|
|
2518
2680
|
for (const entry of entries) {
|
|
2519
2681
|
const absolutePath = join(currentDir, entry.name);
|
|
2520
2682
|
const normalizedPath = normalizeRelativePath(sourceDir, absolutePath);
|
|
2521
|
-
if (!normalizedPath || shouldExclude(normalizedPath)) continue;
|
|
2683
|
+
if (!normalizedPath || shouldExclude(normalizedPath, customIgnorePatterns)) continue;
|
|
2522
2684
|
if (entry.isDirectory()) {
|
|
2523
2685
|
await walk(absolutePath);
|
|
2524
2686
|
continue;
|
|
2525
2687
|
}
|
|
2526
2688
|
if (!entry.isFile()) continue;
|
|
2527
|
-
|
|
2528
|
-
files.push({
|
|
2689
|
+
candidates.push({
|
|
2529
2690
|
absolutePath,
|
|
2530
|
-
path: normalizedPath
|
|
2531
|
-
sha,
|
|
2532
|
-
size
|
|
2691
|
+
path: normalizedPath
|
|
2533
2692
|
});
|
|
2534
2693
|
}
|
|
2535
2694
|
}
|
|
2536
2695
|
await walk(sourceDir);
|
|
2696
|
+
await runWithConcurrency(candidates, FILE_HASH_CONCURRENCY, async ({ absolutePath, path }) => {
|
|
2697
|
+
const { sha, size } = await hashFile(absolutePath);
|
|
2698
|
+
files.push({
|
|
2699
|
+
absolutePath,
|
|
2700
|
+
path,
|
|
2701
|
+
sha,
|
|
2702
|
+
size
|
|
2703
|
+
});
|
|
2704
|
+
});
|
|
2705
|
+
files.sort((a, b) => a.path.localeCompare(b.path));
|
|
2537
2706
|
return files;
|
|
2538
2707
|
}
|
|
2539
2708
|
async function runWithConcurrency(items, concurrency, worker) {
|
|
@@ -2571,16 +2740,23 @@ function getDeploymentError(metadata) {
|
|
|
2571
2740
|
if (!metadata || typeof metadata.error !== "object" || metadata.error === null) return null;
|
|
2572
2741
|
return metadata.error.errorMessage ?? null;
|
|
2573
2742
|
}
|
|
2574
|
-
async function pollDeployment(context, deploymentId, deps) {
|
|
2743
|
+
async function pollDeployment(context, deploymentId, deps, verbose) {
|
|
2575
2744
|
const pollIntervalMs = deps.pollIntervalMs ?? POLL_INTERVAL_MS$1;
|
|
2576
2745
|
const pollTimeoutMs = deps.pollTimeoutMs ?? POLL_TIMEOUT_MS$1;
|
|
2577
2746
|
const startTime = Date.now();
|
|
2578
2747
|
let deployment = null;
|
|
2748
|
+
let firstPoll = true;
|
|
2749
|
+
let lastStatus = null;
|
|
2579
2750
|
while (Date.now() - startTime < pollTimeoutMs) {
|
|
2580
|
-
if (pollIntervalMs > 0) await new Promise((done) => setTimeout(done, pollIntervalMs));
|
|
2751
|
+
if (!firstPoll && pollIntervalMs > 0) await new Promise((done) => setTimeout(done, pollIntervalMs));
|
|
2752
|
+
firstPoll = false;
|
|
2581
2753
|
await ossFetch(context, `/api/deployments/${encodeURIComponent(deploymentId)}/sync`, deps, { method: "POST" }).catch(() => void 0);
|
|
2582
2754
|
deployment = await ossFetch(context, `/api/deployments/${encodeURIComponent(deploymentId)}`, deps);
|
|
2583
2755
|
const status = deployment.status.toUpperCase();
|
|
2756
|
+
if (verbose || status !== lastStatus) {
|
|
2757
|
+
consola.info(`Deployment status: ${status} (${formatDuration(Date.now() - startTime)} elapsed)`);
|
|
2758
|
+
lastStatus = status;
|
|
2759
|
+
}
|
|
2584
2760
|
if (status === "READY") break;
|
|
2585
2761
|
if (status === "ERROR" || status === "CANCELED") throw new Error(getDeploymentError(deployment.metadata) ?? `Deployment failed with status: ${deployment.status}`);
|
|
2586
2762
|
if (pollIntervalMs === 0) break;
|
|
@@ -2594,8 +2770,13 @@ async function pollDeployment(context, deploymentId, deps) {
|
|
|
2594
2770
|
}
|
|
2595
2771
|
async function deploySiteProject(options) {
|
|
2596
2772
|
options.deps.onEvent?.("scan");
|
|
2773
|
+
const scanStart = Date.now();
|
|
2774
|
+
consola.start("Scanning deploy files");
|
|
2597
2775
|
const localFiles = await (options.deps.collectDeploymentFiles ?? collectDeploymentFiles)(options.sourceDir);
|
|
2598
2776
|
if (localFiles.length === 0) throw new Error("No deployable files found in the source directory.");
|
|
2777
|
+
consola.success(`Scanned ${localFiles.length} files (${formatBytes$1(deploymentFileSize(localFiles))}) in ${formatDuration(Date.now() - scanStart)}`);
|
|
2778
|
+
const createStart = Date.now();
|
|
2779
|
+
consola.start("Creating Forgeway deployment");
|
|
2599
2780
|
const createResult = await ossFetch(options.context, `/api/deployments/sites/${encodeURIComponent(options.siteId)}/deploy`, options.deps, {
|
|
2600
2781
|
method: "POST",
|
|
2601
2782
|
body: JSON.stringify({
|
|
@@ -2607,15 +2788,25 @@ async function deploySiteProject(options) {
|
|
|
2607
2788
|
}))
|
|
2608
2789
|
})
|
|
2609
2790
|
});
|
|
2791
|
+
if (options.verbose) consola.info(`Deployment manifest accepted in ${formatDuration(Date.now() - createStart)}`);
|
|
2610
2792
|
const localFileByPath = new Map(localFiles.map((file) => [file.path, file]));
|
|
2611
|
-
|
|
2793
|
+
const pendingFiles = createResult.files.filter((file) => !file.uploadedAt);
|
|
2794
|
+
const pendingSize = pendingFiles.reduce((total, file) => total + file.size, 0);
|
|
2795
|
+
if (pendingFiles.length === 0) consola.success("No file uploads needed");
|
|
2796
|
+
else consola.start(`Uploading ${pendingFiles.length} changed files (${formatBytes$1(pendingSize)})`);
|
|
2797
|
+
let uploadedCount = 0;
|
|
2798
|
+
await runWithConcurrency(pendingFiles, DIRECT_UPLOAD_CONCURRENCY, async (manifestFile) => {
|
|
2612
2799
|
const localFile = localFileByPath.get(manifestFile.path);
|
|
2613
2800
|
if (!localFile) throw new Error(`Forgeway returned an unknown file path: ${manifestFile.path}`);
|
|
2614
2801
|
if (localFile.sha !== manifestFile.sha || localFile.size !== manifestFile.size) throw new Error(`Forgeway file metadata mismatch for: ${manifestFile.path}`);
|
|
2615
2802
|
await uploadDirectDeploymentFile(options.context, createResult.id, manifestFile, localFile, options.deps);
|
|
2803
|
+
uploadedCount += 1;
|
|
2804
|
+
if (options.verbose || uploadedCount === pendingFiles.length || uploadedCount % 10 === 0) consola.info(`Uploaded ${uploadedCount}/${pendingFiles.length} files`);
|
|
2616
2805
|
});
|
|
2806
|
+
consola.start("Starting Forgeway deployment");
|
|
2617
2807
|
await startDirectDeployment(options.context, createResult.id, options.startBody, options.deps);
|
|
2618
|
-
|
|
2808
|
+
consola.start("Waiting for Forgeway deployment");
|
|
2809
|
+
const result = await pollDeployment(options.context, createResult.id, options.deps, options.verbose);
|
|
2619
2810
|
return {
|
|
2620
2811
|
deploymentId: createResult.id,
|
|
2621
2812
|
...result,
|
|
@@ -2632,7 +2823,12 @@ async function persistForgewayCliEnv(cwd, params) {
|
|
|
2632
2823
|
}
|
|
2633
2824
|
function formatDinewayAdminBootstrapMessage(bootstrap) {
|
|
2634
2825
|
if (!bootstrap) return "";
|
|
2635
|
-
if (bootstrap.status === "already_issued") return
|
|
2826
|
+
if (bootstrap.status === "already_issued") return [
|
|
2827
|
+
"Dineway admin access is ready. Local CLI defaults were saved to .env.",
|
|
2828
|
+
`Dineway admin email: ${bootstrap.adminEmail}`,
|
|
2829
|
+
`Dineway admin bootstrap: ${bootstrap.message}`,
|
|
2830
|
+
"CLI example: npx dineway whoami"
|
|
2831
|
+
].join("\n");
|
|
2636
2832
|
return [
|
|
2637
2833
|
"Dineway admin access is ready. Local CLI defaults were saved to .env.",
|
|
2638
2834
|
`Dineway admin email: ${bootstrap.adminEmail}`,
|
|
@@ -2645,7 +2841,10 @@ async function deployForgeway(cwd, options, deps = {}) {
|
|
|
2645
2841
|
const sourceDir = resolve(cwd);
|
|
2646
2842
|
if (!(await stat(sourceDir).catch(() => null))?.isDirectory()) throw new Error(`"${sourceDir}" is not a valid directory.`);
|
|
2647
2843
|
if (EXCLUDE_PATTERNS.includes(basename(sourceDir))) throw new Error(`"${basename(sourceDir)}" is an excluded directory and cannot be deployed.`);
|
|
2648
|
-
|
|
2844
|
+
await ensureProjectGitignoreDinewayLocalState(cwd);
|
|
2845
|
+
const verbose = isVerboseDeploy(options);
|
|
2846
|
+
const seedPath = options.skipSeed ? null : await resolveSeedPath$1(cwd, options.seed);
|
|
2847
|
+
if (options.skipSeed) consola.info("Skipping seed initialization by request");
|
|
2649
2848
|
const { context, restaurant } = await resolveProjectContext(cwd, options, deps);
|
|
2650
2849
|
await ensureRestaurantClaim(context, restaurant, deps);
|
|
2651
2850
|
const { site } = await resolveDeploymentSite(cwd, context, options, seedPath, deps, restaurant);
|
|
@@ -2654,15 +2853,22 @@ async function deployForgeway(cwd, options, deps = {}) {
|
|
|
2654
2853
|
consola.start("Initializing Forgeway managed database");
|
|
2655
2854
|
try {
|
|
2656
2855
|
deps.onEvent?.("initialize");
|
|
2657
|
-
await initializeSiteDatabaseBeforeDeploy({
|
|
2856
|
+
const initialization = await initializeSiteDatabaseBeforeDeploy({
|
|
2658
2857
|
context,
|
|
2659
2858
|
siteId: site.id,
|
|
2660
2859
|
database,
|
|
2661
2860
|
cwd,
|
|
2662
2861
|
seedPath,
|
|
2862
|
+
force: options.forceDbInit === true,
|
|
2863
|
+
verbose,
|
|
2663
2864
|
deps
|
|
2664
2865
|
});
|
|
2665
|
-
consola.success("Database initialized");
|
|
2866
|
+
if (initialization.status === "skipped") consola.success("Database already initialized for current schema and seed (local metadata)");
|
|
2867
|
+
else {
|
|
2868
|
+
const applied = initialization.result?.migrations.applied.length ?? 0;
|
|
2869
|
+
const seedSummary = formatSeedSummary(initialization.result?.seed);
|
|
2870
|
+
consola.success(`Database initialized: ${applied} migrations applied, ${seedSummary}`);
|
|
2871
|
+
}
|
|
2666
2872
|
} catch (error) {
|
|
2667
2873
|
consola.error("Database initialization failed");
|
|
2668
2874
|
throw error;
|
|
@@ -2674,12 +2880,13 @@ async function deployForgeway(cwd, options, deps = {}) {
|
|
|
2674
2880
|
siteId: site.id,
|
|
2675
2881
|
sourceDir,
|
|
2676
2882
|
startBody: {},
|
|
2677
|
-
deps
|
|
2883
|
+
deps,
|
|
2884
|
+
verbose
|
|
2678
2885
|
});
|
|
2679
2886
|
const url = deployment.liveUrl ?? `https://${site.domain}`;
|
|
2680
2887
|
await (deps.writeProjectEnv ?? persistForgewayCliEnv)(cwd, {
|
|
2681
2888
|
siteUrl: url,
|
|
2682
|
-
token: deployment.dinewayAdminBootstrap?.
|
|
2889
|
+
token: deployment.dinewayAdminBootstrap?.apiToken
|
|
2683
2890
|
});
|
|
2684
2891
|
const bootstrapMessage = formatDinewayAdminBootstrapMessage(deployment.dinewayAdminBootstrap);
|
|
2685
2892
|
const statusMessage = deployment.isReady ? `Deploy complete: ${url}` : `Deploy started for ${site.domain}. Check Forgeway for build status.`;
|
|
@@ -3246,6 +3453,21 @@ const deployCommand = defineCommand({
|
|
|
3246
3453
|
type: "string",
|
|
3247
3454
|
description: "Seed file path for Forgeway Dineway initialization",
|
|
3248
3455
|
required: false
|
|
3456
|
+
},
|
|
3457
|
+
"skip-seed": {
|
|
3458
|
+
type: "boolean",
|
|
3459
|
+
description: "Skip applying seed data during Forgeway database initialization",
|
|
3460
|
+
default: false
|
|
3461
|
+
},
|
|
3462
|
+
"force-db-init": {
|
|
3463
|
+
type: "boolean",
|
|
3464
|
+
description: "Force Forgeway database migrations and seed even when initialization fingerprints match",
|
|
3465
|
+
default: false
|
|
3466
|
+
},
|
|
3467
|
+
verbose: {
|
|
3468
|
+
type: "boolean",
|
|
3469
|
+
description: "Show detailed Forgeway deploy progress and timing logs",
|
|
3470
|
+
default: false
|
|
3249
3471
|
}
|
|
3250
3472
|
},
|
|
3251
3473
|
async run({ args }) {
|
|
@@ -3262,7 +3484,10 @@ const deployCommand = defineCommand({
|
|
|
3262
3484
|
restaurantName: args["restaurant-name"],
|
|
3263
3485
|
city: args.city,
|
|
3264
3486
|
database: args.database,
|
|
3265
|
-
seed: args.seed
|
|
3487
|
+
seed: args.seed,
|
|
3488
|
+
skipSeed: args["skip-seed"],
|
|
3489
|
+
forceDbInit: args["force-db-init"],
|
|
3490
|
+
verbose: args.verbose
|
|
3266
3491
|
};
|
|
3267
3492
|
consola.start(`Preparing ${target.label} deploy`);
|
|
3268
3493
|
await ensureTargetCli(target, cwd, {
|
|
@@ -3290,8 +3515,13 @@ const deployCommand = defineCommand({
|
|
|
3290
3515
|
const build = options.dryRun ? null : await runBuild(cwd, options.env);
|
|
3291
3516
|
consola.info(options.dryRun ? "[dry-run] Would run build" : `Built with ${build?.display}`);
|
|
3292
3517
|
}
|
|
3293
|
-
if (!options.dryRun)
|
|
3294
|
-
|
|
3518
|
+
if (!options.dryRun) {
|
|
3519
|
+
await ensureProjectGitignoreDinewayLocalState(cwd);
|
|
3520
|
+
await writeDeployTarget(cwd, target.name);
|
|
3521
|
+
} else {
|
|
3522
|
+
consola.info("[dry-run] Would ensure Dineway local state entries in .gitignore");
|
|
3523
|
+
consola.info(`[dry-run] Would save .dineway/deploy.json target=${target.name}`);
|
|
3524
|
+
}
|
|
3295
3525
|
const result = await target.deploy(cwd, options);
|
|
3296
3526
|
if (result.url) consola.success(result.url);
|
|
3297
3527
|
consola.success(result.message);
|