@ttsc/evidence 0.28.6 → 0.30.0

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.
@@ -0,0 +1,116 @@
1
+ package evidence
2
+
3
+ import (
4
+ "io/fs"
5
+ "os"
6
+ "path/filepath"
7
+ "sort"
8
+ "strings"
9
+ )
10
+
11
+ // A rooted reference selects entries from disk and follows their exports only
12
+ // inside its declared root. Ordinary imports do not expand the population.
13
+ func materializeRootedTypeScriptReference(claim claimSpec, reference referenceSpec, loader *typeScriptLoader) (referenceState, []string) {
14
+ state := referenceState{Spec: reference, Healthy: false, UnitsByScope: map[string][]*evidenceUnit{}}
15
+ context := claimLabel(claim) + " " + referenceLabel(reference)
16
+ base := reference.Base
17
+ if info, err := os.Stat(base.Absolute); err != nil || !info.IsDir() {
18
+ cause := "the path names a file"
19
+ if err != nil {
20
+ cause = causeText(err)
21
+ }
22
+ return state, []string{context + " could not read TypeScript reference root '" + populationRootLabel(base) + "': " + cause + ". Select an existing readable directory containing the reference files."}
23
+ }
24
+ from, resolved := resolvedBaseDirectory(base)
25
+ if !resolved {
26
+ return state, []string{unresolvedBaseProblem(base, artifactTypeScript)}
27
+ }
28
+ from, resolved = physicalTypeScriptPath(from)
29
+ if !resolved {
30
+ return state, []string{unresolvedBaseProblem(base, artifactTypeScript)}
31
+ }
32
+ entries := map[string]bool{}
33
+ problems := []string{}
34
+ err := filepath.WalkDir(from, func(current string, entry fs.DirEntry, walkErr error) error {
35
+ if walkErr != nil {
36
+ return walkErr
37
+ }
38
+ relative, err := filepath.Rel(from, current)
39
+ if err != nil {
40
+ return err
41
+ }
42
+ relative = filepath.ToSlash(relative)
43
+ if entry.IsDir() {
44
+ if relative != "." && (entry.Name() == "node_modules" || !reference.Files.couldMatchDescendant(relative)) {
45
+ return filepath.SkipDir
46
+ }
47
+ return nil
48
+ }
49
+ if isTypeScriptPath(relative) && reference.Files.matches(relative) {
50
+ entries[loader.projectPath(base.display(relative))] = true
51
+ }
52
+ return nil
53
+ })
54
+ if err != nil {
55
+ problems = append(problems, context+" could not list TypeScript root '"+populationRootLabel(base)+"': "+causeText(err)+". Restore filesystem access before evaluating coverage.")
56
+ }
57
+ // An open Program snapshot can precede its first save. It is authoritative
58
+ // on the same terms as an edited file that already exists on disk.
59
+ for module := range loader.program {
60
+ identity, resolved := loader.moduleIdentity(module)
61
+ if !resolved {
62
+ continue
63
+ }
64
+ relative, ok := relativeProjectPath(from, resolveProjectPath(loader.identityRoot, identity))
65
+ if ok && reference.Files.matches(relative) {
66
+ entries[loader.projectPath(base.display(relative))] = true
67
+ }
68
+ }
69
+ for entry := range entries {
70
+ state.Paths = append(state.Paths, entry)
71
+ }
72
+ sort.Strings(state.Paths)
73
+ confined := *loader
74
+ confined.boundary = &base
75
+ confined.resolved = map[string]string{}
76
+ confined.failures = map[string]string{}
77
+ population := materializeEntryUnits(&confined, state.Paths, reference.Symbols)
78
+ state.Units, state.Hidden, state.Published = population.Units, population.Hidden, population.Published
79
+ state.Code = population.Code
80
+ applyTraversedScopes(&state, population.Reached)
81
+ failed := []string{}
82
+ for module := range confined.failures {
83
+ failed = append(failed, module)
84
+ }
85
+ sort.Strings(failed)
86
+ for _, module := range failed {
87
+ problems = append(problems, context+" could not load TypeScript module '"+module+"': "+confined.failures[module]+". Repair the export or explicitly select a root containing its implementation.")
88
+ }
89
+ state.Healthy = len(problems) == 0
90
+ if !state.Healthy {
91
+ return state, problems
92
+ }
93
+ if len(state.Paths) == 0 {
94
+ return state, []string{context + " matched no typescript files for " + describePopulation(base, reference.Files) + ". Correct the configured root and files."}
95
+ }
96
+ if len(state.Units) == 0 {
97
+ return state, []string{context + " found no selected evidence units (" + reference.Symbols.names() + "). Select symbol kinds present in its exported declarations."}
98
+ }
99
+ return state, nil
100
+ }
101
+
102
+ func (loader *typeScriptLoader) withinBoundary(module string) bool {
103
+ if loader.boundary == nil {
104
+ return true
105
+ }
106
+ base, baseOK := loader.moduleIdentity(loader.boundary.Absolute)
107
+ source, sourceOK := loader.moduleIdentity(module)
108
+ if !baseOK || !sourceOK {
109
+ return false
110
+ }
111
+ relative, ok := relativeProjectPath(resolveProjectPath(loader.identityRoot, base), resolveProjectPath(loader.identityRoot, source))
112
+ if !ok || strings.Contains("/"+relative+"/", "/node_modules/") {
113
+ return false
114
+ }
115
+ return true
116
+ }
@@ -0,0 +1,156 @@
1
+ package evidence
2
+
3
+ import (
4
+ "encoding/json"
5
+ "sort"
6
+
7
+ "github.com/samchon/ttsc/packages/lint/rule"
8
+ )
9
+
10
+ func decodeGraphSeverity(raw json.RawMessage, path string) (*rule.Severity, []string) {
11
+ if len(raw) == 0 {
12
+ return nil, nil
13
+ }
14
+ var severity rule.Severity
15
+ var value any
16
+ if err := json.Unmarshal(raw, &value); err != nil {
17
+ return nil, []string{"Invalid evidence/graph configuration at " + path + ": expected a lint severity value."}
18
+ }
19
+ switch value {
20
+ case "off", float64(0):
21
+ severity = rule.SeverityOff
22
+ case "warning", "warn", float64(1):
23
+ severity = rule.SeverityWarn
24
+ case "error", float64(2):
25
+ severity = rule.SeverityError
26
+ default:
27
+ return nil, []string{"Invalid evidence/graph configuration at " + path + ": expected 'off', 'warning', 'warn', 'error', 0, 1, or 2; omit severity or use undefined to inherit the enclosing level."}
28
+ }
29
+ return &severity, nil
30
+ }
31
+
32
+ // Resolve inheritance once, before populations are loaded. A nil severity is
33
+ // unset; an explicit zero disables its population.
34
+ func resolveGraphSeverities(config *graphConfig, severity rule.Severity) {
35
+ for i := range config.Claims {
36
+ claim := &config.Claims[i]
37
+ claim.Level = severity
38
+ if claim.Severity != nil {
39
+ claim.Level = *claim.Severity
40
+ }
41
+ for j := range claim.References {
42
+ reference := &claim.References[j]
43
+ reference.Level = claim.Level
44
+ if reference.Severity != nil {
45
+ reference.Level = *reference.Severity
46
+ }
47
+ }
48
+ }
49
+ }
50
+
51
+ type graphDiagnostic struct {
52
+ Message string
53
+ Severity rule.Severity
54
+ }
55
+
56
+ type graphDiagnostics []graphDiagnostic
57
+
58
+ func (problems graphDiagnostics) add(severity rule.Severity, messages ...string) graphDiagnostics {
59
+ for _, message := range messages {
60
+ problems = append(problems, graphDiagnostic{Message: message, Severity: severity})
61
+ }
62
+ return problems
63
+ }
64
+
65
+ func (problems graphDiagnostics) report(ctx *rule.ProjectContext) {
66
+ levels := map[string]rule.Severity{}
67
+ for _, problem := range problems {
68
+ levels[problem.Message] = max(levels[problem.Message], problem.Severity)
69
+ }
70
+ messages := make([]string, 0, len(levels))
71
+ for message := range levels {
72
+ messages = append(messages, message)
73
+ }
74
+ sort.Strings(messages)
75
+ for _, message := range messages {
76
+ ctx.ReportSeverity(levels[message], message)
77
+ }
78
+ }
79
+
80
+ // A shared source problem belongs to every population that reads it. Keep the
81
+ // strongest owning level without pooling independent coverage obligations.
82
+ func populationSeverity(config graphConfig, kind artifactKind, base populationBase, path string, symbol string, descendants bool) rule.Severity {
83
+ var severity rule.Severity
84
+ matches := func(candidate populationBase, files globSet, symbols symbolSet) bool {
85
+ return candidate.Absolute == base.Absolute &&
86
+ (path == "" || files.matches(path) || descendants && files.couldMatchDescendant(path)) &&
87
+ (symbol == "*" || symbols.contains(symbol))
88
+ }
89
+ for _, claim := range config.Claims {
90
+ if claim.Type == kind && matches(claim.Base, claim.Files, claim.Symbols) {
91
+ severity = max(severity, claim.Level)
92
+ }
93
+ for _, reference := range claim.References {
94
+ if reference.Type == kind && reference.Package == "" && matches(reference.Base, reference.Files, reference.Symbols) {
95
+ severity = max(severity, reference.Level)
96
+ }
97
+ }
98
+ }
99
+ return severity
100
+ }
101
+
102
+ func artifactSeverity(config graphConfig, kind artifactKind) rule.Severity {
103
+ var severity rule.Severity
104
+ for _, claim := range config.Claims {
105
+ if claim.Type == kind {
106
+ severity = max(severity, claim.Level)
107
+ }
108
+ for _, reference := range claim.References {
109
+ if reference.Type == kind {
110
+ severity = max(severity, reference.Level)
111
+ }
112
+ }
113
+ }
114
+ return severity
115
+ }
116
+
117
+ func swaggerSeverity(config graphConfig, source string) rule.Severity {
118
+ var severity rule.Severity
119
+ for _, claim := range config.Claims {
120
+ for _, reference := range claim.References {
121
+ if reference.Type == artifactSwagger && reference.Source == source {
122
+ severity = max(severity, reference.Level)
123
+ }
124
+ }
125
+ }
126
+ return severity
127
+ }
128
+
129
+ func ownerSeverity(owners []claimState) rule.Severity {
130
+ var severity rule.Severity
131
+ for _, owner := range owners {
132
+ severity = max(severity, owner.Spec.Level)
133
+ }
134
+ return severity
135
+ }
136
+
137
+ func inventorySeverity(config graphConfig, kind artifactKind, address string, symbol string) rule.Severity {
138
+ var severity rule.Severity
139
+ for _, base := range configuredBases(config, kind) {
140
+ if relative, ok := base.relativeOf(address); ok {
141
+ severity = max(severity, populationSeverity(config, kind, base, relative, symbol, false))
142
+ }
143
+ }
144
+ return severity
145
+ }
146
+
147
+ // Empty or failed populations contribute no file to the shared Prisma parse.
148
+ func prismaSetSeverity(config graphConfig, inventories map[string]*artifactInventory) rule.Severity {
149
+ var severity rule.Severity
150
+ for address, inventory := range inventories {
151
+ if inventory != nil && inventory.FailureBase == "" {
152
+ severity = max(severity, inventorySeverity(config, artifactPrisma, address, "*"))
153
+ }
154
+ }
155
+ return severity
156
+ }
package/native/swagger.go CHANGED
@@ -11,6 +11,8 @@ import (
11
11
  "sort"
12
12
  "strings"
13
13
  "time"
14
+
15
+ "github.com/samchon/ttsc/packages/lint/rule"
14
16
  )
