@smoothbricks/lmao-ttsc 0.0.0-bootstrap.0 → 0.1.6

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,2 @@
1
+ export { default, register } from '@ttsc/unplugin/bun-register';
2
+ //# sourceMappingURL=bun-register.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bun-register.d.ts","sourceRoot":"","sources":["../src/bun-register.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC"}
@@ -0,0 +1 @@
1
+ export { default, register } from '@ttsc/unplugin/bun-register';
@@ -0,0 +1,7 @@
1
+ import type { TtscUnpluginOptions } from '@ttsc/unplugin';
2
+ import type { BunLikePlugin } from '@ttsc/unplugin/bun';
3
+ export declare function createBunTtscPlugin(options?: TtscUnpluginOptions): BunLikePlugin;
4
+ export default createBunTtscPlugin;
5
+ export type { BunLikeBuild, BunLoader } from '@ttsc/unplugin/bun';
6
+ export type { BunLikePlugin, TtscUnpluginOptions };
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAKxD,wBAAgB,mBAAmB,CAAC,OAAO,CAAC,EAAE,mBAAmB,GAAG,aAAa,CAahF;eAEc,mBAAmB;AAClC,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAClE,YAAY,EAAE,aAAa,EAAE,mBAAmB,EAAE,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,15 @@
1
+ import * as bunAdapterModule from '@ttsc/unplugin/bun';
2
+ const bunAdapter = bunAdapterModule.default;
3
+ export function createBunTtscPlugin(options) {
4
+ if (typeof bunAdapter === 'function') {
5
+ return bunAdapter(options);
6
+ }
7
+ if (typeof bunAdapter === 'object' &&
8
+ bunAdapter !== null &&
9
+ 'default' in bunAdapter &&
10
+ typeof bunAdapter.default === 'function') {
11
+ return bunAdapter.default(options);
12
+ }
13
+ throw new TypeError('@ttsc/unplugin/bun did not export a Bun adapter factory');
14
+ }
15
+ export default createBunTtscPlugin;
package/package.json CHANGED
@@ -1,14 +1,123 @@
1
1
  {
2
2
  "name": "@smoothbricks/lmao-ttsc",
3
- "version": "0.0.0-bootstrap.0",
4
- "description": "Bootstrap placeholder for @smoothbricks/lmao-ttsc. Real releases are published by SmoothBricks CI.",
3
+ "version": "0.1.6",
4
+ "type": "module",
5
+ "sideEffects": [
6
+ "./dist/bun-register.js"
7
+ ],
8
+ "main": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ "./package.json": "./package.json",
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "import": "./dist/index.js"
15
+ },
16
+ "./bun": {
17
+ "types": "./dist/index.d.ts",
18
+ "import": "./dist/index.js"
19
+ },
20
+ "./bun-register": {
21
+ "types": "./dist/bun-register.d.ts",
22
+ "import": "./dist/bun-register.js"
23
+ },
24
+ "./ttsc-plugin": {
25
+ "types": "./plugin.d.cts",
26
+ "require": "./plugin.cjs",
27
+ "default": "./plugin.cjs"
28
+ }
29
+ },
30
+ "ttsc": {
31
+ "plugin": {
32
+ "transform": "@smoothbricks/lmao-ttsc/ttsc-plugin"
33
+ }
34
+ },
35
+ "files": [
36
+ "dist",
37
+ "plugin.cjs",
38
+ "plugin.d.cts",
39
+ "plugin",
40
+ "!**/*.tsbuildinfo"
41
+ ],
42
+ "dependencies": {
43
+ "@ttsc/unplugin": "0.18.4",
44
+ "ttsc": "^0.18.4",
45
+ "typescript": "^7.0.2"
46
+ },
47
+ "devDependencies": {
48
+ "@smoothbricks/lmao": "0.2.1",
49
+ "@smoothbricks/validation": "0.1.2"
50
+ },
51
+ "nx": {
52
+ "sourceRoot": "packages/lmao-ttsc/src",
53
+ "projectType": "library",
54
+ "targets": {
55
+ "lint": {
56
+ "dependsOn": [
57
+ "go-vet",
58
+ "tsc-js",
59
+ "typecheck",
60
+ "typecheck-tests"
61
+ ]
62
+ },
63
+ "go-vet": {
64
+ "executor": "@smoothbricks/nx-plugin:bounded-exec",
65
+ "cache": true,
66
+ "inputs": [
67
+ "{projectRoot}/plugin/**/*.go",
68
+ "{projectRoot}/plugin/go.mod",
69
+ "{projectRoot}/scripts/gen-go-work.mjs"
70
+ ],
71
+ "options": {
72
+ "command": "bun ../scripts/gen-go-work.mjs && go vet ./...",
73
+ "cwd": "{projectRoot}/plugin",
74
+ "timeoutMs": 600000,
75
+ "killAfterMs": 1e4
76
+ }
77
+ },
78
+ "go-test": {
79
+ "executor": "@smoothbricks/nx-plugin:bounded-exec",
80
+ "cache": true,
81
+ "inputs": [
82
+ "{projectRoot}/plugin/**/*.go",
83
+ "{projectRoot}/plugin/go.mod",
84
+ "{projectRoot}/scripts/gen-go-work.mjs"
85
+ ],
86
+ "options": {
87
+ "command": "bun ../scripts/gen-go-work.mjs && go test ./...",
88
+ "cwd": "{projectRoot}/plugin",
89
+ "timeoutMs": 600000,
90
+ "killAfterMs": 1e4
91
+ }
92
+ },
93
+ "test": {
94
+ "executor": "@smoothbricks/nx-plugin:bounded-exec",
95
+ "dependsOn": [
96
+ "go-test"
97
+ ],
98
+ "options": {
99
+ "command": "bun test --timeout=30000 --parallel=3",
100
+ "cwd": "{projectRoot}",
101
+ "timeoutMs": 120000,
102
+ "killAfterMs": 1e4
103
+ }
104
+ }
105
+ },
106
+ "tags": [
107
+ "npm:public"
108
+ ],
109
+ "name": "lmao-ttsc"
110
+ },
5
111
  "license": "MIT",
