nodenetcdf 4.9.3 → 4.9.31

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.
package/src/Dimension.cpp CHANGED
@@ -25,6 +25,7 @@ void Dimension::Init(v8::Local<v8::Object> exports)
25
25
  tpl->SetClassName(v8::String::NewFromUtf8(isolate, "Dimension", v8::NewStringType::kNormal).ToLocalChecked());
26
26
  tpl->InstanceTemplate()->SetInternalFieldCount(1);
27
27
  NODE_SET_PROTOTYPE_METHOD(tpl, "inspect", Dimension::Inspect);
28
+ NODE_SET_PROTOTYPE_METHOD(tpl, "toJSON", Dimension::ToJSON);
28
29
  tpl->InstanceTemplate()->SetAccessor(
29
30
  v8::String::NewFromUtf8(isolate, "id", v8::NewStringType::kNormal).ToLocalChecked(), Dimension::GetId);
30
31
  tpl->InstanceTemplate()->SetAccessor(
@@ -102,4 +103,39 @@ void Dimension::Inspect(const v8::FunctionCallbackInfo<v8::Value> &args)
102
103
  args.GetReturnValue().Set(
103
104
  v8::String::NewFromUtf8(isolate, "[object Dimension]", v8::NewStringType::kNormal).ToLocalChecked());
104
105
  }
106
+
107
+ void Dimension::ToJSON(const v8::FunctionCallbackInfo<v8::Value> &args)
108
+ {
109
+ v8::Isolate *isolate = args.GetIsolate();
110
+ v8::Local<v8::Context> context = isolate->GetCurrentContext();
111
+ const auto *obj = node::ObjectWrap::Unwrap<Dimension>(args.Holder());
112
+
113
+ // Use internalized strings for better performance
114
+ v8::Local<v8::String> id_str = v8::String::NewFromUtf8Literal(isolate, "id");
115
+ v8::Local<v8::String> name_str = v8::String::NewFromUtf8Literal(isolate, "name");
116
+ v8::Local<v8::String> length_str = v8::String::NewFromUtf8Literal(isolate, "length");
117
+
118
+ v8::Local<v8::Object> json = v8::Object::New(isolate);
119
+
120
+ // Add id
121
+ (void)json->CreateDataProperty(context, id_str, v8::Integer::New(isolate, obj->id));
122
+
123
+ // Add name
124
+ std::array<char, NC_MAX_NAME + 1> name{};
125
+ if (obj->get_name(name.data()))
126
+ {
127
+ (void)json->CreateDataProperty(context, name_str,
128
+ v8::String::NewFromUtf8(isolate, name.data(), v8::NewStringType::kInternalized).ToLocalChecked());
129
+ }
130
+
131
+ // Add length
132
+ size_t len = 0;
133
+ const int retval = nc_inq_dimlen(obj->parent_id, obj->id, &len);
134
+ if (retval == NC_NOERR)
135
+ {
136
+ (void)json->CreateDataProperty(context, length_str, v8::Integer::New(isolate, static_cast<int32_t>(len)));
137
+ }
138
+
139
+ args.GetReturnValue().Set(json);
140
+ }
105
141
  } // namespace nodenetcdfjs
package/src/Dimension.h CHANGED
@@ -30,6 +30,7 @@ class Dimension : public node::ObjectWrap
30
30
  static void SetName(v8::Local<v8::String> property, v8::Local<v8::Value> val,
31
31
  const v8::PropertyCallbackInfo<void> &info);
32
32
  static void Inspect(const v8::FunctionCallbackInfo<v8::Value> &args);
33
+ static void ToJSON(const v8::FunctionCallbackInfo<v8::Value> &args);
33
34
 
34
35
  int id{-1};
35
36
  int parent_id{-1};
package/src/File.cpp CHANGED
@@ -38,6 +38,7 @@ void File::Init(v8::Local<v8::Object> exports)
38
38
  NODE_SET_PROTOTYPE_METHOD(tpl, "sync", File::Sync);
39
39
  NODE_SET_PROTOTYPE_METHOD(tpl, "close", File::Close);
40
40
  NODE_SET_PROTOTYPE_METHOD(tpl, "inspect", File::Inspect);
41
+ NODE_SET_PROTOTYPE_METHOD(tpl, "toJSON", File::ToJSON);
41
42
  constructor.Reset(isolate, tpl->GetFunction(isolate->GetCurrentContext()).ToLocalChecked());
