dfindexeddb 20240402__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.
@@ -66,8 +66,9 @@ def _Output(structure, output):
66
66
 
67
67
  def DbCommand(args):
68
68
  """The CLI for processing leveldb folders."""
69
- for rec in record.LevelDBRecord.FromDir(args.source):
70
- _Output(rec, output=args.output)
69
+ for leveldb_record in record.FolderReader(
70
+ args.source).GetRecords(use_manifest=args.use_manifest):
71
+ _Output(leveldb_record, output=args.output)
71
72
 
72
73
 
73
74
  def LdbCommand(args):
@@ -159,6 +160,10 @@ def App():
159
160
  required=True,
160
161
  type=pathlib.Path,
161
162
  help='The source leveldb directory')
163
+ parser_db.add_argument(
164
+ '--use_manifest',
165
+ action='store_true',
166
+ help='Use manifest file to determine active/deleted records.')
162
167
  parser_db.add_argument(
163
168
  '-o',
164
169
  '--output',
@@ -249,8 +254,7 @@ def App():
249
254
  '-v',
250
255
  '--version_history',
251
256
  action='store_true',
252
- help='Parses the leveldb version history.'
253
- )
257
+ help='Parses the leveldb version history.')
254
258
  parser_descriptor.set_defaults(func=DescriptorCommand)
255
259
 
256
260
  args = parser.parse_args()
@@ -25,6 +25,8 @@ PACKED_SEQUENCE_AND_TYPE_LENGTH = 8
25
25
  SEQUENCE_LENGTH = 7
26
26
  TYPE_LENGTH = 1
27
27
 
28
+ MANIFEST_FILENAME_PATTERN = r'MANIFEST-[0-9]{6}'
29
+
28
30
 
29
31
  class BlockCompressionType(enum.IntEnum):
30
32
  """Block compression types."""
@@ -16,9 +16,12 @@
16
16
  from __future__ import annotations
17
17
  import dataclasses
18
18
  import pathlib
19
+ import re
19
20
  import sys
20
- from typing import Any, Generator, Union
21
+ from typing import Generator, Optional, Union
21
22
 
23
+ from dfindexeddb import errors
24
+ from dfindexeddb.leveldb import definitions
22
25
  from dfindexeddb.leveldb import descriptor
23
26
  from dfindexeddb.leveldb import ldb
24
27
  from dfindexeddb.leveldb import log
@@ -34,19 +37,22 @@ class LevelDBRecord:
34
37
  Attributes:
35
38
  path: the file path where the record was parsed from.
36
39
  record: the leveldb record.
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).
42
+ recovered: True if the record is a recovered record.
37
43
  """
38
44
  path: str
39
45
  record: Union[
40
46
  ldb.KeyValueRecord,
41
- log.ParsedInternalKey,
42
- descriptor.VersionEdit]
47
+ log.ParsedInternalKey]
48
+ level: Optional[int] = None
49
+ recovered: Optional[bool] = None
43
50
 
44
51
  @classmethod
45
52
  def FromFile(
46
53
  cls,
47
- file_path: pathlib.Path,
48
- include_versionedit: bool = False
49
- ) -> Generator[LevelDBRecord, Any, Any]:
54
+ file_path: pathlib.Path
55
+ ) -> Generator[LevelDBRecord, None, None]:
50
56
  """Yields leveldb records from the given path.
51
57
 
52
58
  Yields:
@@ -54,7 +60,6 @@ class LevelDBRecord:
54
60
 
55
61
  Args:
56
62
  file_path: the file path.
