gencow 0.1.165 → 0.1.166
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/bin/gencow.mjs +1 -1
- package/lib/auth-command.mjs +9 -4
- package/lib/backup-command.mjs +57 -3
- package/lib/cli-project-runtime.mjs +16 -4
- package/lib/codegen/index.mjs +6 -6
- package/lib/cron-manifest.mjs +128 -28
- package/lib/db-command.mjs +18 -8
- package/lib/deploy-package-runtime.mjs +27 -10
- package/lib/dev-cloud-migrations.mjs +6 -3
- package/lib/dev-local-command.mjs +31 -7
- package/lib/drizzle-generate.mjs +34 -0
- package/lib/static-deploy-command.mjs +9 -6
- package/lib/tar-archive.mjs +61 -0
- package/lib/template-marketplace-command.mjs +41 -18
- package/package.json +1 -1
- package/server/index.js +1 -0
- package/server/index.js.map +2 -2
package/bin/gencow.mjs
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* gencow db:restore — restore DB from backup
|
|
12
12
|
* gencow db:studio — open Drizzle Studio (visual DB browser)
|
|
13
13
|
* gencow dashboard — open /_admin in browser
|
|
14
|
-
* gencow backup — manage database backups (list/create/restore/delete/download)
|
|
14
|
+
* gencow backup — manage database backups (list/create/restore/restore-file/delete/download)
|
|
15
15
|
* gencow templates — publish/list/download/clone marketplace templates
|
|
16
16
|
* gencow help — show this help
|
|
17
17
|
*/
|
package/lib/auth-command.mjs
CHANGED
|
@@ -7,7 +7,7 @@ export const CREDS_PATH_LABEL = "~/.gencow/credentials.json";
|
|
|
7
7
|
|
|
8
8
|
export function getBrowserOpenCommand(platform = process.platform) {
|
|
9
9
|
if (platform === "darwin") return "open";
|
|
10
|
-
if (platform === "win32") return
|
|
10
|
+
if (platform === "win32") return 'cmd /c start ""';
|
|
11
11
|
return "xdg-open";
|
|
12
12
|
}
|
|
13
13
|
|
|
@@ -18,9 +18,14 @@ export function formatWhoamiExpiry(expiresAt, now = Date.now()) {
|
|
|
18
18
|
return `${expiresDate.toLocaleString()} (${daysLeft} days left)`;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
async function
|
|
22
|
-
const {
|
|
23
|
-
|
|
21
|
+
export async function openBrowserUrl(url, options = {}) {
|
|
22
|
+
const { openImpl } = options;
|
|
23
|
+
const openFn = openImpl ?? (await import("open")).default;
|
|
24
|
+
await openFn(url);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function defaultOpenUrl(url) {
|
|
28
|
+
await openBrowserUrl(url);
|
|
24
29
|
}
|
|
25
30
|
|
|
26
31
|
function sleep(ms, setTimeoutImpl) {
|
package/lib/backup-command.mjs
CHANGED
|
@@ -17,6 +17,18 @@ export function resolveBackupAppId(restArgs, gencowJson = null) {
|
|
|
17
17
|
return appId;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
export function resolveBackupFilePath(restArgs) {
|
|
21
|
+
for (let i = 0; i < restArgs.length; i++) {
|
|
22
|
+
const arg = restArgs[i];
|
|
23
|
+
if (arg === "--app" || arg === "-a") {
|
|
24
|
+
i++;
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
if (!arg.startsWith("-")) return arg;
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
|
|
20
32
|
export function formatBackupSize(bytes) {
|
|
21
33
|
if (bytes < 1024) return `${bytes} B`;
|
|
22
34
|
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
@@ -39,16 +51,17 @@ function renderBackupHelp(logImpl = log, errorImpl = error, subCmd = null) {
|
|
|
39
51
|
logImpl(` gencow backup list List all backups`);
|
|
40
52
|
logImpl(` gencow backup create [note] Create a manual backup`);
|
|
41
53
|
logImpl(` gencow backup restore <id> Restore from backup`);
|
|
54
|
+
logImpl(` gencow backup restore-file <path> Restore from a downloaded dump file`);
|
|
42
55
|
logImpl(` gencow backup delete <id> Delete a backup`);
|
|
43
56
|
logImpl(` gencow backup download <id> Download backup file (Pro+)\n`);
|
|
44
57
|
}
|
|
45
58
|
|
|
46
|
-
async function promptRestoreConfirm(
|
|
59
|
+
async function promptRestoreConfirm(sourceLabel, { createInterfaceImpl }) {
|
|
47
60
|
const createInterface = createInterfaceImpl ?? (await import("readline")).createInterface;
|
|
48
61
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
49
62
|
return await new Promise((resolveAnswer) => {
|
|
50
63
|
rl.question(
|
|
51
|
-
`\n ${YELLOW}⚠ 현재 데이터베이스가
|
|
64
|
+
`\n ${YELLOW}⚠ 현재 데이터베이스가 ${sourceLabel}로 교체됩니다. 계속하시겠습니까? (y/N): ${RESET}`,
|
|
52
65
|
(answer) => {
|
|
53
66
|
rl.close();
|
|
54
67
|
const normalized = answer.trim().toLowerCase();
|
|
@@ -66,6 +79,7 @@ export function createBackupCommand({
|
|
|
66
79
|
infoImpl = info,
|
|
67
80
|
logImpl = log,
|
|
68
81
|
platformFetchImpl = platformFetch,
|
|
82
|
+
readFileAsyncImpl,
|
|
69
83
|
readFileSyncImpl = readFileSync,
|
|
70
84
|
requireCredsImpl = requireCreds,
|
|
71
85
|
resolvePathImpl = resolve,
|
|
@@ -155,7 +169,7 @@ export function createBackupCommand({
|
|
|
155
169
|
return;
|
|
156
170
|
}
|
|
157
171
|
|
|
158
|
-
const confirmed = await promptRestoreConfirm(backupId
|
|
172
|
+
const confirmed = await promptRestoreConfirm(`백업 #${backupId}`, { createInterfaceImpl });
|
|
159
173
|
if (!confirmed) {
|
|
160
174
|
infoImpl("Restore cancelled.");
|
|
161
175
|
return;
|
|
@@ -173,6 +187,46 @@ export function createBackupCommand({
|
|
|
173
187
|
return;
|
|
174
188
|
}
|
|
175
189
|
|
|
190
|
+
case "restore-file": {
|
|
191
|
+
const filePath = resolveBackupFilePath(restArgs);
|
|
192
|
+
if (!filePath) {
|
|
193
|
+
errorImpl("Specify backup file path: gencow backup restore-file <path>");
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
if (!existsSyncImpl(filePath)) {
|
|
197
|
+
errorImpl(`Backup file not found: ${filePath}`);
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const confirmed = await promptRestoreConfirm(`파일 ${filePath}`, { createInterfaceImpl });
|
|
202
|
+
if (!confirmed) {
|
|
203
|
+
infoImpl("Restore cancelled.");
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
logImpl(`\n${BOLD}Restoring from file ${filePath}...${RESET}\n`);
|
|
208
|
+
const readFileAsync = readFileAsyncImpl ?? (await import("fs/promises")).readFile;
|
|
209
|
+
const buffer = await readFileAsync(filePath);
|
|
210
|
+
const form = new FormData();
|
|
211
|
+
form.append("file", new Blob([buffer]), filePath.split(/[\\/]/).pop() || "backup.dump");
|
|
212
|
+
const response = await platformFetchImpl(
|
|
213
|
+
creds,
|
|
214
|
+
`/platform/apps/${appId}/backups/restore-file`,
|
|
215
|
+
{
|
|
216
|
+
method: "POST",
|
|
217
|
+
body: form,
|
|
218
|
+
},
|
|
219
|
+
);
|
|
220
|
+
if (!response.ok) {
|
|
221
|
+
errorImpl(
|
|
222
|
+
`Restore failed: ${(await response.json?.().catch(() => ({}))).error || response.statusText}`,
|
|
223
|
+
);
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
successImpl("Database restored from file. App restarted.");
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
|
|
176
230
|
case "delete":
|
|
177
231
|
case "rm": {
|
|
178
232
|
const backupId = parseInt(restArgs[0], 10);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { execSync, spawn } from "child_process";
|
|
2
2
|
import { appendFileSync, existsSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync } from "fs";
|
|
3
3
|
import { createRequire } from "module";
|
|
4
|
-
import { basename, dirname, join, resolve } from "path";
|
|
4
|
+
import { basename, delimiter, dirname, join, resolve } from "path";
|
|
5
5
|
import { fileURLToPath, pathToFileURL } from "url";
|
|
6
6
|
|
|
7
7
|
import { resolveCodegenServerPublishDir } from "./codegen-command.mjs";
|
|
@@ -400,9 +400,20 @@ function ensureEsbuildConsistency(options = {}) {
|
|
|
400
400
|
}
|
|
401
401
|
|
|
402
402
|
export function buildDrizzleKitCommand(subcmd, options = {}) {
|
|
403
|
-
const {
|
|
403
|
+
const {
|
|
404
|
+
cwd = process.cwd(),
|
|
405
|
+
existsSyncImpl = existsSync,
|
|
406
|
+
platform = process.platform,
|
|
407
|
+
resolvePathImpl = resolve,
|
|
408
|
+
} = options;
|
|
404
409
|
const localBin = resolvePathImpl(cwd, "node_modules/.bin/drizzle-kit");
|
|
405
|
-
const
|
|
410
|
+
const windowsBin = `${localBin}.cmd`;
|
|
411
|
+
const resolvedBin = platform === "win32" && existsSyncImpl(windowsBin)
|
|
412
|
+
? windowsBin
|
|
413
|
+
: existsSyncImpl(localBin)
|
|
414
|
+
? localBin
|
|
415
|
+
: null;
|
|
416
|
+
const cmd = resolvedBin ? `"${resolvedBin}" ${subcmd}` : `npx drizzle-kit ${subcmd}`;
|
|
406
417
|
const env = ensureEsbuildConsistency({ ...options, cwd });
|
|
407
418
|
return { cmd, env };
|
|
408
419
|
}
|
|
@@ -509,6 +520,7 @@ export function buildEnv(config, options = {}) {
|
|
|
509
520
|
findServerRootImpl = findServerRoot,
|
|
510
521
|
processEnv = process.env,
|
|
511
522
|
existsSyncImpl = existsSync,
|
|
523
|
+
pathDelimiter = delimiter,
|
|
512
524
|
readFileSyncImpl = readFileSync,
|
|
513
525
|
resolvePathImpl = resolve,
|
|
514
526
|
} = options;
|
|
@@ -531,7 +543,7 @@ export function buildEnv(config, options = {}) {
|
|
|
531
543
|
const existingNodePath = processEnv.NODE_PATH || "";
|
|
532
544
|
const nodePath = [cwdNodeModules, cliNodeModules, serverNodeModules, monoNodeModules, existingNodePath]
|
|
533
545
|
.filter(Boolean)
|
|
534
|
-
.join(
|
|
546
|
+
.join(pathDelimiter);
|
|
535
547
|
|
|
536
548
|
const drizzleSchemaPaths = resolveDrizzleSchemaPaths(config, {
|
|
537
549
|
cwd,
|
package/lib/codegen/index.mjs
CHANGED
|
@@ -8953,7 +8953,7 @@ function sortArtifacts(artifacts) {
|
|
|
8953
8953
|
// src/codegen/infra/auth-schema-worker.ts
|
|
8954
8954
|
import { spawnSync } from "node:child_process";
|
|
8955
8955
|
import { unlinkSync, writeFileSync } from "node:fs";
|
|
8956
|
-
import { resolve } from "node:path";
|
|
8956
|
+
import { delimiter, resolve } from "node:path";
|
|
8957
8957
|
import { pathToFileURL } from "node:url";
|
|
8958
8958
|
function buildGeneratorScript(authPath, emitRelations = true) {
|
|
8959
8959
|
const authUrl = pathToFileURL(authPath).href;
|
|
@@ -9015,7 +9015,7 @@ function runAuthSchemaWorker({
|
|
|
9015
9015
|
encoding: "utf8",
|
|
9016
9016
|
env: {
|
|
9017
9017
|
...process.env,
|
|
9018
|
-
NODE_PATH: [resolve(cwd, "node_modules"), process.env.NODE_PATH ?? ""].filter(Boolean).join(
|
|
9018
|
+
NODE_PATH: [resolve(cwd, "node_modules"), process.env.NODE_PATH ?? ""].filter(Boolean).join(delimiter)
|
|
9019
9019
|
}
|
|
9020
9020
|
});
|
|
9021
9021
|
if (result.error) throw result.error;
|
|
@@ -11942,8 +11942,8 @@ function emoji() {
|
|
|
11942
11942
|
}
|
|
11943
11943
|
var ipv4 = /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/;
|
|
11944
11944
|
var ipv6 = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))$/;
|
|
11945
|
-
var mac = (
|
|
11946
|
-
const escapedDelim = escapeRegex(
|
|
11945
|
+
var mac = (delimiter3) => {
|
|
11946
|
+
const escapedDelim = escapeRegex(delimiter3 ?? ":");
|
|
11947
11947
|
return new RegExp(`^(?:[0-9A-F]{2}${escapedDelim}){5}[0-9A-F]{2}$|^(?:[0-9a-f]{2}${escapedDelim}){5}[0-9a-f]{2}$`);
|
|
11948
11948
|
};
|
|
11949
11949
|
var cidrv4 = /^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\/([0-9]|[1-2][0-9]|3[0-2])$/;
|
|
@@ -25120,7 +25120,7 @@ function assertSourceEntrypoints(input) {
|
|
|
25120
25120
|
}
|
|
25121
25121
|
|
|
25122
25122
|
// src/codegen/frontend/table-introspector.ts
|
|
25123
|
-
import { relative as relative3, resolve as resolve7 } from "node:path";
|
|
25123
|
+
import { delimiter as delimiter2, relative as relative3, resolve as resolve7 } from "node:path";
|
|
25124
25124
|
function createTableIntrospector(deps) {
|
|
25125
25125
|
return {
|
|
25126
25126
|
extract(input) {
|
|
@@ -25141,7 +25141,7 @@ async function extractTables(input, deps) {
|
|
|
25141
25141
|
args: [scriptPath],
|
|
25142
25142
|
cwd: workDir,
|
|
25143
25143
|
env: {
|
|
25144
|
-
NODE_PATH: [resolve7(input.projectRoot, "node_modules"), process.env.NODE_PATH ?? ""].filter(Boolean).join(
|
|
25144
|
+
NODE_PATH: [resolve7(input.projectRoot, "node_modules"), process.env.NODE_PATH ?? ""].filter(Boolean).join(delimiter2)
|
|
25145
25145
|
}
|
|
25146
25146
|
});
|
|
25147
25147
|
if (result.exitCode !== 0) {
|
package/lib/cron-manifest.mjs
CHANGED
|
@@ -284,19 +284,48 @@ function collectInternalProcedureNames(source) {
|
|
|
284
284
|
|
|
285
285
|
function parseDefineApiCronJobsSource(source) {
|
|
286
286
|
const stripped = stripComments(source);
|
|
287
|
-
|
|
288
287
|
const internalNames = collectInternalProcedureNames(stripped);
|
|
288
|
+
const parseJob = ({ name, method, scheduleArgs, actionSource }) => {
|
|
289
|
+
if (!name) return null;
|
|
290
|
+
let pattern = null;
|
|
291
|
+
if (method === "interval") pattern = intervalOptionsToPattern(scheduleArgs);
|
|
292
|
+
if (method === "daily") {
|
|
293
|
+
const hour = parseNumericOption(scheduleArgs, "hour");
|
|
294
|
+
const minute = parseNumericOption(scheduleArgs, "minute") ?? 0;
|
|
295
|
+
if (hour !== undefined) pattern = `${minute} ${hour} * * *`;
|
|
296
|
+
}
|
|
297
|
+
if (method === "weekly") {
|
|
298
|
+
const dayOfWeek = parseNumericOption(scheduleArgs, "dayOfWeek");
|
|
299
|
+
const hour = parseNumericOption(scheduleArgs, "hour");
|
|
300
|
+
const minute = parseNumericOption(scheduleArgs, "minute") ?? 0;
|
|
301
|
+
if (dayOfWeek !== undefined && hour !== undefined) pattern = `${minute} ${hour} * * ${dayOfWeek}`;
|
|
302
|
+
}
|
|
303
|
+
if (method === "cron") pattern = parseStringLiteral(scheduleArgs);
|
|
304
|
+
if (!pattern) return null;
|
|
305
|
+
|
|
306
|
+
const procedureName = parseStringLiteral(actionSource) ?? internalNames.get(actionSource?.trim());
|
|
307
|
+
if (!procedureName) {
|
|
308
|
+
return { skipped: { name, reason: "inline or non-internal action is not supported in cloud cron manifest" } };
|
|
309
|
+
}
|
|
310
|
+
return { job: { name, pattern, procedureName } };
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
const entries = [];
|
|
314
|
+
let entryOrder = 0;
|
|
315
|
+
const addParsed = (index, parsed) => {
|
|
316
|
+
if (!parsed) return;
|
|
317
|
+
entries.push({ index, order: entryOrder, parsed });
|
|
318
|
+
entryOrder += 1;
|
|
319
|
+
};
|
|
320
|
+
|
|
289
321
|
const builderNames = new Set();
|
|
290
322
|
const builderRegex = /\bconst\s+([A-Za-z_$][\w$]*)\s*=\s*cronJobs\s*\(\s*\)\s*;?/g;
|
|
291
323
|
let builderMatch;
|
|
292
324
|
while ((builderMatch = builderRegex.exec(stripped))) {
|
|
293
325
|
builderNames.add(builderMatch[1]);
|
|
294
326
|
}
|
|
295
|
-
if (builderNames.size === 0) return { jobs: [], skipped: [], skippedInlineCount: 0 };
|
|
296
327
|
|
|
297
328
|
const callRegex = /\b([A-Za-z_$][\w$]*)\.(interval|daily|weekly|cron)\s*\(([\s\S]*?)\)\s*;?/g;
|
|
298
|
-
const jobs = [];
|
|
299
|
-
const skipped = [];
|
|
300
329
|
let match;
|
|
301
330
|
while ((match = callRegex.exec(stripped))) {
|
|
302
331
|
const builderName = match[1];
|
|
@@ -305,34 +334,54 @@ function parseDefineApiCronJobsSource(source) {
|
|
|
305
334
|
const method = match[2];
|
|
306
335
|
const args = splitTopLevelArgs(match[3] ?? "");
|
|
307
336
|
const name = parseStringLiteral(args[0]);
|
|
308
|
-
|
|
337
|
+
addParsed(
|
|
338
|
+
match.index,
|
|
339
|
+
parseJob({ name, method, scheduleArgs: args[1], actionSource: args[2]?.trim() }),
|
|
340
|
+
);
|
|
341
|
+
}
|
|
309
342
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
343
|
+
const chainRegex = /\b(?:export\s+)?const\s+[A-Za-z_$][\w$]*\s*=\s*cronJobs\s*\(\s*\)([\s\S]*?);/g;
|
|
344
|
+
while ((match = chainRegex.exec(stripped))) {
|
|
345
|
+
const chainSource = match[1] ?? "";
|
|
346
|
+
const chainCallRegex = /\.(interval|daily|weekly|cron)\s*\(([\s\S]*?)\)/g;
|
|
347
|
+
let chainMatch;
|
|
348
|
+
while ((chainMatch = chainCallRegex.exec(chainSource))) {
|
|
349
|
+
const args = splitTopLevelArgs(chainMatch[2] ?? "");
|
|
350
|
+
addParsed(
|
|
351
|
+
match.index + chainMatch.index,
|
|
352
|
+
parseJob({
|
|
353
|
+
name: parseStringLiteral(args[0]),
|
|
354
|
+
method: chainMatch[1],
|
|
355
|
+
scheduleArgs: args[1],
|
|
356
|
+
actionSource: args[2]?.trim(),
|
|
357
|
+
}),
|
|
358
|
+
);
|
|
322
359
|
}
|
|
323
|
-
|
|
324
|
-
if (!pattern) continue;
|
|
360
|
+
}
|
|
325
361
|
|
|
326
|
-
|
|
327
|
-
const
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
362
|
+
const declarativeRegex =
|
|
363
|
+
/\b(?:export\s+)?const\s+[A-Za-z_$][\w$]*\s*=\s*cron\s*\.\s*name\s*\(\s*(['"`])([^'"`]+)\1\s*\)\s*\.\s*(interval|daily|weekly|cron)\s*\(([\s\S]*?)\)\s*\.\s*handler\s*\(([\s\S]*?)\)\s*;?/g;
|
|
364
|
+
while ((match = declarativeRegex.exec(stripped))) {
|
|
365
|
+
addParsed(
|
|
366
|
+
match.index,
|
|
367
|
+
parseJob({
|
|
368
|
+
name: match[2],
|
|
369
|
+
method: match[3],
|
|
370
|
+
scheduleArgs: match[4],
|
|
371
|
+
actionSource: splitTopLevelArgs(match[5] ?? "")[0]?.trim(),
|
|
372
|
+
}),
|
|
373
|
+
);
|
|
334
374
|
}
|
|
335
375
|
|
|
376
|
+
const jobs = [];
|
|
377
|
+
const skipped = [];
|
|
378
|
+
entries
|
|
379
|
+
.sort((a, b) => a.index - b.index || a.order - b.order)
|
|
380
|
+
.forEach(({ parsed }) => {
|
|
381
|
+
if (parsed.job) jobs.push(parsed.job);
|
|
382
|
+
if (parsed.skipped) skipped.push(parsed.skipped);
|
|
383
|
+
});
|
|
384
|
+
|
|
336
385
|
return { jobs, skipped, skippedInlineCount: skipped.length };
|
|
337
386
|
}
|
|
338
387
|
|
|
@@ -501,6 +550,50 @@ async function collectDefineApiCronJobsWithShimFallback(params) {
|
|
|
501
550
|
}
|
|
502
551
|
}
|
|
503
552
|
|
|
553
|
+
function resolveLocalImportFile({ importerFile, specifier, existsSyncImpl, resolvePathImpl }) {
|
|
554
|
+
if (!specifier.startsWith(".")) return null;
|
|
555
|
+
const base = resolvePathImpl(dirname(importerFile), specifier);
|
|
556
|
+
const candidates = [
|
|
557
|
+
base,
|
|
558
|
+
`${base}.ts`,
|
|
559
|
+
`${base}.js`,
|
|
560
|
+
resolvePathImpl(base, "index.ts"),
|
|
561
|
+
resolvePathImpl(base, "index.js"),
|
|
562
|
+
];
|
|
563
|
+
return candidates.find((candidate) => existsSyncImpl(candidate)) ?? null;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
function collectLocalImportSourceText({
|
|
567
|
+
source,
|
|
568
|
+
file,
|
|
569
|
+
existsSyncImpl,
|
|
570
|
+
readFileSyncImpl,
|
|
571
|
+
resolvePathImpl,
|
|
572
|
+
}) {
|
|
573
|
+
const imports = [];
|
|
574
|
+
const fromRegex = /\bimport\s+[^'"]*?\s+from\s+["']([^"']+)["']/g;
|
|
575
|
+
const sideEffectRegex = /\bimport\s+["']([^"']+)["']/g;
|
|
576
|
+
for (const regex of [fromRegex, sideEffectRegex]) {
|
|
577
|
+
let match;
|
|
578
|
+
while ((match = regex.exec(source))) imports.push(match[1]);
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
const seen = new Set();
|
|
582
|
+
const chunks = [];
|
|
583
|
+
for (const specifier of imports) {
|
|
584
|
+
const importedFile = resolveLocalImportFile({
|
|
585
|
+
importerFile: file,
|
|
586
|
+
specifier,
|
|
587
|
+
existsSyncImpl,
|
|
588
|
+
resolvePathImpl,
|
|
589
|
+
});
|
|
590
|
+
if (!importedFile || seen.has(importedFile)) continue;
|
|
591
|
+
seen.add(importedFile);
|
|
592
|
+
chunks.push(readFileSyncImpl(importedFile, "utf8"));
|
|
593
|
+
}
|
|
594
|
+
return chunks.join("\n");
|
|
595
|
+
}
|
|
596
|
+
|
|
504
597
|
export async function writeCronManifestForProject({
|
|
505
598
|
cwd,
|
|
506
599
|
functionsDir = "gencow",
|
|
@@ -526,7 +619,14 @@ export async function writeCronManifestForProject({
|
|
|
526
619
|
|
|
527
620
|
if (!cronsFile && apiFile) {
|
|
528
621
|
const source = readFileSyncImpl(apiFile, "utf8");
|
|
529
|
-
const
|
|
622
|
+
const importedSource = collectLocalImportSourceText({
|
|
623
|
+
source,
|
|
624
|
+
file: apiFile,
|
|
625
|
+
existsSyncImpl,
|
|
626
|
+
readFileSyncImpl,
|
|
627
|
+
resolvePathImpl,
|
|
628
|
+
});
|
|
629
|
+
const staticParsed = parseDefineApiCronJobsSource(`${source}\n${importedSource}`);
|
|
530
630
|
let parsed = staticParsed;
|
|
531
631
|
if (shouldLoadDefineApiCrons(source)) {
|
|
532
632
|
try {
|
package/lib/db-command.mjs
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { execSync } from "child_process";
|
|
2
2
|
import { cpSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, unlinkSync } from "fs";
|
|
3
3
|
import { dirname, resolve } from "path";
|
|
4
|
+
import { runDrizzleGenerateWithFallback } from "./drizzle-generate.mjs";
|
|
4
5
|
import { BOLD, CYAN, DIM, GREEN, RED, RESET, error, info, log, success, warn } from "./output.mjs";
|
|
5
6
|
import { platformFetch, requireCreds } from "./platform-client.mjs";
|
|
7
|
+
import { createTarGzipArchive } from "./tar-archive.mjs";
|
|
6
8
|
|
|
7
9
|
export function resolveDbCloudTarget(args, gencowJson = null) {
|
|
8
10
|
const isProd = args.includes("--prod");
|
|
@@ -139,6 +141,7 @@ export function createDbCommands({
|
|
|
139
141
|
runInServer,
|
|
140
142
|
statSyncImpl = statSync,
|
|
141
143
|
successImpl = success,
|
|
144
|
+
tarCreateImpl,
|
|
142
145
|
unlinkSyncImpl = unlinkSync,
|
|
143
146
|
cpSyncImpl = cpSync,
|
|
144
147
|
dirnameImpl = dirname,
|
|
@@ -444,11 +447,13 @@ export function createDbCommands({
|
|
|
444
447
|
|
|
445
448
|
infoImpl("Generating schema migrations...");
|
|
446
449
|
try {
|
|
447
|
-
|
|
448
|
-
execSyncImpl(dk.cmd, {
|
|
450
|
+
runDrizzleGenerateWithFallback({
|
|
449
451
|
cwd: cwdImpl(),
|
|
450
|
-
|
|
452
|
+
drizzleKitCmdImpl,
|
|
453
|
+
env: processEnv,
|
|
454
|
+
execSyncImpl,
|
|
451
455
|
stdio: "inherit",
|
|
456
|
+
warnImpl,
|
|
452
457
|
});
|
|
453
458
|
successImpl("Migrations generated");
|
|
454
459
|
} catch (e) {
|
|
@@ -469,8 +474,11 @@ export function createDbCommands({
|
|
|
469
474
|
mkdirSyncImpl(dirnameImpl(tmpBundle), { recursive: true });
|
|
470
475
|
|
|
471
476
|
const tarSourceDir = functionsDir.startsWith("./") ? functionsDir.slice(2) : functionsDir;
|
|
472
|
-
|
|
473
|
-
|
|
477
|
+
await createTarGzipArchive({
|
|
478
|
+
cwd: cwdImpl(),
|
|
479
|
+
file: tmpBundle,
|
|
480
|
+
entries: [`${tarSourceDir}/`],
|
|
481
|
+
tarCreateImpl,
|
|
474
482
|
});
|
|
475
483
|
|
|
476
484
|
const bundleData = readFileSyncImpl(tmpBundle);
|
|
@@ -518,11 +526,13 @@ export function createDbCommands({
|
|
|
518
526
|
if (isMonorepo()) {
|
|
519
527
|
runInServer("pnpm db:generate", buildEnv(config));
|
|
520
528
|
} else {
|
|
521
|
-
|
|
522
|
-
execSyncImpl(dk.cmd, {
|
|
529
|
+
runDrizzleGenerateWithFallback({
|
|
523
530
|
cwd: cwdImpl(),
|
|
524
|
-
|
|
531
|
+
drizzleKitCmdImpl,
|
|
532
|
+
env: buildEnv(config),
|
|
533
|
+
execSyncImpl,
|
|
525
534
|
stdio: "inherit",
|
|
535
|
+
warnImpl,
|
|
526
536
|
});
|
|
527
537
|
}
|
|
528
538
|
successImpl("Migration files generated in gencow/migrations/");
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { execSync } from "child_process";
|
|
2
|
-
import { existsSync, mkdirSync, readFileSync, statSync, unlinkSync, writeFileSync } from "fs";
|
|
2
|
+
import { cpSync, existsSync, mkdirSync, readFileSync, rmSync, statSync, unlinkSync, writeFileSync } from "fs";
|
|
3
3
|
import { dirname, resolve } from "path";
|
|
4
4
|
import {
|
|
5
5
|
fetchAppDiagnosticLogsViaRest,
|
|
6
6
|
normalizeDiagnosticLogLines,
|
|
7
7
|
} from "./app-diagnostics.mjs";
|
|
8
8
|
import { CRON_MANIFEST_RELATIVE_PATH, writeCronManifestForProject } from "./cron-manifest.mjs";
|
|
9
|
+
import { getCommandErrorMessage, runDrizzleGenerateWithFallback } from "./drizzle-generate.mjs";
|
|
9
10
|
import { CYAN, DIM, RED, RESET, YELLOW, error, info, log, success, warn } from "./output.mjs";
|
|
10
11
|
import { platformFetch, rpcMutation } from "./platform-client.mjs";
|
|
12
|
+
import { createTarGzipArchive } from "./tar-archive.mjs";
|
|
11
13
|
|
|
12
14
|
export function writeProjectMetadata({
|
|
13
15
|
appId,
|
|
@@ -44,15 +46,18 @@ export function createDeployPackageRuntime({
|
|
|
44
46
|
loadConfigImpl,
|
|
45
47
|
logImpl = log,
|
|
46
48
|
mkdirSyncImpl = mkdirSync,
|
|
49
|
+
cpSyncImpl = cpSync,
|
|
47
50
|
platformFetchImpl = platformFetch,
|
|
48
51
|
processRef = process,
|
|
49
52
|
readFileSyncImpl = readFileSync,
|
|
50
53
|
resolvePathImpl = resolve,
|
|
54
|
+
rmSyncImpl = rmSync,
|
|
51
55
|
rpcMutationImpl = rpcMutation,
|
|
52
56
|
setIntervalImpl = setInterval,
|
|
53
57
|
setTimeoutImpl = setTimeout,
|
|
54
58
|
statSyncImpl = statSync,
|
|
55
59
|
successImpl = success,
|
|
60
|
+
tarCreateImpl,
|
|
56
61
|
unlinkSyncImpl = unlinkSync,
|
|
57
62
|
verifyAppReadyImpl,
|
|
58
63
|
warnImpl = warn,
|
|
@@ -106,16 +111,18 @@ export function createDeployPackageRuntime({
|
|
|
106
111
|
stdio: "inherit",
|
|
107
112
|
});
|
|
108
113
|
} else {
|
|
109
|
-
|
|
110
|
-
execGen(dk.cmd, {
|
|
114
|
+
runDrizzleGenerateWithFallback({
|
|
111
115
|
cwd,
|
|
112
|
-
|
|
116
|
+
drizzleKitCmdImpl,
|
|
117
|
+
env: genEnv ?? processRef.env,
|
|
118
|
+
execSyncImpl: execGen,
|
|
113
119
|
stdio: "inherit",
|
|
120
|
+
warnImpl,
|
|
114
121
|
});
|
|
115
122
|
}
|
|
116
123
|
successImpl("Schema -> migrations synced");
|
|
117
124
|
} catch (caught) {
|
|
118
|
-
const message = (caught
|
|
125
|
+
const message = getCommandErrorMessage(caught);
|
|
119
126
|
if (
|
|
120
127
|
message.includes("No schema changes") ||
|
|
121
128
|
message.includes("nothing to migrate") ||
|
|
@@ -280,16 +287,26 @@ export function createDeployPackageRuntime({
|
|
|
280
287
|
const src = resolvePathImpl(cwd, file);
|
|
281
288
|
const dst = resolvePathImpl(tmpDir, file);
|
|
282
289
|
if (file.endsWith("/")) {
|
|
283
|
-
|
|
290
|
+
cpSyncImpl(src, dst, { recursive: true });
|
|
284
291
|
} else if (existsSyncImpl(src)) {
|
|
285
292
|
mkdirSyncImpl(dirname(dst), { recursive: true });
|
|
286
|
-
|
|
293
|
+
cpSyncImpl(src, dst);
|
|
287
294
|
}
|
|
288
295
|
}
|
|
289
|
-
|
|
290
|
-
|
|
296
|
+
await createTarGzipArchive({
|
|
297
|
+
cwd: tmpDir,
|
|
298
|
+
file: tmpBundle,
|
|
299
|
+
entries: ["."],
|
|
300
|
+
tarCreateImpl,
|
|
301
|
+
});
|
|
302
|
+
rmSyncImpl(tmpDir, { recursive: true, force: true });
|
|
291
303
|
} else {
|
|
292
|
-
|
|
304
|
+
await createTarGzipArchive({
|
|
305
|
+
cwd,
|
|
306
|
+
file: tmpBundle,
|
|
307
|
+
entries: filesToPack,
|
|
308
|
+
tarCreateImpl,
|
|
309
|
+
});
|
|
293
310
|
}
|
|
294
311
|
} catch (caught) {
|
|
295
312
|
errorImpl(`Packaging failed: ${caught.message}`);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { execSync } from "child_process";
|
|
2
|
+
import { runDrizzleGenerateWithFallback } from "./drizzle-generate.mjs";
|
|
2
3
|
|
|
3
4
|
export function runDevCloudMigrationGenerate(config, deps) {
|
|
4
5
|
const cwd = deps.cwdImpl();
|
|
@@ -18,11 +19,13 @@ export function runDevCloudMigrationGenerate(config, deps) {
|
|
|
18
19
|
return "server";
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
|
|
22
|
-
execSyncImpl(dk.cmd, {
|
|
22
|
+
runDrizzleGenerateWithFallback({
|
|
23
23
|
cwd,
|
|
24
|
-
|
|
24
|
+
drizzleKitCmdImpl: deps.drizzleKitCmdImpl,
|
|
25
|
+
env: genEnv,
|
|
26
|
+
execSyncImpl,
|
|
25
27
|
stdio: "inherit",
|
|
28
|
+
warnImpl: deps.warnImpl,
|
|
26
29
|
});
|
|
27
30
|
return "local";
|
|
28
31
|
}
|