dfindexeddb 20240417__py3-none-any.whl → 20240501__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.
@@ -18,7 +18,7 @@ import dataclasses
18
18
  import pathlib
19
19
  import re
20
20
  import sys
21
- from typing import Any, Generator, Optional, Union
21
+ from typing import Generator, Optional, Union
22
22
 
23
23
  from dfindexeddb import errors
24
24
  from dfindexeddb.leveldb import definitions
@@ -37,7 +37,8 @@ class LevelDBRecord:
37
37
  Attributes:
38
38
  path: the file path where the record was parsed from.
39
39
  record: the leveldb record.
40
- level: the leveldb level, None indicates the record came from a log file.
40
+ level: the leveldb level, None indicates the record came from a log file or
41
+ a file not part of the active file set (determined by a MANIFEST file).
41
42
  recovered: True if the record is a recovered record.
42
43
  """
43
44
  path: str
@@ -51,7 +52,7 @@ class LevelDBRecord:
51
52
  def FromFile(
52
53
  cls,
53
54
  file_path: pathlib.Path
54
- ) -> Generator[LevelDBRecord, Any, Any]:
55
+ ) -> Generator[LevelDBRecord, None, None]:
55
56
  """Yields leveldb records from the given path.
56
57
 
57
58
  Yields:
@@ -74,45 +75,47 @@ class LevelDBRecord:
74
75
  else:
75
76
  print(f'Unsupported file type {file_path.as_posix()}', file=sys.stderr)
76
77
 
77
- @classmethod
78
- def FromDir(
79
- cls,
80
- path: pathlib.Path
81
- ) -> Generator[LevelDBRecord, Any, Any]:
82
- """Yields LevelDBRecords from the given directory.
78
+
79
+ class FolderReader:
80
+ """A LevelDB folder reader.
81
+
82
+ Attributes:
83
+ foldername (str): the source LevelDB folder.
84
+ """
85
+
86
+ def __init__(self, foldername: pathlib.Path):
87
+ """Initializes the FolderReader.
83
88
 
84
89
  Args:
85
- path: the file path.
90
+ foldername: the source LevelDB folder.
86
91
 
87
- Yields:
88
- LevelDBRecords
92
+ Raises:
93
+ ValueError: if foldername is None or not a directory.
89
94
  """
90
- if not path or not path.is_dir():
91
- raise ValueError(f'{path} is not a directory')
92
- for file_path in path.iterdir():
93
- yield from cls.FromFile(file_path=file_path)
95
+ if not foldername or not foldername.is_dir():
96
+ raise ValueError(f'{foldername} is None or not a directory')
97
+ self.foldername = foldername
94
98
 
95
- @classmethod
96
- def FromManifest(
97
- cls,
98
- path: pathlib.Path
99
- ) -> Generator[LevelDBRecord, Any, Any]:
100
- """Yields LevelDBRecords from the given directory using the manifest.
99
+ def LogFiles(self) -> Generator[pathlib.Path, None, None]:
100
+ """Returns the log filenames."""
101
+ yield from self.foldername.glob('*.log')
101
102
 
102
- Args:
103
- path: the file path.
103
+ def LdbFiles(self) -> Generator[pathlib.Path, None, None]:
104
+ """Returns the ldb filenames."""
105
+ yield from self.foldername.glob('*.ldb')
104
106
 
105
- Yields:
106
- LevelDBRecords
107
+ def Manifest(self) -> Generator[pathlib.Path, None, None]:
108
+ """Returns the Manifest filenames."""
109
+ yield from self.foldername.glob('MANIFEST-*')
110
+
111
+ def GetCurrentManifestPath(self) -> pathlib.Path:
112
+ """Returns the path of the current manifest file.
107
113
 
108
114
  Raises:
109
- ParserError: if the CURRENT or MANIFEST-* file does not exist.
110
- ValueError: if path is not a directory.
115
+ ParserError: when the CURRENT file does not exist/contain the expected
116
+ content or when the expected MANIFEST file does not exist.
111
117
  """