57
- include_versionedit: include VersionEdit records from descriptor files.
58
63
  """
59
64
  if file_path.name.endswith('.log'):
60
65
  for record in log.FileReader(
@@ -64,39 +69,249 @@ class LevelDBRecord:
64
69
  for record in ldb.FileReader(file_path.as_posix()).GetKeyValueRecords():
65
70
  yield cls(path=file_path.as_posix(), record=record)
66
71
  elif file_path.name.startswith('MANIFEST'):
67
- if not include_versionedit:
68
- print(f'Ignoring {file_path.as_posix()}', file=sys.stderr)
69
- return
70
- for record in descriptor.FileReader(
71
- file_path.as_posix()).GetVersionEdits():
72
- yield cls(path=file_path.as_posix(), record=record)
72
+ print(f'Ignoring descriptor file {file_path.as_posix()}', file=sys.stderr)
73
73
  elif file_path.name in ('LOCK', 'CURRENT', 'LOG', 'LOG.old'):
74
74
  print(f'Ignoring {file_path.as_posix()}', file=sys.stderr)
75
75
  else:
76
76
  print(f'Unsupported file type {file_path.as_posix()}', file=sys.stderr)
77
77
 
78
- @classmethod
79
- def FromDir(
80
- cls,
81
- path: pathlib.Path,
82
- include_versionedit: bool = False
83
- ) -> Generator[LevelDBRecord, Any, Any]:
84
- """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.
88
+
89
+ Args:
90
+ foldername: the source LevelDB folder.
91
+
92
+ Raises:
93
+ ValueError: if foldername is None or not a directory.
94
+ """
95
+ if not foldername or not foldername.is_dir():
96
+ raise ValueError(f'{foldername} is None or not a directory')
97
+ self.foldername = foldername
98
+
99
+ def LogFiles(self) -> Generator[pathlib.Path, None, None]:
100
+ """Returns the log filenames."""
101
+ yield from self.foldername.glob('*.log')
102
+
103
+ def LdbFiles(self) -> Generator[pathlib.Path, None, None]:
104
+ """Returns the ldb filenames."""
105
+ yield from self.foldername.glob('*.ldb')
106
+
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.
113
+
114
+ Raises:
115
+ ParserError: when the CURRENT file does not exist/contain the expected
116
+ content or when the expected MANIFEST file does not exist.
117
+ """
118
+ current_path = self.foldername / 'CURRENT'
119
+ if not current_path.exists():
120
+ raise errors.ParserError(f'{current_path!s} does not exist.')
121
+
122
+ current_manifest = current_path.read_text().strip()
123
+ manifest_regex = re.compile(definitions.MANIFEST_FILENAME_PATTERN)
124
+ if not manifest_regex.fullmatch(current_manifest):
125
+ raise errors.ParserError(
126
+ f'{current_path!s} does not contain the expected content')
127
+
128
+ manifest_path = self.foldername / current_manifest
129
+ if not manifest_path.exists():
130
+ raise errors.ParserError(f'{manifest_path!s} does not exist.')
131
+ return manifest_path
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()
140
+ latest_version = descriptor.FileReader(
141
+ str(current_manifest_path)).GetLatestVersion()
142
+ if not latest_version:
143
+ raise errors.ParserError(
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.
85
152
 
86
153
  Args:
87
- path: the file path.
88
- include_versionedit: include VersionEdit records from descriptor files.
154
+ filename: the source LevelDB file.
89
155
 
90
156
  Yields:
91
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)
92
169
 
93
- Raises:
94
- ValueError: if path is not a directory.
170
+ def _GetLogRecords(
171
+ self,
172
+ filename: pathlib.Path
173
+ ) -> Generator[LevelDBRecord, None, None]:
174
+ """Yields the LevelDBRecords from a log file.
175
+
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
211
+ log_records = []
212
+ if latest_version.current_log:
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)
217
+ else:
218
+ print('No current log file.', file=sys.stderr)
219
+
220
+ # read and cache the records from the "young" or 0-level
221
+ young_records = []
222
+ for active_file in latest_version.active_files.get(0, {}).keys():
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)
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.
235
+ active_records = {}
236
+ for record in sorted(
237
+ log_records,
238
+ key=lambda record: record.record.sequence_number,
239
+ reverse=True):
240
+ if record.record.key not in active_records:
241
+ record.recovered = False
242
+ active_records[record.record.key] = record
243
+ else:
244
+ record.recovered = True
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
250
+ for record in sorted(
251
+ young_records,
252
+ key=lambda record: record.record.sequence_number,
253
+ reverse=True):
254
+ if record.record.key not in active_records:
255
+ record.recovered = False
256
+ active_records[record.record.key] = record
257
+ else:
258
+ record.recovered = True
259
+ record.level = 0
260
+
261
+ yield from sorted(
262
+ log_records + young_records,
263
+ key=lambda record: record.record.sequence_number,
264
+ reverse=False)
265
+
266
+ # read records from the active files in each level (except the 0 level)
267
+ # and update the recovered and level attribute.
268
+ if latest_version.active_files.keys():
269
+ for level in range(1, max(latest_version.active_files.keys()) + 1):
270
+ for filename in latest_version.active_files.get(level, []):
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.
95
312
  """
