lakeql 0.1.8 → 0.1.9

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.
@@ -1,5 +1,6 @@
1
- import { Lake, stableStringify, vectorValue, scalarVectorValue, batchFromColumns, withObjectStoreReadControls, throwIfAborted, readControlSignal, readParquetObjectBatches, readIcebergParquetDeletes } from './chunk-MXGEAVHL.js';
2
- import { LakeqlError, jsonSafeValue, matches } from './chunk-TFD5RFKB.js';
1
+ import { Lake, vectorValue, scalarVectorValue, batchFromColumns, withObjectStoreReadControls, throwIfAborted, readControlSignal, readParquetObjectBatches, readIcebergParquetDeletes } from './chunk-6XXXYVXT.js';
2
+ import { stableStringify } from './chunk-ZVHJD6R3.js';
3
+ import { LakeqlError, jsonSafeValue, matches } from './chunk-4VJQ56HF.js';
3
4
 
4
5
  // ../core/dist/in-memory.js
5
6
  var InMemoryRowsScanner = class {
@@ -157,6 +157,147 @@ function fractionForUnit(nanos, unit) {
157
157
  }
158
158
  }
159
159
 
160
+ // ../core/dist/interval.js
161
+ var NANOS_PER_SECOND = 1000000000n;
162
+ var NANOS_PER_MILLI2 = 1000000n;
163
+ var NANOS_PER_MICRO2 = 1000n;
164
+ function intervalValue(input) {
165
+ const trimmed = input.trim();
166
+ if (trimmed.length === 0)
167
+ throwInterval(input);
168
+ let rest = trimmed;
169
+ let months = 0;
170
+ let days = 0;
171
+ let nanoseconds = 0n;
172
+ let matchedAny = false;
173
+ const leadingTime = /^([+-]?\d{1,2}:\d{2}(?::\d{2}(?:\.\d{1,9})?)?)\b/u.exec(rest);
174
+ if (leadingTime?.[1] !== void 0) {
175
+ nanoseconds += parseIntervalTime(leadingTime[1], input);
176
+ rest = rest.slice(leadingTime[0].length).trim();
177
+ matchedAny = true;
178
+ }
179
+ const partPattern = /([+-]?(?:\d+(?:\.\d+)?|\.\d+))\s*(years?|mons?|months?|days?|hours?|hrs?|minutes?|mins?|seconds?|secs?|milliseconds?|millis?|microseconds?|micros?|nanoseconds?|nanos?)\b/giu;
180
+ let cursor = 0;
181
+ for (const match of rest.matchAll(partPattern)) {
182
+ if (match.index === void 0 || rest.slice(cursor, match.index).trim().length > 0) {
183
+ throwInterval(input);
184
+ }
185
+ const amount = Number(match[1]);
186
+ const unit = match[2]?.toLowerCase() ?? "";
187
+ if (!Number.isFinite(amount))
188
+ throwInterval(input);
189
+ if (unit.startsWith("year"))
190
+ months += integerAmount(amount, unit, input) * 12;
191
+ else if (unit === "mon" || unit === "mons" || unit.startsWith("month"))
192
+ months += integerAmount(amount, unit, input);
193
+ else if (unit.startsWith("day"))
194
+ days += integerAmount(amount, unit, input);
195
+ else if (unit.startsWith("hour") || unit === "hr" || unit === "hrs")
196
+ nanoseconds += decimalNanoseconds(match[1] ?? "", 60n * 60n * NANOS_PER_SECOND);
197
+ else if (unit.startsWith("minute") || unit === "min" || unit === "mins")
198
+ nanoseconds += decimalNanoseconds(match[1] ?? "", 60n * NANOS_PER_SECOND);
199
+ else if (unit.startsWith("second") || unit === "sec" || unit === "secs")
200
+ nanoseconds += decimalNanoseconds(match[1] ?? "", NANOS_PER_SECOND);
201
+ else if (unit.startsWith("millisecond") || unit === "milli" || unit === "millis")
202
+ nanoseconds += decimalNanoseconds(match[1] ?? "", NANOS_PER_MILLI2);
203
+ else if (unit.startsWith("microsecond") || unit === "micro" || unit === "micros")
204
+ nanoseconds += decimalNanoseconds(match[1] ?? "", NANOS_PER_MICRO2);
205
+ else if (unit.startsWith("nanosecond") || unit === "nano" || unit === "nanos")
206
+ nanoseconds += BigInt(integerAmount(amount, unit, input));
207
+ else
208
+ throwInterval(input);
209
+ matchedAny = true;
210
+ cursor = match.index + match[0].length;
211
+ const after = rest.slice(cursor).trimStart();
212
+ const time = /^(\d{1,2}:\d{2}(?::\d{2}(?:\.\d{1,9})?)?)\b/u.exec(after);
213
+ if (time?.[1] !== void 0) {
214
+ nanoseconds += parseIntervalTime(time[1], input);
215
+ cursor = rest.length - after.slice(time[0].length).length;
216
+ }
217
+ }
218
+ if (!matchedAny || rest.slice(cursor).trim().length > 0)
219
+ throwInterval(input);
220
+ return { kind: "interval", months, days, nanoseconds: nanoseconds.toString() };
221
+ }
222
+ function isIntervalValue(value) {
223
+ return typeof value === "object" && value !== null && "kind" in value && value.kind === "interval" && "months" in value && Number.isInteger(value.months) && "days" in value && Number.isInteger(value.days) && "nanoseconds" in value && typeof value.nanoseconds === "string" && /^-?[0-9]+$/u.test(value.nanoseconds);
224
+ }
225
+ function isNonNegativeInterval(value) {
226
+ return value.months >= 0 && value.days >= 0 && BigInt(value.nanoseconds) >= 0n;
227
+ }
228
+ function intervalToString(value) {
229
+ const parts = [];
230
+ if (value.months !== 0)
231
+ parts.push(`${value.months} months`);
232
+ if (value.days !== 0)
233
+ parts.push(`${value.days} days`);
234
+ const nanos = BigInt(value.nanoseconds);
235
+ if (nanos !== 0n || parts.length === 0)
236
+ parts.push(`${nanos} nanoseconds`);
237
+ return parts.join(" ");
238
+ }
239
+ function applyIntervalToTimestamp(value, interval, direction) {
240
+ const date = new Date(Number(value.epochNanoseconds / NANOS_PER_MILLI2));
241
+ if (interval.months !== 0)
242
+ shiftUtcMonthsClamped(date, direction * interval.months);
243
+ if (interval.days !== 0)
244
+ date.setUTCDate(date.getUTCDate() + direction * interval.days);
245
+ const nanosWithinMilli = value.epochNanoseconds % NANOS_PER_MILLI2;
246
+ const epochNanoseconds2 = BigInt(date.getTime()) * NANOS_PER_MILLI2 + nanosWithinMilli + BigInt(direction) * BigInt(interval.nanoseconds);
247
+ return new TimestampValue(epochNanoseconds2, value.unit, value.isAdjustedToUTC);
248
+ }
249
+ function integerAmount(value, unit, input) {
250
+ if (!Number.isInteger(value)) {
251
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", `Interval ${unit} component must be an integer`, {
252
+ interval: input,
253
+ unit
254
+ });
255
+ }
256
+ return value;
257
+ }
258
+ function decimalNanoseconds(value, unitNanos) {
259
+ const match = /^([+-]?)(?:(\d+)(?:\.(\d+))?|\.(\d+))$/u.exec(value);
260
+ if (match === null) {
261
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", "Interval component exceeds nanosecond precision");
262
+ }
263
+ const sign = match[1] === "-" ? -1n : 1n;
264
+ const whole = BigInt(match[2] ?? "0");
265
+ const fraction = match[3] ?? match[4] ?? "";
266
+ const scale = 10n ** BigInt(fraction.length);
267
+ const numerator = whole * scale + BigInt(fraction || "0");
268
+ const scaled = numerator * unitNanos;
269
+ if (scaled % scale !== 0n) {
270
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", "Interval component exceeds nanosecond precision");
271
+ }
272
+ return sign * (scaled / scale);
273
+ }
274
+ function shiftUtcMonthsClamped(date, months) {
275
+ const day = date.getUTCDate();
276
+ const targetMonthStart = new Date(date.getTime());
277
+ targetMonthStart.setUTCDate(1);
278
+ targetMonthStart.setUTCMonth(targetMonthStart.getUTCMonth() + months);
279
+ const daysInTargetMonth = new Date(Date.UTC(targetMonthStart.getUTCFullYear(), targetMonthStart.getUTCMonth() + 1, 0)).getUTCDate();
280
+ date.setUTCFullYear(targetMonthStart.getUTCFullYear(), targetMonthStart.getUTCMonth(), Math.min(day, daysInTargetMonth));
281
+ }
282
+ function parseIntervalTime(value, input) {
283
+ const match = /^([+-]?\d{1,2}):(\d{2})(?::(\d{2})(?:\.(\d{1,9}))?)?$/u.exec(value);
284
+ if (match === null || match[1] === void 0 || match[2] === void 0)
285
+ throwInterval(input);
286
+ const sign = match[1].startsWith("-") ? -1n : 1n;
287
+ const hours = BigInt(Math.abs(Number(match[1])));
288
+ const minutes = BigInt(Number(match[2]));
289
+ const seconds = BigInt(Number(match[3] ?? "0"));
290
+ if (minutes >= 60n || seconds >= 60n)
291
+ throwInterval(input);
292
+ const fraction = BigInt((match[4] ?? "").padEnd(9, "0") || "0");
293
+ return sign * ((hours * 60n + minutes) * 60n * NANOS_PER_SECOND + seconds * NANOS_PER_SECOND + fraction);
294
+ }
295
+ function throwInterval(input) {
296
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", `Invalid interval literal ${input}`, {
297
+ interval: input
298
+ });
299
+ }
300
+
160
301
  // ../core/dist/regex-functions.js
