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.
- package/dist/_virtual/_rolldown/runtime.js +36 -0
- package/dist/ai/agents/agent.js +5 -7
- package/dist/ai/agents/index.js +1 -2
- package/dist/ai/agents/types.js +1 -1
- package/dist/ai/index.js +1 -2
- package/dist/ai/providers/rtzr/api.js +2 -3
- package/dist/ai/providers/rtzr/error.js +14 -29
- package/dist/ai/providers/rtzr/index.js +1 -2
- package/dist/ai/providers/rtzr/model.js +13 -20
- package/dist/ai/providers/rtzr/options.js +2 -3
- package/dist/ai/providers/rtzr/provider.js +2 -3
- package/dist/ai/providers/rtzr/utils.js +12 -21
- package/dist/api/base-frame.js +4 -4
- package/dist/api/caster.js +21 -38
- package/dist/api/code-converters.js +41 -98
- package/dist/api/config.d.ts +1 -10
- package/dist/api/config.d.ts.map +1 -1
- package/dist/api/config.js +9 -8
- package/dist/api/context.js +2 -3
- package/dist/api/decorators.js +80 -116
- package/dist/api/index.js +2 -3
- package/dist/api/secret.js +6 -10
- package/dist/api/sonamu.d.ts.map +1 -1
- package/dist/api/sonamu.js +210 -406
- package/dist/api/validator.js +5 -8
- package/dist/api/websocket-helpers.js +22 -33
- package/dist/auth/audit-log/builders.d.ts.map +1 -1
- package/dist/auth/audit-log/builders.js +4 -3
- package/dist/auth/audit-log/client-ip.d.ts +3 -0
- package/dist/auth/audit-log/client-ip.d.ts.map +1 -0
- package/dist/auth/audit-log/client-ip.js +27 -0
- package/dist/auth/audit-log/events.d.ts +1 -0
- package/dist/auth/audit-log/events.d.ts.map +1 -1
- package/dist/auth/audit-log/events.js +2 -2
- package/dist/auth/audit-log/plugin.js +30 -61
- package/dist/auth/audit-log-ingestor.d.ts.map +1 -1
- package/dist/auth/audit-log-ingestor.js +26 -43
- package/dist/auth/auth-generator.js +16 -41
- package/dist/auth/better-auth-entities.js +3 -4
- package/dist/auth/better-auth-request.d.ts +3 -0
- package/dist/auth/better-auth-request.d.ts.map +1 -0
- package/dist/auth/better-auth-request.js +23 -0
- package/dist/auth/index.js +2 -3
- package/dist/auth/knex-adapter.js +18 -45
- package/dist/auth/plugins/entity-definitions/admin.js +2 -2
- package/dist/auth/plugins/entity-definitions/anonymous.js +2 -2
- package/dist/auth/plugins/entity-definitions/api-key.js +2 -2
- package/dist/auth/plugins/entity-definitions/audit-log.js +2 -2
- package/dist/auth/plugins/entity-definitions/index.js +2 -3
- package/dist/auth/plugins/entity-definitions/jwt.js +2 -2
- package/dist/auth/plugins/entity-definitions/organization.js +2 -2
- package/dist/auth/plugins/entity-definitions/passkey.js +2 -2
- package/dist/auth/plugins/entity-definitions/phone-number.js +2 -2
- package/dist/auth/plugins/entity-definitions/sso.js +2 -2
- package/dist/auth/plugins/entity-definitions/two-factor.js +2 -2
- package/dist/auth/plugins/entity-definitions/types.js +1 -1
- package/dist/auth/plugins/entity-definitions/username.js +2 -2
- package/dist/auth/plugins/index.js +1 -2
- package/dist/auth/plugins/wrappers/admin.js +2 -3
- package/dist/auth/plugins/wrappers/anonymous.js +2 -3
- package/dist/auth/plugins/wrappers/api-key.js +2 -3
- package/dist/auth/plugins/wrappers/index.js +1 -2
- package/dist/auth/plugins/wrappers/jwt.js +2 -3
- package/dist/auth/plugins/wrappers/organization.js +2 -3
- package/dist/auth/plugins/wrappers/passkey.js +2 -3
- package/dist/auth/plugins/wrappers/phone-number.js +2 -3
- package/dist/auth/plugins/wrappers/sso.js +2 -3
- package/dist/auth/plugins/wrappers/two-factor.js +2 -3
- package/dist/auth/plugins/wrappers/username.js +2 -3
- package/dist/bin/build-config.js +2 -2
- package/dist/bin/cli.js +152 -259
- package/dist/bin/fixture.d.ts.map +1 -1
- package/dist/bin/fixture.js +55 -97
- package/dist/bin/hmr-hook-register.js +3 -3
- package/dist/bin/migrate-targets.d.ts +3 -0
- package/dist/bin/migrate-targets.d.ts.map +1 -0
- package/dist/bin/migrate-targets.js +11 -0
- package/dist/bin/test-command.js +25 -55
- package/dist/bin/ts-loader-register.js +5 -6
- package/dist/bin/ts-loader-registration.js +6 -13
- package/dist/cache/cache-manager.js +3 -4
- package/dist/cache/decorator.js +11 -21
- package/dist/cache/drivers.js +2 -3
- package/dist/cache/index.js +2 -3
- package/dist/cache/types.js +1 -1
- package/dist/cache-control/cache-control.js +21 -34
- package/dist/cache-control/types.js +1 -1
- package/dist/compress/compress.js +10 -10
- package/dist/compress/index.js +1 -2
- package/dist/compress/types.js +1 -1
- package/dist/cone/cone-generator.js +25 -63
- package/dist/database/_batch_update.js +26 -46
- package/dist/database/base-model.js +44 -97
- package/dist/database/base-model.types.js +1 -1
- package/dist/database/db.d.ts +8 -14
- package/dist/database/db.d.ts.map +1 -1
- package/dist/database/db.js +127 -72
- package/dist/database/knex.js +5 -8
- package/dist/database/puri-subset.types.js +1 -1
- package/dist/database/puri-wrapper.js +11 -15
- package/dist/database/puri.js +117 -234
- package/dist/database/puri.types.js +3 -4
- package/dist/database/transaction-context.js +4 -5
- package/dist/database/upsert-builder.js +109 -176
- package/dist/dict/en.d.ts +1 -0
- package/dist/dict/en.d.ts.map +1 -1
- package/dist/dict/en.js +4 -4
- package/dist/dict/index.js +2 -3
- package/dist/dict/ko.d.ts +1 -0
- package/dist/dict/ko.d.ts.map +1 -1
- package/dist/dict/ko.js +4 -4
- package/dist/dict/rc-keys.js +3 -4
- package/dist/dict/sd.js +8 -19
- package/dist/dict/sonamu-dictionary.js +141 -284
- package/dist/dict/types.js +1 -1
- package/dist/dict/utils.js +4 -5
- package/dist/entity/entity-manager.d.ts +2 -2
- package/dist/entity/entity-manager.js +34 -82
- package/dist/entity/entity-template-cone.js +33 -66
- package/dist/entity/entity.js +157 -311
- package/dist/env.d.ts +14 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +75 -0
- package/dist/exceptions/error-handler.js +2 -3
- package/dist/exceptions/so-exceptions.js +7 -5
- package/dist/filter/index.js +1 -2
- package/dist/filter/types.js +3 -4
- package/dist/filter/utils.js +21 -54
- package/dist/index.js +9 -8
- package/dist/logger/category.js +6 -12
- package/dist/logger/configure.js +23 -34
- package/dist/migration/code-generation.js +147 -315
- package/dist/migration/index-where-predicate.js +52 -144
- package/dist/migration/migration-set.js +19 -33
- package/dist/migration/migrator.d.ts +2 -0
- package/dist/migration/migrator.d.ts.map +1 -1
- package/dist/migration/migrator.js +69 -53
- package/dist/migration/postgresql-schema-reader.js +126 -225
- package/dist/migration/slack-confirm.d.ts +1 -0
- package/dist/migration/slack-confirm.d.ts.map +1 -1
- package/dist/migration/slack-confirm.js +28 -38
- package/dist/migration/types.js +1 -1
- package/dist/naite/messaging-types.js +1 -1
- package/dist/naite/naite-reporter.js +15 -32
- package/dist/naite/naite.js +43 -76
- package/dist/ssr/index.js +6 -9
- package/dist/ssr/registry.js +10 -18
- package/dist/ssr/renderer.js +10 -21
- package/dist/ssr/types.js +1 -1
- package/dist/storage/base-file.js +5 -10
- package/dist/storage/buffered-file.js +3 -4
- package/dist/storage/drivers.js +2 -3
- package/dist/storage/index.js +2 -3
- package/dist/storage/s3-driver.js +5 -9
- package/dist/storage/storage-manager.js +5 -5
- package/dist/storage/types.js +1 -1
- package/dist/storage/uploaded-file.js +4 -6
- package/dist/stream/index.js +1 -2
- package/dist/stream/sse.js +8 -13
- package/dist/stream/ws-audience-resolver.js +5 -5
- package/dist/stream/ws-audience.js +3 -4
- package/dist/stream/ws-cluster-bus.js +3 -4
- package/dist/stream/ws-core.js +1 -1
- package/dist/stream/ws-delivery.js +11 -25
- package/dist/stream/ws-local-connection-store.js +9 -18
- package/dist/stream/ws-presence-store.js +43 -97
- package/dist/stream/ws-registry.js +17 -22
- package/dist/stream/ws-telemetry-memory.js +38 -45
- package/dist/stream/ws-telemetry-trace.js +4 -6
- package/dist/stream/ws-telemetry.js +82 -135
- package/dist/stream/ws.js +47 -91
- package/dist/syncer/api-parser.js +81 -147
- package/dist/syncer/checksum.js +14 -32
- package/dist/syncer/code-generator.js +29 -47
- package/dist/syncer/entity-operations.js +17 -27
- package/dist/syncer/event-batcher.js +8 -15
- package/dist/syncer/file-patterns.js +3 -4
- package/dist/syncer/file-tracking.js +6 -10
- package/dist/syncer/index.js +1 -2
- package/dist/syncer/module-loader.js +10 -26
- package/dist/syncer/syncer-actions.js +19 -37
- package/dist/syncer/syncer.js +46 -98
- package/dist/syncer/watcher.js +12 -26
- package/dist/tasks/decorator.js +7 -11
- package/dist/tasks/step-wrapper.js +7 -8
- package/dist/tasks/workflow-manager.js +18 -25
- package/dist/template/entity-converter.js +40 -64
- package/dist/template/helpers.js +32 -63
- package/dist/template/implementations/entity.template.js +7 -11
- package/dist/template/implementations/entry-server.template.js +2 -3
- package/dist/template/implementations/generated.template.js +25 -51
- package/dist/template/implementations/generated_http.template.js +31 -58
- package/dist/template/implementations/generated_sso.template.js +45 -85
- package/dist/template/implementations/init_types.template.js +4 -7
- package/dist/template/implementations/model.template.js +5 -10
- package/dist/template/implementations/model_test.template.js +2 -3
- package/dist/template/implementations/queries.template.js +4 -7
- package/dist/template/implementations/sd.template.js +17 -35
- package/dist/template/implementations/services.template.js +18 -30
- package/dist/template/implementations/view_form.template.js +72 -125
- package/dist/template/implementations/view_id_all_select.template.js +2 -3
- package/dist/template/implementations/view_list.template.js +86 -143
- package/dist/template/implementations/view_search_input.template.js +2 -3
- package/dist/template/index.js +5 -8
- package/dist/template/template-manager.js +13 -26
- package/dist/template/template-types.js +2 -3
- package/dist/template/template.js +7 -11
- package/dist/template/zod-converter.js +173 -348
- package/dist/testing/_relation-graph.js +18 -37
- package/dist/testing/bootstrap.js +5 -8
- package/dist/testing/data-explorer.js +34 -78
- package/dist/testing/dev-test-routes.js +54 -60
- package/dist/testing/dev-vitest-manager.js +33 -84
- package/dist/testing/faker-mappings.js +3 -4
- package/dist/testing/fixture-generator.d.ts +2 -1
- package/dist/testing/fixture-generator.d.ts.map +1 -1
- package/dist/testing/fixture-generator.js +159 -321
- package/dist/testing/fixture-loader.js +2 -2
- package/dist/testing/fixture-manager.d.ts.map +1 -1
- package/dist/testing/fixture-manager.js +124 -227
- package/dist/testing/global-setup.d.ts.map +1 -1
- package/dist/testing/global-setup.js +29 -17
- package/dist/testing/index.d.ts +1 -0
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +5 -4
- package/dist/testing/naite-vitest-reporter.js +2 -3
- package/dist/testing/parallel-db-manager.js +5 -3
- package/dist/testing/vitest-helpers.d.ts.map +1 -1
- package/dist/testing/vitest-helpers.js +15 -12
- package/dist/types/types.d.ts +14 -14
- package/dist/types/types.js +27 -50
- package/dist/ui/ai-api.js +6 -11
- package/dist/ui/ai-client.js +86 -134
- package/dist/ui/api.js +100 -196
- package/dist/ui/cdd-service.js +78 -130
- package/dist/ui/cdd-types.js +1 -1
- package/dist/ui-web/assets/{index-Df8q-fhb.js → index-DFStGyd0.js} +49 -49
- package/dist/ui-web/assets/index-Dx4ap5i4.css +1 -0
- package/dist/ui-web/index.html +2 -2
- package/dist/utils/async-utils.js +13 -25
- package/dist/utils/class-name.js +3 -4
- package/dist/utils/console-util.js +11 -26
- package/dist/utils/controller.d.ts.map +1 -1
- package/dist/utils/controller.js +14 -12
- package/dist/utils/esm-utils.js +5 -8
- package/dist/utils/formatter.js +10 -22
- package/dist/utils/fs-utils.js +14 -25
- package/dist/utils/lodash-able.js +3 -4
- package/dist/utils/model.js +7 -14
- package/dist/utils/object-utils.js +41 -73
- package/dist/utils/path-utils.js +5 -9
- package/dist/utils/process-utils.js +4 -7
- package/dist/utils/sql-parser.js +6 -13
- package/dist/utils/type-utils.js +16 -26
- package/dist/utils/utils.js +18 -40
- package/dist/utils/zod-error.js +9 -16
- package/dist/vector/chunking.js +24 -37
- package/dist/vector/config.js +2 -2
- package/dist/vector/embedding.js +8 -19
- package/dist/vector/index.js +1 -2
- package/dist/vector/types.js +1 -1
- package/package.json +8 -8
- package/src/__tests__/env.test.ts +127 -0
- package/src/api/__tests__/config.test.ts +10 -1
- package/src/api/config.ts +4 -12
- package/src/api/sonamu.ts +28 -33
- package/src/auth/audit-log/builders.ts +3 -0
- package/src/auth/audit-log/client-ip.ts +38 -0
- package/src/auth/audit-log/events.ts +1 -0
- package/src/auth/audit-log-ingestor.ts +22 -15
- package/src/auth/better-auth-request.ts +20 -0
- package/src/bin/__tests__/migrate-targets.test.ts +28 -0
- package/src/bin/__tests__/test-command.test.ts +82 -1
- package/src/bin/cli.ts +9 -18
- package/src/bin/fixture.ts +5 -4
- package/src/bin/migrate-targets.ts +7 -0
- package/src/bin/test-command.ts +2 -2
- package/src/database/__tests__/db.test.ts +175 -0
- package/src/database/db.ts +193 -71
- package/src/dict/en.ts +2 -0
- package/src/dict/ko.ts +2 -0
- package/src/env.ts +123 -0
- package/src/migration/__tests__/migrator.test.ts +149 -0
- package/src/migration/migrator.ts +74 -17
- package/src/migration/slack-confirm.ts +21 -0
- package/src/skills/sonamu/database.md +1 -1
- package/src/skills/sonamu/puri.md +33 -0
- package/src/skills/sonamu/testing-devrunner.md +1 -1
- package/src/stream/ws-telemetry-memory.ts +2 -2
- package/src/syncer/checksum.ts +7 -12
- package/src/testing/fixture-generator.ts +2 -1
- package/src/testing/fixture-manager.ts +3 -4
- package/src/testing/global-setup.ts +42 -18
- package/src/testing/index.ts +1 -0
- package/src/testing/vitest-helpers.ts +14 -0
- package/src/utils/controller.ts +14 -7
- package/tsdown.api.config.ts +6 -0
- package/dist/_virtual/rolldown_runtime.js +0 -39
- package/dist/ui-web/assets/index-D4rYm-Xz.css +0 -1
package/dist/vector/embedding.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { Sonamu, init_sonamu } from "../api/sonamu.js";
|
|
2
2
|
import { DEFAULT_VECTOR_CONFIG } from "./config.js";
|
|
3
3
|
import { embedMany } from "ai";
|
|
4
|
-
|
|
5
4
|
//#region src/vector/embedding.ts
|
|
6
5
|
init_sonamu();
|
|
7
6
|
/**
|
|
@@ -40,9 +39,7 @@ var EmbeddingClass = class {
|
|
|
40
39
|
async getVoyageClient() {
|
|
41
40
|
const { VoyageAIClient } = await import("voyageai");
|
|
42
41
|
const apiKey = Sonamu.secrets?.voyage_api_key ?? process.env.VOYAGE_API_KEY;
|
|
43
|
-
if (!apiKey)
|
|
44
|
-
throw new Error("VOYAGE_API_KEY가 설정되지 않았습니다. 환경변수를 확인하세요.");
|
|
45
|
-
}
|
|
42
|
+
if (!apiKey) throw new Error("VOYAGE_API_KEY가 설정되지 않았습니다. 환경변수를 확인하세요.");
|
|
46
43
|
return new VoyageAIClient({ apiKey });
|
|
47
44
|
}
|
|
48
45
|
/**
|
|
@@ -51,9 +48,7 @@ var EmbeddingClass = class {
|
|
|
51
48
|
async getOpenAIProvider() {
|
|
52
49
|
const { createOpenAI } = await import("@ai-sdk/openai");
|
|
53
50
|
const apiKey = Sonamu.secrets?.openai_api_key ?? process.env.OPENAI_API_KEY;
|
|
54
|
-
if (!apiKey)
|
|
55
|
-
throw new Error("OPENAI_API_KEY가 설정되지 않았습니다. 환경변수를 확인하세요.");
|
|
56
|
-
}
|
|
51
|
+
if (!apiKey) throw new Error("OPENAI_API_KEY가 설정되지 않았습니다. 환경변수를 확인하세요.");
|
|
57
52
|
return createOpenAI({ apiKey });
|
|
58
53
|
}
|
|
59
54
|
/**
|
|
@@ -65,9 +60,7 @@ var EmbeddingClass = class {
|
|
|
65
60
|
*/
|
|
66
61
|
async embed(texts, provider, inputType = "document", onProgress) {
|
|
67
62
|
const maxBatchSize = provider === "voyage" ? this.config.voyage.batchSize : this.config.openai.batchSize;
|
|
68
|
-
if (texts.length <= maxBatchSize)
|
|
69
|
-
return provider === "voyage" ? await this.embedVoyage(texts, inputType) : await this.embedOpenAI(texts);
|
|
70
|
-
}
|
|
63
|
+
if (texts.length <= maxBatchSize) return provider === "voyage" ? await this.embedVoyage(texts, inputType) : await this.embedOpenAI(texts);
|
|
71
64
|
const batches = Array.from({ length: Math.ceil(texts.length / maxBatchSize) }, (_, i) => texts.slice(i * maxBatchSize, (i + 1) * maxBatchSize));
|
|
72
65
|
const results = await Promise.all(batches.map((batch) => provider === "voyage" ? this.embedVoyage(batch, inputType) : this.embedOpenAI(batch)));
|
|
73
66
|
onProgress?.(texts.length, texts.length);
|
|
@@ -77,8 +70,7 @@ var EmbeddingClass = class {
|
|
|
77
70
|
* 단일 텍스트 임베딩 (편의 메서드)
|
|
78
71
|
*/
|
|
79
72
|
async embedOne(text, provider, inputType = "document") {
|
|
80
|
-
|
|
81
|
-
return results[0];
|
|
73
|
+
return (await this.embed([text], provider, inputType))[0];
|
|
82
74
|
}
|
|
83
75
|
/**
|
|
84
76
|
* Voyage AI 임베딩
|
|
@@ -91,9 +83,7 @@ var EmbeddingClass = class {
|
|
|
91
83
|
model: voyageConfig.model,
|
|
92
84
|
inputType
|
|
93
85
|
});
|
|
94
|
-
if (!response.data)
|
|
95
|
-
throw new Error("Voyage API: 응답 데이터가 없습니다.");
|
|
96
|
-
}
|
|
86
|
+
if (!response.data) throw new Error("Voyage API: 응답 데이터가 없습니다.");
|
|
97
87
|
return response.data.map((item) => ({
|
|
98
88
|
embedding: item.embedding ?? [],
|
|
99
89
|
model: voyageConfig.model,
|
|
@@ -106,9 +96,8 @@ var EmbeddingClass = class {
|
|
|
106
96
|
async embedOpenAI(texts) {
|
|
107
97
|
const openai = await this.getOpenAIProvider();
|
|
108
98
|
const openaiConfig = this.config.openai;
|
|
109
|
-
const model = openai.embeddingModel(openaiConfig.model);
|
|
110
99
|
const { embeddings, usage } = await embedMany({
|
|
111
|
-
model,
|
|
100
|
+
model: openai.embeddingModel(openaiConfig.model),
|
|
112
101
|
values: texts
|
|
113
102
|
});
|
|
114
103
|
return embeddings.map((embedding) => ({
|
|
@@ -125,7 +114,7 @@ var EmbeddingClass = class {
|
|
|
125
114
|
}
|
|
126
115
|
};
|
|
127
116
|
const Embedding = new EmbeddingClass();
|
|
128
|
-
|
|
129
117
|
//#endregion
|
|
130
118
|
export { Embedding };
|
|
131
|
-
|
|
119
|
+
|
|
120
|
+
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZW1iZWRkaW5nLmpzIiwibmFtZXMiOltdLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy92ZWN0b3IvZW1iZWRkaW5nLnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IHR5cGUgT3BlbkFJUHJvdmlkZXIgfSBmcm9tIFwiQGFpLXNkay9vcGVuYWlcIjtcbmltcG9ydCB7IGVtYmVkTWFueSB9IGZyb20gXCJhaVwiO1xuaW1wb3J0IHsgdHlwZSBFbWJlZGRpbmdNb2RlbCB9IGZyb20gXCJhaVwiO1xuaW1wb3J0IHsgdHlwZSBWb3lhZ2VBSUNsaWVudCB9IGZyb20gXCJ2b3lhZ2VhaVwiO1xuXG5pbXBvcnQgeyBTb25hbXUgfSBmcm9tIFwiLi4vYXBpL3NvbmFtdVwiO1xuaW1wb3J0IHsgREVGQVVMVF9WRUNUT1JfQ09ORklHIH0gZnJvbSBcIi4vY29uZmlnXCI7XG5pbXBvcnQge1xuICB0eXBlIEVtYmVkZGluZ1Byb3ZpZGVyLFxuICB0eXBlIEVtYmVkZGluZ1Jlc3VsdCxcbiAgdHlwZSBQcm9ncmVzc0NhbGxiYWNrLFxuICB0eXBlIFZlY3RvckNvbmZpZyxcbiAgdHlwZSBWZWN0b3JJbnB1dFR5cGUsXG59IGZyb20gXCIuL3R5cGVzXCI7XG5cbi8qKlxuICog7J6E67Kg65SpIO2BtOudvOydtOyWuO2KuFxuICogVm95YWdlIEFJ7JmAIE9wZW5BSSDsnoTrsqDrlKnsnYQgU0RLIOuwqeyLneycvOuhnCDthrXtlakg7KeA7JuQXG4gKi9cbmNsYXNzIEVtYmVkZGluZ0NsYXNzIHtcbiAgcHJpdmF0ZSBjb25maWc6IFZlY3RvckNvbmZpZztcblxuICBjb25zdHJ1Y3Rvcihjb25maWc6IFBhcnRpYWw8VmVjdG9yQ29uZmlnPiA9IHt9KSB7XG4gICAgdGhpcy5jb25maWcgPSB7XG4gICAgICB2b3lhZ2U6IHsgLi4uREVGQVVMVF9WRUNUT1JfQ09ORklHLnZveWFnZSwgLi4uY29uZmlnLnZveWFnZSB9LFxuICAgICAgb3BlbmFpOiB7IC4uLkRFRkFVTFRfVkVDVE9SX0NPTkZJRy5vcGVuYWksIC4uLmNvbmZpZy5vcGVuYWkgfSxcbiAgICAgIGNodW5raW5nOiB7IC4uLkRFRkFVTFRfVkVDVE9SX0NPTkZJRy5jaHVua2luZywgLi4uY29uZmlnLmNodW5raW5nIH0sXG4gICAgICBzZWFyY2g6IHsgLi4uREVGQVVMVF9WRUNUT1JfQ09ORklHLnNlYXJjaCwgLi4uY29uZmlnLnNlYXJjaCB9LFxuICAgICAgcGd2ZWN0b3I6IHsgLi4uREVGQVVMVF9WRUNUT1JfQ09ORklHLnBndmVjdG9yLCAuLi5jb25maWcucGd2ZWN0b3IgfSxcbiAgICB9O1xuICB9XG5cbiAgLyoqXG4gICAqIFZveWFnZSBBSSDtgbTrnbzsnbTslrjtirgg7LSI6riw7ZmUXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIGdldFZveWFnZUNsaWVudCgpOiBQcm9taXNlPFZveWFnZUFJQ2xpZW50PiB7XG4gICAgY29uc3QgeyBWb3lhZ2VBSUNsaWVudCB9ID0gYXdhaXQgaW1wb3J0KFwidm95YWdlYWlcIik7XG4gICAgY29uc3QgYXBpS2V5ID0gU29uYW11LnNlY3JldHM/LnZveWFnZV9hcGlfa2V5ID8/IHByb2Nlc3MuZW52LlZPWUFHRV9BUElfS0VZO1xuICAgIGlmICghYXBpS2V5KSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXCJWT1lBR0VfQVBJX0tFWeqwgCDshKTsoJXrkJjsp4Ag7JWK7JWY7Iq164uI64ukLiDtmZjqsr3rs4DsiJjrpbwg7ZmV7J247ZWY7IS47JqULlwiKTtcbiAgICB9XG4gICAgcmV0dXJuIG5ldyBWb3lhZ2VBSUNsaWVudCh7IGFwaUtleSB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBPcGVuQUkgcHJvdmlkZXIg7IOd7ISxXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIGdldE9wZW5BSVByb3ZpZGVyKCk6IFByb21pc2U8T3BlbkFJUHJvdmlkZXI+IHtcbiAgICBjb25zdCB7IGNyZWF0ZU9wZW5BSSB9ID0gYXdhaXQgaW1wb3J0KFwiQGFpLXNkay9vcGVuYWlcIik7XG4gICAgY29uc3QgYXBpS2V5ID0gU29uYW11LnNlY3JldHM/Lm9wZW5haV9hcGlfa2V5ID8/IHByb2Nlc3MuZW52Lk9QRU5BSV9BUElfS0VZO1xuICAgIGlmICghYXBpS2V5KSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXCJPUEVOQUlfQVBJX0tFWeqwgCDshKTsoJXrkJjsp4Ag7JWK7JWY7Iq164uI64ukLiDtmZjqsr3rs4DsiJjrpbwg7ZmV7J247ZWY7IS47JqULlwiKTtcbiAgICB9XG4gICAgcmV0dXJuIGNyZWF0ZU9wZW5BSSh7IGFwaUtleSB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiDthY3siqTtirgg7J6E67Kg65SpIOyDneyEsVxuICAgKiBAcGFyYW0gdGV4dHMgLSDsnoTrsqDrlKntlaAg7YWN7Iqk7Yq4IOuwsOyXtCAoYmF0Y2hTaXpl7J207IOBIOyLnCDsnpDrj5kg67aE7ZWgKVxuICAgKiBAcGFyYW0gcHJvdmlkZXIgLSAndm95YWdlJyB8ICdvcGVuYWknXG4gICAqIEBwYXJhbSBpbnB1dFR5cGUgLSAnZG9jdW1lbnQnIHwgJ3F1ZXJ5JyAoVm95YWdlIEFJ66eMIO2VtOuLuSlcbiAgICogQHBhcmFtIG9uUHJvZ3Jlc3MgLSDsp4TtlonrpaAg7L2c67CxXG4gICAqL1xuICBhc3luYyBlbWJlZChcbiAgICB0ZXh0czogc3RyaW5nW10sXG4gICAgcHJvdmlkZXI6IEVtYmVkZGluZ1Byb3ZpZGVyLFxuICAgIGlucHV0VHlwZTogVmVjdG9ySW5wdXRUeXBlID0gXCJkb2N1bWVudFwiLFxuICAgIG9uUHJvZ3Jlc3M/OiBQcm9ncmVzc0NhbGxiYWNrLFxuICApOiBQcm9taXNlPEVtYmVkZGluZ1Jlc3VsdFtdPiB7XG4gICAgY29uc3QgbWF4QmF0Y2hTaXplID1cbiAgICAgIHByb3ZpZGVyID09PSBcInZveWFnZVwiID8gdGhpcy5jb25maWcudm95YWdlLmJhdGNoU2l6ZSA6IHRoaXMuY29uZmlnLm9wZW5haS5iYXRjaFNpemU7XG5cbiAgICAvLyBiYXRjaFNpemXsnbTtlZjrqbQg67CU66GcIO2YuOy2nFxuICAgIGlmICh0ZXh0cy5sZW5ndGggPD0gbWF4QmF0Y2hTaXplKSB7XG4gICAgICByZXR1cm4gcHJvdmlkZXIgPT09IFwidm95YWdlXCJcbiAgICAgICAgPyBhd2FpdCB0aGlzLmVtYmVkVm95YWdlKHRleHRzLCBpbnB1dFR5cGUpXG4gICAgICAgIDogYXdhaXQgdGhpcy5lbWJlZE9wZW5BSSh0ZXh0cyk7XG4gICAgfVxuXG4gICAgLy8gYmF0Y2hTaXpl7J207IOB7J2066m0IOyekOuPmeycvOuhnCDrgpjriKDshJwg7LKY66asXG4gICAgY29uc3QgYmF0Y2hlcyA9IEFycmF5LmZyb20oeyBsZW5ndGg6IE1hdGguY2VpbCh0ZXh0cy5sZW5ndGggLyBtYXhCYXRjaFNpemUpIH0sIChfLCBpKSA9PlxuICAgICAgdGV4dHMuc2xpY2UoaSAqIG1heEJhdGNoU2l6ZSwgKGkgKyAxKSAqIG1heEJhdGNoU2l6ZSksXG4gICAgKTtcblxuICAgIGNvbnN0IHJlc3VsdHMgPSBhd2FpdCBQcm9taXNlLmFsbChcbiAgICAgIGJhdGNoZXMubWFwKChiYXRjaCkgPT5cbiAgICAgICAgcHJvdmlkZXIgPT09IFwidm95YWdlXCIgPyB0aGlzLmVtYmVkVm95YWdlKGJhdGNoLCBpbnB1dFR5cGUpIDogdGhpcy5lbWJlZE9wZW5BSShiYXRjaCksXG4gICAgICApLFxuICAgICk7XG5cbiAgICBvblByb2dyZXNzPy4odGV4dHMubGVuZ3RoLCB0ZXh0cy5sZW5ndGgpO1xuICAgIHJldHVybiByZXN1bHRzLmZsYXQoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiDri6jsnbwg7YWN7Iqk7Yq4IOyehOuyoOuUqSAo7Y647J2YIOuplOyEnOuTnClcbiAgICovXG4gIGFzeW5jIGVtYmVkT25lKFxuICAgIHRleHQ6IHN0cmluZyxcbiAgICBwcm92aWRlcjogRW1iZWRkaW5nUHJvdmlkZXIsXG4gICAgaW5wdXRUeXBlOiBWZWN0b3JJbnB1dFR5cGUgPSBcImRvY3VtZW50XCIsXG4gICk6IFByb21pc2U8RW1iZWRkaW5nUmVzdWx0PiB7XG4gICAgY29uc3QgcmVzdWx0cyA9IGF3YWl0IHRoaXMuZW1iZWQoW3RleHRdLCBwcm92aWRlciwgaW5wdXRUeXBlKTtcbiAgICByZXR1cm4gcmVzdWx0c1swXTtcbiAgfVxuXG4gIC8qKlxuICAgKiBWb3lhZ2UgQUkg7J6E67Kg65SpXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIGVtYmVkVm95YWdlKFxuICAgIHRleHRzOiBzdHJpbmdbXSxcbiAgICBpbnB1dFR5cGU6IFZlY3RvcklucHV0VHlwZSxcbiAgKTogUHJvbWlzZTxFbWJlZGRpbmdSZXN1bHRbXT4ge1xuICAgIGNvbnN0IGNsaWVudCA9IGF3YWl0IHRoaXMuZ2V0Vm95YWdlQ2xpZW50KCk7XG4gICAgY29uc3Qgdm95YWdlQ29uZmlnID0gdGhpcy5jb25maWcudm95YWdlO1xuXG4gICAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBjbGllbnQuZW1iZWQoe1xuICAgICAgaW5wdXQ6IHRleHRzLFxuICAgICAgbW9kZWw6IHZveWFnZUNvbmZpZy5tb2RlbCxcbiAgICAgIGlucHV0VHlwZTogaW5wdXRUeXBlLFxuICAgIH0pO1xuICAgIGlmICghcmVzcG9uc2UuZGF0YSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFwiVm95YWdlIEFQSTog7J2R64u1IOuNsOydtO2EsOqwgCDsl4bsirXri4jri6QuXCIpO1xuICAgIH1cblxuICAgIHJldHVybiByZXNwb25zZS5kYXRhLm1hcCgoaXRlbSkgPT4gKHtcbiAgICAgIGVtYmVkZGluZzogaXRlbS5lbWJlZGRpbmcgPz8gW10sXG4gICAgICBtb2RlbDogdm95YWdlQ29uZmlnLm1vZGVsLFxuICAgICAgdG9rZW5Db3VudDogcmVzcG9uc2UudXNhZ2U/LnRvdGFsVG9rZW5zID8/IDAsXG4gICAgfSkpO1xuICB9XG5cbiAgLyoqXG4gICAqIE9wZW5BSSDsnoTrsqDrlKlcbiAgICovXG4gIHByaXZhdGUgYXN5bmMgZW1iZWRPcGVuQUkodGV4dHM6IHN0cmluZ1tdKTogUHJvbWlzZTxFbWJlZGRpbmdSZXN1bHRbXT4ge1xuICAgIGNvbnN0IG9wZW5haSA9IGF3YWl0IHRoaXMuZ2V0T3BlbkFJUHJvdmlkZXIoKTtcbiAgICBjb25zdCBvcGVuYWlDb25maWcgPSB0aGlzLmNvbmZpZy5vcGVuYWk7XG4gICAgY29uc3QgbW9kZWwgPSBvcGVuYWkuZW1iZWRkaW5nTW9kZWwob3BlbmFpQ29uZmlnLm1vZGVsKTtcblxuICAgIGNvbnN0IHsgZW1iZWRkaW5ncywgdXNhZ2UgfSA9IGF3YWl0IGVtYmVkTWFueSh7XG4gICAgICBtb2RlbDogbW9kZWwgYXMgRW1iZWRkaW5nTW9kZWwsXG4gICAgICB2YWx1ZXM6IHRleHRzLFxuICAgIH0pO1xuXG4gICAgcmV0dXJuIGVtYmVkZGluZ3MubWFwKChlbWJlZGRpbmcpID0+ICh7XG4gICAgICBlbWJlZGRpbmcsXG4gICAgICBtb2RlbDogb3BlbmFpQ29uZmlnLm1vZGVsLFxuICAgICAgdG9rZW5Db3VudDogdXNhZ2U/LnRva2VucyA/PyAwLFxuICAgIH0pKTtcbiAgfVxuXG4gIC8qKlxuICAgKiDsnoTrsqDrlKkgcHJvdmlkZXLsnZgg7LCo7JuQIOyImCDrsJjtmZhcbiAgICovXG4gIGdldERpbWVuc2lvbnMocHJvdmlkZXI6IEVtYmVkZGluZ1Byb3ZpZGVyKTogbnVtYmVyIHtcbiAgICByZXR1cm4gcHJvdmlkZXIgPT09IFwidm95YWdlXCIgPyB0aGlzLmNvbmZpZy52b3lhZ2UuZGltZW5zaW9ucyA6IHRoaXMuY29uZmlnLm9wZW5haS5kaW1lbnNpb25zO1xuICB9XG59XG5leHBvcnQgY29uc3QgRW1iZWRkaW5nID0gbmV3IEVtYmVkZGluZ0NsYXNzKCk7XG4iXSwibWFwcGluZ3MiOiI7Ozs7WUFLc0M7Ozs7O0FBY3RDLElBQU0saUJBQU4sTUFBcUI7Q0FDbkI7Q0FFQSxZQUFZLFNBQWdDLENBQUMsR0FBRztFQUM5QyxLQUFLLFNBQVM7R0FDWixRQUFRO0lBQUUsR0FBRyxzQkFBc0I7SUFBUSxHQUFHLE9BQU87R0FBTztHQUM1RCxRQUFRO0lBQUUsR0FBRyxzQkFBc0I7SUFBUSxHQUFHLE9BQU87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
|
package/dist/vector/index.js
CHANGED
|
@@ -2,5 +2,4 @@ import { DEFAULT_VECTOR_CONFIG, createVectorConfig } from "./config.js";
|
|
|
2
2
|
import { Chunking } from "./chunking.js";
|
|
3
3
|
import { Embedding } from "./embedding.js";
|
|
4
4
|
import "./types.js";
|
|
5
|
-
|
|
6
|
-
export { Chunking, DEFAULT_VECTOR_CONFIG, Embedding, createVectorConfig };
|
|
5
|
+
export { Chunking, DEFAULT_VECTOR_CONFIG, Embedding, createVectorConfig };
|
package/dist/vector/types.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sonamu",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.1",
|
|
4
4
|
"description": "Sonamu — TypeScript Fullstack API Framework",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"framework",
|
|
@@ -120,19 +120,19 @@
|
|
|
120
120
|
"minimatch": "^10.0.3",
|
|
121
121
|
"node-cron": "^4.2.1",
|
|
122
122
|
"node-sql-parser": "^5.2.0",
|
|
123
|
-
"oxfmt": "^0.
|
|
124
|
-
"oxlint": "^1.
|
|
123
|
+
"oxfmt": "^0.55.0",
|
|
124
|
+
"oxlint": "^1.70.0",
|
|
125
125
|
"pg": "^8.16.3",
|
|
126
126
|
"prompts": "^2.4.2",
|
|
127
127
|
"qs": "^6.14.1",
|
|
128
128
|
"radashi": "^12.2.0",
|
|
129
129
|
"tsicli": "^1.0.5",
|
|
130
|
-
"vite": "8.0.
|
|
131
|
-
"vitest": "^4.1.
|
|
130
|
+
"vite": "8.0.16",
|
|
131
|
+
"vitest": "^4.1.9",
|
|
132
|
+
"@sonamu-kit/hmr-hook": "^0.5.1",
|
|
132
133
|
"@sonamu-kit/hmr-runner": "^0.2.0",
|
|
133
134
|
"@sonamu-kit/tasks": "^0.3.1",
|
|
134
|
-
"@sonamu-kit/ts-loader": "^2.2.0"
|
|
135
|
-
"@sonamu-kit/hmr-hook": "^0.5.1"
|
|
135
|
+
"@sonamu-kit/ts-loader": "^2.2.0"
|
|
136
136
|
},
|
|
137
137
|
"devDependencies": {
|
|
138
138
|
"@types/bcrypt": "^6",
|
|
@@ -143,7 +143,7 @@
|
|
|
143
143
|
"@types/prompts": "^2.0.14",
|
|
144
144
|
"@types/qs": "^6.14.0",
|
|
145
145
|
"nodemon": "^3.1.10",
|
|
146
|
-
"tsdown": "^0.
|
|
146
|
+
"tsdown": "^0.22.3",
|
|
147
147
|
"typescript": "^6.0.3"
|
|
148
148
|
},
|
|
149
149
|
"peerDependencies": {
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
6
|
+
|
|
7
|
+
import { readAllEnvironmentSnapshots, readEnvironmentSnapshot } from "../env";
|
|
8
|
+
|
|
9
|
+
describe("readAllEnvironmentSnapshots", () => {
|
|
10
|
+
const tempRoots: string[] = [];
|
|
11
|
+
|
|
12
|
+
afterEach(async () => {
|
|
13
|
+
await Promise.all(
|
|
14
|
+
tempRoots.splice(0).map((rootPath) => rm(rootPath, { recursive: true, force: true })),
|
|
15
|
+
);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("does not reuse the current environment dotenv values as other environments' base values", async () => {
|
|
19
|
+
const rootPath = await mkdtemp(path.join(os.tmpdir(), "sonamu-env-test-"));
|
|
20
|
+
tempRoots.push(rootPath);
|
|
21
|
+
|
|
22
|
+
await mkdir(rootPath, { recursive: true });
|
|
23
|
+
await writeFile(path.join(rootPath, ".env"), "SONAMU_DB_USER=base_user\n");
|
|
24
|
+
await writeFile(
|
|
25
|
+
path.join(rootPath, ".env.development"),
|
|
26
|
+
"SONAMU_DB_HOST=development.example.com\nSONAMU_DB_PASSWORD=development_password\n",
|
|
27
|
+
);
|
|
28
|
+
await writeFile(path.join(rootPath, ".env.staging"), "SONAMU_DB_HOST=staging.example.com\n");
|
|
29
|
+
|
|
30
|
+
const snapshots = readAllEnvironmentSnapshots(rootPath, {
|
|
31
|
+
NODE_ENV: "development",
|
|
32
|
+
SONAMU_DB_PASSWORD: "shell_password",
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
expect(snapshots.development.SONAMU_DB_HOST).toBe("development.example.com");
|
|
36
|
+
expect(snapshots.development.SONAMU_DB_PASSWORD).toBe("shell_password");
|
|
37
|
+
expect(snapshots.staging.SONAMU_DB_HOST).toBe("staging.example.com");
|
|
38
|
+
expect(snapshots.staging.SONAMU_DB_PASSWORD).toBe("shell_password");
|
|
39
|
+
expect(snapshots.staging.SONAMU_DB_USER).toBe("base_user");
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("throws when both common and environment dotenv files are missing", async () => {
|
|
43
|
+
const rootPath = await mkdtemp(path.join(os.tmpdir(), "sonamu-env-test-"));
|
|
44
|
+
tempRoots.push(rootPath);
|
|
45
|
+
|
|
46
|
+
await writeFile(path.join(rootPath, ".env.local"), "SONAMU_DB_HOST=local.example.com\n");
|
|
47
|
+
|
|
48
|
+
expect(() => readAllEnvironmentSnapshots(rootPath)).toThrow(
|
|
49
|
+
/Missing Sonamu dotenv file.*\.env.*\.env\.test/,
|
|
50
|
+
);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("allows environment snapshots when only the common dotenv file exists", async () => {
|
|
54
|
+
const rootPath = await mkdtemp(path.join(os.tmpdir(), "sonamu-env-test-"));
|
|
55
|
+
tempRoots.push(rootPath);
|
|
56
|
+
|
|
57
|
+
await writeFile(path.join(rootPath, ".env"), "SONAMU_DB_HOST=common.example.com\n");
|
|
58
|
+
|
|
59
|
+
const snapshots = readAllEnvironmentSnapshots(rootPath);
|
|
60
|
+
|
|
61
|
+
expect(snapshots.development.SONAMU_DB_HOST).toBe("common.example.com");
|
|
62
|
+
expect(snapshots.production.SONAMU_DB_HOST).toBe("common.example.com");
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("allows an environment snapshot when only the environment dotenv file exists", async () => {
|
|
66
|
+
const rootPath = await mkdtemp(path.join(os.tmpdir(), "sonamu-env-test-"));
|
|
67
|
+
tempRoots.push(rootPath);
|
|
68
|
+
|
|
69
|
+
await writeFile(path.join(rootPath, ".env.development"), "SONAMU_DB_HOST=dev.example.com\n");
|
|
70
|
+
|
|
71
|
+
const snapshot = readEnvironmentSnapshot(rootPath, "development");
|
|
72
|
+
|
|
73
|
+
expect(snapshot.SONAMU_DB_HOST).toBe("dev.example.com");
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("keeps exported environment variables over dotenv file values", async () => {
|
|
77
|
+
const rootPath = await mkdtemp(path.join(os.tmpdir(), "sonamu-env-test-"));
|
|
78
|
+
tempRoots.push(rootPath);
|
|
79
|
+
|
|
80
|
+
await writeFile(
|
|
81
|
+
path.join(rootPath, ".env.development"),
|
|
82
|
+
"SONAMU_DB_HOST=file-host\nSONAMU_DB_PASSWORD=file-password\n",
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
const snapshot = readEnvironmentSnapshot(rootPath, "development", {
|
|
86
|
+
SONAMU_DB_HOST: "runtime-host",
|
|
87
|
+
SONAMU_DB_PASSWORD: "runtime-password",
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
expect(snapshot.SONAMU_DB_HOST).toBe("runtime-host");
|
|
91
|
+
expect(snapshot.SONAMU_DB_PASSWORD).toBe("runtime-password");
|
|
92
|
+
expect(snapshot.NODE_ENV).toBe("development");
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("lets environment dotenv values override common dotenv values", async () => {
|
|
96
|
+
const rootPath = await mkdtemp(path.join(os.tmpdir(), "sonamu-env-test-"));
|
|
97
|
+
tempRoots.push(rootPath);
|
|
98
|
+
|
|
99
|
+
await writeFile(path.join(rootPath, ".env"), "SONAMU_DB_HOST=common.example.com\n");
|
|
100
|
+
await writeFile(path.join(rootPath, ".env.production"), "SONAMU_DB_HOST=prod.example.com\n");
|
|
101
|
+
|
|
102
|
+
const snapshot = readEnvironmentSnapshot(rootPath, "production");
|
|
103
|
+
|
|
104
|
+
expect(snapshot.SONAMU_DB_HOST).toBe("prod.example.com");
|
|
105
|
+
expect(snapshot.NODE_ENV).toBe("production");
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it("does not let preloaded common dotenv values override environment dotenv values", async () => {
|
|
109
|
+
const rootPath = await mkdtemp(path.join(os.tmpdir(), "sonamu-env-test-"));
|
|
110
|
+
tempRoots.push(rootPath);
|
|
111
|
+
|
|
112
|
+
await writeFile(
|
|
113
|
+
path.join(rootPath, ".env"),
|
|
114
|
+
"SONAMU_DB_HOST=common.example.com\nSONAMU_DB_USER=common_user\n",
|
|
115
|
+
);
|
|
116
|
+
await writeFile(path.join(rootPath, ".env.production"), "SONAMU_DB_HOST=prod.example.com\n");
|
|
117
|
+
|
|
118
|
+
const snapshot = readEnvironmentSnapshot(rootPath, "production", {
|
|
119
|
+
SONAMU_DB_HOST: "common.example.com",
|
|
120
|
+
SONAMU_DB_USER: "shell_user",
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
expect(snapshot.SONAMU_DB_HOST).toBe("prod.example.com");
|
|
124
|
+
expect(snapshot.SONAMU_DB_USER).toBe("shell_user");
|
|
125
|
+
expect(snapshot.NODE_ENV).toBe("production");
|
|
126
|
+
});
|
|
127
|
+
});
|
|
@@ -21,7 +21,9 @@ function resetRegisterState() {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
async function createTempRoot(): Promise<string> {
|
|
24
|
-
|
|
24
|
+
const rootPath = await mkdtemp(path.join(os.tmpdir(), "sonamu-config-test-"));
|
|
25
|
+
await writeFile(path.join(rootPath, ".env"), "SONAMU_DB_HOST=localhost\n");
|
|
26
|
+
return rootPath;
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
async function writeSourceFixture(rootPath: string): Promise<void> {
|
|
@@ -136,6 +138,7 @@ describe("loadConfig", () => {
|
|
|
136
138
|
const tempRoots: string[] = [];
|
|
137
139
|
const originalHot = process.env.HOT;
|
|
138
140
|
const originalVitest = process.env.VITEST;
|
|
141
|
+
const originalNodeEnv = process.env.NODE_ENV;
|
|
139
142
|
|
|
140
143
|
beforeEach(() => {
|
|
141
144
|
vi.resetModules();
|
|
@@ -162,6 +165,12 @@ describe("loadConfig", () => {
|
|
|
162
165
|
process.env.VITEST = originalVitest;
|
|
163
166
|
}
|
|
164
167
|
|
|
168
|
+
if (originalNodeEnv === undefined) {
|
|
169
|
+
delete process.env.NODE_ENV;
|
|
170
|
+
} else {
|
|
171
|
+
process.env.NODE_ENV = originalNodeEnv;
|
|
172
|
+
}
|
|
173
|
+
|
|
165
174
|
await Promise.all(
|
|
166
175
|
tempRoots.splice(0).map((rootPath) => rm(rootPath, { recursive: true, force: true })),
|
|
167
176
|
);
|
package/src/api/config.ts
CHANGED
|
@@ -17,6 +17,7 @@ import { type Knex } from "knex";
|
|
|
17
17
|
|
|
18
18
|
import { type CacheConfig } from "../cache/types";
|
|
19
19
|
import { type SonamuDBConfig } from "../database/db";
|
|
20
|
+
import { applyCurrentSnapshotToProcessEnv } from "../env";
|
|
20
21
|
import { type SonamuLoggingOptions } from "../logger/configure";
|
|
21
22
|
import { type StorageConfig } from "../storage/types";
|
|
22
23
|
import { type WebSocketRuntimeOptions } from "../stream/ws";
|
|
@@ -94,19 +95,8 @@ export type SonamuConfig<TSinkId extends string = string, TFilterId extends stri
|
|
|
94
95
|
database: {
|
|
95
96
|
// 데이터베이스(pg는 pg 모듈, pgnative는 pg-native 모듈의 설치가 필요합니다.)
|
|
96
97
|
database?: "pg" | "pgnative";
|
|
97
|
-
// 기본 데이터베이스 이름
|
|
98
|
-
name: string;
|
|
99
98
|
// 모든 환경에 적용될 기본 Knex 옵션
|
|
100
|
-
defaultOptions
|
|
101
|
-
// 환경별 설정
|
|
102
|
-
environments?: {
|
|
103
|
-
development?: DatabaseConfig;
|
|
104
|
-
development_slave?: DatabaseConfig;
|
|
105
|
-
production?: DatabaseConfig;
|
|
106
|
-
production_slave?: DatabaseConfig;
|
|
107
|
-
fixture?: DatabaseConfig;
|
|
108
|
-
test?: DatabaseConfig;
|
|
109
|
-
};
|
|
99
|
+
defaultOptions?: DatabaseConfig;
|
|
110
100
|
};
|
|
111
101
|
|
|
112
102
|
logging?: false | SonamuLoggingOptions<TSinkId, TFilterId>;
|
|
@@ -319,6 +309,8 @@ export function defineConfig(config: Executable<SonamuConfig>): Promise<SonamuCo
|
|
|
319
309
|
* @returns
|
|
320
310
|
*/
|
|
321
311
|
export async function loadConfig(rootPath: string): Promise<SonamuConfig> {
|
|
312
|
+
applyCurrentSnapshotToProcessEnv(rootPath);
|
|
313
|
+
|
|
322
314
|
const shouldLoadSourceConfig = process.env.HOT === "yes" || process.env.VITEST === "true";
|
|
323
315
|
const configPath = shouldLoadSourceConfig
|
|
324
316
|
? `${rootPath}/src/sonamu.config.ts`
|
package/src/api/sonamu.ts
CHANGED
|
@@ -15,6 +15,7 @@ import { type WebSocket } from "ws";
|
|
|
15
15
|
import { type ZodObject } from "zod";
|
|
16
16
|
|
|
17
17
|
import { BASE_FIELD_MAPPINGS } from "../auth/better-auth-entities";
|
|
18
|
+
import { createBetterAuthRequest } from "../auth/better-auth-request";
|
|
18
19
|
import { applyCacheHeaders, CachePresets } from "../cache-control/cache-control";
|
|
19
20
|
import { type CacheControlConfig, type CacheControlRequest } from "../cache-control/types";
|
|
20
21
|
import { type CacheConfig, type CacheManager } from "../cache/types";
|
|
@@ -24,6 +25,7 @@ import { DB } from "../database/db";
|
|
|
24
25
|
import { type SonamuDBConfig } from "../database/db";
|
|
25
26
|
import { SD, setSDConfig } from "../dict/sd";
|
|
26
27
|
import { type LocalizedString } from "../dict/types";
|
|
28
|
+
import { getSonamuEnvironment, readAllEnvironmentSnapshots } from "../env";
|
|
27
29
|
import { NotFoundException } from "../exceptions/so-exceptions";
|
|
28
30
|
import { BufferedFile } from "../storage/buffered-file";
|
|
29
31
|
import { type StorageManager } from "../storage/storage-manager";
|
|
@@ -231,6 +233,7 @@ class SonamuClass {
|
|
|
231
233
|
// API 루트 패스
|
|
232
234
|
const { findApiRootPath } = await import("../utils/utils");
|
|
233
235
|
this.apiRootPath = apiRootPath ?? findApiRootPath();
|
|
236
|
+
const baseEnvBeforeConfigLoad = { ...process.env };
|
|
234
237
|
|
|
235
238
|
// 설정을 로딩하는 것부터 시작
|
|
236
239
|
const configStart = performance.now();
|
|
@@ -240,6 +243,7 @@ class SonamuClass {
|
|
|
240
243
|
setSDConfig(this.config.i18n);
|
|
241
244
|
// sonamu.config.ts 기본값 설정
|
|
242
245
|
this.config.database.database = this.config.database.database ?? "pg";
|
|
246
|
+
this.config.database.defaultOptions = this.config.database.defaultOptions ?? {};
|
|
243
247
|
this.config.database.defaultOptions.client = this.config.database.database ?? "pg";
|
|
244
248
|
|
|
245
249
|
// 로깅 설정
|
|
@@ -252,7 +256,15 @@ class SonamuClass {
|
|
|
252
256
|
|
|
253
257
|
// DB 로드
|
|
254
258
|
const { DB } = await import("../database/db");
|
|
255
|
-
|
|
259
|
+
const { isLocal: isLocalEnvironment } = await import("../utils/controller");
|
|
260
|
+
const environmentSnapshots = isLocalEnvironment()
|
|
261
|
+
? readAllEnvironmentSnapshots(this.apiRootPath, baseEnvBeforeConfigLoad)
|
|
262
|
+
: undefined;
|
|
263
|
+
this.dbConfig = DB.generateDBConfig(
|
|
264
|
+
this.config.database,
|
|
265
|
+
this.config.projectName,
|
|
266
|
+
environmentSnapshots,
|
|
267
|
+
);
|
|
256
268
|
DB.setConfig(this.dbConfig);
|
|
257
269
|
|
|
258
270
|
// Entity 로드
|
|
@@ -1104,12 +1116,6 @@ class SonamuClass {
|
|
|
1104
1116
|
traceContext = this.websocketRuntime.telemetryController.createConnectionContext({
|
|
1105
1117
|
headers: request.headers,
|
|
1106
1118
|
});
|
|
1107
|
-
runGuards({
|
|
1108
|
-
guards: api.options.guards,
|
|
1109
|
-
config,
|
|
1110
|
-
request,
|
|
1111
|
-
api,
|
|
1112
|
-
});
|
|
1113
1119
|
|
|
1114
1120
|
preRegisterPhase = "query";
|
|
1115
1121
|
const reqBody = await this.parseWebSocketRequestParams(api, request);
|
|
@@ -1128,6 +1134,16 @@ class SonamuClass {
|
|
|
1128
1134
|
|
|
1129
1135
|
const scopedWs = this.createScopedWebSocketConnection(rawWs, () => wsContext);
|
|
1130
1136
|
wsContext = await this.createWebSocketContext(config, request, scopedWs);
|
|
1137
|
+
|
|
1138
|
+
await this.asyncLocalStorage.run({ context: wsContext }, async () => {
|
|
1139
|
+
runGuards({
|
|
1140
|
+
guards: api.options.guards,
|
|
1141
|
+
config,
|
|
1142
|
+
request,
|
|
1143
|
+
api,
|
|
1144
|
+
});
|
|
1145
|
+
});
|
|
1146
|
+
|
|
1131
1147
|
this.websocketRuntime.activateConnection(rawWs.id);
|
|
1132
1148
|
preRegisterPhase = "handler";
|
|
1133
1149
|
|
|
@@ -1790,29 +1806,9 @@ class SonamuClass {
|
|
|
1790
1806
|
method: ["GET", "POST"],
|
|
1791
1807
|
url: `${basePath}/*`,
|
|
1792
1808
|
handler: async (request, reply) => {
|
|
1793
|
-
const
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
// IP 헤더 fallback: 프록시가 표준 IP 헤더를 주입하지 않는 환경에서도
|
|
1797
|
-
// better-auth/infra의 getClientIpFromRequest()가 IP를 인식할 수 있도록
|
|
1798
|
-
// Fastify가 resolve한 request.ip를 x-real-ip로 주입한다.
|
|
1799
|
-
const IP_HEADERS = [
|
|
1800
|
-
"cf-connecting-ip",
|
|
1801
|
-
"x-forwarded-for",
|
|
1802
|
-
"x-real-ip",
|
|
1803
|
-
"x-vercel-forwarded-for",
|
|
1804
|
-
];
|
|
1805
|
-
if (request.ip && !IP_HEADERS.some((h) => headers.has(h))) {
|
|
1806
|
-
headers.set("x-real-ip", request.ip);
|
|
1807
|
-
}
|
|
1808
|
-
|
|
1809
|
-
const req = new Request(url.toString(), {
|
|
1810
|
-
method: request.method,
|
|
1811
|
-
headers,
|
|
1812
|
-
...(request.body ? { body: JSON.stringify(request.body) } : {}),
|
|
1813
|
-
});
|
|
1814
|
-
|
|
1815
|
-
const response = await this.auth.handler(req);
|
|
1809
|
+
const response = await this.auth.handler(
|
|
1810
|
+
createBetterAuthRequest(request, options.advanced?.ipAddress?.ipAddressHeaders),
|
|
1811
|
+
);
|
|
1816
1812
|
|
|
1817
1813
|
reply.status(response.status);
|
|
1818
1814
|
response.headers.forEach((value: string, key: string) => {
|
|
@@ -1825,8 +1821,7 @@ class SonamuClass {
|
|
|
1825
1821
|
|
|
1826
1822
|
private async printStartupSummary() {
|
|
1827
1823
|
const chalk = (await import("chalk")).default;
|
|
1828
|
-
const
|
|
1829
|
-
const activePreset = env === "production" ? "production_master" : "development_master";
|
|
1824
|
+
const activePreset = getSonamuEnvironment();
|
|
1830
1825
|
|
|
1831
1826
|
const dim = (msg: string) => console.log(chalk.dim(`✓ ${msg}`));
|
|
1832
1827
|
const green = (msg: string) => console.log(chalk.green(`✓ ${msg}`));
|
|
@@ -1843,7 +1838,7 @@ class SonamuClass {
|
|
|
1843
1838
|
| { host?: string; port?: number; database?: string }
|
|
1844
1839
|
| undefined;
|
|
1845
1840
|
const host = conn?.host ?? "localhost";
|
|
1846
|
-
const addr = `@ ${host}:${conn?.port ?? 5432}/${conn?.database ??
|
|
1841
|
+
const addr = `@ ${host}:${conn?.port ?? 5432}/${conn?.database ?? "(unknown)"}`;
|
|
1847
1842
|
const padded = name.padEnd(maxLen);
|
|
1848
1843
|
const remoteTag = isLocal() && !isLocalHost(host) ? chalk.yellow(` \u26a0 remote`) : "";
|
|
1849
1844
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
|
|
1
3
|
import {
|
|
2
4
|
type AccountSnapshot,
|
|
3
5
|
type AuditLogEvent,
|
|
@@ -24,6 +26,7 @@ const createEvent = (
|
|
|
24
26
|
location?: BuilderLocation,
|
|
25
27
|
): AuditLogEvent => ({
|
|
26
28
|
...base,
|
|
29
|
+
eventId: randomUUID(),
|
|
27
30
|
eventData: {
|
|
28
31
|
...data,
|
|
29
32
|
triggeredBy: trigger.triggeredBy,
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { isIP } from "node:net";
|
|
2
|
+
|
|
3
|
+
const CLIENT_IP_HEADERS = [
|
|
4
|
+
"cf-connecting-ip",
|
|
5
|
+
"x-forwarded-for",
|
|
6
|
+
"x-real-ip",
|
|
7
|
+
"x-vercel-forwarded-for",
|
|
8
|
+
] as const;
|
|
9
|
+
|
|
10
|
+
const HEADER_NAME_PATTERN = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
|
|
11
|
+
|
|
12
|
+
export function replaceClientIpHeaders(
|
|
13
|
+
headers: Headers,
|
|
14
|
+
clientIp: string,
|
|
15
|
+
configuredHeaders: readonly string[] = [],
|
|
16
|
+
): void {
|
|
17
|
+
const candidates = new Set(
|
|
18
|
+
[...CLIENT_IP_HEADERS, ...configuredHeaders]
|
|
19
|
+
.map((header) => header.trim().toLowerCase())
|
|
20
|
+
.filter((header) => HEADER_NAME_PATTERN.test(header)),
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
for (const header of candidates) {
|
|
24
|
+
headers.delete(header);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
for (const header of candidates) {
|
|
28
|
+
headers.set(header, clientIp);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function getValidClientIp(value: unknown): string | null {
|
|
33
|
+
if (typeof value !== "string" || value.length > 45 || isIP(value) === 0) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return value;
|
|
38
|
+
}
|
|
@@ -2,6 +2,7 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
|
|
3
3
|
import { type Knex } from "knex";
|
|
4
4
|
|
|
5
|
+
import { getValidClientIp } from "./audit-log/client-ip";
|
|
5
6
|
import { type AuditLogEvent } from "./audit-log/events";
|
|
6
7
|
|
|
7
8
|
const AUDIT_EVENT_SOURCE = "better_auth";
|
|
@@ -137,6 +138,10 @@ function computeDedupeKey(parts: {
|
|
|
137
138
|
return createHash("sha256").update(raw).digest("hex");
|
|
138
139
|
}
|
|
139
140
|
|
|
141
|
+
function computeEventIdDedupeKey(source: string, eventId: string): string {
|
|
142
|
+
return createHash("sha256").update(`${source}|${eventId}`).digest("hex");
|
|
143
|
+
}
|
|
144
|
+
|
|
140
145
|
/**
|
|
141
146
|
* sonamuAuditLog 플러그인이 구성한 AuditLogEvent를 audit_events 테이블에 적재합니다.
|
|
142
147
|
* ON CONFLICT (dedupe_key) DO NOTHING으로 중복을 silent 무시합니다.
|
|
@@ -172,26 +177,28 @@ export async function ingestAuditEvent(db: Knex, event: AuditLogEvent): Promise<
|
|
|
172
177
|
const trigger_context = pickString(eventData, "triggerContext");
|
|
173
178
|
const user_agent = pickString(eventData, "userAgent");
|
|
174
179
|
|
|
175
|
-
const ip_address = event.ipAddress
|
|
180
|
+
const ip_address = getValidClientIp(event.ipAddress);
|
|
176
181
|
const city = event.city ?? null;
|
|
177
182
|
const country = event.country ?? null;
|
|
178
183
|
const country_code = event.countryCode ?? null;
|
|
179
184
|
|
|
180
185
|
const category = classifyCategory(event.eventType);
|
|
181
|
-
const dedupe_key =
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
186
|
+
const dedupe_key = event.eventId
|
|
187
|
+
? computeEventIdDedupeKey(AUDIT_EVENT_SOURCE, event.eventId)
|
|
188
|
+
: computeDedupeKey({
|
|
189
|
+
source: AUDIT_EVENT_SOURCE,
|
|
190
|
+
event_type: event.eventType,
|
|
191
|
+
event_key: event.eventKey,
|
|
192
|
+
actor_user_id,
|
|
193
|
+
subject_user_id,
|
|
194
|
+
organization_id,
|
|
195
|
+
team_id,
|
|
196
|
+
session_id,
|
|
197
|
+
identifier,
|
|
198
|
+
reason,
|
|
199
|
+
action,
|
|
200
|
+
occurred_at,
|
|
201
|
+
});
|
|
195
202
|
|
|
196
203
|
// ON CONFLICT DO NOTHING: dedupe_key UNIQUE 위반을 silent 무시.
|
|
197
204
|
// try/catch 방식은 PG 커넥션을 aborted 상태로 만드므로 사용하지 않는다.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type FastifyRequest } from "fastify";
|
|
2
|
+
|
|
3
|
+
import { convertFastifyHeadersToStandard } from "../utils/utils";
|
|
4
|
+
import { replaceClientIpHeaders } from "./audit-log/client-ip";
|
|
5
|
+
|
|
6
|
+
export function createBetterAuthRequest(
|
|
7
|
+
request: FastifyRequest,
|
|
8
|
+
ipAddressHeaders?: readonly string[],
|
|
9
|
+
): Request {
|
|
10
|
+
const url = new URL(request.url, `http://${request.headers.host}`);
|
|
11
|
+
const headers = convertFastifyHeadersToStandard(request.headers);
|
|
12
|
+
|
|
13
|
+
replaceClientIpHeaders(headers, request.ip, ipAddressHeaders);
|
|
14
|
+
|
|
15
|
+
return new Request(url.toString(), {
|
|
16
|
+
method: request.method,
|
|
17
|
+
headers,
|
|
18
|
+
...(request.body ? { body: JSON.stringify(request.body) } : {}),
|
|
19
|
+
});
|
|
20
|
+
}
|