112
- if not path or not path.is_dir():
113
- raise ValueError(f'{path} is not a directory')
114
-
115
- current_path = path / 'CURRENT'
118
+ current_path = self.foldername / 'CURRENT'
116
119
  if not current_path.exists():
117
120
  raise errors.ParserError(f'{current_path!s} does not exist.')
118
121
 
@@ -122,34 +125,113 @@ class LevelDBRecord:
122
125
  raise errors.ParserError(
123
126
  f'{current_path!s} does not contain the expected content')
124
127
 
125
- manifest_path = path / current_manifest
128
+ manifest_path = self.foldername / current_manifest
126
129
  if not manifest_path.exists():
127
130
  raise errors.ParserError(f'{manifest_path!s} does not exist.')
131
+ return manifest_path
128
132
 
133
+ def GetLatestVersion(self) -> descriptor.LevelDBVersion:
134
+ """Returns the latest LevelDBVersion.
135
+
136
+ Raises:
137
+ ParserError: when the leveldb version could not be parsed.
138
+ """
139
+ current_manifest_path = self.GetCurrentManifestPath()
129
140
  latest_version = descriptor.FileReader(
130
- str(manifest_path)).GetLatestVersion()
141
+ str(current_manifest_path)).GetLatestVersion()
131
142
  if not latest_version:
132
143
  raise errors.ParserError(
133
- f'Could not parse a leveldb version from {manifest_path!s}')
144
+ f'Could not parse a leveldb version from {current_manifest_path!s}')
145
+ return latest_version
146
+
147
+ def _GetRecordsByFile(
148
+ self, filename: pathlib.Path) -> Generator[LevelDBRecord, None, None]:
149
+ """Yields the LevelDBRecords from a file.
150
+
151
+ Non-log/ldb files are ignored.
152
+
153
+ Args:
154
+ filename: the source LevelDB file.
155
+
156
+ Yields:
157
+ LevelDBRecords
158
+ """
159
+ if filename.name.endswith('.log'):
160
+ yield from self._GetLogRecords(filename)
161
+ elif filename.name.endswith('.ldb'):
162
+ yield from self._GetLdbRecords(filename)
163
+ elif filename.name.startswith('MANIFEST'):
164
+ print(f'Ignoring descriptor file {filename.as_posix()}', file=sys.stderr)
165
+ elif filename.name in ('LOCK', 'CURRENT', 'LOG', 'LOG.old'):
166
+ print(f'Ignoring {filename.as_posix()}', file=sys.stderr)
167
+ else:
168
+ print(f'Unsupported file type {filename.as_posix()}', file=sys.stderr)
169
+
170
+ def _GetLogRecords(
171
+ self,
172
+ filename: pathlib.Path
173
+ ) -> Generator[LevelDBRecord, None, None]:
174
+ """Yields the LevelDBRecords from a log file.
134
175
 
135
- # read log records
176
+ Args:
177
+ filename: the source LevelDB file.
178
+
179
+ Yields:
180
+ LevelDBRecords
181
+ """
182
+ for record in log.FileReader(filename.as_posix()).GetParsedInternalKeys():
183
+ yield LevelDBRecord(path=filename.as_posix(), record=record)
184
+
185
+ def _GetLdbRecords(
186
+ self,
187
+ filename: pathlib.Path
188
+ ) -> Generator[LevelDBRecord, None, None]:
189
+ """Yields the LevelDBRecords from a log file.
190
+
191
+ Args:
192
+ filename: the source LevelDB file.
193
+
194
+ Yields:
195
+ LevelDBRecords
196
+ """
197
+ for record in ldb.FileReader(filename.as_posix()).GetKeyValueRecords():
198
+ yield LevelDBRecord(path=filename.as_posix(), record=record)
199
+
200
+ def _RecordsByManifest(self) -> Generator[LevelDBRecord, None, None]:
201
+ """Yields LevelDBRecords using active files determined by the MANIFEST file.
202
+
203
+ Yields:
204
+ LevelDBRecords.
205
+ """
206
+ latest_version = self.GetLatestVersion()
207
+
208
+ processed_files = set()
209
+
210
+ # read and cache the log records
136
211
  log_records = []
