@zyno-io/ts-server-foundation 26.714.5 → 26.714.423
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/src/app/base.js +1 -1
- package/dist/src/app/index.js +1 -1
- package/dist/src/database/database.js +1 -1
- package/dist/src/database/index.js +1 -1
- package/dist/src/database/metadata.js +1 -1
- package/dist/src/database/query.d.ts.map +1 -1
- package/dist/src/database/query.js +6 -8
- package/dist/src/database/query.js.map +1 -1
- package/dist/src/devconsole/server.js +3 -3
- package/dist/src/devconsole/server.js.map +1 -1
- package/dist/src/di/container.js +1 -1
- package/dist/src/di/index.js +1 -1
- package/dist/src/di/module.js +1 -1
- package/dist/src/http/base.js +1 -1
- package/dist/src/http/index.js +1 -1
- package/dist/src/http/request.js +2 -2
- package/dist/src/http/response.js +3 -3
- package/dist/src/http/router.js +2 -3
- package/dist/src/http/workflow.js +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/openapi/index.js +1 -1
- package/dist/src/openapi/schema.js +1 -1
- package/dist/src/openapi/types.js +1 -1
- package/dist/src/reflection/conversion.d.ts.map +1 -1
- package/dist/src/reflection/conversion.js +3 -5
- package/dist/src/reflection/conversion.js.map +1 -1
- package/dist/src/reflection/deserializer.js +1 -1
- package/dist/src/reflection/index.js +1 -1
- package/dist/src/reflection/model.js +1 -1
- package/dist/src/reflection/primitive-conversion.d.ts +2 -0
- package/dist/src/reflection/primitive-conversion.d.ts.map +1 -0
- package/dist/src/reflection/primitive-conversion.js +19 -0
- package/dist/src/reflection/primitive-conversion.js.map +1 -0
- package/dist/src/reflection/reflection-class.js +3 -3
- package/dist/src/reflection/type-utils.d.ts.map +1 -1
- package/dist/src/reflection/type-utils.js +13 -0
- package/dist/src/reflection/type-utils.js.map +1 -1
- package/dist/src/services/index.js +1 -1
- package/dist/src/services/mesh-client/index.js +1 -1
- package/dist/src/services/mesh-client/mesh-client-redis-registry.js +1 -1
- package/dist/src/services/mesh-client/mesh-client-registry.js +1 -1
- package/dist/src/services/mesh-client/types.js +1 -1
- package/dist/src/testing/index.js +4 -4
- package/dist/src/testing/index.js.map +1 -1
- package/dist/src/testing/sql.js +1 -1
- package/dist/src/type-compiler/go/ast_metadata.go +1 -1
- package/dist/src/type-compiler/go/collect.go +17 -2
- package/dist/src/type-compiler/go/emission_plan.go +1 -1
- package/dist/src/type-compiler/go/plugin_test.go +148 -1
- package/dist/src/type-compiler/go/receive_type.go +67 -1
- package/dist/src/type-compiler/go/text_parse.go +65 -2
- package/dist/src/type-compiler/go/type_expr.go +5 -9
- package/dist/src/type-compiler/go/typia_expr.go +104 -15
- package/docs/content/database.md +11 -9
- package/docs/content/sql.md +1 -1
- package/package.json +1 -1
|
@@ -10,12 +10,8 @@ func typeExpr(info *fileInfo, reg *registry, raw string) string {
|
|
|
10
10
|
return typeExprCtx(info, reg, raw, &typeContext{seen: map[string]bool{}})
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
func internalTypeExprAt(info *fileInfo, reg *registry, raw string, pos int) string {
|
|
14
|
-
return typeExprCtx(info, reg, raw, &typeContext{seen: map[string]bool{}, pos: pos})
|
|
15
|
-
}
|
|
16
|
-
|
|
17
13
|
func typeExprCtx(info *fileInfo, reg *registry, raw string, ctx *typeContext) string {
|
|
18
|
-
raw = strings.TrimSpace(raw)
|
|
14
|
+
raw = strings.TrimSpace(stripTypeComments(raw))
|
|
19
15
|
raw = trimParens(raw)
|
|
20
16
|
raw = strings.TrimSpace(strings.TrimPrefix(raw, "readonly "))
|
|
21
17
|
if raw == "" || raw == "unknown" {
|
|
@@ -415,7 +411,7 @@ func renderUtilityProperty(info *fileInfo, reg *registry, prop utilityProperty,
|
|
|
415
411
|
if prop.owner != nil {
|
|
416
412
|
owner = prop.owner
|
|
417
413
|
}
|
|
418
|
-
t :=
|
|
414
|
+
t := internalTypeExprForNodeCtx(owner, reg, prop.typeText, prop.typeNode, ctx)
|
|
419
415
|
if prop.optional {
|
|
420
416
|
t = "{kind: 12, types: [" + t + ", {kind: 4}]}"
|
|
421
417
|
}
|
|
@@ -436,7 +432,7 @@ func renderPreferredUtilityProperty(info *fileInfo, reg *registry, prop utilityP
|
|
|
436
432
|
}
|
|
437
433
|
var t string
|
|
438
434
|
if sourceTypeNeedsInternalPropertyMetadata(owner, reg, prop.typeText, &typeContext{seen: map[string]bool{}, pos: pos}, map[string]bool{}) {
|
|
439
|
-
t =
|
|
435
|
+
t = internalTypeExprForNode(owner, reg, prop.typeText, prop.typeNode, pos)
|
|
440
436
|
} else {
|
|
441
437
|
t = typeExprForNodePreferred(owner, reg, prop.typeText, prop.typeNode, pos, true)
|
|
442
438
|
}
|
|
@@ -634,7 +630,7 @@ func utilitySourceProperties(info *fileInfo, reg *registry, source string, ctx *
|
|
|
634
630
|
if class, owner, ok := resolveClassRefAt(info, reg, source, ctx.pos); ok {
|
|
635
631
|
props := make([]utilityProperty, 0, len(class.properties))
|
|
636
632
|
for _, prop := range class.properties {
|
|
637
|
-
props = append(props, utilityProperty{name: prop.name, typeText: prop.typeText, optional: prop.optional, owner: owner})
|
|
633
|
+
props = append(props, utilityProperty{name: prop.name, typeText: prop.typeText, typeNode: prop.typeNode, optional: prop.optional, owner: owner})
|
|
638
634
|
}
|
|
639
635
|
return props, owner, true
|
|
640
636
|
}
|
|
@@ -1097,7 +1093,7 @@ func aliasTypeExprCtx(owner *fileInfo, reg *registry, alias aliasInfo, name stri
|
|
|
1097
1093
|
if strings.TrimSpace(alias.metadataText) != "" {
|
|
1098
1094
|
return withTypeName(alias.metadataText, name)
|
|
1099
1095
|
}
|
|
1100
|
-
return withTypeName(
|
|
1096
|
+
return withTypeName(internalTypeExprForNodeCtx(owner, reg, alias.body, alias.typeNode, ctx), name)
|
|
1101
1097
|
}
|
|
1102
1098
|
|
|
1103
1099
|
func ensureAliasMetadata(owner *fileInfo, reg *registry, name string, alias aliasInfo) aliasInfo {
|
|
@@ -18,6 +18,7 @@ func typeExprForNode(info *fileInfo, reg *registry, raw string, node *shimast.No
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
func typeExprForNodePreferred(info *fileInfo, reg *registry, raw string, node *shimast.Node, pos int, preferTypia bool) string {
|
|
21
|
+
raw = strings.TrimSpace(stripTypeComments(raw))
|
|
21
22
|
if preferTypia {
|
|
22
23
|
if expr, ok := preferredWrapperTypeExprForNode(info, reg, raw, node, pos); ok {
|
|
23
24
|
return expr
|
|
@@ -25,16 +26,16 @@ func typeExprForNodePreferred(info *fileInfo, reg *registry, raw string, node *s
|
|
|
25
26
|
if expr, ok := preferredCachedAliasTypeExpr(info, reg, raw, pos); ok {
|
|
26
27
|
return expr
|
|
27
28
|
}
|
|
28
|
-
if expr, ok := preferredNullishAliasTypeExpr(info, reg, raw, pos); ok {
|
|
29
|
+
if expr, ok := preferredNullishAliasTypeExpr(info, reg, raw, node, pos); ok {
|
|
29
30
|
return expr
|
|
30
31
|
}
|
|
31
|
-
if expr, ok := preferredDateRootTypeExpr(info, reg, raw, pos); ok {
|
|
32
|
+
if expr, ok := preferredDateRootTypeExpr(info, reg, raw, node, pos); ok {
|
|
32
33
|
return expr
|
|
33
34
|
}
|
|
34
35
|
if expr, ok := preferredExternalImportedTypeExpr(info, raw); ok {
|
|
35
36
|
return expr
|
|
36
37
|
}
|
|
37
|
-
if expr, ok := preferredExternalImportedCompositeTypeExpr(info, reg, raw, pos); ok {
|
|
38
|
+
if expr, ok := preferredExternalImportedCompositeTypeExpr(info, reg, raw, node, pos); ok {
|
|
38
39
|
return expr
|
|
39
40
|
}
|
|
40
41
|
if expr, ok := preferredNamedInterfaceTypeExpr(info, reg, raw, pos); ok {
|
|
@@ -47,14 +48,78 @@ func typeExprForNodePreferred(info *fileInfo, reg *registry, raw string, node *s
|
|
|
47
48
|
}
|
|
48
49
|
}
|
|
49
50
|
if shouldUseTextTypeExpr(raw) {
|
|
50
|
-
return
|
|
51
|
+
return internalTypeExprForNode(info, reg, raw, node, pos)
|
|
51
52
|
}
|
|
52
53
|
if shouldUseTypiaType(info, reg, raw) && canPreferTypiaType(info, reg, raw) {
|
|
53
54
|
if expr, ok := typiaTypeExprForNode(info, reg, raw, node, pos); ok {
|
|
54
55
|
return typiaSourceNamedExpr(info, reg, expr, raw)
|
|
55
56
|
}
|
|
56
57
|
}
|
|
57
|
-
return
|
|
58
|
+
return internalTypeExprForNode(info, reg, raw, node, pos)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
func internalTypeExprForNode(info *fileInfo, reg *registry, raw string, node *shimast.Node, pos int) string {
|
|
62
|
+
return internalTypeExprForNodeCtx(info, reg, raw, node, &typeContext{seen: map[string]bool{}, pos: pos})
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
func internalTypeExprForNodeCtx(info *fileInfo, reg *registry, raw string, node *shimast.Node, ctx *typeContext) string {
|
|
66
|
+
if ctx == nil {
|
|
67
|
+
ctx = &typeContext{seen: map[string]bool{}}
|
|
68
|
+
}
|
|
69
|
+
if node == nil {
|
|
70
|
+
return typeExprCtx(info, reg, raw, ctx)
|
|
71
|
+
}
|
|
72
|
+
if node.Kind == shimast.KindParenthesizedType {
|
|
73
|
+
inner := node.AsParenthesizedTypeNode().Type
|
|
74
|
+
if innerRaw, ok := sourceTypeNodeText(info, inner); ok {
|
|
75
|
+
return internalTypeExprForNodeCtx(info, reg, innerRaw, inner, ctx)
|
|
76
|
+
}
|
|
77
|
+
return typeExprCtx(info, reg, raw, ctx)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
var kind int
|
|
81
|
+
var nodes []*shimast.Node
|
|
82
|
+
switch node.Kind {
|
|
83
|
+
case shimast.KindUnionType:
|
|
84
|
+
kind = 12
|
|
85
|
+
if types := node.AsUnionTypeNode().Types; types != nil {
|
|
86
|
+
nodes = types.Nodes
|
|
87
|
+
}
|
|
88
|
+
case shimast.KindIntersectionType:
|
|
89
|
+
kind = 13
|
|
90
|
+
if types := node.AsIntersectionTypeNode().Types; types != nil {
|
|
91
|
+
nodes = types.Nodes
|
|
92
|
+
}
|
|
93
|
+
default:
|
|
94
|
+
return typeExprCtx(info, reg, raw, ctx)
|
|
95
|
+
}
|
|
96
|
+
if len(nodes) < 2 {
|
|
97
|
+
return typeExprCtx(info, reg, raw, ctx)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
types := make([]string, 0, len(nodes))
|
|
101
|
+
for _, child := range nodes {
|
|
102
|
+
childRaw, ok := sourceTypeNodeText(info, child)
|
|
103
|
+
if !ok {
|
|
104
|
+
return typeExprCtx(info, reg, raw, ctx)
|
|
105
|
+
}
|
|
106
|
+
types = append(types, internalTypeExprForNodeCtx(info, reg, childRaw, child, ctx))
|
|
107
|
+
}
|
|
108
|
+
return "{kind: " + strconv.Itoa(kind) + ", types: [" + strings.Join(types, ", ") + "]}"
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
func sourceTypeNodeText(info *fileInfo, node *shimast.Node) (string, bool) {
|
|
112
|
+
if node == nil {
|
|
113
|
+
return "", false
|
|
114
|
+
}
|
|
115
|
+
file := shimast.GetSourceFileOfNode(node)
|
|
116
|
+
if file == nil && info != nil {
|
|
117
|
+
file = info.file
|
|
118
|
+
}
|
|
119
|
+
if file == nil {
|
|
120
|
+
return "", false
|
|
121
|
+
}
|
|
122
|
+
return nodeText(file, node), true
|
|
58
123
|
}
|
|
59
124
|
|
|
60
125
|
func preferredCachedAliasTypeExpr(info *fileInfo, reg *registry, raw string, pos int) (string, bool) {
|
|
@@ -77,22 +142,46 @@ func preferredCachedAliasTypeExpr(info *fileInfo, reg *registry, raw string, pos
|
|
|
77
142
|
return aliasTypeExprCtx(owner, reg, alias, raw, &typeContext{seen: map[string]bool{}, pos: pos}), true
|
|
78
143
|
}
|
|
79
144
|
|
|
80
|
-
func preferredNullishAliasTypeExpr(info *fileInfo, reg *registry, raw string, pos int) (string, bool) {
|
|
145
|
+
func preferredNullishAliasTypeExpr(info *fileInfo, reg *registry, raw string, node *shimast.Node, pos int) (string, bool) {
|
|
81
146
|
types := []string{}
|
|
82
|
-
aliasCount, ok := collectNullishAliasTypeExprs(info, reg, raw, pos, &types)
|
|
147
|
+
aliasCount, ok := collectNullishAliasTypeExprs(info, reg, raw, node, pos, &types)
|
|
83
148
|
if !ok || aliasCount != 1 || len(types) <= 1 {
|
|
84
149
|
return "", false
|
|
85
150
|
}
|
|
86
151
|
return "{kind: 12, types: [" + strings.Join(types, ", ") + "]}", true
|
|
87
152
|
}
|
|
88
153
|
|
|
89
|
-
func collectNullishAliasTypeExprs(info *fileInfo, reg *registry, raw string, pos int, types *[]string) (int, bool) {
|
|
154
|
+
func collectNullishAliasTypeExprs(info *fileInfo, reg *registry, raw string, node *shimast.Node, pos int, types *[]string) (int, bool) {
|
|
90
155
|
raw = strings.TrimSpace(trimParens(raw))
|
|
156
|
+
if node != nil && node.Kind == shimast.KindParenthesizedType {
|
|
157
|
+
inner := node.AsParenthesizedTypeNode().Type
|
|
158
|
+
if innerRaw, ok := sourceTypeNodeText(info, inner); ok {
|
|
159
|
+
return collectNullishAliasTypeExprs(info, reg, innerRaw, inner, pos, types)
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if node != nil && node.Kind == shimast.KindUnionType {
|
|
163
|
+
union := node.AsUnionTypeNode()
|
|
164
|
+
if union.Types != nil && len(union.Types.Nodes) > 1 {
|
|
165
|
+
aliasCount := 0
|
|
166
|
+
for _, child := range union.Types.Nodes {
|
|
167
|
+
childRaw, ok := sourceTypeNodeText(info, child)
|
|
168
|
+
if !ok {
|
|
169
|
+
return 0, false
|
|
170
|
+
}
|
|
171
|
+
count, ok := collectNullishAliasTypeExprs(info, reg, childRaw, child, pos, types)
|
|
172
|
+
if !ok {
|
|
173
|
+
return 0, false
|
|
174
|
+
}
|
|
175
|
+
aliasCount += count
|
|
176
|
+
}
|
|
177
|
+
return aliasCount, true
|
|
178
|
+
}
|
|
179
|
+
}
|
|
91
180
|
parts := nonEmptyParts(splitTop(raw, "|"))
|
|
92
181
|
if len(parts) > 1 {
|
|
93
182
|
aliasCount := 0
|
|
94
183
|
for _, part := range parts {
|
|
95
|
-
count, ok := collectNullishAliasTypeExprs(info, reg, part, pos, types)
|
|
184
|
+
count, ok := collectNullishAliasTypeExprs(info, reg, part, nil, pos, types)
|
|
96
185
|
if !ok {
|
|
97
186
|
return 0, false
|
|
98
187
|
}
|
|
@@ -117,11 +206,11 @@ func collectNullishAliasTypeExprs(info *fileInfo, reg *registry, raw string, pos
|
|
|
117
206
|
}
|
|
118
207
|
}
|
|
119
208
|
|
|
120
|
-
func preferredDateRootTypeExpr(info *fileInfo, reg *registry, raw string, pos int) (string, bool) {
|
|
209
|
+
func preferredDateRootTypeExpr(info *fileInfo, reg *registry, raw string, node *shimast.Node, pos int) (string, bool) {
|
|
121
210
|
if !sourceTypeIsDateRootType(info, reg, raw, &typeContext{seen: map[string]bool{}, pos: pos}, map[string]bool{}) {
|
|
122
211
|
return "", false
|
|
123
212
|
}
|
|
124
|
-
return
|
|
213
|
+
return internalTypeExprForNode(info, reg, raw, node, pos), true
|
|
125
214
|
}
|
|
126
215
|
|
|
127
216
|
func preferredExternalImportedTypeExpr(info *fileInfo, raw string) (string, bool) {
|
|
@@ -136,25 +225,25 @@ func preferredExternalImportedTypeExpr(info *fileInfo, raw string) (string, bool
|
|
|
136
225
|
return externalImportedTypeExpr(ref, raw), true
|
|
137
226
|
}
|
|
138
227
|
|
|
139
|
-
func preferredExternalImportedCompositeTypeExpr(info *fileInfo, reg *registry, raw string, pos int) (string, bool) {
|
|
228
|
+
func preferredExternalImportedCompositeTypeExpr(info *fileInfo, reg *registry, raw string, node *shimast.Node, pos int) (string, bool) {
|
|
140
229
|
raw = strings.TrimSpace(trimParens(raw))
|
|
141
230
|
if raw == "" || isIdentifierName(raw) {
|
|
142
231
|
return "", false
|
|
143
232
|
}
|
|
144
233
|
if parts := nonEmptyParts(splitTop(raw, "|")); len(parts) > 1 {
|
|
145
234
|
if typeContainsExternalImportReferenceInParts(info, reg, parts, map[string]bool{}) {
|
|
146
|
-
return
|
|
235
|
+
return internalTypeExprForNode(info, reg, raw, node, pos), true
|
|
147
236
|
}
|
|
148
237
|
}
|
|
149
238
|
if parts := nonEmptyParts(splitTop(raw, "&")); len(parts) > 1 {
|
|
150
239
|
if typeContainsExternalImportReferenceInParts(info, reg, parts, map[string]bool{}) {
|
|
151
|
-
return
|
|
240
|
+
return internalTypeExprForNode(info, reg, raw, node, pos), true
|
|
152
241
|
}
|
|
153
242
|
}
|
|
154
243
|
if strings.HasPrefix(raw, "[") && strings.HasSuffix(raw, "]") {
|
|
155
244
|
parts := nonEmptyParts(splitTop(strings.TrimSpace(raw[1:len(raw)-1]), ","))
|
|
156
245
|
if typeContainsExternalImportReferenceInParts(info, reg, parts, map[string]bool{}) {
|
|
157
|
-
return
|
|
246
|
+
return internalTypeExprForNode(info, reg, raw, node, pos), true
|
|
158
247
|
}
|
|
159
248
|
}
|
|
160
249
|
return "", false
|
package/docs/content/database.md
CHANGED
|
@@ -63,14 +63,14 @@ The migration generator reads entity metadata to infer table names, column names
|
|
|
63
63
|
|
|
64
64
|
Database I/O does not run the reflected HTTP-input deserializer or validator over whole entities. TSF instead performs a small set of column-aware storage conversions and otherwise relies on the database driver and database engine.
|
|
65
65
|
|
|
66
|
-
| Path | Outbound conversion | Inbound conversion
|
|
67
|
-
| ---------------------------------------------- | ------------------------------------------------------------- |
|
|
68
|
-
| Entity `save()` / `saveEntity()` | Column-aware | N/A
|
|
69
|
-
| Query-builder filters and patches | Column-aware | N/A
|
|
70
|
-
| Query-builder entity hydration | N/A | Column-aware
|
|
71
|
-
| `createEntity()` and related constructors | None; assigns application values and fills framework defaults | N/A
|
|
72
|
-
| `rawFind<T>()` / `rawFindOne<T>()` | Generic SQL binding normalization only | Driver values returned unchanged
|
|
73
|
-
| `rawFindUnsafe<T>()` / `rawFindOneUnsafe<T>()` | Generic SQL binding normalization only |
|
|
66
|
+
| Path | Outbound conversion | Inbound conversion | Reflected validation |
|
|
67
|
+
| ---------------------------------------------- | ------------------------------------------------------------- | ------------------------------------------------------------------ | -------------------- |
|
|
68
|
+
| Entity `save()` / `saveEntity()` | Column-aware | N/A | No |
|
|
69
|
+
| Query-builder filters and patches | Column-aware | N/A | No |
|
|
70
|
+
| Query-builder entity hydration | N/A | Column-aware | No |
|
|
71
|
+
| `createEntity()` and related constructors | None; assigns application values and fills framework defaults | N/A | No |
|
|
72
|
+
| `rawFind<T>()` / `rawFindOne<T>()` | Generic SQL binding normalization only | Driver values returned unchanged | No |
|
|
73
|
+
| `rawFindUnsafe<T>()` / `rawFindOneUnsafe<T>()` | Generic SQL binding normalization only | Type-directed `deserialize<T>()` with an explicit or inferable `T` | No |
|
|
74
74
|
|
|
75
75
|
Column-aware writes perform these conversions:
|
|
76
76
|
|
|
@@ -340,7 +340,9 @@ await db.rawExecute(
|
|
|
340
340
|
|
|
341
341
|
`rawQuery<T>()` is the base row-returning method, and `rawFind<T>()` is its alias. `rawFindOne()` returns the first row or `undefined`. `rawExecute()` returns `{ affectedRows, rowCount?, insertId?, warningStatus? }`.
|
|
342
342
|
|
|
343
|
-
The generic type on `rawFind<T>()` is a TypeScript-only assertion: returned rows keep the values produced by the database driver. The unsafe string methods have an additional compiler-injected reflected type slot, so `rawFindUnsafe<T>()` and `rawFindOneUnsafe<T>()`
|
|
343
|
+
The generic type on `rawFind<T>()` is a TypeScript-only assertion: returned rows keep the values produced by the database driver. The unsafe string methods have an additional compiler-injected reflected type slot, so an explicit or otherwise inferable `T` on `rawFindUnsafe<T>()` and `rawFindOneUnsafe<T>()` passes rows through `deserialize<T>()`. A call without a resolvable row type keeps the driver's values unchanged.
|
|
344
|
+
|
|
345
|
+
Reflected raw-row deserialization is type-directed rather than column-aware. It recursively converts numeric boolean values such as MySQL `TINYINT(1)` (`0`, `1`, or other numeric values) to booleans, converts numeric strings to numbers when the target is `number`, and can construct reflected classes. Consequently, type precision-sensitive `DECIMAL` results as `string`, not `number`, and treat deserialized entity instances as unmanaged objects without ORM snapshots. The operation does not validate rows; prefer the query builder when entity column-aware hydration and persistence tracking are required.
|
|
344
346
|
|
|
345
347
|
The unsafe variants accept a SQL string and bindings:
|
|
346
348
|
|
package/docs/content/sql.md
CHANGED
|
@@ -107,7 +107,7 @@ await session.rawExecuteUnsafe('UPDATE users SET active = ? WHERE id = ?', [fals
|
|
|
107
107
|
|
|
108
108
|
Prefer the `sql` tag for new code because identifiers and values stay explicit in the same expression.
|
|
109
109
|
|
|
110
|
-
When TSF's metadata compiler can
|
|
110
|
+
When TSF's metadata compiler can resolve an explicit or inferable `T`, `rawFindUnsafe<T>()` and `rawFindOneUnsafe<T>()` reflected-deserialize returned rows. Numeric values targeting booleans are converted to `true` or `false`, including MySQL `TINYINT(1)` results. Numeric strings targeting `number` are also converted, so precision-sensitive `DECIMAL` results should be typed as `string`. Calls without a resolvable row type keep driver-native values. Typed raw reads do not validate rows or attach ORM persistence state; use query-builder entity reads for column-aware hydration.
|
|
111
111
|
|
|
112
112
|
## Rendering For Logs Or Tests
|
|
113
113
|
|