node_to_json 0.1.1__py3-none-any.whl

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,452 @@
1
+ from .type_util import PYDict, BNode, BGroup
2
+ from .node_registry import register_node_setter, bpy_types_funcs, BtypeFn
3
+
4
+
5
+ set_node_exclude = ['type', 'parent', 'inputs', 'outputs', 'bl_idname', 'object', 'image', 'material', 'scene']
6
+
7
+
8
+ #### HELPER FUNCS ####
9
+
10
+
11
+ def set_node_attr(node: BNode, attr: PYDict, *args: str) -> None:
12
+ for a in attr:
13
+ if not isinstance(attr[a], dict):
14
+ try:
15
+ if hasattr(node, a) and not isinstance(attr[a], type(None)) and a not in set_node_exclude + list(args):
16
+ if a == 'location' and attr['parent'] in [None, '']:
17
+ continue
18
+ else:
19
+ setattr(node, a, attr[a])
20
+ except Exception as e:
21
+ continue
22
+
23
+
24
+ def set_nested_attr(node: BNode, attr: str, data: PYDict, *args: str) -> object | None:
25
+ if hasattr(node, attr):
26
+ _attr = getattr(node, attr, None)
27
+ if _attr:
28
+ set_node_attr(_attr, data[attr], *args)
29
+ return _attr
30
+ return
31
+
32
+
33
+ def set_listed_attr(node: BNode, attr: str, data: PYDict) -> object:
34
+ if hasattr(node, attr):
35
+ _attr = getattr(node, attr, None)
36
+ if _attr:
37
+ _attr.clear()
38
+ if data[attr] not in [None, [], '']:
39
+ for idx, i in enumerate(data[attr]):
40
+ _attr.new(i['socket_type'], i['name'])
41
+ for a in i:
42
+ if hasattr(_attr, a) and not isinstance(data[attr][idx][a], type(None)) and a not in ['socket_type', 'name']:
43
+ setattr(_attr, a, data[attr][idx][a])
44
+ return _attr
45
+
46
+
47
+ def set_enum_attr(node: BNode, attr: str, data: PYDict) -> object:
48
+ if hasattr(node, attr):
49
+ _attr = getattr(node, attr, None)
50
+ if _attr:
51
+ _attr.clear()
52
+ for idx, i in enumerate(data[attr]):
53
+ _attr.new(i['name'])
54
+ for a in i:
55
+ if hasattr(_attr, a) and not isinstance(data[attr][idx][a], type(None)) and a not in ['name']:
56
+ setattr(_attr, a, data[attr][idx][a])
57
+ return _attr
58
+
59
+
60
+ def set_capture_listed_attr(node: BNode, attr: str, data: PYDict) -> object:
61
+ if hasattr(node, attr):
62
+ _attr = getattr(node, attr, None)
63
+ if _attr:
64
+ _attr.clear()
65
+ for idx, i in enumerate(data[attr]):
66
+ _attr.new(i['data_type'], i['name'])
67
+ for a in i:
68
+ if hasattr(_attr, a) and not isinstance(data[attr][idx][a], type(None)) and a not in ['data_type', 'name']:
69
+ setattr(_attr, a, data[attr][idx][a])
70
+ return _attr
71
+
72
+
73
+ def set_bpy_collection(ob: BNode, attr: PYDict, *default_data: float) -> None:
74
+ for i in range(len(ob) - 1):
75
+ ob.remove(ob[i])
76
+ if hasattr(ob, 'update'):
77
+ ob.update()
78
+ for i in range(len(attr) - 1):
79
+ ob.new(*default_data)
80
+ if hasattr(ob, 'update'):
81
+ ob.update()
82
+ for idx, e in enumerate(attr):
83
+ for a in e:
84
+ if hasattr(ob[idx], a) and not isinstance(attr[idx][a], type(None)):
85
+ try:
86
+ setattr(ob[idx], a, attr[idx][a])
87
+ except Exception as e_:
88
+ continue
89
+
90
+
91
+ def set_loop_items_attr(node: BNode, attr: str, data: PYDict) -> None:
92
+ _items = getattr(node, attr, None)
93
+ if _items:
94
+ _items.clear()
95
+ for a in data[attr]:
96
+ ii = _items.new(a['socket_type'], a['name'])
97
+ if hasattr(ii, 'structure_type') and 'structure_type' in list(a.keys()):
98
+ ii.structure_type = a['structure_type']
99
+
100
+
101
+ def set_node_from_data(node: BNode, attr: PYDict, group_dict: dict[str, BGroup]) -> BtypeFn | None:
102
+ """Set attributes for a node from data.
103
+
104
+ :param node: Node to set data to.
105
+ :type node: Node
106
+ :param attr: Data used to set attributes.
107
+ :type attr: dict[str, Any]
108
+ :param group_dict: Dict of all node groups in the main node group.
109
+ :type group_dict: dict[str, Node Group]
110
+ :return: Function to set node attributes.
111
+ :rtype: Function | None"""
112
+ _func = bpy_types_funcs.get(node.bl_idname)
113
+ if _func is None:
114
+ try:
115
+ if node.bl_idname in ['GeometryNodeCustomGroup', 'GeometryNodeGroup', 'ShaderNodeCustomGroup', 'ShaderNodeGroup', 'NodeGroup', 'TextureNodeGroup', 'CompositorNodeCustomGroup', 'CompositorNodeGroup']:
116
+ set_geo_group(node, attr, group_dict)
117
+ else:
118
+ set_node_attr(node, attr)
119
+ except Exception as e:
120
+ pass
121
+ else:
122
+ _func(node, attr)
123
+
124
+
125
+ #######################################################################################################
126
+
127
+
128
+ #### COLOR RAMP ####
129
+ def set_color_ramp_attr(node, attr):
130
+ color_ramp = getattr(node, 'color_ramp', None)
131
+ if color_ramp:
132
+ set_node_attr(color_ramp, attr['color_ramp'], 'elements')
133
+ elements = getattr(color_ramp, 'elements', None)
134
+ if elements:
135
+ set_bpy_collection(elements, attr['color_ramp']['elements'], 0.0)
136
+ elements.update()
137
+
138
+
139
+ #### IMAGE USER ####
140
+ def set_img_user_attr(node, attr):
141
+ set_nested_attr(node, 'image_user', attr)
142
+
143
+
144
+ #### TEXTURE MAPPING ####
145
+ def set_tex_mapping_attr(node, attr):
146
+ set_nested_attr(node, 'texture_mapping', attr)
147
+
148
+
149
+ #### COLOR MAPPING ####
150
+ def set_col_mapping_attr(node, attr):
151
+ color_mapping = getattr(node, 'color_mapping', None)
152
+ if color_mapping:
153
+ set_node_attr(color_mapping, attr['color_mapping'], 'color_ramp', 'elements')
154
+ if attr['color_mapping']['use_color_ramp']:
155
+ set_color_ramp_attr(color_mapping, attr['color_mapping'])
156
+
157
+
158
+ #### CURVE MAPPING ####
159
+ def set_curve_mapping_attr(node, attr, mapping_='mapping'):
160
+ mapping = set_nested_attr(node, mapping_, attr, 'curves')
161
+ if mapping:
162
+ curves = getattr(mapping, 'curves', None)
163
+ if curves:
164
+ ct = len(curves)
165
+ for idx, pt in enumerate(attr[mapping_]['curves']):
166
+ if idx < ct:
167
+ points = curves[idx].points
168
+ pct = len(points)
169
+ _pct = len(attr[mapping_]['curves'][idx]['points'])
170
+ _ct = pct - _pct
171
+ for p in range(_ct):
172
+ try:
173
+ points.remove(points[p])
174
+ except Exception as e:
175
+ continue
176
+ points.update()
177
+ for p in range(abs(_ct)):
178
+ points.new(.5, .5)
179
+ points.update()
180
+ for ij, j in enumerate(attr[mapping_]['curves'][idx]['points']):
181
+ for _a in j:
182
+ if hasattr(points[ij], _a) and not isinstance(j[_a], type(None)):
183
+ setattr(points[ij], _a, j[_a])
184
+ curves.update()
185
+ mapping.update()
186
+ if hasattr(node, 'update'):
187
+ node.update()
188
+
189
+
190
+ #### LOOP INPUT ####
191
+ def set_loop_input_attr(node, attr):
192
+ if attr['paired_output'] not in [None, '']:
193
+ node.pair_with_output(node.id_data.nodes[attr['paired_output']['name']])
194
+
195
+
196
+ #### CURVE TIME ####
197
+ def set_curve_time_attr(node, attr):
198
+ curve = getattr(node, 'curve', None)
199
+ if curve:
200
+ set_curve_mapping_attr(curve, attr['curve'], mapping_='curve')
201
+
202
+
203
+ #### IMAGE FORMAT SETTINGS ####
204
+ def set_image_format_settings(img_form, attr):
205
+ set_node_attr(img_form, attr, 'display_settings', 'linear_colorspace_settings', 'stereo_3d_format', 'view_settings')
206
+ disp_sett = getattr(img_form, 'display_settings', None)
207
+ if disp_sett:
208
+ set_node_attr(disp_sett, attr['display_settings'])
209
+ cs_sett = getattr(img_form, 'linear_colorspace_settings', None)
210
+ if cs_sett:
211
+ set_node_attr(cs_sett, attr['linear_colorspace_settings'])
212
+ s3d_sett = getattr(img_form, 'stereo_3d_format', None)
213
+ if s3d_sett:
214
+ set_node_attr(s3d_sett, attr['stereo_3d_format'])
215
+ view_sett = getattr(img_form, 'view_settings', None)
216
+ if view_sett:
217
+ set_node_attr(view_sett, attr['view_settings'], 'curve_mapping', 'is_hdr', 'support_emulation')
218
+ if attr['view_settings']['use_curve_mapping']:
219
+ set_curve_mapping_attr(view_sett, attr['view_settings']['curve_mapping'], mapping_='curve_mapping')
220
+
221
+
222
+ #######################################################################################################
223
+
224
+
225
+ #### COLOR RAMP ####
226
+ @register_node_setter('ShaderNodeValToRGB', 'TextureNodeValToRGB')
227
+ def set_color_ramp(node, attr):
228
+ set_node_attr(node, attr, 'color_ramp', 'elements')
229
+ set_color_ramp_attr(node, attr)
230
+
231
+
232
+ #### COLOR MAPPING, TEXTURE MAPPING, IMAGE USER ####
233
+ @register_node_setter('ShaderNodeTexWave', 'ShaderNodeTexVoronoi', 'ShaderNodeTexSky', 'ShaderNodeTexNoise', 'ShaderNodeTexMagic', 'ShaderNodeTexImage', 'ShaderNodeTexGradient', 'ShaderNodeTexGabor', 'ShaderNodeTexEnvironment', 'ShaderNodeTexChecker', 'ShaderNodeTexBrick')
234
+ def set_img_col_tex_mapping(node, attr):
235
+ set_node_attr(node, attr, 'color_mapping', 'color_ramp', 'elements', 'texture_mapping', 'image_user')
236
+ if hasattr(node, 'image_user'):
237
+ set_img_user_attr(node, attr)
238
+ if hasattr(node, 'texture_mapping'):
239
+ set_tex_mapping_attr(node, attr)
240
+ if hasattr(node, 'color_mapping'):
241
+ set_col_mapping_attr(node, attr)
242
+
243
+
244
+ #### CURVE MAPPING ####
245
+ @register_node_setter('ShaderNodeVectorCurve', 'ShaderNodeRGBCurve', 'ShaderNodeFloatCurve', 'TextureNodeCurveRGB', 'CompositorNodeCurveRGB', 'CompositorNodeHueCorrect')
246
+ def set_curve_mapping(node, attr):
247
+ set_node_attr(node, attr, 'curves', 'points', 'mapping')
248
+ set_curve_mapping_attr(node, attr)
249
+ node.update()
250
+
251
+
252
+ #### BAKE ####
253
+ @register_node_setter('GeometryNodeBake')
254
+ def set_bake(node, attr):
255
+ set_node_attr(node, attr, 'bake_items')
256
+ node.bake_items.clear()
257
+ for idx, a in enumerate(attr['bake_items']):
258
+ if a['socket_type'] == 'CUSTOM':
259
+ node.inputs[idx].hide = attr['inputs'][idx]['hide']
260
+ node.outputs[idx].hide = attr['outputs'][idx]['hide']
261
+ else:
262
+ bake = node.bake_items.new(a['socket_type'], a['name'])
263
+ bake.attribute_domain = a['attribute_domain']
264
+ bake.is_attribute = a['is_attribute']
265
+
266
+
267
+ #### REPEAT, FOREACH, SIMULATION, CLOSURE ####
268
+ @register_node_setter('GeometryNodeRepeatInput', 'GeometryNodeForeachGeometryElementInput', 'GeometryNodeSimulationInput', 'NodeClosureInput')
269
+ def set_loop_input(node, attr):
270
+ set_node_attr(node, attr, 'paired_output')
271
+ set_loop_input_attr(node, attr)
272
+
273
+
274
+ #### REPEAT ####
275
+ @register_node_setter('GeometryNodeRepeatOutput')
276
+ def set_repeat_output(node, attr):
277
+ set_node_attr(node, attr, 'repeat_items')
278
+ set_loop_items_attr(node, 'repeat_items', attr)
279
+
280
+
281
+ #### FOREACH ####
282
+ @register_node_setter('GeometryNodeForeachGeometryElementOutput')
283
+ def set_foreach_output(node, attr):
284
+ set_node_attr(node, attr, 'input_items', 'main_items', 'generation_items')
285
+ set_loop_items_attr(node, 'input_items', attr)
286
+ set_loop_items_attr(node, 'main_items', attr)
287
+ set_loop_items_attr(node, 'generation_items', attr)
288
+
289
+
290
+ #### SIMULATION ####
291
+ @register_node_setter('GeometryNodeSimulationOutput')
292
+ def set_sim_output(node, attr):
293
+ set_node_attr(node, attr, 'state_items')
294
+ set_loop_items_attr(node, 'state_items', attr)
295
+
296
+
297
+ #### CLOSURE ####
298
+ @register_node_setter('NodeClosureOutput')
299
+ def set_closure_output(node, attr):
300
+ set_node_attr(node, attr, 'input_items', 'output_items')
301
+ set_loop_items_attr(node, 'input_items', attr)
302
+ set_loop_items_attr(node, 'output_items', attr)
303
+
304
+
305
+ #### MENU ####
306
+ @register_node_setter('GeometryNodeMenuSwitch')
307
+ def set_menu(node, attr):
308
+ set_node_attr(node, attr, 'enum_items')
309
+ set_enum_attr(node, 'enum_items', attr)
310
+
311
+
312
+ #### VIEWER ####
313
+ @register_node_setter('GeometryNodeViewer')
314
+ def set_viewer(node, attr):
315
+ set_node_attr(node, attr, 'viewer_items')
316
+ set_listed_attr(node, 'viewer_items', attr)
317
+
318
+
319
+ #### CAPTURE ####
320
+ @register_node_setter('GeometryNodeCaptureAttribute')
321
+ def set_capture_attr(node, attr):
322
+ set_node_attr(node, attr, 'capture_items')
323
+ node.capture_items.clear()
324
+ for idx, a in enumerate(attr['capture_items']):
325
+ ca = node.capture_items.new('FLOAT', a['name'])
326
+ ca.data_type = a['data_type']
327
+
328
+
329
+ #### FORMAT ####
330
+ @register_node_setter('NodeFunctionFormatStringItem')
331
+ def set_format(node, attr):
332
+ set_node_attr(node, attr, 'format_items')
333
+ set_loop_items_attr(node, 'format_items', attr)
334
+
335
+
336
+ #### COMBINEBUNDLE, SEPARATEBUNDLE ####
337
+ @register_node_setter('NodeCombineBundle', 'NodeSeparateBundleItem')
338
+ def set_bundle(node, attr):
339
+ set_node_attr(node, attr, 'bundle_items')
340
+ set_loop_items_attr(node, 'bundle_items', attr)
341
+
342
+
343
+ #### EVALUTECLOSURE ####
344
+ @register_node_setter('NodeEvaluateClosure')
345
+ def set_evalute_closure(node, attr):
346
+ set_node_attr(node, attr, 'input_items', 'output_items')
347
+ set_loop_items_attr(node, 'input_items', attr)
348
+ set_loop_items_attr(node, 'output_items', attr)
349
+
350
+
351
+ #### GETGEOMETRYCOMPONENT ####
352
+ @register_node_setter('GeometryNodeGetGeometryComponent')
353
+ def set_geometry_component(node, attr):
354
+ set_node_attr(node, attr)
355
+ inputs = getattr(node, 'inputs', None)
356
+ if inputs:
357
+ inputs[1].default_value = attr['inputs'][1]['default_value']
358
+ inputs[2].default_value = attr['inputs'][2]['default_value']
359
+
360
+
361
+ #### CURVE TIME ####
362
+ @register_node_setter('TextureNodeCurveTime', 'CompositorNodeTime')
363
+ def set_curve_time(node, attr):
364
+ set_node_attr(node, attr)
365
+ set_curve_time_attr(node, attr)
366
+ node.update()
367
+
368
+
369
+ #### CLOSURETOLISTITEM ####
370
+ @register_node_setter('GeometryNodeClosureToListItems')
371
+ def set_closure_to_list(node, attr):
372
+ set_node_attr(node, attr, 'list_items')
373
+ node.list_items.clear()
374
+ for idx, a in enumerate(attr['list_items']):
375
+ ctl = node.list_items.new(a['socket_type'], a['name'])
376
+ ctl.structure_type = a['structure_type']
377
+
378
+
379
+ ### CURVEHANDLETYPESELECTION ###
380
+ @register_node_setter('GeometryNodeCurveHandleTypeSelection', 'GeometryNodeCurveSetHandles')
381
+ def set_curve_handle_type_sel(node, attr):
382
+ set_node_attr(node, attr, 'mode')
383
+ node.mode = set(attr['mode'])
384
+
385
+
386
+ #### IMAGE ####
387
+ @register_node_setter('TextureNodeImage')
388
+ def set_image_attr(node, attr):
389
+ set_node_attr(node, attr, 'image_user')
390
+ set_img_user_attr(node, attr)
391
+
392
+
393
+ #### GROUP ####
394
+ def set_geo_group(node, attr, group_dict):
395
+ if hasattr(node, 'node_tree'):
396
+ node.node_tree = group_dict[attr['node_tree']]
397
+ set_node_attr(node, attr, 'node_tree', 'node_group')
398
+
399
+
400
+ #### CONVERTTODISPLAY ####
401
+ @register_node_setter('CompositorNodeConvertToDisplay')
402
+ def set_convert_to_disp(node, attr):
403
+ set_node_attr(node, attr, 'curves', 'points', 'curve_mapping', 'display_settings', 'view_settings')
404
+ set_nested_attr(node, 'display_settings', attr)
405
+ view_settings = set_nested_attr(node, 'view_settings', attr, 'curve_mapping')
406
+ if attr['view_settings']["use_curve_mapping"]:
407
+ set_curve_mapping_attr(view_settings, attr['view_settings']['curve_mapping'], mapping_='curve_mapping')
408
+ node.update()
409
+
410
+
411
+ #### CRYPTOMATTEV2 ####
412
+ @register_node_setter('CompositorNodeCryptomatteV2')
413
+ def set_crytomatte_v2(node, attr):
414
+ set_node_attr(node, attr, 'entries', 'scene', 'has_layers', 'has_views')
415
+
416
+
417
+ #### IMAGE ####
418
+ @register_node_setter('CompositorNodeImage')
419
+ def set_comp_image(node, attr):
420
+ set_node_attr(node, attr, 'image', 'has_layers', 'has_views')
421
+
422
+
423
+ #### KEYING SCREEN ####
424
+ @register_node_setter('CompositorNodeKeyingScreen', 'CompositorNodeMovieClip', 'CompositorNodeMovieDistortion', 'CompositorNodePlaneTrackDeform', 'CompositorNodeStabilize', 'CompositorNodeTrackPos')
425
+ def set_keying_screen(node, attr):
426
+ set_node_attr(node, attr, 'clip')
427
+
428
+
429
+ #### MASK ####
430
+ @register_node_setter('CompositorNodeMask')
431
+ def set_comp_mask(node, attr):
432
+ set_node_attr(node, attr, 'mask')
433
+
434
+
435
+ #### OUTPUT FILE ####
436
+ @register_node_setter('CompositorNodeOutputFile')
437
+ def set_output_file(node, attr):
438
+ set_node_attr(node, attr, 'file_output_items', 'format')
439
+ _format = getattr(node, 'format', None)
440
+ if _format:
441
+ set_image_format_settings(_format, attr['format'])
442
+ node.file_output_items.clear()
443
+ for idx, a in enumerate(attr['file_output_items']):
444
+ if a['socket_type'] == 'CUSTOM':
445
+ node.inputs[idx].hide = attr['inputs'][idx]['hide']
446
+ node.outputs[idx].hide = attr['outputs'][idx]['hide']
447
+ else:
448
+ output_file = node.file_output_items.new(a['socket_type'], a['name'])
449
+ set_node_attr(output_file, a, 'color', 'format', 'name', 'socket_type')
450
+ set_image_format_settings(output_file, a['format'])
451
+ node.update()
452
+
@@ -0,0 +1,10 @@
1
+ import bpy
2
+
3
+ type PYObject = int | str | float | bool | None
4
+ type PYContainer = list | tuple | set
5
+ type PYDict = dict[str | PYObject | list[PYObject] | dict[str | PYObject]]
6
+ type JSON = PYObject | dict[str | "JSON"] | list["JSON"]
7
+ type JSONObject = dict[str | JSON]
8
+ type JSONList = list[JSON]
9
+ type BNode = bpy.types.Node | object
10
+ type BGroup = bpy.types.GeometryNodeCustomGroup | bpy.types.GeometryNodeGroup | bpy.types.ShaderNodeCustomGroup | bpy.types.ShaderNodeGroup | bpy.types.NodeGroup | bpy.types.TextureNodeGroup | bpy.types.CompositorNodeCustomGroup | bpy.types.CompositorNodeGroup
@@ -0,0 +1,13 @@
1
+ Metadata-Version: 2.4
2
+ Name: node_to_json
3
+ Version: 0.1.1
4
+ Summary: Blender 5.2 Serialize Node Groups to JSON files.Load Node Groups from JSON files.
5
+ Author: Demingo Hill (Noizirom) (C)
6
+ License-Expression: GPL-3.0-only
7
+ Requires-Python: >=3.13
8
+ Description-Content-Type: text/markdown
9
+
10
+ # Node To JSON
11
+
12
+ ## Convert Blender 5.2 Nodes to JSON files. Convert JSON objects to Blender Nodes.
13
+ ### Created to work with Blender Addons and Extensions to serialize nodes. It is only intended to work within Blender's python interpreter and requires the bpy module.
@@ -0,0 +1,13 @@
1
+ node_to_json/__init__.py,sha256=aQ9aoVLJBu-xT1CKPLltajT1Au18YRGdyf6WQAawUyo,2034
2
+ node_to_json/asset_funcs.py,sha256=M_qQitD5tgDT-0ggmfGjBQYieywhAmJptwxWnPZMnDg,2667
3
+ node_to_json/func_util.py,sha256=s3jbntColY5xvTrB5ICR0hPRcfxFDECQ2p4BxOsfmbA,3615
4
+ node_to_json/json_io.py,sha256=1bVT33h71f2wPtNfX5j99kMf04BKaNQSd0oC6YETPXU,892
5
+ node_to_json/node_getters.py,sha256=GXfCBvMnTnJOl0XYV9olchtcNS8y6JUSspQeP0M_dzE,17814
6
+ node_to_json/node_registry.py,sha256=nj9zGxKZwZzhfGGGWzmCkhFZWZTpj0cx3ohEgwtWJ4M,1734
7
+ node_to_json/node_setters.py,sha256=T_umv_399bEnKfll9ZO7AdvHzeo0X6jDsQ53-g12IAo,18674
8
+ node_to_json/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ node_to_json/setter_funcs.py,sha256=KKJvlqJ7PDRhAdv4GAdH6aC7P_cMI-UXk8opBOCo8JU,17096
10
+ node_to_json/type_util.py,sha256=2OZVBh2BlQuVhp6fOYflFeJvmzB3DKwb7yFHmDrTna8,598
11
+ node_to_json-0.1.1.dist-info/WHEEL,sha256=ZFFp7t7R4RYQ5KYZkmiFWoQvHay7SrTmn-6ZYfoFZ3U,80
12
+ node_to_json-0.1.1.dist-info/METADATA,sha256=4xKfGOjQRyTq59kURe03rkaNPmFXvA83tHPvXXmY0zs,550
13
+ node_to_json-0.1.1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: uv 0.12.7
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any