sonamu 0.9.20 → 0.10.1

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.
Files changed (299) hide show
  1. package/dist/_virtual/_rolldown/runtime.js +36 -0
  2. package/dist/ai/agents/agent.js +5 -7
  3. package/dist/ai/agents/index.js +1 -2
  4. package/dist/ai/agents/types.js +1 -1
  5. package/dist/ai/index.js +1 -2
  6. package/dist/ai/providers/rtzr/api.js +2 -3
  7. package/dist/ai/providers/rtzr/error.js +14 -29
  8. package/dist/ai/providers/rtzr/index.js +1 -2
  9. package/dist/ai/providers/rtzr/model.js +13 -20
  10. package/dist/ai/providers/rtzr/options.js +2 -3
  11. package/dist/ai/providers/rtzr/provider.js +2 -3
  12. package/dist/ai/providers/rtzr/utils.js +12 -21
  13. package/dist/api/base-frame.js +4 -4
  14. package/dist/api/caster.js +21 -38
  15. package/dist/api/code-converters.js +41 -98
  16. package/dist/api/config.d.ts +1 -10
  17. package/dist/api/config.d.ts.map +1 -1
  18. package/dist/api/config.js +9 -8
  19. package/dist/api/context.js +2 -3
  20. package/dist/api/decorators.js +80 -116
  21. package/dist/api/index.js +2 -3
  22. package/dist/api/secret.js +6 -10
  23. package/dist/api/sonamu.d.ts.map +1 -1
  24. package/dist/api/sonamu.js +210 -406
  25. package/dist/api/validator.js +5 -8
  26. package/dist/api/websocket-helpers.js +22 -33
  27. package/dist/auth/audit-log/builders.d.ts.map +1 -1
  28. package/dist/auth/audit-log/builders.js +4 -3
  29. package/dist/auth/audit-log/client-ip.d.ts +3 -0
  30. package/dist/auth/audit-log/client-ip.d.ts.map +1 -0
  31. package/dist/auth/audit-log/client-ip.js +27 -0
  32. package/dist/auth/audit-log/events.d.ts +1 -0
  33. package/dist/auth/audit-log/events.d.ts.map +1 -1
  34. package/dist/auth/audit-log/events.js +2 -2
  35. package/dist/auth/audit-log/plugin.js +30 -61
  36. package/dist/auth/audit-log-ingestor.d.ts.map +1 -1
  37. package/dist/auth/audit-log-ingestor.js +26 -43
  38. package/dist/auth/auth-generator.js +16 -41
  39. package/dist/auth/better-auth-entities.js +3 -4
  40. package/dist/auth/better-auth-request.d.ts +3 -0
  41. package/dist/auth/better-auth-request.d.ts.map +1 -0
  42. package/dist/auth/better-auth-request.js +23 -0
  43. package/dist/auth/index.js +2 -3
  44. package/dist/auth/knex-adapter.js +18 -45
  45. package/dist/auth/plugins/entity-definitions/admin.js +2 -2
  46. package/dist/auth/plugins/entity-definitions/anonymous.js +2 -2
  47. package/dist/auth/plugins/entity-definitions/api-key.js +2 -2
  48. package/dist/auth/plugins/entity-definitions/audit-log.js +2 -2
  49. package/dist/auth/plugins/entity-definitions/index.js +2 -3
  50. package/dist/auth/plugins/entity-definitions/jwt.js +2 -2
  51. package/dist/auth/plugins/entity-definitions/organization.js +2 -2
  52. package/dist/auth/plugins/entity-definitions/passkey.js +2 -2
  53. package/dist/auth/plugins/entity-definitions/phone-number.js +2 -2
  54. package/dist/auth/plugins/entity-definitions/sso.js +2 -2
  55. package/dist/auth/plugins/entity-definitions/two-factor.js +2 -2
  56. package/dist/auth/plugins/entity-definitions/types.js +1 -1
  57. package/dist/auth/plugins/entity-definitions/username.js +2 -2
  58. package/dist/auth/plugins/index.js +1 -2
  59. package/dist/auth/plugins/wrappers/admin.js +2 -3
  60. package/dist/auth/plugins/wrappers/anonymous.js +2 -3
  61. package/dist/auth/plugins/wrappers/api-key.js +2 -3
  62. package/dist/auth/plugins/wrappers/index.js +1 -2
  63. package/dist/auth/plugins/wrappers/jwt.js +2 -3
  64. package/dist/auth/plugins/wrappers/organization.js +2 -3
  65. package/dist/auth/plugins/wrappers/passkey.js +2 -3
  66. package/dist/auth/plugins/wrappers/phone-number.js +2 -3
  67. package/dist/auth/plugins/wrappers/sso.js +2 -3
  68. package/dist/auth/plugins/wrappers/two-factor.js +2 -3
  69. package/dist/auth/plugins/wrappers/username.js +2 -3
  70. package/dist/bin/build-config.js +2 -2
  71. package/dist/bin/cli.js +152 -259
  72. package/dist/bin/fixture.d.ts.map +1 -1
  73. package/dist/bin/fixture.js +55 -97
  74. package/dist/bin/hmr-hook-register.js +3 -3
  75. package/dist/bin/migrate-targets.d.ts +3 -0
  76. package/dist/bin/migrate-targets.d.ts.map +1 -0
  77. package/dist/bin/migrate-targets.js +11 -0
  78. package/dist/bin/test-command.js +25 -55
  79. package/dist/bin/ts-loader-register.js +5 -6
  80. package/dist/bin/ts-loader-registration.js +6 -13
  81. package/dist/cache/cache-manager.js +3 -4
  82. package/dist/cache/decorator.js +11 -21
  83. package/dist/cache/drivers.js +2 -3
  84. package/dist/cache/index.js +2 -3
  85. package/dist/cache/types.js +1 -1
  86. package/dist/cache-control/cache-control.js +21 -34
  87. package/dist/cache-control/types.js +1 -1
  88. package/dist/compress/compress.js +10 -10
  89. package/dist/compress/index.js +1 -2
  90. package/dist/compress/types.js +1 -1
  91. package/dist/cone/cone-generator.js +25 -63
  92. package/dist/database/_batch_update.js +26 -46
  93. package/dist/database/base-model.js +44 -97
  94. package/dist/database/base-model.types.js +1 -1
  95. package/dist/database/db.d.ts +8 -14
  96. package/dist/database/db.d.ts.map +1 -1
  97. package/dist/database/db.js +127 -72
  98. package/dist/database/knex.js +5 -8
  99. package/dist/database/puri-subset.types.js +1 -1
  100. package/dist/database/puri-wrapper.js +11 -15
  101. package/dist/database/puri.js +117 -234
  102. package/dist/database/puri.types.js +3 -4
  103. package/dist/database/transaction-context.js +4 -5
  104. package/dist/database/upsert-builder.js +109 -176
  105. package/dist/dict/en.d.ts +1 -0
  106. package/dist/dict/en.d.ts.map +1 -1
  107. package/dist/dict/en.js +4 -4
  108. package/dist/dict/index.js +2 -3
  109. package/dist/dict/ko.d.ts +1 -0
  110. package/dist/dict/ko.d.ts.map +1 -1
  111. package/dist/dict/ko.js +4 -4
  112. package/dist/dict/rc-keys.js +3 -4
  113. package/dist/dict/sd.js +8 -19
  114. package/dist/dict/sonamu-dictionary.js +141 -284
  115. package/dist/dict/types.js +1 -1
  116. package/dist/dict/utils.js +4 -5
  117. package/dist/entity/entity-manager.d.ts +2 -2
  118. package/dist/entity/entity-manager.js +34 -82
  119. package/dist/entity/entity-template-cone.js +33 -66
  120. package/dist/entity/entity.js +157 -311
  121. package/dist/env.d.ts +14 -0
  122. package/dist/env.d.ts.map +1 -0
  123. package/dist/env.js +75 -0
  124. package/dist/exceptions/error-handler.js +2 -3
  125. package/dist/exceptions/so-exceptions.js +7 -5
  126. package/dist/filter/index.js +1 -2
  127. package/dist/filter/types.js +3 -4
  128. package/dist/filter/utils.js +21 -54
  129. package/dist/index.js +9 -8
  130. package/dist/logger/category.js +6 -12
  131. package/dist/logger/configure.js +23 -34
  132. package/dist/migration/code-generation.js +147 -315
  133. package/dist/migration/index-where-predicate.js +52 -144
  134. package/dist/migration/migration-set.js +19 -33
  135. package/dist/migration/migrator.d.ts +2 -0
  136. package/dist/migration/migrator.d.ts.map +1 -1
  137. package/dist/migration/migrator.js +69 -53
  138. package/dist/migration/postgresql-schema-reader.js +126 -225
  139. package/dist/migration/slack-confirm.d.ts +1 -0
  140. package/dist/migration/slack-confirm.d.ts.map +1 -1
  141. package/dist/migration/slack-confirm.js +28 -38
  142. package/dist/migration/types.js +1 -1
  143. package/dist/naite/messaging-types.js +1 -1
  144. package/dist/naite/naite-reporter.js +15 -32
  145. package/dist/naite/naite.js +43 -76
  146. package/dist/ssr/index.js +6 -9
  147. package/dist/ssr/registry.js +10 -18
  148. package/dist/ssr/renderer.js +10 -21
  149. package/dist/ssr/types.js +1 -1
  150. package/dist/storage/base-file.js +5 -10
  151. package/dist/storage/buffered-file.js +3 -4
  152. package/dist/storage/drivers.js +2 -3
  153. package/dist/storage/index.js +2 -3
  154. package/dist/storage/s3-driver.js +5 -9
  155. package/dist/storage/storage-manager.js +5 -5
  156. package/dist/storage/types.js +1 -1
  157. package/dist/storage/uploaded-file.js +4 -6
  158. package/dist/stream/index.js +1 -2
  159. package/dist/stream/sse.js +8 -13
  160. package/dist/stream/ws-audience-resolver.js +5 -5
  161. package/dist/stream/ws-audience.js +3 -4
  162. package/dist/stream/ws-cluster-bus.js +3 -4
  163. package/dist/stream/ws-core.js +1 -1
  164. package/dist/stream/ws-delivery.js +11 -25
  165. package/dist/stream/ws-local-connection-store.js +9 -18
  166. package/dist/stream/ws-presence-store.js +43 -97
  167. package/dist/stream/ws-registry.js +17 -22
  168. package/dist/stream/ws-telemetry-memory.js +38 -45
  169. package/dist/stream/ws-telemetry-trace.js +4 -6
  170. package/dist/stream/ws-telemetry.js +82 -135
  171. package/dist/stream/ws.js +47 -91
  172. package/dist/syncer/api-parser.js +81 -147
  173. package/dist/syncer/checksum.js +14 -32
  174. package/dist/syncer/code-generator.js +29 -47
  175. package/dist/syncer/entity-operations.js +17 -27
  176. package/dist/syncer/event-batcher.js +8 -15
  177. package/dist/syncer/file-patterns.js +3 -4
  178. package/dist/syncer/file-tracking.js +6 -10
  179. package/dist/syncer/index.js +1 -2
  180. package/dist/syncer/module-loader.js +10 -26
  181. package/dist/syncer/syncer-actions.js +19 -37
  182. package/dist/syncer/syncer.js +46 -98
  183. package/dist/syncer/watcher.js +12 -26
  184. package/dist/tasks/decorator.js +7 -11
  185. package/dist/tasks/step-wrapper.js +7 -8
  186. package/dist/tasks/workflow-manager.js +18 -25
  187. package/dist/template/entity-converter.js +40 -64
  188. package/dist/template/helpers.js +32 -63
  189. package/dist/template/implementations/entity.template.js +7 -11
  190. package/dist/template/implementations/entry-server.template.js +2 -3
  191. package/dist/template/implementations/generated.template.js +25 -51
  192. package/dist/template/implementations/generated_http.template.js +31 -58
  193. package/dist/template/implementations/generated_sso.template.js +45 -85
  194. package/dist/template/implementations/init_types.template.js +4 -7
  195. package/dist/template/implementations/model.template.js +5 -10
  196. package/dist/template/implementations/model_test.template.js +2 -3
  197. package/dist/template/implementations/queries.template.js +4 -7
  198. package/dist/template/implementations/sd.template.js +17 -35
  199. package/dist/template/implementations/services.template.js +18 -30
  200. package/dist/template/implementations/view_form.template.js +72 -125
  201. package/dist/template/implementations/view_id_all_select.template.js +2 -3
  202. package/dist/template/implementations/view_list.template.js +86 -143
  203. package/dist/template/implementations/view_search_input.template.js +2 -3
  204. package/dist/template/index.js +5 -8
  205. package/dist/template/template-manager.js +13 -26
  206. package/dist/template/template-types.js +2 -3
  207. package/dist/template/template.js +7 -11
  208. package/dist/template/zod-converter.js +173 -348
  209. package/dist/testing/_relation-graph.js +18 -37
  210. package/dist/testing/bootstrap.js +5 -8
  211. package/dist/testing/data-explorer.js +34 -78
  212. package/dist/testing/dev-test-routes.js +54 -60
  213. package/dist/testing/dev-vitest-manager.js +33 -84
  214. package/dist/testing/faker-mappings.js +3 -4
  215. package/dist/testing/fixture-generator.d.ts +2 -1
  216. package/dist/testing/fixture-generator.d.ts.map +1 -1
  217. package/dist/testing/fixture-generator.js +159 -321
  218. package/dist/testing/fixture-loader.js +2 -2
  219. package/dist/testing/fixture-manager.d.ts.map +1 -1
  220. package/dist/testing/fixture-manager.js +124 -227
  221. package/dist/testing/global-setup.d.ts.map +1 -1
  222. package/dist/testing/global-setup.js +29 -17
  223. package/dist/testing/index.d.ts +1 -0
  224. package/dist/testing/index.d.ts.map +1 -1
  225. package/dist/testing/index.js +5 -4
  226. package/dist/testing/naite-vitest-reporter.js +2 -3
  227. package/dist/testing/parallel-db-manager.js +5 -3
  228. package/dist/testing/vitest-helpers.d.ts.map +1 -1
  229. package/dist/testing/vitest-helpers.js +15 -12
  230. package/dist/types/types.d.ts +14 -14
  231. package/dist/types/types.js +27 -50
  232. package/dist/ui/ai-api.js +6 -11
  233. package/dist/ui/ai-client.js +86 -134
  234. package/dist/ui/api.js +100 -196
  235. package/dist/ui/cdd-service.js +78 -130
  236. package/dist/ui/cdd-types.js +1 -1
  237. package/dist/ui-web/assets/{index-Df8q-fhb.js → index-DFStGyd0.js} +49 -49
  238. package/dist/ui-web/assets/index-Dx4ap5i4.css +1 -0
  239. package/dist/ui-web/index.html +2 -2
  240. package/dist/utils/async-utils.js +13 -25
  241. package/dist/utils/class-name.js +3 -4
  242. package/dist/utils/console-util.js +11 -26
  243. package/dist/utils/controller.d.ts.map +1 -1
  244. package/dist/utils/controller.js +14 -12
  245. package/dist/utils/esm-utils.js +5 -8
  246. package/dist/utils/formatter.js +10 -22
  247. package/dist/utils/fs-utils.js +14 -25
  248. package/dist/utils/lodash-able.js +3 -4
  249. package/dist/utils/model.js +7 -14
  250. package/dist/utils/object-utils.js +41 -73
  251. package/dist/utils/path-utils.js +5 -9
  252. package/dist/utils/process-utils.js +4 -7
  253. package/dist/utils/sql-parser.js +6 -13
  254. package/dist/utils/type-utils.js +16 -26
  255. package/dist/utils/utils.js +18 -40
  256. package/dist/utils/zod-error.js +9 -16
  257. package/dist/vector/chunking.js +24 -37
  258. package/dist/vector/config.js +2 -2
  259. package/dist/vector/embedding.js +8 -19
  260. package/dist/vector/index.js +1 -2
  261. package/dist/vector/types.js +1 -1
  262. package/package.json +8 -8
  263. package/src/__tests__/env.test.ts +127 -0
  264. package/src/api/__tests__/config.test.ts +10 -1
  265. package/src/api/config.ts +4 -12
  266. package/src/api/sonamu.ts +28 -33
  267. package/src/auth/audit-log/builders.ts +3 -0
  268. package/src/auth/audit-log/client-ip.ts +38 -0
  269. package/src/auth/audit-log/events.ts +1 -0
  270. package/src/auth/audit-log-ingestor.ts +22 -15
  271. package/src/auth/better-auth-request.ts +20 -0
  272. package/src/bin/__tests__/migrate-targets.test.ts +28 -0
  273. package/src/bin/__tests__/test-command.test.ts +82 -1
  274. package/src/bin/cli.ts +9 -18
  275. package/src/bin/fixture.ts +5 -4
  276. package/src/bin/migrate-targets.ts +7 -0
  277. package/src/bin/test-command.ts +2 -2
  278. package/src/database/__tests__/db.test.ts +175 -0
  279. package/src/database/db.ts +193 -71
  280. package/src/dict/en.ts +2 -0
  281. package/src/dict/ko.ts +2 -0
  282. package/src/env.ts +123 -0
  283. package/src/migration/__tests__/migrator.test.ts +149 -0
  284. package/src/migration/migrator.ts +74 -17
  285. package/src/migration/slack-confirm.ts +21 -0
  286. package/src/skills/sonamu/database.md +1 -1
  287. package/src/skills/sonamu/puri.md +33 -0
  288. package/src/skills/sonamu/testing-devrunner.md +1 -1
  289. package/src/stream/ws-telemetry-memory.ts +2 -2
  290. package/src/syncer/checksum.ts +7 -12
  291. package/src/testing/fixture-generator.ts +2 -1
  292. package/src/testing/fixture-manager.ts +3 -4
  293. package/src/testing/global-setup.ts +42 -18
  294. package/src/testing/index.ts +1 -0
  295. package/src/testing/vitest-helpers.ts +14 -0
  296. package/src/utils/controller.ts +14 -7
  297. package/tsdown.api.config.ts +6 -0
  298. package/dist/_virtual/rolldown_runtime.js +0 -39
  299. package/dist/ui-web/assets/index-D4rYm-Xz.css +0 -1