137
212
  if latest_version.current_log:
138
- current_log = path / latest_version.current_log
139
- if current_log.exists():
140
- for log_record in cls.FromFile(file_path=current_log):
141
- log_records.append(log_record)
213
+ current_log_filename = self.foldername / latest_version.current_log
214
+ if current_log_filename.exists():
215
+ log_records = list(self._GetLogRecords(filename=current_log_filename))
216
+ processed_files.add(current_log_filename)
142
217
  else:
143
218
  print('No current log file.', file=sys.stderr)
144
219
 
145
- # read records from the "young" or 0-level
220
+ # read and cache the records from the "young" or 0-level
146
221
  young_records = []
147
222
  for active_file in latest_version.active_files.get(0, {}).keys():
148
- current_young = path / active_file
149
- if current_young.exists():
150
- for young_record in cls.FromFile(current_young):
151
- young_records.append(young_record)
223
+ current_young_filename = self.foldername / active_file
224
+ if current_young_filename.exists():
225
+ young_records = list(self._GetLdbRecords(current_young_filename))
226
+ processed_files.add(current_young_filename)
227
+ else:
228
+ print(
229
+ f'Could not find {current_young_filename} for level 0.',
230
+ file=sys.stderr)
152
231
 
232
+ # sort the log records by the leveldb sequence number in reverse
233
+ # order and update the recovered attribute based on the highest sequence
234
+ # number for a key.
153
235
  active_records = {}
154
236
  for record in sorted(
155
237
  log_records,
@@ -161,6 +243,10 @@ class LevelDBRecord:
161
243
  else:
162
244
  record.recovered = True
163
245
 
246
+ # sort the young records by the leveldb sequence number in reverse
247
+ # order and update:
248
+ # * the recovered attribute based on the highest sequence number for a key
249
+ # * the level attribute to 0
164
250
  for record in sorted(
165
251
  young_records,
166
252
  key=lambda record: record.record.sequence_number,
@@ -177,14 +263,55 @@ class LevelDBRecord:
177
263
  key=lambda record: record.record.sequence_number,
178
264
  reverse=False)
179
265
 
266
+ # read records from the active files in each level (except the 0 level)
267
+ # and update the recovered and level attribute.
180
268
  if latest_version.active_files.keys():
181
269
  for level in range(1, max(latest_version.active_files.keys()) + 1):
182
270
  for filename in latest_version.active_files.get(level, []):
183
- current_filename = path / filename
184
- for record in cls.FromFile(file_path=current_filename):
185
- if record.record.key in active_records:
186
- record.recovered = True
187
- else:
188
- record.recovered = False
189
- record.level = level
190
- yield record
271
+ current_filename = self.foldername / filename
272
+ if current_filename.exists():
273
+ processed_files.add(current_filename)
274
+ for record in self._GetLdbRecords(filename=current_filename):
275
+ record.recovered = record.record.key in active_records
276
+ record.level = level
277
+ yield record
278
+ else:
279
+ print(
280
+ f'Could not find {current_filename} for level {level}.',
281
+ file=sys.stderr)
282
+
283
+ # as a final step, parse any other log/ldb files which we will consider
284
+ # any records as recovered since they are not listed in the the active file
285
+ # set.
286
+ for log_file in self.LogFiles():
287
+ if log_file in processed_files:
288
+ continue
289
+ for record in self._GetLogRecords(filename=log_file):
290
+ record.recovered = True
291
+ yield record
292
+
293
+ for ldb_file in self.LdbFiles():
294
+ if ldb_file in processed_files:
295
+ continue
296
+ for record in self._GetLdbRecords(filename=ldb_file):
297
+ record.recovered = True
298
+ yield record
299
+
300
+ def GetRecords(
301
+ self,
302
+ use_manifest: bool = False
303
+ ) -> Generator[LevelDBRecord, None, None]:
304
+ """Yield LevelDBRecords.
305
+
306
+ Args:
307
+ use_manifest: True to use the current manifest in the folder as a means to
308
+ find the active file set.
309
+
310
+ Yields:
311
+ LevelDBRecords.
312
+ """
313
+ if use_manifest:
314
+ yield from self._RecordsByManifest()
315
+ else:
316
+ for filename in self.foldername.iterdir():
317
+ yield from LevelDBRecord.FromFile(filename)
dfindexeddb/version.py CHANGED
@@ -15,7 +15,7 @@
15
15
  """Version information for dfIndexeddb."""
16
16
 
17
17
 
18
- __version__ = "20240417"
18
+ __version__ = "20240501"
19
19
 
20
20
 
21
21
  def GetVersion():
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dfindexeddb
3
- Version: 20240417
3
+ Version: 20240501
4
4
  Summary: dfindexeddb is an experimental Python tool for performing digital forensic analysis of IndexedDB and leveldb files.
5
5
  Author-email: Syd Pleno <sydp@google.com>
6
6
  Maintainer-email: dfIndexeddb Developers <dfindexeddb-dev@googlegroups.com>
@@ -223,12 +223,12 @@ Requires-Dist: zstd ==1.5.5.1
223
223
  # dfIndexeddb
224
224
 
225
225
  dfindexeddb is an experimental Python tool for performing digital forensic
226
- analysis of IndexedDB and leveldb files.
226
+ analysis of IndexedDB and LevelDB files.
227
227
 
228
- It parses leveldb, IndexedDB and javascript structures from these files without
228
+ It parses LevelDB, IndexedDB and JavaScript structures from these files without
229
229
  requiring native libraries. (Note: only a subset of IndexedDB key types and
230
- Javascript types for Chromium-based browsers are currently supported. Safari
231
- and Firefox are under development).
230
+ JavaScript types for Safari and Chromium-based browsers are currently supported.
231
+ Firefox is under development).
232
232
 
