crous 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,819 @@
1
+ #include <node_api.h>
2
+ #include <string.h>
3
+ #include <stdlib.h>
4
+ #include <stdio.h>
5
+ #include "crous.h"
6
+
7
+ /* ============================================================================
8
+ ERROR HANDLING
9
+ ============================================================================ */
10
+
11
+ static napi_value throw_crous_error(napi_env env, const char* msg) {
12
+ napi_throw_error(env, "CrousError", msg);
13
+ return NULL;
14
+ }
15
+
16
+ static napi_value throw_encode_error(napi_env env, const char* msg) {
17
+ napi_throw_error(env, "CrousEncodeError", msg);
18
+ return NULL;
19
+ }
20
+
21
+ static napi_value throw_decode_error(napi_env env, const char* msg) {
22
+ napi_throw_error(env, "CrousDecodeError", msg);
23
+ return NULL;
24
+ }
25
+
26
+ /* ============================================================================
27
+ CUSTOM SERIALIZER/DECODER REGISTRIES
28
+ ============================================================================ */
29
+
30
+ /* Global references for custom serializers and decoders */
31
+ static napi_ref custom_serializers_ref = NULL;
32
+ static napi_ref custom_decoders_ref = NULL;
33
+ static napi_ref type_to_tag_ref = NULL;
34
+ static uint32_t next_custom_tag = 100;
35
+
36
+ /* ============================================================================
37
+ CONVERSION: NODE.JS VALUE -> CROUS VALUE
38
+ ============================================================================ */
39
+
40
+ static crous_value* napi_to_crous(napi_env env, napi_value value, napi_value default_func, crous_err_t *err);
41
+
42
+ static crous_value* try_custom_serializer(napi_env env, napi_value obj, napi_value default_func,
43
+ crous_err_t *err, int *handled) {
44
+ *handled = 0;
45
+ *err = CROUS_OK;
46
+
47
+ // Get custom serializers map
48
+ napi_value serializers;
49
+ if (custom_serializers_ref == NULL) {
50
+ return NULL;
51
+ }
52
+
53
+ napi_status status = napi_get_reference_value(env, custom_serializers_ref, &serializers);
54
+ if (status != napi_ok || serializers == NULL) {
55
+ return NULL;
56
+ }
57
+
58
+ // Get constructor of object
59
+ napi_value constructor;
60
+ status = napi_get_named_property(env, obj, "constructor", &constructor);
61
+ if (status != napi_ok) {
62
+ return NULL;
63
+ }
64
+
65
+ // Look up serializer
66
+ napi_value serializer;
67
+ bool has_property;
68
+ status = napi_has_property(env, serializers, constructor, &has_property);
69
+ if (status != napi_ok || !has_property) {
70
+ // Try default_func if provided
71
+ if (default_func != NULL) {
72
+ napi_valuetype type;
73
+ napi_typeof(env, default_func, &type);
74
+ if (type == napi_function) {
75
+ serializer = default_func;
76
+ *handled = 1;
77
+ } else {
78
+ return NULL;
79
+ }
80
+ } else {
81
+ return NULL;
82
+ }
83
+ } else {
84
+ status = napi_get_property(env, serializers, constructor, &serializer);
85
+ if (status != napi_ok) {
86
+ return NULL;
87
+ }
88
+ *handled = 1;
89
+ }
90
+
91
+ // Call serializer
92
+ napi_value global;
93
+ napi_get_global(env, &global);
94
+ napi_value result;
95
+ status = napi_call_function(env, global, serializer, 1, &obj, &result);
96
+ if (status != napi_ok) {
97
+ *err = CROUS_ERR_ENCODE;
98
+ return NULL;
99
+ }
100
+
101
+ // Get tag for this type (default 100)
102
+ uint32_t tag = 100;
103
+ if (type_to_tag_ref != NULL) {
104
+ napi_value tag_map;
105
+ status = napi_get_reference_value(env, type_to_tag_ref, &tag_map);
106
+ if (status == napi_ok) {
107
+ napi_value tag_value;
108
+ bool has_tag;
109
+ status = napi_has_property(env, tag_map, constructor, &has_tag);
110
+ if (status == napi_ok && has_tag) {
111
+ status = napi_get_property(env, tag_map, constructor, &tag_value);
112
+ if (status == napi_ok) {
113
+ napi_get_value_uint32(env, tag_value, &tag);
114
+ }
115
+ }
116
+ }
117
+ }
118
+
119
+ // Convert result to crous_value
120
+ crous_value *inner = napi_to_crous(env, result, NULL, err);
121
+ if (*err != CROUS_OK || inner == NULL) {
122
+ return NULL;
123
+ }
124
+
125
+ // Wrap in tagged value
126
+ crous_value *tagged = crous_value_new_tagged(tag, inner);
127
+ if (!tagged) {
128
+ crous_value_free_tree(inner);
129
+ *err = CROUS_ERR_OOM;
130
+ return NULL;
131
+ }
132
+
133
+ return tagged;
134
+ }
135
+
136
+ static crous_value* napi_to_crous(napi_env env, napi_value value, napi_value default_func, crous_err_t *err) {
137
+ *err = CROUS_OK;
138
+ napi_status status;
139
+ napi_valuetype type;
140
+
141
+ status = napi_typeof(env, value, &type);
142
+ if (status != napi_ok) {
143
+ *err = CROUS_ERR_ENCODE;
144
+ return NULL;
145
+ }
146
+
147
+ switch (type) {
148
+ case napi_undefined:
149
+ case napi_null:
150
+ return crous_value_new_null();
151
+
152
+ case napi_boolean: {
153
+ bool val;
154
+ status = napi_get_value_bool(env, value, &val);
155
+ if (status != napi_ok) {
156
+ *err = CROUS_ERR_ENCODE;
157
+ return NULL;
158
+ }
159
+ return crous_value_new_bool(val ? 1 : 0);
160
+ }
161
+
162
+ case napi_number: {
163
+ double num;
164
+ status = napi_get_value_double(env, value, &num);
165
+ if (status != napi_ok) {
166
+ *err = CROUS_ERR_ENCODE;
167
+ return NULL;
168
+ }
169
+
170
+ // Check if it's an integer
171
+ if (num == (int64_t)num && num >= -9223372036854775807LL && num <= 9223372036854775807LL) {
172
+ return crous_value_new_int((int64_t)num);
173
+ } else {
174
+ return crous_value_new_float(num);
175
+ }
176
+ }
177
+
178
+ case napi_string: {
179
+ size_t str_len;
180
+ status = napi_get_value_string_utf8(env, value, NULL, 0, &str_len);
181
+ if (status != napi_ok) {
182
+ *err = CROUS_ERR_ENCODE;
183
+ return NULL;
184
+ }
185
+
186
+ char *buf = (char*)malloc(str_len + 1);
187
+ if (!buf) {
188
+ *err = CROUS_ERR_OOM;
189
+ return NULL;
190
+ }
191
+
192
+ size_t copied;
193
+ status = napi_get_value_string_utf8(env, value, buf, str_len + 1, &copied);
194
+ if (status != napi_ok) {
195
+ free(buf);
196
+ *err = CROUS_ERR_ENCODE;
197
+ return NULL;
198
+ }
199
+
200
+ crous_value *result = crous_value_new_string(buf, copied);
201
+ free(buf);
202
+ return result;
203
+ }
204
+
205
+ case napi_object: {
206
+ // Check if it's a Buffer
207
+ bool is_buffer;
208
+ status = napi_is_buffer(env, value, &is_buffer);
209
+ if (status == napi_ok && is_buffer) {
210
+ void *data;
211
+ size_t length;
212
+ status = napi_get_buffer_info(env, value, &data, &length);
213
+ if (status != napi_ok) {
214
+ *err = CROUS_ERR_ENCODE;
215
+ return NULL;
216
+ }
217
+ return crous_value_new_bytes((const uint8_t*)data, length);
218
+ }
219
+
220
+ // Check if it's an Array
221
+ bool is_array;
222
+ status = napi_is_array(env, value, &is_array);
223
+ if (status == napi_ok && is_array) {
224
+ uint32_t length;
225
+ status = napi_get_array_length(env, value, &length);
226
+ if (status != napi_ok) {
227
+ *err = CROUS_ERR_ENCODE;
228
+ return NULL;
229
+ }
230
+
231
+ crous_value *list = crous_value_new_list((size_t)length);
232
+ if (!list) {
233
+ *err = CROUS_ERR_OOM;
234
+ return NULL;
235
+ }
236
+
237
+ for (uint32_t i = 0; i < length; i++) {
238
+ napi_value element;
239
+ status = napi_get_element(env, value, i, &element);
240
+ if (status != napi_ok) {
241
+ crous_value_free_tree(list);
242
+ *err = CROUS_ERR_ENCODE;
243
+ return NULL;
244
+ }
245
+
246
+ crous_value *item = napi_to_crous(env, element, default_func, err);
247
+ if (*err != CROUS_OK) {
248
+ crous_value_free_tree(list);
249
+ return NULL;
250
+ }
251
+
252
+ if (crous_value_list_append(list, item) != CROUS_OK) {
253
+ crous_value_free_tree(list);
254
+ crous_value_free_tree(item);
255
+ *err = CROUS_ERR_OOM;
256
+ return NULL;
257
+ }
258
+ }
259
+
260
+ return list;
261
+ }
262
+
263
+ // Check if it's a Set
264
+ napi_value set_constructor;
265
+ napi_value global;
266
+ napi_get_global(env, &global);
267
+ napi_get_named_property(env, global, "Set", &set_constructor);
268
+
269
+ bool is_set = false;
270
+ napi_instanceof(env, value, set_constructor, &is_set);
271
+
272
+ if (is_set) {
273
+ int handled = 0;
274
+ crous_value *result = try_custom_serializer(env, value, default_func, err, &handled);
275
+ if (handled) return result;
276
+
277
+ // Convert Set to Array
278
+ napi_value array_from;
279
+ napi_value array_constructor;
280
+ napi_get_named_property(env, global, "Array", &array_constructor);
281
+ napi_get_named_property(env, array_constructor, "from", &array_from);
282
+
283
+ napi_value as_array;
284
+ napi_call_function(env, global, array_from, 1, &value, &as_array);
285
+
286
+ crous_value *list_val = napi_to_crous(env, as_array, default_func, err);
287
+ if (*err != CROUS_OK) return NULL;
288
+
289
+ crous_value *tagged = crous_value_new_tagged(90, list_val);
290
+ if (!tagged) {
291
+ crous_value_free_tree(list_val);
292
+ *err = CROUS_ERR_OOM;
293
+ return NULL;
294
+ }
295
+ return tagged;
296
+ }
297
+
298
+ // Regular object (dictionary)
299
+ napi_value property_names;
300
+ status = napi_get_property_names(env, value, &property_names);
301
+ if (status != napi_ok) {
302
+ *err = CROUS_ERR_ENCODE;
303
+ return NULL;
304
+ }
305
+
306
+ uint32_t prop_count;
307
+ status = napi_get_array_length(env, property_names, &prop_count);
308
+ if (status != napi_ok) {
309
+ *err = CROUS_ERR_ENCODE;
310
+ return NULL;
311
+ }
312
+
313
+ crous_value *dict = crous_value_new_dict((size_t)prop_count);
314
+ if (!dict) {
315
+ *err = CROUS_ERR_OOM;
316
+ return NULL;
317
+ }
318
+
319
+ for (uint32_t i = 0; i < prop_count; i++) {
320
+ napi_value prop_name;
321
+ status = napi_get_element(env, property_names, i, &prop_name);
322
+ if (status != napi_ok) {
323
+ crous_value_free_tree(dict);
324
+ *err = CROUS_ERR_ENCODE;
325
+ return NULL;
326
+ }
327
+
328
+ size_t key_len;
329
+ status = napi_get_value_string_utf8(env, prop_name, NULL, 0, &key_len);
330
+ if (status != napi_ok) {
331
+ crous_value_free_tree(dict);
332
+ *err = CROUS_ERR_ENCODE;
333
+ return NULL;
334
+ }
335
+
336
+ char *key_buf = (char*)malloc(key_len + 1);
337
+ if (!key_buf) {
338
+ crous_value_free_tree(dict);
339
+ *err = CROUS_ERR_OOM;
340
+ return NULL;
341
+ }
342
+
343
+ size_t copied;
344
+ status = napi_get_value_string_utf8(env, prop_name, key_buf, key_len + 1, &copied);
345
+ if (status != napi_ok) {
346
+ free(key_buf);
347
+ crous_value_free_tree(dict);
348
+ *err = CROUS_ERR_ENCODE;
349
+ return NULL;
350
+ }
351
+
352
+ napi_value prop_value;
353
+ status = napi_get_property(env, value, prop_name, &prop_value);
354
+ if (status != napi_ok) {
355
+ free(key_buf);
356
+ crous_value_free_tree(dict);
357
+ *err = CROUS_ERR_ENCODE;
358
+ return NULL;
359
+ }
360
+
361
+ crous_value *cval = napi_to_crous(env, prop_value, default_func, err);
362
+ if (*err != CROUS_OK) {
363
+ free(key_buf);
364
+ crous_value_free_tree(dict);
365
+ return NULL;
366
+ }
367
+
368
+ if (crous_value_dict_set_binary(dict, key_buf, copied, cval) != CROUS_OK) {
369
+ free(key_buf);
370
+ crous_value_free_tree(dict);
371
+ crous_value_free_tree(cval);
372
+ *err = CROUS_ERR_OOM;
373
+ return NULL;
374
+ }
375
+
376
+ free(key_buf);
377
+ }
378
+
379
+ return dict;
380
+ }
381
+
382
+ default: {
383
+ // Try custom serializer
384
+ int handled = 0;
385
+ crous_value *result = try_custom_serializer(env, value, default_func, err, &handled);
386
+ if (handled) return result;
387
+
388
+ *err = CROUS_ERR_INVALID_TYPE;
389
+ return NULL;
390
+ }
391
+ }
392
+ }
393
+
394
+ /* ============================================================================
395
+ CONVERSION: CROUS VALUE -> NODE.JS VALUE
396
+ ============================================================================ */
397
+
398
+ static napi_value crous_to_napi(napi_env env, const crous_value *v, napi_value object_hook) {
399
+ if (!v) {
400
+ napi_value result;
401
+ napi_get_null(env, &result);
402
+ return result;
403
+ }
404
+
405
+ napi_status status;
406
+ napi_value result;
407
+
408
+ switch (crous_value_get_type(v)) {
409
+ case CROUS_TYPE_NULL:
410
+ napi_get_null(env, &result);
411
+ return result;
412
+
413
+ case CROUS_TYPE_BOOL:
414
+ napi_get_boolean(env, crous_value_get_bool(v), &result);
415
+ return result;
416
+
417
+ case CROUS_TYPE_INT:
418
+ napi_create_int64(env, crous_value_get_int(v), &result);
419
+ return result;
420
+
421
+ case CROUS_TYPE_FLOAT:
422
+ napi_create_double(env, crous_value_get_float(v), &result);
423
+ return result;
424
+
425
+ case CROUS_TYPE_STRING: {
426
+ size_t len;
427
+ const char *data = crous_value_get_string(v, &len);
428
+ napi_create_string_utf8(env, data, len, &result);
429
+ return result;
430
+ }
431
+
432
+ case CROUS_TYPE_BYTES: {
433
+ size_t len;
434
+ const uint8_t *data = crous_value_get_bytes(v, &len);
435
+ void *buffer_data;
436
+ status = napi_create_buffer_copy(env, len, data, &buffer_data, &result);
437
+ if (status != napi_ok) {
438
+ napi_get_null(env, &result);
439
+ }
440
+ return result;
441
+ }
442
+
443
+ case CROUS_TYPE_LIST: {
444
+ size_t size = crous_value_list_size(v);
445
+ napi_create_array_with_length(env, size, &result);
446
+
447
+ for (size_t i = 0; i < size; i++) {
448
+ napi_value element = crous_to_napi(env, crous_value_list_get(v, i), object_hook);
449
+ napi_set_element(env, result, (uint32_t)i, element);
450
+ }
451
+
452
+ return result;
453
+ }
454
+
455
+ case CROUS_TYPE_TUPLE: {
456
+ // Tuples are represented as arrays in JavaScript
457
+ size_t size = crous_value_list_size(v);
458
+ napi_create_array_with_length(env, size, &result);
459
+
460
+ for (size_t i = 0; i < size; i++) {
461
+ napi_value element = crous_to_napi(env, crous_value_list_get(v, i), object_hook);
462
+ napi_set_element(env, result, (uint32_t)i, element);
463
+ }
464
+
465
+ return result;
466
+ }
467
+
468
+ case CROUS_TYPE_DICT: {
469
+ napi_create_object(env, &result);
470
+
471
+ size_t size = crous_value_dict_size(v);
472
+ for (size_t i = 0; i < size; i++) {
473
+ const crous_dict_entry *entry = crous_value_dict_get_entry(v, i);
474
+ if (!entry) continue;
475
+
476
+ napi_value key;
477
+ napi_create_string_utf8(env, entry->key, entry->key_len, &key);
478
+
479
+ napi_value val = crous_to_napi(env, entry->value, object_hook);
480
+ napi_set_property(env, result, key, val);
481
+ }
482
+
483
+ // Apply object_hook if provided
484
+ if (object_hook != NULL) {
485
+ napi_valuetype hook_type;
486
+ napi_typeof(env, object_hook, &hook_type);
487
+ if (hook_type == napi_function) {
488
+ napi_value global;
489
+ napi_get_global(env, &global);
490
+ napi_value hooked_result;
491
+ status = napi_call_function(env, global, object_hook, 1, &result, &hooked_result);
492
+ if (status == napi_ok) {
493
+ return hooked_result;
494
+ }
495
+ }
496
+ }
497
+
498
+ return result;
499
+ }
500
+
501
+ case CROUS_TYPE_TAGGED: {
502
+ uint32_t tag = crous_value_get_tag(v);
503
+ const crous_value *inner = crous_value_get_tagged_inner(v);
504
+
505
+ // Check for built-in tags
506
+ if (tag == 90) {
507
+ // Set
508
+ napi_value array = crous_to_napi(env, inner, object_hook);
509
+ napi_value global;
510
+ napi_get_global(env, &global);
511
+ napi_value set_constructor;
512
+ napi_get_named_property(env, global, "Set", &set_constructor);
513
+ napi_new_instance(env, set_constructor, 1, &array, &result);
514
+ return result;
515
+ }
516
+
517
+ // Check for custom decoder
518
+ if (custom_decoders_ref != NULL) {
519
+ napi_value decoders;
520
+ status = napi_get_reference_value(env, custom_decoders_ref, &decoders);
521
+ if (status == napi_ok) {
522
+ napi_value tag_key;
523
+ napi_create_uint32(env, tag, &tag_key);
524
+
525
+ bool has_decoder;
526
+ status = napi_has_property(env, decoders, tag_key, &has_decoder);
527
+ if (status == napi_ok && has_decoder) {
528
+ napi_value decoder;
529
+ status = napi_get_property(env, decoders, tag_key, &decoder);
530
+ if (status == napi_ok) {
531
+ napi_value inner_js = crous_to_napi(env, inner, object_hook);
532
+ napi_value global;
533
+ napi_get_global(env, &global);
534
+ status = napi_call_function(env, global, decoder, 1, &inner_js, &result);
535
+ if (status == napi_ok) {
536
+ return result;
537
+ }
538
+ }
539
+ }
540
+ }
541
+ }
542
+
543
+ // No decoder found, return inner value
544
+ return crous_to_napi(env, inner, object_hook);
545
+ }
546
+
547
+ default:
548
+ napi_get_null(env, &result);
549
+ return result;
550
+ }
551
+ }
552
+
553
+ /* ============================================================================
554
+ EXPORTED FUNCTIONS
555
+ ============================================================================ */
556
+
557
+ static napi_value dumps(napi_env env, napi_callback_info info) {
558
+ size_t argc = 2;
559
+ napi_value args[2];
560
+ napi_status status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);
561
+
562
+ if (status != napi_ok || argc < 1) {
563
+ napi_throw_type_error(env, NULL, "Wrong number of arguments");
564
+ return NULL;
565
+ }
566
+
567
+ napi_value default_func = (argc > 1) ? args[1] : NULL;
568
+
569
+ crous_err_t err;
570
+ crous_value *value = napi_to_crous(env, args[0], default_func, &err);
571
+ if (!value || err != CROUS_OK) {
572
+ return throw_encode_error(env, crous_err_str(err));
573
+ }
574
+
575
+ uint8_t *buf = NULL;
576
+ size_t size = 0;
577
+ err = crous_encode(value, &buf, &size);
578
+ crous_value_free_tree(value);
579
+
580
+ if (err != CROUS_OK) {
581
+ free(buf);
582
+ return throw_encode_error(env, crous_err_str(err));
583
+ }
584
+
585
+ napi_value result;
586
+ void *result_data;
587
+ status = napi_create_buffer_copy(env, size, buf, &result_data, &result);
588
+ free(buf);
589
+
590
+ if (status != napi_ok) {
591
+ return throw_encode_error(env, "Failed to create buffer");
592
+ }
593
+
594
+ return result;
595
+ }
596
+
597
+ static napi_value loads(napi_env env, napi_callback_info info) {
598
+ size_t argc = 2;
599
+ napi_value args[2];
600
+ napi_status status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);
601
+
602
+ if (status != napi_ok || argc < 1) {
603
+ napi_throw_type_error(env, NULL, "Wrong number of arguments");
604
+ return NULL;
605
+ }
606
+
607
+ bool is_buffer;
608
+ status = napi_is_buffer(env, args[0], &is_buffer);
609
+ if (status != napi_ok || !is_buffer) {
610
+ napi_throw_type_error(env, NULL, "First argument must be a Buffer");
611
+ return NULL;
612
+ }
613
+
614
+ void *data;
615
+ size_t length;
616
+ status = napi_get_buffer_info(env, args[0], &data, &length);
617
+ if (status != napi_ok) {
618
+ return throw_decode_error(env, "Failed to get buffer data");
619
+ }
620
+
621
+ napi_value object_hook = (argc > 1) ? args[1] : NULL;
622
+
623
+ crous_value *value = NULL;
624
+ crous_err_t err = crous_decode((const uint8_t*)data, length, &value);
625
+
626
+ if (err != CROUS_OK) {
627
+ if (value) crous_value_free_tree(value);
628
+ return throw_decode_error(env, crous_err_str(err));
629
+ }
630
+
631
+ napi_value result = crous_to_napi(env, value, object_hook);
632
+ crous_value_free_tree(value);
633
+
634
+ return result;
635
+ }
636
+
637
+ static napi_value register_serializer(napi_env env, napi_callback_info info) {
638
+ size_t argc = 2;
639
+ napi_value args[2];
640
+ napi_status status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);
641
+
642
+ if (status != napi_ok || argc < 2) {
643
+ napi_throw_type_error(env, NULL, "Expected 2 arguments: type and function");
644
+ return NULL;
645
+ }
646
+
647
+ napi_valuetype arg1_type;
648
+ napi_typeof(env, args[1], &arg1_type);
649
+ if (arg1_type != napi_function) {
650
+ napi_throw_type_error(env, NULL, "Second argument must be a function");
651
+ return NULL;
652
+ }
653
+
654
+ // Initialize serializers map if needed
655
+ if (custom_serializers_ref == NULL) {
656
+ napi_value serializers;
657
+ napi_create_object(env, &serializers);
658
+ napi_create_reference(env, serializers, 1, &custom_serializers_ref);
659
+ }
660
+
661
+ napi_value serializers;
662
+ napi_get_reference_value(env, custom_serializers_ref, &serializers);
663
+ napi_set_property(env, serializers, args[0], args[1]);
664
+
665
+ // Initialize type_to_tag if needed
666
+ if (type_to_tag_ref == NULL) {
667
+ napi_value tag_map;
668
+ napi_create_object(env, &tag_map);
669
+ napi_create_reference(env, tag_map, 1, &type_to_tag_ref);
670
+ }
671
+
672
+ // Assign tag
673
+ napi_value tag_map;
674
+ napi_get_reference_value(env, type_to_tag_ref, &tag_map);
675
+ napi_value tag_value;
676
+ napi_create_uint32(env, next_custom_tag++, &tag_value);
677
+ napi_set_property(env, tag_map, args[0], tag_value);
678
+
679
+ napi_value result;
680
+ napi_get_undefined(env, &result);
681
+ return result;
682
+ }
683
+
684
+ static napi_value unregister_serializer(napi_env env, napi_callback_info info) {
685
+ size_t argc = 1;
686
+ napi_value args[1];
687
+ napi_status status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);
688
+
689
+ if (status != napi_ok || argc < 1) {
690
+ napi_throw_type_error(env, NULL, "Expected 1 argument");
691
+ return NULL;
692
+ }
693
+
694
+ if (custom_serializers_ref != NULL) {
695
+ napi_value serializers;
696
+ napi_get_reference_value(env, custom_serializers_ref, &serializers);
697
+ bool deleted;
698
+ napi_delete_property(env, serializers, args[0], &deleted);
699
+ }
700
+
701
+ if (type_to_tag_ref != NULL) {
702
+ napi_value tag_map;
703
+ napi_get_reference_value(env, type_to_tag_ref, &tag_map);
704
+ bool deleted;
705
+ napi_delete_property(env, tag_map, args[0], &deleted);
706
+ }
707
+
708
+ napi_value result;
709
+ napi_get_undefined(env, &result);
710
+ return result;
711
+ }
712
+
713
+ static napi_value register_decoder(napi_env env, napi_callback_info info) {
714
+ size_t argc = 2;
715
+ napi_value args[2];
716
+ napi_status status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);
717
+
718
+ if (status != napi_ok || argc < 2) {
719
+ napi_throw_type_error(env, NULL, "Expected 2 arguments: tag and function");
720
+ return NULL;
721
+ }
722
+
723
+ napi_valuetype arg1_type;
724
+ napi_typeof(env, args[1], &arg1_type);
725
+ if (arg1_type != napi_function) {
726
+ napi_throw_type_error(env, NULL, "Second argument must be a function");
727
+ return NULL;
728
+ }
729
+
730
+ // Initialize decoders map if needed
731
+ if (custom_decoders_ref == NULL) {
732
+ napi_value decoders;
733
+ napi_create_object(env, &decoders);
734
+ napi_create_reference(env, decoders, 1, &custom_decoders_ref);
735
+ }
736
+
737
+ napi_value decoders;
738
+ napi_get_reference_value(env, custom_decoders_ref, &decoders);
739
+ napi_set_property(env, decoders, args[0], args[1]);
740
+
741
+ napi_value result;
742
+ napi_get_undefined(env, &result);
743
+ return result;
744
+ }
745
+
746
+ static napi_value unregister_decoder(napi_env env, napi_callback_info info) {
747
+ size_t argc = 1;
748
+ napi_value args[1];
749
+ napi_status status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);
750
+
751
+ if (status != napi_ok || argc < 1) {
752
+ napi_throw_type_error(env, NULL, "Expected 1 argument");
753
+ return NULL;
754
+ }
755
+
756
+ if (custom_decoders_ref != NULL) {
757
+ napi_value decoders;
758
+ napi_get_reference_value(env, custom_decoders_ref, &decoders);
759
+ bool deleted;
760
+ napi_delete_property(env, decoders, args[0], &deleted);
761
+ }
762
+
763
+ napi_value result;
764
+ napi_get_undefined(env, &result);
765
+ return result;
766
+ }
767
+
768
+ /* ============================================================================
769
+ MODULE INITIALIZATION
770
+ ============================================================================ */
771
+
772
+ static napi_value Init(napi_env env, napi_value exports) {
773
+ napi_status status;
774
+ napi_value fn;
775
+
776
+ // Create dumps function
777
+ status = napi_create_function(env, "dumps", NAPI_AUTO_LENGTH, dumps, NULL, &fn);
778
+ if (status != napi_ok) return NULL;
779
+ status = napi_set_named_property(env, exports, "dumps", fn);
780
+ if (status != napi_ok) return NULL;
781
+
782
+ // Create loads function
783
+ status = napi_create_function(env, "loads", NAPI_AUTO_LENGTH, loads, NULL, &fn);
784
+ if (status != napi_ok) return NULL;
785
+ status = napi_set_named_property(env, exports, "loads", fn);
786
+ if (status != napi_ok) return NULL;
787
+
788
+ // Create register_serializer function
789
+ status = napi_create_function(env, "register_serializer", NAPI_AUTO_LENGTH,
790
+ register_serializer, NULL, &fn);
791
+ if (status != napi_ok) return NULL;
792
+ status = napi_set_named_property(env, exports, "registerSerializer", fn);
793
+ if (status != napi_ok) return NULL;
794
+
795
+ // Create unregister_serializer function
796
+ status = napi_create_function(env, "unregister_serializer", NAPI_AUTO_LENGTH,
797
+ unregister_serializer, NULL, &fn);
798
+ if (status != napi_ok) return NULL;
799
+ status = napi_set_named_property(env, exports, "unregisterSerializer", fn);
800
+ if (status != napi_ok) return NULL;
801
+
802
+ // Create register_decoder function
803
+ status = napi_create_function(env, "register_decoder", NAPI_AUTO_LENGTH,
804
+ register_decoder, NULL, &fn);
805
+ if (status != napi_ok) return NULL;
806
+ status = napi_set_named_property(env, exports, "registerDecoder", fn);
807
+ if (status != napi_ok) return NULL;
808
+
809
+ // Create unregister_decoder function
810
+ status = napi_create_function(env, "unregister_decoder", NAPI_AUTO_LENGTH,
811
+ unregister_decoder, NULL, &fn);
812
+ if (status != napi_ok) return NULL;
813
+ status = napi_set_named_property(env, exports, "unregisterDecoder", fn);
814
+ if (status != napi_ok) return NULL;
815
+
816
+ return exports;
817
+ }
818
+
819
+ NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)