rayforce-wasm 0.1.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,1591 @@
1
+ /**
2
+ * RayforceDB JavaScript SDK
3
+ *
4
+ * Full-featured zero-copy wrapper for RayforceDB WASM module.
5
+ * Provides TypedArray views over native Rayforce vectors for efficient data access.
6
+ *
7
+ * @module rayforce
8
+ * @version 0.1.0
9
+ */
10
+
11
+ // ============================================================================
12
+ // SDK Factory
13
+ // ============================================================================
14
+
15
+ /**
16
+ * Creates a new RayforceDB SDK instance.
17
+ * @param {Object} wasmModule - The initialized Emscripten WASM module
18
+ * @returns {RayforceSDK} The SDK instance
19
+ */
20
+ export function createRayforceSDK(wasmModule) {
21
+ return new RayforceSDK(wasmModule);
22
+ }
23
+
24
+ // ============================================================================
25
+ // Type Constants
26
+ // ============================================================================
27
+
28
+ export const Types = Object.freeze({
29
+ LIST: 0,
30
+ B8: 1,
31
+ U8: 2,
32
+ I16: 3,
33
+ I32: 4,
34
+ I64: 5,
35
+ SYMBOL: 6,
36
+ DATE: 7,
37
+ TIME: 8,
38
+ TIMESTAMP: 9,
39
+ F64: 10,
40
+ GUID: 11,
41
+ C8: 12,
42
+ TABLE: 98,
43
+ DICT: 99,
44
+ LAMBDA: 100,
45
+ NULL: 126,
46
+ ERR: 127,
47
+ });
48
+
49
+ // Element sizes for each type (in bytes)
50
+ const ELEMENT_SIZES = {
51
+ [Types.B8]: 1,
52
+ [Types.U8]: 1,
53
+ [Types.C8]: 1,
54
+ [Types.I16]: 2,
55
+ [Types.I32]: 4,
56
+ [Types.I64]: 8,
57
+ [Types.F64]: 8,
58
+ [Types.DATE]: 4,
59
+ [Types.TIME]: 4,
60
+ [Types.TIMESTAMP]: 8,
61
+ [Types.SYMBOL]: 8,
62
+ [Types.GUID]: 16,
63
+ [Types.LIST]: 4, // pointer size in WASM32
64
+ };
65
+
66
+ // TypedArray constructors for each type
67
+ const TYPED_ARRAY_MAP = {
68
+ [Types.B8]: Int8Array,
69
+ [Types.U8]: Uint8Array,
70
+ [Types.C8]: Uint8Array,
71
+ [Types.I16]: Int16Array,
72
+ [Types.I32]: Int32Array,
73
+ [Types.I64]: BigInt64Array,
74
+ [Types.F64]: Float64Array,
75
+ [Types.DATE]: Int32Array,
76
+ [Types.TIME]: Int32Array,
77
+ [Types.TIMESTAMP]: BigInt64Array,
78
+ [Types.SYMBOL]: BigInt64Array,
79
+ };
80
+
81
+ // ============================================================================
82
+ // Main SDK Class
83
+ // ============================================================================
84
+
85
+ class RayforceSDK {
86
+ constructor(wasm) {
87
+ this._wasm = wasm;
88
+ this._cmdCounter = 0;
89
+ this._setupBindings();
90
+ }
91
+
92
+ _setupBindings() {
93
+ const w = this._wasm;
94
+
95
+ // Core functions
96
+ this._evalCmd = w.cwrap('eval_cmd', 'number', ['string', 'string']);
97
+ this._evalStr = w.cwrap('eval_str', 'number', ['string']);
98
+ this._strOfObj = w.cwrap('strof_obj', 'string', ['number']);
99
+ this._dropObj = w.cwrap('drop_obj', null, ['number']);
100
+ this._cloneObj = w.cwrap('clone_obj', 'number', ['number']);
101
+ this._versionStr = w.cwrap('version_str', 'string', []);
102
+
103
+ // Type introspection
104
+ this._getObjType = w.cwrap('get_obj_type', 'number', ['number']);
105
+ this._getObjLen = w.cwrap('get_obj_len', 'number', ['number']);
106
+ this._isObjAtom = w.cwrap('is_obj_atom', 'number', ['number']);
107
+ this._isObjVector = w.cwrap('is_obj_vector', 'number', ['number']);
108
+ this._isObjNull = w.cwrap('is_obj_null', 'number', ['number']);
109
+ this._isObjError = w.cwrap('is_obj_error', 'number', ['number']);
110
+ this._getObjRc = w.cwrap('get_obj_rc', 'number', ['number']);
111
+
112
+ // Memory access
113
+ this._getDataPtr = w.cwrap('get_data_ptr', 'number', ['number']);
114
+ this._getElementSize = w.cwrap('get_element_size', 'number', ['number']);
115
+ this._getDataByteSize = w.cwrap('get_data_byte_size', 'number', ['number']);
116
+
117
+ // Scalar constructors
118
+ this._initB8 = w.cwrap('init_b8', 'number', ['number']);
119
+ this._initU8 = w.cwrap('init_u8', 'number', ['number']);
120
+ this._initC8 = w.cwrap('init_c8', 'number', ['number']);
121
+ this._initI16 = w.cwrap('init_i16', 'number', ['number']);
122
+ this._initI32 = w.cwrap('init_i32', 'number', ['number']);
123
+ this._initI64 = w.cwrap('init_i64', 'number', ['number']);
124
+ this._initF64 = w.cwrap('init_f64', 'number', ['number']);
125
+ this._initDate = w.cwrap('init_date', 'number', ['number']);
126
+ this._initTime = w.cwrap('init_time', 'number', ['number']);
127
+ this._initTimestamp = w.cwrap('init_timestamp', 'number', ['number']);
128
+ this._initSymbolStr = w.cwrap('init_symbol_str', 'number', ['string', 'number']);
129
+ this._initStringStr = w.cwrap('init_string_str', 'number', ['string', 'number']);
130
+
131
+ // Scalar readers
132
+ this._readB8 = w.cwrap('read_b8', 'number', ['number']);
133
+ this._readU8 = w.cwrap('read_u8', 'number', ['number']);
134
+ this._readC8 = w.cwrap('read_c8', 'number', ['number']);
135
+ this._readI16 = w.cwrap('read_i16', 'number', ['number']);
136
+ this._readI32 = w.cwrap('read_i32', 'number', ['number']);
137
+ this._readI64 = w.cwrap('read_i64', 'number', ['number']);
138
+ this._readF64 = w.cwrap('read_f64', 'number', ['number']);
139
+ this._readDate = w.cwrap('read_date', 'number', ['number']);
140
+ this._readTime = w.cwrap('read_time', 'number', ['number']);
141
+ this._readTimestamp = w.cwrap('read_timestamp', 'number', ['number']);
142
+ this._readSymbolId = w.cwrap('read_symbol_id', 'number', ['number']);
143
+ this._symbolToStr = w.cwrap('symbol_to_str', 'string', ['number']);
144
+
145
+ // Vector operations
146
+ this._initVector = w.cwrap('init_vector', 'number', ['number', 'number']);
147
+ this._initList = w.cwrap('init_list', 'number', ['number']);
148
+ this._vecAtIdx = w.cwrap('vec_at_idx', 'number', ['number', 'number']);
149
+ this._atIdx = w.cwrap('at_idx', 'number', ['number', 'number']);
150
+ this._atObj = w.cwrap('at_obj', 'number', ['number', 'number']);
151
+ this._pushObj = w.cwrap('push_obj', 'number', ['number', 'number']);
152
+ this._insObj = w.cwrap('ins_obj', 'number', ['number', 'number', 'number']);
153
+
154
+ // Dict operations
155
+ this._initDict = w.cwrap('init_dict', 'number', ['number', 'number']);
156
+ this._dictKeys = w.cwrap('dict_keys', 'number', ['number']);
157
+ this._dictVals = w.cwrap('dict_vals', 'number', ['number']);
158
+ this._dictGet = w.cwrap('dict_get', 'number', ['number', 'number']);
159
+
160
+ // Table operations
161
+ this._initTable = w.cwrap('init_table', 'number', ['number', 'number']);
162
+ this._tableKeys = w.cwrap('table_keys', 'number', ['number']);
163
+ this._tableVals = w.cwrap('table_vals', 'number', ['number']);
164
+ this._tableCol = w.cwrap('table_col', 'number', ['number', 'string', 'number']);
165
+ this._tableRow = w.cwrap('table_row', 'number', ['number', 'number']);
166
+ this._tableCount = w.cwrap('table_count', 'number', ['number']);
167
+
168
+ // Query operations
169
+ this._querySelect = w.cwrap('query_select', 'number', ['number']);
170
+ this._queryUpdate = w.cwrap('query_update', 'number', ['number']);
171
+ this._tableInsert = w.cwrap('table_insert', 'number', ['number', 'number']);
172
+ this._tableUpsert = w.cwrap('table_upsert', 'number', ['number', 'number', 'number']);
173
+
174
+ // Other operations
175
+ this._internSymbol = w.cwrap('intern_symbol', 'number', ['string', 'number']);
176
+ this._globalSet = w.cwrap('global_set', null, ['number', 'number']);
177
+ this._quoteObj = w.cwrap('quote_obj', 'number', ['number']);
178
+ this._serialize = w.cwrap('serialize', 'number', ['number']);
179
+ this._deserialize = w.cwrap('deserialize', 'number', ['number']);
180
+ this._getTypeName = w.cwrap('get_type_name', 'string', ['number']);
181
+ }
182
+
183
+ // ==========================================================================
184
+ // Core Methods
185
+ // ==========================================================================
186
+
187
+ /**
188
+ * Get RayforceDB version string
189
+ * @returns {string}
190
+ */
191
+ get version() {
192
+ return this._versionStr();
193
+ }
194
+
195
+ /**
196
+ * Evaluate a Rayfall expression
197
+ * @param {string} code - The expression to evaluate
198
+ * @param {string} [sourceName] - Optional source name for error tracking
199
+ * @returns {RayObject} The result wrapped in appropriate type
200
+ */
201
+ eval(code, sourceName) {
202
+ const ptr = this._evalCmd(code, sourceName || `eval:${++this._cmdCounter}`);
203
+ return this._wrapPtr(ptr);
204
+ }
205
+
206
+ /**
207
+ * Evaluate and return raw result (for internal use)
208
+ * @param {string} code
209
+ * @returns {number} Raw pointer
210
+ */
211
+ _evalRaw(code) {
212
+ return this._evalCmd(code, `eval:${++this._cmdCounter}`);
213
+ }
214
+
215
+ /**
216
+ * Format any RayObject to string
217
+ * @param {RayObject|number} obj
218
+ * @returns {string}
219
+ */
220
+ format(obj) {
221
+ const ptr = obj instanceof RayObject ? obj._ptr : obj;
222
+ return this._strOfObj(ptr);
223
+ }
224
+
225
+ // ==========================================================================
226
+ // Type Wrapping
227
+ // ==========================================================================
228
+
229
+ /**
230
+ * Wrap a raw pointer in the appropriate RayObject subclass
231
+ * @param {number} ptr
232
+ * @returns {RayObject}
233
+ */
234
+ _wrapPtr(ptr) {
235
+ if (ptr === 0) return new RayNull(this, 0);
236
+
237
+ const type = this._getObjType(ptr);
238
+ const isAtom = this._isObjAtom(ptr);
239
+ const absType = type < 0 ? -type : type;
240
+
241
+ // Check for error
242
+ if (type === Types.ERR) {
243
+ return new RayError(this, ptr);
244
+ }
245
+
246
+ // Check for null
247
+ if (type === Types.NULL || this._isObjNull(ptr)) {
248
+ return new RayNull(this, ptr);
249
+ }
250
+
251
+ // Atoms (scalars)
252
+ if (isAtom) {
253
+ switch (absType) {
254
+ case Types.B8: return new B8(this, ptr);
255
+ case Types.U8: return new U8(this, ptr);
256
+ case Types.C8: return new C8(this, ptr);
257
+ case Types.I16: return new I16(this, ptr);
258
+ case Types.I32: return new I32(this, ptr);
259
+ case Types.I64: return new I64(this, ptr);
260
+ case Types.F64: return new F64(this, ptr);
261
+ case Types.DATE: return new RayDate(this, ptr);
262
+ case Types.TIME: return new RayTime(this, ptr);
263
+ case Types.TIMESTAMP: return new RayTimestamp(this, ptr);
264
+ case Types.SYMBOL: return new Symbol(this, ptr);
265
+ case Types.GUID: return new GUID(this, ptr);
266
+ default: return new RayObject(this, ptr);
267
+ }
268
+ }
269
+
270
+ // Vectors and containers
271
+ switch (type) {
272
+ case Types.C8: return new RayString(this, ptr);
273
+ case Types.LIST: return new List(this, ptr);
274
+ case Types.DICT: return new Dict(this, ptr);
275
+ case Types.TABLE: return new Table(this, ptr);
276
+ case Types.LAMBDA: return new Lambda(this, ptr);
277
+ default:
278
+ // Numeric vectors
279
+ if (TYPED_ARRAY_MAP[type]) {
280
+ return new Vector(this, ptr, type);
281
+ }
282
+ return new RayObject(this, ptr);
283
+ }
284
+ }
285
+
286
+ // ==========================================================================
287
+ // Constructors
288
+ // ==========================================================================
289
+
290
+ /**
291
+ * Create a boolean value
292
+ * @param {boolean} value
293
+ * @returns {B8}
294
+ */
295
+ b8(value) {
296
+ return new B8(this, this._initB8(value ? 1 : 0));
297
+ }
298
+
299
+ /**
300
+ * Create an unsigned byte value
301
+ * @param {number} value
302
+ * @returns {U8}
303
+ */
304
+ u8(value) {
305
+ return new U8(this, this._initU8(value & 0xFF));
306
+ }
307
+
308
+ /**
309
+ * Create a character value
310
+ * @param {string} value - Single character
311
+ * @returns {C8}
312
+ */
313
+ c8(value) {
314
+ const code = value.charCodeAt(0);
315
+ return new C8(this, this._initC8(code));
316
+ }
317
+
318
+ /**
319
+ * Create a 16-bit integer
320
+ * @param {number} value
321
+ * @returns {I16}
322
+ */
323
+ i16(value) {
324
+ return new I16(this, this._initI16(value | 0));
325
+ }
326
+
327
+ /**
328
+ * Create a 32-bit integer
329
+ * @param {number} value
330
+ * @returns {I32}
331
+ */
332
+ i32(value) {
333
+ return new I32(this, this._initI32(value | 0));
334
+ }
335
+
336
+ /**
337
+ * Create a 64-bit integer
338
+ * @param {number|bigint} value
339
+ * @returns {I64}
340
+ */
341
+ i64(value) {
342
+ // Note: JS number can only safely represent up to 2^53
343
+ return new I64(this, this._initI64(Number(value)));
344
+ }
345
+
346
+ /**
347
+ * Create a 64-bit float
348
+ * @param {number} value
349
+ * @returns {F64}
350
+ */
351
+ f64(value) {
352
+ return new F64(this, this._initF64(value));
353
+ }
354
+
355
+ /**
356
+ * Create a date (days since 2000-01-01)
357
+ * @param {number|Date} value - Days or JS Date object
358
+ * @returns {RayDate}
359
+ */
360
+ date(value) {
361
+ let days;
362
+ if (value instanceof Date) {
363
+ // Convert JS Date to days since 2000-01-01
364
+ const epoch = new Date(2000, 0, 1);
365
+ days = Math.floor((value - epoch) / (1000 * 60 * 60 * 24));
366
+ } else {
367
+ days = value | 0;
368
+ }
369
+ return new RayDate(this, this._initDate(days));
370
+ }
371
+
372
+ /**
373
+ * Create a time (milliseconds since midnight)
374
+ * @param {number|Date} value - Milliseconds or JS Date object
375
+ * @returns {RayTime}
376
+ */
377
+ time(value) {
378
+ let ms;
379
+ if (value instanceof Date) {
380
+ ms = value.getHours() * 3600000 +
381
+ value.getMinutes() * 60000 +
382
+ value.getSeconds() * 1000 +
383
+ value.getMilliseconds();
384
+ } else {
385
+ ms = value | 0;
386
+ }
387
+ return new RayTime(this, this._initTime(ms));
388
+ }
389
+
390
+ /**
391
+ * Create a timestamp (nanoseconds since 2000-01-01)
392
+ * @param {number|bigint|Date} value - Nanoseconds or JS Date
393
+ * @returns {RayTimestamp}
394
+ */
395
+ timestamp(value) {
396
+ let ns;
397
+ if (value instanceof Date) {
398
+ const epoch = new Date(2000, 0, 1);
399
+ ns = Number(value - epoch) * 1000000; // ms to ns
400
+ } else {
401
+ ns = Number(value);
402
+ }
403
+ return new RayTimestamp(this, this._initTimestamp(ns));
404
+ }
405
+
406
+ /**
407
+ * Create a symbol (interned string)
408
+ * @param {string} value
409
+ * @returns {Symbol}
410
+ */
411
+ symbol(value) {
412
+ return new Symbol(this, this._initSymbolStr(value, value.length));
413
+ }
414
+
415
+ /**
416
+ * Create a string
417
+ * @param {string} value
418
+ * @returns {RayString}
419
+ */
420
+ string(value) {
421
+ return new RayString(this, this._initStringStr(value, value.length));
422
+ }
423
+
424
+ /**
425
+ * Create a vector of specified type
426
+ * @param {number} type - Type code from Types
427
+ * @param {number|Array} lengthOrData - Length or array of values
428
+ * @returns {Vector}
429
+ */
430
+ vector(type, lengthOrData) {
431
+ if (Array.isArray(lengthOrData)) {
432
+ const arr = lengthOrData;
433
+ const vec = new Vector(this, this._initVector(type, arr.length), type);
434
+ const view = vec.typedArray;
435
+ for (let i = 0; i < arr.length; i++) {
436
+ if (type === Types.I64 || type === Types.TIMESTAMP || type === Types.SYMBOL) {
437
+ view[i] = BigInt(arr[i]);
438
+ } else {
439
+ view[i] = arr[i];
440
+ }
441
+ }
442
+ return vec;
443
+ }
444
+ return new Vector(this, this._initVector(type, lengthOrData), type);
445
+ }
446
+
447
+ /**
448
+ * Create a list (mixed-type container)
449
+ * @param {Array} [items] - Optional array of items
450
+ * @returns {List}
451
+ */
452
+ list(items) {
453
+ const len = items ? items.length : 0;
454
+ const list = new List(this, this._initList(len));
455
+ if (items) {
456
+ for (let i = 0; i < items.length; i++) {
457
+ list.set(i, items[i]);
458
+ }
459
+ }
460
+ return list;
461
+ }
462
+
463
+ /**
464
+ * Create a dict (key-value mapping)
465
+ * @param {Object} obj - JS object to convert
466
+ * @returns {Dict}
467
+ */
468
+ dict(obj) {
469
+ const keys = Object.keys(obj);
470
+ const keyVec = this.vector(Types.SYMBOL, keys.length);
471
+ const keyView = keyVec.typedArray;
472
+ for (let i = 0; i < keys.length; i++) {
473
+ keyView[i] = BigInt(this._internSymbol(keys[i], keys[i].length));
474
+ }
475
+
476
+ const valList = this.list(Object.values(obj).map(v => this._toRayObject(v)));
477
+ return new Dict(this, this._initDict(keyVec._ptr, valList._ptr));
478
+ }
479
+
480
+ /**
481
+ * Create a table from column definitions
482
+ * @param {Object} columns - Object with column names as keys and arrays as values
483
+ * @returns {Table}
484
+ */
485
+ table(columns) {
486
+ const colNames = Object.keys(columns);
487
+ const keyVec = this.vector(Types.SYMBOL, colNames.length);
488
+ const keyView = keyVec.typedArray;
489
+ for (let i = 0; i < colNames.length; i++) {
490
+ keyView[i] = BigInt(this._internSymbol(colNames[i], colNames[i].length));
491
+ }
492
+
493
+ const valList = this.list();
494
+ for (const name of colNames) {
495
+ const data = columns[name];
496
+ const col = this._arrayToVector(data);
497
+ valList.push(col);
498
+ }
499
+
500
+ return new Table(this, this._initTable(keyVec._ptr, valList._ptr));
501
+ }
502
+
503
+ /**
504
+ * Convert JS array to appropriate vector type
505
+ * @param {Array} arr
506
+ * @returns {Vector}
507
+ */
508
+ _arrayToVector(arr) {
509
+ if (arr.length === 0) {
510
+ return this.vector(Types.I64, 0);
511
+ }
512
+
513
+ const first = arr[0];
514
+ let type;
515
+
516
+ if (typeof first === 'boolean') {
517
+ type = Types.B8;
518
+ } else if (typeof first === 'number') {
519
+ type = Number.isInteger(first) ? Types.I64 : Types.F64;
520
+ } else if (typeof first === 'bigint') {
521
+ type = Types.I64;
522
+ } else if (typeof first === 'string') {
523
+ type = Types.SYMBOL;
524
+ } else if (first instanceof Date) {
525
+ type = Types.TIMESTAMP;
526
+ } else {
527
+ // Default to list for mixed types
528
+ return this.list(arr.map(v => this._toRayObject(v)));
529
+ }
530
+
531
+ const vec = this.vector(type, arr.length);
532
+ const view = vec.typedArray;
533
+
534
+ for (let i = 0; i < arr.length; i++) {
535
+ if (type === Types.SYMBOL) {
536
+ view[i] = BigInt(this._internSymbol(arr[i], arr[i].length));
537
+ } else if (type === Types.I64 || type === Types.TIMESTAMP) {
538
+ if (arr[i] instanceof Date) {
539
+ const epoch = new Date(2000, 0, 1);
540
+ view[i] = BigInt((arr[i] - epoch) * 1000000);
541
+ } else {
542
+ view[i] = BigInt(arr[i]);
543
+ }
544
+ } else if (type === Types.B8) {
545
+ view[i] = arr[i] ? 1 : 0;
546
+ } else {
547
+ view[i] = arr[i];
548
+ }
549
+ }
550
+
551
+ return vec;
552
+ }
553
+
554
+ /**
555
+ * Convert JS value to RayObject
556
+ * @param {any} value
557
+ * @returns {RayObject}
558
+ */
559
+ _toRayObject(value) {
560
+ if (value instanceof RayObject) return value;
561
+ if (value === null || value === undefined) return new RayNull(this, 0);
562
+ if (typeof value === 'boolean') return this.b8(value);
563
+ if (typeof value === 'number') {
564
+ return Number.isInteger(value) ? this.i64(value) : this.f64(value);
565
+ }
566
+ if (typeof value === 'bigint') return this.i64(value);
567
+ if (typeof value === 'string') return this.symbol(value);
568
+ if (value instanceof Date) return this.timestamp(value);
569
+ if (Array.isArray(value)) return this._arrayToVector(value);
570
+ if (typeof value === 'object') return this.dict(value);
571
+ return new RayNull(this, 0);
572
+ }
573
+
574
+ // ==========================================================================
575
+ // Utility Methods
576
+ // ==========================================================================
577
+
578
+ /**
579
+ * Set a global variable
580
+ * @param {string} name
581
+ * @param {RayObject|any} value
582
+ */
583
+ set(name, value) {
584
+ const sym = this.symbol(name);
585
+ const val = value instanceof RayObject ? value : this._toRayObject(value);
586
+ this._globalSet(sym._ptr, val._ptr);
587
+ }
588
+
589
+ /**
590
+ * Get a global variable
591
+ * @param {string} name
592
+ * @returns {RayObject}
593
+ */
594
+ get(name) {
595
+ return this.eval(name);
596
+ }
597
+
598
+ /**
599
+ * Get type name string
600
+ * @param {number} typeCode
601
+ * @returns {string}
602
+ */
603
+ typeName(typeCode) {
604
+ return this._getTypeName(typeCode);
605
+ }
606
+ }
607
+
608
+ // ============================================================================
609
+ // Base RayObject Class
610
+ // ============================================================================
611
+
612
+ class RayObject {
613
+ constructor(sdk, ptr) {
614
+ this._sdk = sdk;
615
+ this._ptr = ptr;
616
+ this._owned = true;
617
+ }
618
+
619
+ /**
620
+ * Get raw pointer value
621
+ * @returns {number}
622
+ */
623
+ get ptr() {
624
+ return this._ptr;
625
+ }
626
+
627
+ /**
628
+ * Get type code
629
+ * @returns {number}
630
+ */
631
+ get type() {
632
+ return this._sdk._getObjType(this._ptr);
633
+ }
634
+
635
+ /**
636
+ * Get absolute type code (without sign)
637
+ * @returns {number}
638
+ */
639
+ get absType() {
640
+ const t = this.type;
641
+ return t < 0 ? -t : t;
642
+ }
643
+
644
+ /**
645
+ * Check if this is an atom (scalar)
646
+ * @returns {boolean}
647
+ */
648
+ get isAtom() {
649
+ return this._sdk._isObjAtom(this._ptr) !== 0;
650
+ }
651
+
652
+ /**
653
+ * Check if this is a vector
654
+ * @returns {boolean}
655
+ */
656
+ get isVector() {
657
+ return this._sdk._isObjVector(this._ptr) !== 0;
658
+ }
659
+
660
+ /**
661
+ * Check if this is null
662
+ * @returns {boolean}
663
+ */
664
+ get isNull() {
665
+ return this._sdk._isObjNull(this._ptr) !== 0;
666
+ }
667
+
668
+ /**
669
+ * Check if this is an error
670
+ * @returns {boolean}
671
+ */
672
+ get isError() {
673
+ return this._sdk._isObjError(this._ptr) !== 0;
674
+ }
675
+
676
+ /**
677
+ * Get length (1 for atoms)
678
+ * @returns {number}
679
+ */
680
+ get length() {
681
+ return this._sdk._getObjLen(this._ptr);
682
+ }
683
+
684
+ /**
685
+ * Get reference count
686
+ * @returns {number}
687
+ */
688
+ get refCount() {
689
+ return this._sdk._getObjRc(this._ptr);
690
+ }
691
+
692
+ /**
693
+ * Clone this object
694
+ * @returns {RayObject}
695
+ */
696
+ clone() {
697
+ return this._sdk._wrapPtr(this._sdk._cloneObj(this._ptr));
698
+ }
699
+
700
+ /**
701
+ * Format to string
702
+ * @returns {string}
703
+ */
704
+ toString() {
705
+ return this._sdk.format(this._ptr);
706
+ }
707
+
708
+ /**
709
+ * Convert to JavaScript value
710
+ * @returns {any}
711
+ */
712
+ toJS() {
713
+ return this.toString();
714
+ }
715
+
716
+ /**
717
+ * Free this object's memory
718
+ */
719
+ drop() {
720
+ if (this._owned && this._ptr !== 0) {
721
+ this._sdk._dropObj(this._ptr);
722
+ this._ptr = 0;
723
+ this._owned = false;
724
+ }
725
+ }
726
+
727
+ /**
728
+ * Release ownership (don't drop on GC)
729
+ * @returns {number} The raw pointer
730
+ */
731
+ release() {
732
+ this._owned = false;
733
+ return this._ptr;
734
+ }
735
+ }
736
+
737
+ // ============================================================================
738
+ // Scalar Types
739
+ // ============================================================================
740
+
741
+ class B8 extends RayObject {
742
+ static typeCode = -Types.B8;
743
+
744
+ get value() {
745
+ return this._sdk._readB8(this._ptr) !== 0;
746
+ }
747
+
748
+ toJS() {
749
+ return this.value;
750
+ }
751
+ }
752
+
753
+ class U8 extends RayObject {
754
+ static typeCode = -Types.U8;
755
+
756
+ get value() {
757
+ return this._sdk._readU8(this._ptr);
758
+ }
759
+
760
+ toJS() {
761
+ return this.value;
762
+ }
763
+ }
764
+
765
+ class C8 extends RayObject {
766
+ static typeCode = -Types.C8;
767
+
768
+ get value() {
769
+ return String.fromCharCode(this._sdk._readC8(this._ptr));
770
+ }
771
+
772
+ toJS() {
773
+ return this.value;
774
+ }
775
+ }
776
+
777
+ class I16 extends RayObject {
778
+ static typeCode = -Types.I16;
779
+
780
+ get value() {
781
+ return this._sdk._readI16(this._ptr);
782
+ }
783
+
784
+ toJS() {
785
+ return this.value;
786
+ }
787
+ }
788
+
789
+ class I32 extends RayObject {
790
+ static typeCode = -Types.I32;
791
+
792
+ get value() {
793
+ return this._sdk._readI32(this._ptr);
794
+ }
795
+
796
+ toJS() {
797
+ return this.value;
798
+ }
799
+ }
800
+
801
+ class I64 extends RayObject {
802
+ static typeCode = -Types.I64;
803
+
804
+ get value() {
805
+ return this._sdk._readI64(this._ptr);
806
+ }
807
+
808
+ toJS() {
809
+ return this.value;
810
+ }
811
+ }
812
+
813
+ class F64 extends RayObject {
814
+ static typeCode = -Types.F64;
815
+
816
+ get value() {
817
+ return this._sdk._readF64(this._ptr);
818
+ }
819
+
820
+ toJS() {
821
+ return this.value;
822
+ }
823
+ }
824
+
825
+ class RayDate extends RayObject {
826
+ static typeCode = -Types.DATE;
827
+
828
+ /**
829
+ * Get days since 2000-01-01
830
+ */
831
+ get value() {
832
+ return this._sdk._readDate(this._ptr);
833
+ }
834
+
835
+ /**
836
+ * Convert to JS Date
837
+ */
838
+ toJS() {
839
+ const epoch = new Date(2000, 0, 1);
840
+ return new Date(epoch.getTime() + this.value * 24 * 60 * 60 * 1000);
841
+ }
842
+ }
843
+
844
+ class RayTime extends RayObject {
845
+ static typeCode = -Types.TIME;
846
+
847
+ /**
848
+ * Get milliseconds since midnight
849
+ */
850
+ get value() {
851
+ return this._sdk._readTime(this._ptr);
852
+ }
853
+
854
+ toJS() {
855
+ const ms = this.value;
856
+ const hours = Math.floor(ms / 3600000);
857
+ const minutes = Math.floor((ms % 3600000) / 60000);
858
+ const seconds = Math.floor((ms % 60000) / 1000);
859
+ const millis = ms % 1000;
860
+ return { hours, minutes, seconds, milliseconds: millis };
861
+ }
862
+ }
863
+
864
+ class RayTimestamp extends RayObject {
865
+ static typeCode = -Types.TIMESTAMP;
866
+
867
+ /**
868
+ * Get nanoseconds since 2000-01-01
869
+ */
870
+ get value() {
871
+ return this._sdk._readTimestamp(this._ptr);
872
+ }
873
+
874
+ toJS() {
875
+ const epoch = new Date(2000, 0, 1);
876
+ return new Date(epoch.getTime() + this.value / 1000000);
877
+ }
878
+ }
879
+
880
+ class Symbol extends RayObject {
881
+ static typeCode = -Types.SYMBOL;
882
+
883
+ /**
884
+ * Get interned symbol ID
885
+ */
886
+ get id() {
887
+ return this._sdk._readSymbolId(this._ptr);
888
+ }
889
+
890
+ /**
891
+ * Get symbol string value
892
+ */
893
+ get value() {
894
+ return this._sdk._symbolToStr(this.id);
895
+ }
896
+
897
+ toJS() {
898
+ return this.value;
899
+ }
900
+ }
901
+
902
+ class GUID extends RayObject {
903
+ static typeCode = -Types.GUID;
904
+
905
+ toJS() {
906
+ // Format GUID bytes as string
907
+ return this.toString();
908
+ }
909
+ }
910
+
911
+ // ============================================================================
912
+ // Null and Error Types
913
+ // ============================================================================
914
+
915
+ class RayNull extends RayObject {
916
+ static typeCode = Types.NULL;
917
+
918
+ get isNull() {
919
+ return true;
920
+ }
921
+
922
+ toJS() {
923
+ return null;
924
+ }
925
+ }
926
+
927
+ class RayError extends RayObject {
928
+ static typeCode = Types.ERR;
929
+
930
+ get isError() {
931
+ return true;
932
+ }
933
+
934
+ get message() {
935
+ return this.toString();
936
+ }
937
+
938
+ toJS() {
939
+ throw new Error(this.message);
940
+ }
941
+ }
942
+
943
+ // ============================================================================
944
+ // Vector with Zero-Copy TypedArray View
945
+ // ============================================================================
946
+
947
+ class Vector extends RayObject {
948
+ constructor(sdk, ptr, elementType) {
949
+ super(sdk, ptr);
950
+ this._elementType = elementType !== undefined ? elementType : sdk._getObjType(ptr);
951
+ this._typedArray = null;
952
+ }
953
+
954
+ get elementType() {
955
+ return this._elementType;
956
+ }
957
+
958
+ /**
959
+ * Get zero-copy TypedArray view over the vector data.
960
+ * WARNING: This view is only valid while the Vector exists.
961
+ * @returns {TypedArray}
962
+ */
963
+ get typedArray() {
964
+ if (this._typedArray === null) {
965
+ const ArrayType = TYPED_ARRAY_MAP[this._elementType];
966
+ if (!ArrayType) {
967
+ throw new Error(`No TypedArray for type ${this._elementType}`);
968
+ }
969
+
970
+ const dataPtr = this._sdk._getDataPtr(this._ptr);
971
+ const byteSize = this._sdk._getDataByteSize(this._ptr);
972
+ const elementSize = ELEMENT_SIZES[this._elementType];
973
+ const length = byteSize / elementSize;
974
+
975
+ // Create view over WASM memory
976
+ this._typedArray = new ArrayType(
977
+ this._sdk._wasm.HEAPU8.buffer,
978
+ dataPtr,
979
+ length
980
+ );
981
+ }
982
+ return this._typedArray;
983
+ }
984
+
985
+ /**
986
+ * Get element at index
987
+ * @param {number} idx
988
+ * @param {boolean} [raw=false] - If true, return raw value without conversion
989
+ * @returns {any}
990
+ */
991
+ at(idx, raw = false) {
992
+ if (idx < 0) idx = this.length + idx;
993
+ if (idx < 0 || idx >= this.length) {
994
+ throw new RangeError(`Index ${idx} out of bounds [0, ${this.length})`);
995
+ }
996
+ const val = this.typedArray[idx];
997
+
998
+ // Convert symbol IDs to strings
999
+ if (!raw && this._elementType === Types.SYMBOL) {
1000
+ return this._sdk._symbolToStr(Number(val));
1001
+ }
1002
+
1003
+ // Convert BigInt to Number if safe
1004
+ if (!raw && typeof val === 'bigint') {
1005
+ const n = Number(val);
1006
+ if (Number.isSafeInteger(n)) return n;
1007
+ }
1008
+
1009
+ return val;
1010
+ }
1011
+
1012
+ /**
1013
+ * Set element at index
1014
+ * @param {number} idx
1015
+ * @param {any} value
1016
+ */
1017
+ set(idx, value) {
1018
+ if (idx < 0) idx = this.length + idx;
1019
+ if (idx < 0 || idx >= this.length) {
1020
+ throw new RangeError(`Index ${idx} out of bounds [0, ${this.length})`);
1021
+ }
1022
+ this.typedArray[idx] = value;
1023
+ }
1024
+
1025
+ /**
1026
+ * Convert to JS array (copies data)
1027
+ * @returns {Array}
1028
+ */
1029
+ toJS() {
1030
+ const arr = Array.from(this.typedArray);
1031
+
1032
+ // Convert BigInt to Number for I64 types if safe
1033
+ if (this._elementType === Types.I64 ||
1034
+ this._elementType === Types.TIMESTAMP ||
1035
+ this._elementType === Types.SYMBOL) {
1036
+ return arr.map(v => {
1037
+ if (this._elementType === Types.SYMBOL) {
1038
+ return this._sdk._symbolToStr(Number(v));
1039
+ }
1040
+ const n = Number(v);
1041
+ return Number.isSafeInteger(n) ? n : v;
1042
+ });
1043
+ }
1044
+
1045
+ return arr;
1046
+ }
1047
+
1048
+ /**
1049
+ * Iterator support
1050
+ */
1051
+ *[globalThis.Symbol.iterator]() {
1052
+ const view = this.typedArray;
1053
+ for (let i = 0; i < view.length; i++) {
1054
+ yield view[i];
1055
+ }
1056
+ }
1057
+ }
1058
+
1059
+ // ============================================================================
1060
+ // String (Character Vector)
1061
+ // ============================================================================
1062
+
1063
+ class RayString extends Vector {
1064
+ constructor(sdk, ptr) {
1065
+ super(sdk, ptr, Types.C8);
1066
+ }
1067
+
1068
+ /**
1069
+ * Get string value
1070
+ * @returns {string}
1071
+ */
1072
+ get value() {
1073
+ const decoder = new TextDecoder('utf-8');
1074
+ return decoder.decode(this.typedArray);
1075
+ }
1076
+
1077
+ toJS() {
1078
+ return this.value;
1079
+ }
1080
+
1081
+ toString() {
1082
+ return this.value;
1083
+ }
1084
+ }
1085
+
1086
+ // ============================================================================
1087
+ // List (Mixed-Type Container)
1088
+ // ============================================================================
1089
+
1090
+ class List extends RayObject {
1091
+ /**
1092
+ * Get element at index
1093
+ * @param {number} idx
1094
+ * @returns {RayObject}
1095
+ */
1096
+ at(idx) {
1097
+ if (idx < 0) idx = this.length + idx;
1098
+ if (idx < 0 || idx >= this.length) {
1099
+ throw new RangeError(`Index ${idx} out of bounds [0, ${this.length})`);
1100
+ }
1101
+ const ptr = this._sdk._atIdx(this._ptr, idx);
1102
+ return this._sdk._wrapPtr(ptr);
1103
+ }
1104
+
1105
+ /**
1106
+ * Set element at index
1107
+ * @param {number} idx
1108
+ * @param {RayObject|any} value
1109
+ */
1110
+ set(idx, value) {
1111
+ if (idx < 0) idx = this.length + idx;
1112
+ const obj = value instanceof RayObject ? value : this._sdk._toRayObject(value);
1113
+ this._sdk._wasm.ccall('ins_obj', 'number',
1114
+ ['number', 'number', 'number'],
1115
+ [this._ptr, idx, obj._ptr]);
1116
+ }
1117
+
1118
+ /**
1119
+ * Push element to end
1120
+ * @param {RayObject|any} value
1121
+ */
1122
+ push(value) {
1123
+ const obj = value instanceof RayObject ? value : this._sdk._toRayObject(value);
1124
+ // Use stack allocation for the pointer-to-pointer
1125
+ const stackSave = this._sdk._wasm.stackSave();
1126
+ const ptrPtr = this._sdk._wasm.stackAlloc(4);
1127
+ this._sdk._wasm.setValue(ptrPtr, this._ptr, 'i32');
1128
+ this._sdk._pushObj(ptrPtr, obj._ptr);
1129
+ this._ptr = this._sdk._wasm.getValue(ptrPtr, 'i32');
1130
+ this._sdk._wasm.stackRestore(stackSave);
1131
+ }
1132
+
1133
+ /**
1134
+ * Convert to JS array
1135
+ * @returns {Array}
1136
+ */
1137
+ toJS() {
1138
+ const result = [];
1139
+ for (let i = 0; i < this.length; i++) {
1140
+ result.push(this.at(i).toJS());
1141
+ }
1142
+ return result;
1143
+ }
1144
+
1145
+ *[globalThis.Symbol.iterator]() {
1146
+ for (let i = 0; i < this.length; i++) {
1147
+ yield this.at(i);
1148
+ }
1149
+ }
1150
+ }
1151
+
1152
+ // ============================================================================
1153
+ // Dict (Key-Value Mapping)
1154
+ // ============================================================================
1155
+
1156
+ class Dict extends RayObject {
1157
+ /**
1158
+ * Get keys as symbol vector
1159
+ * @returns {Vector}
1160
+ */
1161
+ keys() {
1162
+ return this._sdk._wrapPtr(this._sdk._dictKeys(this._ptr));
1163
+ }
1164
+
1165
+ /**
1166
+ * Get values as list
1167
+ * @returns {List}
1168
+ */
1169
+ values() {
1170
+ return this._sdk._wrapPtr(this._sdk._dictVals(this._ptr));
1171
+ }
1172
+
1173
+ /**
1174
+ * Get value by key
1175
+ * @param {string|Symbol} key
1176
+ * @returns {RayObject}
1177
+ */
1178
+ get(key) {
1179
+ const keyObj = typeof key === 'string' ? this._sdk.symbol(key) : key;
1180
+ const ptr = this._sdk._dictGet(this._ptr, keyObj._ptr);
1181
+ return this._sdk._wrapPtr(ptr);
1182
+ }
1183
+
1184
+ /**
1185
+ * Check if key exists
1186
+ * @param {string} key
1187
+ * @returns {boolean}
1188
+ */
1189
+ has(key) {
1190
+ return !this.get(key).isNull;
1191
+ }
1192
+
1193
+ /**
1194
+ * Convert to JS object
1195
+ * @returns {Object}
1196
+ */
1197
+ toJS() {
1198
+ const result = {};
1199
+ const keys = this.keys();
1200
+ const vals = this.values();
1201
+
1202
+ for (let i = 0; i < keys.length; i++) {
1203
+ const keyStr = this._sdk._symbolToStr(Number(keys.at(i)));
1204
+ result[keyStr] = vals.at(i).toJS();
1205
+ }
1206
+
1207
+ return result;
1208
+ }
1209
+
1210
+ *[globalThis.Symbol.iterator]() {
1211
+ const keys = this.keys();
1212
+ const vals = this.values();
1213
+ for (let i = 0; i < keys.length; i++) {
1214
+ const keyStr = this._sdk._symbolToStr(Number(keys.at(i)));
1215
+ yield [keyStr, vals.at(i)];
1216
+ }
1217
+ }
1218
+ }
1219
+
1220
+ // ============================================================================
1221
+ // Table
1222
+ // ============================================================================
1223
+
1224
+ class Table extends RayObject {
1225
+ /**
1226
+ * Get column names
1227
+ * @returns {Vector}
1228
+ */
1229
+ columns() {
1230
+ return this._sdk._wrapPtr(this._sdk._tableKeys(this._ptr));
1231
+ }
1232
+
1233
+ /**
1234
+ * Get column names as string array
1235
+ * @returns {string[]}
1236
+ */
1237
+ columnNames() {
1238
+ const cols = this.columns();
1239
+ const names = [];
1240
+ for (let i = 0; i < cols.length; i++) {
1241
+ // at() already converts symbol IDs to strings
1242
+ names.push(cols.at(i));
1243
+ }
1244
+ return names;
1245
+ }
1246
+
1247
+ /**
1248
+ * Get all values as list of vectors
1249
+ * @returns {List}
1250
+ */
1251
+ values() {
1252
+ return this._sdk._wrapPtr(this._sdk._tableVals(this._ptr));
1253
+ }
1254
+
1255
+ /**
1256
+ * Get column by name
1257
+ * @param {string} name
1258
+ * @returns {Vector}
1259
+ */
1260
+ col(name) {
1261
+ return this._sdk._wrapPtr(this._sdk._tableCol(this._ptr, name, name.length));
1262
+ }
1263
+
1264
+ /**
1265
+ * Get row by index
1266
+ * @param {number} idx
1267
+ * @returns {Dict}
1268
+ */
1269
+ row(idx) {
1270
+ return this._sdk._wrapPtr(this._sdk._tableRow(this._ptr, idx));
1271
+ }
1272
+
1273
+ /**
1274
+ * Get row count
1275
+ * @returns {number}
1276
+ */
1277
+ get rowCount() {
1278
+ return this._sdk._tableCount(this._ptr);
1279
+ }
1280
+
1281
+ /**
1282
+ * Create a select query builder
1283
+ * @param {...string} cols - Column names to select
1284
+ * @returns {SelectQuery}
1285
+ */
1286
+ select(...cols) {
1287
+ return new SelectQuery(this._sdk, this).select(...cols);
1288
+ }
1289
+
1290
+ /**
1291
+ * Create a where clause
1292
+ * @param {Expression} condition
1293
+ * @returns {SelectQuery}
1294
+ */
1295
+ where(condition) {
1296
+ return new SelectQuery(this._sdk, this).where(condition);
1297
+ }
1298
+
1299
+ /**
1300
+ * Insert data into table
1301
+ * @param {Object|Array} data
1302
+ * @returns {Table}
1303
+ */
1304
+ insert(data) {
1305
+ let insertData;
1306
+ if (Array.isArray(data)) {
1307
+ insertData = this._sdk.list(data.map(v => this._sdk._toRayObject(v)));
1308
+ } else {
1309
+ insertData = this._sdk.dict(data);
1310
+ }
1311
+ const newPtr = this._sdk._tableInsert(this._ptr, insertData._ptr);
1312
+ return this._sdk._wrapPtr(newPtr);
1313
+ }
1314
+
1315
+ /**
1316
+ * Convert to JS object with column arrays
1317
+ * @returns {Object}
1318
+ */
1319
+ toJS() {
1320
+ const result = {};
1321
+ const names = this.columnNames();
1322
+ const vals = this.values();
1323
+
1324
+ for (let i = 0; i < names.length; i++) {
1325
+ result[names[i]] = vals.at(i).toJS();
1326
+ }
1327
+
1328
+ return result;
1329
+ }
1330
+
1331
+ /**
1332
+ * Convert to array of row objects
1333
+ * @returns {Object[]}
1334
+ */
1335
+ toRows() {
1336
+ const names = this.columnNames();
1337
+ const count = this.rowCount;
1338
+ const rows = [];
1339
+
1340
+ for (let i = 0; i < count; i++) {
1341
+ const row = {};
1342
+ for (const name of names) {
1343
+ row[name] = this.col(name).at(i);
1344
+ if (typeof row[name] === 'bigint') {
1345
+ const n = Number(row[name]);
1346
+ row[name] = Number.isSafeInteger(n) ? n : row[name];
1347
+ }
1348
+ }
1349
+ rows.push(row);
1350
+ }
1351
+
1352
+ return rows;
1353
+ }
1354
+ }
1355
+
1356
+ // ============================================================================
1357
+ // Lambda (Function)
1358
+ // ============================================================================
1359
+
1360
+ class Lambda extends RayObject {
1361
+ /**
1362
+ * Call the lambda with arguments
1363
+ * @param {...any} args
1364
+ * @returns {RayObject}
1365
+ */
1366
+ call(...args) {
1367
+ // Build call expression
1368
+ const argList = args.map(a => {
1369
+ if (a instanceof RayObject) return a.toString();
1370
+ if (typeof a === 'string') return `\`${a}`;
1371
+ return String(a);
1372
+ }).join(' ');
1373
+
1374
+ const expr = `(${this.toString()} ${argList})`;
1375
+ return this._sdk.eval(expr);
1376
+ }
1377
+ }
1378
+
1379
+ // ============================================================================
1380
+ // Query Builder
1381
+ // ============================================================================
1382
+
1383
+ /**
1384
+ * Expression builder for query conditions
1385
+ */
1386
+ class Expr {
1387
+ constructor(sdk, parts) {
1388
+ this._sdk = sdk;
1389
+ this._parts = parts;
1390
+ }
1391
+
1392
+ /**
1393
+ * Create a column reference
1394
+ * @param {string} name
1395
+ * @returns {Expr}
1396
+ */
1397
+ static col(sdk, name) {
1398
+ return new Expr(sdk, [`\`${name}`]);
1399
+ }
1400
+
1401
+ // Comparison operators
1402
+ eq(value) { return this._binOp('=', value); }
1403
+ ne(value) { return this._binOp('<>', value); }
1404
+ lt(value) { return this._binOp('<', value); }
1405
+ le(value) { return this._binOp('<=', value); }
1406
+ gt(value) { return this._binOp('>', value); }
1407
+ ge(value) { return this._binOp('>=', value); }
1408
+
1409
+ // Logical operators
1410
+ and(other) { return this._logicOp('and', other); }
1411
+ or(other) { return this._logicOp('or', other); }
1412
+ not() { return new Expr(this._sdk, ['(not', ...this._parts, ')']); }
1413
+
1414
+ // Aggregations
1415
+ sum() { return new Expr(this._sdk, ['(sum', ...this._parts, ')']); }
1416
+ avg() { return new Expr(this._sdk, ['(avg', ...this._parts, ')']); }
1417
+ min() { return new Expr(this._sdk, ['(min', ...this._parts, ')']); }
1418
+ max() { return new Expr(this._sdk, ['(max', ...this._parts, ')']); }
1419
+ count() { return new Expr(this._sdk, ['(count', ...this._parts, ')']); }
1420
+ first() { return new Expr(this._sdk, ['(first', ...this._parts, ')']); }
1421
+ last() { return new Expr(this._sdk, ['(last', ...this._parts, ')']); }
1422
+ distinct() { return new Expr(this._sdk, ['(distinct', ...this._parts, ')']); }
1423
+
1424
+ _binOp(op, value) {
1425
+ const valStr = this._valueToStr(value);
1426
+ return new Expr(this._sdk, [`(${op}`, ...this._parts, valStr, ')']);
1427
+ }
1428
+
1429
+ _logicOp(op, other) {
1430
+ return new Expr(this._sdk, [`(${op}`, ...this._parts, ...other._parts, ')']);
1431
+ }
1432
+
1433
+ _valueToStr(value) {
1434
+ if (value instanceof Expr) return value.toString();
1435
+ if (typeof value === 'string') return `"${value}"`;
1436
+ if (value instanceof Date) {
1437
+ // Format as Rayforce timestamp
1438
+ return value.toISOString();
1439
+ }
1440
+ return String(value);
1441
+ }
1442
+
1443
+ toString() {
1444
+ return this._parts.join(' ');
1445
+ }
1446
+ }
1447
+
1448
+ /**
1449
+ * SELECT query builder
1450
+ */
1451
+ class SelectQuery {
1452
+ constructor(sdk, table) {
1453
+ this._sdk = sdk;
1454
+ this._table = table;
1455
+ this._selectCols = null;
1456
+ this._whereCond = null;
1457
+ this._byCols = null;
1458
+ this._computedCols = {};
1459
+ }
1460
+
1461
+ /**
1462
+ * Specify columns to select
1463
+ * @param {...string|Expr} cols
1464
+ * @returns {SelectQuery}
1465
+ */
1466
+ select(...cols) {
1467
+ const q = this._clone();
1468
+ q._selectCols = cols;
1469
+ return q;
1470
+ }
1471
+
1472
+ /**
1473
+ * Add computed column
1474
+ * @param {string} name
1475
+ * @param {Expr} expr
1476
+ * @returns {SelectQuery}
1477
+ */
1478
+ withColumn(name, expr) {
1479
+ const q = this._clone();
1480
+ q._computedCols[name] = expr;
1481
+ return q;
1482
+ }
1483
+
1484
+ /**
1485
+ * Add WHERE condition
1486
+ * @param {Expr} condition
1487
+ * @returns {SelectQuery}
1488
+ */
1489
+ where(condition) {
1490
+ const q = this._clone();
1491
+ q._whereCond = q._whereCond ? q._whereCond.and(condition) : condition;
1492
+ return q;
1493
+ }
1494
+
1495
+ /**
1496
+ * Add GROUP BY columns
1497
+ * @param {...string} cols
1498
+ * @returns {SelectQuery}
1499
+ */
1500
+ groupBy(...cols) {
1501
+ const q = this._clone();
1502
+ q._byCols = cols;
1503
+ return q;
1504
+ }
1505
+
1506
+ /**
1507
+ * Column reference helper
1508
+ * @param {string} name
1509
+ * @returns {Expr}
1510
+ */
1511
+ col(name) {
1512
+ return Expr.col(this._sdk, name);
1513
+ }
1514
+
1515
+ /**
1516
+ * Execute the query
1517
+ * @returns {Table}
1518
+ */
1519
+ execute() {
1520
+ // Build query dict
1521
+ const query = {};
1522
+
1523
+ // from clause
1524
+ query.from = this._table._ptr;
1525
+
1526
+ // select columns
1527
+ if (this._selectCols) {
1528
+ for (const col of this._selectCols) {
1529
+ if (typeof col === 'string') {
1530
+ query[col] = col;
1531
+ } else if (col instanceof Expr) {
1532
+ // Need alias for expressions
1533
+ }
1534
+ }
1535
+ }
1536
+
1537
+ // computed columns
1538
+ for (const [name, expr] of Object.entries(this._computedCols)) {
1539
+ query[name] = expr.toString();
1540
+ }
1541
+
1542
+ // where clause
1543
+ if (this._whereCond) {
1544
+ query.where = this._whereCond.toString();
1545
+ }
1546
+
1547
+ // group by
1548
+ if (this._byCols) {
1549
+ query.by = {};
1550
+ for (const col of this._byCols) {
1551
+ query.by[col] = col;
1552
+ }
1553
+ }
1554
+
1555
+ // Build and execute query
1556
+ const queryDict = this._sdk.dict(query);
1557
+ return this._sdk._wrapPtr(this._sdk._querySelect(queryDict._ptr));
1558
+ }
1559
+
1560
+ _clone() {
1561
+ const q = new SelectQuery(this._sdk, this._table);
1562
+ q._selectCols = this._selectCols;
1563
+ q._whereCond = this._whereCond;
1564
+ q._byCols = this._byCols;
1565
+ q._computedCols = { ...this._computedCols };
1566
+ return q;
1567
+ }
1568
+ }
1569
+
1570
+ // ============================================================================
1571
+ // Exports
1572
+ // ============================================================================
1573
+
1574
+ export {
1575
+ RayforceSDK,
1576
+ RayObject,
1577
+ RayNull,
1578
+ RayError,
1579
+ B8, U8, C8, I16, I32, I64, F64,
1580
+ RayDate, RayTime, RayTimestamp,
1581
+ Symbol, GUID,
1582
+ Vector, RayString, List, Dict, Table, Lambda,
1583
+ Expr, SelectQuery,
1584
+ };
1585
+
1586
+ // Default export for UMD/CDN usage
1587
+ export default {
1588
+ createRayforceSDK,
1589
+ Types,
1590
+ Expr,
1591
+ };