233
233
  The content of IndexedDB files is dependent on what a web application stores
234
234
  locally/offline using the web browser's
@@ -275,7 +275,7 @@ include:
275
275
 
276
276
  ## Usage
277
277
 
278
- Two CLI tools for parsing IndexedDB/leveldb files are available after
278
+ Two CLI tools for parsing IndexedDB/LevelDB files are available after
279
279
  installation:
280
280
 
281
281
 
@@ -297,49 +297,42 @@ options:
297
297
  -h, --help show this help message and exit
298
298
  ```
299
299
 
300
- To parse Indexeddb records from a LevelDB folder, use the following command:
300
+ #### Examples:
301
301
 
302
- ```
303
- dfindexeddb db -h
304
- usage: dfindexeddb db [-h] -s SOURCE [--use_manifest] [-o {json,jsonl,repr}]
302
+ To parse IndexedDB records from an sqlite file for Safari and output the
303
+ results as JSON-L, use the following command:
305
304
 
306
- options:
307
- -h, --help show this help message and exit
308
- -s SOURCE, --source SOURCE
309
- The source leveldb folder
310
- --use_manifest Use manifest file to determine active/recovered records.
311
- -o {json,jsonl,repr}, --output {json,jsonl,repr}
312
- Output format. Default is json
305
+ ```
306
+ dfindexeddb db -s SOURCE --format safari -o jsonl
313
307
  ```
314
308
 
315
- To parse Indexeddb records from a LevelDB ldb (.ldb) file, use the following
316
- command:
309
+ To parse IndexedDB records from a LevelDB folder for Chrome/Chromium, using the
310
+ manifest file to determine recovered records and output as JSON, use the
311
+ following command:
317
312
 
318
313
  ```
319
- dfindexeddb ldb -h
320
- usage: dfindexeddb ldb [-h] -s SOURCE [-o {json,jsonl,repr}]
314
+ dfindexeddb db -s SOURCE --format chrome --use_manifest
315
+ ```
316
+
317
+ To parse IndexedDB records from a LevelDB ldb (.ldb) file and output the
318
+ results as JSON-L, use the following command:
321
319
 