42
43
  exports->Set(isolate->GetCurrentContext(),
43
44
  v8::String::NewFromUtf8(isolate, "File", v8::NewStringType::kNormal).ToLocalChecked(),
@@ -167,4 +168,17 @@ void File::Inspect(const v8::FunctionCallbackInfo<v8::Value> &args)
167
168
  args.GetReturnValue().Set(
168
169
  v8::String::NewFromUtf8(isolate, "[object File]", v8::NewStringType::kNormal).ToLocalChecked());
169
170
  }
171
+
172
+ void File::ToJSON(const v8::FunctionCallbackInfo<v8::Value> &args)
173
+ {
174
+ v8::Isolate *isolate = args.GetIsolate();
175
+ v8::Local<v8::Context> context = isolate->GetCurrentContext();
176
+
177
+ // Get the root group and serialize it
178
+ v8::Local<v8::String> rootProp = v8::String::NewFromUtf8(isolate, "root", v8::NewStringType::kNormal).ToLocalChecked();
179
+ v8::Local<v8::Value> root = args.Holder()->Get(context, rootProp).ToLocalChecked();
180
+
181
+ // The root group's toJSON will automatically serialize all children recursively
182
+ args.GetReturnValue().Set(root);
183
+ }
170
184
  } // namespace nodenetcdfjs
package/src/File.h CHANGED
@@ -30,6 +30,7 @@ class File : public node::ObjectWrap
30
30
  static void Close(const v8::FunctionCallbackInfo<v8::Value> &args);
31
31
  static void Sync(const v8::FunctionCallbackInfo<v8::Value> &args);
32
32
  static void Inspect(const v8::FunctionCallbackInfo<v8::Value> &args);
33
+ static void ToJSON(const v8::FunctionCallbackInfo<v8::Value> &args);
33
34
 
34
35
  static v8::Persistent<v8::Function> constructor;
35
36
 
package/src/Group.cpp CHANGED
@@ -31,6 +31,7 @@ void Group::Init(v8::Local<v8::Object> exports)
31
31
  NODE_SET_PROTOTYPE_METHOD(tpl, "addSubgroup", Group::AddSubgroup);
32
32
  NODE_SET_PROTOTYPE_METHOD(tpl, "addAttribute", Group::AddAttribute);
33
33
  NODE_SET_PROTOTYPE_METHOD(tpl, "inspect", Group::Inspect);
34
+ NODE_SET_PROTOTYPE_METHOD(tpl, "toJSON", Group::ToJSON);
34
35
  tpl->InstanceTemplate()->SetAccessor(
35
36
  v8::String::NewFromUtf8(isolate, "id", v8::NewStringType::kNormal).ToLocalChecked(), Group::GetId);