15
17
 
16
18
  const swaggerBridgeTimeout = 60 * time.Second
@@ -94,7 +96,7 @@ type swaggerOperation struct {
94
96
  func loadSwaggerInventories(
95
97
  root string,
96
98
  config graphConfig,
97
- ) (map[string]*artifactInventory, []string) {
99
+ ) (map[string]*artifactInventory, graphDiagnostics) {
98
100
  sources := configuredSwaggerSources(config)
99
101
  inventories := map[string]*artifactInventory{}
100
102
  for _, source := range sources {
@@ -114,15 +116,16 @@ func loadSwaggerInventories(
114
116
  // TypeScript keystroke that triggers a rebuild.
115
117
  digests := swaggerContentDigests(root, sources)
116
118
  pending := []string{}
117
- problems := []string{}
119
+ problems := graphDiagnostics{}
120
+ severity := artifactSeverity(config, artifactSwagger)
118
121
  for _, source := range sources {
119
122
  outcome, hit := lookupSwaggerDocument(source, digests[source])
120
123
  if !hit {
121
124
  pending = append(pending, source)
122
125
  continue
123
126
  }
124
- problems = append(
125
- problems,
127
+ problems = problems.add(
128
+ swaggerSeverity(config, source),
126
129
  swaggerUnitsFromOutcome(source, inventories[source], outcome)...,
127
130
  )
128
131
  }
@@ -140,30 +143,34 @@ func loadSwaggerInventories(
140
143
  inventoryProblem{Symbol: "operation", Message: message},
141
144
  )
142
145
  }
143
- return inventories, append(problems, message)
146
+ var pendingSeverity rule.Severity
147
+ for _, source := range pending {
148
+ pendingSeverity = max(pendingSeverity, swaggerSeverity(config, source))
149
+ }
150
+ return inventories, problems.add(pendingSeverity, message)
144
151
  }
145
152
 
146
153
  seen := map[string]bool{}
147
154
  for _, document := range result.Documents {
148
155
  inventory := inventories[document.Source]
149
156
  if inventory == nil {
150
- problems = append(
151
- problems,
157
+ problems = problems.add(
158
+ severity,
152
159
  "Evidence graph Swagger normalizer returned an unconfigured source '"+displaySwaggerSource(document.Source)+"'. Reinstall @ttsc/evidence; the native and JavaScript bridge contracts disagree.",
153
160
  )
154
161
  continue
155
162
  }
156
163
  if seen[document.Source] {
157
- problems = append(
158
- problems,
164
+ problems = problems.add(
165
+ swaggerSeverity(config, document.Source),
159
166
  "Evidence graph Swagger normalizer returned source '"+displaySwaggerSource(document.Source)+"' more than once. Reinstall @ttsc/evidence; the native and JavaScript bridge contracts disagree.",
160
167
  )
161
168
  continue
162
169
  }
163
170
  seen[document.Source] = true
164
171
  outcome := swaggerDocumentOutcome{Operations: document.Operations}
165
- problems = append(
166
- problems,
172
+ problems = problems.add(
173
+ swaggerSeverity(config, document.Source),
167
174
  swaggerUnitsFromOutcome(document.Source, inventory, outcome)...,
168
175
  )
169
176
  rememberSwaggerDocument(document.Source, document.Digest, outcome)
@@ -171,8 +178,8 @@ func loadSwaggerInventories(
171
178
  for _, problem := range result.Problems {
172
179
  inventory := inventories[problem.Source]
173
180
  if inventory == nil {
174
- problems = append(
175
- problems,
181
+ problems = problems.add(
182
+ severity,
176
183
  "Evidence graph Swagger normalizer rejected an unconfigured source '"+displaySwaggerSource(problem.Source)+"'. Reinstall @ttsc/evidence; the native and JavaScript bridge contracts disagree.",
177
184
  )
178
185
  continue
@@ -182,8 +189,8 @@ func loadSwaggerInventories(
182
189
  Rejected: true,
183
190
  Problem: problem.Message,
184
191
  }
185
- problems = append(
186
- problems,
192
+ problems = problems.add(
193
+ swaggerSeverity(config, problem.Source),
187
194
  swaggerUnitsFromOutcome(problem.Source, inventory, outcome)...,
188
195
  )
189
196
  rememberSwaggerDocument(problem.Source, problem.Digest, outcome)
@@ -198,7 +205,7 @@ func loadSwaggerInventories(
198
205
  inventories[source].Problems,
199
206
  inventoryProblem{Symbol: "operation", Message: message},
200
207
  )
201
- problems = append(problems, message)
208
+ problems = problems.add(swaggerSeverity(config, source), message)
202
209
  }
203
210
  return inventories, problems
204
211
  }
@@ -71,16 +71,13 @@ func extendTypeScriptInventories(
71
71
  // three kinds converge: health alone now decides that the claim stays active,
72
72
  // and the population it names is the one the author has to repair.
73
73
  //
74
- // Only claim bases can reach a declared root here. A TypeScript reference selects
75
- // the active program with `files` or an installed package with `package` and is
76
- // refused a `root` outright, so every reference base of this kind is the default
77
- // one, which this pass leaves alone: `baseDirectoryProblem` returns on it, and
78
- // the resolver question below is asked only of a root someone declared.
74
+ // Check passes claim populations here. Rooted references validate their disk
75
+ // directories after claim activation in materializeRootedTypeScriptReference.
79
76
  func typeScriptBaseProblems(
80
77
  config graphConfig,
81
78
  inventories map[string]*artifactInventory,
82
- ) []string {
83
- problems := []string{}
79
+ ) graphDiagnostics {
80
+ problems := graphDiagnostics{}
84
81
  for _, base := range configuredBases(config, artifactTypeScript) {
85
82
  problem := baseDirectoryProblem(base, artifactTypeScript)
86
83
  // The stat accepts a chain of links the resolver will not finish, and this
@@ -111,7 +108,7 @@ func typeScriptBaseProblems(
111
108
  continue
112
109
  }
113
110
  recordPopulationFailure(inventories, artifactTypeScript, base)
114
- problems = append(problems, problem)
111
+ problems = problems.add(populationSeverity(config, artifactTypeScript, base, "", "*", false), problem)
115
112
  }
116
113
  return problems
117
114
  }
@@ -245,6 +242,7 @@ func scanTypeScriptInventoryAt(
245
242
  file *shimast.SourceFile,
246
243
  ) *artifactInventory {
247
244
  inventory := &artifactInventory{
245
+ Source: file,
248
246
  Address: address.Key,
249
247
  Path: address.Display,
250
248
  Type: artifactTypeScript,
@@ -528,7 +526,7 @@ func collectTypeScriptStatements(
528
526
  if typeOnlyProjection {
529
527
  continue
530
528
  }
531
- name := declarationName(statement.Name())
529
+ name := typeScriptDeclarationName(statement)
532
530
  if name == "" {
533
531
  continue
534
532
  }
@@ -583,7 +581,7 @@ func collectTypeScriptStatements(
583
581
  addTypeScriptHost(supportedHosts, statement, symbol)
584
582
  }
585
583
  case shimast.KindClassDeclaration:
586
- name := declarationName(statement.Name())
584
+ name := typeScriptDeclarationName(statement)
587
585
  if name == "" {
588
586
  continue
589
587
  }
@@ -1377,7 +1375,7 @@ func collectHiddenDeclarationNames(
1377
1375
  if statement == nil {
1378
1376
  continue
1379
1377
  }
1380
- name := declarationName(statement.Name())
1378
+ name := typeScriptDeclarationName(statement)
1381
1379
  if name == "" {
1382
1380
  continue
1383
1381
  }
@@ -1701,6 +1699,12 @@ func walkTypeScriptNode(node *shimast.Node, visit func(*shimast.Node)) {
1701
1699
  type exportedName struct {
1702
1700
  Public string
1703
1701
  TypeOnly bool
1702
+ // IdentityOnly materializes a default's local declaration without
1703
+ // publishing its binding as a named export.
1704
+ IdentityOnly bool
1705
+ // ValueIdentity retains the value side needed by a default alias while
1706
+ // TypeOnly still describes this named export's public restriction.
1707
+ ValueIdentity bool
1704
1708
  }
1705
1709
 
1706
1710
  func collectLocalExportNames(
@@ -1748,6 +1752,21 @@ func collectLocalExportNames(
1748
1752
  })
1749
1753
  }
1750
1754
  }
1755
+ for local, binding := range collectDefaultExportBindings(statements) {
1756
+ if len(exports[local]) == 0 {
1757
+ binding.IdentityOnly = true
1758
+ exports[local] = []exportedName{binding}
1759
+ } else if !binding.TypeOnly {
1760
+ selected := 0
1761
+ for index, name := range exports[local] {
1762
+ current := exports[local][selected]
1763
+ if current.TypeOnly && !name.TypeOnly || current.TypeOnly == name.TypeOnly && name.Public < current.Public {
1764
+ selected = index
1765
+ }
1766
+ }
1767
+ exports[local][selected].ValueIdentity = true
1768
+ }
1769
+ }
1751
1770
  return exports
1752
1771
  }
1753
1772
 
@@ -1787,6 +1806,9 @@ func publicTypeScriptExports(
1787
1806
  names[local] = exportedName{Public: local}
1788
1807
  }
1789
1808
  for _, exported := range exports[local] {
1809
+ if exported.ValueIdentity {
1810
+ exported.TypeOnly = false
1811
+ }
1790
1812
  if exported.TypeOnly && !allowTypeOnly {
1791
1813
  continue
1792
1814
  }
@@ -1820,9 +1842,6 @@ func declarationName(node *shimast.Node) string {
1820
1842
  shimast.KindStringLiteral,
1821
1843
  shimast.KindNumericLiteral:
1822
1844
  name := node.Text()
1823
- if containsWhitespace(name) {
1824
- return ""
1825
- }
1826
1845
  return name
1827
1846
  default:
1828
1847
  return ""
@@ -29,11 +29,12 @@ import (
29
29
  // depend on the hit.
30
30
  var typeScriptInventories = newTypeScriptInventoryCache()
31
31
 
32
- // typeScriptInventoryKey identifies one scan. The address is part of it because
33
- // one physical file can be addressed differently under two configured roots,
34
- // and the address is baked into every unit identity the scan produces.
32
+ // typeScriptInventoryKey identifies one scan. Both the unit address and display
33
+ // path belong to its result. Root aliases can share unit IDs while a file has
34
+ // different project-relative paths; reusing the old path can read another file.
35
35
  type typeScriptInventoryKey struct {
36
36
  address string
37
+ display string
37
38
  file *shimast.SourceFile
38
39
  }
39
40
 
@@ -69,7 +70,7 @@ func (cache *typeScriptInventoryCache) scan(
69
70
  if cache == nil || file == nil {
70
71
  return scanTypeScriptInventoryAt(address, file)
71
72
  }
72
- key := typeScriptInventoryKey{address: address.Key, file: file}
73
+ key := typeScriptInventoryKey{address: address.Key, display: address.Display, file: file}
73
74
  cache.mutex.Lock()
74
75
  if inventory, hit := cache.live[key]; hit {
75
76
  cache.mutex.Unlock()
@@ -1,7 +1,6 @@
1
1
  package evidence
2
2
 
3
3
  import (
4
- "sort"
5
4
  "strings"
6
5
 
7
6
  shimast "github.com/microsoft/typescript-go/shim/ast"
@@ -142,14 +141,14 @@ func readableCommentBody(comment string) string {
142
141
  func unreadableTypeScriptTags(
143
142
  inventories map[string]*artifactInventory,
144
143
  governed map[string]bool,
145
- ) []string {
146
- reported := []string{}
144
+ config graphConfig,
145
+ ) graphDiagnostics {
146
+ reported := graphDiagnostics{}
147
147
  for address, inventory := range inventories {
148
- if inventory == nil || !governed[address] {
148
+ if inventory == nil || len(inventory.Unreadable) == 0 || !governed[address] {
149
149
  continue
150
150
  }
151
- reported = append(reported, inventory.Unreadable...)
151
+ reported = reported.add(inventorySeverity(config, artifactTypeScript, address, "*"), inventory.Unreadable...)
152
152
  }
153
- sort.Strings(reported)
154
153
  return reported
155
154
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ttsc/evidence",
3
- "version": "0.28.6",
3
+ "version": "0.30.0",
4
4
  "description": "Evidence-graph lint contributor for @ttsc/lint: declare provenance with a JSDoc @evidence tag and fail the build when it is missing or dangling.",
5
5
  "main": "lib/index.js",
6
6
  "exports": {
@@ -42,19 +42,19 @@
42
42
  "url": "https://github.com/samchon/ttsc/issues"
43
43
  },
44
44
  "peerDependencies": {
45
- "@ttsc/lint": ">=0.24.0"
45
+ "@ttsc/lint": ">0.28.6"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@types/node": "^25.3.0",
49
49
  "rimraf": "^6.1.2",
50
50
  "typescript": "^7.0.2",
51
- "@ttsc/lint": "0.28.6",
52
- "ttsc": "0.28.6"
51
+ "@ttsc/lint": "0.30.0",
52
+ "ttsc": "0.30.0"
53
53
  },
54
54
  "dependencies": {
55
55
  "@prisma/prisma-schema-wasm": "7.10.0-2.d0368e25c88e68caa4d283b5f2d88d1b76ed9356",
56
- "@typia/interface": "14.0.0",
57
- "@typia/utils": "14.0.0",
56
+ "@typia/interface": "^14.0.6",
57
+ "@typia/utils": "^14.0.6",
58
58
  "yaml": "^2.9.0"
59
59
  },
60
60
  "homepage": "https://ttsc.dev",