322
- options:
323
- -h, --help show this help message and exit
324
- -s SOURCE, --source SOURCE
325
- The source .ldb file.
326
- -o {json,jsonl,repr}, --output {json,jsonl,repr}
327
- Output format. Default is json
320
+ ```
321
+ dfindexeddb ldb -s SOURCE -o jsonl
328
322
  ```
329
323
 
330
- To parse Indexeddb records from a LevelDB log (.log) file, use the following
331
- command:
324
+ To parse IndexedDB records from a LevelDB log (.log) file and output the
325
+ results as the Python printable representation, use the following command:
332
326
 
333
327
  ```
334
- dfindexeddb log -h
335
- usage: dfindexeddb log [-h] -s SOURCE [-o {json,jsonl,repr}]
328
+ dfindexeddb log -s SOURCE -o repr
329
+ ```
330
+
331
+ To parse a file as a Chrome/Chromium IndexedDB blink value and output the
332
+ results as JSON:
336
333
 
337
- options:
338
- -h, --help show this help message and exit
339
- -s SOURCE, --source SOURCE
340
- The source .log file.
341
- -o {json,jsonl,repr}, --output {json,jsonl,repr}
342
- Output format. Default is json
334
+ ```
335
+ dfindexeddb blink -s SOURCE
343
336
  ```
344
337
 
345
338
  ### LevelDB
@@ -361,52 +354,32 @@ options:
361
354
  -h, --help show this help message and exit
362
355
  ```
363
356
 
357
+ #### Examples
358
+
364
359
  To parse records from a LevelDB folder, use the following command:
365
360
 
366
361
  ```
367
- dfindexeddb db -h
368
- usage: dfindexeddb db [-h] -s SOURCE [--use_manifest] [-o {json,jsonl,repr}]
369
-
370
- options:
371
- -h, --help show this help message and exit
372
- -s SOURCE, --source SOURCE
373
- The source leveldb folder
374
- --use_manifest Use manifest file to determine active/recovered records.
375
- -o {json,jsonl,repr}, --output {json,jsonl,repr}
376
- Output format. Default is json
362
+ dfindexeddb db -s SOURCE
377
363
  ```
378
364
 
379
- To parse records from a LevelDB log (.log) file, use the following command:
365
+ To parse blocks / physical records/ write batches / internal key records from a
366
+ LevelDB log (.log) file, use the following command, specifying the type (block,
367
+ physical_records, etc) via the `-t` option. By default, internal key records are parsed:
380
368
 
381
369
  ```
382
- $ dfleveldb log -s SOURCE [-o {json,jsonl,repr}] [-t {blocks,physical_records,write_batches,parsed_internal_key}]
383
-
384
- options:
385
- -h, --help show this help message and exit
386
- -s SOURCE, --source SOURCE
387
- The source leveldb file
388
- -o {json,jsonl,repr}, --output {json,jsonl,repr}
389
- Output format. Default is json
390
- -t {blocks,physical_records,write_batches,parsed_internal_key}, --structure_type {blocks,physical_records,write_batches,parsed_internal_key}
391
- Parses the specified structure. Default is parsed_internal_key.
370
+ $ dfleveldb log -s SOURCE [-t {blocks,physical_records,write_batches,parsed_internal_key}]
392
371
  ```
393
372
 
394
- To parse records from a LevelDB table (.ldb) file, use the following command:
373
+ To parse blocks / records from a LevelDB table (.ldb) file, use the following
374
+ command, specifying the type (blocks, records) via the `-t` option. By
375
+ default, records are parsed:
395
376
 
396
377
  ```