36
37
  tpl->InstanceTemplate()->SetAccessor(
@@ -244,6 +245,7 @@ void Group::GetId(v8::Local<v8::String> property, const v8::PropertyCallbackInfo
244
245
  void Group::GetVariables(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value> &info)
245
246
  {
246
247
  v8::Isolate *isolate = info.GetIsolate();
248
+ v8::Local<v8::Context> context = isolate->GetCurrentContext();
247
249
  const auto *obj = node::ObjectWrap::Unwrap<Group>(info.Holder());
248
250
 
249
251
  int nvars = 0;
@@ -270,8 +272,8 @@ void Group::GetVariables(v8::Local<v8::String> property, const v8::PropertyCallb
270
272
  auto *v = new Variable(var_ids[i], obj->id);
271
273
  if (v->get_name(name.data()))
272
274
  {
273
- result->Set(isolate->GetCurrentContext(),
274
- v8::String::NewFromUtf8(isolate, name.data(), v8::NewStringType::kNormal).ToLocalChecked(),
275
+ (void)result->CreateDataProperty(context,
276
+ v8::String::NewFromUtf8(isolate, name.data(), v8::NewStringType::kInternalized).ToLocalChecked(),
275
277
  v->handle());
276
278
  }
277
279
  else
@@ -285,6 +287,7 @@ void Group::GetVariables(v8::Local<v8::String> property, const v8::PropertyCallb
285
287
  void Group::GetDimensions(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value> &info)
286
288
  {
287
289
  v8::Isolate *isolate = info.GetIsolate();
290
+ v8::Local<v8::Context> context = isolate->GetCurrentContext();
288
291
  const auto *obj = node::ObjectWrap::Unwrap<Group>(info.Holder());
289
292
 
290
293
  int ndims = 0;
@@ -311,10 +314,8 @@ void Group::GetDimensions(v8::Local<v8::String> property, const v8::PropertyCall
311
314
  auto *d = new Dimension(dim_ids[i], obj->id);
312
315
  if (d->get_name(name.data()))
313
316
  {
314
- // Suppress warning about unused return value
315
- (void)result->Set(
316
- isolate->GetCurrentContext(),
317
- v8::String::NewFromUtf8(isolate, name.data(), v8::NewStringType::kNormal).ToLocalChecked(),
317
+ (void)result->CreateDataProperty(context,
318
+ v8::String::NewFromUtf8(isolate, name.data(), v8::NewStringType::kInternalized).ToLocalChecked(),
318
319
  d->handle());
319
320
  }
320
321
  else
@@ -326,6 +327,7 @@ void Group::GetDimensions(v8::Local<v8::String> property, const v8::PropertyCall
326
327
  void Group::GetUnlimited(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value> &info)
327
328
  {
328
329
  v8::Isolate *isolate = info.GetIsolate();
330
+ v8::Local<v8::Context> context = isolate->GetCurrentContext();
329
331
  const auto *obj = node::ObjectWrap::Unwrap<Group>(info.Holder());
330
332
 
331
333
  int ndims = 0;
@@ -352,9 +354,8 @@ void Group::GetUnlimited(v8::Local<v8::String> property, const v8::PropertyCallb
352
354
  auto *d = new Dimension(dim_ids[i], obj->id);
353
355
  if (d->get_name(name.data()))
354
356
  {
355
- // Suppress warning about unused return value
356
- (void)result->Set(isolate->GetCurrentContext(),
357
- v8::String::NewFromUtf8(isolate, name.data(), v8::NewStringType::kNormal).ToLocalChecked(),
357
+ (void)result->CreateDataProperty(context,
358
+ v8::String::NewFromUtf8(isolate, name.data(), v8::NewStringType::kInternalized).ToLocalChecked(),
358
359
  d->handle());
359
360
  }
360
361
  else return;
@@ -365,6 +366,7 @@ void Group::GetUnlimited(v8::Local<v8::String> property, const v8::PropertyCallb
365
366
  void Group::GetAttributes(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value> &info)
366
367
  {
367
368
  v8::Isolate *isolate = info.GetIsolate();
369
+ v8::Local<v8::Context> context = isolate->GetCurrentContext();
368
370
  const auto *obj = node::ObjectWrap::Unwrap<Group>(info.Holder());
369
371
 
370
372
  int natts = 0;
@@ -387,8 +389,8 @@ void Group::GetAttributes(v8::Local<v8::String> property, const v8::PropertyCall
387
389
  return;
388
390
  }
389
391
  auto *a = new Attribute(name.data(), NC_GLOBAL, obj->id);
390
- result->Set(isolate->GetCurrentContext(),
391
- v8::String::NewFromUtf8(isolate, name.data(), v8::NewStringType::kNormal).ToLocalChecked(),
392
+ (void)result->CreateDataProperty(context,
393
+ v8::String::NewFromUtf8(isolate, name.data(), v8::NewStringType::kInternalized).ToLocalChecked(),
392
394
  a->handle());
393
395
  }
394
396
  info.GetReturnValue().Set(result);
@@ -397,6 +399,7 @@ void Group::GetAttributes(v8::Local<v8::String> property, const v8::PropertyCall
397
399
  void Group::GetSubgroups(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value> &info)
398
400
  {
399
401
  v8::Isolate *isolate = info.GetIsolate();
402
+ v8::Local<v8::Context> context = isolate->GetCurrentContext();
400
403
  const auto *obj = node::ObjectWrap::Unwrap<Group>(info.Holder());
401
404
 
402
405
  int ngrps = 0;
@@ -423,9 +426,8 @@ void Group::GetSubgroups(v8::Local<v8::String> property, const v8::PropertyCallb
423
426
  auto *g = new Group(grp_ids[i]);
424
427
  if (g->get_name(name.data()))
425
428
  {
426
- // Suppress warning about unused return value
427
- (void)result->Set(isolate->GetCurrentContext(),
428
- v8::String::NewFromUtf8(isolate, name.data(), v8::NewStringType::kNormal).ToLocalChecked(),
429
+ (void)result->CreateDataProperty(context,
430
+ v8::String::NewFromUtf8(isolate, name.data(), v8::NewStringType::kInternalized).ToLocalChecked(),
429
431
  g->handle());
430
432
  }
431
433
  else
@@ -480,4 +482,75 @@ void Group::Inspect(const v8::FunctionCallbackInfo<v8::Value> &args)
480
482
  args.GetReturnValue().Set(
481
483
  v8::String::NewFromUtf8(isolate, "[object Group]", v8::NewStringType::kNormal).ToLocalChecked());
482
484
  }
485
+
486
+ void Group::ToJSON(const v8::FunctionCallbackInfo<v8::Value> &args)
487
+ {
488
+ v8::Isolate *isolate = args.GetIsolate();
489
+ v8::Local<v8::Context> context = isolate->GetCurrentContext();
490
+ const auto *obj = node::ObjectWrap::Unwrap<Group>(args.Holder());
491
+
492
+ // Use internalized strings for better performance
493
+ v8::Local<v8::String> id_str = v8::String::NewFromUtf8Literal(isolate, "id");
494
+ v8::Local<v8::String> name_str = v8::String::NewFromUtf8Literal(isolate, "name");
495
+ v8::Local<v8::String> fullname_str = v8::String::NewFromUtf8Literal(isolate, "fullname");
496
+ v8::Local<v8::String> dimensions_str = v8::String::NewFromUtf8Literal(isolate, "dimensions");
497
+ v8::Local<v8::String> variables_str = v8::String::NewFromUtf8Literal(isolate, "variables");
498
+ v8::Local<v8::String> attributes_str = v8::String::NewFromUtf8Literal(isolate, "attributes");
499
+ v8::Local<v8::String> subgroups_str = v8::String::NewFromUtf8Literal(isolate, "subgroups");
500
+
501
+ v8::Local<v8::Object> json = v8::Object::New(isolate);
502
+
503
+ // Add id
504
+ (void)json->CreateDataProperty(context, id_str, v8::Integer::New(isolate, obj->id));
505
+
506
+ // Add name
507
+ std::array<char, NC_MAX_NAME + 1> name{};
508
+ if (obj->get_name(name.data()))
509
+ {
510
+ (void)json->CreateDataProperty(context, name_str,
511
+ v8::String::NewFromUtf8(isolate, name.data(), v8::NewStringType::kInternalized).ToLocalChecked());
512
+ }
513
+
514
+ // Add fullname
515
+ size_t len = 0;
516
+ int retval = nc_inq_grpname_len(obj->id, &len);
517
+ if (retval == NC_NOERR)
518
+ {
519
+ std::vector<char> fullname(len + 1, '\0');
520
+ retval = nc_inq_grpname_full(obj->id, nullptr, fullname.data());
521
+ if (retval == NC_NOERR)
522
+ {
523
+ (void)json->CreateDataProperty(context, fullname_str,
524
+ v8::String::NewFromUtf8(isolate, fullname.data(), v8::NewStringType::kInternalized).ToLocalChecked());
525
+ }
526
+ }
527
+
528
+ // Add dimensions, variables, attributes, subgroups
529
+ // These will be accessed through property getters which trigger the respective Get* methods
530
+ v8::Local<v8::Value> dimensions = args.Holder()->Get(context, dimensions_str).ToLocalChecked();
531
+ if (dimensions->IsObject())
532
+ {
533
+ (void)json->CreateDataProperty(context, dimensions_str, dimensions);
534
+ }
535
+
536
+ v8::Local<v8::Value> variables = args.Holder()->Get(context, variables_str).ToLocalChecked();
537
+ if (variables->IsObject())
538
+ {
539
+ (void)json->CreateDataProperty(context, variables_str, variables);
540
+ }
541
+
542
+ v8::Local<v8::Value> attributes = args.Holder()->Get(context, attributes_str).ToLocalChecked();
543
+ if (attributes->IsObject())
544
+ {
545
+ (void)json->CreateDataProperty(context, attributes_str, attributes);
546
+ }
547
+
548
+ v8::Local<v8::Value> subgroups = args.Holder()->Get(context, subgroups_str).ToLocalChecked();
549
+ if (subgroups->IsObject())
550
+ {
551
+ (void)json->CreateDataProperty(context, subgroups_str, subgroups);
552
+ }
553
+
554
+ args.GetReturnValue().Set(json);
555
+ }
483
556
  } // namespace nodenetcdfjs
package/src/Group.h CHANGED
@@ -40,6 +40,7 @@ class Group : public node::ObjectWrap
40
40
  static void AddSubgroup(const v8::FunctionCallbackInfo<v8::Value> &args);
41
41
  static void AddVariable(const v8::FunctionCallbackInfo<v8::Value> &args);
42
42
  static void Inspect(const v8::FunctionCallbackInfo<v8::Value> &args);
43
+ static void ToJSON(const v8::FunctionCallbackInfo<v8::Value> &args);
43
44
 
44
45
  int id{-1};
45
46
  };
package/src/Variable.cpp CHANGED
@@ -42,6 +42,7 @@ void Variable::Init(v8::Local<v8::Object> exports)
42
42
  NODE_SET_PROTOTYPE_METHOD(tpl, "writeStridedSlice", Variable::WriteStridedSlice);
43
43
  NODE_SET_PROTOTYPE_METHOD(tpl, "addAttribute", Variable::AddAttribute);
44
44
  NODE_SET_PROTOTYPE_METHOD(tpl, "inspect", Variable::Inspect);
45
+ NODE_SET_PROTOTYPE_METHOD(tpl, "toJSON", Variable::ToJSON);
45
46
  tpl->InstanceTemplate()->SetAccessor(
46
47
  v8::String::NewFromUtf8(isolate, "id", v8::NewStringType::kNormal).ToLocalChecked(), Variable::GetId);
47
48
  tpl->InstanceTemplate()->SetAccessor(
@@ -678,6 +679,7 @@ void Variable::GetDimensions(v8::Local<v8::String> property, const v8::PropertyC
678
679
  void Variable::GetAttributes(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value> &info)
679
680
  {
680
681
  v8::Isolate *isolate = info.GetIsolate();
682
+ v8::Local<v8::Context> context = isolate->GetCurrentContext();
681
683
  Variable *obj = node::ObjectWrap::Unwrap<Variable>(info.Holder());
682
684
  int natts;
683
685
  int retval = nc_inq_varnatts(obj->parent_id, obj->id, &natts);
@@ -697,8 +699,8 @@ void Variable::GetAttributes(v8::Local<v8::String> property, const v8::PropertyC
697
699
  return;
698
700
  }
699
701
  Attribute *a = new Attribute(name, obj->id, obj->parent_id);
700
- result->Set(isolate->GetCurrentContext(),
701
- v8::String::NewFromUtf8(isolate, name, v8::NewStringType::kNormal).ToLocalChecked(), a->handle());
702
+ (void)result->CreateDataProperty(context,
703
+ v8::String::NewFromUtf8(isolate, name, v8::NewStringType::kInternalized).ToLocalChecked(), a->handle());
702
704
  }
703
705
  info.GetReturnValue().Set(result);
704
706
  }
@@ -1349,4 +1351,52 @@ void Variable::Inspect(const v8::FunctionCallbackInfo<v8::Value> &args)
1349
1351
  args.GetReturnValue().Set(
1350
1352
  v8::String::NewFromUtf8(isolate, "[object Variable]", v8::NewStringType::kNormal).ToLocalChecked());
1351
1353
  }
1354
+
1355
+ void Variable::ToJSON(const v8::FunctionCallbackInfo<v8::Value> &args)
1356
+ {
1357
+ v8::Isolate *isolate = args.GetIsolate();
1358
+ v8::Local<v8::Context> context = isolate->GetCurrentContext();
1359
+ const auto *obj = node::ObjectWrap::Unwrap<Variable>(args.Holder());
1360
+
1361
+ // Use internalized strings for better performance
1362
+ v8::Local<v8::String> id_str = v8::String::NewFromUtf8Literal(isolate, "id");
1363
+ v8::Local<v8::String> name_str = v8::String::NewFromUtf8Literal(isolate, "name");
1364
+ v8::Local<v8::String> type_str = v8::String::NewFromUtf8Literal(isolate, "type");
1365
+ v8::Local<v8::String> dimensions_str = v8::String::NewFromUtf8Literal(isolate, "dimensions");
1366
+ v8::Local<v8::String> attributes_str = v8::String::NewFromUtf8Literal(isolate, "attributes");
1367
+
1368
+ v8::Local<v8::Object> json = v8::Object::New(isolate);
1369
+
1370
+ // Add id
1371
+ (void)json->CreateDataProperty(context, id_str, v8::Integer::New(isolate, obj->id));
1372
+
1373
+ // Add name
1374
+ char name[NC_MAX_NAME + 1];
1375
+ if (obj->get_name(name))
1376
+ {
1377
+ (void)json->CreateDataProperty(context, name_str,
1378
+ v8::String::NewFromUtf8(isolate, name, v8::NewStringType::kInternalized).ToLocalChecked());
1379
+ }
1380
+
1381
+ // Add type
1382
+ const char *type_name = (obj->type < NC_BYTE || obj->type > NC_UINT) ? "unknown" : type_names[obj->type];
1383
+ (void)json->CreateDataProperty(context, type_str,
1384
+ v8::String::NewFromUtf8(isolate, type_name, v8::NewStringType::kInternalized).ToLocalChecked());
1385
+
1386
+ // Add dimensions array with serialized dimension objects
1387
+ v8::Local<v8::Value> dimensions = args.Holder()->Get(context, dimensions_str).ToLocalChecked();
1388
+ if (dimensions->IsArray())
1389
+ {
1390
+ (void)json->CreateDataProperty(context, dimensions_str, dimensions);
1391
+ }
1392
+
1393
+ // Add attributes object with serialized attribute objects
1394
+ v8::Local<v8::Value> attributes = args.Holder()->Get(context, attributes_str).ToLocalChecked();
1395
+ if (attributes->IsObject())
1396
+ {
1397
+ (void)json->CreateDataProperty(context, attributes_str, attributes);
1398
+ }
1399
+
1400
+ args.GetReturnValue().Set(json);
1401
+ }
1352
1402
  } // namespace nodenetcdfjs
package/src/Variable.h CHANGED
@@ -68,6 +68,7 @@ class Variable : public node::ObjectWrap
68
68
  static void SetCompressionLevel(v8::Local<v8::String> property, v8::Local<v8::Value> val,
69
69
  const v8::PropertyCallbackInfo<void> &info);
70
70
  static void Inspect(const v8::FunctionCallbackInfo<v8::Value> &args);
71
+ static void ToJSON(const v8::FunctionCallbackInfo<v8::Value> &args);
71
72
 
72
73
  static v8::Persistent<v8::Function> constructor;
73
74
 
package/test/attribute.js CHANGED
@@ -33,4 +33,66 @@ describe('Attribute', function() {
33
33
  expect(attributes["name"].value).to.equal("air_pressure");
34
34
  });
35
35
  });
36
+
37
+ describe('toJSON', function() {
38
+ it('should serialize attribute without crashing', function() {
39
+ var file = new nodenetcdf.File("test/test_hgroups.nc", "r");
40
+ var attributes = file.root.subgroups["mozaic_flight_2012030419144751_ascent"].attributes;
41
+ var attr = attributes["airport_dep"];
42
+
43
+ // Explicitly call toJSON to test the V8 ToLocalChecked fix
44
+ var json = attr.toJSON();
45
+
46
+ expect(json).to.be.an('object');
47
+ expect(json).to.have.property('name', 'airport_dep');
48
+ expect(json).to.have.property('value', 'FRA');
49
+ });
50
+
51
+ it('should handle variable attribute toJSON without crashing', function() {
52
+ var file = new nodenetcdf.File("test/test_hgroups.nc", "r");
53
+ var attributes = file.root.subgroups["mozaic_flight_2012030419144751_ascent"].variables["air_press"].attributes;
54
+ var attr = attributes["name"];
55
+
56
+ // Explicitly call toJSON to test the V8 ToLocalChecked fix
57
+ var json = attr.toJSON();
58
+
59
+ expect(json).to.be.an('object');
60
+ expect(json).to.have.property('name', 'name');
61
+ expect(json).to.have.property('value', 'air_pressure');
62
+ });
63
+
64
+ it('should work with JSON.stringify', function() {
65
+ var file = new nodenetcdf.File("test/test_hgroups.nc", "r");
66
+ var attributes = file.root.subgroups["mozaic_flight_2012030419144751_ascent"].attributes;
67
+ var attr = attributes["airport_dep"];
68
+
69
+ // Test that JSON.stringify (which calls toJSON internally) works
70
+ var jsonString = JSON.stringify(attr);
71
+ var json = JSON.parse(jsonString);
72
+
73
+ expect(json).to.be.an('object');
74
+ expect(json).to.have.property('name', 'airport_dep');
75
+ expect(json).to.have.property('value', 'FRA');
76
+ });
77
+
78
+ it('should handle errors gracefully and return null value on type conversion errors', function() {
79
+ var file = new nodenetcdf.File("test/test_hgroups.nc", "r");
80
+ var attributes = file.root.subgroups["mozaic_flight_2012030419144751_ascent"].attributes;
81
+
82
+ // Iterate through all attributes to find any that might have type issues
83
+ // The toJSON method should handle these gracefully
84
+ for (var attrName in attributes) {
85
+ var attr = attributes[attrName];
86
+
87
+ // This should not crash even if there are type conversion errors
88
+ var json = attr.toJSON();
89
+
90
+ expect(json).to.be.an('object');
91
+ expect(json).to.have.property('name');
92
+ expect(json).to.have.property('value');
93
+ // Value should either be a valid value or null if there was an error
94
+ expect(json.value === null || typeof json.value !== 'undefined').to.be.true;
95
+ }
96
+ });
97
+ });
36
98
  });
package/test/json.js ADDED
@@ -0,0 +1,140 @@
1
+ var expect = require("chai").expect,
2
+ nodenetcdf = require("../build/Release/nodenetcdf.node");
3
+
4
+ describe('JSON Serialization', function() {
5
+ var file;
6
+
7
+ beforeEach(function() {
8
+ file = new nodenetcdf.File("test/testrh.nc", "r");
9
+ });
10
+
11
+ afterEach(function() {
12
+ file.close();
13
+ });
14
+
15
+ describe('Dimension', function() {
16
+ it('should serialize dimension to JSON', function() {
17
+ var dim = file.root.dimensions['dim1'];
18
+ var json = JSON.parse(JSON.stringify(dim));
19
+
20
+ expect(json).to.be.an('object');
21
+ expect(json).to.have.property('id');
22
+ expect(json).to.have.property('name', 'dim1');
23
+ expect(json).to.have.property('length', 10000);
24
+ });
25
+
26
+ it('should have all required properties in JSON', function() {
27
+ var dim = file.root.dimensions['dim1'];
28
+ var json = JSON.parse(JSON.stringify(dim));
29
+
30
+ expect(json.id).to.be.a('number');
31
+ expect(json.name).to.be.a('string');
32
+ expect(json.length).to.be.a('number');
33
+ });
34
+ });
35
+
36
+ describe('Attribute', function() {
37
+ it('should serialize group attribute to JSON', function() {
38
+ var attributes = file.root.attributes;
39
+ for (var attrName in attributes) {
40
+ var attr = attributes[attrName];
41
+ var json = JSON.parse(JSON.stringify(attr));
42
+
43
+ expect(json).to.be.an('object');
44
+ expect(json).to.have.property('name');
45
+ expect(json).to.have.property('value');
46
+ return; // Test first attribute
47
+ }
48
+ });
49
+
50
+ it('should serialize variable attribute to JSON', function() {
51
+ var variables = file.root.variables;
52
+ for (var varName in variables) {
53
+ var variable = variables[varName];
54
+ var varAttrs = variable.attributes;
55
+ for (var attrName in varAttrs) {
56
+ var attr = varAttrs[attrName];
57
+ var json = JSON.parse(JSON.stringify(attr));
58
+
59
+ expect(json).to.be.an('object');
60
+ expect(json).to.have.property('name');
61
+ expect(json).to.have.property('value');
62
+ return; // Test first attribute
63
+ }
64
+ }
65
+ });
66
+ });
67
+
68
+ describe('Variable', function() {
69
+ it('should serialize variable to JSON', function() {
70
+ var variable = file.root.variables['var1'];
71
+ var json = JSON.parse(JSON.stringify(variable));
72
+
73
+ expect(json).to.be.an('object');
74
+ expect(json).to.have.property('id');
75
+ expect(json).to.have.property('name', 'var1');
76
+ expect(json).to.have.property('type', 'double');
77
+ });
78
+
79
+ it('should have all required properties in JSON', function() {
80
+ var variable = file.root.variables['var1'];
81
+ var json = JSON.parse(JSON.stringify(variable));
82
+
83
+ expect(json.id).to.be.a('number');
84
+ expect(json.name).to.be.a('string');
85
+ expect(json.type).to.be.a('string');
86
+ });
87
+ });
88
+
89
+ describe('Group', function() {
90
+ it('should serialize group to JSON', function() {
91
+ var group = file.root;
92
+ var json = JSON.parse(JSON.stringify(group));
93
+
94
+ expect(json).to.be.an('object');
95
+ expect(json).to.have.property('id');
96
+ expect(json).to.have.property('name');
97
+ expect(json).to.have.property('fullname');
98
+ });
99
+
100
+ it('should have all required properties in JSON', function() {
101
+ var group = file.root;
102
+ var json = JSON.parse(JSON.stringify(group));
103
+
104
+ expect(json.id).to.be.a('number');
105
+ expect(json.name).to.be.a('string');
106
+ expect(json.fullname).to.be.a('string');
107
+ });
108
+
109
+ it('should recursively serialize all children', function() {
110
+ var group = file.root;
111
+ var json = JSON.parse(JSON.stringify(group));
112
+
113
+ expect(json).to.have.property('dimensions');
114
+ expect(json).to.have.property('variables');
115
+ expect(json).to.have.property('attributes');
116
+ expect(json).to.have.property('subgroups');
117
+ });
118
+ });
119
+
120
+ describe('File', function() {
121
+ it('should serialize file to JSON', function() {
122
+ var json = JSON.parse(JSON.stringify(file));
123
+
124
+ expect(json).to.be.an('object');
125
+ expect(json).to.have.property('id');
126
+ expect(json).to.have.property('name');
127
+ expect(json).to.have.property('fullname');
128
+ });
129
+
130
+ it('should recursively serialize entire file structure', function() {
131
+ var json = JSON.parse(JSON.stringify(file));
132
+
133
+ // File should serialize as root group with all children
134
+ expect(json).to.have.property('dimensions');
135
+ expect(json).to.have.property('variables');
136
+ expect(json.variables).to.be.an('object');
137
+ expect(json.dimensions).to.be.an('object');
138
+ });
139
+ });
140
+ });
@@ -0,0 +1,87 @@
1
+ // TypeScript test file to verify type definitions
2
+ import { File, Group, Variable, Dimension, Attribute, FileMode, FileFormat, NetCDFDataType } from '../index';
3
+
4
+ // Test file creation
5
+ const file1 = new File('test.nc', 'c!', 'nodenetcdf');
6
+ const file2 = new File('test2.nc', 'r');
7
+
8
+ // Test root group access
9
+ const root: Group = file1.root;
10
+
11
+ // Test adding dimensions
12
+ const timeDim: Dimension = root.addDimension('time', 0); // unlimited
13
+ const latDim: Dimension = root.addDimension('lat', 180);
14
+ const lonDim: Dimension = root.addDimension('lon', 360);
15
+
16
+ // Test dimension properties
17
+ const dimId: number = timeDim.id;
18
+ const dimLength: number = latDim.length;
19
+ let dimName: string = lonDim.name;
20
+ dimName = 'longitude'; // should be settable
21
+ lonDim.name = 'longitude';
22
+
23
+ // Test adding variables
24
+ const tempVar: Variable = root.addVariable('temperature', 'float', [timeDim, latDim, lonDim]);
25
+ const presVar: Variable = root.addVariable('pressure', 'double', ['time', 'lat', 'lon']);
26
+
27
+ // Test variable properties
28
+ const varId: number = tempVar.id;
29
+ const varType: NetCDFDataType = tempVar.type;
30
+ const varDims: Dimension[] = tempVar.dimensions;
31
+ const varAttrs: { [name: string]: Attribute } = tempVar.attributes;
32
+
33
+ // Test variable settings
34
+ tempVar.name = 'temp';
35
+ tempVar.endianness = 'little';
36
+ tempVar.checksumMode = 'fletcher32';
37
+ tempVar.chunkMode = 'chunked';
38
+ tempVar.chunkSizes = [1, 90, 180];
39
+ tempVar.fillMode = 'fill';
40
+ tempVar.fillValue = -999.0;
41
+ tempVar.compressionShuffle = true;
42
+ tempVar.compressionDeflate = true;
43
+ tempVar.compressionLevel = 6;
44
+
45
+ // Test reading and writing
46
+ const data: any = tempVar.read();
47
+ tempVar.write([1, 2, 3, 4, 5]);
48
+
49
+ const sliceData: any = tempVar.readSlice([0, 0, 0], [1, 10, 10]);
50
+ tempVar.writeSlice([0, 0, 0], [1, 10, 10], sliceData);
51
+
52
+ const stridedData: any = tempVar.readStridedSlice([0, 0, 0], [1, 90, 180], [1, 2, 2]);
53
+ tempVar.writeStridedSlice([0, 0, 0], [1, 90, 180], [1, 2, 2], stridedData);
54
+
55
+ // Test attributes
56
+ const attr1: Attribute = tempVar.addAttribute('units', 'string', 'degrees_C');
57
+ const attr2: Attribute = root.addAttribute('title', 'string', 'Test NetCDF File');
58
+
59
+ const attrName: string = attr1.name;
60
+ const attrValue: any = attr1.value;
61
+ attr1.name = 'unit';
62
+ attr1.value = 'Celsius';
63
+ attr1.delete();
64
+
65
+ // Test subgroups
66
+ const subgroup: Group = root.addSubgroup('forecast');
67
+ const subgroupId: number = subgroup.id;
68
+ const subgroupVars: { [name: string]: Variable } = subgroup.variables;
69
+ const subgroupDims: { [name: string]: Dimension } = subgroup.dimensions;
70
+ const subgroupAttrs: { [name: string]: Attribute } = subgroup.attributes;
71
+ const subgroupSubgroups: { [name: string]: Group } = subgroup.subgroups;
72
+ const subgroupName: string = subgroup.name;
73
+ const subgroupFullname: string = subgroup.fullname;
74
+ const unlimitedDim: Dimension | null = subgroup.unlimited;
75
+
76
+ // Test file operations
77
+ file1.sync();
78
+ file1.close();
79
+
80
+ // Test inspect methods
81
+ const fileInspect: string = file1.inspect();
82
+ const groupInspect: string = root.inspect();
83
+ const varInspect: string = tempVar.inspect();
84
+ const dimInspect: string = timeDim.inspect();
85
+ const attrInspect: string = attr2.inspect();
86
+
87
+ console.log('TypeScript type checking passed!');