@ekodb/ekodb-client 0.6.1 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,506 @@
1
+ /**
2
+ * Unit tests for ekoDB TypeScript client utility functions
3
+ */
4
+
5
+ import { describe, it, expect } from "vitest";
6
+ import {
7
+ getValue,
8
+ getValues,
9
+ extractRecord,
10
+ getDateTimeValue,
11
+ getUUIDValue,
12
+ getDecimalValue,
13
+ getDurationValue,
14
+ getBytesValue,
15
+ getBinaryValue,
16
+ getArrayValue,
17
+ getSetValue,
18
+ getVectorValue,
19
+ getObjectValue,
20
+ Field,
21
+ } from "./utils";
22
+
23
+ // ============================================================================
24
+ // getValue Tests
25
+ // ============================================================================
26
+
27
+ describe("getValue", () => {
28
+ it("extracts value from wrapped string field", () => {
29
+ const field = { type: "String", value: "hello world" };
30
+ expect(getValue(field)).toBe("hello world");
31
+ });
32
+
33
+ it("extracts value from wrapped integer field", () => {
34
+ const field = { type: "Integer", value: 42 };
35
+ expect(getValue(field)).toBe(42);
36
+ });
37
+
38
+ it("extracts value from wrapped float field", () => {
39
+ const field = { type: "Float", value: 3.14 };
40
+ expect(getValue(field)).toBe(3.14);
41
+ });
42
+
43
+ it("extracts value from wrapped boolean field", () => {
44
+ const field = { type: "Boolean", value: true };
45
+ expect(getValue(field)).toBe(true);
46
+ });
47
+
48
+ it("extracts value from wrapped null field", () => {
49
+ const field = { type: "Null", value: null };
50
+ expect(getValue(field)).toBeNull();
51
+ });
52
+
53
+ it("returns plain string as-is", () => {
54
+ expect(getValue("plain string")).toBe("plain string");
55
+ });
56
+
57
+ it("returns plain number as-is", () => {
58
+ expect(getValue(123)).toBe(123);
59
+ });
60
+
61
+ it("returns plain boolean as-is", () => {
62
+ expect(getValue(true)).toBe(true);
63
+ expect(getValue(false)).toBe(false);
64
+ });
65
+
66
+ it("returns null as-is", () => {
67
+ expect(getValue(null)).toBeNull();
68
+ });
69
+
70
+ it("returns undefined as-is", () => {
71
+ expect(getValue(undefined)).toBeUndefined();
72
+ });
73
+
74
+ it("returns plain array as-is", () => {
75
+ const arr = [1, 2, 3];
76
+ expect(getValue(arr)).toEqual([1, 2, 3]);
77
+ });
78
+
79
+ it("returns object without value key as-is", () => {
80
+ const obj = { name: "test", count: 5 };
81
+ expect(getValue(obj)).toEqual({ name: "test", count: 5 });
82
+ });
83
+
84
+ it("supports generic type parameter", () => {
85
+ const field = { type: "String", value: "typed" };
86
+ const result: string = getValue<string>(field);
87
+ expect(result).toBe("typed");
88
+ });
89
+ });
90
+
91
+ // ============================================================================
92
+ // getValues Tests
93
+ // ============================================================================
94
+
95
+ describe("getValues", () => {
96
+ it("extracts multiple field values from record", () => {
97
+ const record = {
98
+ name: { type: "String", value: "John" },
99
+ age: { type: "Integer", value: 30 },
100
+ active: { type: "Boolean", value: true },
101
+ };
102
+ const result = getValues(record, ["name", "age", "active"]);
103
+ expect(result).toEqual({ name: "John", age: 30, active: true });
104
+ });
105
+
106
+ it("extracts only requested fields", () => {
107
+ const record = {
108
+ name: { type: "String", value: "John" },
109
+ age: { type: "Integer", value: 30 },
110
+ email: { type: "String", value: "john@example.com" },
111
+ };
112
+ const result = getValues(record, ["name", "email"]);
113
+ expect(result).toEqual({ name: "John", email: "john@example.com" });
114
+ expect(result).not.toHaveProperty("age");
115
+ });
116
+
117
+ it("handles missing fields gracefully", () => {
118
+ const record = {
119
+ name: { type: "String", value: "John" },
120
+ };
121
+ const result = getValues(record, ["name", "missing"]);
122
+ expect(result.name).toBe("John");
123
+ expect(result.missing).toBeUndefined();
124
+ });
125
+
126
+ it("handles empty record", () => {
127
+ const result = getValues({}, ["name", "age"]);
128
+ expect(result.name).toBeUndefined();
129
+ expect(result.age).toBeUndefined();
130
+ });
131
+
132
+ it("handles empty fields list", () => {
133
+ const record = { name: { type: "String", value: "John" } };
134
+ const result = getValues(record, []);
135
+ expect(result).toEqual({});
136
+ });
137
+
138
+ it("handles mixed wrapped and plain values", () => {
139
+ const record = {
140
+ wrapped: { type: "String", value: "wrapped_value" },
141
+ plain: "plain_value",
142
+ };
143
+ const result = getValues(record, ["wrapped", "plain"]);
144
+ expect(result).toEqual({ wrapped: "wrapped_value", plain: "plain_value" });
145
+ });
146
+ });
147
+
148
+ // ============================================================================
149
+ // Specialized Value Extractors Tests
150
+ // ============================================================================
151
+
152
+ describe("getDateTimeValue", () => {
153
+ it("parses ISO format datetime string", () => {
154
+ const field = { type: "DateTime", value: "2024-01-15T10:30:00Z" };
155
+ const result = getDateTimeValue(field);
156
+ expect(result).toBeInstanceOf(Date);
157
+ expect(result?.getFullYear()).toBe(2024);
158
+ expect(result?.getMonth()).toBe(0); // January is 0
159
+ expect(result?.getDate()).toBe(15);
160
+ });
161
+
162
+ it("parses ISO format with timezone offset", () => {
163
+ const field = { type: "DateTime", value: "2024-01-15T10:30:00+00:00" };
164
+ const result = getDateTimeValue(field);
165
+ expect(result).toBeInstanceOf(Date);
166
+ expect(result?.getFullYear()).toBe(2024);
167
+ });
168
+
169
+ it("returns Date object as-is", () => {
170
+ const dt = new Date(2024, 0, 15, 10, 30, 0);
171
+ const field = { type: "DateTime", value: dt };
172
+ const result = getDateTimeValue(field);
173
+ expect(result).toBe(dt);
174
+ });
175
+
176
+ it("returns null for invalid datetime string", () => {
177
+ const field = { type: "DateTime", value: "not-a-datetime" };
178
+ const result = getDateTimeValue(field);
179
+ expect(result).toBeNull();
180
+ });
181
+
182
+ it("returns null for null value", () => {
183
+ expect(getDateTimeValue(null)).toBeNull();
184
+ });
185
+ });
186
+
187
+ describe("getUUIDValue", () => {
188
+ it("extracts UUID from wrapped field", () => {
189
+ const field = {
190
+ type: "UUID",
191
+ value: "550e8400-e29b-41d4-a716-446655440000",
192
+ };
193
+ expect(getUUIDValue(field)).toBe("550e8400-e29b-41d4-a716-446655440000");
194
+ });
195
+
196
+ it("extracts UUID from plain string", () => {
197
+ expect(getUUIDValue("550e8400-e29b-41d4-a716-446655440000")).toBe(
198
+ "550e8400-e29b-41d4-a716-446655440000",
199
+ );
200
+ });
201
+
202
+ it("returns null for non-string", () => {
203
+ expect(getUUIDValue(12345)).toBeNull();
204
+ });
205
+ });
206
+
207
+ describe("getDecimalValue", () => {
208
+ it("extracts decimal from integer", () => {
209
+ const field = { type: "Decimal", value: 42 };
210
+ expect(getDecimalValue(field)).toBe(42);
211
+ });
212
+
213
+ it("extracts decimal from float", () => {
214
+ const field = { type: "Decimal", value: 3.14159 };
215
+ expect(getDecimalValue(field)).toBeCloseTo(3.14159);
216
+ });
217
+
218
+ it("extracts decimal from string representation", () => {
219
+ const field = { type: "Decimal", value: "123.456" };
220
+ expect(getDecimalValue(field)).toBeCloseTo(123.456);
221
+ });
222
+
223
+ it("returns null for invalid decimal string", () => {
224
+ const field = { type: "Decimal", value: "not-a-number" };
225
+ expect(getDecimalValue(field)).toBeNull();
226
+ });
227
+ });
228
+
229
+ describe("getDurationValue", () => {
230
+ it("extracts duration from number (milliseconds)", () => {
231
+ const field = { type: "Duration", value: 3600000 };
232
+ expect(getDurationValue(field)).toBe(3600000);
233
+ });
234
+
235
+ it("extracts duration from secs/nanos object", () => {
236
+ const field = { type: "Duration", value: { secs: 10, nanos: 500000000 } };
237
+ expect(getDurationValue(field)).toBeCloseTo(10500); // 10.5 seconds in ms
238
+ });
239
+
240
+ it("extracts duration from plain number", () => {
241
+ expect(getDurationValue(10500)).toBe(10500);
242
+ });
243
+ });
244
+
245
+ describe("getBytesValue", () => {
246
+ it("extracts bytes from Uint8Array", () => {
247
+ const bytes = new Uint8Array([104, 101, 108, 108, 111]);
248
+ const field = { type: "Bytes", value: bytes };
249
+ const result = getBytesValue(field);
250
+ expect(result).toEqual(bytes);
251
+ });
252
+
253
+ it("extracts bytes from array of integers", () => {
254
+ const field = { type: "Bytes", value: [104, 101, 108, 108, 111] };
255
+ const result = getBytesValue(field);
256
+ expect(result).toBeInstanceOf(Uint8Array);
257
+ expect(Array.from(result!)).toEqual([104, 101, 108, 108, 111]);
258
+ });
259
+
260
+ it("extracts bytes from base64 string", () => {
261
+ const field = { type: "Bytes", value: btoa("hello") };
262
+ const result = getBytesValue(field);
263
+ expect(result).toBeInstanceOf(Uint8Array);
264
+ expect(new TextDecoder().decode(result!)).toBe("hello");
265
+ });
266
+
267
+ it("returns null for invalid bytes", () => {
268
+ expect(getBytesValue(12345)).toBeNull();
269
+ });
270
+ });
271
+
272
+ describe("getBinaryValue", () => {
273
+ it("extracts binary from Uint8Array (alias for getBytesValue)", () => {
274
+ const bytes = new Uint8Array([1, 2, 3, 4]);
275
+ const field = { type: "Binary", value: bytes };
276
+ expect(getBinaryValue(field)).toEqual(bytes);
277
+ });
278
+ });
279
+
280
+ describe("getArrayValue", () => {
281
+ it("extracts array from wrapped field", () => {
282
+ const field = { type: "Array", value: [1, 2, 3, 4, 5] };
283
+ expect(getArrayValue(field)).toEqual([1, 2, 3, 4, 5]);
284
+ });
285
+
286
+ it("extracts array from plain array", () => {
287
+ expect(getArrayValue([1, 2, 3])).toEqual([1, 2, 3]);
288
+ });
289
+
290
+ it("returns null for non-array", () => {
291
+ expect(getArrayValue("not an array")).toBeNull();
292
+ });
293
+ });
294
+
295
+ describe("getSetValue", () => {
296
+ it("extracts set from wrapped field", () => {
297
+ const field = { type: "Set", value: ["a", "b", "c"] };
298
+ expect(getSetValue(field)).toEqual(["a", "b", "c"]);
299
+ });
300
+ });
301
+
302
+ describe("getVectorValue", () => {
303
+ it("extracts vector from array of floats", () => {
304
+ const field = { type: "Vector", value: [0.1, 0.2, 0.3, 0.4] };
305
+ expect(getVectorValue(field)).toEqual([0.1, 0.2, 0.3, 0.4]);
306
+ });
307
+
308
+ it("converts integers to floats", () => {
309
+ const field = { type: "Vector", value: [1, 2, 3] };
310
+ expect(getVectorValue(field)).toEqual([1, 2, 3]);
311
+ });
312
+
313
+ it("filters out invalid values", () => {
314
+ const field = { type: "Vector", value: [1, "invalid", 3] };
315
+ const result = getVectorValue(field);
316
+ expect(result).toEqual([1, 3]);
317
+ });
318
+ });
319
+
320
+ describe("getObjectValue", () => {
321
+ it("extracts object from wrapped field", () => {
322
+ const field = { type: "Object", value: { key: "value", count: 5 } };
323
+ expect(getObjectValue(field)).toEqual({ key: "value", count: 5 });
324
+ });
325
+
326
+ it("returns null for non-object", () => {
327
+ expect(getObjectValue([1, 2, 3])).toBeNull();
328
+ });
329
+
330
+ it("returns null for array (not a plain object)", () => {
331
+ const field = { type: "Object", value: [1, 2, 3] };
332
+ expect(getObjectValue(field)).toBeNull();
333
+ });
334
+ });
335
+
336
+ // ============================================================================
337
+ // Field Builder Tests
338
+ // ============================================================================
339
+
340
+ describe("Field builders", () => {
341
+ it("builds string field", () => {
342
+ expect(Field.string("hello")).toEqual({ type: "String", value: "hello" });
343
+ });
344
+
345
+ it("builds integer field", () => {
346
+ expect(Field.integer(42)).toEqual({ type: "Integer", value: 42 });
347
+ });
348
+
349
+ it("builds integer field (floors float)", () => {
350
+ expect(Field.integer(42.9)).toEqual({ type: "Integer", value: 42 });
351
+ });
352
+
353
+ it("builds float field", () => {
354
+ expect(Field.float(3.14)).toEqual({ type: "Float", value: 3.14 });
355
+ });
356
+
357
+ it("builds boolean field", () => {
358
+ expect(Field.boolean(true)).toEqual({ type: "Boolean", value: true });
359
+ expect(Field.boolean(false)).toEqual({ type: "Boolean", value: false });
360
+ });
361
+
362
+ it("builds UUID field", () => {
363
+ const uuid = "550e8400-e29b-41d4-a716-446655440000";
364
+ expect(Field.uuid(uuid)).toEqual({ type: "UUID", value: uuid });
365
+ });
366
+
367
+ it("builds decimal field", () => {
368
+ expect(Field.decimal("123.456")).toEqual({
369
+ type: "Decimal",
370
+ value: "123.456",
371
+ });
372
+ });
373
+
374
+ it("builds dateTime field from Date", () => {
375
+ const dt = new Date("2024-01-15T10:30:00Z");
376
+ const result = Field.dateTime(dt);
377
+ expect(result.type).toBe("DateTime");
378
+ expect(result.value).toBe(dt.toISOString());
379
+ });
380
+
381
+ it("builds dateTime field from string", () => {
382
+ const result = Field.dateTime("2024-01-15T10:30:00Z");
383
+ expect(result).toEqual({ type: "DateTime", value: "2024-01-15T10:30:00Z" });
384
+ });
385
+
386
+ it("builds duration field", () => {
387
+ expect(Field.duration(3600000)).toEqual({
388
+ type: "Duration",
389
+ value: 3600000,
390
+ });
391
+ });
392
+
393
+ it("builds number field", () => {
394
+ expect(Field.number(42)).toEqual({ type: "Number", value: 42 });
395
+ expect(Field.number(3.14)).toEqual({ type: "Number", value: 3.14 });
396
+ });
397
+
398
+ it("builds array field", () => {
399
+ expect(Field.array([1, 2, 3])).toEqual({ type: "Array", value: [1, 2, 3] });
400
+ });
401
+
402
+ it("builds set field", () => {
403
+ expect(Field.set(["a", "b", "c"])).toEqual({
404
+ type: "Set",
405
+ value: ["a", "b", "c"],
406
+ });
407
+ });
408
+
409
+ it("builds vector field", () => {
410
+ expect(Field.vector([0.1, 0.2, 0.3])).toEqual({
411
+ type: "Vector",
412
+ value: [0.1, 0.2, 0.3],
413
+ });
414
+ });
415
+
416
+ it("builds object field", () => {
417
+ expect(Field.object({ key: "value" })).toEqual({
418
+ type: "Object",
419
+ value: { key: "value" },
420
+ });
421
+ });
422
+
423
+ it("builds bytes field from Uint8Array", () => {
424
+ const bytes = new Uint8Array([104, 101, 108, 108, 111]);
425
+ const result = Field.bytes(bytes);
426
+ expect(result.type).toBe("Bytes");
427
+ expect(typeof result.value).toBe("string"); // Base64 encoded
428
+ });
429
+
430
+ it("builds bytes field from base64 string", () => {
431
+ const result = Field.bytes("aGVsbG8=");
432
+ expect(result).toEqual({ type: "Bytes", value: "aGVsbG8=" });
433
+ });
434
+
435
+ it("builds binary field from Uint8Array", () => {
436
+ const bytes = new Uint8Array([1, 2, 3, 4]);
437
+ const result = Field.binary(bytes);
438
+ expect(result.type).toBe("Binary");
439
+ expect(typeof result.value).toBe("string"); // Base64 encoded
440
+ });
441
+ });
442
+
443
+ // ============================================================================
444
+ // extractRecord Tests
445
+ // ============================================================================
446
+
447
+ describe("extractRecord", () => {
448
+ it("extracts all fields from a record", () => {
449
+ const record = {
450
+ id: "user_123",
451
+ name: { type: "String", value: "John Doe" },
452
+ age: { type: "Integer", value: 30 },
453
+ active: { type: "Boolean", value: true },
454
+ };
455
+ const result = extractRecord(record);
456
+ expect(result).toEqual({
457
+ id: "user_123",
458
+ name: "John Doe",
459
+ age: 30,
460
+ active: true,
461
+ });
462
+ });
463
+
464
+ it("preserves id field as-is", () => {
465
+ const record = {
466
+ id: "user_123",
467
+ name: { type: "String", value: "John" },
468
+ };
469
+ const result = extractRecord(record);
470
+ expect(result.id).toBe("user_123");
471
+ });
472
+
473
+ it("handles nested objects", () => {
474
+ const record = {
475
+ user: { type: "Object", value: { name: "John", role: "admin" } },
476
+ tags: { type: "Array", value: ["python", "rust"] },
477
+ };
478
+ const result = extractRecord(record);
479
+ expect(result.user).toEqual({ name: "John", role: "admin" });
480
+ expect(result.tags).toEqual(["python", "rust"]);
481
+ });
482
+
483
+ it("handles empty record", () => {
484
+ expect(extractRecord({})).toEqual({});
485
+ });
486
+
487
+ it("handles null/undefined input", () => {
488
+ expect(extractRecord(null)).toBeNull();
489
+ expect(extractRecord(undefined)).toBeUndefined();
490
+ });
491
+
492
+ it("handles non-object input", () => {
493
+ expect(extractRecord("string")).toBe("string");
494
+ expect(extractRecord(123)).toBe(123);
495
+ });
496
+
497
+ it("handles null field values", () => {
498
+ const record = {
499
+ name: { type: "String", value: "John" },
500
+ optional: { type: "Null", value: null },
501
+ };
502
+ const result = extractRecord(record);
503
+ expect(result.name).toBe("John");
504
+ expect(result.optional).toBeNull();
505
+ });
506
+ });
package/src/utils.ts CHANGED
@@ -12,7 +12,7 @@
12
12
  *