397
- $ dfleveldb ldb -s SOURCE [-o {json,jsonl,repr}] [-t {blocks,records}]
398
-
399
- options:
400
- -h, --help show this help message and exit
401
- -s SOURCE, --source SOURCE
402
- The source leveldb file
403
- -o {json,jsonl,repr}, --output {json,jsonl,repr}
404
- Output format. Default is json
405
- -t {blocks,records}, --structure_type {blocks,records}
406
- Parses the specified structure. Default is records.
378
+ $ dfleveldb ldb -s SOURCE [-t {blocks,records}]
407
379
  ```
408
380
 
409
- To parse version edit records from a Descriptor (MANIFEST) file:
381
+ To parse version edit records from a Descriptor (MANIFEST) file, use the
382
+ following command:
410
383
 
411
384
  ```
412
385
  $ dfleveldb descriptor -s SOURCE [-o {json,jsonl,repr}] [-t {blocks,physical_records,versionedit} | -v]
@@ -1,29 +1,32 @@
1
1
  dfindexeddb/__init__.py,sha256=KPYL9__l8od6_OyDfGRTgaJ6iy_fqIgZ-dS2S-e3Rac,599
2
2
  dfindexeddb/errors.py,sha256=PNpwyf_lrPc4TE77oAakX3mu5D_YcP3f80wq8Y1LkvY,749
3
3
  dfindexeddb/utils.py,sha256=pV2blFnMxDwk3kBRK6UVji66ctkYpm6wfH9p0jCC7Nk,8797
4
- dfindexeddb/version.py,sha256=9l1f_ZrRYqrqSAqNJB60Bvu4BzyNlWXvowSOCQQtpKw,751
4
+ dfindexeddb/version.py,sha256=w3jTfQPKTCKVn_YxB-eYjw2WM5lN7n0625noN-CZErc,751
5
5
  dfindexeddb/indexeddb/__init__.py,sha256=kExXSVBCTKCD5BZJkdMfUMqGksH-DMJxP2_lI0gq-BE,575
6
- dfindexeddb/indexeddb/cli.py,sha256=apS5gfiJdkBJs5rLCAol5LzJ_d9lrHyWWfSqGupqp_w,6851
6
+ dfindexeddb/indexeddb/cli.py,sha256=MsVqxIhZWXoiBEDbysO9gWztsUkZ8cwFMCYmgSZViLw,5980
7
7
  dfindexeddb/indexeddb/utils.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
8
  dfindexeddb/indexeddb/chromium/__init__.py,sha256=kExXSVBCTKCD5BZJkdMfUMqGksH-DMJxP2_lI0gq-BE,575
9
9
  dfindexeddb/indexeddb/chromium/blink.py,sha256=Sa5BvDkuwXg2IPc4iUDkNA9cbQKpMnXd3bPpNWSPG0I,32122
10
10
  dfindexeddb/indexeddb/chromium/definitions.py,sha256=1a-AmHVZ95uDB6se_fdarwJR8q0tFMQNh2xrZ2-VxN8,8739
11
- dfindexeddb/indexeddb/chromium/record.py,sha256=VJAXBgqWirS8Xjatf8_lNsepvZ3ix00JSpg8UveK9jE,45079
11
+ dfindexeddb/indexeddb/chromium/record.py,sha256=kdItbJC5Mo6xWW71QylWvvKxtS4yYJ4BGc_ABHaHIiM,47366
12
12
  dfindexeddb/indexeddb/chromium/v8.py,sha256=NsbMgA6nRcAfdLg6CFwWadwsDS6TJ95-4MrgphaTuLw,22102
13
13
  dfindexeddb/indexeddb/firefox/__init__.py,sha256=kExXSVBCTKCD5BZJkdMfUMqGksH-DMJxP2_lI0gq-BE,575
14
14
  dfindexeddb/indexeddb/safari/__init__.py,sha256=kExXSVBCTKCD5BZJkdMfUMqGksH-DMJxP2_lI0gq-BE,575
