gencow 0.1.103 → 0.1.105
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 +24 -8
- package/core/index.js +6 -1
- package/package.json +1 -1
- package/server/index.js +72 -31
- package/server/index.js.map +2 -2
package/bin/gencow.mjs
CHANGED
|
@@ -191,6 +191,20 @@ function isStandaloneProject() {
|
|
|
191
191
|
return existsSync(resolve(process.cwd(), "gencow.json"));
|
|
192
192
|
}
|
|
193
193
|
|
|
194
|
+
/**
|
|
195
|
+
* drizzle-kit CLI 명령어 빌드 — 로컬 바이너리 우선 사용.
|
|
196
|
+
* pnpm 모노레포에서 npx가 esbuild 네이티브 바이너리를 잘못 resolve하여
|
|
197
|
+
* "Host version X does not match binary version Y" 에러 방지.
|
|
198
|
+
* 로컬 ./node_modules/.bin/drizzle-kit이 있으면 직접 실행, 없으면 npx fallback.
|
|
199
|
+
*/
|
|
200
|
+
function _drizzleKitCmd(subcmd) {
|
|
201
|
+
const localBin = resolve(process.cwd(), "node_modules/.bin/drizzle-kit");
|
|
202
|
+
if (existsSync(localBin)) {
|
|
203
|
+
return `"${localBin}" ${subcmd}`;
|
|
204
|
+
}
|
|
205
|
+
return `npx drizzle-kit ${subcmd}`;
|
|
206
|
+
}
|
|
207
|
+
|
|
194
208
|
function findServerRoot() {
|
|
195
209
|
// 1st: monorepo structure (walk up from CWD to find packages/server)
|
|
196
210
|
let dir = process.cwd();
|
|
@@ -1251,7 +1265,7 @@ ${hasPrompt ? `
|
|
|
1251
1265
|
});
|
|
1252
1266
|
} else {
|
|
1253
1267
|
// Standalone: use npx drizzle-kit directly
|
|
1254
|
-
execSync("
|
|
1268
|
+
execSync(_drizzleKitCmd("generate"), {
|
|
1255
1269
|
cwd: process.cwd(),
|
|
1256
1270
|
env: genEnv,
|
|
1257
1271
|
stdio: "inherit", // 프롬프트 패스스루 (rename 등)
|
|
@@ -1435,7 +1449,7 @@ ${hasPrompt ? `
|
|
|
1435
1449
|
// 🆕 로컬 drizzle-kit generate 실행 (프롬프트 패스스루)
|
|
1436
1450
|
info("스키마 마이그레이션 생성 중...");
|
|
1437
1451
|
try {
|
|
1438
|
-
execSync("
|
|
1452
|
+
execSync(_drizzleKitCmd("generate"), {
|
|
1439
1453
|
cwd: process.cwd(),
|
|
1440
1454
|
stdio: "inherit",
|
|
1441
1455
|
});
|
|
@@ -1503,7 +1517,7 @@ ${hasPrompt ? `
|
|
|
1503
1517
|
runInServer("pnpm db:generate", buildEnv(config));
|
|
1504
1518
|
} else {
|
|
1505
1519
|
// Standalone: npx drizzle-kit generate (프롬프트 패스스루)
|
|
1506
|
-
execSync("
|
|
1520
|
+
execSync(_drizzleKitCmd("generate"), {
|
|
1507
1521
|
cwd: process.cwd(),
|
|
1508
1522
|
env: buildEnv(config),
|
|
1509
1523
|
stdio: "inherit",
|
|
@@ -1983,7 +1997,7 @@ ${BOLD}Examples:${RESET}
|
|
|
1983
1997
|
});
|
|
1984
1998
|
} else {
|
|
1985
1999
|
// Standalone: npx drizzle-kit generate (프롬프트 패스스루 — rename 감지 대화형 포함)
|
|
1986
|
-
execGen("
|
|
2000
|
+
execGen(_drizzleKitCmd("generate"), {
|
|
1987
2001
|
cwd: process.cwd(),
|
|
1988
2002
|
stdio: "inherit",
|
|
1989
2003
|
});
|
|
@@ -2148,6 +2162,7 @@ ${BOLD}Examples:${RESET}
|
|
|
2148
2162
|
method: "POST",
|
|
2149
2163
|
headers: {
|
|
2150
2164
|
"Content-Type": "application/octet-stream",
|
|
2165
|
+
"X-Deploy-Local-Dir": process.cwd(),
|
|
2151
2166
|
},
|
|
2152
2167
|
body: bundleBuffer,
|
|
2153
2168
|
});
|
|
@@ -2202,6 +2217,7 @@ ${BOLD}Examples:${RESET}
|
|
|
2202
2217
|
method: "POST",
|
|
2203
2218
|
headers: {
|
|
2204
2219
|
"Content-Type": "application/octet-stream",
|
|
2220
|
+
"X-Deploy-Local-Dir": process.cwd(),
|
|
2205
2221
|
},
|
|
2206
2222
|
body: retryBuffer,
|
|
2207
2223
|
});
|
|
@@ -2491,7 +2507,7 @@ ${BOLD}Examples:${RESET}
|
|
|
2491
2507
|
if (existsSync(schemaPath)) {
|
|
2492
2508
|
info("스키마 마이그레이션 생성 중...");
|
|
2493
2509
|
try {
|
|
2494
|
-
execSync("
|
|
2510
|
+
execSync(_drizzleKitCmd("generate"), {
|
|
2495
2511
|
cwd: backendRoot,
|
|
2496
2512
|
stdio: "inherit", // ← 프롬프트 패스스루!
|
|
2497
2513
|
});
|
|
@@ -2610,7 +2626,7 @@ ${BOLD}Examples:${RESET}
|
|
|
2610
2626
|
|
|
2611
2627
|
const backendDeployRes = await platformFetch(creds, `/platform/apps/${appId}/deploy?env=${envTarget}`, {
|
|
2612
2628
|
method: "POST",
|
|
2613
|
-
headers: { "Content-Type": "application/octet-stream" },
|
|
2629
|
+
headers: { "Content-Type": "application/octet-stream", "X-Deploy-Local-Dir": process.cwd() },
|
|
2614
2630
|
body: backendBuffer,
|
|
2615
2631
|
});
|
|
2616
2632
|
|
|
@@ -2693,7 +2709,7 @@ ${BOLD}Examples:${RESET}
|
|
|
2693
2709
|
|
|
2694
2710
|
const deployRes = await platformFetch(creds, `/platform/apps/${appId}/deploy-static`, {
|
|
2695
2711
|
method: "POST",
|
|
2696
|
-
headers: { "Content-Type": "application/octet-stream" },
|
|
2712
|
+
headers: { "Content-Type": "application/octet-stream", "X-Deploy-Local-Dir": process.cwd() },
|
|
2697
2713
|
body: bundleBuffer,
|
|
2698
2714
|
});
|
|
2699
2715
|
|
|
@@ -3955,7 +3971,7 @@ process.exit(0);
|
|
|
3955
3971
|
}
|
|
3956
3972
|
try {
|
|
3957
3973
|
const { execSync } = await import("child_process");
|
|
3958
|
-
execSync("
|
|
3974
|
+
execSync(_drizzleKitCmd("generate"), {
|
|
3959
3975
|
cwd: process.cwd(),
|
|
3960
3976
|
stdio: "inherit",
|
|
3961
3977
|
});
|
package/core/index.js
CHANGED
|
@@ -1665,7 +1665,12 @@ function createScheduler() {
|
|
|
1665
1665
|
actions.set(name, handler);
|
|
1666
1666
|
},
|
|
1667
1667
|
async executeAction(name, args) {
|
|
1668
|
-
|
|
1668
|
+
try {
|
|
1669
|
+
await executeAction(name, args);
|
|
1670
|
+
} catch (error) {
|
|
1671
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
1672
|
+
console.error(`[scheduler] executeAction("${name}") failed: ${msg}`);
|
|
1673
|
+
}
|
|
1669
1674
|
}
|
|
1670
1675
|
};
|
|
1671
1676
|
}
|
package/package.json
CHANGED
package/server/index.js
CHANGED
|
@@ -1673,7 +1673,12 @@ function createScheduler() {
|
|
|
1673
1673
|
actions.set(name21, handler);
|
|
1674
1674
|
},
|
|
1675
1675
|
async executeAction(name21, args) {
|
|
1676
|
-
|
|
1676
|
+
try {
|
|
1677
|
+
await executeAction(name21, args);
|
|
1678
|
+
} catch (error95) {
|
|
1679
|
+
const msg = error95 instanceof Error ? error95.message : String(error95);
|
|
1680
|
+
console.error(`[scheduler] executeAction("${name21}") failed: ${msg}`);
|
|
1681
|
+
}
|
|
1677
1682
|
}
|
|
1678
1683
|
};
|
|
1679
1684
|
}
|
|
@@ -82259,10 +82264,6 @@ if (IS_BAAS) {
|
|
|
82259
82264
|
const contentLength = c.res.headers.get("content-length");
|
|
82260
82265
|
if (contentLength) {
|
|
82261
82266
|
runtimeBuffer.add("bandwidth_bytes", parseInt(contentLength, 10));
|
|
82262
|
-
} else if (c.res.body) {
|
|
82263
|
-
const cloned = c.res.clone();
|
|
82264
|
-
const buf = await cloned.arrayBuffer();
|
|
82265
|
-
runtimeBuffer.add("bandwidth_bytes", buf.byteLength);
|
|
82266
82267
|
}
|
|
82267
82268
|
} catch {
|
|
82268
82269
|
}
|
|
@@ -82637,25 +82638,19 @@ async function main() {
|
|
|
82637
82638
|
// @ts-ignore
|
|
82638
82639
|
duplex: "half"
|
|
82639
82640
|
});
|
|
82641
|
+
const buf = await proxyRes.arrayBuffer();
|
|
82640
82642
|
if (appName) {
|
|
82641
|
-
|
|
82642
|
-
const cl = proxyRes.headers.get("content-length");
|
|
82643
|
-
if (cl) {
|
|
82644
|
-
addProxyMetric(appName, parseInt(cl, 10));
|
|
82645
|
-
} else if (proxyRes.body) {
|
|
82646
|
-
const buf = await proxyRes.arrayBuffer();
|
|
82647
|
-
addProxyMetric(appName, buf.byteLength);
|
|
82648
|
-
return new Response(buf, {
|
|
82649
|
-
status: proxyRes.status,
|
|
82650
|
-
headers: proxyRes.headers
|
|
82651
|
-
});
|
|
82652
|
-
}
|
|
82653
|
-
} catch {
|
|
82654
|
-
}
|
|
82643
|
+
addProxyMetric(appName, buf.byteLength);
|
|
82655
82644
|
}
|
|
82656
|
-
|
|
82645
|
+
const resHeaders = new Headers(proxyRes.headers);
|
|
82646
|
+
if (buf.byteLength > 0) {
|
|
82647
|
+
resHeaders.set("content-length", String(buf.byteLength));
|
|
82648
|
+
} else {
|
|
82649
|
+
resHeaders.delete("content-length");
|
|
82650
|
+
}
|
|
82651
|
+
return new Response(buf, {
|
|
82657
82652
|
status: proxyRes.status,
|
|
82658
|
-
headers:
|
|
82653
|
+
headers: resHeaders
|
|
82659
82654
|
});
|
|
82660
82655
|
}
|
|
82661
82656
|
async function serveStaticFile(c, dataDir, pathname, appName) {
|
|
@@ -82771,6 +82766,22 @@ async function main() {
|
|
|
82771
82766
|
if (sleepingApps.length > 0) {
|
|
82772
82767
|
console.log(`[platform] Registered ${sleepingApps.length} sleeping app(s) into memory for future wake \u2713`);
|
|
82773
82768
|
}
|
|
82769
|
+
const allAppsForCronSync = [...runningApps, ...sleepingApps];
|
|
82770
|
+
if (allAppsForCronSync.length > 0) {
|
|
82771
|
+
const { syncCronJobs: _syncCronJobs } = await import(resolve5(functionsPath, "../src/provisioner.ts"));
|
|
82772
|
+
let cronSyncCount = 0;
|
|
82773
|
+
for (const row of allAppsForCronSync) {
|
|
82774
|
+
try {
|
|
82775
|
+
await _syncCronJobs(row.id, row.name, row.dataDir);
|
|
82776
|
+
cronSyncCount++;
|
|
82777
|
+
} catch (e) {
|
|
82778
|
+
console.warn(`[cron] ${row.name}: boot-time cron sync failed:`, e.message);
|
|
82779
|
+
}
|
|
82780
|
+
}
|
|
82781
|
+
if (cronSyncCount > 0) {
|
|
82782
|
+
console.log(`[platform] Boot-time cron sync complete for ${cronSyncCount}/${allAppsForCronSync.length} app(s) \u2713`);
|
|
82783
|
+
}
|
|
82784
|
+
}
|
|
82774
82785
|
} catch (e) {
|
|
82775
82786
|
const msg = e instanceof Error ? e.message : String(e);
|
|
82776
82787
|
console.error("[platform] Auto-recovery failed:", msg);
|
|
@@ -82808,15 +82819,27 @@ async function main() {
|
|
|
82808
82819
|
try {
|
|
82809
82820
|
const { canSleep: _canSleep, sleepApp: _sleepApp, getLastAccess: _getLastAccess, getAppMeta: _getAppMeta } = await import(resolve5(functionsPath, "../src/provisioner.ts"));
|
|
82810
82821
|
const metaMap = _getAppMeta();
|
|
82822
|
+
const allApps = await db.select({ name: appsTable.name, status: appsTable.status }).from(appsTable);
|
|
82823
|
+
const dbStatusMap = new Map(allApps.map((a) => [a.name, a.status]));
|
|
82811
82824
|
for (const [appName] of metaMap) {
|
|
82812
82825
|
if (sleepCount >= MAX_SLEEP_PER_CYCLE) break;
|
|
82826
|
+
const dbStatus = dbStatusMap.get(appName);
|
|
82827
|
+
if (dbStatus === "sleeping") {
|
|
82828
|
+
console.log(`[sleep] SWEEP: ${appName} is "sleeping" in DB but active in memory \u2014 enforcing sleep...`);
|
|
82829
|
+
try {
|
|
82830
|
+
await _sleepApp(appName);
|
|
82831
|
+
sleepCount++;
|
|
82832
|
+
} catch (e) {
|
|
82833
|
+
console.error(`[sleep] Failed to sweep ghost app ${appName}:`, e);
|
|
82834
|
+
}
|
|
82835
|
+
continue;
|
|
82836
|
+
}
|
|
82813
82837
|
const last = _getLastAccess(appName) || 0;
|
|
82814
82838
|
const canSlp = _canSleep(appName);
|
|
82815
82839
|
if (!canSlp) continue;
|
|
82816
82840
|
const idleMin = Math.floor((Date.now() - last) / 6e4);
|
|
82817
82841
|
try {
|
|
82818
82842
|
await _sleepApp(appName);
|
|
82819
|
-
await db.update(appsTable).set({ status: "sleeping" }).where(eqOp(appsTable.name, appName));
|
|
82820
82843
|
console.log(`[sleep] ${appName}: idle ${idleMin}m \u2014 sleeping \u2713`);
|
|
82821
82844
|
sleepCount++;
|
|
82822
82845
|
} catch (e) {
|
|
@@ -82909,6 +82932,8 @@ async function main() {
|
|
|
82909
82932
|
console.error(`[cron] ${label}: failed (${elapsed}s) \u2014 ${errMsg}`);
|
|
82910
82933
|
}
|
|
82911
82934
|
}
|
|
82935
|
+
let _cronEmptyCount = 0;
|
|
82936
|
+
const CRON_EMPTY_LOG_INTERVAL = 5;
|
|
82912
82937
|
setInterval(async () => {
|
|
82913
82938
|
try {
|
|
82914
82939
|
const dueJobs = await rawSql(
|
|
@@ -82921,15 +82946,31 @@ async function main() {
|
|
|
82921
82946
|
ORDER BY cj.next_run_at ASC
|
|
82922
82947
|
LIMIT 50`
|
|
82923
82948
|
);
|
|
82924
|
-
if (dueJobs.length === 0)
|
|
82925
|
-
|
|
82926
|
-
|
|
82927
|
-
|
|
82928
|
-
await Promise.allSettled(batch.map((job) => executeCronJob(job)));
|
|
82929
|
-
if (i + CRON_CONCURRENCY < dueJobs.length) {
|
|
82930
|
-
await new Promise((r) => setTimeout(r, 1e3));
|
|
82949
|
+
if (dueJobs.length === 0) {
|
|
82950
|
+
_cronEmptyCount++;
|
|
82951
|
+
if (_cronEmptyCount % CRON_EMPTY_LOG_INTERVAL === 0) {
|
|
82952
|
+
console.log(`[cron] No due jobs (${_cronEmptyCount} consecutive empty polls)`);
|
|
82931
82953
|
}
|
|
82954
|
+
return;
|
|
82932
82955
|
}
|
|
82956
|
+
_cronEmptyCount = 0;
|
|
82957
|
+
console.log(`[cron] ${dueJobs.length} due cron job(s) found`);
|
|
82958
|
+
for (const job of dueJobs) {
|
|
82959
|
+
const nextRun = cronCalculateNextRun2(job.schedule);
|
|
82960
|
+
await rawSql(
|
|
82961
|
+
`UPDATE cron_jobs SET next_run_at = $1 WHERE id = $2`,
|
|
82962
|
+
[nextRun, job.id]
|
|
82963
|
+
);
|
|
82964
|
+
}
|
|
82965
|
+
void (async () => {
|
|
82966
|
+
for (let i = 0; i < dueJobs.length; i += CRON_CONCURRENCY) {
|
|
82967
|
+
const batch = dueJobs.slice(i, i + CRON_CONCURRENCY);
|
|
82968
|
+
await Promise.allSettled(batch.map((job) => executeCronJob(job)));
|
|
82969
|
+
if (i + CRON_CONCURRENCY < dueJobs.length) {
|
|
82970
|
+
await new Promise((r) => setTimeout(r, 1e3));
|
|
82971
|
+
}
|
|
82972
|
+
}
|
|
82973
|
+
})();
|
|
82933
82974
|
} catch (err) {
|
|
82934
82975
|
console.error(`[cron] Scheduler error:`, err.message);
|
|
82935
82976
|
}
|
|
@@ -83253,7 +83294,7 @@ async function main() {
|
|
|
83253
83294
|
if (!IS_BAAS && Array.isArray(result) && result.length >= 2) {
|
|
83254
83295
|
checkMixedUserIds(name21, result);
|
|
83255
83296
|
}
|
|
83256
|
-
return c.json(result);
|
|
83297
|
+
return c.json(result ?? null);
|
|
83257
83298
|
} catch (err) {
|
|
83258
83299
|
const status = err?.code === "FUNCTION_TIMEOUT" ? 408 : err instanceof GencowValidationError ? 400 : 500;
|
|
83259
83300
|
return c.json({ error: err.message, ...err?.code ? { code: err.code } : {} }, status);
|
|
@@ -83288,7 +83329,7 @@ async function main() {
|
|
|
83288
83329
|
name21
|
|
83289
83330
|
);
|
|
83290
83331
|
await invalidateQueries(mut.invalidates, ctx);
|
|
83291
|
-
return c.json(result, 201);
|
|
83332
|
+
return c.json(result ?? null, 201);
|
|
83292
83333
|
} catch (err) {
|
|
83293
83334
|
const status = err?.code === "FUNCTION_TIMEOUT" ? 408 : err instanceof GencowValidationError ? 400 : 500;
|
|
83294
83335
|
return c.json({ error: err.message, ...err?.code ? { code: err.code } : {} }, status);
|