13
13
  * @example
14
14
  * ```typescript
15
- * const user = await client.findByID('users', userId);
15
+ * const user = await client.findById('users', userId);
16
16
  * const email = getValue(user.email); // Extracts string from { type: 'String', value: 'user@example.com' }
17
17
  * const age = getValue(user.age); // Extracts number from { type: 'Integer', value: 25 }
18
18
  * ```
@@ -34,7 +34,7 @@ export function getValue<T = any>(field: any): T {
34
34
  *
35
35
  * @example
36
36
  * ```typescript
37
- * const user = await client.findByID('users', userId);
37
+ * const user = await client.findById('users', userId);
38
38
  * const { email, first_name, status } = getValues(user, ['email', 'first_name', 'status']);
39
39
  * ```
40
40
  */
@@ -171,7 +171,7 @@ export function getObjectValue<T = any>(field: any): T | null {
171
171
  *
172
172
  * @example
173
173
  * ```typescript
174
- * const user = await client.findByID('users', userId);
174
+ * const user = await client.findById('users', userId);
175
175
  * const plainUser = extractRecord(user);
176
176
  * // { id: '123', email: 'user@example.com', first_name: 'John', ... }
177
177
  * ```
@@ -191,3 +191,165 @@ export function extractRecord<T extends Record<string, any>>(record: any): T {
191
191
  }
192
192
  return result as T;
193
193
  }
194
+
195
+ // ============================================================================
196
+ // Wrapped Type Builders
197
+ // ============================================================================
198
+ // These functions create wrapped type objects for sending to ekoDB.
199
+ // Use these when inserting/updating records with special field types.
200
+ //
201
+ // Example:
202
+ // await client.insert("orders", {
203
+ // id: Field.uuid("550e8400-e29b-41d4-a716-446655440000"),
204
+ // total: Field.decimal("99.99"),
205
+ // created_at: Field.dateTime(new Date()),
206
+ // tags: Field.set(["sale", "featured"]),
207
+ // });
208
+
209
+ export interface WrappedFieldValue {
210
+ type: string;
211
+ value: unknown;
212
+ }
213
+
214
+ /**
215
+ * Field builders for creating wrapped type values to send to ekoDB.
216
+ * These are the inverse of the getValue* extraction functions.
217
+ */
218
+ export const Field = {
219
+ /**
220
+ * Create a UUID field value
221
+ * @param value - UUID string (e.g., "550e8400-e29b-41d4-a716-446655440000")
222
+ */
223
+ uuid: (value: string): WrappedFieldValue => ({
224
+ type: "UUID",
225
+ value,
226
+ }),
227
+
228
+ /**
229
+ * Create a Decimal field value for precise numeric values
230
+ * @param value - Decimal as string (e.g., "99.99") to preserve precision
231
+ */
232
+ decimal: (value: string): WrappedFieldValue => ({
233
+ type: "Decimal",
234
+ value,
235
+ }),
236
+
237
+ /**
238
+ * Create a DateTime field value
239
+ * @param value - Date object or RFC3339 string
240
+ */
241
+ dateTime: (value: Date | string): WrappedFieldValue => ({
242
+ type: "DateTime",
243
+ value: value instanceof Date ? value.toISOString() : value,
244
+ }),
245
+
246
+ /**
247
+ * Create a Duration field value
248
+ * @param milliseconds - Duration in milliseconds
249
+ */
250
+ duration: (milliseconds: number): WrappedFieldValue => ({
251
+ type: "Duration",
252
+ value: milliseconds,
253
+ }),
254
+
255
+ /**
256
+ * Create a Number field value (flexible numeric type)
257
+ * @param value - Integer or float
258
+ */
259
+ number: (value: number): WrappedFieldValue => ({
260
+ type: "Number",
261
+ value,
262
+ }),
263
+
264
+ /**
265
+ * Create a Set field value (unique elements)
266
+ * @param values - Array of values (duplicates will be removed by server)
267
+ */
268
+ set: <T>(values: T[]): WrappedFieldValue => ({
269
+ type: "Set",
270
+ value: values,
271
+ }),
272
+
273
+ /**
274
+ * Create a Vector field value (for embeddings/similarity search)
275
+ * @param values - Array of numbers representing the vector
276
+ */
277
+ vector: (values: number[]): WrappedFieldValue => ({
278
+ type: "Vector",
279
+ value: values,
280
+ }),
281
+
282
+ /**
283
+ * Create a Binary field value
284
+ * @param value - Base64 encoded string or Uint8Array
285
+ */
286
+ binary: (value: string | Uint8Array): WrappedFieldValue => ({
287
+ type: "Binary",
288
+ value:
289
+ value instanceof Uint8Array ? btoa(String.fromCharCode(...value)) : value,
290
+ }),
291
+
292
+ /**
293
+ * Create a Bytes field value
294
+ * @param value - Base64 encoded string or Uint8Array
295
+ */
296
+ bytes: (value: string | Uint8Array): WrappedFieldValue => ({
297
+ type: "Bytes",
298
+ value:
299
+ value instanceof Uint8Array ? btoa(String.fromCharCode(...value)) : value,
300
+ }),
301
+
302
+ /**
303
+ * Create an Array field value
304
+ * @param values - Array of values
305
+ */
306
+ array: <T>(values: T[]): WrappedFieldValue => ({
307
+ type: "Array",
308
+ value: values,
309
+ }),
310
+
311
+ /**
312
+ * Create an Object field value
313
+ * @param value - Object/map of key-value pairs
314
+ */
315
+ object: (value: Record<string, unknown>): WrappedFieldValue => ({
316
+ type: "Object",
317
+ value,
318
+ }),
319
+
320
+ /**
321
+ * Create a String field value (explicit wrapping)
322
+ * @param value - String value
323
+ */
324
+ string: (value: string): WrappedFieldValue => ({
325
+ type: "String",
326
+ value,
327
+ }),
328
+
329
+ /**
330
+ * Create an Integer field value (explicit wrapping)
331
+ * @param value - Integer value
332
+ */
333
+ integer: (value: number): WrappedFieldValue => ({
334
+ type: "Integer",
335
+ value: Math.floor(value),
336
+ }),
337
+
338
+ /**
339
+ * Create a Float field value (explicit wrapping)
340
+ * @param value - Float value
341
+ */
342
+ float: (value: number): WrappedFieldValue => ({
343
+ type: "Float",
344
+ value,
345
+ }),
346
+
347
+ /**
348
+ * Create a Boolean field value (explicit wrapping)
349
+ * @param value - Boolean value
350
+ */
351
+ boolean: (value: boolean): WrappedFieldValue => ({
352
+ type: "Boolean",
353
+ value,
354
+ }),
355
+ };