15
+ dfindexeddb/indexeddb/safari/definitions.py,sha256=nW8MmYx9Ob86W4pxm4QD4Xvr5QjoV34-U7wDhm2GIr0,2779
16
+ dfindexeddb/indexeddb/safari/record.py,sha256=bzoMSgpXs2SsEOKHjVh9tkJDZtzGkQByq3G5dK_Yd7Q,8010
17
+ dfindexeddb/indexeddb/safari/webkit.py,sha256=eQeKXOcVdCaZKM4xekw1d3iLTynXc_e-GCT_x9iSJNs,21595
15
18
  dfindexeddb/leveldb/__init__.py,sha256=KPYL9__l8od6_OyDfGRTgaJ6iy_fqIgZ-dS2S-e3Rac,599
16
- dfindexeddb/leveldb/cli.py,sha256=K_AnRyNteYbyd8Lz-4bPHJpyeFEYFqSPNE_wpp1-FrY,8060
19
+ dfindexeddb/leveldb/cli.py,sha256=H1QmbZ2jQ75LqsxPaiOjflljDtYGCSxtsDI3mEf7BNU,7982
17
20
  dfindexeddb/leveldb/definitions.py,sha256=lPW_kjc47vyoGOoEWfgWvKcpGbN-0h7XXwCeMoFmYKk,1486
18
21
  dfindexeddb/leveldb/descriptor.py,sha256=WR3irG16oIE6VbaP9UPnzOD3KlHR8GYFnoeG6ySJUzU,12211
19
22
  dfindexeddb/leveldb/ldb.py,sha256=mN-M7PLtE_VLZCbCbzRgjkSezbMUhgDjgWgPgIxJ1jM,8087
20
23
  dfindexeddb/leveldb/log.py,sha256=QeH8oESOPEZUjANGiDRSmXZa2SuoKlPFBJY7SxTV1lg,9209
21
- dfindexeddb/leveldb/record.py,sha256=0wXVibHC8QX8NQRsG9jcTlEb2rvaBFta20pOXdKFRMU,6170
24
+ dfindexeddb/leveldb/record.py,sha256=_xmeEBXz5nimqteOMAf0zYoi8wDLgG27F4BhLk6N21Q,10747
22
25
  dfindexeddb/leveldb/utils.py,sha256=RgEEZ7Z35m3CcOUypAiViQSzKjBgSXZ3aeJhQjY3H9w,3748
23
- dfindexeddb-20240417.dist-info/AUTHORS,sha256=QbvjbAom57fpEkekkCVFUj0B9KUMGraR510aUMBC-PE,286
24
- dfindexeddb-20240417.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
25
- dfindexeddb-20240417.dist-info/METADATA,sha256=Mw_W56yj0csKALgtIpn1gS4Po8sfSn6bPMnpp5Hm_1o,19937
26
- dfindexeddb-20240417.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
27
- dfindexeddb-20240417.dist-info/entry_points.txt,sha256=WG9YNLZ9lBx4Q9QF6wS4dZdZfADT3Zs4_-MV5TcA0ls,102
28
- dfindexeddb-20240417.dist-info/top_level.txt,sha256=X9OTaub1c8S_JJ7g-f8JdkhhdiZ4x1j4eni1hdUCwE4,12
29
- dfindexeddb-20240417.dist-info/RECORD,,
26
+ dfindexeddb-20240501.dist-info/AUTHORS,sha256=QbvjbAom57fpEkekkCVFUj0B9KUMGraR510aUMBC-PE,286
27
+ dfindexeddb-20240501.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
28
+ dfindexeddb-20240501.dist-info/METADATA,sha256=Ejikzq8iS6BHrSroiBS4A_xR7O4GJnwVsmyoMF25jUM,18498
29
+ dfindexeddb-20240501.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
30
+ dfindexeddb-20240501.dist-info/entry_points.txt,sha256=WG9YNLZ9lBx4Q9QF6wS4dZdZfADT3Zs4_-MV5TcA0ls,102
31
+ dfindexeddb-20240501.dist-info/top_level.txt,sha256=X9OTaub1c8S_JJ7g-f8JdkhhdiZ4x1j4eni1hdUCwE4,12
32
+ dfindexeddb-20240501.dist-info/RECORD,,