ttsc 0.16.5 → 0.16.7
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/cmd/graphdump/main.go +1 -1
- package/internal/graph/decorators.go +6 -8
- package/internal/graph/dump.go +38 -54
- package/internal/graph/dump_serializes_the_full_graph_test.go +1 -1
- package/internal/graph/edges.go +43 -0
- package/internal/graph/graph.go +5 -2
- package/internal/graph/value_call_edges_attribute_function_assignments_to_members_test.go +59 -0
- package/package.json +8 -8
package/cmd/graphdump/main.go
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Command graphdump builds the @ttsc/graph code graph for a project and prints
|
|
2
2
|
// the entire graph (every node and every edge, with none of the MCP response
|
|
3
3
|
// caps) as one JSON document. It is the repo-internal one-shot used by the
|
|
4
|
-
// graph
|
|
4
|
+
// benchmark graph viewer pipeline; the shipped equivalent is `ttscgraph dump`. Both
|
|
5
5
|
// serialize through graph.MarshalDump.
|
|
6
6
|
package main
|
|
7
7
|
|
|
@@ -6,9 +6,8 @@ import (
|
|
|
6
6
|
|
|
7
7
|
// DecoratorArgument is one argument written on a decorator. Literal holds the
|
|
8
8
|
// statically-resolved value when the argument is a string or boolean literal
|
|
9
|
-
// and is nil otherwise
|
|
9
|
+
// and is nil otherwise.
|
|
10
10
|
type DecoratorArgument struct {
|
|
11
|
-
Text string
|
|
12
11
|
Literal any
|
|
13
12
|
}
|
|
14
13
|
|
|
@@ -26,8 +25,8 @@ type Decorator struct {
|
|
|
26
25
|
}
|
|
27
26
|
|
|
28
27
|
// collectDecorators records the decorators on each class plus its methods and
|
|
29
|
-
// properties, descending into namespace bodies. It is syntactic
|
|
30
|
-
// name and literal arguments are read straight from the AST
|
|
28
|
+
// properties, descending into namespace bodies. It is syntactic: a decorator's
|
|
29
|
+
// name and literal arguments are read straight from the AST because a consumer
|
|
31
30
|
// keys on the written convention name, not the resolved decorator symbol.
|
|
32
31
|
func (g *Graph) collectDecorators(path string, statements []*shimast.Node) {
|
|
33
32
|
for _, statement := range statements {
|
|
@@ -105,11 +104,10 @@ func decoratorFact(dec *shimast.Node) *Decorator {
|
|
|
105
104
|
return fact
|
|
106
105
|
}
|
|
107
106
|
|
|
108
|
-
// decoratorArg captures one decorator argument. Only string and boolean
|
|
109
|
-
// resolve to a Literal
|
|
110
|
-
// non-literal argument keeps its source Text with a nil Literal.
|
|
107
|
+
// decoratorArg captures one decorator argument. Only string and boolean
|
|
108
|
+
// literals resolve to a Literal, the values a consumer can use directly.
|
|
111
109
|
func decoratorArg(arg *shimast.Node) DecoratorArgument {
|
|
112
|
-
out := DecoratorArgument{
|
|
110
|
+
out := DecoratorArgument{}
|
|
113
111
|
switch arg.Kind {
|
|
114
112
|
case shimast.KindStringLiteral, shimast.KindNoSubstitutionTemplateLiteral:
|
|
115
113
|
out.Literal = arg.Text()
|
package/internal/graph/dump.go
CHANGED
|
@@ -8,7 +8,7 @@ import (
|
|
|
8
8
|
)
|
|
9
9
|
|
|
10
10
|
// dump.go projects a built graph onto the JSON wire contract `ttscgraph dump`
|
|
11
|
-
// prints
|
|
11
|
+
// prints: the IGraphDump shape the @ttsc/graph engine loads (and the 3D viewer
|
|
12
12
|
// reduces). The internal Node/Edge model stays narrow (so the resident MCP path
|
|
13
13
|
// is untouched); the richer schema is produced here:
|
|
14
14
|
//
|
|
@@ -16,7 +16,7 @@ import (
|
|
|
16
16
|
// - one EdgeValueCall splits by Origin into "calls" / "instantiates" /
|
|
17
17
|
// "renders", and EdgeHeritage into "extends" / "implements";
|
|
18
18
|
// - every edge is tagged checker-resolved / high (the graph's whole contract);
|
|
19
|
-
// - byte spans become 1-based line/col Evidence
|
|
19
|
+
// - byte spans become 1-based line/col Evidence ranges;
|
|
20
20
|
// - decorator facts ride on their target node;
|
|
21
21
|
// - file paths are project-relative and the output is sorted, so the dump is
|
|
22
22
|
// deterministic and diffable.
|
|
@@ -33,14 +33,12 @@ type DumpEvidence struct {
|
|
|
33
33
|
StartCol int `json:"startCol,omitempty"`
|
|
34
34
|
EndLine int `json:"endLine,omitempty"`
|
|
35
35
|
EndCol int `json:"endCol,omitempty"`
|
|
36
|
-
Text string `json:"text,omitempty"`
|
|
37
36
|
}
|
|
38
37
|
|
|
39
38
|
// DumpDecoratorArgument is one decorator argument; Literal is set only for a
|
|
40
39
|
// statically-resolved string or boolean literal.
|
|
41
40
|
type DumpDecoratorArgument struct {
|
|
42
|
-
|
|
43
|
-
Literal any `json:"literal,omitempty"`
|
|
41
|
+
Literal any `json:"literal,omitempty"`
|
|
44
42
|
}
|
|
45
43
|
|
|
46
44
|
// DumpDecorator is a decorator as written on a declaration, carried on its
|
|
@@ -53,17 +51,18 @@ type DumpDecorator struct {
|
|
|
53
51
|
// DumpNode is the wire shape of a graph node. Lowercase json keys are the
|
|
54
52
|
// contract; the Go field names are not.
|
|
55
53
|
type DumpNode struct {
|
|
56
|
-
ID
|
|
57
|
-
Kind
|
|
58
|
-
Name
|
|
59
|
-
QualifiedName
|
|
60
|
-
File
|
|
61
|
-
External
|
|
62
|
-
Ignored
|
|
63
|
-
Exported
|
|
64
|
-
Modifiers
|
|
65
|
-
Evidence
|
|
66
|
-
|
|
54
|
+
ID string `json:"id"`
|
|
55
|
+
Kind string `json:"kind"`
|
|
56
|
+
Name string `json:"name"`
|
|
57
|
+
QualifiedName string `json:"qualifiedName,omitempty"`
|
|
58
|
+
File string `json:"file"`
|
|
59
|
+
External bool `json:"external"`
|
|
60
|
+
Ignored bool `json:"ignored,omitempty"`
|
|
61
|
+
Exported bool `json:"exported,omitempty"`
|
|
62
|
+
Modifiers []string `json:"modifiers,omitempty"`
|
|
63
|
+
Evidence *DumpEvidence `json:"evidence,omitempty"`
|
|
64
|
+
Implementation *DumpEvidence `json:"implementation,omitempty"`
|
|
65
|
+
Decorators []DumpDecorator `json:"decorators,omitempty"`
|
|
67
66
|
}
|
|
68
67
|
|
|
69
68
|
// DumpEdge is the wire shape of a graph edge. Lowercase json keys are the
|
|
@@ -98,7 +97,10 @@ func NewDump(g *Graph, project, tsconfig string, ignored map[string]bool, source
|
|
|
98
97
|
for _, d := range g.Decorators {
|
|
99
98
|
args := make([]DumpDecoratorArgument, 0, len(d.Arguments))
|
|
100
99
|
for _, a := range d.Arguments {
|
|
101
|
-
|
|
100
|
+
if a.Literal == nil {
|
|
101
|
+
continue
|
|
102
|
+
}
|
|
103
|
+
args = append(args, DumpDecoratorArgument{Literal: a.Literal})
|
|
102
104
|
}
|
|
103
105
|
decByNode[d.Target] = append(decByNode[d.Target], DumpDecorator{Name: d.Name, Arguments: args})
|
|
104
106
|
}
|
|
@@ -107,17 +109,18 @@ func NewDump(g *Graph, project, tsconfig string, ignored map[string]bool, source
|
|
|
107
109
|
for _, n := range g.Nodes {
|
|
108
110
|
name, qualified := nodeNames(n)
|
|
109
111
|
nodes = append(nodes, DumpNode{
|
|
110
|
-
ID:
|
|
111
|
-
Kind:
|
|
112
|
-
Name:
|
|
113
|
-
QualifiedName:
|
|
114
|
-
File:
|
|
115
|
-
External:
|
|
116
|
-
Ignored:
|
|
117
|
-
Exported:
|
|
118
|
-
Modifiers:
|
|
119
|
-
Evidence:
|
|
120
|
-
|
|
112
|
+
ID: ctx.relID(n.ID),
|
|
113
|
+
Kind: string(n.Kind),
|
|
114
|
+
Name: name,
|
|
115
|
+
QualifiedName: qualified,
|
|
116
|
+
File: ctx.rel(n.File),
|
|
117
|
+
External: n.External,
|
|
118
|
+
Ignored: ignored[n.File],
|
|
119
|
+
Exported: n.Exported,
|
|
120
|
+
Modifiers: n.Modifiers,
|
|
121
|
+
Evidence: ctx.evidence(n.File, n.Pos, n.End),
|
|
122
|
+
Implementation: ctx.evidence(n.ImplementationFile, n.ImplementationPos, n.ImplementationEnd),
|
|
123
|
+
Decorators: decByNode[n.ID],
|
|
121
124
|
})
|
|
122
125
|
}
|
|
123
126
|
sort.Slice(nodes, func(i, j int) bool { return nodes[i].ID < nodes[j].ID })
|
|
@@ -199,7 +202,7 @@ func wireEdgeKind(kind EdgeKind, origin string) string {
|
|
|
199
202
|
// nodeNames returns a node's simple name and, when it is owner-qualified, its
|
|
200
203
|
// full qualified form for the wire. The simple name is the symbol's own name
|
|
201
204
|
// recorded at build time, so a quoted member whose name contains a dot
|
|
202
|
-
// (`"a.b"`
|
|
205
|
+
// (`"a.b"` becomes Name `C.a.b`) splits exactly; the qualified form is the full Name
|
|
203
206
|
// when it differs from the simple name, and "" for a top-level declaration.
|
|
204
207
|
// A node without a recorded simple name (a future virtual node) falls back to
|
|
205
208
|
// the last dot-separated segment.
|
|
@@ -262,9 +265,8 @@ func (c *dumpContext) relID(id string) string {
|
|
|
262
265
|
}
|
|
263
266
|
|
|
264
267
|
// evidence builds the line/col span for a byte range in file, or nil when the
|
|
265
|
-
// span is absent or no source is available.
|
|
266
|
-
|
|
267
|
-
func (c *dumpContext) evidence(file string, pos, end int, includeText bool) *DumpEvidence {
|
|
268
|
+
// span is absent or no source is available.
|
|
269
|
+
func (c *dumpContext) evidence(file string, pos, end int) *DumpEvidence {
|
|
268
270
|
if pos < 0 || c.sources == nil {
|
|
269
271
|
return nil
|
|
270
272
|
}
|
|
@@ -289,15 +291,12 @@ func (c *dumpContext) evidence(file string, pos, end int, includeText bool) *Dum
|
|
|
289
291
|
ev := &DumpEvidence{File: c.rel(file), StartLine: sl, StartCol: sc}
|
|
290
292
|
if end > pos && end <= len(text) {
|
|
291
293
|
ev.EndLine, ev.EndCol = ls.at(end)
|
|
292
|
-
if includeText {
|
|
293
|
-
ev.Text = excerpt(text[pos:end])
|
|
294
|
-
}
|
|
295
294
|
}
|
|
296
295
|
return ev
|
|
297
296
|
}
|
|
298
297
|
|
|
299
|
-
// firstCodeOffset advances past leading trivia
|
|
300
|
-
// and /* */ block comments
|
|
298
|
+
// firstCodeOffset advances past leading trivia: whitespace, // line comments,
|
|
299
|
+
// and /* */ block comments from pos to the first code byte, or len(text) if
|
|
301
300
|
// the rest is all trivia. It mirrors how a token's real start is found, so an
|
|
302
301
|
// evidence span lands on the declaration rather than the comment above it.
|
|
303
302
|
func firstCodeOffset(text string, pos int) int {
|
|
@@ -328,15 +327,14 @@ func firstCodeOffset(text string, pos int) int {
|
|
|
328
327
|
return i
|
|
329
328
|
}
|
|
330
329
|
|
|
331
|
-
// edgeEvidence is the evidence for an edge's source expression
|
|
332
|
-
// excerpt of the expression text.
|
|
330
|
+
// edgeEvidence is the evidence range for an edge's source expression.
|
|
333
331
|
func (c *dumpContext) edgeEvidence(e *Edge) *DumpEvidence {
|
|
334
332
|
// The edge's span lives in the From node's file.
|
|
335
333
|
file := nodeFile(e.From)
|
|
336
334
|
if file == "" {
|
|
337
335
|
return nil
|
|
338
336
|
}
|
|
339
|
-
return c.evidence(file, e.Pos, e.End
|
|
337
|
+
return c.evidence(file, e.Pos, e.End)
|
|
340
338
|
}
|
|
341
339
|
|
|
342
340
|
// nodeFile recovers the source file path embedded in a node id
|
|
@@ -348,20 +346,6 @@ func nodeFile(id string) string {
|
|
|
348
346
|
return ""
|
|
349
347
|
}
|
|
350
348
|
|
|
351
|
-
// excerpt is a single-line, length-capped slice of source text for inline
|
|
352
|
-
// evidence.
|
|
353
|
-
func excerpt(s string) string {
|
|
354
|
-
s = strings.TrimSpace(s)
|
|
355
|
-
if i := strings.IndexByte(s, '\n'); i >= 0 {
|
|
356
|
-
s = s[:i]
|
|
357
|
-
}
|
|
358
|
-
if len(s) > 160 {
|
|
359
|
-
// Drop a trailing partial UTF-8 rune so the excerpt cannot end mid-character.
|
|
360
|
-
s = strings.TrimSpace(strings.ToValidUTF8(s[:160], "")) + "…"
|
|
361
|
-
}
|
|
362
|
-
return s
|
|
363
|
-
}
|
|
364
|
-
|
|
365
349
|
// lineStarts holds the byte offset of each line's start, so an offset maps to a
|
|
366
350
|
// 1-based line/column by binary search.
|
|
367
351
|
type lineStarts []int
|
|
@@ -130,7 +130,7 @@ export function main(): void {
|
|
|
130
130
|
}
|
|
131
131
|
// The dump carries no ceremony keys: it is wholly checker-resolved, so it has
|
|
132
132
|
// no per-edge trust flags and no schema version to negotiate.
|
|
133
|
-
for _, gone := range []string{`"schemaVersion"`, `"provenance":`, `"confidence":`} {
|
|
133
|
+
for _, gone := range []string{`"schemaVersion"`, `"provenance":`, `"confidence":`, `"text":`} {
|
|
134
134
|
if strings.Contains(s, gone) {
|
|
135
135
|
t.Fatalf("dump still emits removed key %s:\n%s", gone, s)
|
|
136
136
|
}
|
package/internal/graph/edges.go
CHANGED
|
@@ -304,6 +304,11 @@ func (g *Graph) callsWithin(checker *shimchecker.Checker, from string, node *shi
|
|
|
304
304
|
if !isInvokedAccess(child) {
|
|
305
305
|
g.accessEdge(checker, from, child)
|
|
306
306
|
}
|
|
307
|
+
case shimast.KindBinaryExpression:
|
|
308
|
+
if target := g.assignedFunctionTarget(checker, from, child); target != "" {
|
|
309
|
+
g.recordImplementation(target, child)
|
|
310
|
+
g.callsWithin(checker, target, child.AsBinaryExpression().Right)
|
|
311
|
+
}
|
|
307
312
|
case shimast.KindJsxSelfClosingElement:
|
|
308
313
|
// `<Component />` is a use of the component; an intrinsic tag (`<div />`)
|
|
309
314
|
// resolves to nothing and is dropped by callEdge.
|
|
@@ -320,6 +325,40 @@ func (g *Graph) callsWithin(checker *shimchecker.Checker, from string, node *shi
|
|
|
320
325
|
})
|
|
321
326
|
}
|
|
322
327
|
|
|
328
|
+
func (g *Graph) assignedFunctionTarget(checker *shimchecker.Checker, from string, node *shimast.Node) string {
|
|
329
|
+
binary := node.AsBinaryExpression()
|
|
330
|
+
if binary == nil ||
|
|
331
|
+
binary.OperatorToken == nil ||
|
|
332
|
+
binary.Right == nil ||
|
|
333
|
+
binary.OperatorToken.Kind != shimast.KindEqualsToken ||
|
|
334
|
+
!shimast.IsFunctionLike(binary.Right) {
|
|
335
|
+
return ""
|
|
336
|
+
}
|
|
337
|
+
target := Resolve(checker, binary.Left)
|
|
338
|
+
if target == nil || target.Symbol == nil {
|
|
339
|
+
return ""
|
|
340
|
+
}
|
|
341
|
+
to := g.ensureTargetNode(target)
|
|
342
|
+
if to == "" || to == from {
|
|
343
|
+
return ""
|
|
344
|
+
}
|
|
345
|
+
return to
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
func (g *Graph) recordImplementation(id string, assignment *shimast.Node) {
|
|
349
|
+
node := g.Nodes[id]
|
|
350
|
+
if node == nil || node.External || node.ImplementationFile != "" {
|
|
351
|
+
return
|
|
352
|
+
}
|
|
353
|
+
file := shimast.GetSourceFileOfNode(assignment)
|
|
354
|
+
if file == nil {
|
|
355
|
+
return
|
|
356
|
+
}
|
|
357
|
+
node.ImplementationFile = file.FileName()
|
|
358
|
+
node.ImplementationPos = assignment.Pos()
|
|
359
|
+
node.ImplementationEnd = assignment.End()
|
|
360
|
+
}
|
|
361
|
+
|
|
323
362
|
func isInvokedAccess(access *shimast.Node) bool {
|
|
324
363
|
parent := access.Parent
|
|
325
364
|
if parent == nil {
|
|
@@ -406,6 +445,10 @@ func (g *Graph) typeRefsWithin(checker *shimchecker.Checker, from string, node *
|
|
|
406
445
|
if imp := child.AsImportTypeNode(); imp != nil && imp.Qualifier != nil {
|
|
407
446
|
g.typeRefEdge(checker, from, imp.Qualifier)
|
|
408
447
|
}
|
|
448
|
+
case shimast.KindBinaryExpression:
|
|
449
|
+
if target := g.assignedFunctionTarget(checker, from, child); target != "" {
|
|
450
|
+
g.typeRefsWithin(checker, target, child.AsBinaryExpression().Right)
|
|
451
|
+
}
|
|
409
452
|
}
|
|
410
453
|
g.typeRefsWithin(checker, from, child)
|
|
411
454
|
return false
|
package/internal/graph/graph.go
CHANGED
|
@@ -47,8 +47,11 @@ type Node struct {
|
|
|
47
47
|
// Pos and End bound the declaration in its source file (byte offsets). They
|
|
48
48
|
// are for display, never identity, so an edit that shifts them does not re-key
|
|
49
49
|
// the node.
|
|
50
|
-
Pos
|
|
51
|
-
End
|
|
50
|
+
Pos int
|
|
51
|
+
End int
|
|
52
|
+
ImplementationFile string
|
|
53
|
+
ImplementationPos int
|
|
54
|
+
ImplementationEnd int
|
|
52
55
|
}
|
|
53
56
|
|
|
54
57
|
// EdgeKind classifies a relationship between two nodes.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
package graph
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"path/filepath"
|
|
5
|
+
"testing"
|
|
6
|
+
|
|
7
|
+
"github.com/samchon/ttsc/packages/ttsc/driver"
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
// TestValueCallEdgesAttributeFunctionAssignmentsToMembers verifies function
|
|
11
|
+
// assignments to resolved members are walked as that member's implementation.
|
|
12
|
+
//
|
|
13
|
+
// Libraries sometimes attach methods by assigning an arrow/function expression
|
|
14
|
+
// to a typed property instead of declaring a class method body. The class-level
|
|
15
|
+
// walk sees the assignment, but graph flow still needs the assigned member node
|
|
16
|
+
// to own calls made inside the RHS function.
|
|
17
|
+
//
|
|
18
|
+
// 1. Compile a fixture where `inst.run = () => helper()` assigns a typed class
|
|
19
|
+
// method implementation.
|
|
20
|
+
// 2. Build the graph.
|
|
21
|
+
// 3. Assert the `Service.run` method has a value-call edge to `helper`.
|
|
22
|
+
func TestValueCallEdgesAttributeFunctionAssignmentsToMembers(t *testing.T) {
|
|
23
|
+
root := t.TempDir()
|
|
24
|
+
writeFile(t, filepath.Join(root, "tsconfig.json"), fixtureTSConfig)
|
|
25
|
+
writeFile(t, filepath.Join(root, "src", "main.ts"), `export function helper(): void {}
|
|
26
|
+
export class Service {
|
|
27
|
+
run(): void {}
|
|
28
|
+
}
|
|
29
|
+
export function install(inst: Service): void {
|
|
30
|
+
inst.run = (): void => {
|
|
31
|
+
helper();
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
`)
|
|
35
|
+
|
|
36
|
+
prog, diags, err := driver.LoadProgram(root, "tsconfig.json", driver.LoadProgramOptions{})
|
|
37
|
+
if err != nil {
|
|
38
|
+
t.Fatal(err)
|
|
39
|
+
}
|
|
40
|
+
if len(diags) != 0 {
|
|
41
|
+
t.Fatalf("unexpected diagnostics: %v", diags)
|
|
42
|
+
}
|
|
43
|
+
defer func() { _ = prog.Close() }()
|
|
44
|
+
|
|
45
|
+
graph := Build(prog)
|
|
46
|
+
path := sourceFile(t, prog, "main.ts").FileName()
|
|
47
|
+
|
|
48
|
+
run := nodeID(path, "Service.run", NodeMethod)
|
|
49
|
+
helper := nodeID(path, "helper", NodeFunction)
|
|
50
|
+
|
|
51
|
+
if !hasEdge(graph, run, helper, EdgeValueCall) {
|
|
52
|
+
t.Fatalf("missing value-call edge Service.run -> helper (assigned implementation); edges: %v", graph.Edges)
|
|
53
|
+
}
|
|
54
|
+
if graph.Nodes[run].ImplementationFile != path ||
|
|
55
|
+
graph.Nodes[run].ImplementationPos <= 0 ||
|
|
56
|
+
graph.Nodes[run].ImplementationEnd <= graph.Nodes[run].ImplementationPos {
|
|
57
|
+
t.Fatalf("Service.run missing assigned implementation span: %+v", graph.Nodes[run])
|
|
58
|
+
}
|
|
59
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ttsc",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.7",
|
|
4
4
|
"description": "General-purpose TypeScript-Go compiler, runtime, plugin host, and LSP host.",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -32,13 +32,13 @@
|
|
|
32
32
|
"typescript": "7.0.1-rc"
|
|
33
33
|
},
|
|
34
34
|
"optionalDependencies": {
|
|
35
|
-
"@ttsc/linux-
|
|
36
|
-
"@ttsc/linux-
|
|
37
|
-
"@ttsc/
|
|
38
|
-
"@ttsc/darwin-arm64": "0.16.
|
|
39
|
-
"@ttsc/
|
|
40
|
-
"@ttsc/win32-
|
|
41
|
-
"@ttsc/
|
|
35
|
+
"@ttsc/linux-arm": "0.16.7",
|
|
36
|
+
"@ttsc/linux-x64": "0.16.7",
|
|
37
|
+
"@ttsc/linux-arm64": "0.16.7",
|
|
38
|
+
"@ttsc/darwin-arm64": "0.16.7",
|
|
39
|
+
"@ttsc/win32-x64": "0.16.7",
|
|
40
|
+
"@ttsc/win32-arm64": "0.16.7",
|
|
41
|
+
"@ttsc/darwin-x64": "0.16.7"
|
|
42
42
|
},
|
|
43
43
|
"sideEffects": false,
|
|
44
44
|
"files": [
|