96
- if path.is_dir():
97
- for file_path in path.iterdir():
98
- yield from cls.FromFile(
99
- file_path=file_path,
100
- include_versionedit=include_versionedit)
313
+ if use_manifest:
314
+ yield from self._RecordsByManifest()
101
315
  else:
102
- raise ValueError(f'{path} is not a directory')
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__ = "20240402"
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: 20240402
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
 
@@ -283,16 +283,56 @@ installation:
283
283
 
284
284
  ```
285
285
  $ dfindexeddb -h
286
- usage: dfindexeddb [-h] -s SOURCE [-o {json,jsonl,repr}]
286
+ usage: dfindexeddb [-h] {db,ldb,log} ...
287
287
 
288
288
  A cli tool for parsing indexeddb files
289
289
 
290
+ positional arguments:
291
+ {db,ldb,log}
292
+ db Parse a directory as indexeddb.
293
+ ldb Parse a ldb file as indexeddb.
294
+ log Parse a log file as indexeddb.
295
+
290
296
  options:
291
- -h, --help show this help message and exit
292
- -s SOURCE, --source SOURCE
293
- The source leveldb folder
294
- -o {json,jsonl,repr}, --output {json,jsonl,repr}
295
- Output format. Default is json
297
+ -h, --help show this help message and exit
298
+ ```
299
+
300
+ #### Examples:
301
+
302
+ To parse IndexedDB records from an sqlite file for Safari and output the
303
+ results as JSON-L, use the following command:
304
+
305
+ ```
306
+ dfindexeddb db -s SOURCE --format safari -o jsonl
307
+ ```
308
+
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:
312
+
313
+ ```
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:
319
+
320
+ ```
321
+ dfindexeddb ldb -s SOURCE -o jsonl
322
+ ```
323
+
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:
326
+
327
+ ```
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:
333
+
334
+ ```
335
+ dfindexeddb blink -s SOURCE
296
336
  ```
297
337
 
298
338
  ### LevelDB
@@ -314,37 +354,32 @@ options:
314
354
  -h, --help show this help message and exit
315
355
  ```
316
356
 
317
- To parse records from a LevelDB log (.log) file, use the following command:
357
+ #### Examples
318
358
 
319
- ```
320
- $ dfleveldb log -s SOURCE [-o {json,jsonl,repr}] [-t {blocks,physical_records,write_batches,parsed_internal_key}]
359
+ To parse records from a LevelDB folder, use the following command:
321
360
 
322
- options:
323
- -h, --help show this help message and exit
324
- -s SOURCE, --source SOURCE
325
- The source leveldb file
326
- -o {json,jsonl,repr}, --output {json,jsonl,repr}
327
- Output format. Default is json
328
- -t {blocks,physical_records,write_batches,parsed_internal_key}, --structure_type {blocks,physical_records,write_batches,parsed_internal_key}
329
- Parses the specified structure. Default is parsed_internal_key.
361
+ ```
362
+ dfindexeddb db -s SOURCE
330
363
  ```
331
364
 
332
- To parse records from a LevelDB table (.ldb) 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:
333
368
 
334
369
  ```
335
- $ dfleveldb ldb -s SOURCE [-o {json,jsonl,repr}] [-t {blocks,records}]
370
+ $ dfleveldb log -s SOURCE [-t {blocks,physical_records,write_batches,parsed_internal_key}]
371
+ ```
336
372
 
337
- options:
338
- -h, --help show this help message and exit
339
- -s SOURCE, --source SOURCE
340
- The source leveldb file
341
- -o {json,jsonl,repr}, --output {json,jsonl,repr}
342
- Output format. Default is json
343
- -t {blocks,records}, --structure_type {blocks,records}
344
- Parses the specified structure. Default is records.
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:
376
+
377
+ ```
378
+ $ dfleveldb ldb -s SOURCE [-t {blocks,records}]
345
379
  ```
346
380
 
347
- 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:
348
383
 