@@ -0,0 +1,28 @@
1
+ import { afterEach, describe, expect, it } from "vitest";
2
+
3
+ import { getMigrateRunTargets } from "../migrate-targets";
4
+
5
+ describe("getMigrateRunTargets", () => {
6
+ const originalEnv = { ...process.env };
7
+
8
+ afterEach(() => {
9
+ for (const key of Object.keys(process.env)) {
10
+ if (!(key in originalEnv)) {
11
+ delete process.env[key];
12
+ }
13
+ }
14
+ Object.assign(process.env, originalEnv);
15
+ });
16
+
17
+ it("includes fixture when running migrations in the test environment", () => {
18
+ process.env.NODE_ENV = "test";
19
+
20
+ expect(getMigrateRunTargets()).toEqual(["test", "fixture"]);
21
+ });
22
+
23
+ it("uses only the current environment outside test", () => {
24
+ process.env.NODE_ENV = "staging";
25
+
26
+ expect(getMigrateRunTargets()).toEqual(["staging"]);
27
+ });
28
+ });
@@ -1,4 +1,9 @@
1
- import { describe, expect, it } from "vitest";
1
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2
+
3
+ const originalArgv = process.argv;
4
+ const originalNodeEnv = process.env.NODE_ENV;
5
+ const originalVitest = process.env.VITEST;
6
+ const originalFetch = globalThis.fetch;
2
7
 