161
302
  function regexpMatchesValue(value, pattern, options = "") {
162
303
  const regexOptions = parseRegexOptions("regexp_matches", options, { allowGlobal: false });
@@ -234,6 +375,7 @@ var GEO_BACKEND_FUNCTIONS = /* @__PURE__ */ new Set([
234
375
  "st_disjoint",
235
376
  "st_contains",
236
377
  "st_within",
378
+ "st_dwithin",
237
379
  "h3_within",
238
380
  "h3_cell",
239
381
  "h3_parent"
@@ -251,7 +393,7 @@ function requireGeoBackend() {
251
393
  async function loadGeoBackend() {
252
394
  if (geoBackend !== null)
253
395
  return;
254
- const module = await import('./geo-backend-TSQJWAAB.js');
396
+ const module = await import('./geo-backend-MY6MET3L.js');
255
397
  module.installGeoBackend();
256
398
  }
257
399
  async function ensureGeoBackendForExprs(exprs) {
@@ -341,6 +483,10 @@ function matches(expr, row) {
341
483
  function jsonSafeValue(value) {
342
484
  if (isTimestampValue(value))
343
485
  return value.toJSON();
486
+ if (isIntervalValue(value))
487
+ return intervalToString(value);
488
+ if (value instanceof Uint8Array)
489
+ return bytesToHex(value);
344
490
  if (typeof value === "bigint") {
345
491
  const asNumber = Number(value);
346
492
  return Number.isSafeInteger(asNumber) ? asNumber : value.toString();
@@ -364,7 +510,7 @@ function rowValue(row, name) {
364
510
  throw new LakeqlError("LAKEQL_UNKNOWN_COLUMN", `Unknown column ${name}`, { column: name });
365
511
  }
366
512
  const value = row[name];
367
- if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean" || typeof value === "bigint" || isTimestampValue(value)) {
513
+ if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean" || typeof value === "bigint" || value instanceof Uint8Array || isTimestampValue(value) || isIntervalValue(value)) {
368
514
  return value;
369
515
  }
370
516
  throw new LakeqlError("LAKEQL_TYPE_ERROR", `Column ${name} is not a scalar value`, {
@@ -384,6 +530,12 @@ function toSqlBoolean(value) {
384
530
  function compare(op, left, right) {
385
531
  if (left === null || right === null)
386
532
  return null;
533
+ if (left instanceof Uint8Array || right instanceof Uint8Array) {
534
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", "Cannot compare binary values", {
535
+ leftType: evalValueType(left),
536
+ rightType: evalValueType(right)
537
+ });
538
+ }
387
539
  if (isTimestampValue(left) || isTimestampValue(right)) {
388
540
  const leftTimestamp = timestampEvalValue(left);
389
541
  const rightTimestamp = timestampEvalValue(right);
@@ -395,6 +547,12 @@ function compare(op, left, right) {
395
547
  }
396
548
  return compareOrder(op, compareTimestampValues(leftTimestamp, rightTimestamp));
397
549
  }
550
+ if (isIntervalValue(left) || isIntervalValue(right)) {
551
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", "Cannot compare interval values", {
552
+ leftType: evalValueType(left),
553
+ rightType: evalValueType(right)
554
+ });
555
+ }
398
556
  if (typeof left !== typeof right && !(isNumberLike(left) && isNumberLike(right))) {
399
557
  throw new LakeqlError("LAKEQL_TYPE_ERROR", "Cannot compare values of different types", {
400
558
  leftType: typeof left,
@@ -428,6 +586,10 @@ function timestampEvalValue(value) {
428
586
  return void 0;
429
587
  }
430
588
  function evalValueType(value) {
589
+ if (value instanceof Uint8Array)
590
+ return "binary";
591
+ if (isIntervalValue(value))
592
+ return "interval";
431
593
  return isTimestampValue(value) ? "timestamp" : typeof value;
432
594
  }
433
595
  function arithmetic(op, left, right) {
@@ -572,6 +734,8 @@ function callFunction(name, args) {
572
734
  return spatialPredicate(fn, args, "within");
573
735
  case "st_disjoint":
574
736
  return spatialPredicate(fn, args, "disjoint");
737
+ case "st_dwithin":
738
+ return stDWithin(fn, args);
575
739
  case "st_distance":
576
740
  return spatialMeasurement(fn, args, bboxDistance);
577
741
  case "st_area":
@@ -731,6 +895,24 @@ function spatialPredicate(name, args, op) {
731
895
  return bboxContains(eb, ea) && geo.booleanContains(b, a);
732
896
  }
733
897
  }
898
+ function stDWithin(name, args) {
899
+ requireArgCount(name, args, 3);
900
+ const left = args[0] ?? null;
901
+ const right = args[1] ?? null;
902
+ const distance = args[2] ?? null;
903
+ if (left === null || right === null || distance === null)
904
+ return null;
905
+ if (typeof distance !== "number" || !Number.isFinite(distance) || distance < 0) {
906
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", "st_dwithin() expects a non-negative distance");
907
+ }
908
+ const a = toGeometry(parseGeometry(left, name), name);
909
+ const b = toGeometry(parseGeometry(right, name), name);
910
+ const ea = envelopeOf(a);
911
+ const eb = envelopeOf(b);
912
+ if (bboxDistance(ea, eb) > distance)
913
+ return false;
914
+ return geometryDistance(a, b) <= distance;
915
+ }
734
916
  function envelopeOf(geometry) {
735
917
  switch (geometry.type) {
736
918
  case "Point":
@@ -874,8 +1056,10 @@ function envelope(value, name) {
874
1056
  return envelopeFromGeometry(parseGeometry(value, name), name);
875
1057
  }
876
1058
  function parseGeometry(value, name) {
1059
+ if (value instanceof Uint8Array)
1060
+ return parseWkbGeometry(value, name);
877
1061
  if (typeof value !== "string")
878
- throwType(name, "GeoJSON or BBox JSON string", value);
1062
+ throwType(name, "GeoJSON, WKT point, BBox JSON, or WKB bytes", value);
879
1063
  const wkt = parseWktGeometry(value);
880
1064
  if (wkt !== void 0)
881
1065
  return wkt;
@@ -892,6 +1076,129 @@ function parseGeometry(value, name) {
892
1076
  }
893
1077
  return parsed;
894
1078
  }
1079
+ function parseWkbGeometry(value, name) {
1080
+ const reader = new WkbReader(value, name);
1081
+ const geometry = reader.geometry();
1082
+ if (!reader.done()) {
1083
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", `${name}() received trailing WKB bytes`);
1084
+ }
1085
+ return geometry;
1086
+ }
1087
+ var WkbReader = class {
1088
+ bytes;
1089
+ name;
1090
+ offset = 0;
1091
+ littleEndian = true;
1092
+ constructor(bytes, name) {
1093
+ this.bytes = bytes;
1094
+ this.name = name;
1095
+ }
1096
+ done() {
1097
+ return this.offset === this.bytes.byteLength;
1098
+ }
1099
+ geometry() {
1100
+ this.byteOrder();
1101
+ const header = this.typeHeader();
1102
+ if (header.hasSrid)
1103
+ this.uint32();
1104
+ switch (header.type) {
1105
+ case 1:
1106
+ return { type: "Point", coordinates: this.position(header.dimensions) };
1107
+ case 2:
1108
+ return { type: "LineString", coordinates: this.positions(header.dimensions) };
1109
+ case 3:
1110
+ return {
1111
+ type: "Polygon",
1112
+ coordinates: this.rings(header.dimensions)
1113
+ };
1114
+ default:
1115
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", `${this.name}() supports WKB Point, LineString, or Polygon geometry`, { wkbType: header.rawType });
1116
+ }
1117
+ }
1118
+ byteOrder() {
1119
+ const order = this.uint8();
1120
+ if (order === 0) {
1121
+ this.littleEndian = false;
1122
+ return;
1123
+ }
1124
+ if (order === 1) {
1125
+ this.littleEndian = true;
1126
+ return;
1127
+ }
1128
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", `${this.name}() WKB byte order is invalid`);
1129
+ }
1130
+ typeHeader() {
1131
+ const rawType = this.uint32();
1132
+ const hasZFlag = (rawType & 2147483648) !== 0;
1133
+ const hasMFlag = (rawType & 1073741824) !== 0;
1134
+ const hasSrid = (rawType & 536870912) !== 0;
1135
+ const baseWithEwkbFlags = rawType & 268435455;
1136
+ const isoFamily = Math.trunc(baseWithEwkbFlags / 1e3);
1137
+ const type = baseWithEwkbFlags % 1e3;
1138
+ const hasZ = hasZFlag || isoFamily === 1 || isoFamily === 3;
1139
+ const hasM = hasMFlag || isoFamily === 2 || isoFamily === 3;
1140
+ return {
1141
+ rawType,
1142
+ type,
1143
+ dimensions: 2 + (hasZ ? 1 : 0) + (hasM ? 1 : 0),
1144
+ hasSrid
1145
+ };
1146
+ }
1147
+ rings(dimensions) {
1148
+ const count = this.uint32();
1149
+ const rings = [];
1150
+ for (let index = 0; index < count; index += 1) {
1151
+ rings.push(this.positions(dimensions));
1152
+ }
1153
+ return rings;
1154
+ }
1155
+ positions(dimensions) {
1156
+ const count = this.uint32();
1157
+ const points = [];
1158
+ for (let index = 0; index < count; index += 1) {
1159
+ points.push(this.position(dimensions));
1160
+ }
1161
+ return points;
1162
+ }
1163
+ position(dimensions) {
1164
+ if (dimensions < 2) {
1165
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", `${this.name}() WKB coordinate dimension is invalid`);
1166
+ }
1167
+ const x = this.float64();
1168
+ const y = this.float64();
1169
+ for (let index = 2; index < dimensions; index += 1)
1170
+ this.float64();
1171
+ if (!Number.isFinite(x) || !Number.isFinite(y)) {
1172
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", `${this.name}() WKB coordinates must be finite`);
1173
+ }
1174
+ return [x, y];
1175
+ }
1176
+ uint8() {
1177
+ this.require(1);
1178
+ const value = this.bytes[this.offset];
1179
+ this.offset += 1;
1180
+ return value ?? 0;
1181
+ }
1182
+ uint32() {
1183
+ this.require(4);
1184
+ const view = new DataView(this.bytes.buffer, this.bytes.byteOffset + this.offset, 4);
1185
+ const value = view.getUint32(0, this.littleEndian);
1186
+ this.offset += 4;
1187
+ return value;
1188
+ }
1189
+ float64() {
1190
+ this.require(8);
1191
+ const view = new DataView(this.bytes.buffer, this.bytes.byteOffset + this.offset, 8);
1192
+ const value = view.getFloat64(0, this.littleEndian);
1193
+ this.offset += 8;
1194
+ return value;
1195
+ }
1196
+ require(bytes) {
1197
+ if (this.offset + bytes > this.bytes.byteLength) {
1198
+ throw new LakeqlError("LAKEQL_TYPE_ERROR", `${this.name}() received truncated WKB`);
1199
+ }
1200
+ }
1201
+ };
895
1202
  function parseWktGeometry(value) {
896
1203
  const point = /^POINT\s*\(\s*([+-]?(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?)\s+([+-]?(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?)\s*\)$/iu.exec(value);
897
1204
  if (point !== null) {
@@ -956,6 +1263,113 @@ function bboxDistance(left, right) {
956
1263
  const dy = left.maxy < right.miny ? right.miny - left.maxy : right.maxy < left.miny ? left.miny - right.maxy : 0;
957
1264
  return Math.hypot(dx, dy);
958
1265
  }
1266
+ function geometryDistance(left, right) {
1267
+ if (requireGeoBackend().booleanIntersects(left, right))
1268
+ return 0;
1269
+ const leftSegments = geometrySegments(left);
1270
+ const rightSegments = geometrySegments(right);
1271
+ const leftPoints = geometryPoints(left);
1272
+ const rightPoints = geometryPoints(right);
1273
+ let best = Number.POSITIVE_INFINITY;
1274
+ for (const point of leftPoints) {
1275
+ for (const other of rightPoints)
1276
+ best = Math.min(best, pointDistance(point, other));
1277
+ for (const segment of rightSegments)
1278
+ best = Math.min(best, pointSegmentDistance(point, segment));
1279
+ }
1280
+ for (const point of rightPoints) {
1281
+ for (const segment of leftSegments)
1282
+ best = Math.min(best, pointSegmentDistance(point, segment));
1283
+ }
1284
+ for (const leftSegment of leftSegments) {
1285
+ for (const rightSegment of rightSegments) {
1286
+ best = Math.min(best, segmentDistance(leftSegment, rightSegment));
1287
+ }
1288
+ }
1289
+ return best;
1290
+ }
1291
+ function geometryPoints(geometry) {
1292
+ switch (geometry.type) {
1293
+ case "Point":
1294
+ return [geometry.coordinates];
1295
+ case "LineString":
1296
+ return geometry.coordinates;
1297
+ case "Polygon":
1298
+ return geometry.coordinates.flat();
1299
+ }
1300
+ }
1301
+ function geometrySegments(geometry) {
1302
+ switch (geometry.type) {
1303
+ case "Point":
1304
+ return [];
1305
+ case "LineString":
1306
+ return pathSegments(geometry.coordinates, false);
1307
+ case "Polygon":
1308
+ return geometry.coordinates.flatMap((ring) => pathSegments(ring, true));
1309
+ }
1310
+ }
1311
+ function pathSegments(points, closed) {
1312
+ const out = [];
1313
+ for (let index = 1; index < points.length; index += 1) {
1314
+ const previous = points[index - 1];
1315
+ const current = points[index];
1316
+ if (previous !== void 0 && current !== void 0)
1317
+ out.push([previous, current]);
1318
+ }
1319
+ if (closed && points.length > 1) {
1320
+ const first = points[0];
1321
+ const last = points[points.length - 1];
1322
+ if (first !== void 0 && last !== void 0 && (first[0] !== last[0] || first[1] !== last[1])) {
1323
+ out.push([last, first]);
1324
+ }
1325
+ }
1326
+ return out;
1327
+ }
1328
+ function segmentDistance(left, right) {
1329
+ if (segmentsIntersect(left, right))
1330
+ return 0;
1331
+ return Math.min(pointSegmentDistance(left[0], right), pointSegmentDistance(left[1], right), pointSegmentDistance(right[0], left), pointSegmentDistance(right[1], left));
1332
+ }
1333
+ function pointDistance(left, right) {
1334
+ return Math.hypot(left[0] - right[0], left[1] - right[1]);
1335
+ }
1336
+ function pointSegmentDistance(point, segment) {
1337
+ const [start, end] = segment;
1338
+ const dx = end[0] - start[0];
1339
+ const dy = end[1] - start[1];
1340
+ const lengthSq = dx * dx + dy * dy;
1341
+ if (lengthSq === 0)
1342
+ return pointDistance(point, start);
1343
+ const t = Math.max(0, Math.min(1, ((point[0] - start[0]) * dx + (point[1] - start[1]) * dy) / lengthSq));
1344
+ return pointDistance(point, [start[0] + t * dx, start[1] + t * dy]);
1345
+ }
1346
+ function segmentsIntersect(left, right) {
1347
+ const [a, b] = left;
1348
+ const [c, d] = right;
1349
+ const abC = orientation(a, b, c);
1350
+ const abD = orientation(a, b, d);
1351
+ const cdA = orientation(c, d, a);
1352
+ const cdB = orientation(c, d, b);
1353
+ if (abC === 0 && pointOnSegment(c, left))
1354
+ return true;
1355
+ if (abD === 0 && pointOnSegment(d, left))
1356
+ return true;
1357
+ if (cdA === 0 && pointOnSegment(a, right))
1358
+ return true;
1359
+ if (cdB === 0 && pointOnSegment(b, right))
1360
+ return true;
1361
+ return abC * abD < 0 && cdA * cdB < 0;
1362
+ }
1363
+ function orientation(a, b, c) {
1364
+ const cross = (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]);
1365
+ if (Math.abs(cross) < Number.EPSILON)
1366
+ return 0;
1367
+ return cross > 0 ? 1 : -1;
1368
+ }
1369
+ function pointOnSegment(point, segment) {
1370
+ const [start, end] = segment;
1371
+ return point[0] >= Math.min(start[0], end[0]) && point[0] <= Math.max(start[0], end[0]) && point[1] >= Math.min(start[1], end[1]) && point[1] <= Math.max(start[1], end[1]);
1372
+ }
959
1373
  function geometryArea(geometry, name) {
960
1374
  if (geometry.type === "BBox") {
961
1375
  const box = bboxFromRecord(geometry, name);
@@ -1148,5 +1562,11 @@ function throwType(name, expected, value) {
1148
1562
  function pad(value) {
1149
1563
  return String(value).padStart(2, "0");
1150
1564
  }
1565
+ function bytesToHex(bytes) {
1566
+ let out = "";
1567
+ for (const byte of bytes)
1568
+ out += byte.toString(16).padStart(2, "0");
1569
+ return out;
1570
+ }
1151
1571
 
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 };
1572
+ export { ERROR_CODES, LakeqlError, TimestampValue, __commonJS, __toESM, applyIntervalToTimestamp, bboxDistance, bboxIntersects, compareTimestampValues, encodeJsonLine, ensureGeoBackendForExprs, envelopeFromGeometry, envelopeOf, evaluate, geometryDistance, intervalToString, intervalValue, isIntervalValue, isLakeqlError, isNonNegativeInterval, isTimestampValue, jsonSafeValue, loadGeoBackend, matches, parseGeometry, regexpMatchesValue, regexpReplaceValue, requireGeoBackend, setGeoBackend, timestampEpochForUnit, timestampFromEpoch, timestampToIsoString, timestampValueFromIso, toGeometry };