sonamu 0.11.4 → 0.11.6

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.
@@ -5,8 +5,8 @@
5
5
  <link rel="icon" type="image/svg+xml" href="/sonamu-ui/setting.svg" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <title>{{projectName}}: Sonamu UI</title>
8
- <script type="module" crossorigin src="/sonamu-ui/assets/index-DV1AAq6W.js"></script>
9
- <link rel="stylesheet" crossorigin href="/sonamu-ui/assets/index-D3QNH-Dv.css">
8
+ <script type="module" crossorigin src="/sonamu-ui/assets/index-C_RUxOdN.js"></script>
9
+ <link rel="stylesheet" crossorigin href="/sonamu-ui/assets/index-5i4uZVES.css">
10
10
  </head>
11
11
  <body>
12
12
  <div id="root"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sonamu",
3
- "version": "0.11.4",
3
+ "version": "0.11.6",
4
4
  "description": "Sonamu — TypeScript Fullstack API Framework",
5
5
  "keywords": [
6
6
  "framework",
@@ -1,15 +1,30 @@
1
- import { access, mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
1
+ import { createHash } from "node:crypto";
2
+ import { access, mkdir, mkdtemp, readFile, rm, unlink, writeFile } from "node:fs/promises";
2
3
  import os from "node:os";
3
4
  import path from "node:path";
4
5
 
5
- import { afterEach, describe, expect, it, vi } from "vitest";
6
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
6
7
 
7
8
  import { type SonamuConfig } from "../../api/config";
8
9
  import { Sonamu } from "../../api/sonamu";
10
+ import { Template__entry_server } from "../../template/implementations/entry-server.template";
11
+ import { Template__http_validators } from "../../template/implementations/http_validators.template";
12
+ import { type Template } from "../../template/template";
13
+ import { TemplateManager } from "../../template/template-manager";
9
14
  import { type AbsolutePath } from "../../utils/path-utils";
10
- import { Syncer, type SyncerDependencies } from "../syncer";
15
+ import { findChangedFilesUsingChecksums } from "../checksum";
16
+ import { Syncer } from "../syncer";
11
17
  import { actionGenerateHttpValidators } from "../syncer-actions";
12
18
 
19
+ type ZodCompilerPolicy = NonNullable<SonamuConfig["validation"]>["zodCompiler"];
20
+
21
+ type TestProject = {
22
+ apiRootPath: AbsolutePath;
23
+ lockPath: string;
24
+ registryPath: AbsolutePath;
25
+ syncer: Syncer;
26
+ };
27
+
13
28
  function toAbsolutePath(filePath: string): AbsolutePath {
14
29
  if (!path.isAbsolute(filePath)) {
15
30
  throw new Error(`절대 경로가 필요합니다: ${filePath}`);
@@ -18,24 +33,14 @@ function toAbsolutePath(filePath: string): AbsolutePath {
18
33
  return filePath as AbsolutePath;
19
34
  }
20
35
 
21
- function createDependencies() {
22
- return {
23
- actionCopySharedToTargetsIfNotExists: vi
24
- .fn<SyncerDependencies["actionCopySharedToTargetsIfNotExists"]>()
25
- .mockResolvedValue(undefined),
26
- actionGenerateHttpValidators: vi.fn<SyncerDependencies["actionGenerateHttpValidators"]>(),
27
- actionGenerateSsrEntryServerIfNotExists: vi
28
- .fn<SyncerDependencies["actionGenerateSsrEntryServerIfNotExists"]>()
29
- .mockResolvedValue([]),
30
- findChangedFilesUsingChecksums: vi.fn<SyncerDependencies["findChangedFilesUsingChecksums"]>(),
31
- renewChecksums: vi.fn<SyncerDependencies["renewChecksums"]>().mockResolvedValue(undefined),
32
- } satisfies SyncerDependencies;
36
+ function sha1(content: string): string {
37
+ return createHash("sha1").update(content).digest("hex");
33
38
  }
34
39
 
35
40
  const testConfig = {
36
41
  api: { dir: "api", route: { prefix: "/api" } },
37
42
  i18n: { defaultLocale: "ko", supportedLocales: ["ko"] },
38
- sync: { targets: [] },
43
+ sync: { targets: ["web"] },
39
44
  database: {},
40
45
  server: {
41
46
  apiConfig: {
@@ -47,91 +52,120 @@ const testConfig = {
47
52
 
48
53
  describe("HTTP validator registry sync 정리", () => {
49
54
  const tempRoots: string[] = [];
55
+ let sonamuSnapshot: ReturnType<typeof Sonamu.captureTestingSnapshot>;
56
+ let registeredTemplates: Template[];
57
+ let templateManagerWasAutoloaded: boolean;
58
+
59
+ beforeEach(() => {
60
+ sonamuSnapshot = Sonamu.captureTestingSnapshot();
61
+ registeredTemplates = TemplateManager.getAllKeys().map((key) => TemplateManager.get(key));
62
+ templateManagerWasAutoloaded = TemplateManager.isAutoloaded;
63
+
64
+ TemplateManager.reset();
65
+ TemplateManager.registerAll([new Template__http_validators(), new Template__entry_server()]);
66
+ });
50
67
 
51
68
  afterEach(async () => {
52
69
  vi.restoreAllMocks();
70
+ Sonamu.restoreTestingSnapshot(sonamuSnapshot);
71
+ TemplateManager.reset();
72
+ TemplateManager.registerAll(registeredTemplates);
73
+ TemplateManager.isAutoloaded = templateManagerWasAutoloaded;
53
74
  await Promise.all(
54
75
  tempRoots.splice(0).map((rootPath) => rm(rootPath, { recursive: true, force: true })),
55
76
  );
56
77
  });
57
78
 
79
+ async function createTestProject(zodCompiler: ZodCompilerPolicy): Promise<TestProject> {
80
+ const appRootPath = await mkdtemp(path.join(os.tmpdir(), "sonamu-registry-sync-test-"));
81
+ tempRoots.push(appRootPath);
82
+ const apiRootPath = toAbsolutePath(path.join(appRootPath, "api"));
83
+ const registryPath = toAbsolutePath(
84
+ path.join(apiRootPath, "src/application/sonamu.validators.generated.ts"),
85
+ );
86
+ await Promise.all([
87
+ mkdir(path.dirname(registryPath), { recursive: true }),
88
+ mkdir(path.join(appRootPath, "web/src/services"), { recursive: true }),
89
+ ]);
90
+
91
+ const syncer = new Syncer();
92
+ Sonamu.apiRootPath = apiRootPath;
93
+ Sonamu.config = {
94
+ ...testConfig,
95
+ validation: { zodCompiler },
96
+ };
97
+ Sonamu.syncer = syncer;
98
+
99
+ return {
100
+ apiRootPath,
101
+ lockPath: path.join(apiRootPath, "sonamu.lock"),
102
+ registryPath,
103
+ syncer,
104
+ };
105
+ }
106
+
107
+ async function readLock(lockPath: string): Promise<Array<{ path: string; checksum: string }>> {
108
+ // SAFETY: renewChecksums가 쓰는 sonamu.lock 직렬화 계약을 검증하는 테스트다.
109
+ return JSON.parse(await readFile(lockPath, "utf-8")) as Array<{
110
+ path: string;
111
+ checksum: string;
112
+ }>;
113
+ }
114
+
58
115
  it.each([
59
116
  { label: "opt-out", value: false as const },
60
117
  { label: "JIT", value: { api: "jit" as const } },
61
- ])("$label 정책은 남아 있는 generated registry를 제거한다", async ({ value }) => {
62
- const apiRootPath = await mkdtemp(path.join(os.tmpdir(), "sonamu-registry-sync-test-"));
63
- tempRoots.push(apiRootPath);
64
- const registryPath = path.join(apiRootPath, "src/application/sonamu.validators.generated.ts");
65
- await mkdir(path.dirname(registryPath), { recursive: true });
118
+ ])("$label 정책은 실제 generated registry를 제거한다", async ({ value }) => {
119
+ const { registryPath } = await createTestProject(value);
66
120
  await writeFile(registryPath, "export const validators = new Map();\n");
67
- Sonamu.apiRootPath = toAbsolutePath(apiRootPath);
68
- Sonamu.config = {
69
- ...testConfig,
70
- api: { ...testConfig.api, dir: "." },
71
- validation: { zodCompiler: value },
72
- };
73
121
 
74
122
  await actionGenerateHttpValidators();
75
123
 
76
124
  await expect(access(registryPath)).rejects.toMatchObject({ code: "ENOENT" });
77
125
  });
78
126
 
79
- it("registry-only drift를 경고로 넘기지 않고 즉시 재생성한다", async () => {
80
- const appRootPath = await mkdtemp(path.join(os.tmpdir(), "sonamu-registry-drift-test-"));
81
- tempRoots.push(appRootPath);
82
- const apiRootPath = path.join(appRootPath, "api");
83
- const registryPath = toAbsolutePath(
84
- path.join(apiRootPath, "src/application/sonamu.validators.generated.ts"),
85
- );
86
- await mkdir(path.dirname(registryPath), { recursive: true });
127
+ it("registry-only drift를 정본으로 복구하고 갱신된 checksum lock을 남긴다", async () => {
128
+ const { lockPath, registryPath, syncer } = await createTestProject({ api: "aot" });
129
+ await actionGenerateHttpValidators();
130
+ const canonicalRegistry = await readFile(registryPath, "utf-8");
131
+ await syncer.renewChecksums();
87
132
  await writeFile(registryPath, "// drifted registry\n");
88
- Sonamu.apiRootPath = toAbsolutePath(apiRootPath);
89
- Sonamu.config = {
90
- ...testConfig,
91
- validation: { zodCompiler: { api: "aot" } },
92
- };
93
- const dependencies = createDependencies();
94
- dependencies.actionGenerateHttpValidators.mockResolvedValue(registryPath);
95
- dependencies.findChangedFilesUsingChecksums.mockResolvedValue([registryPath]);
96
- vi.spyOn(console, "warn").mockImplementation(() => {});
133
+ const warn = vi.spyOn(console, "warn").mockImplementation(() => undefined);
97
134
 
98
- await new Syncer(dependencies).sync();
135
+ await syncer.sync();
99
136
 
100
- expect(dependencies.actionGenerateHttpValidators).toHaveBeenCalledTimes(1);
101
- expect(dependencies.renewChecksums).toHaveBeenCalledTimes(1);
102
- });
103
-
104
- it("삭제된 registry가 checksum 변경 목록에서 빠져도 다른 tracked 변경 처리 중 복구한다", async () => {
105
- const appRootPath = await mkdtemp(path.join(os.tmpdir(), "sonamu-missing-registry-test-"));
106
- tempRoots.push(appRootPath);
107
- const apiRootPath = path.join(appRootPath, "api");
108
- const registryPath = toAbsolutePath(
109
- path.join(apiRootPath, "src/application/sonamu.validators.generated.ts"),
110
- );
111
- const unrelatedPath = toAbsolutePath(
112
- path.join(apiRootPath, "src/application/queries.generated.ts"),
137
+ expect(await readFile(registryPath, "utf-8")).toBe(canonicalRegistry);
138
+ const registryLock = (await readLock(lockPath)).find((entry) =>
139
+ entry.path.endsWith("sonamu.validators.generated.ts"),
113
140
  );
114
- await mkdir(path.dirname(unrelatedPath), { recursive: true });
115
- await writeFile(unrelatedPath, "// unrelated tracked change\n");
116
- Sonamu.apiRootPath = toAbsolutePath(apiRootPath);
117
- Sonamu.config = {
118
- ...testConfig,
119
- validation: { zodCompiler: { api: "aot" } },
120
- };
121
- const order: string[] = [];
122
- const dependencies = createDependencies();
123
- dependencies.actionGenerateHttpValidators.mockImplementation(async () => {
124
- order.push("registry");
125
- return registryPath;
141
+ expect(registryLock).toEqual({
142
+ path: "api/src/application/sonamu.validators.generated.ts",
143
+ checksum: sha1(canonicalRegistry),
126
144
  });
127
- dependencies.findChangedFilesUsingChecksums.mockResolvedValue([unrelatedPath]);
128
- dependencies.renewChecksums.mockImplementation(async () => {
129
- order.push("checksum");
130
- });
131
- vi.spyOn(console, "warn").mockImplementation(() => {});
132
-
133
- await new Syncer(dependencies).sync();
145
+ await expect(findChangedFilesUsingChecksums()).resolves.toEqual([]);
146
+ expect(warn).not.toHaveBeenCalled();
147
+ });
134
148
 
135
- expect(order).toEqual(["registry", "checksum"]);
149
+ it("삭제된 registry는 다른 tracked 변경을 처리할 때 lock 갱신 전에 복구한다", async () => {
150
+ const { apiRootPath, lockPath, registryPath, syncer } = await createTestProject({ api: "aot" });
151
+ const otherGeneratedPath = path.join(apiRootPath, "src/application/queries.generated.ts");
152
+ await actionGenerateHttpValidators();
153
+ const canonicalRegistry = await readFile(registryPath, "utf-8");
154
+ await writeFile(otherGeneratedPath, "// original generated output\n");
155
+ await syncer.renewChecksums();
156
+ await unlink(registryPath);
157
+ await writeFile(otherGeneratedPath, "// drifted generated output\n");
158
+ const warn = vi.spyOn(console, "warn").mockImplementation(() => undefined);
159
+
160
+ await syncer.sync();
161
+
162
+ expect(await readFile(registryPath, "utf-8")).toBe(canonicalRegistry);
163
+ const lock = await readLock(lockPath);
164
+ expect(lock).toContainEqual({
165
+ path: "api/src/application/sonamu.validators.generated.ts",
166
+ checksum: sha1(canonicalRegistry),
167
+ });
168
+ await expect(findChangedFilesUsingChecksums()).resolves.toEqual([]);
169
+ expect(warn.mock.calls.flat().join(" ")).toContain("queries.generated.ts");
136
170
  });
137
171
  });
@@ -37,22 +37,6 @@ import * as SyncerActions from "./syncer-actions";
37
37
  type DiffGroupKey = FileType | "unknown";
38
38
  type DiffGroups = Partial<Record<DiffGroupKey, AbsolutePath[]>>;
39
39
 
40
- export interface SyncerDependencies {
41
- actionCopySharedToTargetsIfNotExists: typeof SyncerActions.actionCopySharedToTargetsIfNotExists;
42
- actionGenerateHttpValidators: typeof SyncerActions.actionGenerateHttpValidators;
43
- actionGenerateSsrEntryServerIfNotExists: typeof SyncerActions.actionGenerateSsrEntryServerIfNotExists;
44
- findChangedFilesUsingChecksums: typeof findChangedFilesUsingChecksums;
45
- renewChecksums: typeof renewChecksums;
46
- }
47
-
48
- const defaultSyncerDependencies: SyncerDependencies = {
49
- actionCopySharedToTargetsIfNotExists: SyncerActions.actionCopySharedToTargetsIfNotExists,
50
- actionGenerateHttpValidators: SyncerActions.actionGenerateHttpValidators,
51
- actionGenerateSsrEntryServerIfNotExists: SyncerActions.actionGenerateSsrEntryServerIfNotExists,
52
- findChangedFilesUsingChecksums,
53
- renewChecksums,
54
- };
55
-
56
40
  export class Syncer {
57
41
  apis: LoadedApis = [];
58
42
  types: LoadedTypes = {};
@@ -60,8 +44,6 @@ export class Syncer {
60
44
  workflows: Map<string, WorkflowMetadata[]> = new Map();
61
45
  eventEmitter: EventEmitter = new EventEmitter();
62
46
 
63
- constructor(private readonly dependencies: SyncerDependencies = defaultSyncerDependencies) {}
64
-
65
47
  /**
66
48
  * 체크섬이 변경된 부분에 대해 싱크를 진행합니다.
67
49
  * dev 서버가 처음 떴을 때, sonamu sync 할 때 실행됩니다. 이후에는 hmrAndSync 경로를 타요.
@@ -71,14 +53,14 @@ export class Syncer {
71
53
  // 초기 부트스트랩! 얘네들은 idempotent하고 가볍기 때문에 무지성 실행해도 됩니다.
72
54
  // 얘네들은 sonamu.lock에 들어가지도 않고 따라서 HMR 경로를 타지도 않는 친구들입니다.
73
55
  // 그래서 아무 때나 그냥 돌려주면 되는데, hmrAndSync에서 매번 하는 것은 낭비이니 여기서 한 번만 합니다.
74
- await this.dependencies.actionCopySharedToTargetsIfNotExists();
75
- await this.dependencies.actionGenerateSsrEntryServerIfNotExists();
56
+ await SyncerActions.actionCopySharedToTargetsIfNotExists();
57
+ await SyncerActions.actionGenerateSsrEntryServerIfNotExists();
76
58
 
77
59
  // 바뀐 것이 없으면 그냥 넘어가요.
78
- const changedFiles = await this.dependencies.findChangedFilesUsingChecksums();
60
+ const changedFiles = await findChangedFilesUsingChecksums();
79
61
  if (changedFiles.length === 0) {
80
62
  if (await this.reconcileHttpValidatorRegistry()) {
81
- await this.dependencies.renewChecksums();
63
+ await renewChecksums();
82
64
  }
83
65
  console.log(chalk.black.bgGreen(centerText("All files are synced!")));
84
66
  return;
@@ -94,7 +76,7 @@ export class Syncer {
94
76
  await this.reconcileHttpValidatorRegistry();
95
77
 
96
78
  // 싱크 액션이 끝나면 항상 체크섬을 다시 갱신합니다.
97
- await this.dependencies.renewChecksums();
79
+ await renewChecksums();
98
80
  },
99
81
  { whenThisHappens: "SIGUSR2", waitForUpTo: 20000 },
100
82
  );
@@ -112,7 +94,7 @@ export class Syncer {
112
94
  }
113
95
 
114
96
  // 변경 목록이 registry 삭제를 놓쳐도 checksum 갱신 전에 정책과 산출물을 다시 맞춥니다.
115
- await this.dependencies.actionGenerateHttpValidators();
97
+ await SyncerActions.actionGenerateHttpValidators();
116
98
  return true;
117
99
  }
118
100
 
@@ -351,7 +333,7 @@ export class Syncer {
351
333
 
352
334
  // 여기는 별로 중요한 파트는 아닙니다.
353
335
  // 아래의 if 전개를 깔끔하게 하려고 만든 DSL 같은 거라서, 무시하셔도 됩니다.
354
- const { changeMatches, nothingMatches, unhandledPaths } = this.changeMatcher(
336
+ const { changeMatches, unhandledPaths, unhandledChangesLeft } = this.changeMatcher(
355
337
  diffTypes,
356
338
  diffGroups,
357
339
  );
@@ -372,30 +354,24 @@ export class Syncer {
372
354
  await this.handleConfigChanges();
373
355
  }
374
356
 
375
- if (changeMatches("i18n", "entity" /*레이블*/, "config" /*defaultLocale등*/)) {
376
- await this.handleSonamuDictionaryRelatedChanges();
357
+ if (changeMatches("workflow")) {
358
+ // 뭐 딱히 안 해요. 그래도 이 if 조건은 eval되어야 하기 때문에 남겨둡니다 ㅎㅎ
377
359
  }
378
360
 
379
- const metadataChanged = diffTypes.some((type) =>
380
- ["entity", "types", "model", "frame", "config"].includes(type),
381
- );
382
- if (changeMatches("httpValidatorsGenerated") && !metadataChanged) {
383
- // registry 자체 drift는 최신 in-memory metadata로 즉시 덮어써 lock 갱신 전에 정합성을 복구합니다.
384
- await this.dependencies.actionGenerateHttpValidators();
361
+ if (changeMatches("i18n", "entity" /*레이블*/, "config" /*defaultLocale등*/)) {
362
+ await this.handleSonamuDictionaryRelatedChanges();
385
363
  }
386
364
 
387
- if (metadataChanged) {
388
- // 모든 생성/복사 액션 뒤 최신 metadata를 다시 읽어 registry를 한 번만 확정합니다.
389
- await this.autoloadTypes();
390
- await this.autoloadModels();
391
- await this.autoloadApis();
392
- await this.dependencies.actionGenerateHttpValidators();
365
+ if (changeMatches("entity", "types", "model", "frame", "config", "httpValidatorsGenerated")) {
366
+ // 생성물(httpValidatorsGenerated)에 드리프트가 생긴 경우도 처리합니다.
367
+ // 다른 것 안 바뀌고 딱 httpValidatorsGenerated만 바뀐 경우에도 동등한 처리(=무지성 오토로드)를 합니다. 그냥 이뻐서요 ㅋ
368
+ await this.handleHttpValidatorRelatedChanges();
393
369
  }
394
370
 
395
- if (nothingMatches()) {
396
- // 파일 변경은 감지되었으나 저 위 어느 changeMatches에도 걸리지 않은 파일들이 drifts입니다.
397
- // syncer는 소스의 변경에는 반응하지만 산출물의 변경(drift)에는 직접적으로 반응하지 않습니다.
398
- // 대신 이 drift에 대해 경고 정도만 출력해줍니다.
371
+ // 위에서 처리 안 된 변경들이 있다?
372
+ // 체크섬 관리 대상이긴 한데 위에서 핸들링이 안 된 것이라면 결국 생성물입니다.
373
+ // 이 친구들이 변경된 것은 drift로 간주해도 마땅합니다.
374
+ if (unhandledChangesLeft()) {
399
375
  await this.handleDrifts(unhandledPaths());
400
376
  }
401
377
 
@@ -442,18 +418,18 @@ export class Syncer {
442
418
  return matching.length > 0;
443
419
  };
444
420
 
445
- /**
446
- * changeMatches로 매칭된 것이 하나도 없는지 여부를 가져옵니다.
447
- */
448
- const nothingMatches = () => handled.size === 0;
449
-
450
421
  /**
451
422
  * 어떤 changeMatches 호출에도 걸리지 않은 FileType들의 실제 파일 경로를 모아서 반환합니다.
452
423
  */
453
424
  const unhandledPaths = (): AbsolutePath[] =>
454
425
  diffTypes.filter((t) => !handled.has(t)).flatMap((t) => diffGroups[t] ?? []);
455
426
 
456
- return { changeMatches, nothingMatches, unhandledPaths };
427
+ /**
428
+ * changeMatches 호출에도 걸리지 않은 FileType들이 존재하는지 확인합니다.
429
+ */
430
+ const unhandledChangesLeft = () => unhandledPaths().length > 0;
431
+
432
+ return { changeMatches, unhandledPaths, unhandledChangesLeft };
457
433
  }
458
434
 
459
435
  async handleTruthSourceChanges(diffGroups: DiffGroups): Promise<void> {
@@ -542,18 +518,27 @@ export class Syncer {
542
518
  await SyncerActions.actionSyncSonamuDictionary();
543
519
  }
544
520
 
521
+ async handleHttpValidatorRelatedChanges(): Promise<void> {
522
+ await this.autoloadTypes();
523
+ await this.autoloadModels();
524
+ await this.autoloadApis();
525
+ await SyncerActions.actionGenerateHttpValidators();
526
+ }
527
+
545
528
  async handleDrifts(drifts: AbsolutePath[]): Promise<void> {
546
- if (drifts.length > 0) {
547
- console.warn(
548
- chalk.yellow(
549
- "⚠️ Sonamu가 자동 생성한 파일에 대한 변경이 감지되었습니다. 파일이 Sonamu watcher 외부에서 변경된 것으로 추정됩니다.",
550
- ),
551
- );
552
- for (const p of drifts) {
553
- console.warn(chalk.yellow(` - ${path.relative(Sonamu.appRootPath, p)}`));
554
- }
555
- console.warn(chalk.dim(" → `pnpm sonamu sync --force`를 권장합니다."));
529
+ if (drifts.length === 0) {
530
+ return;
531
+ }
532
+
533
+ console.warn(
534
+ chalk.yellow(
535
+ "⚠️ Sonamu가 자동 생성한 파일에 대한 변경이 감지되었습니다. 파일이 Sonamu watcher 외부에서 변경된 것으로 추정됩니다.",
536
+ ),
537
+ );
538
+ for (const p of drifts) {
539
+ console.warn(chalk.yellow(` - ${path.relative(Sonamu.appRootPath, p)}`));
556
540
  }
541
+ console.warn(chalk.dim(" → `pnpm sonamu sync --force`를 권장합니다."));
557
542
  }
558
543
 
559
544
  /**
@@ -672,6 +657,6 @@ export class Syncer {
672
657
  * 하위호환용 프록시 메소드입니다.
673
658
  */
674
659
  async renewChecksums(): Promise<void> {
675
- return await this.dependencies.renewChecksums();
660
+ return await renewChecksums();
676
661
  }
677
662
  }