112
+ "publishConfig": {
113
+ "access": "public"
114
+ },
6
115
  "repository": {
7
116
  "type": "git",
8
117
  "url": "git+https://github.com/smoothbricks/codebase.git",
9
118
  "directory": "packages/lmao-ttsc"
10
119
  },
11
- "publishConfig": {
12
- "access": "public"
120
+ "scripts": {
121
+ "test": "nx run lmao-ttsc:test --outputStyle=stream"
13
122
  }
14
123
  }
package/plugin/go.mod ADDED
@@ -0,0 +1,9 @@
1
+ module smoothbricks.dev/lmao-ttsc-plugin
2
+
3
+ go 1.26
4
+
5
+ require (
6
+ github.com/samchon/ttsc/packages/ttsc v0.0.0
7
+ github.com/microsoft/typescript-go/shim/ast v0.0.0
8
+ github.com/microsoft/typescript-go/shim/printer v0.0.0
9
+ )
@@ -0,0 +1,296 @@
1
+ // loginline.go — partial inlining of statement-level log chains.
2
+ //
3
+ // The SpanBuffer design makes this a PARTIAL inline by construction: row
4
+ // allocation stays runtime (`logger._checkOverflow()` +
5
+ // `buffer._traceRoot.writeLogEntry(buffer, ENTRY_TYPE)` — overflow chaining,
6
+ // capacity tuning, and timestamps live there), while every fluent dispatch
7
+ // after it becomes a direct write at the returned row index through the
8
+ // buffer's STABLE column getters (lazy getters allocate on first access, so
9
+ // the transformer needs no eager/lazy knowledge).
10
+ //
11
+ // ctx.log.info('msg').userId('u1');
12
+ //
13
+ // becomes
14
+ //
15
+ // {
16
+ // const $$l = ctx.log;
17
+ // $$l._checkOverflow();
18
+ // const $$b = $$l._buffer;
19
+ // const $$i = $$b._traceRoot.writeLogEntry($$b, 8);
20
+ // $$l._writeIndex = $$i;
21
+ // if ($$b.message_values) { $$b.message_values[$$i] = 'msg';
22
+ // if ($$b.message_nulls) $$b.message_nulls[$$i >>> 3] |= 1 << ($$i & 7); }
23
+ // $$b.constructor.stats.totalWrites++;
24
+ // if ($$b.line_values) { $$b.line_values[$$i] = 3; ... }
25
+ // if ($$b.userId_values) { $$b.userId_values[$$i] = 'u1'; ... }
26
+ // }
27
+ //
28
+ // Parity contract is the GENERATED SpanLogger source
29
+ // (packages/lmao/src/lib/codegen/spanLoggerGenerator.ts): raw getter writes
30
+ // with the same if-guards for message/line/category/text/number fields;
31
+ // enum and boolean fields go through the buffer's positional method
32
+ // (`$$b.field($$i, v)`) exactly like the generated setters, with the
33
+ // string→index switch constant-folded for literal enum values.
34
+ //
35
+ // Conservative bails (chain left untouched, runtime path is always correct):
36
+ // receiver not Checker-proved SpanLogger/GeneratedSpanLogger, a second
37
+ // log-level call in the same chain, a chained method that is neither a
38
+ // schema field nor line/with, non-single-arg links, non-object-literal
39
+ // with(), or non-statement context.
40
+ package main
41
+
42
+ import (
43
+ "strings"
44
+
45
+ shimast "github.com/microsoft/typescript-go/shim/ast"
46
+ shimchecker "github.com/microsoft/typescript-go/shim/checker"
47
+ )
48
+
49
+ var spanLoggerTypeNames = []string{"SpanLogger", "GeneratedSpanLogger", "FluentLogEntry"}
50
+
51
+ // Entry-type dictionary values, pinned to
52
+ // packages/lmao/src/lib/schema/systemSchema.ts (ENTRY_TYPE_*). The fixture
53
+ // parity test exercises these; a runtime renumbering fails it.
54
+ var logEntryTypes = map[string]int{
55
+ "trace": 6,
56
+ "debug": 7,
57
+ "info": 8,
58
+ "warn": 9,
59
+ "error": 10,
60
+ }
61
+
62
+ type logInline struct {
63
+ list *shimast.NodeList
64
+ index int
65
+ logExpr *shimast.Node // the `ctx.log` expression
66
+ level string // info/debug/...
67
+ message *shimast.Node
68
+ templateID uint16 // nonzero only for checker-proved Op-local literals
69
+ line int // source line of the log call (spec 01o §6, folded in)
70
+ lineArg *shimast.Node // explicit .line(N) argument if present in source
71
+ writes []tagWrite // chained attribute writes in execution order
72
+ schema map[string]schemaField
73
+ }
74
+
75
+ func isSpanLoggerType(chk *shimchecker.Checker, t *shimchecker.Type) bool {
76
+ name := chk.TypeToString(t)
77
+ for _, w := range spanLoggerTypeNames {
78
+ if name == w || strings.HasPrefix(name, w+"<") {
79
+ return true
80
+ }
81
+ }
82
+ if sym := shimchecker.Type_getTypeNameSymbol(t); sym != nil {
83
+ for _, w := range spanLoggerTypeNames {
84
+ if sym.Name == w {
85
+ return true
86
+ }
87
+ }
88
+ }
89
+ return false
90
+ }
91
+
92
+ // findLogInline analyzes a statement-level call chain. Checker queries only;
93
+ // no mutation (must run in the collect phase).
94
+ func (t *fileTransformer) findLogInline(call *shimast.CallExpression) (*logInline, bool) {
95
+ if t.checker == nil {
96
+ return nil, false
97
+ }
98
+ type link struct {
99
+ name string
100
+ args []*shimast.Node
101
+ node *shimast.CallExpression
102
+ }
103
+ var links []link
104
+ current := call
105
+ for {
106
+ expr := current.Expression
107
+ if expr.Kind != shimast.KindPropertyAccessExpression {
108
+ return nil, false
109
+ }
110
+ pa := expr.AsPropertyAccessExpression()
111
+ links = append([]link{{name: shimast.NodeText(pa.Name()), args: current.Arguments.Nodes, node: current}}, links...)
112
+ next := pa.Expression
113
+ if next.Kind == shimast.KindCallExpression {
114
+ current = next.AsCallExpression()
115
+ continue
116
+ }
117
+ // Chain root: the receiver of the FIRST link. It must be the log
118
+ // object, Checker-proved.
119
+ if len(links) == 0 || !logMethods[links[0].name] {
120
+ return nil, false
121
+ }
122
+ recvType := t.checker.GetTypeAtLocation(next)
123
+ if recvType == nil || !isSpanLoggerType(t.checker, recvType) {
124
+ return nil, false
125
+ }
126
+ if len(links[0].args) != 1 {
127
+ return nil, false
128
+ }
129
+
130
+ in := &logInline{
131
+ logExpr: next,
132
+ level: links[0].name,
133
+ message: links[0].args[0],
134
+ templateID: t.logTemplateIDs[links[0].node],
135
+ line: t.lineOf(links[0].node.AsNode()),
136
+ schema: extractLogSchema(t.checker, recvType),
137
+ }
138
+ for _, l := range links[1:] {
139
+ switch {
140
+ case logMethods[l.name]:
141
+ return nil, false // second log entry in one chain: bail
142
+ case l.name == "line":
143
+ if len(l.args) != 1 {
144
+ return nil, false
145
+ }
146
+ in.lineArg = l.args[0]
147
+ case l.name == "with":
148
+ if len(l.args) != 1 || l.args[0].Kind != shimast.KindObjectLiteralExpression {
149
+ return nil, false
150
+ }
151
+ for _, prop := range l.args[0].AsObjectLiteralExpression().Properties.Nodes {
152
+ if prop.Kind != shimast.KindPropertyAssignment {
153
+ return nil, false
154
+ }
155
+ p := prop.AsPropertyAssignment()
156
+ nameNode := p.Name()
157
+ if nameNode.Kind != shimast.KindIdentifier && nameNode.Kind != shimast.KindStringLiteral {
158
+ return nil, false
159
+ }
160
+ field := shimast.NodeText(nameNode)
161
+ if _, known := in.schema[field]; !known {
162
+ return nil, false
163
+ }
164
+ in.writes = append(in.writes, tagWrite{field: field, arg: p.Initializer})
165
+ }
166
+ default:
167
+ if len(l.args) != 1 {
168
+ return nil, false
169
+ }
170
+ if _, known := in.schema[l.name]; !known {
171
+ return nil, false // not a schema field setter: bail, never miscompile
172
+ }
173
+ in.writes = append(in.writes, tagWrite{field: l.name, arg: l.args[0]})
174
+ }
175
+ }
176
+ return in, true
177
+ }
178
+ }
179
+
180
+ // extractLogSchema reuses the tag schema extraction but excludes the log/system
181
+ // methods that would otherwise classify as string fields.
182
+ func extractLogSchema(chk *shimchecker.Checker, logType *shimchecker.Type) map[string]schemaField {
183
+ schema := extractSchema(chk, logType)
184
+ for name := range logMethods {
185
+ delete(schema, name)
186
+ }
187
+ delete(schema, "line")
188
+ return schema
189
+ }
190
+
191
+ // --- emission -------------------------------------------------------------------
192
+
193
+ // guardedRawWrite emits the generated-setter shape:
194
+ //
195
+ // if (b.f_values) { b.f_values[i] = v; if (b.f_nulls) b.f_nulls[i>>>3] |= 1 << (i&7); }
196
+ func guardedRawWrite(buf, idx *shimast.Node, field string, value *shimast.Node) *shimast.Node {
197
+ values := func() *shimast.Node { return propAccess(buf, field+"_values") }
198
+ nulls := func() *shimast.Node { return propAccess(buf, field+"_nulls") }
199
+ at := func(arr *shimast.Node) *shimast.Node {
200
+ return factory.NewElementAccessExpression(arr, nil, idx, shimast.NodeFlagsNone)
201
+ }
202
+ // b.f_nulls[i >>> 3] |= 1 << (i & 7)
203
+ nullSlot := factory.NewElementAccessExpression(nulls(), nil,
204
+ factory.NewBinaryExpression(nil, idx, nil, factory.NewToken(shimast.KindGreaterThanGreaterThanGreaterThanToken), num(3)),
205
+ shimast.NodeFlagsNone)
206
+ nullBit := factory.NewBinaryExpression(nil, num(1), nil, factory.NewToken(shimast.KindLessThanLessThanToken),
207
+ factory.NewParenthesizedExpression(
208
+ factory.NewBinaryExpression(nil, idx, nil, factory.NewToken(shimast.KindAmpersandToken), num(7))))
209
+ setNull := factory.NewIfStatement(nulls(),
210
+ factory.NewBlock(factory.NewNodeList([]*shimast.Node{
211
+ binaryStmt(nullSlot, shimast.KindBarEqualsToken, nullBit),
212
+ }), false), nil)
213
+ body := []*shimast.Node{
214
+ binaryStmt(at(values()), shimast.KindEqualsToken, value),
215
+ setNull,
216
+ }
217
+ return factory.NewIfStatement(values(), factory.NewBlock(factory.NewNodeList(body), true), nil)
218
+ }
219
+
220
+ // applyLogInlines splices replacement blocks (phase B — no checker use).
221
+ func (t *fileTransformer) applyLogInlines(inlines []logInline) {
222
+ for _, in := range inlines {
223
+ logger := ident("$$l")
224
+ buf := ident("$$b")
225
+ idx := ident("$$i")
226
+ // Overflow happy-path inline: one compare instead of a method call;
227
+ // the (rare) overflow path still runs the runtime's _checkOverflow,
228
+ // which owns buffer switching, capacity tuning, and scope prefill.
229
+ overflowCheck := factory.NewIfStatement(
230
+ factory.NewBinaryExpression(nil,
231
+ propAccess(propAccess(ident("$$l"), "_buffer"), "_writeIndex"), nil,
232
+ factory.NewToken(shimast.KindGreaterThanEqualsToken),
233
+ propAccess(propAccess(ident("$$l"), "_buffer"), "_capacity")),
234
+ factory.NewBlock(factory.NewNodeList([]*shimast.Node{
235
+ factory.NewExpressionStatement(callExpr(propAccess(ident("$$l"), "_checkOverflow"), nil)),
236
+ }), false), nil)
237
+ stmts := []*shimast.Node{
238
+ constDecl(logger, in.logExpr),
239
+ overflowCheck,
240
+ constDecl(buf, propAccess(logger, "_buffer")),
241
+ constDecl(idx, callExpr(propAccess(propAccess(buf, "_traceRoot"), "writeLogEntry"),
242
+ []*shimast.Node{buf, num(logEntryTypes[in.level])})),
243
+ binaryStmt(propAccess(logger, "_writeIndex"), shimast.KindEqualsToken, idx),
244
+ }
245
+ if in.templateID != 0 {
246
+ stmts = append(stmts, binaryStmt(
247
+ factory.NewElementAccessExpression(propAccess(buf, "_messageTemplateIds"), nil, idx, shimast.NodeFlagsNone),
248
+ shimast.KindEqualsToken,
249
+ num(int(in.templateID)),
250
+ ))
251
+ } else {
252
+ stmts = append(stmts, guardedRawWrite(buf, idx, "message", in.message))
253
+ }
254
+ stmts = append(stmts, factory.NewExpressionStatement(factory.NewPostfixUnaryExpression(
255
+ propAccess(propAccess(propAccess(buf, "constructor"), "stats"), "totalWrites"),
256
+ shimast.KindPlusPlusToken)))
257
+
258
+ lineValue := in.lineArg
259
+ if lineValue == nil {
260
+ lineValue = num(in.line) // §6 semantics folded into the inline
261
+ }
262
+ stmts = append(stmts, guardedRawWrite(buf, idx, "line", lineValue))
263
+
264
+ for _, w := range in.writes {
265
+ info := in.schema[w.field]
266
+ switch info.kind {
267
+ case fieldEnum:
268
+ // Positional buffer method, string→index folded when literal
269
+ // (mirrors the generated enum override setter).
270
+ var enumIdx *shimast.Node
271
+ if isCompileTimeLiteral(w.arg) && w.arg.Kind == shimast.KindStringLiteral {
272
+ v := 0
273
+ for i, ev := range info.enumValues {
274
+ if ev == shimast.NodeText(w.arg) {
275
+ v = i
276
+ break
277
+ }
278
+ }
279
+ enumIdx = num(v)
280
+ } else {
281
+ enumIdx = enumSwitchIIFE(w.arg, info.enumValues)
282
+ }
283
+ stmts = append(stmts, factory.NewExpressionStatement(
284
+ callExpr(propAccess(buf, w.field), []*shimast.Node{idx, enumIdx})))
285
+ case fieldBool:
286
+ // Bit-packed at row granularity — go through the buffer's
287
+ // positional method like the generated setter does.
288
+ stmts = append(stmts, factory.NewExpressionStatement(
289
+ callExpr(propAccess(buf, w.field), []*shimast.Node{idx, w.arg})))
290
+ default:
291
+ stmts = append(stmts, guardedRawWrite(buf, idx, w.field, w.arg))
292
+ }
293
+ }
294
+ in.list.Nodes[in.index] = factory.NewBlock(factory.NewNodeList(stmts), true)
295
+ }
296
+ }