3
8
  // process.argv 파싱 로직을 검증하는 유닛 테스트
4
9
  // test-command.ts의 파싱 로직을 동일하게 구현하여 독립 검증
@@ -83,3 +88,79 @@ describe("test-command argument parsing", () => {
83
88
  expect(showTraces).toBe(true);
84
89
  });
85
90
  });
91
+
92
+ describe("test-command dev server endpoint resolution", () => {
93
+ beforeEach(() => {
94
+ vi.resetModules();
95
+ vi.restoreAllMocks();
96
+ process.argv = [process.execPath, "sonamu", "test", "--status"];
97
+ process.env.NODE_ENV = "development";
98
+ delete process.env.VITEST;
99
+ });
100
+
101
+ afterEach(() => {
102
+ vi.resetModules();
103
+ vi.restoreAllMocks();
104
+ process.argv = originalArgv;
105
+ globalThis.fetch = originalFetch;
106
+
107
+ if (originalNodeEnv === undefined) {
108
+ delete process.env.NODE_ENV;
109
+ } else {
110
+ process.env.NODE_ENV = originalNodeEnv;
111
+ }
112
+
113
+ if (originalVitest === undefined) {
114
+ delete process.env.VITEST;
115
+ } else {
116
+ process.env.VITEST = originalVitest;
117
+ }
118
+ });
119
+
120
+ it("status 명령은 NODE_ENV=test로 바꾸지 않고 dev server 설정의 endpoint를 호출한다", async () => {
121
+ const loadConfig = vi.fn(async () => {
122
+ expect(process.env.NODE_ENV).toBe("development");
123
+ expect(process.env.VITEST).toBe("true");
124
+ return {
125
+ server: {
126
+ listen: {
127
+ host: "127.0.0.1",
128
+ port: 4401,
129
+ },
130
+ },
131
+ test: {
132
+ devRunner: {
133
+ enabled: true,
134
+ routePrefix: "/__dev_test__",
135
+ },
136
+ },
137
+ };
138
+ });
139
+ const fetch = vi.fn(async () => {
140
+ return new Response(
141
+ JSON.stringify({
142
+ ready: true,
143
+ running: false,
144
+ lastRunAt: null,
145
+ sseAvailable: true,
146
+ }),
147
+ {
148
+ status: 200,
149
+ headers: { "Content-Type": "application/json" },
150
+ },
151
+ );
152
+ });
153
+
154
+ vi.doMock("../../api/config", () => ({ loadConfig }));
155
+ vi.doMock("../../utils/utils", () => ({ findApiRootPath: () => "/tmp/api" }));
156
+ vi.spyOn(console, "log").mockImplementation(() => {});
157
+ globalThis.fetch = fetch as typeof globalThis.fetch;
158
+
159
+ const { testCommand } = await import("../test-command");
160
+ await testCommand();
161
+
162
+ expect(loadConfig).toHaveBeenCalledWith("/tmp/api");
163
+ expect(fetch).toHaveBeenCalledWith("http://127.0.0.1:4401/__dev_test__/status");
164
+ expect(process.env.NODE_ENV).toBe("development");
165
+ });
166
+ });
package/src/bin/cli.ts CHANGED
@@ -1,8 +1,3 @@
1
- import chalk from "chalk";
2
- import dotenv from "dotenv";
3
-
4
- dotenv.config();
5
-
6
1
  import assert from "assert";
