@leonardovida-md/drizzle-neo-duckdb 1.0.2 → 1.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.
@@ -1,10 +1,4 @@
1
- import {
2
- Column,
3
- SQL,
4
- getTableName,
5
- is,
6
- sql,
7
- } from 'drizzle-orm';
1
+ import { Column, SQL, getTableName, is, sql } from 'drizzle-orm';
8
2
  import type { SelectedFields } from 'drizzle-orm/pg-core';
9
3
 
10
4
  function mapEntries(
@@ -38,8 +32,7 @@ function mapEntries(
38
32
  }
39
33
 
40
34
  if (is(value, SQL) || is(value, Column)) {
41
- const aliased =
42
- is(value, SQL) ? value : sql`${value}`.mapWith(value);
35
+ const aliased = is(value, SQL) ? value : sql`${value}`.mapWith(value);
43
36
  return [key, aliased.as(qualified)];
44
37
  }
45
38
 
package/src/utils.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  export { aliasFields } from './sql/selection.ts';
2
- export { adaptArrayOperators, queryAdapter } from './sql/query-rewriters.ts';
2
+ export { adaptArrayOperators } from './sql/query-rewriters.ts';
3
3
  export { mapResultRow } from './sql/result-mapper.ts';
@@ -0,0 +1,156 @@
1
+ /**
2
+ * DuckDB wrapper value helpers that are safe for client-side bundles.
3
+ * These utilities only tag values; conversion to native bindings lives
4
+ * in value-wrappers.ts to avoid pulling @duckdb/node-api into browsers.
5
+ */
6
+ export const DUCKDB_VALUE_MARKER = Symbol.for('drizzle-duckdb:value');
7
+
8
+ export type DuckDBValueKind =
9
+ | 'list'
10
+ | 'array'
11
+ | 'struct'
12
+ | 'map'
13
+ | 'timestamp'
14
+ | 'blob'
15
+ | 'json';
16
+
17
+ export interface DuckDBValueWrapper<
18
+ TKind extends DuckDBValueKind = DuckDBValueKind,
19
+ TData = unknown,
20
+ > {
21
+ readonly [DUCKDB_VALUE_MARKER]: true;
22
+ readonly kind: TKind;
23
+ readonly data: TData;
24
+ }
25
+
26
+ export interface ListValueWrapper
27
+ extends DuckDBValueWrapper<'list', unknown[]> {
28
+ readonly elementType?: string;
29
+ }
30
+
31
+ export interface ArrayValueWrapper
32
+ extends DuckDBValueWrapper<'array', unknown[]> {
33
+ readonly elementType?: string;
34
+ readonly fixedLength?: number;
35
+ }
36
+
37
+ export interface StructValueWrapper
38
+ extends DuckDBValueWrapper<'struct', Record<string, unknown>> {
39
+ readonly schema?: Record<string, string>;
40
+ }
41
+
42
+ export interface MapValueWrapper
43
+ extends DuckDBValueWrapper<'map', Record<string, unknown>> {
44
+ readonly valueType?: string;
45
+ }
46
+
47
+ export interface TimestampValueWrapper
48
+ extends DuckDBValueWrapper<'timestamp', Date | string> {
49
+ readonly withTimezone: boolean;
50
+ readonly precision?: number;
51
+ }
52
+
53
+ export interface BlobValueWrapper
54
+ extends DuckDBValueWrapper<'blob', Buffer | Uint8Array> {}
55
+
56
+ export interface JsonValueWrapper extends DuckDBValueWrapper<'json', unknown> {}
57
+
58
+ export type AnyDuckDBValueWrapper =
59
+ | ListValueWrapper
60
+ | ArrayValueWrapper
61
+ | StructValueWrapper
62
+ | MapValueWrapper
63
+ | TimestampValueWrapper
64
+ | BlobValueWrapper
65
+ | JsonValueWrapper;
66
+
67
+ export function isDuckDBWrapper(
68
+ value: unknown
69
+ ): value is AnyDuckDBValueWrapper {
70
+ return (
71
+ value !== null &&
72
+ typeof value === 'object' &&
73
+ DUCKDB_VALUE_MARKER in value &&
74
+ (value as DuckDBValueWrapper)[DUCKDB_VALUE_MARKER] === true
75
+ );
76
+ }
77
+
78
+ export function wrapList(
79
+ data: unknown[],
80
+ elementType?: string
81
+ ): ListValueWrapper {
82
+ return {
83
+ [DUCKDB_VALUE_MARKER]: true,
84
+ kind: 'list',
85
+ data,
86
+ elementType,
87
+ };
88
+ }
89
+
90
+ export function wrapArray(
91
+ data: unknown[],
92
+ elementType?: string,
93
+ fixedLength?: number
94
+ ): ArrayValueWrapper {
95
+ return {
96
+ [DUCKDB_VALUE_MARKER]: true,
97
+ kind: 'array',
98
+ data,
99
+ elementType,
100
+ fixedLength,
101
+ };
102
+ }
103
+
104
+ export function wrapStruct(
105
+ data: Record<string, unknown>,
106
+ schema?: Record<string, string>
107
+ ): StructValueWrapper {
108
+ return {
109
+ [DUCKDB_VALUE_MARKER]: true,
110
+ kind: 'struct',
111
+ data,
112
+ schema,
113
+ };
114
+ }
115
+
116
+ export function wrapMap(
117
+ data: Record<string, unknown>,
118
+ valueType?: string
119
+ ): MapValueWrapper {
120
+ return {
121
+ [DUCKDB_VALUE_MARKER]: true,
122
+ kind: 'map',
123
+ data,
124
+ valueType,
125
+ };
126
+ }
127
+
128
+ export function wrapTimestamp(
129
+ data: Date | string,
130
+ withTimezone: boolean,
131
+ precision?: number
132
+ ): TimestampValueWrapper {
133
+ return {
134
+ [DUCKDB_VALUE_MARKER]: true,
135
+ kind: 'timestamp',
136
+ data,
137
+ withTimezone,
138
+ precision,
139
+ };
140
+ }
141
+
142
+ export function wrapBlob(data: Buffer | Uint8Array): BlobValueWrapper {
143
+ return {
144
+ [DUCKDB_VALUE_MARKER]: true,
145
+ kind: 'blob',
146
+ data,
147
+ };
148
+ }
149
+
150
+ export function wrapJson(data: unknown): JsonValueWrapper {
151
+ return {
152
+ [DUCKDB_VALUE_MARKER]: true,
153
+ kind: 'json',
154
+ data,
155
+ };
156
+ }
@@ -0,0 +1,155 @@
1
+ import {
2
+ listValue,
3
+ arrayValue,
4
+ structValue,
5
+ mapValue,
6
+ blobValue,
7
+ timestampValue,
8
+ timestampTZValue,
9
+ type DuckDBValue,
10
+ type DuckDBMapEntry,
11
+ } from '@duckdb/node-api';
12
+ import {
13
+ DUCKDB_VALUE_MARKER,
14
+ isDuckDBWrapper,
15
+ wrapArray,
16
+ wrapBlob,
17
+ wrapJson,
18
+ wrapList,
19
+ wrapMap,
20
+ wrapStruct,
21
+ wrapTimestamp,
22
+ type AnyDuckDBValueWrapper,
23
+ type DuckDBValueWrapper,
24
+ type ArrayValueWrapper,
25
+ type BlobValueWrapper,
26
+ type JsonValueWrapper,
27
+ type ListValueWrapper,
28
+ type MapValueWrapper,
29
+ type StructValueWrapper,
30
+ type TimestampValueWrapper,
31
+ type DuckDBValueKind,
32
+ } from './value-wrappers-core.ts';
33
+
34
+ /**
35
+ * Convert a Date or string to microseconds since Unix epoch.
36
+ * Handles both Date objects and ISO-like timestamp strings.
37
+ */
38
+ function dateToMicros(value: Date | string): bigint {
39
+ if (value instanceof Date) {
40
+ return BigInt(value.getTime()) * 1000n;
41
+ }
42
+
43
+ // For strings, normalize the format for reliable parsing
44
+ // Handle both 'YYYY-MM-DD HH:MM:SS' and 'YYYY-MM-DDTHH:MM:SS' formats
45
+ let normalized = value;
46
+ if (!value.includes('T') && value.includes(' ')) {
47
+ // Convert 'YYYY-MM-DD HH:MM:SS' to ISO format
48
+ normalized = value.replace(' ', 'T');
49
+ }
50
+ // Add 'Z' suffix if no timezone offset to treat as UTC
51
+ if (!normalized.endsWith('Z') && !/[+-]\d{2}:?\d{2}$/.test(normalized)) {
52
+ normalized += 'Z';
53
+ }
54
+
55
+ const date = new Date(normalized);
56
+ if (isNaN(date.getTime())) {
57
+ throw new Error(`Invalid timestamp string: ${value}`);
58
+ }
59
+ return BigInt(date.getTime()) * 1000n;
60
+ }
61
+
62
+ /**
63
+ * Convert Buffer or Uint8Array to Uint8Array.
64
+ */
65
+ function toUint8Array(data: Buffer | Uint8Array): Uint8Array {
66
+ return data instanceof Uint8Array && !(data instanceof Buffer)
67
+ ? data
68
+ : new Uint8Array(data);
69
+ }
70
+
71
+ /**
72
+ * Convert struct entries to DuckDB struct value entries.
73
+ */
74
+ function convertStructEntries(
75
+ data: Record<string, unknown>,
76
+ toValue: (v: unknown) => DuckDBValue
77
+ ): Record<string, DuckDBValue> {
78
+ const entries: Record<string, DuckDBValue> = {};
79
+ for (const [key, val] of Object.entries(data)) {
80
+ entries[key] = toValue(val);
81
+ }
82
+ return entries;
83
+ }
84
+
85
+ /**
86
+ * Convert map entries to DuckDB map entry format.
87
+ */
88
+ function convertMapEntries(
89
+ data: Record<string, unknown>,
90
+ toValue: (v: unknown) => DuckDBValue
91
+ ): DuckDBMapEntry[] {
92
+ return Object.entries(data).map(([key, val]) => ({
93
+ key: key as DuckDBValue,
94
+ value: toValue(val),
95
+ }));
96
+ }
97
+
98
+ /**
99
+ * Convert a wrapper to a DuckDB Node API value.
100
+ * Uses exhaustive switch for compile-time safety.
101
+ */
102
+ export function wrapperToNodeApiValue(
103
+ wrapper: AnyDuckDBValueWrapper,
104
+ toValue: (v: unknown) => DuckDBValue
105
+ ): DuckDBValue {
106
+ switch (wrapper.kind) {
107
+ case 'list':
108
+ return listValue(wrapper.data.map(toValue));
109
+ case 'array':
110
+ return arrayValue(wrapper.data.map(toValue));
111
+ case 'struct':
112
+ return structValue(convertStructEntries(wrapper.data, toValue));
113
+ case 'map':
114
+ return mapValue(convertMapEntries(wrapper.data, toValue));
115
+ case 'timestamp':
116
+ return wrapper.withTimezone
117
+ ? timestampTZValue(dateToMicros(wrapper.data))
118
+ : timestampValue(dateToMicros(wrapper.data));
119
+ case 'blob':
120
+ return blobValue(toUint8Array(wrapper.data));
121
+ case 'json':
122
+ // JSON is stored as VARCHAR in DuckDB - stringify at binding time
123
+ return JSON.stringify(wrapper.data);
124
+ default: {
125
+ // Exhaustive check - TypeScript will error if a case is missing
126
+ const _exhaustive: never = wrapper;
127
+ throw new Error(
128
+ `Unknown wrapper kind: ${(_exhaustive as AnyDuckDBValueWrapper).kind}`
129
+ );
130
+ }
131
+ }
132
+ }
133
+
134
+ // Re-export core helpers for convenience and backward compatibility.
135
+ export {
136
+ DUCKDB_VALUE_MARKER,
137
+ isDuckDBWrapper,
138
+ wrapArray,
139
+ wrapBlob,
140
+ wrapJson,
141
+ wrapList,
142
+ wrapMap,
143
+ wrapStruct,
144
+ wrapTimestamp,
145
+ type AnyDuckDBValueWrapper,
146
+ type DuckDBValueWrapper,
147
+ type ArrayValueWrapper,
148
+ type BlobValueWrapper,
149
+ type JsonValueWrapper,
150
+ type ListValueWrapper,
151
+ type MapValueWrapper,
152
+ type StructValueWrapper,
153
+ type TimestampValueWrapper,
154
+ type DuckDBValueKind,
155
+ } from './value-wrappers-core.ts';