fraiseql 2.1.1

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/README.md ADDED
@@ -0,0 +1,784 @@
1
+ # FraiseQL v2 - TypeScript Schema Authoring
2
+
3
+ > Compiled GraphQL execution engine - Schema authoring in TypeScript
4
+
5
+ FraiseQL v2 is a high-performance GraphQL engine that compiles schemas at build-time for zero-cost query execution. This package provides **schema authoring in TypeScript** that generates JSON schemas consumed by the Rust compiler.
6
+
7
+ **Key Principle**: TypeScript is for **authoring only** - no runtime FFI, no language bindings. Just pure JSON generation.
8
+
9
+ ## Architecture
10
+
11
+ ```
12
+ TypeScript Code (decorators)
13
+
14
+ schema.json
15
+
16
+ fraiseql-cli compile
17
+
18
+ schema.compiled.json
19
+
20
+ Rust Runtime (fraiseql-server)
21
+ ```
22
+
23
+ ## Installation
24
+
25
+ ```bash
26
+ npm install fraiseql
27
+ # or
28
+ yarn add fraiseql
29
+ # or
30
+ pnpm add fraiseql
31
+ ```
32
+
33
+ **Requirements**: Node.js 18+
34
+
35
+ ## Quick Start
36
+
37
+ ### 1. Define Types
38
+
39
+ ```typescript
40
+ import * as fraiseql from "fraiseql";
41
+
42
+ @fraiseql.type()
43
+ class User {
44
+ id!: number;
45
+ name!: string;
46
+ email!: string;
47
+ }
48
+
49
+ // Register fields (TypeScript doesn't preserve type info at runtime)
50
+ fraiseql.registerTypeFields("User", [
51
+ { name: "id", type: "Int", nullable: false },
52
+ { name: "name", type: "String", nullable: false },
53
+ { name: "email", type: "String", nullable: false },
54
+ ]);
55
+ ```
56
+
57
+ ### 2. Define Queries
58
+
59
+ ```typescript
60
+ @fraiseql.query({ sqlSource: "v_user" })
61
+ function users(limit: number = 10, offset: number = 0): User[] {
62
+ throw new Error("Not executed");
63
+ }
64
+
65
+ fraiseql.registerQuery(
66
+ "users",
67
+ "User",
68
+ true, // returns list
69
+ false, // not nullable
70
+ [
71
+ { name: "limit", type: "Int", nullable: false, default: 10 },
72
+ { name: "offset", type: "Int", nullable: false, default: 0 },
73
+ ],
74
+ "Get all users",
75
+ { sql_source: "v_user" }
76
+ );
77
+ ```
78
+
79
+ ### 3. Define Mutations
80
+
81
+ ```typescript
82
+ @fraiseql.mutation({ sqlSource: "fn_create_user", operation: "CREATE" })
83
+ function createUser(name: string, email: string): User {
84
+ throw new Error("Not executed");
85
+ }
86
+
87
+ fraiseql.registerMutation(
88
+ "createUser",
89
+ "User",
90
+ false, // single item
91
+ false, // not nullable
92
+ [
93
+ { name: "name", type: "String", nullable: false },
94
+ { name: "email", type: "String", nullable: false },
95
+ ],
96
+ "Create a new user",
97
+ { sql_source: "fn_create_user", operation: "CREATE" }
98
+ );
99
+ ```
100
+
101
+ ### 4. Export Schema
102
+
103
+ ```typescript
104
+ // At end of file
105
+ if (require.main === module) {
106
+ fraiseql.exportSchema("schema.json");
107
+ }
108
+ ```
109
+
110
+ ### 5. Compile
111
+
112
+ ```bash
113
+ # Generate compiled schema
114
+ fraiseql-cli compile schema.json
115
+
116
+ # Start server
117
+ fraiseql-server --schema schema.compiled.json --port 3000
118
+ ```
119
+
120
+ ## API Reference
121
+
122
+ ### Decorators
123
+
124
+ #### `@Type(config?)`
125
+
126
+ Mark a class as a GraphQL type.
127
+
128
+ ```typescript
129
+ @fraiseql.type()
130
+ class User {
131
+ id!: number;
132
+ name!: string;
133
+ }
134
+ ```
135
+
136
+ **Note**: Decorators alone don't capture field types. Use `registerTypeFields()` to provide field metadata.
137
+
138
+ #### `@Query(config)`
139
+
140
+ Mark a function as a GraphQL query.
141
+
142
+ ```typescript
143
+ @fraiseql.query({ sqlSource: "v_user" })
144
+ function users(limit: number = 10): User[] {
145
+ throw new Error("Not executed");
146
+ }
147
+ ```
148
+
149
+ **Config Options**:
150
+
151
+ - `sqlSource`: SQL view/table name (required for data operations)
152
+ - `autoParams`: Auto-parameter configuration
153
+ - Other custom configuration
154
+
155
+ #### `@Mutation(config)`
156
+
157
+ Mark a function as a GraphQL mutation.
158
+
159
+ ```typescript
160
+ @fraiseql.mutation({ sqlSource: "fn_create_user", operation: "CREATE" })
161
+ function createUser(name: string): User {
162
+ throw new Error("Not executed");
163
+ }
164
+ ```
165
+
166
+ **Config Options**:
167
+
168
+ - `sqlSource`: SQL function name (required)
169
+ - `operation`: "CREATE" | "UPDATE" | "DELETE" | "CUSTOM"
170
+ - Other custom configuration
171
+
172
+ #### `@FactTable(config)`
173
+
174
+ Mark a class as a fact table for analytics.
175
+
176
+ ```typescript
177
+ @fraiseql.FactTable({
178
+ tableName: "tf_sales",
179
+ measures: ["revenue", "quantity"],
180
+ dimensionPaths: [
181
+ {
182
+ name: "category",
183
+ json_path: "data->>'category'",
184
+ data_type: "text",
185
+ },
186
+ ],
187
+ })
188
+ @fraiseql.type()
189
+ class Sale {
190
+ id!: number;
191
+ revenue!: number;
192
+ quantity!: number;
193
+ }
194
+ ```
195
+
196
+ #### `@AggregateQuery(config)`
197
+
198
+ Mark a function as an aggregate query on a fact table.
199
+
200
+ ```typescript
201
+ @fraiseql.AggregateQuery({
202
+ factTable: "tf_sales",
203
+ autoGroupBy: true,
204
+ autoAggregates: true,
205
+ })
206
+ @fraiseql.query()
207
+ function salesAggregate(): Record<string, unknown>[] {
208
+ throw new Error("Not executed");
209
+ }
210
+ ```
211
+
212
+ #### `@Subscription(config?)`
213
+
214
+ Mark a function as a GraphQL subscription for real-time events.
215
+
216
+ Subscriptions in FraiseQL are **compiled database event projections** sourced from LISTEN/NOTIFY or CDC, not resolver-based.
217
+
218
+ ```typescript
219
+ @fraiseql.Subscription({ topic: "order_events" })
220
+ function orderCreated(userId?: string): Order {
221
+ pass;
222
+ }
223
+ ```
224
+
225
+ ### Subscription Configuration
226
+
227
+ **SubscriptionConfig Options**:
228
+
229
+ - `entityType`: Entity type being subscribed to (defaults to return type)
230
+ - `topic`: Optional topic/channel name for filtering events
231
+ - `operation`: Single event type filter - "CREATE" | "UPDATE" | "DELETE"
232
+ - `operations`: Multiple event type filters - ["CREATE", "UPDATE", "DELETE"]
233
+
234
+ **Manual Registration**:
235
+
236
+ ```typescript
237
+ fraiseql.registerSubscription(
238
+ "orderCreated", // name
239
+ "Order", // entityType
240
+ false, // nullable
241
+ [
242
+ { name: "userId", type: "String", nullable: true }
243
+ ], // filter arguments
244
+ "Subscribe to new orders",
245
+ { topic: "order_events", operation: "CREATE" }
246
+ );
247
+ ```
248
+
249
+ **Subscription Patterns**:
250
+
251
+ 1. **Event Type Filtering** - Subscribe to specific operations
252
+
253
+ ```typescript
254
+ fraiseql.registerSubscription(
255
+ "userCreated",
256
+ "User",
257
+ false,
258
+ [],
259
+ "New user registrations",
260
+ { operation: "CREATE" } // Only CREATE events
261
+ );
262
+ ```
263
+
264
+ 1. **Topic-Based Subscriptions** - Route to different channels
265
+
266
+ ```typescript
267
+ fraiseql.registerSubscription(
268
+ "criticalOrders",
269
+ "Order",
270
+ false,
271
+ [],
272
+ "High-priority orders",
273
+ { topic: "orders.critical", operation: "CREATE" }
274
+ );
275
+ ```
276
+
277
+ 1. **Filtered Subscriptions** - Target specific records
278
+
279
+ ```typescript
280
+ fraiseql.registerSubscription(
281
+ "customerOrders",
282
+ "Order",
283
+ false,
284
+ [{ name: "customerId", type: "ID", nullable: false }], // Filter by customer
285
+ "Orders for specific customer"
286
+ );
287
+ ```
288
+
289
+ 1. **Change Data Capture (CDC)** - Capture all changes
290
+
291
+ ```typescript
292
+ fraiseql.registerSubscription(
293
+ "userCDC",
294
+ "User",
295
+ false,
296
+ [],
297
+ "All user changes",
298
+ { operations: ["CREATE", "UPDATE", "DELETE"] }
299
+ );
300
+ ```
301
+
302
+ 1. **Alerts and Notifications** - Complex filtering
303
+
304
+ ```typescript
305
+ fraiseql.registerSubscription(
306
+ "unusualOrders",
307
+ "Order",
308
+ false,
309
+ [
310
+ { name: "minAmount", type: "Decimal", nullable: false },
311
+ { name: "timeWindowMinutes", type: "Int", nullable: true }
312
+ ],
313
+ "Alert on high-value orders",
314
+ { operation: "CREATE" }
315
+ );
316
+ ```
317
+
318
+ ### Type System Decorators
319
+
320
+ #### `enum_(name, values, config?)`
321
+
322
+ Define a GraphQL enum type.
323
+
324
+ ```typescript
325
+ const OrderStatus = fraiseql.enum_("OrderStatus", {
326
+ PENDING: "pending",
327
+ SHIPPED: "shipped",
328
+ DELIVERED: "delivered",
329
+ }, {
330
+ description: "Status of an order"
331
+ });
332
+ ```
333
+
334
+ Then use in types:
335
+
336
+ ```typescript
337
+ fraiseql.registerTypeFields("Order", [
338
+ { name: "id", type: "ID", nullable: false },
339
+ { name: "status", type: "OrderStatus", nullable: false },
340
+ ]);
341
+ ```
342
+
343
+ #### `interface_(name, fields, config?)`
344
+
345
+ Define a GraphQL interface - shared fields for multiple types.
346
+
347
+ ```typescript
348
+ const Node = fraiseql.interface_("Node", [
349
+ { name: "id", type: "ID", nullable: false },
350
+ { name: "createdAt", type: "DateTime", nullable: false },
351
+ ], {
352
+ description: "An object with a globally unique ID"
353
+ });
354
+ ```
355
+
356
+ Types can implement interfaces:
357
+
358
+ ```typescript
359
+ fraiseql.registerTypeFields("User", [
360
+ { name: "id", type: "ID", nullable: false },
361
+ { name: "createdAt", type: "DateTime", nullable: false },
362
+ { name: "name", type: "String", nullable: false },
363
+ ]);
364
+ ```
365
+
366
+ #### `union(name, memberTypes, config?)`
367
+
368
+ Define a GraphQL union - polymorphic return type.
369
+
370
+ ```typescript
371
+ const SearchResult = fraiseql.union("SearchResult",
372
+ ["User", "Post", "Comment"],
373
+ { description: "Result of a search query" }
374
+ );
375
+ ```
376
+
377
+ Then use in queries:
378
+
379
+ ```typescript
380
+ fraiseql.registerQuery(
381
+ "search",
382
+ "SearchResult", // Returns union
383
+ true, // returns list
384
+ false, // not nullable
385
+ [{ name: "query", type: "String", nullable: false }],
386
+ "Search across content"
387
+ );
388
+ ```
389
+
390
+ #### `input(name, fields, config?)`
391
+
392
+ Define a GraphQL input type - structured parameters.
393
+
394
+ ```typescript
395
+ const CreateUserInput = fraiseql.input("CreateUserInput", [
396
+ { name: "email", type: "Email", nullable: false },
397
+ { name: "name", type: "String", nullable: false },
398
+ { name: "role", type: "String", nullable: false, default: "user" },
399
+ ], {
400
+ description: "Input for creating a new user"
401
+ });
402
+ ```
403
+
404
+ Use in mutations:
405
+
406
+ ```typescript
407
+ fraiseql.registerMutation(
408
+ "createUser",
409
+ "User",
410
+ false,
411
+ false,
412
+ [{ name: "input", type: "CreateUserInput", nullable: false }],
413
+ "Create a new user"
414
+ );
415
+ ```
416
+
417
+ ### Field-Level Metadata
418
+
419
+ Add access control, deprecation markers, and documentation to individual fields:
420
+
421
+ #### `field(options)`
422
+
423
+ Create field metadata for use with `registerTypeFields()`:
424
+
425
+ ```typescript
426
+ fraiseql.registerTypeFields("User", [
427
+ { name: "id", type: "ID", nullable: false },
428
+ {
429
+ name: "salary",
430
+ type: "Decimal",
431
+ nullable: false,
432
+ requiresScope: "read:User.salary",
433
+ description: "Annual salary (requires HR scope)"
434
+ },
435
+ {
436
+ name: "oldEmail",
437
+ type: "String",
438
+ nullable: true,
439
+ deprecated: "Use email instead",
440
+ description: "Legacy email field (deprecated)"
441
+ }
442
+ ]);
443
+ ```
444
+
445
+ **Field Metadata Options**:
446
+
447
+ - `requiresScope: string | string[]` - JWT scope(s) required to access this field (field-level access control)
448
+ - `deprecated: boolean | string` - Mark field as deprecated. Pass a string with migration guidance.
449
+ - `description: string` - Field documentation (appears in GraphQL schema)
450
+
451
+ **Use Cases**:
452
+
453
+ 1. **PII Protection**: Require specific scopes for sensitive fields
454
+
455
+ ```typescript
456
+ {
457
+ name: "ssn",
458
+ type: "String",
459
+ nullable: false,
460
+ requiresScope: "pii:read" // Only users with pii:read scope can query this
461
+ }
462
+ ```
463
+
464
+ 1. **API Versioning**: Deprecate fields with migration guidance
465
+
466
+ ```typescript
467
+ {
468
+ name: "oldPrice",
469
+ type: "Decimal",
470
+ nullable: true,
471
+ deprecated: "Use pricing.current instead - structure moved to pricing object"
472
+ }
473
+ ```
474
+
475
+ 1. **Schema Documentation**: Add rich field descriptions
476
+
477
+ ```typescript
478
+ {
479
+ name: "discount",
480
+ type: "Decimal",
481
+ nullable: false,
482
+ description: "Discount percentage. Access requires orders:view_discounts scope.",
483
+ requiresScope: "orders:view_discounts"
484
+ }
485
+ ```
486
+
487
+ ### Manual Registration Functions
488
+
489
+ When decorators alone don't provide enough type information:
490
+
491
+ #### `registerTypeFields(typeName, fields, description?)`
492
+
493
+ Register type field definitions.
494
+
495
+ ```typescript
496
+ fraiseql.registerTypeFields("User", [
497
+ { name: "id", type: "Int", nullable: false },
498
+ { name: "name", type: "String", nullable: false },
499
+ { name: "email", type: "String", nullable: true },
500
+ ]);
501
+ ```
502
+
503
+ #### `registerQuery(name, returnType, returnsList, nullable, args, description?, config?)`
504
+
505
+ Register a query with full metadata.
506
+
507
+ ```typescript
508
+ fraiseql.registerQuery(
509
+ "users",
510
+ "User",
511
+ true, // returns list
512
+ false, // not nullable
513
+ [
514
+ { name: "limit", type: "Int", nullable: false, default: 10 },
515
+ ],
516
+ "Get all users",
517
+ { sql_source: "v_user" }
518
+ );
519
+ ```
520
+
521
+ #### `registerMutation(name, returnType, returnsList, nullable, args, description?, config?)`
522
+
523
+ Register a mutation with full metadata.
524
+
525
+ ```typescript
526
+ fraiseql.registerMutation(
527
+ "createUser",
528
+ "User",
529
+ false, // single item
530
+ false, // not nullable
531
+ [
532
+ { name: "name", type: "String", nullable: false },
533
+ ],
534
+ "Create a new user",
535
+ { sql_source: "fn_create_user", operation: "CREATE" }
536
+ );
537
+ ```
538
+
539
+ ### Schema Export
540
+
541
+ #### `exportSchema(outputPath, options?)`
542
+
543
+ Export the schema to a JSON file.
544
+
545
+ ```typescript
546
+ fraiseql.exportSchema("schema.json", { pretty: true });
547
+ ```
548
+
549
+ #### `getSchemaDict()`
550
+
551
+ Get the schema as a JavaScript object.
552
+
553
+ ```typescript
554
+ const schema = fraiseql.getSchemaDict();
555
+ console.log(schema.types);
556
+ console.log(schema.queries);
557
+ ```
558
+
559
+ #### `exportSchemaToString(options?)`
560
+
561
+ Export schema to a JSON string.
562
+
563
+ ```typescript
564
+ const json = fraiseql.exportSchemaToString({ pretty: true });
565
+ console.log(json);
566
+ ```
567
+
568
+ ## Supported GraphQL Types
569
+
570
+ ### Scalars
571
+
572
+ - `Int` - 32-bit integer
573
+ - `Float` - Floating point number
574
+ - `String` - Text string
575
+ - `Boolean` - True/False
576
+ - `ID` - Unique identifier
577
+
578
+ ### Modifiers
579
+
580
+ - `T[]` - List type (maps to `[T!]` in GraphQL)
581
+ - `T | null` - Nullable type
582
+ - `T | undefined` - Optional parameter
583
+
584
+ ## Type Mapping
585
+
586
+ TypeScript types are converted to GraphQL types:
587
+
588
+ ```typescript
589
+ // TypeScript → GraphQL
590
+ number → Float
591
+ string → String
592
+ boolean → Boolean
593
+ SomeClass → SomeClass (custom type)
594
+ T[] → [T!] (list)
595
+ T | null → T (nullable)
596
+ T | undefined → T (optional param)
597
+ ```
598
+
599
+ ## Analytics Features
600
+
601
+ ### Fact Tables
602
+
603
+ Fact tables are special analytics tables with:
604
+
605
+ - **Measures**: Numeric columns for aggregation (SUM, AVG, COUNT)
606
+ - **Dimensions**: JSONB column for flexible GROUP BY
607
+ - **Denormalized Filters**: Indexed columns for fast WHERE clauses
608
+
609
+ ```typescript
610
+ @fraiseql.FactTable({
611
+ tableName: "tf_sales", // Must start with "tf_"
612
+ measures: ["revenue", "cost"], // Numeric columns
613
+ dimensionPaths: [
614
+ {
615
+ name: "category",
616
+ json_path: "data->>'category'",
617
+ data_type: "text",
618
+ },
619
+ ],
620
+ })
621
+ @fraiseql.type()
622
+ class Sale {
623
+ id!: number;
624
+ revenue!: number;
625
+ cost!: number;
626
+ customerId!: string;
627
+ }
628
+ ```
629
+
630
+ ### Aggregate Queries
631
+
632
+ Queries that perform GROUP BY aggregations on fact tables:
633
+
634
+ ```typescript
635
+ @fraiseql.AggregateQuery({
636
+ factTable: "tf_sales",
637
+ autoGroupBy: true, // Auto-generate groupBy fields
638
+ autoAggregates: true, // Auto-generate aggregate functions
639
+ })
640
+ @fraiseql.query()
641
+ function salesSummary(): Record<string, unknown>[] {
642
+ throw new Error("Not executed");
643
+ }
644
+ ```
645
+
646
+ These queries support:
647
+
648
+ - `groupBy`: Dimensions and temporal buckets
649
+ - `aggregates`: COUNT, SUM, AVG, MIN, MAX
650
+ - `where`: Pre-aggregation filters
651
+ - `having`: Post-aggregation filters
652
+ - `orderBy`: Sort results
653
+ - Pagination: `limit`, `offset`
654
+
655
+ ## Examples
656
+
657
+ See the `examples/` directory:
658
+
659
+ - **basic_schema.ts** - Simple CRUD queries and mutations
660
+ - **analytics_schema.ts** - Fact tables and aggregate queries
661
+ - **enums-example.ts** - Enum definitions and usage
662
+ - **types-advanced.ts** - Comprehensive type system example (enums, interfaces, unions, input types)
663
+ - **unions-interfaces-example.ts** - Interfaces, unions, and polymorphic queries
664
+ - **field-metadata.ts** - Field-level access control, deprecation, and documentation
665
+ - **subscriptions.ts** - Real-time subscriptions: event filtering, topics, CDC, alerts
666
+ - **comprehensive-example.ts** - Full-featured schema with all FraiseQL capabilities
667
+
668
+ Run examples:
669
+
670
+ ```bash
671
+ npm run example:basic # Generate basic schema
672
+ npm run example:analytics # Generate analytics schema
673
+ npm run example:enums # Generate enum example
674
+ npm run example:advanced # Generate advanced types example
675
+ npm run example:metadata # Generate field metadata example
676
+ npm run example:subscriptions # Generate subscriptions example
677
+ ```
678
+
679
+ ## Development
680
+
681
+ ```bash
682
+ # Install dependencies
683
+ npm install
684
+
685
+ # Build
686
+ npm run build
687
+
688
+ # Run tests
689
+ npm test
690
+
691
+ # Watch mode
692
+ npm run test:watch
693
+
694
+ # Lint
695
+ npm run lint
696
+
697
+ # Format code
698
+ npm run format
699
+ ```
700
+
701
+ ## Testing
702
+
703
+ Tests verify:
704
+
705
+ - Type introspection and conversion
706
+ - Schema registration and retrieval
707
+ - Decorator functionality
708
+ - Schema JSON generation
709
+ - Analytics fact tables and aggregate queries
710
+
711
+ ```bash
712
+ npm test
713
+ ```
714
+
715
+ ## Troubleshooting
716
+
717
+ ### Issue: "Field type information not available"
718
+
719
+ **Cause**: TypeScript doesn't preserve type information at runtime by default.
720
+
721
+ **Solution**: Use `registerTypeFields()` or `registerQuery()`/`registerMutation()` with explicit type metadata.
722
+
723
+ ```typescript
724
+ // Instead of relying on decorators alone:
725
+ fraiseql.registerTypeFields("User", [
726
+ { name: "id", type: "Int", nullable: false },
727
+ // ... other fields
728
+ ]);
729
+ ```
730
+
731
+ ### Issue: "Factory not started: fraiseql-cli not found"
732
+
733
+ **Solution**: Install the CLI tool:
734
+
735
+ ```bash
736
+ # Global installation
737
+ npm install -g fraiseql-cli
738
+
739
+ # Or use local version
740
+ npx fraiseql-cli compile schema.json
741
+ ```
742
+
743
+ ## Performance
744
+
745
+ - **Compile-time**: Negligible (< 100ms for typical schemas)
746
+ - **Runtime**: Zero overhead - SQL is compiled, not interpreted
747
+ - **Schema generation**: Fast JSON serialization
748
+
749
+ ## Architecture Notes
750
+
751
+ ### No Runtime FFI
752
+
753
+ This package generates **JSON only**. There's no FFI, no native bindings, no runtime dependencies on the Rust engine.
754
+
755
+ The workflow is:
756
+
757
+ 1. Write TypeScript with decorators
758
+ 2. Run `exportSchema()` to generate `schema.json`
759
+ 3. Compile with `fraiseql-cli` to get `schema.compiled.json`
760
+ 4. Deploy compiled schema to Rust runtime
761
+
762
+ ### Why Manual Field Registration?
763
+
764
+ TypeScript's decorator system doesn't preserve generic type parameters at runtime. To provide full type information, we require explicit field registration. This is a limitation of the language, not the framework.
765
+
766
+ Future versions may use TypeScript 5.2+ metadata if decorators mature in the standard.
767
+
768
+ ## License
769
+
770
+ MIT
771
+
772
+ ## Support
773
+
774
+ - **Documentation**: <https://docs.fraiseql.io>
775
+ - **Issues**: <https://github.com/fraiseql/fraiseql/issues>
776
+ - **Examples**: See `examples/` directory
777
+
778
+ ## Contributing
779
+
780
+ Contributions welcome! Please follow the contribution guidelines in the main repository.
781
+
782
+ ---
783
+
784
+ **Remember**: FraiseQL TypeScript is for **authoring only**. Runtime execution happens in the Rust engine.