7
2
  import { execSync, spawn } from "child_process";
8
3
  import { cp, mkdir, readdir, readFile, rm, symlink, writeFile } from "fs/promises";
@@ -11,6 +6,7 @@ import os from "os";
11
6
  import path from "path";
12
7
  import process from "process";
13
8
 
9
+ import chalk from "chalk";
14
10
  import knex from "knex";
15
11
  import { type Knex } from "knex";
16
12
  import { tsicli } from "tsicli";
@@ -21,6 +17,7 @@ import { isValidPluginId, SUPPORTED_PLUGIN_IDS } from "../auth/plugins/entity-de
21
17
  import { type BetterAuthPluginId } from "../auth/plugins/entity-definitions";
22
18
  import { type SonamuDBConfig } from "../database/db";
23
19
  import { EntityManager } from "../entity/entity-manager";
20
+ import { getSonamuEnvironment } from "../env";
24
21
  import { Migrator } from "../migration/migrator";
25
22
  import { FixtureManager } from "../testing/fixture-manager";
26
23
  import {
@@ -36,6 +33,7 @@ import { findApiRootPath, findAppRootPath } from "../utils/utils";
36
33
  import { API_ARTIFACTS, WEB_ARTIFACTS } from "./build-config";
37
34
  import { type BuildArtifact } from "./build-config";
38
35
  import { fixtureExploreCommand, fixtureFetchCommand, fixtureGenCommand } from "./fixture";
36
+ import { getMigrateRunTargets } from "./migrate-targets";
39
37
  import { testCommand } from "./test-command";
40
38
 
41
39
  let migrator: Migrator;
@@ -144,8 +142,9 @@ async function bootstrap() {
144
142
  name: "#targets",
145
143
  message: "Please input #targets",
146
144
  choices: [
147
- { title: "Development", value: "development_master" },
148
- { title: "Production", value: "production_master" },
145
+ { title: "Development", value: "development" },
146
+ { title: "Staging", value: "staging" },
147
+ { title: "Production", value: "production" },
149
148
  { title: "Fixture", value: "fixture" },
150
149
  { title: "Test", value: "test" },
151
150
  ],
@@ -284,7 +283,7 @@ function spawnApiDevServer(options?: { extraEnv?: Record<string, string> }) {
284
283
  stdio: "inherit",
285
284
  env: {
286
285
  ...process.env,
287
- NODE_ENV: "development",
286
+ NODE_ENV: process.env.NODE_ENV ?? "development",
288
287
  HOT: "yes", // 얘가 있어야 HMR이 활성화됩니다.
289
288
  API_ROOT_PATH: apiRoot, // 이 경로가 hmr-hook의 루트 디렉토리가 됩니다.
290
289
  ...options?.extraEnv,
@@ -553,15 +552,7 @@ async function migrate_apply(targets: (keyof SonamuDBConfig)[]) {
553
552
 
554
553
  async function migrate_run() {
555
554
  await setupMigrator();
556
- const localHosts = ["localhost", "127.0.0.1", "0.0.0.0", "::1"];
557
- const targets = Object.keys(Sonamu.dbConfig).filter((target) => {
558
- const targetConfig = Sonamu.dbConfig[target as keyof SonamuDBConfig];
559
- const host = (targetConfig?.connection as { host?: string })?.host ?? "localhost";
560
- return localHosts.includes(host.toLowerCase());
561
- });
562
-
563
- // 로컬 데이터베이스에 대해서만 전체 마이그레이션에서 동작
564
- await migrator.runAction("apply", targets as (keyof SonamuDBConfig)[]);
555
+ await migrator.runAction("apply", getMigrateRunTargets());
565
556
  }
566
557
 
567
558
  async function migrate_generate() {
@@ -598,7 +589,7 @@ async function migrate_status() {
598
589
  }
599
590
 
600
591
  async function fixture_init() {
601
- const srcConfig = Sonamu.dbConfig.development_master;
592
+ const srcConfig = Sonamu.dbConfig[getSonamuEnvironment()];
602
593
  const targets = [
603
594
  {
604
595
  label: "(REMOTE) Fixture DB",
@@ -5,6 +5,7 @@ import { Sonamu } from "../api/sonamu";
5
5
  import { DB } from "../database/db";
6
6
  import { createKnexInstance } from "../database/knex";
7
7
  import { EntityManager } from "../entity/entity-manager";
8
+ import { getSonamuEnvironment } from "../env";
8
9
  import { DataExplorer } from "../testing/data-explorer";
9
10
  import { type DataExplorerStrategy } from "../testing/data-explorer";
10
11
  import { FixtureGenerator } from "../testing/fixture-generator";
@@ -129,12 +130,12 @@ export async function fixtureGenCommand(options: FixtureCommandOptions) {
129
130
  const enableLLMCache = !options["no-cache"];
130
131
  const DEFAULT_PASSWORD = "Test1234!";
131
132
 
132
- // 로그인 가능 경로에서는 sourceDb로 development_master 사용
133
+ // 로그인 가능 경로에서는 현재 환경의 읽기 DB를 sourceDb로 사용
133
134
  const sourceDb = DB.getDB("r");
134
135
  const generator = new FixtureGenerator(
135
136
  sourceDb,
136
137
  sourceDb,
137
- "production_master",
138
+ getSonamuEnvironment(),
138
139
  EntityManager,
139
140
  { useLLM, enableLLMCache },
140
141
  );
@@ -395,8 +396,8 @@ export async function fixtureFetchCommand(options: FixtureCommandOptions) {
395
396
  const strategy: DataExplorerStrategy = options.strategy ?? "recent";
396
397
  const limit = options.limit ? Number.parseInt(options.limit, 10) : 10;
397
398
 
398
- // fixture fetch: production 데이터를 fixture DB로 import합니다
399
- const sourceDb = DB.getDB("r"); // production_master (또는 development_master)
399
+ // fixture fetch: 현재 환경 데이터를 fixture DB로 import합니다
400
+ const sourceDb = DB.getDB("r");
400
401
  const fixtureDb = createKnexInstance(Sonamu.dbConfig.fixture);
401
402
  const generator = new FixtureGenerator(sourceDb, fixtureDb, "fixture", EntityManager);
402
403
 
@@ -0,0 +1,7 @@
1
+ import { type SonamuDBConfig } from "../database/db";
2
+ import { getSonamuEnvironment } from "../env";
3
+
4
+ export function getMigrateRunTargets(): (keyof SonamuDBConfig)[] {
5
+ const environment = getSonamuEnvironment();
6
+ return environment === "test" ? ["test", "fixture"] : [environment];
7
+ }
@@ -7,7 +7,7 @@ import { loadConfig } from "../api/config";
7
7
  import { type RunResult, type TestCaseResult } from "../testing";
8
8
  import { findApiRootPath } from "../utils/utils";
9
9
 
10
- async function loadTestConfig(): Promise<SonamuConfig> {
10
+ async function loadDevServerConfig(): Promise<SonamuConfig> {
11
11
  const prevVitest = process.env.VITEST;
12
12
  process.env.VITEST = "true";
13
13
  try {
@@ -37,7 +37,7 @@ function resolveTestBaseUrl(config: SonamuConfig): {
37
37
  export async function testCommand(): Promise<void> {
38
38
  const args = process.argv.slice(3);
39
39
 
40
- const config = await loadTestConfig();
40
+ const config = await loadDevServerConfig();
41
41
 
42
42
  // process.argv 파싱: sonamu test [file...] --pattern "이름" --traces --status
43
43
  const files: string[] = [];
@@ -0,0 +1,175 @@
1
+ import { afterEach, describe, expect, it } from "vitest";
2
+
3
+ import { type EnvironmentSnapshots } from "../../env";
4
+ import { DBClass } from "../db";
5
+
6
+ describe("DBClass.generateDBConfig", () => {
7
+ const originalEnv = { ...process.env };
8
+ const emptySnapshots: EnvironmentSnapshots = {
9
+ test: {},
10
+ development: {},
11
+ staging: {},
12
+ production: {},
13
+ };
14
+
15
+ afterEach(() => {
16
+ for (const key of Object.keys(process.env)) {
17
+ if (!(key in originalEnv)) {
18
+ delete process.env[key];
19
+ }
20
+ }
21
+ Object.assign(process.env, originalEnv);
22
+ });
23
+
24
+ it("keeps defaultOptions.connection values when matching SONAMU_DB env vars are absent", () => {
25
+ delete process.env.SONAMU_DB_HOST;
26
+ delete process.env.SONAMU_DB_PORT;
27
+ delete process.env.SONAMU_DB_USER;
28
+ delete process.env.SONAMU_DB_PASSWORD;
29
+ delete process.env.SONAMU_DB_NAME;
30
+
31
+ const dbConfig = new DBClass().generateDBConfig(
32
+ {
33
+ database: "pg",
34
+ defaultOptions: {
35
+ connection: {
36
+ host: "default-host",
37
+ port: 15432,
38
+ user: "default-user",
39
+ password: "default-password",
40
+ },
41
+ },
42
+ },
43
+ "Miomock",
44
+ );
45
+
46
+ expect(dbConfig.development!.connection).toMatchObject({
47
+ host: "default-host",
48
+ port: 15432,
49
+ user: "default-user",
50
+ password: "default-password",
51
+ database: "miomock_development",
52
+ });
53
+ });
54
+
55
+ it("derives database names from projectName even when defaultOptions.connection.database is set", () => {
56
+ const dbConfig = new DBClass().generateDBConfig(
57
+ {
58
+ database: "pg",
59
+ defaultOptions: {
60
+ connection: {
61
+ database: "legacy_database",
62
+ },
63
+ },
64
+ },
65
+ "Miomock",
66
+ emptySnapshots,
67
+ );
68
+
69
+ expect(dbConfig.development!.connection).toMatchObject({
70
+ database: "miomock_development",
71
+ });
72
+ expect(dbConfig.production!.connection).toMatchObject({
73
+ database: "miomock_production",
74
+ });
75
+ });
76
+
77
+ it("fails fast when legacy database.name or database.environments keys are present", () => {
78
+ const legacyConfig = {
79
+ database: "pg",
80
+ name: "miomock",
81
+ environments: {
82
+ production: {},
83
+ },
84
+ } as Parameters<DBClass["generateDBConfig"]>[0];
85
+
86
+ expect(() => new DBClass().generateDBConfig(legacyConfig, "Miomock")).toThrow(
87
+ /database\.name and database\.environments were removed/,
88
+ );
89
+ });
90
+
91
+ it("lets readonly env vars override only the readonly connection fields", () => {
92
+ process.env.SONAMU_DB_HOST = "writer-host";
93
+ process.env.SONAMU_DB_USER = "writer-user";
94
+ process.env.SONAMU_DB_PASSWORD = "writer-password";
95
+ process.env.SONAMU_DB_READONLY_HOST = "readonly-host";
96
+
97
+ const dbConfig = new DBClass().generateDBConfig(
98
+ {
99
+ database: "pg",
100
+ defaultOptions: {
101
+ connection: {
102
+ port: 15432,
103
+ },
104
+ },
105
+ },
106
+ "Miomock",
107
+ );
108
+
109
+ expect(dbConfig.development_readonly!.connection).toMatchObject({
110
+ host: "readonly-host",
111
+ port: 15432,
112
+ user: "writer-user",
113
+ password: "writer-password",
114
+ database: "miomock_development",
115
+ });
116
+ });
117
+
118
+ it("does not let current process connection values leak into other environment snapshots", () => {
119
+ const dbConfig = new DBClass().generateDBConfig(
120
+ {
121
+ database: "pg",
122
+ defaultOptions: {
123
+ connection: {
124
+ host: "development-host",
125
+ port: 15432,
126
+ user: "development-user",
127
+ password: "development-password",
128
+ ssl: true,
129
+ },
130
+ },
131
+ },
132
+ "Miomock",
133
+ {
134
+ ...emptySnapshots,
135
+ staging: {
136
+ SONAMU_DB_HOST: "staging-host",
137
+ },
138
+ },
139
+ );
140
+
141
+ expect(dbConfig.staging!.connection).toMatchObject({
142
+ host: "staging-host",
143
+ port: 5432,
144
+ user: "postgres",
145
+ database: "miomock_staging",
146
+ ssl: true,
147
+ });
148
+ expect(dbConfig.staging!.connection).not.toMatchObject({
149
+ password: "development-password",
150
+ });
151
+ });
152
+
153
+ it("does not reuse SONAMU_DB_NAME as the fixture database name", () => {
154
+ const dbConfig = new DBClass().generateDBConfig(
155
+ {
156
+ database: "pg",
157
+ defaultOptions: {},
158
+ },
159
+ "Miomock",
160
+ {
161
+ ...emptySnapshots,
162
+ test: {
163
+ SONAMU_DB_NAME: "miomock_test",
164
+ },
165
+ },
166
+ );
167
+
168
+ expect(dbConfig.test!.connection).toMatchObject({
169
+ database: "miomock_test",
170
+ });
171
+ expect(dbConfig.fixture!.connection).toMatchObject({
172
+ database: "miomock_fixture",
173
+ });
174
+ });
175
+ });