gencow 0.1.114 → 0.1.115

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 CHANGED
@@ -2451,13 +2451,9 @@ ${BOLD}Examples:${RESET}
2451
2451
  };
2452
2452
  const tsFiles = scanTsFiles(gencowDir);
2453
2453
  // query(, mutation(, httpAction( 호출이 하나라도 있으면 실질적 백엔드
2454
- // ⚠️ 주석 내 코드 예시를 오탐하지 않도록 주석을 먼저 제거
2455
2454
  const hasApiCalls = tsFiles.some(f => {
2456
2455
  const src = readFileSync(f, "utf8");
2457
- const stripped = src
2458
- .replace(/\/\/.*$/gm, "") // 한줄 주석 제거
2459
- .replace(/\/\*[\s\S]*?\*\//g, ""); // 블록 주석 제거
2460
- return /\b(query|mutation|httpAction)\s*\(/.test(stripped);
2456
+ return /\b(query|mutation|httpAction)\s*\(/.test(src);
2461
2457
  });
2462
2458
  if (!hasApiCalls) {
2463
2459
  isBackendEmpty = true;
@@ -2703,29 +2699,23 @@ ${BOLD}Examples:${RESET}
2703
2699
  warn("서버 시작 실패 원인:");
2704
2700
  for (const line of errData.crashLogs) log(` ${DIM}${line}${RESET}`);
2705
2701
  }
2706
- if (targetDir) {
2707
- // --static 모드: 백엔드 실패해도 프론트엔드 배포는 계속 진행
2708
- warn("백엔드 배포 실패 — 정적 파일 배포는 계속 진행합니다.");
2709
- log("");
2710
- } else {
2711
- error("백엔드 배포 실패로 프론트엔드 배포를 건너뜁니다.");
2712
- process.exit(1);
2713
- }
2714
- } else {
2715
- const backendData = await backendDeployRes.json();
2716
- const backendElapsed = ((Date.now() - backendStartTime) / 1000).toFixed(1);
2717
- success(`백엔드 빌드 완료! (${backendElapsed}s)`);
2702
+ error("백엔드 배포 실패로 프론트엔드 배포를 건너뜁니다.");
2703
+ process.exit(1);
2704
+ }
2718
2705
 
2719
- // Health check 백엔드 앱 URL로 실제 응답 확인
2720
- if (backendData.url) {
2721
- await this._verifyAppReady(backendData.url, appId);
2722
- }
2706
+ const backendData = await backendDeployRes.json();
2707
+ const backendElapsed = ((Date.now() - backendStartTime) / 1000).toFixed(1);
2708
+ success(`백엔드 빌드 완료! (${backendElapsed}s)`);
2723
2709
 
2724
- info(`URL: ${backendData.url}`);
2725
- info(`Hash: ${backendData.bundleHash}`);
2726
- updateEnvLocalUrl(backendData.url);
2727
- log("");
2710
+ // Health check — 백엔드 앱 URL로 실제 응답 확인
2711
+ if (backendData.url) {
2712
+ await this._verifyAppReady(backendData.url, appId);
2728
2713
  }
2714
+
2715
+ info(`URL: ${backendData.url}`);
2716
+ info(`Hash: ${backendData.bundleHash}`);
2717
+ updateEnvLocalUrl(backendData.url);
2718
+ log("");
2729
2719
  log(` ${BOLD}── 프론트엔드 배포 ──────────────────${RESET}\n`);
2730
2720
  }
2731
2721
 
package/core/index.js CHANGED
@@ -1402,7 +1402,6 @@ function query(key, handlerOrDef) {
1402
1402
  }
1403
1403
  var mutationCounter = 0;
1404
1404
  function mutation(nameOrInvalidatesOrDef, handlerOrDef, name) {
1405
- let invalidates;
1406
1405
  let argsSchema;
1407
1406
  let actualHandler;
1408
1407
  let mutName;
@@ -1410,16 +1409,13 @@ function mutation(nameOrInvalidatesOrDef, handlerOrDef, name) {
1410
1409
  if (typeof nameOrInvalidatesOrDef === "string") {
1411
1410
  mutName = nameOrInvalidatesOrDef;
1412
1411
  const def2 = handlerOrDef;
1413
- invalidates = def2.invalidates || [];
1414
1412
  actualHandler = def2.handler;
1415
1413
  argsSchema = def2.args;
1416
1414
  isPublic = def2.public === true;
1417
1415
  } else if (Array.isArray(nameOrInvalidatesOrDef)) {
1418
- invalidates = nameOrInvalidatesOrDef;
1419
1416
  actualHandler = handlerOrDef;
1420
1417
  mutName = name || `mutation_${++mutationCounter}`;
1421
1418
  } else {
1422
- invalidates = nameOrInvalidatesOrDef.invalidates;
1423
1419
  actualHandler = nameOrInvalidatesOrDef.handler;
1424
1420
  argsSchema = nameOrInvalidatesOrDef.args;
1425
1421
  isPublic = nameOrInvalidatesOrDef.public === true;
@@ -1432,7 +1428,6 @@ function mutation(nameOrInvalidatesOrDef, handlerOrDef, name) {
1432
1428
  }
1433
1429
  const def = {
1434
1430
  name: mutName,
1435
- invalidates,
1436
1431
  handler: actualHandler,
1437
1432
  argsSchema,
1438
1433
  isPublic
@@ -1477,8 +1472,11 @@ function deregisterClient(ws) {
1477
1472
  }
1478
1473
  function buildRealtimeCtx(options) {
1479
1474
  const pendingEmits = /* @__PURE__ */ new Map();
1475
+ const _pendingRefresh = [];
1476
+ let _hasEmitted = false;
1480
1477
  return {
1481
1478
  emit(queryKey, data) {
1479
+ _hasEmitted = true;
1482
1480
  const existing = pendingEmits.get(queryKey);
1483
1481
  if (existing) clearTimeout(existing.timer);
1484
1482
  const timer = setTimeout(() => {
@@ -1503,24 +1501,58 @@ function buildRealtimeCtx(options) {
1503
1501
  }
1504
1502
  }, 50);
1505
1503
  pendingEmits.set(queryKey, { data, timer });
1504
+ },
1505
+ refresh(queryKey) {
1506
+ _hasEmitted = true;
1507
+ if (!_pendingRefresh.includes(queryKey)) {
1508
+ _pendingRefresh.push(queryKey);
1509
+ }
1510
+ },
1511
+ get _hasEmitted() {
1512
+ return _hasEmitted;
1513
+ },
1514
+ get _pendingRefresh() {
1515
+ return [..._pendingRefresh];
1516
+ },
1517
+ async _flushRefresh() {
1518
+ if (_pendingRefresh.length === 0) return;
1519
+ const qMap = options?.queryMap ?? queryRegistry;
1520
+ for (const key of _pendingRefresh) {
1521
+ const queryDef = qMap.get(key);
1522
+ if (!queryDef) {
1523
+ console.warn(`[gencow] refresh("${key}"): query not found in registry. Skipping.`);
1524
+ continue;
1525
+ }
1526
+ try {
1527
+ const refreshCtx = options?.buildCtxForRefresh?.() ?? {};
1528
+ const result = await queryDef.handler(refreshCtx, {});
1529
+ if (options?.httpCallback) {
1530
+ options.httpCallback({ type: "emit", queryKey: key, data: result });
1531
+ } else {
1532
+ const clients = subscribers.get(key);
1533
+ if (clients && clients.size > 0) {
1534
+ const message = JSON.stringify({
1535
+ type: "query:updated",
1536
+ query: key,
1537
+ data: result
1538
+ });
1539
+ for (const ws of clients) {
1540
+ try {
1541
+ ws.send(message);
1542
+ } catch {
1543
+ clients.delete(ws);
1544
+ }
1545
+ }
1546
+ }
1547
+ }
1548
+ } catch (e) {
1549
+ console.warn(`[gencow] refresh("${key}") failed:`, e instanceof Error ? e.message : e);
1550
+ }
1551
+ }
1552
+ _pendingRefresh.length = 0;
1506
1553
  }
1507
1554
  };
1508
1555
  }
1509
- async function invalidateQueries(queryKeys, ctx, httpInvalidateCallback) {
1510
- if (queryKeys.length === 0) return;
1511
- if (httpInvalidateCallback) {
1512
- httpInvalidateCallback(queryKeys);
1513
- return;
1514
- }
1515
- const invalidateMsg = JSON.stringify({ type: "invalidate", queries: queryKeys });
1516
- for (const ws of connectedClients) {
1517
- try {
1518
- ws.send(invalidateMsg);
1519
- } catch {
1520
- connectedClients.delete(ws);
1521
- }
1522
- }
1523
- }
1524
1556
  function handleWsMessage(ws, raw) {
1525
1557
  try {
1526
1558
  const msg = typeof raw === "string" ? JSON.parse(raw) : JSON.parse(raw.toString());
@@ -2075,7 +2107,6 @@ function crud(table, options) {
2075
2107
  });
2076
2108
  const createDef = !enabledMethods.has("create") ? void 0 : mutation(`${prefix}.create`, {
2077
2109
  public: isPublic,
2078
- invalidates: [],
2079
2110
  handler: async (ctx, args) => {
2080
2111
  const user = isPublic ? null : ctx.auth.requireAuth();
2081
2112
  let insertData = { ...args };
@@ -2095,7 +2126,6 @@ function crud(table, options) {
2095
2126
  });
2096
2127
  const updateDef = !enabledMethods.has("update") ? void 0 : mutation(`${prefix}.update`, {
2097
2128
  public: isPublic,
2098
- invalidates: [],
2099
2129
  handler: async (ctx, args) => {
2100
2130
  if (!isPublic) ctx.auth.requireAuth();
2101
2131
  const { id, ...updates } = args;
@@ -2121,7 +2151,6 @@ function crud(table, options) {
2121
2151
  });
2122
2152
  const removeDef = !enabledMethods.has("remove") ? void 0 : mutation(`${prefix}.remove`, {
2123
2153
  public: isPublic,
2124
- invalidates: [],
2125
2154
  handler: async (ctx, args) => {
2126
2155
  if (!isPublic) ctx.auth.requireAuth();
2127
2156
  if (options?.softDelete) {
@@ -2164,7 +2193,6 @@ export {
2164
2193
  getSchedulerInfo,
2165
2194
  handleWsMessage,
2166
2195
  httpAction,
2167
- invalidateQueries,
2168
2196
  mutation,
2169
2197
  ownerRls,
2170
2198
  parseArgs,