sonamu 0.10.12 → 0.10.13
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/ai/agents/agent.d.ts +1 -1
- package/dist/ai/agents/agent.d.ts.map +1 -1
- package/dist/ai/agents/agent.js +5 -6
- package/dist/ai/agents/types.d.ts +1 -1
- package/dist/ai/agents/types.d.ts.map +1 -1
- package/dist/ai/providers/rtzr/api.js +18 -18
- package/dist/ai/providers/rtzr/error.d.ts.map +1 -1
- package/dist/ai/providers/rtzr/error.js +17 -15
- package/dist/ai/providers/rtzr/model.d.ts.map +1 -1
- package/dist/ai/providers/rtzr/model.js +3 -2
- package/dist/ai/providers/rtzr/options.d.ts.map +1 -1
- package/dist/ai/providers/rtzr/options.js +16 -16
- package/dist/ai/providers/rtzr/provider.d.ts.map +1 -1
- package/dist/ai/providers/rtzr/provider.js +9 -9
- package/dist/ai/providers/rtzr/utils.d.ts.map +1 -1
- package/dist/ai/providers/rtzr/utils.js +1 -1
- package/dist/api/caster.d.ts.map +1 -1
- package/dist/api/caster.js +17 -17
- package/dist/api/code-converters.d.ts +4 -2
- package/dist/api/code-converters.d.ts.map +1 -1
- package/dist/api/code-converters.js +100 -19
- package/dist/api/config.d.ts +16 -1
- package/dist/api/config.d.ts.map +1 -1
- package/dist/api/config.js +28 -21
- package/dist/api/context.d.ts +2 -2
- package/dist/api/context.d.ts.map +1 -1
- package/dist/api/context.js +1 -1
- package/dist/api/decorators.d.ts.map +1 -1
- package/dist/api/decorators.js +18 -11
- package/dist/api/http-validator.d.ts +36 -0
- package/dist/api/http-validator.d.ts.map +1 -0
- package/dist/api/http-validator.js +72 -0
- package/dist/api/index.js +6 -4
- package/dist/api/sonamu.d.ts +59 -20
- package/dist/api/sonamu.d.ts.map +1 -1
- package/dist/api/sonamu.js +250 -106
- package/dist/api/websocket-helpers.d.ts +7 -5
- package/dist/api/websocket-helpers.d.ts.map +1 -1
- package/dist/api/websocket-helpers.js +6 -6
- package/dist/auth/audit-log/builders.d.ts.map +1 -1
- package/dist/auth/audit-log/builders.js +1 -1
- package/dist/auth/audit-log/client-ip.d.ts +1 -1
- package/dist/auth/audit-log/client-ip.d.ts.map +1 -1
- package/dist/auth/audit-log/client-ip.js +4 -2
- package/dist/auth/audit-log/events.d.ts +5 -1
- package/dist/auth/audit-log/events.d.ts.map +1 -1
- package/dist/auth/audit-log/events.js +1 -1
- package/dist/auth/audit-log/plugin.d.ts +0 -8
- package/dist/auth/audit-log/plugin.d.ts.map +1 -1
- package/dist/auth/audit-log/plugin.js +48 -52
- package/dist/auth/audit-log-ingestor.d.ts.map +1 -1
- package/dist/auth/audit-log-ingestor.js +16 -12
- package/dist/auth/auth-generator.d.ts.map +1 -1
- package/dist/auth/auth-generator.js +1 -1
- package/dist/auth/better-auth-request.d.ts.map +1 -1
- package/dist/auth/better-auth-request.js +6 -5
- package/dist/auth/knex-adapter.js +1 -1
- package/dist/auth/plugins/entity-definitions/index.d.ts +13 -1
- package/dist/auth/plugins/entity-definitions/index.d.ts.map +1 -1
- package/dist/auth/plugins/entity-definitions/index.js +1 -1
- package/dist/auth/plugins/wrappers/admin.d.ts +1 -1
- package/dist/bin/build-config.d.ts +25 -0
- package/dist/bin/build-config.d.ts.map +1 -1
- package/dist/bin/build-config.js +105 -2
- package/dist/bin/cli.js +131 -27
- package/dist/bin/compiler-policy.d.ts +3 -0
- package/dist/bin/compiler-policy.d.ts.map +1 -0
- package/dist/bin/compiler-policy.js +24 -0
- package/dist/bin/fixture.d.ts.map +1 -1
- package/dist/bin/fixture.js +5 -3
- package/dist/bin/test-command.d.ts +22 -1
- package/dist/bin/test-command.d.ts.map +1 -1
- package/dist/bin/test-command.js +18 -10
- package/dist/bin/ts-loader-registration.d.ts +7 -1
- package/dist/bin/ts-loader-registration.d.ts.map +1 -1
- package/dist/bin/ts-loader-registration.js +13 -4
- package/dist/cache/decorator.d.ts.map +1 -1
- package/dist/cache/decorator.js +11 -4
- package/dist/compress/compress.d.ts.map +1 -1
- package/dist/compress/compress.js +1 -1
- package/dist/cone/cone-generator.d.ts +14 -1
- package/dist/cone/cone-generator.d.ts.map +1 -1
- package/dist/cone/cone-generator.js +13 -9
- package/dist/database/_batch_update.js +1 -1
- package/dist/database/base-model.d.ts +22 -10
- package/dist/database/base-model.d.ts.map +1 -1
- package/dist/database/base-model.js +26 -16
- package/dist/database/base-model.types.d.ts +15 -3
- package/dist/database/base-model.types.d.ts.map +1 -1
- package/dist/database/db.d.ts +12 -2
- package/dist/database/db.d.ts.map +1 -1
- package/dist/database/db.js +10 -7
- package/dist/database/knex.d.ts.map +1 -1
- package/dist/database/knex.js +17 -14
- package/dist/database/puri.d.ts +11 -9
- package/dist/database/puri.d.ts.map +1 -1
- package/dist/database/puri.js +93 -79
- package/dist/database/puri.types.d.ts +21 -21
- package/dist/database/puri.types.d.ts.map +1 -1
- package/dist/database/puri.types.js +1 -1
- package/dist/database/upsert-builder.d.ts +12 -10
- package/dist/database/upsert-builder.d.ts.map +1 -1
- package/dist/database/upsert-builder.js +20 -12
- package/dist/dict/sd.d.ts.map +1 -1
- package/dist/dict/sd.js +1 -1
- package/dist/dict/sonamu-dictionary.d.ts.map +1 -1
- package/dist/dict/sonamu-dictionary.js +1 -1
- package/dist/dict/utils.d.ts.map +1 -1
- package/dist/dict/utils.js +7 -2
- package/dist/entity/entity-manager.d.ts +13 -4
- package/dist/entity/entity-manager.d.ts.map +1 -1
- package/dist/entity/entity-manager.js +19 -11
- package/dist/entity/entity-template-cone.d.ts +73 -5
- package/dist/entity/entity-template-cone.d.ts.map +1 -1
- package/dist/entity/entity-template-cone.js +4 -2
- package/dist/entity/entity.d.ts +6 -2
- package/dist/entity/entity.d.ts.map +1 -1
- package/dist/entity/entity.js +30 -28
- package/dist/env.d.ts +3 -1
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js +1 -1
- package/dist/exceptions/error-handler.d.ts.map +1 -1
- package/dist/exceptions/error-handler.js +7 -2
- package/dist/exceptions/so-exceptions.d.ts +24 -20
- package/dist/exceptions/so-exceptions.d.ts.map +1 -1
- package/dist/exceptions/so-exceptions.js +4 -2
- package/dist/filter/utils.d.ts +7 -2
- package/dist/filter/utils.d.ts.map +1 -1
- package/dist/filter/utils.js +8 -6
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -6
- package/dist/logger/configure.d.ts.map +1 -1
- package/dist/logger/configure.js +10 -10
- package/dist/migration/code-generation.d.ts.map +1 -1
- package/dist/migration/code-generation.js +50 -46
- package/dist/migration/index-where-predicate.d.ts.map +1 -1
- package/dist/migration/index-where-predicate.js +22 -8
- package/dist/migration/migration-set.d.ts.map +1 -1
- package/dist/migration/migration-set.js +1 -1
- package/dist/migration/migrator.d.ts +3 -2
- package/dist/migration/migrator.d.ts.map +1 -1
- package/dist/migration/migrator.js +27 -21
- package/dist/migration/postgresql-schema-reader.d.ts.map +1 -1
- package/dist/migration/postgresql-schema-reader.js +51 -36
- package/dist/migration/slack-confirm.d.ts.map +1 -1
- package/dist/migration/slack-confirm.js +1 -1
- package/dist/naite/naite-reporter.js +2 -2
- package/dist/naite/naite.d.ts +74 -15
- package/dist/naite/naite.d.ts.map +1 -1
- package/dist/naite/naite.js +5 -19
- package/dist/runtime-type.d.ts +6 -0
- package/dist/runtime-type.d.ts.map +1 -0
- package/dist/runtime-type.js +26 -0
- package/dist/ssr/hmr-opt-out.d.ts +26 -0
- package/dist/ssr/hmr-opt-out.d.ts.map +1 -0
- package/dist/ssr/hmr-opt-out.js +59 -0
- package/dist/ssr/renderer.d.ts.map +1 -1
- package/dist/ssr/renderer.js +4 -1
- package/dist/storage/base-file.js +14 -14
- package/dist/storage/buffered-file.d.ts +2 -2
- package/dist/storage/buffered-file.d.ts.map +1 -1
- package/dist/storage/buffered-file.js +12 -12
- package/dist/storage/s3-driver.d.ts.map +1 -1
- package/dist/storage/s3-driver.js +1 -1
- package/dist/storage/uploaded-file.d.ts +2 -2
- package/dist/storage/uploaded-file.d.ts.map +1 -1
- package/dist/storage/uploaded-file.js +8 -8
- package/dist/stream/sse.js +11 -11
- package/dist/stream/ws-core.d.ts +1 -1
- package/dist/stream/ws-core.d.ts.map +1 -1
- package/dist/stream/ws-delivery.d.ts +1 -1
- package/dist/stream/ws-delivery.d.ts.map +1 -1
- package/dist/stream/ws-delivery.js +6 -4
- package/dist/stream/ws-local-connection-store.d.ts.map +1 -1
- package/dist/stream/ws-local-connection-store.js +4 -2
- package/dist/stream/ws-presence-store.js +1 -1
- package/dist/stream/ws-registry.d.ts +4 -4
- package/dist/stream/ws-registry.d.ts.map +1 -1
- package/dist/stream/ws-registry.js +4 -2
- package/dist/stream/ws-telemetry.d.ts +16 -12
- package/dist/stream/ws-telemetry.d.ts.map +1 -1
- package/dist/stream/ws-telemetry.js +22 -18
- package/dist/stream/ws.d.ts +18 -12
- package/dist/stream/ws.d.ts.map +1 -1
- package/dist/stream/ws.js +26 -22
- package/dist/syncer/api-parser.js +1 -1
- package/dist/syncer/checksum.d.ts.map +1 -1
- package/dist/syncer/checksum.js +7 -8
- package/dist/syncer/code-generator.d.ts.map +1 -1
- package/dist/syncer/code-generator.js +13 -14
- package/dist/syncer/entity-operations.d.ts.map +1 -1
- package/dist/syncer/entity-operations.js +5 -6
- package/dist/syncer/file-copy.d.ts +5 -0
- package/dist/syncer/file-copy.d.ts.map +1 -0
- package/dist/syncer/file-copy.js +36 -0
- package/dist/syncer/file-patterns.d.ts +1 -0
- package/dist/syncer/file-patterns.d.ts.map +1 -1
- package/dist/syncer/file-patterns.js +5 -2
- package/dist/syncer/file-tracking.d.ts.map +1 -1
- package/dist/syncer/file-tracking.js +5 -4
- package/dist/syncer/filesystem-dependencies.d.ts +15 -0
- package/dist/syncer/filesystem-dependencies.d.ts.map +1 -0
- package/dist/syncer/filesystem-dependencies.js +53 -0
- package/dist/syncer/module-loader.d.ts +1 -1
- package/dist/syncer/module-loader.d.ts.map +1 -1
- package/dist/syncer/module-loader.js +6 -4
- package/dist/syncer/syncer-actions.d.ts +4 -0
- package/dist/syncer/syncer-actions.d.ts.map +1 -1
- package/dist/syncer/syncer-actions.js +34 -17
- package/dist/syncer/syncer.d.ts +17 -6
- package/dist/syncer/syncer.d.ts.map +1 -1
- package/dist/syncer/syncer.js +71 -29
- package/dist/syncer/watcher.d.ts.map +1 -1
- package/dist/syncer/watcher.js +3 -3
- package/dist/tasks/decorator.d.ts +6 -6
- package/dist/tasks/decorator.d.ts.map +1 -1
- package/dist/tasks/decorator.js +4 -4
- package/dist/tasks/step-wrapper.d.ts +6 -4
- package/dist/tasks/step-wrapper.d.ts.map +1 -1
- package/dist/tasks/step-wrapper.js +5 -5
- package/dist/tasks/workflow-manager.d.ts.map +1 -1
- package/dist/tasks/workflow-manager.js +13 -7
- package/dist/template/entity-converter.d.ts +1 -1
- package/dist/template/entity-converter.d.ts.map +1 -1
- package/dist/template/entity-converter.js +2 -2
- package/dist/template/helpers.d.ts +8 -6
- package/dist/template/helpers.d.ts.map +1 -1
- package/dist/template/helpers.js +1 -1
- package/dist/template/implementations/generated.template.d.ts.map +1 -1
- package/dist/template/implementations/generated.template.js +4 -4
- package/dist/template/implementations/generated_http.template.d.ts +7 -4
- package/dist/template/implementations/generated_http.template.d.ts.map +1 -1
- package/dist/template/implementations/generated_http.template.js +21 -21
- package/dist/template/implementations/generated_sso.template.d.ts.map +1 -1
- package/dist/template/implementations/generated_sso.template.js +6 -3
- package/dist/template/implementations/http_validators.template.d.ts +18 -0
- package/dist/template/implementations/http_validators.template.d.ts.map +1 -0
- package/dist/template/implementations/http_validators.template.js +127 -0
- package/dist/template/implementations/model.template.d.ts.map +1 -1
- package/dist/template/implementations/model.template.js +1 -1
- package/dist/template/implementations/queries.template.js +1 -1
- package/dist/template/implementations/sd.template.d.ts.map +1 -1
- package/dist/template/implementations/sd.template.js +79 -39
- package/dist/template/implementations/services.template.d.ts.map +1 -1
- package/dist/template/implementations/services.template.js +6 -4
- package/dist/template/implementations/view_form.template.d.ts +6 -1
- package/dist/template/implementations/view_form.template.d.ts.map +1 -1
- package/dist/template/implementations/view_form.template.js +23 -15
- package/dist/template/implementations/view_list.template.d.ts.map +1 -1
- package/dist/template/implementations/view_list.template.js +11 -6
- package/dist/template/template-manager.d.ts.map +1 -1
- package/dist/template/template-manager.js +9 -7
- package/dist/template/template-types.d.ts +12 -5
- package/dist/template/template-types.d.ts.map +1 -1
- package/dist/template/template-types.js +1 -1
- package/dist/template/template.d.ts +2 -2
- package/dist/template/template.d.ts.map +1 -1
- package/dist/template/template.js +6 -4
- package/dist/template/zod-converter.d.ts +0 -19
- package/dist/template/zod-converter.d.ts.map +1 -1
- package/dist/template/zod-converter.js +100 -84
- package/dist/testing/_relation-graph.d.ts.map +1 -1
- package/dist/testing/_relation-graph.js +2 -1
- package/dist/testing/bootstrap.d.ts.map +1 -1
- package/dist/testing/bootstrap.js +9 -6
- package/dist/testing/data-explorer.d.ts +32 -7
- package/dist/testing/data-explorer.d.ts.map +1 -1
- package/dist/testing/data-explorer.js +77 -31
- package/dist/testing/dev-test-routes.d.ts +8 -1
- package/dist/testing/dev-test-routes.d.ts.map +1 -1
- package/dist/testing/dev-test-routes.js +125 -108
- package/dist/testing/dev-vitest-manager.d.ts +54 -8
- package/dist/testing/dev-vitest-manager.d.ts.map +1 -1
- package/dist/testing/dev-vitest-manager.js +24 -9
- package/dist/testing/faker-mappings.d.ts +412 -1
- package/dist/testing/faker-mappings.d.ts.map +1 -1
- package/dist/testing/faker-mappings.js +1 -1
- package/dist/testing/filesystem.d.ts +2 -0
- package/dist/testing/filesystem.d.ts.map +1 -0
- package/dist/testing/filesystem.js +7 -0
- package/dist/testing/fixture-generator.d.ts +25 -8
- package/dist/testing/fixture-generator.d.ts.map +1 -1
- package/dist/testing/fixture-generator.js +132 -73
- package/dist/testing/fixture-loader.d.ts +3 -1
- package/dist/testing/fixture-loader.d.ts.map +1 -1
- package/dist/testing/fixture-loader.js +1 -20
- package/dist/testing/fixture-manager.d.ts +8 -7
- package/dist/testing/fixture-manager.d.ts.map +1 -1
- package/dist/testing/fixture-manager.js +91 -59
- package/dist/testing/global-setup.d.ts.map +1 -1
- package/dist/testing/global-setup.js +5 -2
- package/dist/testing/index.d.ts +2 -0
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +6 -3
- package/dist/types/types.d.ts +101 -99
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/types.js +234 -225
- package/dist/ui/ai-api.d.ts.map +1 -1
- package/dist/ui/ai-api.js +1 -1
- package/dist/ui/ai-client.d.ts.map +1 -1
- package/dist/ui/ai-client.js +28 -18
- package/dist/ui/api.d.ts.map +1 -1
- package/dist/ui/api.js +135 -128
- package/dist/ui/async-route.d.ts +3 -0
- package/dist/ui/async-route.d.ts.map +1 -0
- package/dist/ui/async-route.js +12 -0
- package/dist/ui/cdd-service.d.ts +9 -7
- package/dist/ui/cdd-service.d.ts.map +1 -1
- package/dist/ui/cdd-service.js +7 -5
- package/dist/ui/migrations-api.d.ts.map +1 -1
- package/dist/ui/migrations-api.js +17 -15
- package/dist/ui-web/assets/index-DV1AAq6W.js +178 -0
- package/dist/ui-web/index.html +1 -1
- package/dist/utils/formatter.d.ts.map +1 -1
- package/dist/utils/formatter.js +7 -5
- package/dist/utils/fs-utils.d.ts +3 -3
- package/dist/utils/fs-utils.d.ts.map +1 -1
- package/dist/utils/fs-utils.js +6 -6
- package/dist/utils/model.d.ts +3 -5
- package/dist/utils/model.d.ts.map +1 -1
- package/dist/utils/model.js +1 -1
- package/dist/utils/object-utils.d.ts +1 -1
- package/dist/utils/object-utils.d.ts.map +1 -1
- package/dist/utils/object-utils.js +17 -16
- package/dist/utils/process-utils.d.ts.map +1 -1
- package/dist/utils/process-utils.js +4 -2
- package/dist/utils/runtime-value.d.ts +13 -0
- package/dist/utils/runtime-value.d.ts.map +1 -0
- package/dist/utils/runtime-value.js +40 -0
- package/dist/utils/sql-parser.d.ts +8 -1
- package/dist/utils/sql-parser.d.ts.map +1 -1
- package/dist/utils/sql-parser.js +6 -3
- package/dist/utils/type-utils.d.ts.map +1 -1
- package/dist/utils/type-utils.js +10 -8
- package/dist/utils/utils.d.ts +2 -2
- package/dist/utils/utils.d.ts.map +1 -1
- package/dist/utils/utils.js +11 -8
- package/dist/utils/zod-error.d.ts.map +1 -1
- package/dist/utils/zod-error.js +7 -4
- package/dist/vector/chunking.d.ts.map +1 -1
- package/dist/vector/chunking.js +1 -1
- package/dist/vector/embedding.d.ts.map +1 -1
- package/dist/vector/embedding.js +1 -1
- package/dist/vector/types.d.ts +3 -2
- package/dist/vector/types.d.ts.map +1 -1
- package/package.json +5 -3
- package/src/ai/agents/agent.ts +5 -5
- package/src/ai/agents/types.ts +1 -1
- package/src/ai/providers/rtzr/__tests__/model.test.ts +2 -2
- package/src/ai/providers/rtzr/error.ts +5 -3
- package/src/ai/providers/rtzr/model.ts +3 -2
- package/src/ai/providers/rtzr/options.ts +10 -5
- package/src/ai/providers/rtzr/provider.ts +10 -10
- package/src/ai/providers/rtzr/utils.ts +3 -1
- package/src/api/__tests__/config.test.ts +11 -21
- package/src/api/__tests__/sonamu.http-validator.test.ts +512 -0
- package/src/api/__tests__/sonamu.websocket.test.ts +51 -41
- package/src/api/__tests__/zod-compiler-policy.test.ts +95 -0
- package/src/api/caster.ts +6 -8
- package/src/api/code-converters.ts +136 -36
- package/src/api/config.ts +62 -4
- package/src/api/context.ts +3 -3
- package/src/api/decorators.ts +28 -17
- package/src/api/http-validator.ts +149 -0
- package/src/api/sonamu.ts +459 -152
- package/src/api/websocket-helpers.ts +24 -17
- package/src/auth/audit-log/builders.ts +3 -2
- package/src/auth/audit-log/client-ip.ts +4 -2
- package/src/auth/audit-log/events.ts +15 -1
- package/src/auth/audit-log/plugin.ts +129 -200
- package/src/auth/audit-log-ingestor.ts +19 -15
- package/src/auth/auth-generator.ts +10 -5
- package/src/auth/better-auth-request.ts +4 -3
- package/src/auth/knex-adapter.ts +8 -2
- package/src/auth/plugins/entity-definitions/index.ts +6 -5
- package/src/bin/__tests__/http-validator-benchmark.test.ts +94 -0
- package/src/bin/__tests__/test-command.test.ts +9 -15
- package/src/bin/__tests__/ts-loader-register.test.ts +15 -17
- package/src/bin/__tests__/zod-compiler-build.test.ts +307 -0
- package/src/bin/build-config.ts +167 -0
- package/src/bin/cli.ts +153 -36
- package/src/bin/compiler-policy.ts +35 -0
- package/src/bin/fixture.ts +14 -3
- package/src/bin/test-command.ts +74 -29
- package/src/bin/ts-loader-registration.ts +22 -4
- package/src/cache/decorator.ts +10 -3
- package/src/compress/compress.ts +17 -4
- package/src/cone/cone-generator.ts +34 -6
- package/src/database/__tests__/db.test.ts +1 -0
- package/src/database/__tests__/puri.test.ts +16 -15
- package/src/database/__tests__/transaction-scope.test.ts +5 -0
- package/src/database/_batch_update.ts +1 -0
- package/src/database/base-model.ts +81 -50
- package/src/database/base-model.types.ts +6 -6
- package/src/database/db.ts +29 -19
- package/src/database/knex.ts +30 -17
- package/src/database/puri-subset.test-d.ts +17 -0
- package/src/database/puri.ts +181 -143
- package/src/database/puri.types.test-d.ts +31 -1
- package/src/database/puri.types.ts +23 -21
- package/src/database/upsert-builder.ts +75 -53
- package/src/dict/sd.ts +15 -5
- package/src/dict/sonamu-dictionary.ts +6 -6
- package/src/dict/utils.ts +9 -1
- package/src/entity/entity-manager.ts +26 -9
- package/src/entity/entity-template-cone.ts +5 -10
- package/src/entity/entity.ts +50 -34
- package/src/env.ts +7 -3
- package/src/exceptions/error-handler.ts +17 -2
- package/src/exceptions/so-exceptions.ts +26 -20
- package/src/filter/utils.ts +21 -11
- package/src/index.ts +3 -0
- package/src/logger/configure.ts +26 -18
- package/src/migration/__tests__/migrator.test.ts +50 -57
- package/src/migration/code-generation.ts +132 -104
- package/src/migration/index-where-predicate.ts +51 -24
- package/src/migration/migration-set.ts +11 -5
- package/src/migration/migrator.ts +68 -41
- package/src/migration/postgresql-schema-reader.ts +52 -47
- package/src/migration/slack-confirm.ts +17 -15
- package/src/naite/naite-reporter.ts +1 -1
- package/src/naite/naite.ts +97 -24
- package/src/runtime-type.ts +34 -0
- package/src/ssr/hmr-opt-out.ts +78 -0
- package/src/ssr/renderer.ts +6 -0
- package/src/storage/base-file.ts +13 -13
- package/src/storage/buffered-file.ts +11 -11
- package/src/storage/s3-driver.ts +4 -1
- package/src/storage/uploaded-file.ts +8 -8
- package/src/stream/__tests__/ws-contracts.test.ts +4 -2
- package/src/stream/__tests__/ws-telemetry.test.ts +1 -1
- package/src/stream/__tests__/ws.test.ts +30 -6
- package/src/stream/sse.ts +12 -11
- package/src/stream/ws-core.ts +1 -1
- package/src/stream/ws-delivery.ts +16 -7
- package/src/stream/ws-local-connection-store.ts +2 -3
- package/src/stream/ws-presence-store.ts +6 -1
- package/src/stream/ws-registry.ts +16 -7
- package/src/stream/ws-telemetry.ts +66 -43
- package/src/stream/ws.ts +73 -39
- package/src/syncer/__tests__/api-parser.websocket-type-ref.test.ts +3 -2
- package/src/syncer/__tests__/http-validator-registry.test.ts +137 -0
- package/src/syncer/api-parser.ts +67 -27
- package/src/syncer/checksum.ts +17 -11
- package/src/syncer/code-generator.ts +25 -17
- package/src/syncer/entity-operations.ts +3 -5
- package/src/syncer/file-copy.ts +46 -0
- package/src/syncer/file-patterns.ts +11 -8
- package/src/syncer/file-tracking.ts +3 -4
- package/src/syncer/filesystem-dependencies.ts +73 -0
- package/src/syncer/module-loader.ts +8 -4
- package/src/syncer/syncer-actions.ts +56 -18
- package/src/syncer/syncer.ts +116 -43
- package/src/syncer/watcher.ts +8 -4
- package/src/tasks/decorator.ts +20 -12
- package/src/tasks/step-wrapper.ts +19 -9
- package/src/tasks/workflow-manager.ts +15 -9
- package/src/template/__tests__/http_validators.template.test.ts +160 -0
- package/src/template/__tests__/services.template.mutation.test.ts +24 -23
- package/src/template/__tests__/services.template.websocket.test.ts +21 -25
- package/src/template/entity-converter.ts +5 -2
- package/src/template/helpers.ts +8 -9
- package/src/template/implementations/generated.template.ts +5 -4
- package/src/template/implementations/generated_http.template.ts +61 -15
- package/src/template/implementations/generated_sso.template.ts +15 -4
- package/src/template/implementations/http_validators.template.ts +175 -0
- package/src/template/implementations/model.template.ts +3 -1
- package/src/template/implementations/sd.template.ts +81 -41
- package/src/template/implementations/services.template.ts +2 -2
- package/src/template/implementations/view_form.template.ts +56 -39
- package/src/template/implementations/view_list.template.ts +31 -23
- package/src/template/template-manager.ts +15 -13
- package/src/template/template-types.ts +13 -5
- package/src/template/template.ts +4 -3
- package/src/template/zod-converter.ts +206 -91
- package/src/testing/__tests__/dev-test-routes.test.ts +85 -25
- package/src/testing/__tests__/dev-vitest-manager.test.ts +15 -15
- package/src/testing/_relation-graph.ts +4 -1
- package/src/testing/bootstrap.ts +12 -5
- package/src/testing/data-explorer.ts +102 -53
- package/src/testing/dev-test-routes.ts +87 -50
- package/src/testing/dev-vitest-manager.ts +88 -32
- package/src/testing/faker-mappings.ts +2 -2
- package/src/testing/filesystem.ts +1 -0
- package/src/testing/fixture-generator.ts +191 -131
- package/src/testing/fixture-loader.ts +5 -1
- package/src/testing/fixture-manager.ts +129 -90
- package/src/testing/global-setup.ts +7 -3
- package/src/testing/index.ts +5 -0
- package/src/types/__tests__/entity-json-schema-search-text.test.ts +9 -3
- package/src/types/types.ts +184 -158
- package/src/ui/ai-api.ts +9 -7
- package/src/ui/ai-client.ts +61 -56
- package/src/ui/api.ts +727 -616
- package/src/ui/async-route.ts +4 -0
- package/src/ui/cdd-service.ts +35 -12
- package/src/ui/migrations-api.ts +63 -51
- package/src/utils/formatter.ts +7 -3
- package/src/utils/fs-utils.ts +5 -5
- package/src/utils/model.ts +4 -5
- package/src/utils/object-utils.ts +32 -22
- package/src/utils/process-utils.ts +4 -1
- package/src/utils/runtime-value.ts +43 -0
- package/src/utils/sql-parser.ts +13 -6
- package/src/utils/type-utils.ts +9 -7
- package/src/utils/utils.ts +19 -21
- package/src/utils/zod-error.ts +4 -2
- package/src/vector/chunking.ts +6 -1
- package/src/vector/embedding.ts +2 -1
- package/src/vector/types.ts +4 -2
- package/tsdown.api.config.ts +5 -2
- package/dist/ui-web/assets/index-C0m1pk6K.js +0 -177
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { type LanguageModel } from "ai";
|
|
1
2
|
import chalk from "chalk";
|
|
2
3
|
import { type Knex } from "knex";
|
|
4
|
+
import { z } from "zod";
|
|
3
5
|
|
|
4
6
|
import { type SonamuDBPreset } from "../database/db";
|
|
5
7
|
import { type Entity } from "../entity/entity";
|
|
@@ -7,27 +9,75 @@ import { type EntityManager } from "../entity/entity-manager";
|
|
|
7
9
|
import { type EntityProp, type FixtureImportResult, type FixtureRecord } from "../types/types";
|
|
8
10
|
import { isBelongsToOneRelationProp, isOneToOneRelationProp, isRelationProp } from "../types/types";
|
|
9
11
|
import { isTest } from "../utils/controller";
|
|
10
|
-
import {
|
|
12
|
+
import { isFunctionValue } from "../utils/runtime-value";
|
|
13
|
+
import { DataExplorer, dataExplorerRecordSchema, dataExplorerValueSchema } from "./data-explorer";
|
|
14
|
+
import { dataSourceConfigSchema } from "./data-explorer";
|
|
15
|
+
import { type DataExplorerRecord, type DataExplorerValue } from "./data-explorer";
|
|
11
16
|
import { type ExploreWithRelationsOptions, type ExploreWithRelationsResult } from "./data-explorer";
|
|
12
17
|
import { fakerMappings } from "./faker-mappings";
|
|
13
18
|
import { type FakerMappings } from "./faker-mappings";
|
|
14
19
|
import { FixtureManager } from "./fixture-manager";
|
|
20
|
+
import { type FixtureSourceRecord } from "./fixture-manager";
|
|
15
21
|
|
|
16
22
|
export type Locale = "ko" | "en" | "ja";
|
|
23
|
+
export type FixtureOverrides = object;
|
|
24
|
+
|
|
25
|
+
type FakerFunction = (...args: DataExplorerValue[]) => DataExplorerValue;
|
|
26
|
+
type FakerNamespaceMember = FakerFunction | FakerNamespace;
|
|
27
|
+
type FakerNamespace = { [name: string]: FakerNamespaceMember };
|
|
28
|
+
|
|
29
|
+
const fakerFunctionSchema = z.custom<FakerFunction>(
|
|
30
|
+
isFunctionValue,
|
|
31
|
+
"Faker 경로의 마지막 값은 함수여야 합니다.",
|
|
32
|
+
);
|
|
33
|
+
const fakerNamespaceObjectSchema = z.object({});
|
|
34
|
+
const dynamicNamespaceSchema = z.custom<FakerNamespace>(
|
|
35
|
+
(candidate) => fakerNamespaceObjectSchema.safeParse(candidate).success,
|
|
36
|
+
"Faker 경로는 객체 namespace여야 합니다.",
|
|
37
|
+
);
|
|
38
|
+
const generatorArgumentsSchema = z.array(dataExplorerValueSchema);
|
|
39
|
+
const fixtureRecordIdSchema = z.union([z.number(), z.string()]);
|
|
40
|
+
const fixtureWhereValueSchema = z.union([z.string(), z.number(), z.boolean(), z.date(), z.null()]);
|
|
41
|
+
|
|
42
|
+
export type FixtureGenerationSpec = {
|
|
43
|
+
entity: string;
|
|
44
|
+
count: number;
|
|
45
|
+
overrides?: FixtureOverrides;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
type GeneratedFixture = {
|
|
49
|
+
entity: string;
|
|
50
|
+
data: DataExplorerRecord;
|
|
51
|
+
explicitId?: boolean;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
function parseFixtureSourceRecord(record: DataExplorerRecord): FixtureSourceRecord {
|
|
55
|
+
return {
|
|
56
|
+
...record,
|
|
57
|
+
id: fixtureRecordIdSchema.parse(record.id),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
17
60
|
|
|
18
61
|
export type FixtureGeneratorOptions = {
|
|
19
62
|
locale?: Locale;
|
|
20
63
|
useLLM?: boolean;
|
|
21
64
|
enableLLMCache?: boolean;
|
|
22
65
|
llmModel?: string;
|
|
66
|
+
/** 테스트나 대체 LLM 공급자를 위한 텍스트 생성 구현 */
|
|
67
|
+
generateText?: FixtureTextGenerator;
|
|
23
68
|
};
|
|
24
69
|
|
|
70
|
+
export type FixtureTextGenerator = (request: {
|
|
71
|
+
model: LanguageModel;
|
|
72
|
+
prompt: string;
|
|
73
|
+
}) => Promise<{ text: string; usage?: { totalTokens?: number } }>;
|
|
74
|
+
|
|
25
75
|
export type GeneratorContext = {
|
|
26
76
|
/** 생성 중인 fixture들 (메모리 상) */
|
|
27
|
-
fixtures: Map<string,
|
|
77
|
+
fixtures: Map<string, DataExplorerRecord>;
|
|
28
78
|
|
|
29
79
|
/** 참조 데이터 캐시 (DataExplorer 결과) */
|
|
30
|
-
referenceCache: Map<string,
|
|
80
|
+
referenceCache: Map<string, DataExplorerRecord[]>;
|
|
31
81
|
|
|
32
82
|
/** 이미 import된 레코드를 추적하여 중복 import를 방지합니다 */
|
|
33
83
|
importedRecords: Set<string>; // "User#123"
|
|
@@ -37,7 +87,7 @@ export class FixtureGenerator {
|
|
|
37
87
|
private dataExplorer: DataExplorer;
|
|
38
88
|
private locale: Locale;
|
|
39
89
|
private mappings: FakerMappings;
|
|
40
|
-
private llmCache: Map<string,
|
|
90
|
+
private llmCache: Map<string, DataExplorerValue> = new Map();
|
|
41
91
|
private entityCache: Map<string, Entity> = new Map();
|
|
42
92
|
private options: FixtureGeneratorOptions;
|
|
43
93
|
|
|
@@ -58,6 +108,7 @@ export class FixtureGenerator {
|
|
|
58
108
|
useLLM: options?.useLLM || false,
|
|
59
109
|
enableLLMCache: options?.enableLLMCache !== false,
|
|
60
110
|
llmModel: options?.llmModel || "claude-sonnet-4-6",
|
|
111
|
+
generateText: options?.generateText,
|
|
61
112
|
};
|
|
62
113
|
}
|
|
63
114
|
|
|
@@ -65,11 +116,12 @@ export class FixtureGenerator {
|
|
|
65
116
|
* Fixture 생성 (단일)
|
|
66
117
|
* @returns 생성된 fixture 데이터 (메모리 상)
|
|
67
118
|
*/
|
|
68
|
-
async generate(
|
|
119
|
+
async generate<Overrides = undefined>(
|
|
69
120
|
entityName: string,
|
|
70
|
-
overrides
|
|
121
|
+
overrides?: Overrides,
|
|
71
122
|
context: GeneratorContext = this.createContext(),
|
|
72
|
-
): Promise<
|
|
123
|
+
): Promise<DataExplorerRecord> {
|
|
124
|
+
const parsedOverrides = dataExplorerRecordSchema.parse(overrides ?? {});
|
|
73
125
|
// Entity 캐싱: 테스트에서 entity cone 수정이 반영되도록 보장
|
|
74
126
|
let entity = this.entityCache.get(entityName);
|
|
75
127
|
if (!entity) {
|
|
@@ -83,7 +135,7 @@ export class FixtureGenerator {
|
|
|
83
135
|
const rowKey = this.options.useLLM ? `${entityName}#row#${Date.now()}` : undefined;
|
|
84
136
|
|
|
85
137
|
// 각 prop별 값 생성
|
|
86
|
-
const fixture:
|
|
138
|
+
const fixture: DataExplorerRecord = {};
|
|
87
139
|
|
|
88
140
|
for (const prop of entity.props) {
|
|
89
141
|
// Virtual prop은 스킵
|
|
@@ -99,8 +151,7 @@ export class FixtureGenerator {
|
|
|
99
151
|
prop.type === "bigInteger" ||
|
|
100
152
|
(prop.type === "string" &&
|
|
101
153
|
"dbDefault" in prop &&
|
|
102
|
-
|
|
103
|
-
prop.dbDefault.includes("nextval"));
|
|
154
|
+
z.string().safeParse(prop.dbDefault).data?.includes("nextval") === true);
|
|
104
155
|
if (hasDbSequence) {
|
|
105
156
|
// generateBatch에서 처리하므로 여기선 스킵
|
|
106
157
|
continue;
|
|
@@ -120,8 +171,8 @@ export class FixtureGenerator {
|
|
|
120
171
|
}
|
|
121
172
|
|
|
122
173
|
// override가 있으면 사용
|
|
123
|
-
if (prop.name in
|
|
124
|
-
fixture[prop.name] =
|
|
174
|
+
if (prop.name in parsedOverrides) {
|
|
175
|
+
fixture[prop.name] = parsedOverrides[prop.name];
|
|
125
176
|
continue;
|
|
126
177
|
}
|
|
127
178
|
|
|
@@ -133,10 +184,10 @@ export class FixtureGenerator {
|
|
|
133
184
|
// BelongsToOne / OneToOne(hasJoinColumn)은 FK 컬럼명({prop.name}_id)으로도 override를 받는다
|
|
134
185
|
const fkColName = `${prop.name}_id`;
|
|
135
186
|
if (
|
|
136
|
-
fkColName in
|
|
187
|
+
fkColName in parsedOverrides &&
|
|
137
188
|
(isBelongsToOneRelationProp(prop) || (isOneToOneRelationProp(prop) && prop.hasJoinColumn))
|
|
138
189
|
) {
|
|
139
|
-
fixture[fkColName] =
|
|
190
|
+
fixture[fkColName] = parsedOverrides[fkColName];
|
|
140
191
|
continue;
|
|
141
192
|
}
|
|
142
193
|
|
|
@@ -158,13 +209,10 @@ export class FixtureGenerator {
|
|
|
158
209
|
try {
|
|
159
210
|
const llmValue = await this.generateWithLLM(cone.note, prop, entity, rowKey);
|
|
160
211
|
// string 타입이고 length 제약이 있으면 초과 시 truncation
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
llmValue.length > prop.length
|
|
166
|
-
) {
|
|
167
|
-
fixture[prop.name] = llmValue.slice(0, prop.length);
|
|
212
|
+
const llmText = z.string().safeParse(llmValue);
|
|
213
|
+
const maxLength = "length" in prop ? z.number().safeParse(prop.length) : undefined;
|
|
214
|
+
if (llmText.success && maxLength?.success && llmText.data.length > maxLength.data) {
|
|
215
|
+
fixture[prop.name] = llmText.data.slice(0, maxLength.data);
|
|
168
216
|
} else {
|
|
169
217
|
fixture[prop.name] = llmValue;
|
|
170
218
|
}
|
|
@@ -186,7 +234,7 @@ export class FixtureGenerator {
|
|
|
186
234
|
|
|
187
235
|
// 4. fixtureDefault 사용
|
|
188
236
|
if (cone?.fixtureDefault !== undefined) {
|
|
189
|
-
fixture[prop.name] = cone.fixtureDefault;
|
|
237
|
+
fixture[prop.name] = dataExplorerValueSchema.parse(cone.fixtureDefault);
|
|
190
238
|
continue;
|
|
191
239
|
}
|
|
192
240
|
|
|
@@ -195,19 +243,22 @@ export class FixtureGenerator {
|
|
|
195
243
|
}
|
|
196
244
|
|
|
197
245
|
// 6. email 필드가 있고 name 필드가 있으면, email의 로컬 파트를 name 기반으로 보정
|
|
198
|
-
|
|
246
|
+
const email = z.string().safeParse(fixture.email);
|
|
247
|
+
if (email.success && !("email" in parsedOverrides)) {
|
|
199
248
|
const nameValue = fixture.name || fixture.username || fixture.full_name || fixture.name_en;
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
const
|
|
249
|
+
const parsedName = z.string().safeParse(nameValue);
|
|
250
|
+
if (parsedName.success) {
|
|
251
|
+
const domain = email.data.split("@")[1] || "example.com";
|
|
252
|
+
const romanized = await this.romanizeName(parsedName.data);
|
|
203
253
|
fixture.email = `${romanized}@${domain}`;
|
|
204
254
|
}
|
|
205
255
|
}
|
|
206
256
|
|
|
207
257
|
// 7. password 필드 암호화
|
|
208
|
-
|
|
258
|
+
const password = z.string().safeParse(fixture.password);
|
|
259
|
+
if (password.success && password.data.length > 0) {
|
|
209
260
|
const bcrypt = await import("bcrypt");
|
|
210
|
-
fixture.password = await bcrypt.hash(
|
|
261
|
+
fixture.password = await bcrypt.hash(password.data, 10);
|
|
211
262
|
}
|
|
212
263
|
|
|
213
264
|
context.fixtures.set(tempId, fixture);
|
|
@@ -221,7 +272,7 @@ export class FixtureGenerator {
|
|
|
221
272
|
entity: Entity,
|
|
222
273
|
prop: EntityProp,
|
|
223
274
|
context: GeneratorContext,
|
|
224
|
-
): Promise<number | null> {
|
|
275
|
+
): Promise<number | string | null> {
|
|
225
276
|
if (!isRelationProp(prop)) {
|
|
226
277
|
throw new Error(`FixtureGenerator: ${entity.id}.${prop.name} is not a relation prop`);
|
|
227
278
|
}
|
|
@@ -243,15 +294,13 @@ export class FixtureGenerator {
|
|
|
243
294
|
const cacheKey = `${prop.with}:${JSON.stringify(dataSource)}`;
|
|
244
295
|
|
|
245
296
|
if (!context.referenceCache.has(cacheKey)) {
|
|
297
|
+
const dataSourceConfig = dataSourceConfigSchema.parse(dataSource.config ?? {});
|
|
246
298
|
const exploreResult = await this.dataExplorer.exploreWithRelations(prop.with, {
|
|
247
299
|
strategy: dataSource.strategy,
|
|
248
|
-
limit:
|
|
249
|
-
((dataSource.config as Record<string, unknown> | undefined)?.limit as
|
|
250
|
-
| number
|
|
251
|
-
| undefined) || 10,
|
|
300
|
+
limit: dataSourceConfig.limit ?? 10,
|
|
252
301
|
includeRelations: true,
|
|
253
302
|
maxDepth: 3,
|
|
254
|
-
...
|
|
303
|
+
...dataSourceConfig,
|
|
255
304
|
});
|
|
256
305
|
context.referenceCache.set(cacheKey, exploreResult.main.records);
|
|
257
306
|
|
|
@@ -263,7 +312,7 @@ export class FixtureGenerator {
|
|
|
263
312
|
if (candidates && candidates.length > 0) {
|
|
264
313
|
// 랜덤하게 하나 선택
|
|
265
314
|
const selected = candidates[Math.floor(Math.random() * candidates.length)];
|
|
266
|
-
return selected.id
|
|
315
|
+
return z.union([z.number(), z.string()]).parse(selected.id);
|
|
267
316
|
}
|
|
268
317
|
}
|
|
269
318
|
|
|
@@ -290,7 +339,7 @@ export class FixtureGenerator {
|
|
|
290
339
|
if (autoCandidates && autoCandidates.length > 0) {
|
|
291
340
|
// 랜덤하게 하나 선택
|
|
292
341
|
const selected = autoCandidates[Math.floor(Math.random() * autoCandidates.length)];
|
|
293
|
-
return selected.id
|
|
342
|
+
return z.union([z.number(), z.string()]).parse(selected.id);
|
|
294
343
|
}
|
|
295
344
|
|
|
296
345
|
// 참조 데이터가 없으면 null 반환 (nullable인 경우)
|
|
@@ -320,7 +369,7 @@ export class FixtureGenerator {
|
|
|
320
369
|
// 1. Related entities import (Company, Department 등)
|
|
321
370
|
for (const [entityId, records] of exploreResult.related.entries()) {
|
|
322
371
|
const entity = this.entityManager.get(entityId);
|
|
323
|
-
const recordsToImport:
|
|
372
|
+
const recordsToImport: DataExplorerRecord[] = [];
|
|
324
373
|
|
|
325
374
|
!isTest() &&
|
|
326
375
|
console.log(
|
|
@@ -346,8 +395,8 @@ export class FixtureGenerator {
|
|
|
346
395
|
);
|
|
347
396
|
const fixtureRecords = await FixtureManager.createFixtureRecord(
|
|
348
397
|
entity,
|
|
349
|
-
record
|
|
350
|
-
{
|
|
398
|
+
parseFixtureSourceRecord(record),
|
|
399
|
+
{ db: this.sourceDb, singleRecord: true },
|
|
351
400
|
);
|
|
352
401
|
allFixtureRecords.push(...fixtureRecords);
|
|
353
402
|
}
|
|
@@ -356,7 +405,7 @@ export class FixtureGenerator {
|
|
|
356
405
|
|
|
357
406
|
// 2. Main entity import (Employee 등)
|
|
358
407
|
const mainEntity = this.entityManager.get(exploreResult.main.entityId);
|
|
359
|
-
const mainRecordsToImport:
|
|
408
|
+
const mainRecordsToImport: DataExplorerRecord[] = [];
|
|
360
409
|
|
|
361
410
|
!isTest() &&
|
|
362
411
|
console.log(
|
|
@@ -384,8 +433,8 @@ export class FixtureGenerator {
|
|
|
384
433
|
);
|
|
385
434
|
const fixtureRecords = await FixtureManager.createFixtureRecord(
|
|
386
435
|
mainEntity,
|
|
387
|
-
record
|
|
388
|
-
{
|
|
436
|
+
parseFixtureSourceRecord(record),
|
|
437
|
+
{ db: this.sourceDb, singleRecord: true },
|
|
389
438
|
);
|
|
390
439
|
allFixtureRecords.push(...fixtureRecords);
|
|
391
440
|
}
|
|
@@ -416,7 +465,7 @@ export class FixtureGenerator {
|
|
|
416
465
|
generator: string,
|
|
417
466
|
prop: EntityProp,
|
|
418
467
|
entity: Entity,
|
|
419
|
-
): Promise<
|
|
468
|
+
): Promise<DataExplorerValue> {
|
|
420
469
|
// Faker.js 표현식만 지원
|
|
421
470
|
if (generator.startsWith("faker.")) {
|
|
422
471
|
// username이나 name 필드는 한국어 faker 사용
|
|
@@ -438,26 +487,25 @@ export class FixtureGenerator {
|
|
|
438
487
|
const parts = path.split(".");
|
|
439
488
|
|
|
440
489
|
// faker 객체에서 함수 찾기
|
|
441
|
-
let
|
|
490
|
+
let member = dynamicNamespaceSchema.parse(faker);
|
|
442
491
|
for (const part of parts) {
|
|
443
|
-
if (
|
|
444
|
-
fn = (fn as Record<string, unknown>)[part];
|
|
445
|
-
} else {
|
|
492
|
+
if (!(part in member)) {
|
|
446
493
|
throw new Error(`FixtureGenerator: Invalid faker path for ${prop.name}: faker.${path}`);
|
|
447
494
|
}
|
|
495
|
+
const nextMember = member[part];
|
|
496
|
+
if (part !== parts.at(-1)) {
|
|
497
|
+
member = dynamicNamespaceSchema.parse(nextMember);
|
|
498
|
+
}
|
|
448
499
|
}
|
|
449
500
|
|
|
450
|
-
|
|
451
|
-
if (typeof fn !== "function") {
|
|
452
|
-
throw new Error(`FixtureGenerator: faker.${path} is not a function (for ${prop.name})`);
|
|
453
|
-
}
|
|
501
|
+
const fn = fakerFunctionSchema.parse(member[parts.at(-1) ?? ""]);
|
|
454
502
|
|
|
455
|
-
let args:
|
|
503
|
+
let args: DataExplorerValue[] = [];
|
|
456
504
|
if (argsStr?.trim()) {
|
|
457
505
|
args = this.parseGeneratorArgs(argsStr, prop.name);
|
|
458
506
|
}
|
|
459
507
|
|
|
460
|
-
return fn(...args);
|
|
508
|
+
return dataExplorerValueSchema.parse(fn(...args));
|
|
461
509
|
} catch (error) {
|
|
462
510
|
!isTest() &&
|
|
463
511
|
console.log(
|
|
@@ -490,7 +538,10 @@ export class FixtureGenerator {
|
|
|
490
538
|
* 4. Enum 타입
|
|
491
539
|
* 5. 타입별 기본값
|
|
492
540
|
*/
|
|
493
|
-
private async generateDefaultValue(
|
|
541
|
+
private async generateDefaultValue(
|
|
542
|
+
prop: EntityProp,
|
|
543
|
+
entity?: Entity,
|
|
544
|
+
): Promise<DataExplorerValue> {
|
|
494
545
|
const fakerModule = await import("@faker-js/faker");
|
|
495
546
|
const faker = fakerModule.faker;
|
|
496
547
|
const fakerKO = fakerModule.fakerKO;
|
|
@@ -680,7 +731,7 @@ export class FixtureGenerator {
|
|
|
680
731
|
_entity: Entity | undefined,
|
|
681
732
|
faker: typeof import("@faker-js/faker").faker,
|
|
682
733
|
_localeFaker: typeof import("@faker-js/faker").faker,
|
|
683
|
-
):
|
|
734
|
+
): DataExplorerValue[] {
|
|
684
735
|
const count = faker.number.int({ min: 1, max: 3 });
|
|
685
736
|
|
|
686
737
|
/** SonamuFile[]은 Sonamu 내장 타입으로 구조가 정해져 있습니다 */
|
|
@@ -726,7 +777,10 @@ export class FixtureGenerator {
|
|
|
726
777
|
*
|
|
727
778
|
* fakerKO, fakerJA도 지원하여 다국어 데이터를 생성합니다.
|
|
728
779
|
*/
|
|
729
|
-
private async executeFakerExpression(
|
|
780
|
+
private async executeFakerExpression(
|
|
781
|
+
expression: string,
|
|
782
|
+
prop: EntityProp,
|
|
783
|
+
): Promise<DataExplorerValue> {
|
|
730
784
|
const fakerModule = await import("@faker-js/faker");
|
|
731
785
|
const faker = fakerModule.faker;
|
|
732
786
|
const fakerKO = fakerModule.fakerKO;
|
|
@@ -735,7 +789,7 @@ export class FixtureGenerator {
|
|
|
735
789
|
/** Faker 표현식이 아닌 리터럴 값은 JSON으로 파싱합니다 */
|
|
736
790
|
if (!expression.startsWith("faker")) {
|
|
737
791
|
try {
|
|
738
|
-
return JSON.parse(expression);
|
|
792
|
+
return dataExplorerValueSchema.parse(JSON.parse(expression));
|
|
739
793
|
} catch {
|
|
740
794
|
return expression;
|
|
741
795
|
}
|
|
@@ -760,25 +814,25 @@ export class FixtureGenerator {
|
|
|
760
814
|
const parts = path.split(".");
|
|
761
815
|
|
|
762
816
|
/** 점 표기법(dot notation)으로 Faker 함수를 찾아갑니다 */
|
|
763
|
-
let
|
|
817
|
+
let member = dynamicNamespaceSchema.parse(selectedFaker);
|
|
764
818
|
for (const part of parts) {
|
|
765
|
-
if (
|
|
766
|
-
fn = (fn as Record<string, unknown>)[part];
|
|
767
|
-
} else {
|
|
819
|
+
if (!(part in member)) {
|
|
768
820
|
throw new Error(`Invalid faker path for ${prop.name}: ${fakerName}.${path}`);
|
|
769
821
|
}
|
|
822
|
+
const nextMember = member[part];
|
|
823
|
+
if (part !== parts.at(-1)) {
|
|
824
|
+
member = dynamicNamespaceSchema.parse(nextMember);
|
|
825
|
+
}
|
|
770
826
|
}
|
|
771
827
|
|
|
772
|
-
|
|
773
|
-
throw new Error(`${fakerName}.${path} is not a function (for ${prop.name})`);
|
|
774
|
-
}
|
|
828
|
+
const fn = fakerFunctionSchema.parse(member[parts.at(-1) ?? ""]);
|
|
775
829
|
|
|
776
|
-
let args:
|
|
830
|
+
let args: DataExplorerValue[] = [];
|
|
777
831
|
if (argsStr?.trim()) {
|
|
778
832
|
args = this.parseGeneratorArgs(argsStr, prop.name);
|
|
779
833
|
}
|
|
780
834
|
|
|
781
|
-
return fn(...args);
|
|
835
|
+
return dataExplorerValueSchema.parse(fn(...args));
|
|
782
836
|
}
|
|
783
837
|
|
|
784
838
|
/**
|
|
@@ -796,7 +850,7 @@ export class FixtureGenerator {
|
|
|
796
850
|
prop: EntityProp,
|
|
797
851
|
entity: Entity,
|
|
798
852
|
rowKey?: string,
|
|
799
|
-
): Promise<
|
|
853
|
+
): Promise<DataExplorerValue> {
|
|
800
854
|
// rowKey가 있으면 row 단위 생성 전략 사용
|
|
801
855
|
if (rowKey) {
|
|
802
856
|
const rowCacheKey = `${rowKey}:${prop.name}`;
|
|
@@ -825,18 +879,19 @@ export class FixtureGenerator {
|
|
|
825
879
|
|
|
826
880
|
const apiKey = this.getApiKey();
|
|
827
881
|
const { createAnthropic } = await import("@ai-sdk/anthropic");
|
|
828
|
-
const
|
|
882
|
+
const generateText = await this.getTextGenerator();
|
|
829
883
|
|
|
830
884
|
const rowResponse = await generateText({
|
|
831
885
|
model: createAnthropic({ apiKey })(this.options.llmModel || "claude-sonnet-4-6"),
|
|
832
886
|
prompt: this.buildRowLLMPrompt(llmProps, entity),
|
|
833
887
|
});
|
|
834
|
-
|
|
888
|
+
const rowResponseText = z.string().safeParse(rowResponse?.text);
|
|
889
|
+
if (!rowResponseText.success) {
|
|
835
890
|
throw new Error("Invalid LLM response");
|
|
836
891
|
}
|
|
837
892
|
|
|
838
893
|
// 응답을 파싱하여 각 필드에 대한 결과를 캐시에 저장
|
|
839
|
-
const rowResult = this.parseRowLLMResponse(
|
|
894
|
+
const rowResult = this.parseRowLLMResponse(rowResponseText.data, llmProps);
|
|
840
895
|
for (const [fieldName, value] of Object.entries(rowResult)) {
|
|
841
896
|
this.llmCache.set(`${rowKey}:${fieldName}`, value);
|
|
842
897
|
}
|
|
@@ -861,7 +916,7 @@ export class FixtureGenerator {
|
|
|
861
916
|
fixtureHint: string,
|
|
862
917
|
prop: EntityProp,
|
|
863
918
|
entity: Entity,
|
|
864
|
-
): Promise<
|
|
919
|
+
): Promise<DataExplorerValue> {
|
|
865
920
|
const cacheKey = `${entity.id}:${prop.name}:${fixtureHint}`;
|
|
866
921
|
if (this.options.enableLLMCache && this.llmCache.has(cacheKey)) {
|
|
867
922
|
return this.llmCache.get(cacheKey);
|
|
@@ -869,17 +924,18 @@ export class FixtureGenerator {
|
|
|
869
924
|
|
|
870
925
|
const apiKey = this.getApiKey();
|
|
871
926
|
const { createAnthropic } = await import("@ai-sdk/anthropic");
|
|
872
|
-
const
|
|
927
|
+
const generateText = await this.getTextGenerator();
|
|
873
928
|
|
|
874
929
|
const singleResponse = await generateText({
|
|
875
930
|
model: createAnthropic({ apiKey })(this.options.llmModel || "claude-sonnet-4-6"),
|
|
876
931
|
prompt: this.buildLLMPrompt(fixtureHint, prop, entity),
|
|
877
932
|
});
|
|
878
|
-
|
|
933
|
+
const singleResponseText = z.string().safeParse(singleResponse?.text);
|
|
934
|
+
if (!singleResponseText.success) {
|
|
879
935
|
throw new Error("Invalid LLM response");
|
|
880
936
|
}
|
|
881
937
|
|
|
882
|
-
const value = this.parseLLMResponse(
|
|
938
|
+
const value = this.parseLLMResponse(singleResponseText.data, prop.type);
|
|
883
939
|
if (this.options.enableLLMCache) {
|
|
884
940
|
this.llmCache.set(cacheKey, value);
|
|
885
941
|
}
|
|
@@ -887,6 +943,14 @@ export class FixtureGenerator {
|
|
|
887
943
|
return value;
|
|
888
944
|
}
|
|
889
945
|
|
|
946
|
+
private async getTextGenerator(): Promise<FixtureTextGenerator> {
|
|
947
|
+
if (this.options.generateText) {
|
|
948
|
+
return this.options.generateText;
|
|
949
|
+
}
|
|
950
|
+
const ai = await import("ai");
|
|
951
|
+
return async (request) => ai.generateText(request);
|
|
952
|
+
}
|
|
953
|
+
|
|
890
954
|
/**
|
|
891
955
|
* row 전체를 한 번에 생성하는 LLM 프롬프트를 만듭니다.
|
|
892
956
|
*/
|
|
@@ -938,7 +1002,7 @@ export class FixtureGenerator {
|
|
|
938
1002
|
? `\n\nOther fields in this entity (for context, do NOT generate these):\n${otherProps}`
|
|
939
1003
|
: "";
|
|
940
1004
|
|
|
941
|
-
const
|
|
1005
|
+
const resultTemplate = props.map((p) => ` "${p.name}": <${p.type}>`).join(",\n");
|
|
942
1006
|
|
|
943
1007
|
const entityContext = entity.cone?.note ? `\nEntity description: ${entity.cone.note}` : "";
|
|
944
1008
|
|
|
@@ -958,35 +1022,35 @@ Rules:
|
|
|
958
1022
|
|
|
959
1023
|
Return exactly this JSON shape:
|
|
960
1024
|
{
|
|
961
|
-
${
|
|
1025
|
+
${resultTemplate}
|
|
962
1026
|
}`;
|
|
963
1027
|
}
|
|
964
1028
|
|
|
965
1029
|
/**
|
|
966
1030
|
* row LLM 응답을 파싱하여 필드별 값으로 변환합니다.
|
|
967
1031
|
*/
|
|
968
|
-
private parseRowLLMResponse(text: string, props: EntityProp[]):
|
|
1032
|
+
private parseRowLLMResponse(text: string, props: EntityProp[]): DataExplorerRecord {
|
|
969
1033
|
const jsonText = text
|
|
970
1034
|
.trim()
|
|
971
1035
|
.replace(/^```json\s*/i, "")
|
|
972
1036
|
.replace(/```\s*$/, "")
|
|
973
1037
|
.trim();
|
|
974
1038
|
|
|
975
|
-
let parsed:
|
|
1039
|
+
let parsed: DataExplorerRecord;
|
|
976
1040
|
try {
|
|
977
|
-
const
|
|
978
|
-
if (
|
|
1041
|
+
const parsedRecord = dataExplorerRecordSchema.safeParse(JSON.parse(jsonText));
|
|
1042
|
+
if (!parsedRecord.success) {
|
|
979
1043
|
!isTest() &&
|
|
980
1044
|
console.warn("[FixtureGenerator] Row LLM response is not a plain object:", text);
|
|
981
1045
|
return {};
|
|
982
1046
|
}
|
|
983
|
-
parsed =
|
|
1047
|
+
parsed = parsedRecord.data;
|
|
984
1048
|
} catch {
|
|
985
1049
|
!isTest() && console.warn("[FixtureGenerator] Failed to parse row LLM response:", text);
|
|
986
1050
|
return {};
|
|
987
1051
|
}
|
|
988
1052
|
|
|
989
|
-
const result:
|
|
1053
|
+
const result: DataExplorerRecord = {};
|
|
990
1054
|
for (const prop of props) {
|
|
991
1055
|
if (prop.name in parsed) {
|
|
992
1056
|
result[prop.name] = this.parseLLMResponse(String(parsed[prop.name] ?? ""), prop.type);
|
|
@@ -1038,13 +1102,13 @@ Rules:
|
|
|
1038
1102
|
return prompt;
|
|
1039
1103
|
}
|
|
1040
1104
|
|
|
1041
|
-
private parseLLMResponse(text: string, propType: string):
|
|
1105
|
+
private parseLLMResponse(text: string, propType: string): DataExplorerValue {
|
|
1042
1106
|
const cleaned = text.trim();
|
|
1043
1107
|
|
|
1044
1108
|
// 배열 타입 처리
|
|
1045
1109
|
if (propType.endsWith("[]")) {
|
|
1046
1110
|
try {
|
|
1047
|
-
const parsed = JSON.parse(cleaned);
|
|
1111
|
+
const parsed = dataExplorerValueSchema.parse(JSON.parse(cleaned));
|
|
1048
1112
|
const baseType = propType.slice(0, -2); // "integer[]" -> "integer"
|
|
1049
1113
|
|
|
1050
1114
|
if (Array.isArray(parsed)) {
|
|
@@ -1054,7 +1118,7 @@ Rules:
|
|
|
1054
1118
|
return this.getDefaultValueForType(baseType);
|
|
1055
1119
|
}
|
|
1056
1120
|
// 객체는 JSON.stringify 후 파싱 (json 타입인 경우)
|
|
1057
|
-
if (
|
|
1121
|
+
if (dataExplorerRecordSchema.safeParse(item).success || Array.isArray(item)) {
|
|
1058
1122
|
return baseType === "json"
|
|
1059
1123
|
? item
|
|
1060
1124
|
: this.parseScalarValue(JSON.stringify(item), baseType);
|
|
@@ -1077,7 +1141,7 @@ Rules:
|
|
|
1077
1141
|
return this.parseScalarValue(cleaned, propType);
|
|
1078
1142
|
}
|
|
1079
1143
|
|
|
1080
|
-
private getDefaultValueForType(propType: string):
|
|
1144
|
+
private getDefaultValueForType(propType: string): DataExplorerValue {
|
|
1081
1145
|
switch (propType) {
|
|
1082
1146
|
case "integer":
|
|
1083
1147
|
return 0;
|
|
@@ -1100,7 +1164,7 @@ Rules:
|
|
|
1100
1164
|
}
|
|
1101
1165
|
}
|
|
1102
1166
|
|
|
1103
|
-
private parseScalarValue(text: string, propType: string):
|
|
1167
|
+
private parseScalarValue(text: string, propType: string): DataExplorerValue {
|
|
1104
1168
|
const cleaned = text.trim();
|
|
1105
1169
|
|
|
1106
1170
|
switch (propType) {
|
|
@@ -1129,7 +1193,7 @@ Rules:
|
|
|
1129
1193
|
}
|
|
1130
1194
|
case "json":
|
|
1131
1195
|
try {
|
|
1132
|
-
return JSON.parse(cleaned);
|
|
1196
|
+
return dataExplorerValueSchema.parse(JSON.parse(cleaned));
|
|
1133
1197
|
} catch {
|
|
1134
1198
|
return cleaned;
|
|
1135
1199
|
}
|
|
@@ -1149,22 +1213,27 @@ Rules:
|
|
|
1149
1213
|
* 2. JS 객체 리터럴 → JSON 변환 후 재시도 (single quote, unquoted key 처리)
|
|
1150
1214
|
* 3. 단순 단일 인자 폴백 (숫자, 문자열)
|
|
1151
1215
|
*/
|
|
1152
|
-
private parseGeneratorArgs(argsStr: string, propName: string):
|
|
1216
|
+
private parseGeneratorArgs(argsStr: string, propName: string): DataExplorerValue[] {
|
|
1153
1217
|
// 1. JSON 직접 파싱
|
|
1218
|
+
let parsedArguments: DataExplorerValue[] | null;
|
|
1154
1219
|
try {
|
|
1155
|
-
|
|
1156
|
-
return Array.isArray(parsed) ? parsed : [parsed];
|
|
1220
|
+
parsedArguments = generatorArgumentsSchema.parse(JSON.parse(`[${argsStr}]`));
|
|
1157
1221
|
} catch {
|
|
1158
|
-
|
|
1222
|
+
parsedArguments = null;
|
|
1223
|
+
}
|
|
1224
|
+
if (parsedArguments) {
|
|
1225
|
+
return parsedArguments;
|
|
1159
1226
|
}
|
|
1160
1227
|
|
|
1161
1228
|
// 2. JS 객체 리터럴 → JSON 변환 후 재시도
|
|
1162
1229
|
try {
|
|
1163
1230
|
const jsonStr = this.convertJsLiteralToJson(argsStr);
|
|
1164
|
-
|
|
1165
|
-
return Array.isArray(parsed) ? parsed : [parsed];
|
|
1231
|
+
parsedArguments = generatorArgumentsSchema.parse(JSON.parse(`[${jsonStr}]`));
|
|
1166
1232
|
} catch {
|
|
1167
|
-
|
|
1233
|
+
parsedArguments = null;
|
|
1234
|
+
}
|
|
1235
|
+
if (parsedArguments) {
|
|
1236
|
+
return parsedArguments;
|
|
1168
1237
|
}
|
|
1169
1238
|
|
|
1170
1239
|
// 3. 단순 단일 인자 폴백
|
|
@@ -1229,6 +1298,14 @@ Rules:
|
|
|
1229
1298
|
return apiKey;
|
|
1230
1299
|
}
|
|
1231
1300
|
|
|
1301
|
+
private hasSequenceDefault(prop: EntityProp): boolean {
|
|
1302
|
+
if (!("dbDefault" in prop)) {
|
|
1303
|
+
return false;
|
|
1304
|
+
}
|
|
1305
|
+
const dbDefault = z.string().safeParse(prop.dbDefault);
|
|
1306
|
+
return dbDefault.success && dbDefault.data.includes("nextval");
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1232
1309
|
private getExpectedFormat(propType: string): string {
|
|
1233
1310
|
// 배열 타입 처리
|
|
1234
1311
|
if (propType.endsWith("[]")) {
|
|
@@ -1460,15 +1537,9 @@ Rules:
|
|
|
1460
1537
|
*
|
|
1461
1538
|
* @returns 저장된 fixture 데이터 (실제 DB ID 포함)
|
|
1462
1539
|
*/
|
|
1463
|
-
async generateBatch(
|
|
1464
|
-
specs: Array<{ entity: string; count: number; overrides?: Record<string, unknown> }>,
|
|
1465
|
-
): Promise<FixtureImportResult[]> {
|
|
1540
|
+
async generateBatch(specs: FixtureGenerationSpec[]): Promise<FixtureImportResult[]> {
|
|
1466
1541
|
const context = this.createContext();
|
|
1467
|
-
const generatedFixtures:
|
|
1468
|
-
entity: string;
|
|
1469
|
-
data: Record<string, unknown>;
|
|
1470
|
-
explicitId?: boolean;
|
|
1471
|
-
}> = [];
|
|
1542
|
+
const generatedFixtures: GeneratedFixture[] = [];
|
|
1472
1543
|
|
|
1473
1544
|
// 1. 각 spec별로 fixture 생성
|
|
1474
1545
|
for (const spec of specs) {
|
|
@@ -1484,10 +1555,7 @@ Rules:
|
|
|
1484
1555
|
// 부모 테이블에서 서브타입 테이블에 없는 id를 조회
|
|
1485
1556
|
let query = this.sourceDb(parentEntity.table).select(`${parentEntity.table}.id`);
|
|
1486
1557
|
for (const [col, val] of Object.entries(parentOverrides)) {
|
|
1487
|
-
query = query.where(
|
|
1488
|
-
`${parentEntity.table}.${col}`,
|
|
1489
|
-
val as string | number | boolean | null,
|
|
1490
|
-
);
|
|
1558
|
+
query = query.where(`${parentEntity.table}.${col}`, fixtureWhereValueSchema.parse(val));
|
|
1491
1559
|
}
|
|
1492
1560
|
query = query
|
|
1493
1561
|
.leftJoin(specEntity.table, `${specEntity.table}.id`, `${parentEntity.table}.id`)
|
|
@@ -1536,10 +1604,7 @@ Rules:
|
|
|
1536
1604
|
!explicitId &&
|
|
1537
1605
|
(idProp?.type === "integer" ||
|
|
1538
1606
|
idProp?.type === "bigInteger" ||
|
|
1539
|
-
(idProp?.type === "string" &&
|
|
1540
|
-
"dbDefault" in (idProp ?? {}) &&
|
|
1541
|
-
typeof (idProp as { dbDefault?: unknown })?.dbDefault === "string" &&
|
|
1542
|
-
((idProp as { dbDefault?: string })?.dbDefault ?? "").includes("nextval")));
|
|
1607
|
+
(idProp?.type === "string" && this.hasSequenceDefault(idProp)));
|
|
1543
1608
|
|
|
1544
1609
|
const dataForRecord = usesSequence
|
|
1545
1610
|
? { ...data, id: Math.floor(Math.random() * 1000000) }
|
|
@@ -1547,7 +1612,7 @@ Rules:
|
|
|
1547
1612
|
|
|
1548
1613
|
const records = await FixtureManager.createFixtureRecord(
|
|
1549
1614
|
entity,
|
|
1550
|
-
dataForRecord
|
|
1615
|
+
parseFixtureSourceRecord(dataForRecord),
|
|
1551
1616
|
{ singleRecord: true },
|
|
1552
1617
|
);
|
|
1553
1618
|
fixtureRecords.push(...records);
|
|
@@ -1572,7 +1637,7 @@ Rules:
|
|
|
1572
1637
|
* generateBatch()를 다시 호출하지 않고 직접 삽입합니다.
|
|
1573
1638
|
*/
|
|
1574
1639
|
private async generateCompanions(
|
|
1575
|
-
specs:
|
|
1640
|
+
specs: FixtureGenerationSpec[],
|
|
1576
1641
|
parentResults: FixtureImportResult[],
|
|
1577
1642
|
): Promise<FixtureImportResult[]> {
|
|
1578
1643
|
const allResults: FixtureImportResult[] = [];
|
|
@@ -1612,10 +1677,7 @@ Rules:
|
|
|
1612
1677
|
const usesSequence =
|
|
1613
1678
|
companionIdProp?.type === "integer" ||
|
|
1614
1679
|
companionIdProp?.type === "bigInteger" ||
|
|
1615
|
-
(companionIdProp?.type === "string" &&
|
|
1616
|
-
"dbDefault" in (companionIdProp ?? {}) &&
|
|
1617
|
-
typeof (companionIdProp as { dbDefault?: unknown })?.dbDefault === "string" &&
|
|
1618
|
-
((companionIdProp as { dbDefault?: string })?.dbDefault ?? "").includes("nextval"));
|
|
1680
|
+
(companionIdProp?.type === "string" && this.hasSequenceDefault(companionIdProp));
|
|
1619
1681
|
const companionCount = companion.count ?? 1;
|
|
1620
1682
|
|
|
1621
1683
|
// 각 parent result에 대해 companion fixture 생성
|
|
@@ -1624,7 +1686,7 @@ Rules:
|
|
|
1624
1686
|
|
|
1625
1687
|
for (const parentResult of entityResults) {
|
|
1626
1688
|
const resolvedOverrides = this.resolveTemplateOverrides(
|
|
1627
|
-
companion.overrides ?? {},
|
|
1689
|
+
dataExplorerRecordSchema.parse(companion.overrides ?? {}),
|
|
1628
1690
|
parentResult.data,
|
|
1629
1691
|
);
|
|
1630
1692
|
resolvedOverrides[fkColName] = parentResult.data.id;
|
|
@@ -1638,10 +1700,7 @@ Rules:
|
|
|
1638
1700
|
|
|
1639
1701
|
const records = await FixtureManager.createFixtureRecord(
|
|
1640
1702
|
companionEntity,
|
|
1641
|
-
dataForRecord
|
|
1642
|
-
id: number | string;
|
|
1643
|
-
[key: string]: string | number | boolean | null;
|
|
1644
|
-
},
|
|
1703
|
+
parseFixtureSourceRecord(dataForRecord),
|
|
1645
1704
|
{ singleRecord: true },
|
|
1646
1705
|
);
|
|
1647
1706
|
companionFixtureRecords.push(...records);
|
|
@@ -1672,13 +1731,14 @@ Rules:
|
|
|
1672
1731
|
* 예: { "account_id": "{{email}}" } → { "account_id": "user@example.com" }
|
|
1673
1732
|
*/
|
|
1674
1733
|
private resolveTemplateOverrides(
|
|
1675
|
-
overrides:
|
|
1734
|
+
overrides: DataExplorerRecord,
|
|
1676
1735
|
parentData: { [key: string]: string | number | boolean | Date | null },
|
|
1677
|
-
):
|
|
1678
|
-
const resolved:
|
|
1736
|
+
): DataExplorerRecord {
|
|
1737
|
+
const resolved: DataExplorerRecord = {};
|
|
1679
1738
|
for (const [key, value] of Object.entries(overrides)) {
|
|
1680
|
-
|
|
1681
|
-
|
|
1739
|
+
const template = z.string().safeParse(value);
|
|
1740
|
+
if (template.success && template.data.startsWith("{{") && template.data.endsWith("}}")) {
|
|
1741
|
+
const fieldName = template.data.slice(2, -2).trim();
|
|
1682
1742
|
if (!(fieldName in parentData)) {
|
|
1683
1743
|
throw new Error(
|
|
1684
1744
|
`템플릿 필드 "${fieldName}"이(가) 부모 fixture 데이터에 존재하지 않습니다 (override key: "${key}")`,
|
|
@@ -1741,8 +1801,8 @@ Rules:
|
|
|
1741
1801
|
for (const record of exploreResult.main.records) {
|
|
1742
1802
|
const records = await FixtureManager.createFixtureRecord(
|
|
1743
1803
|
mainEntity,
|
|
1744
|
-
record
|
|
1745
|
-
{
|
|
1804
|
+
parseFixtureSourceRecord(record),
|
|
1805
|
+
{ db: this.sourceDb, singleRecord: true },
|
|
1746
1806
|
);
|
|
1747
1807
|
fixtureRecords.push(...records);
|
|
1748
1808
|
}
|
|
@@ -1753,8 +1813,8 @@ Rules:
|
|
|
1753
1813
|
for (const record of relatedRecords) {
|
|
1754
1814
|
const records = await FixtureManager.createFixtureRecord(
|
|
1755
1815
|
relatedEntity,
|
|
1756
|
-
record
|
|
1757
|
-
{
|
|
1816
|
+
parseFixtureSourceRecord(record),
|
|
1817
|
+
{ db: this.sourceDb, singleRecord: true },
|
|
1758
1818
|
);
|
|
1759
1819
|
fixtureRecords.push(...records);
|
|
1760
1820
|
}
|