349
384
  ```
350
385
  $ dfleveldb descriptor -s SOURCE [-o {json,jsonl,repr}] [-t {blocks,physical_records,versionedit} | -v]
@@ -0,0 +1,32 @@
1
+ dfindexeddb/__init__.py,sha256=KPYL9__l8od6_OyDfGRTgaJ6iy_fqIgZ-dS2S-e3Rac,599
2
+ dfindexeddb/errors.py,sha256=PNpwyf_lrPc4TE77oAakX3mu5D_YcP3f80wq8Y1LkvY,749
3
+ dfindexeddb/utils.py,sha256=pV2blFnMxDwk3kBRK6UVji66ctkYpm6wfH9p0jCC7Nk,8797
4
+ dfindexeddb/version.py,sha256=w3jTfQPKTCKVn_YxB-eYjw2WM5lN7n0625noN-CZErc,751
5
+ dfindexeddb/indexeddb/__init__.py,sha256=kExXSVBCTKCD5BZJkdMfUMqGksH-DMJxP2_lI0gq-BE,575
6
+ dfindexeddb/indexeddb/cli.py,sha256=MsVqxIhZWXoiBEDbysO9gWztsUkZ8cwFMCYmgSZViLw,5980
7
+ dfindexeddb/indexeddb/utils.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ dfindexeddb/indexeddb/chromium/__init__.py,sha256=kExXSVBCTKCD5BZJkdMfUMqGksH-DMJxP2_lI0gq-BE,575
9
+ dfindexeddb/indexeddb/chromium/blink.py,sha256=Sa5BvDkuwXg2IPc4iUDkNA9cbQKpMnXd3bPpNWSPG0I,32122
10
+ dfindexeddb/indexeddb/chromium/definitions.py,sha256=1a-AmHVZ95uDB6se_fdarwJR8q0tFMQNh2xrZ2-VxN8,8739
11
+ dfindexeddb/indexeddb/chromium/record.py,sha256=kdItbJC5Mo6xWW71QylWvvKxtS4yYJ4BGc_ABHaHIiM,47366
12
+ dfindexeddb/indexeddb/chromium/v8.py,sha256=NsbMgA6nRcAfdLg6CFwWadwsDS6TJ95-4MrgphaTuLw,22102
13
+ dfindexeddb/indexeddb/firefox/__init__.py,sha256=kExXSVBCTKCD5BZJkdMfUMqGksH-DMJxP2_lI0gq-BE,575
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
18
+ dfindexeddb/leveldb/__init__.py,sha256=KPYL9__l8od6_OyDfGRTgaJ6iy_fqIgZ-dS2S-e3Rac,599
19
+ dfindexeddb/leveldb/cli.py,sha256=H1QmbZ2jQ75LqsxPaiOjflljDtYGCSxtsDI3mEf7BNU,7982
20
+ dfindexeddb/leveldb/definitions.py,sha256=lPW_kjc47vyoGOoEWfgWvKcpGbN-0h7XXwCeMoFmYKk,1486
21
+ dfindexeddb/leveldb/descriptor.py,sha256=WR3irG16oIE6VbaP9UPnzOD3KlHR8GYFnoeG6ySJUzU,12211
22
+ dfindexeddb/leveldb/ldb.py,sha256=mN-M7PLtE_VLZCbCbzRgjkSezbMUhgDjgWgPgIxJ1jM,8087
23
+ dfindexeddb/leveldb/log.py,sha256=QeH8oESOPEZUjANGiDRSmXZa2SuoKlPFBJY7SxTV1lg,9209
24
+ dfindexeddb/leveldb/record.py,sha256=_xmeEBXz5nimqteOMAf0zYoi8wDLgG27F4BhLk6N21Q,10747
25
+ dfindexeddb/leveldb/utils.py,sha256=RgEEZ7Z35m3CcOUypAiViQSzKjBgSXZ3aeJhQjY3H9w,3748
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,,
@@ -1,29 +0,0 @@
1
- dfindexeddb/__init__.py,sha256=KPYL9__l8od6_OyDfGRTgaJ6iy_fqIgZ-dS2S-e3Rac,599
2
- dfindexeddb/errors.py,sha256=PNpwyf_lrPc4TE77oAakX3mu5D_YcP3f80wq8Y1LkvY,749
3
- dfindexeddb/utils.py,sha256=pV2blFnMxDwk3kBRK6UVji66ctkYpm6wfH9p0jCC7Nk,8797
4
- dfindexeddb/version.py,sha256=C12mR3wnugr_HU5XlJyKf5YbZV8ogsGEryz9GfirOxk,751
5
- dfindexeddb/indexeddb/__init__.py,sha256=kExXSVBCTKCD5BZJkdMfUMqGksH-DMJxP2_lI0gq-BE,575
6
- dfindexeddb/indexeddb/cli.py,sha256=HX1Gt0VHRozz7Gu01Z33ELA-zwJQse9uxZOLzsZG1dI,3497
7
- dfindexeddb/indexeddb/utils.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- dfindexeddb/indexeddb/chromium/__init__.py,sha256=kExXSVBCTKCD5BZJkdMfUMqGksH-DMJxP2_lI0gq-BE,575
9
- dfindexeddb/indexeddb/chromium/blink.py,sha256=-NtFSdvq0x4UClV7YFlR_D2e9gFQJSAcNstODqHReSw,3566
10
- dfindexeddb/indexeddb/chromium/definitions.py,sha256=yline3y3gmZx6s-dwjpPDNs5HO4zT6KZqPWQfEsHDoM,7413
11
- dfindexeddb/indexeddb/chromium/record.py,sha256=JnDtR6FdnDYj_3TAHngbOFpOJNRtwBAfQn2ovSC2t7A,44731
12
- dfindexeddb/indexeddb/chromium/v8.py,sha256=KapJqC1dbfHXOi0YYSw6Yq9tvX6XriNv6Q52pdti0-I,21902
13
- dfindexeddb/indexeddb/firefox/__init__.py,sha256=kExXSVBCTKCD5BZJkdMfUMqGksH-DMJxP2_lI0gq-BE,575
14
- dfindexeddb/indexeddb/safari/__init__.py,sha256=kExXSVBCTKCD5BZJkdMfUMqGksH-DMJxP2_lI0gq-BE,575
15
- dfindexeddb/leveldb/__init__.py,sha256=KPYL9__l8od6_OyDfGRTgaJ6iy_fqIgZ-dS2S-e3Rac,599
16
- dfindexeddb/leveldb/cli.py,sha256=B86OFZOiMq8Z1AFr_UY5L1D4aIqi78hF7BbyXnSr4a4,7776
17
- dfindexeddb/leveldb/definitions.py,sha256=wwm0uySOeI0-2fG9KvrY-StE0NP3iW5mCEuKYQL4ahg,1436
18
- dfindexeddb/leveldb/descriptor.py,sha256=WR3irG16oIE6VbaP9UPnzOD3KlHR8GYFnoeG6ySJUzU,12211
19
- dfindexeddb/leveldb/ldb.py,sha256=mN-M7PLtE_VLZCbCbzRgjkSezbMUhgDjgWgPgIxJ1jM,8087
20
- dfindexeddb/leveldb/log.py,sha256=QeH8oESOPEZUjANGiDRSmXZa2SuoKlPFBJY7SxTV1lg,9209
21
- dfindexeddb/leveldb/record.py,sha256=AnM4kQb81igmJla5q3rQUYvlzPwZaEHTvauxOC_dtM8,3217
22
- dfindexeddb/leveldb/utils.py,sha256=RgEEZ7Z35m3CcOUypAiViQSzKjBgSXZ3aeJhQjY3H9w,3748
23
- dfindexeddb-20240402.dist-info/AUTHORS,sha256=QbvjbAom57fpEkekkCVFUj0B9KUMGraR510aUMBC-PE,286
24
- dfindexeddb-20240402.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
25
- dfindexeddb-20240402.dist-info/METADATA,sha256=nApZZzcJEpzjs8JKAa3vvNa2zGIcKT78Qz3djSp4-w8,18100
26
- dfindexeddb-20240402.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
27
- dfindexeddb-20240402.dist-info/entry_points.txt,sha256=WG9YNLZ9lBx4Q9QF6wS4dZdZfADT3Zs4_-MV5TcA0ls,102
28
- dfindexeddb-20240402.dist-info/top_level.txt,sha256=X9OTaub1c8S_JJ7g-f8JdkhhdiZ4x1j4eni1hdUCwE4,12
29
- dfindexeddb-20240402.dist-info/RECORD,,