lakeql 0.1.2 → 0.1.8

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,1152 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __commonJS = (cb, mod) => function __require() {
8
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
19
+ // If the importer is in node compatibility mode or this is not an ESM
20
+ // file that has been converted to a CommonJS file using a Babel-
21
+ // compatible transform (i.e. "__esModule" has not been set), then set
22
+ // "default" to the CommonJS "module.exports" for node compatibility.
23
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
24
+ mod
25
+ ));
26
+
27
+ // ../core/dist/errors.js
28
+ var ERROR_CODES = [
29
+ "LAKEQL_PARSE_ERROR",
30
+ "LAKEQL_SQL_UNSUPPORTED",
31
+ "LAKEQL_TYPE_ERROR",
32
+ "LAKEQL_UNKNOWN_TABLE",
33
+ "LAKEQL_UNKNOWN_COLUMN",
34
+ "LAKEQL_UNSUPPORTED_PUSHDOWN",
35
+ "LAKEQL_BUDGET_EXCEEDED",
36
+ "LAKEQL_GROUP_LIMIT_EXCEEDED",
37
+ "LAKEQL_OBJECT_NOT_FOUND",
38
+ "LAKEQL_CATALOG_ERROR",
39
+ "LAKEQL_UNSUPPORTED_ICEBERG_FEATURE",
40
+ "LAKEQL_UNSUPPORTED_PARQUET_FEATURE",
41
+ "LAKEQL_ICEBERG_COMMIT_CONFLICT",
42
+ "LAKEQL_UNSUPPORTED_DELETE_FILES",
43
+ "LAKEQL_PARQUET_READ_ERROR",
44
+ "LAKEQL_PARQUET_WRITE_ERROR",
45
+ "LAKEQL_VALIDATION_ERROR",
46
+ "LAKEQL_BOOKMARK_STALE",
47
+ "LAKEQL_BOOKMARK_INVALID",
48
+ "LAKEQL_ABORTED",
49
+ "LAKEQL_GEO_BACKEND_MISSING"
50
+ ];
51
+ var LakeqlError = class extends Error {
52
+ code;
53
+ details;
54
+ constructor(code, message, details = {}) {
55
+ super(message);
56
+ this.name = "LakeqlError";
57
+ this.code = code;
58
+ this.details = details;
59
+ }
60
+ };
61
+ function isLakeqlError(value) {
62
+ return value instanceof LakeqlError;
63
+ }
64
+
65
+ // ../core/dist/timestamp.js
66
+ var NANOS_PER_MILLI = 1000000n;
67
+ var NANOS_PER_MICRO = 1000n;
68
+ var TimestampValue = class {
69
+ epochNanoseconds;
70
+ unit;
71
+ isAdjustedToUTC;
72
+ constructor(epochNanoseconds2, unit, isAdjustedToUTC = true) {
73
+ this.epochNanoseconds = epochNanoseconds2;
74
+ this.unit = unit;
75
+ this.isAdjustedToUTC = isAdjustedToUTC;
76
+ }
77
+ toJSON() {
78
+ return timestampToIsoString(this);
79
+ }
80
+ toString() {
81
+ return timestampToIsoString(this);
82
+ }
83
+ };
84
+ function timestampFromEpoch(value, unit, isAdjustedToUTC = true) {
85
+ return new TimestampValue(epochNanoseconds(value, unit), unit, isAdjustedToUTC);
86
+ }
87
+ function timestampValueFromIso(value) {
88
+ const match = /^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})(?:\.(\d{1,9}))?Z$/u.exec(value);
89
+ if (!match)
90
+ return void 0;
91
+ const millis = Date.parse(`${match[1]}.000Z`);
92
+ if (!Number.isFinite(millis))
93
+ return void 0;
94
+ const fraction = (match[2] ?? "").padEnd(9, "0");
95
+ const fractionNanos = fraction.length === 0 ? 0n : BigInt(fraction);
96
+ return new TimestampValue(BigInt(millis) * NANOS_PER_MILLI + fractionNanos, unitForFraction(match[2] ?? ""));
97
+ }
98
+ function isTimestampValue(value) {
99
+ return typeof value === "object" && value !== null && "epochNanoseconds" in value && typeof value.epochNanoseconds === "bigint" && "unit" in value && (value.unit === "millis" || value.unit === "micros" || value.unit === "nanos") && "isAdjustedToUTC" in value && typeof value.isAdjustedToUTC === "boolean";
100
+ }
101
+ function timestampToIsoString(value) {
102
+ const wholeMillis = value.epochNanoseconds / NANOS_PER_MILLI;
103
+ const nanosWithinMillis = value.epochNanoseconds % NANOS_PER_MILLI;
104
+ const millisWithinSecond = wholeMillis % 1000n;
105
+ const secondMillis = wholeMillis - millisWithinSecond;
106
+ const base = new Date(Number(secondMillis)).toISOString().replace(/\.\d{3}Z$/u, "");
107
+ const nanosWithinSecond = millisWithinSecond * NANOS_PER_MILLI + nanosWithinMillis;
108
+ const fraction = fractionForUnit(nanosWithinSecond, value.unit);
109
+ return `${base}${fraction}Z`;
110
+ }
111
+ function timestampEpochForUnit(value, unit) {
112
+ switch (unit) {
113
+ case "millis":
114
+ return value.epochNanoseconds / NANOS_PER_MILLI;
115
+ case "micros":
116
+ return value.epochNanoseconds / NANOS_PER_MICRO;
117
+ case "nanos":
118
+ return value.epochNanoseconds;
119
+ }
120
+ }
121
+ function compareTimestampValues(left, right) {
122
+ if (left.epochNanoseconds < right.epochNanoseconds)
123
+ return -1;
124
+ if (left.epochNanoseconds > right.epochNanoseconds)
125
+ return 1;
126
+ return 0;
127
+ }
128
+ function epochNanoseconds(value, unit) {
129
+ switch (unit) {
130
+ case "millis":
131
+ return value * NANOS_PER_MILLI;
132
+ case "micros":
133
+ return value * NANOS_PER_MICRO;
134
+ case "nanos":
135
+ return value;
136
+ }
137
+ }
138
+ function unitForFraction(fraction) {
139
+ if (fraction.length <= 3)
140
+ return "millis";
141
+ if (fraction.length <= 6)
142
+ return "micros";
143
+ return "nanos";
144
+ }
145
+ function fractionForUnit(nanos, unit) {
146
+ switch (unit) {
147
+ case "millis": {
148
+ const millis = nanos / NANOS_PER_MILLI;
149
+ return `.${millis.toString().padStart(3, "0")}`;
150
+ }
151
+ case "micros": {
152
+ const micros = nanos / NANOS_PER_MICRO;
153
+ return `.${micros.toString().padStart(6, "0")}`;
154
+ }
155
+ case "nanos":
156
+ return `.${nanos.toString().padStart(9, "0")}`;
157
+ }
158
+ }
159
+
160
+ // ../core/dist/regex-functions.js
161
+ function regexpMatchesValue(value, pattern, options = "") {
162
+ const regexOptions = parseRegexOptions("regexp_matches", options, { allowGlobal: false });
163
+ return compileRegex("regexp_matches", pattern, regexOptions).test(value);
164
+ }
165
+ function regexpReplaceValue(value, pattern, replacement, options = "") {
166
+ const regexOptions = parseRegexOptions("regexp_replace", options, { allowGlobal: true });
167
+ const regex = compileRegex("regexp_replace", pattern, regexOptions);
168
+ return value.replace(regex, duckdbReplacementToJs(replacement));
169
+ }
170
+ function compileRegex(name, pattern, options) {
171
+ const source = options.literal ? escapeRegexPattern(pattern) : pattern;
172
+ try {
173
+ return new RegExp(source, options.flags);
174
+ } catch (cause) {
175
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", `${name}() received an invalid regular expression`, {
176
+ pattern,
177
+ cause
178
+ });
179
+ }
180
+ }
181
+ function parseRegexOptions(name, options, input) {
182
+ let caseInsensitive = false;
183
+ let multiline = false;
184
+ let dotAll = false;
185
+ let literal = false;
186
+ let global = false;
187
+ for (const option of options) {
188
+ switch (option) {
189
+ case "c":
190
+ caseInsensitive = false;
191
+ break;
192
+ case "i":
193
+ caseInsensitive = true;
194
+ break;
195
+ case "g":
196
+ if (!input.allowGlobal) {
197
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", "g regex option is only valid for regexp_replace()");
198
+ }
199
+ global = true;
200
+ break;
201
+ case "l":
202
+ literal = true;
203
+ break;
204
+ case "m":
205
+ case "n":
206
+ case "p":
207
+ multiline = true;
208
+ break;
209
+ case "s":
210
+ dotAll = true;
211
+ break;
212
+ default:
213
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", `${name}() received an unsupported regex option`, {
214
+ option
215
+ });
216
+ }
217
+ }
218
+ return {
219
+ global,
220
+ literal,
221
+ flags: `${caseInsensitive ? "i" : ""}${multiline ? "m" : ""}${dotAll ? "s" : ""}${global ? "g" : ""}`
222
+ };
223
+ }
224
+ function duckdbReplacementToJs(replacement) {
225
+ return replacement.replace(/\\([1-9])/g, "$$$1");
226
+ }
227
+ function escapeRegexPattern(pattern) {
228
+ return pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
229
+ }
230
+
231
+ // ../core/dist/evaluator.js
232
+ var GEO_BACKEND_FUNCTIONS = /* @__PURE__ */ new Set([
233
+ "st_intersects",
234
+ "st_disjoint",
235
+ "st_contains",
236
+ "st_within",
237
+ "h3_within",
238
+ "h3_cell",
239
+ "h3_parent"
240
+ ]);
241
+ var geoBackend = null;
242
+ function setGeoBackend(backend) {
243
+ geoBackend = backend;
244
+ }
245
+ function requireGeoBackend() {
246
+ if (geoBackend === null) {
247
+ throw new LakeqlError("LAKEQL_GEO_BACKEND_MISSING", "Spatial function requires the geo backend. Query execution loads it automatically; when calling evaluate() directly, await loadGeoBackend() first.");
248
+ }
249
+ return geoBackend;
250
+ }
251
+ async function loadGeoBackend() {
252
+ if (geoBackend !== null)
253
+ return;
254
+ const module = await import('./geo-backend-TSQJWAAB.js');
255
+ module.installGeoBackend();
256
+ }
257
+ async function ensureGeoBackendForExprs(exprs) {
258
+ if (geoBackend !== null)
259
+ return;
260
+ for (const expr of exprs) {
261
+ if (exprNeedsGeoBackend(expr)) {
262
+ await loadGeoBackend();
263
+ return;
264
+ }
265
+ }
266
+ }
267
+ function exprNeedsGeoBackend(expr) {
268
+ if (!expr)
269
+ return false;
270
+ switch (expr.kind) {
271
+ case "literal":
272
+ case "column":
273
+ return false;
274
+ case "compare":
275
+ case "arithmetic":
276
+ return exprNeedsGeoBackend(expr.left) || exprNeedsGeoBackend(expr.right);
277
+ case "in":
278
+ return exprNeedsGeoBackend(expr.target) || expr.values.some(exprNeedsGeoBackend);
279
+ case "between":
280
+ return exprNeedsGeoBackend(expr.target) || exprNeedsGeoBackend(expr.low) || exprNeedsGeoBackend(expr.high);
281
+ case "null-check":
282
+ case "like":
283
+ return exprNeedsGeoBackend(expr.target);
284
+ case "logical":
285
+ return expr.operands.some(exprNeedsGeoBackend);
286
+ case "not":
287
+ return exprNeedsGeoBackend(expr.operand);
288
+ case "call":
289
+ return GEO_BACKEND_FUNCTIONS.has(expr.fn.toLowerCase()) || expr.args.some(exprNeedsGeoBackend);
290
+ case "case":
291
+ return expr.whens.some((branch) => exprNeedsGeoBackend(branch.when) || exprNeedsGeoBackend(branch.value)) || exprNeedsGeoBackend(expr.else);
292
+ }
293
+ }
294
+ var textEncoder = new TextEncoder();
295
+ function evaluate(expr, row) {
296
+ switch (expr.kind) {
297
+ case "literal":
298
+ return expr.value;
299
+ case "column":
300
+ return rowValue(row, expr.name);
301
+ case "compare":
302
+ return compare(expr.op, evaluate(expr.left, row), evaluate(expr.right, row));
303
+ case "in": {
304
+ const target = evaluate(expr.target, row);
305
+ const result = inList(target, expr.values.map((value) => evaluate(value, row)));
306
+ return expr.negated ? sqlNot(result) : result;
307
+ }
308
+ case "between": {
309
+ const target = evaluate(expr.target, row);
310
+ const low = evaluate(expr.low, row);
311
+ const high = evaluate(expr.high, row);
312
+ return sqlAnd(compare("gte", target, low), compare("lte", target, high));
313
+ }
314
+ case "null-check": {
315
+ const result = evaluate(expr.target, row) === null;
316
+ return expr.negated ? !result : result;
317
+ }
318
+ case "logical":
319
+ return expr.op === "and" ? expr.operands.reduce((acc, operand) => sqlAnd(acc, toSqlBoolean(evaluate(operand, row))), true) : expr.operands.reduce((acc, operand) => sqlOr(acc, toSqlBoolean(evaluate(operand, row))), false);
320
+ case "not":
321
+ return sqlNot(toSqlBoolean(evaluate(expr.operand, row)));
322
+ case "like":
323
+ return likeMatch(evaluate(expr.target, row), expr.pattern, expr.caseInsensitive);
324
+ case "call":
325
+ return callFunction(expr.fn, expr.args.map((arg) => evaluate(arg, row)));
326
+ case "arithmetic":
327
+ return arithmetic(expr.op, evaluate(expr.left, row), evaluate(expr.right, row));
328
+ case "case":
329
+ for (const branch of expr.whens) {
330
+ if (toSqlBoolean(evaluate(branch.when, row)) === true)
331
+ return evaluate(branch.value, row);
332
+ }
333
+ return expr.else === void 0 ? null : evaluate(expr.else, row);
334
+ }
335
+ }
336
+ function matches(expr, row) {
337
+ if (!expr)
338
+ return true;
339
+ return toSqlBoolean(evaluate(expr, row)) === true;
340
+ }
341
+ function jsonSafeValue(value) {
342
+ if (isTimestampValue(value))
343
+ return value.toJSON();
344
+ if (typeof value === "bigint") {
345
+ const asNumber = Number(value);
346
+ return Number.isSafeInteger(asNumber) ? asNumber : value.toString();
347
+ }
348
+ if (Array.isArray(value))
349
+ return value.map(jsonSafeValue);
350
+ if (value && typeof value === "object") {
351
+ const out = {};
352
+ for (const [key, inner] of Object.entries(value))
353
+ out[key] = jsonSafeValue(inner);
354
+ return out;
355
+ }
356
+ return value;
357
+ }
358
+ function encodeJsonLine(row) {
359
+ return textEncoder.encode(`${JSON.stringify(jsonSafeValue(row))}
360
+ `);
361
+ }
362
+ function rowValue(row, name) {
363
+ if (!(name in row)) {
364
+ throw new LakeqlError("LAKEQL_UNKNOWN_COLUMN", `Unknown column ${name}`, { column: name });
365
+ }
366
+ const value = row[name];
367
+ if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean" || typeof value === "bigint" || isTimestampValue(value)) {
368
+ return value;
369
+ }
370
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", `Column ${name} is not a scalar value`, {
371
+ column: name,
372
+ valueType: typeof value
373
+ });
374
+ }
375
+ function toSqlBoolean(value) {
376
+ if (value === null)
377
+ return null;
378
+ if (typeof value === "boolean")
379
+ return value;
380
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", "Predicate expression must evaluate to boolean", {
381
+ value
382
+ });
383
+ }
384
+ function compare(op, left, right) {
385
+ if (left === null || right === null)
386
+ return null;
387
+ if (isTimestampValue(left) || isTimestampValue(right)) {
388
+ const leftTimestamp = timestampEvalValue(left);
389
+ const rightTimestamp = timestampEvalValue(right);
390
+ if (leftTimestamp === void 0 || rightTimestamp === void 0) {
391
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", "Cannot compare timestamp with non-timestamp", {
392
+ leftType: evalValueType(left),
393
+ rightType: evalValueType(right)
394
+ });
395
+ }
396
+ return compareOrder(op, compareTimestampValues(leftTimestamp, rightTimestamp));
397
+ }
398
+ if (typeof left !== typeof right && !(isNumberLike(left) && isNumberLike(right))) {
399
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", "Cannot compare values of different types", {
400
+ leftType: typeof left,
401
+ rightType: typeof right
402
+ });
403
+ }
404
+ const order = left < right ? -1 : left > right ? 1 : 0;
405
+ return compareOrder(op, order);
406
+ }
407
+ function compareOrder(op, order) {
408
+ switch (op) {
409
+ case "eq":
410
+ return order === 0;
411
+ case "ne":
412
+ return order !== 0;
413
+ case "lt":
414
+ return order < 0;
415
+ case "lte":
416
+ return order <= 0;
417
+ case "gt":
418
+ return order > 0;
419
+ case "gte":
420
+ return order >= 0;
421
+ }
422
+ }
423
+ function timestampEvalValue(value) {
424
+ if (isTimestampValue(value))
425
+ return value;
426
+ if (typeof value === "string")
427
+ return timestampValueFromIso(value);
428
+ return void 0;
429
+ }
430
+ function evalValueType(value) {
431
+ return isTimestampValue(value) ? "timestamp" : typeof value;
432
+ }
433
+ function arithmetic(op, left, right) {
434
+ if (left === null || right === null)
435
+ return null;
436
+ if (typeof left !== "number" || typeof right !== "number") {
437
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", "Arithmetic expressions require numeric values", {
438
+ leftType: typeof left,
439
+ rightType: typeof right
440
+ });
441
+ }
442
+ switch (op) {
443
+ case "add":
444
+ return left + right;
445
+ case "sub":
446
+ return left - right;
447
+ case "mul":
448
+ return left * right;
449
+ case "div":
450
+ return left / right;
451
+ case "mod":
452
+ return left % right;
453
+ }
454
+ }
455
+ function isNumberLike(value) {
456
+ return typeof value === "number" || typeof value === "bigint";
457
+ }
458
+ function inList(target, values) {
459
+ if (target === null)
460
+ return null;
461
+ let sawNull = false;
462
+ for (const value of values) {
463
+ const result = compare("eq", target, value);
464
+ if (result === true)
465
+ return true;
466
+ if (result === null)
467
+ sawNull = true;
468
+ }
469
+ return sawNull ? null : false;
470
+ }
471
+ function sqlAnd(left, right) {
472
+ if (left === false || right === false)
473
+ return false;
474
+ if (left === null || right === null)
475
+ return null;
476
+ return true;
477
+ }
478
+ function sqlOr(left, right) {
479
+ if (left === true || right === true)
480
+ return true;
481
+ if (left === null || right === null)
482
+ return null;
483
+ return false;
484
+ }
485
+ function sqlNot(value) {
486
+ if (value === null)
487
+ return null;
488
+ return !value;
489
+ }
490
+ function likeMatch(value, pattern, caseInsensitive) {
491
+ if (value === null)
492
+ return null;
493
+ if (typeof value !== "string") {
494
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", "LIKE expects a string value", {
495
+ valueType: typeof value
496
+ });
497
+ }
498
+ const regex = new RegExp(`^${escapeLikePattern(pattern)}$`, caseInsensitive ? "iu" : "u");
499
+ return regex.test(value);
500
+ }
501
+ function escapeLikePattern(pattern) {
502
+ let out = "";
503
+ for (const char of pattern) {
504
+ if (char === "%")
505
+ out += ".*";
506
+ else if (char === "_")
507
+ out += ".";
508
+ else
509
+ out += char.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
510
+ }
511
+ return out;
512
+ }
513
+ function callFunction(name, args) {
514
+ const fn = name.toLowerCase();
515
+ switch (fn) {
516
+ case "lower":
517
+ return unaryString(fn, args, (value) => value.toLowerCase());
518
+ case "upper":
519
+ return unaryString(fn, args, (value) => value.toUpperCase());
520
+ case "trim":
521
+ return unaryString(fn, args, (value) => value.trim());
522
+ case "substr":
523
+ return substr(args);
524
+ case "replace":
525
+ return replace(args);
526
+ case "regexp_matches":
527
+ return regexpMatches(args);
528
+ case "regexp_replace":
529
+ return regexpReplace(args);
530
+ case "coalesce":
531
+ return args.find((value) => value !== null) ?? null;
532
+ case "nullif":
533
+ requireArgCount(fn, args, 2);
534
+ return compare("eq", args[0] ?? null, args[1] ?? null) === true ? null : args[0] ?? null;
535
+ case "cast":
536
+ return cast(args);
537
+ case "year":
538
+ return datePart(fn, args, (date) => date.getUTCFullYear());
539
+ case "month":
540
+ return datePart(fn, args, (date) => date.getUTCMonth() + 1);
541
+ case "day":
542
+ return datePart(fn, args, (date) => date.getUTCDate());
543
+ case "hour":
544
+ return datePart(fn, args, (date) => date.getUTCHours());
545
+ case "date_trunc":
546
+ return dateTrunc(args);
547
+ case "round":
548
+ return round(args);
549
+ case "floor":
550
+ return unaryNumber(fn, args, Math.floor);
551
+ case "ceil":
552
+ return unaryNumber(fn, args, Math.ceil);
553
+ case "abs":
554
+ return unaryNumber(fn, args, Math.abs);
555
+ case "least":
556
+ return leastGreatest(fn, args, "least");
557
+ case "greatest":
558
+ return leastGreatest(fn, args, "greatest");
559
+ case "st_point":
560
+ return stPoint(args);
561
+ case "st_x":
562
+ return pointCoordinate(fn, args, 0);
563
+ case "st_y":
564
+ return pointCoordinate(fn, args, 1);
565
+ case "st_bbox":
566
+ return stBBox(args);
567
+ case "st_intersects":
568
+ return spatialPredicate(fn, args, "intersects");
569
+ case "st_contains":
570
+ return spatialPredicate(fn, args, "contains");
571
+ case "st_within":
572
+ return spatialPredicate(fn, args, "within");
573
+ case "st_disjoint":
574
+ return spatialPredicate(fn, args, "disjoint");
575
+ case "st_distance":
576
+ return spatialMeasurement(fn, args, bboxDistance);
577
+ case "st_area":
578
+ return geometryMeasurement(fn, args, geometryArea);
579
+ case "st_length":
580
+ return geometryMeasurement(fn, args, geometryLength);
581
+ case "st_centroid":
582
+ return geometryTransform(fn, args, geometryCentroid);
583
+ case "st_envelope":
584
+ return geometryTransform(fn, args, (geometry) => JSON.stringify(envelopeFromGeometry(geometry, fn)));
585
+ case "h3_in":
586
+ return h3In(args);
587
+ case "h3_within":
588
+ return h3Within(args);
589
+ case "h3_cell":
590
+ return h3Cell(args);
591
+ case "h3_parent":
592
+ return h3Parent(args);
593
+ default:
594
+ throw new LakeqlError("LAKEQL_UNSUPPORTED_PUSHDOWN", `Unsupported scalar function ${name}`, {
595
+ function: name
596
+ });
597
+ }
598
+ }
599
+ function requireArgCount(name, args, expected) {
600
+ if (args.length !== expected) {
601
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", `${name}() expects ${expected} arguments`, {
602
+ expected,
603
+ received: args.length
604
+ });
605
+ }
606
+ }
607
+ function unaryString(name, args, cb) {
608
+ requireArgCount(name, args, 1);
609
+ const value = args[0] ?? null;
610
+ if (value === null)
611
+ return null;
612
+ if (typeof value !== "string")
613
+ throwType(name, "string", value);
614
+ return cb(value);
615
+ }
616
+ function unaryNumber(name, args, cb) {
617
+ requireArgCount(name, args, 1);
618
+ const value = args[0] ?? null;
619
+ if (value === null)
620
+ return null;
621
+ if (typeof value !== "number")
622
+ throwType(name, "number", value);
623
+ return cb(value);
624
+ }
625
+ function substr(args) {
626
+ requireArgCount("substr", args, 3);
627
+ const value = args[0] ?? null;
628
+ const start = args[1] ?? null;
629
+ const length = args[2] ?? null;
630
+ if (value === null || start === null || length === null)
631
+ return null;
632
+ if (typeof value !== "string")
633
+ throwType("substr", "string", value);
634
+ if (typeof start !== "number" || typeof length !== "number") {
635
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", "substr() start and length must be numbers");
636
+ }
637
+ return value.slice(start, start + length);
638
+ }
639
+ function replace(args) {
640
+ requireArgCount("replace", args, 3);
641
+ const value = args[0] ?? null;
642
+ const search = args[1] ?? null;
643
+ const replacement = args[2] ?? null;
644
+ if (value === null || search === null || replacement === null)
645
+ return null;
646
+ if (typeof value !== "string")
647
+ throwType("replace", "string", value);
648
+ if (typeof search !== "string" || typeof replacement !== "string") {
649
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", "replace() search and replacement must be strings");
650
+ }
651
+ return value.replaceAll(search, replacement);
652
+ }
653
+ function regexpMatches(args) {
654
+ if (args.length < 2 || args.length > 3) {
655
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", "regexp_matches() expects 2 or 3 arguments", {
656
+ received: args.length
657
+ });
658
+ }
659
+ const value = args[0] ?? null;
660
+ const pattern = args[1] ?? null;
661
+ const options = args[2] ?? "";
662
+ if (value === null || pattern === null || options === null)
663
+ return null;
664
+ if (typeof value !== "string" || typeof pattern !== "string" || typeof options !== "string") {
665
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", "regexp_matches() value, pattern, and options must be strings");
666
+ }
667
+ return regexpMatchesValue(value, pattern, options);
668
+ }
669
+ function regexpReplace(args) {
670
+ if (args.length < 3 || args.length > 4) {
671
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", "regexp_replace() expects 3 or 4 arguments", {
672
+ received: args.length
673
+ });
674
+ }
675
+ const value = args[0] ?? null;
676
+ const pattern = args[1] ?? null;
677
+ const replacement = args[2] ?? null;
678
+ const options = args[3] ?? "";
679
+ if (value === null || pattern === null || replacement === null || options === null)
680
+ return null;
681
+ if (typeof value !== "string" || typeof pattern !== "string" || typeof replacement !== "string" || typeof options !== "string") {
682
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", "regexp_replace() value, pattern, replacement, and options must be strings");
683
+ }
684
+ return regexpReplaceValue(value, pattern, replacement, options);
685
+ }
686
+ function stPoint(args) {
687
+ requireArgCount("st_point", args, 2);
688
+ const [lon, lat] = args;
689
+ if (!finiteNumber(lon) || !finiteNumber(lat)) {
690
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", "st_point() expects finite lon/lat numbers");
691
+ }
692
+ return JSON.stringify({ type: "Point", coordinates: [lon, lat] });
693
+ }
694
+ function pointCoordinate(name, args, index) {
695
+ requireArgCount(name, args, 1);
696
+ const value = args[0] ?? null;
697
+ if (value === null)
698
+ return null;
699
+ return pointFromGeometry(parseGeometry(value, name), name)[index];
700
+ }
701
+ function stBBox(args) {
702
+ requireArgCount("st_bbox", args, 4);
703
+ const [minx, miny, maxx, maxy] = args;
704
+ if (!finiteNumber(minx) || !finiteNumber(miny) || !finiteNumber(maxx) || !finiteNumber(maxy)) {
705
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", "st_bbox() expects finite number bounds");
706
+ }
707
+ if (minx > maxx || miny > maxy) {
708
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", "st_bbox() bounds must be ordered min <= max");
709
+ }
710
+ return JSON.stringify({ type: "BBox", minx, miny, maxx, maxy });
711
+ }
712
+ function spatialPredicate(name, args, op) {
713
+ requireArgCount(name, args, 2);
714
+ const left = args[0] ?? null;
715
+ const right = args[1] ?? null;
716
+ if (left === null || right === null)
717
+ return null;
718
+ const a = toGeometry(parseGeometry(left, name), name);
719
+ const b = toGeometry(parseGeometry(right, name), name);
720
+ const ea = envelopeOf(a);
721
+ const eb = envelopeOf(b);
722
+ const geo = requireGeoBackend();
723
+ switch (op) {
724
+ case "intersects":
725
+ return bboxIntersects(ea, eb) && geo.booleanIntersects(a, b);
726
+ case "disjoint":
727
+ return !bboxIntersects(ea, eb) || !geo.booleanIntersects(a, b);
728
+ case "contains":
729
+ return bboxContains(ea, eb) && geo.booleanContains(a, b);
730
+ case "within":
731
+ return bboxContains(eb, ea) && geo.booleanContains(b, a);
732
+ }
733
+ }
734
+ function envelopeOf(geometry) {
735
+ switch (geometry.type) {
736
+ case "Point":
737
+ return pointsEnvelope([geometry.coordinates]);
738
+ case "LineString":
739
+ return pointsEnvelope(geometry.coordinates);
740
+ case "Polygon":
741
+ return pointsEnvelope(geometry.coordinates.flat());
742
+ }
743
+ }
744
+ function toGeometry(parsed, name) {
745
+ switch (parsed.type) {
746
+ case "Point":
747
+ return { type: "Point", coordinates: pointFromGeometry(parsed, name) };
748
+ case "LineString":
749
+ return { type: "LineString", coordinates: lineStringPoints(parsed, name) };
750
+ case "Polygon":
751
+ return { type: "Polygon", coordinates: polygonRings(parsed, name) };
752
+ case "BBox":
753
+ return { type: "Polygon", coordinates: [bboxRing(bboxFromRecord(parsed, name))] };
754
+ default:
755
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", `${name}() supports Point, LineString, Polygon, or BBox geometry`);
756
+ }
757
+ }
758
+ function bboxRing(box) {
759
+ return [
760
+ [box.minx, box.miny],
761
+ [box.maxx, box.miny],
762
+ [box.maxx, box.maxy],
763
+ [box.minx, box.maxy],
764
+ [box.minx, box.miny]
765
+ ];
766
+ }
767
+ function polygonRings(record, name) {
768
+ const rings = record.coordinates;
769
+ if (!Array.isArray(rings) || rings.length === 0) {
770
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", `${name}() Polygon coordinates are invalid`);
771
+ }
772
+ return rings.map((ring) => {
773
+ if (!Array.isArray(ring) || ring.length === 0 || !ring.every(isPosition)) {
774
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", `${name}() Polygon coordinates are invalid`);
775
+ }
776
+ return closeRing(ring);
777
+ });
778
+ }
779
+ function closeRing(ring) {
780
+ const first = ring[0];
781
+ const last = ring[ring.length - 1];
782
+ if (first && last && (first[0] !== last[0] || first[1] !== last[1])) {
783
+ return [...ring, first];
784
+ }
785
+ return ring;
786
+ }
787
+ function spatialMeasurement(name, args, measure) {
788
+ requireArgCount(name, args, 2);
789
+ const left = args[0] ?? null;
790
+ const right = args[1] ?? null;
791
+ if (left === null || right === null)
792
+ return null;
793
+ return measure(envelope(left, name), envelope(right, name));
794
+ }
795
+ function geometryMeasurement(name, args, measure) {
796
+ requireArgCount(name, args, 1);
797
+ const value = args[0] ?? null;
798
+ if (value === null)
799
+ return null;
800
+ return measure(parseGeometry(value, name), name);
801
+ }
802
+ function geometryTransform(name, args, transform) {
803
+ requireArgCount(name, args, 1);
804
+ const value = args[0] ?? null;
805
+ if (value === null)
806
+ return null;
807
+ return transform(parseGeometry(value, name));
808
+ }
809
+ function h3In(args) {
810
+ requireArgCount("h3_in", args, 2);
811
+ const cell = args[0] ?? null;
812
+ const cells = args[1] ?? null;
813
+ if (cell === null || cells === null)
814
+ return null;
815
+ if (typeof cell !== "string" || typeof cells !== "string") {
816
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", "h3_in() expects a string cell and JSON cell list");
817
+ }
818
+ const parsed = JSON.parse(cells);
819
+ if (!Array.isArray(parsed) || !parsed.every((value) => typeof value === "string")) {
820
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", "h3_in() cell list must be a JSON string array");
821
+ }
822
+ return parsed.includes(cell);
823
+ }
824
+ function h3Within(args) {
825
+ requireArgCount("h3_within", args, 3);
826
+ const cell = args[0] ?? null;
827
+ const origin = args[1] ?? null;
828
+ const k = args[2] ?? null;
829
+ if (cell === null || origin === null || k === null)
830
+ return null;
831
+ if (typeof cell !== "string" || typeof origin !== "string") {
832
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", "h3_within() expects string cells");
833
+ }
834
+ validateH3Cell(cell, "cell");
835
+ validateH3Cell(origin, "origin");
836
+ if (typeof k !== "number" || !Number.isInteger(k) || k < 0) {
837
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", "h3_within() expects a non-negative integer radius");
838
+ }
839
+ return requireGeoBackend().gridDisk(origin, k).includes(cell);
840
+ }
841
+ function h3Cell(args) {
842
+ requireArgCount("h3_cell", args, 3);
843
+ const lat = args[0] ?? null;
844
+ const lon = args[1] ?? null;
845
+ const res = args[2] ?? null;
846
+ if (!finiteNumber(lat) || !finiteNumber(lon)) {
847
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", "h3_cell() expects finite lat/lon numbers");
848
+ }
849
+ if (typeof res !== "number" || !Number.isInteger(res) || res < 0 || res > 15) {
850
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", "h3_cell() expects an integer resolution 0..15");
851
+ }
852
+ return requireGeoBackend().latLngToCell(lat, lon, res);
853
+ }
854
+ function h3Parent(args) {
855
+ requireArgCount("h3_parent", args, 2);
856
+ const cell = args[0] ?? null;
857
+ const res = args[1] ?? null;
858
+ if (cell === null || res === null)
859
+ return null;
860
+ if (typeof cell !== "string")
861
+ throwType("h3_parent", "string", cell);
862
+ validateH3Cell(cell, "cell");
863
+ if (typeof res !== "number" || !Number.isInteger(res) || res < 0 || res > 15) {
864
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", "h3_parent() expects an integer resolution 0..15");
865
+ }
866
+ return requireGeoBackend().cellToParent(cell, res);
867
+ }
868
+ function validateH3Cell(cell, label) {
869
+ if (!requireGeoBackend().isValidCell(cell)) {
870
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", `h3 cell ${label} is invalid`, { cell });
871
+ }
872
+ }
873
+ function envelope(value, name) {
874
+ return envelopeFromGeometry(parseGeometry(value, name), name);
875
+ }
876
+ function parseGeometry(value, name) {
877
+ if (typeof value !== "string")
878
+ throwType(name, "GeoJSON or BBox JSON string", value);
879
+ const wkt = parseWktGeometry(value);
880
+ if (wkt !== void 0)
881
+ return wkt;
882
+ let parsed;
883
+ try {
884
+ parsed = JSON.parse(value);
885
+ } catch (cause) {
886
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", `${name}() received invalid geometry JSON`, {
887
+ cause
888
+ });
889
+ }
890
+ if (!isRecord(parsed) || typeof parsed.type !== "string") {
891
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", `${name}() expects GeoJSON or BBox JSON`);
892
+ }
893
+ return parsed;
894
+ }
895
+ function parseWktGeometry(value) {
896
+ const point = /^POINT\s*\(\s*([+-]?(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?)\s+([+-]?(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?)\s*\)$/iu.exec(value);
897
+ if (point !== null) {
898
+ const lon = Number(point[1]);
899
+ const lat = Number(point[2]);
900
+ if (Number.isFinite(lon) && Number.isFinite(lat)) {
901
+ return { type: "Point", coordinates: [lon, lat] };
902
+ }
903
+ }
904
+ return void 0;
905
+ }
906
+ function envelopeFromGeometry(parsed, name) {
907
+ if (parsed.type === "BBox")
908
+ return bboxFromRecord(parsed, name);
909
+ if (parsed.type === "Point")
910
+ return pointEnvelope(parsed, name);
911
+ if (parsed.type === "LineString")
912
+ return lineStringEnvelope(parsed, name);
913
+ if (parsed.type === "Polygon")
914
+ return polygonEnvelope(parsed, name);
915
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", `${name}() supports Point, LineString, Polygon, or BBox geometry`);
916
+ }
917
+ function bboxFromRecord(record, name) {
918
+ const { minx, miny, maxx, maxy } = record;
919
+ if (!finiteNumber(minx) || !finiteNumber(miny) || !finiteNumber(maxx) || !finiteNumber(maxy)) {
920
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", `${name}() BBox values must be finite numbers`);
921
+ }
922
+ return { minx, miny, maxx, maxy };
923
+ }
924
+ function pointEnvelope(record, name) {
925
+ const [x, y] = pointFromGeometry(record, name);
926
+ return { minx: x, miny: y, maxx: x, maxy: y };
927
+ }
928
+ function lineStringEnvelope(record, name) {
929
+ return pointsEnvelope(lineStringPoints(record, name));
930
+ }
931
+ function polygonEnvelope(record, name) {
932
+ const points = polygonPoints(record, name);
933
+ return pointsEnvelope(points);
934
+ }
935
+ function pointsEnvelope(points) {
936
+ let minx = Number.POSITIVE_INFINITY;
937
+ let miny = Number.POSITIVE_INFINITY;
938
+ let maxx = Number.NEGATIVE_INFINITY;
939
+ let maxy = Number.NEGATIVE_INFINITY;
940
+ for (const [x, y] of points) {
941
+ minx = Math.min(minx, x);
942
+ miny = Math.min(miny, y);
943
+ maxx = Math.max(maxx, x);
944
+ maxy = Math.max(maxy, y);
945
+ }
946
+ return { minx, miny, maxx, maxy };
947
+ }
948
+ function bboxIntersects(left, right) {
949
+ return left.maxx >= right.minx && left.minx <= right.maxx && left.maxy >= right.miny && left.miny <= right.maxy;
950
+ }
951
+ function bboxContains(left, right) {
952
+ return left.minx <= right.minx && left.miny <= right.miny && left.maxx >= right.maxx && left.maxy >= right.maxy;
953
+ }
954
+ function bboxDistance(left, right) {
955
+ const dx = left.maxx < right.minx ? right.minx - left.maxx : right.maxx < left.minx ? left.minx - right.maxx : 0;
956
+ const dy = left.maxy < right.miny ? right.miny - left.maxy : right.maxy < left.miny ? left.miny - right.maxy : 0;
957
+ return Math.hypot(dx, dy);
958
+ }
959
+ function geometryArea(geometry, name) {
960
+ if (geometry.type === "BBox") {
961
+ const box = bboxFromRecord(geometry, name);
962
+ return (box.maxx - box.minx) * (box.maxy - box.miny);
963
+ }
964
+ if (geometry.type === "Polygon")
965
+ return Math.abs(ringArea(polygonPoints(geometry, name)));
966
+ if (geometry.type === "Point" || geometry.type === "LineString")
967
+ return 0;
968
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", `${name}() unsupported geometry type`, {
969
+ type: geometry.type
970
+ });
971
+ }
972
+ function geometryLength(geometry, name) {
973
+ if (geometry.type === "BBox") {
974
+ const box = bboxFromRecord(geometry, name);
975
+ return 2 * (box.maxx - box.minx + (box.maxy - box.miny));
976
+ }
977
+ if (geometry.type === "LineString")
978
+ return pathLength(lineStringPoints(geometry, name));
979
+ if (geometry.type === "Polygon")
980
+ return pathLength(polygonPoints(geometry, name));
981
+ if (geometry.type === "Point")
982
+ return 0;
983
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", `${name}() unsupported geometry type`, {
984
+ type: geometry.type
985
+ });
986
+ }
987
+ function geometryCentroid(geometry) {
988
+ const box = envelopeFromGeometry(geometry, "st_centroid");
989
+ return JSON.stringify({
990
+ type: "Point",
991
+ coordinates: [(box.minx + box.maxx) / 2, (box.miny + box.maxy) / 2]
992
+ });
993
+ }
994
+ function pointFromGeometry(record, name) {
995
+ if (record.type !== "Point" || !isPosition(record.coordinates)) {
996
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", `${name}() Point coordinates are invalid`);
997
+ }
998
+ return record.coordinates;
999
+ }
1000
+ function lineStringPoints(record, name) {
1001
+ const points = record.coordinates;
1002
+ if (!Array.isArray(points) || points.length === 0 || !points.every(isPosition)) {
1003
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", `${name}() LineString coordinates are invalid`);
1004
+ }
1005
+ return points;
1006
+ }
1007
+ function polygonPoints(record, name) {
1008
+ const rings = record.coordinates;
1009
+ if (!Array.isArray(rings)) {
1010
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", `${name}() Polygon coordinates are invalid`);
1011
+ }
1012
+ const points = rings.flat();
1013
+ if (points.length === 0 || !points.every(isPosition)) {
1014
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", `${name}() Polygon coordinates are invalid`);
1015
+ }
1016
+ return points;
1017
+ }
1018
+ function ringArea(points) {
1019
+ let area = 0;
1020
+ for (let index = 0; index < points.length; index += 1) {
1021
+ const current = points[index];
1022
+ const next = points[(index + 1) % points.length];
1023
+ if (current === void 0 || next === void 0)
1024
+ continue;
1025
+ area += current[0] * next[1] - next[0] * current[1];
1026
+ }
1027
+ return area / 2;
1028
+ }
1029
+ function pathLength(points) {
1030
+ let length = 0;
1031
+ for (let index = 1; index < points.length; index += 1) {
1032
+ const previous = points[index - 1];
1033
+ const current = points[index];
1034
+ if (previous === void 0 || current === void 0)
1035
+ continue;
1036
+ length += Math.hypot(current[0] - previous[0], current[1] - previous[1]);
1037
+ }
1038
+ return length;
1039
+ }
1040
+ function isPosition(value) {
1041
+ return Array.isArray(value) && value.length === 2 && typeof value[0] === "number" && typeof value[1] === "number" && Number.isFinite(value[0]) && Number.isFinite(value[1]);
1042
+ }
1043
+ function finiteNumber(value) {
1044
+ return typeof value === "number" && Number.isFinite(value);
1045
+ }
1046
+ function isRecord(value) {
1047
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1048
+ }
1049
+ function cast(args) {
1050
+ requireArgCount("cast", args, 2);
1051
+ const value = args[0] ?? null;
1052
+ const target = args[1] ?? null;
1053
+ if (value === null)
1054
+ return null;
1055
+ if (typeof target !== "string")
1056
+ throwType("cast", "string type name", target ?? null);
1057
+ switch (target) {
1058
+ case "string":
1059
+ return String(value);
1060
+ case "float64":
1061
+ case "number": {
1062
+ const number = Number(value);
1063
+ return Number.isNaN(number) ? null : number;
1064
+ }
1065
+ case "boolean":
1066
+ return Boolean(value);
1067
+ default:
1068
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", `Unsupported cast target ${target}`, { target });
1069
+ }
1070
+ }
1071
+ function datePart(name, args, cb) {
1072
+ requireArgCount(name, args, 1);
1073
+ const date = parseDateArg(name, args[0] ?? null);
1074
+ return date ? cb(date) : null;
1075
+ }
1076
+ function dateTrunc(args) {
1077
+ requireArgCount("date_trunc", args, 2);
1078
+ const part = args[0] ?? null;
1079
+ const value = args[1] ?? null;
1080
+ if (part === null || value === null)
1081
+ return null;
1082
+ if (typeof part !== "string")
1083
+ throwType("date_trunc", "string part", part);
1084
+ const date = parseDateArg("date_trunc", value);
1085
+ if (!date)
1086
+ return null;
1087
+ if (part === "year")
1088
+ return `${date.getUTCFullYear()}-01-01T00:00:00.000Z`;
1089
+ if (part === "month") {
1090
+ return `${date.getUTCFullYear()}-${pad(date.getUTCMonth() + 1)}-01T00:00:00.000Z`;
1091
+ }
1092
+ if (part === "day") {
1093
+ return `${date.getUTCFullYear()}-${pad(date.getUTCMonth() + 1)}-${pad(date.getUTCDate())}T00:00:00.000Z`;
1094
+ }
1095
+ if (part === "hour") {
1096
+ return `${date.getUTCFullYear()}-${pad(date.getUTCMonth() + 1)}-${pad(date.getUTCDate())}T${pad(date.getUTCHours())}:00:00.000Z`;
1097
+ }
1098
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", `Unsupported date_trunc part ${part}`, { part });
1099
+ }
1100
+ function round(args) {
1101
+ if (args.length < 1 || args.length > 2) {
1102
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", "round() expects 1 or 2 arguments", {
1103
+ received: args.length
1104
+ });
1105
+ }
1106
+ const value = args[0] ?? null;
1107
+ const places = args[1] ?? 0;
1108
+ if (value === null || places === null)
1109
+ return null;
1110
+ if (typeof value !== "number" || typeof places !== "number") {
1111
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", "round() arguments must be numbers");
1112
+ }
1113
+ const scale = 10 ** places;
1114
+ return Math.round(value * scale) / scale;
1115
+ }
1116
+ function leastGreatest(name, args, mode) {
1117
+ if (args.length === 0) {
1118
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", `${name}() expects at least 1 argument`);
1119
+ }
1120
+ if (args.some((value) => value === null))
1121
+ return null;
1122
+ let best = args[0] ?? null;
1123
+ for (const value of args.slice(1)) {
1124
+ if (compare(mode === "least" ? "lt" : "gt", value, best) === true)
1125
+ best = value;
1126
+ }
1127
+ return best;
1128
+ }
1129
+ function parseDateArg(name, value) {
1130
+ if (value === null)
1131
+ return null;
1132
+ if (isTimestampValue(value))
1133
+ return new Date(Number(value.epochNanoseconds / 1000000n));
1134
+ if (typeof value !== "string" && typeof value !== "number")
1135
+ throwType(name, "date string", value);
1136
+ const date = new Date(value);
1137
+ if (Number.isNaN(date.valueOf())) {
1138
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", `${name}() received an invalid date`, { value });
1139
+ }
1140
+ return date;
1141
+ }
1142
+ function throwType(name, expected, value) {
1143
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", `${name}() expects ${expected}`, {
1144
+ expected,
1145
+ received: typeof value
1146
+ });
1147
+ }
1148
+ function pad(value) {
1149
+ return String(value).padStart(2, "0");
1150
+ }
1151
+
1152
+ export { ERROR_CODES, LakeqlError, TimestampValue, __commonJS, __toESM, bboxIntersects, compareTimestampValues, encodeJsonLine, ensureGeoBackendForExprs, envelopeFromGeometry, envelopeOf, evaluate, isLakeqlError, isTimestampValue, jsonSafeValue, loadGeoBackend, matches, parseGeometry, regexpMatchesValue, regexpReplaceValue, requireGeoBackend, setGeoBackend, timestampEpochForUnit, timestampFromEpoch, timestampToIsoString, timestampValueFromIso, toGeometry };