dfindexeddb 20240305__py3-none-any.whl → 20240324__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.
@@ -15,12 +15,13 @@
15
15
  """Parser for LevelDB Log (.log) files."""
16
16
  from __future__ import annotations
17
17
 
18
- from dataclasses import dataclass, field
18
+ from dataclasses import dataclass
19
19
  import io
20
20
  from typing import BinaryIO, Generator, Iterable, Optional
21
21
 
22
- from dfindexeddb import utils
22
+ from dfindexeddb import errors
23
23
  from dfindexeddb.leveldb import definitions
24
+ from dfindexeddb.leveldb import utils
24
25
 
25
26
 
26
27
  @dataclass
@@ -29,18 +30,17 @@ class ParsedInternalKey:
29
30
 
30
31
  Attributes:
31
32
  offset: the offset of the record.
32
- type: the record type.
33
+ record_type: the record type.
33
34
  sequence_number: the sequence number (inferred from the relative location
34
35
  the ParsedInternalKey in a WriteBatch.)
35
36
  key: the record key.
36
37
  value: the record value.
37
38
  """
38
39
  offset: int
39
- type: int
40
+ record_type: definitions.InternalRecordType
40
41
  sequence_number: int
41
42
  key: bytes
42
43
  value: bytes
43
- __type__: str = 'ParsedInternalKey'
44
44
 
45
45
  @classmethod
46
46
  def FromDecoder(
@@ -65,15 +65,17 @@ class ParsedInternalKey:
65
65
  """
66
66
  offset, record_type = decoder.DecodeUint8()
67
67
  _, key = decoder.DecodeBlobWithLength()
68
- if record_type == 1:
68
+ record_type = definitions.InternalRecordType(record_type)
69
+
70
+ if record_type == definitions.InternalRecordType.VALUE:
69
71
  _, value = decoder.DecodeBlobWithLength()
70
- elif record_type == 0:
72
+ elif record_type == definitions.InternalRecordType.DELETED:
71
73
  value = b''
72
74
  else:
73
75
  raise ValueError(f'Invalid record type {record_type}')
74
76
  return cls(
75
77
  offset=base_offset + offset,
76
- type=record_type,
78
+ record_type=record_type,
77
79
  key=key,
78
80
  value=value,
79
81
  sequence_number=sequence_number)
@@ -92,7 +94,7 @@ class WriteBatch(utils.FromDecoderMixin):
92
94
  offset: int
93
95
  sequence_number: int
94
96
  count: int
95
- records: Iterable[ParsedInternalKey] = field(repr=False)
97
+ records: Iterable[ParsedInternalKey]
96
98
 
97
99
  @classmethod
98
100
  def FromDecoder(
@@ -142,7 +144,7 @@ class PhysicalRecord(utils.FromDecoderMixin):
142
144
  checksum: int
143
145
  length: int
144
146
  record_type: definitions.LogFilePhysicalRecordType
145
- contents: bytes = field(repr=False)
147
+ contents: bytes
146
148
  contents_offset: int
147
149
 
148
150
  PHYSICAL_HEADER_LENGTH = 7
@@ -163,8 +165,13 @@ class PhysicalRecord(utils.FromDecoderMixin):
163
165
  """
164
166
  offset, checksum = decoder.DecodeUint32()
165
167
  _, length = decoder.DecodeUint16()
166
- record_type = definitions.LogFilePhysicalRecordType(
167
- decoder.DecodeUint8()[1])
168
+ _, record_type_byte = decoder.DecodeUint8()
169
+ try:
170
+ record_type = definitions.LogFilePhysicalRecordType(record_type_byte)
171
+ except ValueError as error:
172
+ raise errors.ParserError(
173
+ f'Error parsing record type of Physical Record at offset '
174
+ f'{offset + base_offset}') from error
168
175
  contents_offset, contents = decoder.ReadBytes(length)
169
176
  return cls(
170
177
  base_offset=base_offset,
@@ -185,7 +192,7 @@ class Block:
185
192
  data: the block data.
186
193
  """
187
194
  offset: int
188
- data: bytes = field(repr=False)
195
+ data: bytes
189
196
 
190
197
  BLOCK_SIZE = 32768
191
198
 
@@ -276,7 +283,7 @@ class FileReader:
276
283
  """
277
284
  buffer = bytearray()
278
285
  for physical_record in self.GetPhysicalRecords():
279
- if(physical_record.record_type ==
286
+ if (physical_record.record_type ==
280
287
  definitions.LogFilePhysicalRecordType.FULL):
281
288
  buffer = physical_record.contents
282
289
  offset = physical_record.contents_offset + physical_record.base_offset
@@ -295,13 +302,13 @@ class FileReader:
295
302
  yield WriteBatch.FromBytes(buffer, base_offset=offset)
296
303
  buffer = bytearray()
297
304
 
298
- def GetKeyValueRecords(self) -> Generator[ParsedInternalKey, None, None]:
299
- """Returns an iterator of KeyValueRecord instances.
305
+ def GetParsedInternalKeys(self) -> Generator[ParsedInternalKey, None, None]:
306
+ """Returns an iterator of ParsedInternalKey instances.
300
307
 
301
- A batch can contain on or more key value records.
308
+ A batch can contain one or more key value records.
302
309
 
303
310
  Yields:
304
- KeyValueRecord
311
+ ParsedInternalKey
305
312
  """
306
313
  for batch in self.GetWriteBatches():
307
314
  yield from batch.records
@@ -0,0 +1,102 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright 2024 Google LLC
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # https://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ """A module for records from LevelDB files."""
16
+ from __future__ import annotations
17
+ import dataclasses
18
+ import pathlib
19
+ import sys
20
+ from typing import Any, Generator, Union
21
+
22
+ from dfindexeddb.leveldb import descriptor
23
+ from dfindexeddb.leveldb import ldb
24
+ from dfindexeddb.leveldb import log
25
+
26
+
27
+ @dataclasses.dataclass
28
+ class LevelDBRecord:
29
+ """A leveldb record.
30
+
31
+ A record can come from a log file, a table (ldb) file or a descriptor
32
+ (MANIFEST) file.
33
+
34
+ Attributes:
35
+ path: the file path where the record was parsed from.
36
+ record: the leveldb record.
37
+ """
38
+ path: str
39
+ record: Union[
40
+ ldb.KeyValueRecord,
41
+ log.ParsedInternalKey,
42
+ descriptor.VersionEdit]
43
+
44
+ @classmethod
45
+ def FromFile(
46
+ cls,
47
+ file_path: pathlib.Path,
48
+ include_versionedit: bool = False
49
+ ) -> Generator[LevelDBRecord, Any, Any]:
50
+ """Yields leveldb records from the given path.
51
+
52
+ Yields:
53
+ LevelDBRecords
54
+
55
+ Args:
56
+ file_path: the file path.
57
+ include_versionedit: include VersionEdit records from descriptor files.
58
+ """
59
+ if file_path.name.endswith('.log'):
60
+ for record in log.FileReader(
61
+ file_path.as_posix()).GetParsedInternalKeys():
62
+ yield cls(path=file_path.as_posix(), record=record)
63
+ elif file_path.name.endswith('.ldb'):
64
+ for record in ldb.FileReader(file_path.as_posix()).GetKeyValueRecords():
65
+ yield cls(path=file_path.as_posix(), record=record)
66
+ 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)
73
+ elif file_path.name in ('LOCK', 'CURRENT', 'LOG', 'LOG.old'):
74
+ print(f'Ignoring {file_path.as_posix()}', file=sys.stderr)
75
+ else:
76
+ print(f'Unsupported file type {file_path.as_posix()}', file=sys.stderr)
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.
85
+
86
+ Args:
87
+ path: the file path.
88
+ include_versionedit: include VersionEdit records from descriptor files.
89
+
90
+ Yields:
91
+ LevelDBRecords
92
+
93
+ Raises:
94
+ ValueError: if path is not a directory.
95
+ """
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)
101
+ else:
102
+ raise ValueError(f'{path} is not a directory')
@@ -0,0 +1,116 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright 2024 Google LLC
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # https://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ """Helper/utility classes for LevelDB."""
16
+ from __future__ import annotations
17
+ import io
18
+ from typing import BinaryIO, Tuple, Type, TypeVar
19
+
20
+ from dfindexeddb import errors
21
+ from dfindexeddb import utils
22
+
23
+
24
+ class LevelDBDecoder(utils.StreamDecoder):
25
+ """A helper class to decode data types from LevelDB files."""
26
+
27
+ def DecodeBool(self) -> Tuple[int, bool]:
28
+ """Returns a Tuple of the offset of decoding and the bool value."""
29
+ offset, buffer = self.ReadBytes(1)
30
+ return offset, buffer[0] is not None
31
+
32
+ def DecodeString(self) -> Tuple[int, str]:
33
+ """Returns a tuple of the offset of decoding and the string value.
34
+
35
+ Raises:
36
+ errors.DecoderError: when the parsed string buffer is not even (i.e.
37
+ cannot be decoded as a UTF-16-BE string.
38
+ """
39
+ offset = self.stream.tell()
40
+ buffer = self.stream.read()
41
+ if len(buffer) % 2:
42
+ raise errors.DecoderError(
43
+ f'Odd number of bytes encountered at offset {offset}')
44
+ return offset, buffer.decode('utf-16-be')
45
+
46
+ def DecodeLengthPrefixedSlice(self) -> Tuple[int, bytes]:
47
+ """Returns a tuple of the offset of decoding and the byte 'slice'."""
48
+ offset, num_bytes = self.DecodeUint32Varint()
49
+ _, blob = self.ReadBytes(num_bytes)
50
+ return offset, blob
51
+
52
+ def DecodeBlobWithLength(self) -> Tuple[int, bytes]:
53
+ """Returns a tuple of a the offset of decoding and the binary blob."""
54
+ offset, num_bytes = self.DecodeUint64Varint()
55
+ _, blob = self.ReadBytes(num_bytes)
56
+ return offset, blob
57
+
58
+ def DecodeStringWithLength(self, encoding='utf-16-be') -> Tuple[int, str]:
59
+ """Returns a tuple of the offset of decoding and the string value."""
60
+ offset, length = self.DecodeUint64Varint()
61
+ _, buffer = self.ReadBytes(length*2)
62
+ return offset, buffer.decode(encoding=encoding)
63
+
64
+
65
+ T = TypeVar('T')
66
+
67
+
68
+ class FromDecoderMixin:
69
+ """A mixin for parsing dataclass attributes using a LevelDBDecoder."""
70
+
71
+ @classmethod
72
+ def FromDecoder(
73
+ cls: Type[T], decoder: LevelDBDecoder, base_offset: int = 0) -> T:
74
+ """Decodes a class type from the current position of a LevelDBDecoder.
75
+
76
+ Args:
77
+ decoder: the LevelDBDecoder.
78
+ base_offset: the base offset.
79
+
80
+ Returns:
81
+ The class instance.
82
+
83
+ Raises:
84
+ NotImplementedError if the child class does not implement this method.
85
+ """
86
+ raise NotImplementedError
87
+
88
+ @classmethod
89
+ def FromStream(
90
+ cls: Type[T], stream: BinaryIO, base_offset: int = 0) -> T:
91
+ """Decodes a class type from the current position of a binary stream.
92
+
93
+ Args:
94
+ stream: the binary stream.
95
+ base_offset: the base offset of the binary stream.
96
+
97
+ Returns:
98
+ The class instance.
99
+ """
100
+ decoder = LevelDBDecoder(stream)
101
+ return cls.FromDecoder(decoder=decoder, base_offset=base_offset)
102
+
103
+ @classmethod
104
+ def FromBytes(
105
+ cls: Type[T], raw_data: bytes, base_offset: int = 0) -> T:
106
+ """Parses a class type from raw bytes.
107
+
108
+ Args:
109
+ raw_data: the raw data.
110
+ base_offset: the base offset of the raw data.
111
+
112
+ Returns:
113
+ The class instance.
114
+ """
115
+ stream = io.BytesIO(raw_data)
116
+ return cls.FromStream(stream=stream, base_offset=base_offset)
dfindexeddb/utils.py CHANGED
@@ -13,6 +13,7 @@
13
13
  # See the License for the specific language governing permissions and
14
14
  # limitations under the License.
15
15
  """Utilities for dfindexeddb."""
16
+ from __future__ import annotations
16
17
  import io
17
18
  import os
18
19
  import struct
@@ -206,48 +207,6 @@ class StreamDecoder:
206
207
  return self.DecodeZigzagVarint(max_bytes=10)
207
208
 
208
209
 
209
-
210
- class LevelDBDecoder(StreamDecoder):
211
- """A helper class to decode data types from LevelDB files."""
212
-
213
- def DecodeBool(self) -> Tuple[int, bool]:
214
- """Returns a Tuple of the offset of decoding and the bool value."""
215
- offset, buffer = self.ReadBytes(1)
216
- return offset, buffer[0] is not None
217
-
218
- def DecodeString(self) -> Tuple[int, str]:
219
- """Returns a tuple of the offset of decoding and the string value.
220
-
221
- Raises:
222
- errors.DecoderError: when the parsed string buffer is not even (i.e.
223
- cannot be decoded as a UTF-16-BE string.
224
- """
225
- offset = self.stream.tell()
226
- buffer = self.stream.read()
227
- if len(buffer) % 2:
228
- raise errors.DecoderError(
229
- f'Odd number of bytes encountered at offset {offset}')
230
- return offset, buffer.decode('utf-16-be')
231
-
232
- def DecodeLengthPrefixedSlice(self) -> Tuple[int, bytes]:
233
- """Returns a tuple of the offset of decoding and the byte 'slice'."""
234
- offset, num_bytes = self.DecodeUint32Varint()
235
- _, blob = self.ReadBytes(num_bytes)
236
- return offset, blob
237
-
238
- def DecodeBlobWithLength(self) -> Tuple[int, bytes]:
239
- """Returns a tuple of a the offset of decoding and the binary blob."""
240
- offset, num_bytes = self.DecodeUint64Varint()
241
- _, blob = self.ReadBytes(num_bytes)
242
- return offset, blob
243
-
244
- def DecodeStringWithLength(self, encoding='utf-16-be') -> Tuple[int, str]:
245
- """Returns a tuple of the offset of decoding and the string value."""
246
- offset, length = self.DecodeUint64Varint()
247
- _, buffer = self.ReadBytes(length*2)
248
- return offset, buffer.decode(encoding=encoding)
249
-
250
-
251
210
  T = TypeVar('T')
252
211
 
253
212
 
@@ -256,11 +215,11 @@ class FromDecoderMixin:
256
215
 
257
216
  @classmethod
258
217
  def FromDecoder(
259
- cls: Type[T], decoder: LevelDBDecoder, base_offset: int = 0) -> T:
260
- """Decodes a class type from the current position of a LevelDBDecoder.
218
+ cls: Type[T], decoder: StreamDecoder, base_offset: int = 0) -> T:
219
+ """Decodes a class type from the current position of a StreamDecoder.
261
220
 
262
221
  Args:
263
- decoder: the LevelDBDecoder.
222
+ decoder: the StreamDecoder.
264
223
  base_offset: the base offset.
265
224
 
266
225
  Returns:
@@ -283,7 +242,7 @@ class FromDecoderMixin:
283
242
  Returns:
284
243
  The class instance.
285
244
  """
286
- decoder = LevelDBDecoder(stream)
245
+ decoder = StreamDecoder(stream)
287
246
  return cls.FromDecoder(decoder=decoder, base_offset=base_offset)
288
247
 
289
248
  @classmethod
dfindexeddb/version.py CHANGED
@@ -14,7 +14,7 @@
14
14
  # limitations under the License.
15
15
  """Version information for dfIndexeddb."""
16
16
 
17
- __version__ = "20240305"
17
+ __version__ = "20240324"
18
18
 
19
19
 
20
20
  def GetVersion():
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dfindexeddb
3
- Version: 20240305
3
+ Version: 20240324
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>
@@ -226,7 +226,9 @@ dfindexeddb is an experimental Python tool for performing digital forensic
226
226
  analysis of IndexedDB and leveldb files.
227
227
 
228
228
  It parses leveldb, IndexedDB and javascript structures from these files without
229
- requiring native libraries.
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
232
 
231
233
  The content of IndexedDB files is dependent on what a web application stores
232
234
  locally/offline using the web browser's
@@ -236,25 +238,34 @@ include:
236
238
  * emails and contact information from an e-mail application,
237
239
  * images and metadata from a photo gallery application
238
240
 
241
+
239
242
  ## Installation
240
243
 
244
+ 1. [Linux] Install the snappy compression development package
245
+
241
246
  ```
242
- $ pip install dfindexeddb
247
+ $ sudo apt install libsnappy-dev
243
248
  ```
244
249
 
245
- ## Installation from source
250
+ 2. Create a virtual environment and install the package
246
251
 
247
- ### Linux
252
+ ```
253
+ $ python3 -m venv .venv
254
+ $ source .venv/bin/activate
255
+ $ pip install dfindexeddb
256
+ ```
248
257
 
249
- 1. Install the snappy compression development package
258
+ ## Installation from source
259
+
260
+ 1. [Linux] Install the snappy compression development package
250
261
 
251
262
  ```
252
263
  $ sudo apt install libsnappy-dev
253
264
  ```
254
265
 
255
- 2. Clone or download the repository to your local machine.
266
+ 2. Clone or download/unzip the repository to your local machine.
256
267
 
257
- 3. Create a virutal environemnt and install the package
268
+ 3. Create a virtual environment and install the package
258
269
 
259
270
  ```
260
271
  $ python3 -m venv .venv
@@ -264,55 +275,58 @@ $ pip install dfindexeddb
264
275
 
265
276
  ## Usage
266
277
 
267
- A CLI tool is available after installation:
278
+ Two CLI tools for parsing IndexedDB/leveldb files are available after
279
+ installation:
280
+
281
+
282
+ ### IndexedDB
268
283
 
269
284
  ```
270
285
  $ dfindexeddb -h
271
- usage: dfindexeddb [-h] -s SOURCE [--json] {log,ldb,indexeddb} ...
272
-
273
- A cli tool for the dfindexeddb package
286
+ usage: dfindexeddb [-h] -s SOURCE [--json]
274
287
 
275
- positional arguments:
276
- {log,ldb,indexeddb}
288
+ A cli tool for parsing indexeddb files
277
289
 
278
290
  options:
291
+ -h, --help show this help message and exit
279
292
  -s SOURCE, --source SOURCE
280
- The source leveldb file
293
+ The source leveldb folder
281
294
  --json Output as JSON
282
295
  ```
283
296
 
284
- To parse a LevelDB .log file:
297
+ ### LevelDB
285
298
 
286
299
  ```
287
- $ dfindexeddb -s <SOURCE> log -h
288
- usage: dfindexeddb log [-h] {blocks,physical_records,write_batches,parsed_internal_key,records}
300
+ $ dfleveldb -h
301
+ usage: dfleveldb [-h] {db,log,ldb,descriptor} ...
302
+
303
+ A cli tool for parsing leveldb files
289
304
 
290
305
  positional arguments:
291
- {blocks,physical_records,write_batches,parsed_internal_key,records}
306
+ {db,log,ldb,descriptor}
307
+ db Parse a directory as leveldb.
308
+ log Parse a leveldb log file.
309
+ ldb Parse a leveldb table (.ldb) file.
310
+ descriptor Parse a leveldb descriptor (MANIFEST) file.
292
311
 
293
312
  options:
294
313
  -h, --help show this help message and exit
295
314
  ```
296
315
 
297
- To parse a LevelDB .ldb file:
316
+ To parse records from a LevelDB log (.log) file, use the following command:
298
317
 
299
318
  ```
300
- $ dfindexeddb -s <SOURCE> ldb -h
301
- usage: dfindexeddb ldb [-h] {blocks,records}
319
+ $ dfleveldb log -s <SOURCE> [--json]
320
+ ```
302
321
 
303
- positional arguments:
304
- {blocks,records}
322
+ To parse records from a LevelDB table (.ldb) file, use the following command:
305
323
 
306
- options:
307
- -h, --help show this help message and exit
324
+ ```
325
+ $ dfleveldb ldb -s <SOURCE> [--json]
308
326
  ```
309
327
 
310
- To parse a LevelDB .ldb or .log file as IndexedDB:
328
+ To parse version edit records from a Descriptor (MANIFEST) file:
311
329
 
312
330
  ```
313
- $ dfindexeddb -s <SOURCE> indexeddb -h
314
- usage: dfindexeddb indexeddb [-h]
315
-
316
- options:
317
- -h, --help show this help message and exit
331
+ $ dfleveldb descriptor -s <SOURCE> [--json]
318
332
  ```
@@ -0,0 +1,26 @@
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=EVJT5s12vbz8xf-Q1XokuIHYzjSFiD4qJS1qBmpWjdc,750
5
+ dfindexeddb/indexeddb/__init__.py,sha256=kExXSVBCTKCD5BZJkdMfUMqGksH-DMJxP2_lI0gq-BE,575
6
+ dfindexeddb/indexeddb/blink.py,sha256=voC5P33MkyF9gMYJL6XVu2jDk68auTj3qxsCZw_x9rs,3548
7
+ dfindexeddb/indexeddb/chromium.py,sha256=wczfAYSVVWwfzrSf3O_DxZmjVy4b6fzBom6mr9nKZHA,44713
8
+ dfindexeddb/indexeddb/cli.py,sha256=drTSV2Eu-DKQev8xRqgifX68vTAjW0Nt_kUqT79-ZWo,3235
9
+ dfindexeddb/indexeddb/definitions.py,sha256=yline3y3gmZx6s-dwjpPDNs5HO4zT6KZqPWQfEsHDoM,7413
10
+ dfindexeddb/indexeddb/utils.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
+ dfindexeddb/indexeddb/v8.py,sha256=ldqpc9T1kG7BOdjnHjQ5hNO9OCXZ3_Zd6vRSpC-NrEA,21893
12
+ dfindexeddb/leveldb/__init__.py,sha256=KPYL9__l8od6_OyDfGRTgaJ6iy_fqIgZ-dS2S-e3Rac,599
13
+ dfindexeddb/leveldb/cli.py,sha256=tqu8Vpw45d38zVGLvTNDiNtppHoakj_4IrzURzQsEv8,6702
14
+ dfindexeddb/leveldb/definitions.py,sha256=wwm0uySOeI0-2fG9KvrY-StE0NP3iW5mCEuKYQL4ahg,1436
15
+ dfindexeddb/leveldb/descriptor.py,sha256=cw7A_KDiKf3ZyYThPbvC-LtnPM7Jvn3zr2jPswRBTq4,10426
16
+ dfindexeddb/leveldb/ldb.py,sha256=mN-M7PLtE_VLZCbCbzRgjkSezbMUhgDjgWgPgIxJ1jM,8087
17
+ dfindexeddb/leveldb/log.py,sha256=QeH8oESOPEZUjANGiDRSmXZa2SuoKlPFBJY7SxTV1lg,9209
18
+ dfindexeddb/leveldb/record.py,sha256=AnM4kQb81igmJla5q3rQUYvlzPwZaEHTvauxOC_dtM8,3217
19
+ dfindexeddb/leveldb/utils.py,sha256=RgEEZ7Z35m3CcOUypAiViQSzKjBgSXZ3aeJhQjY3H9w,3748
20
+ dfindexeddb-20240324.dist-info/AUTHORS,sha256=QbvjbAom57fpEkekkCVFUj0B9KUMGraR510aUMBC-PE,286
21
+ dfindexeddb-20240324.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
22
+ dfindexeddb-20240324.dist-info/METADATA,sha256=YT7g3UbA5SXyZZLKSYwwL8Yra8Vi40xh4eJ_G7rD1M4,16473
23
+ dfindexeddb-20240324.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
24
+ dfindexeddb-20240324.dist-info/entry_points.txt,sha256=WG9YNLZ9lBx4Q9QF6wS4dZdZfADT3Zs4_-MV5TcA0ls,102
25
+ dfindexeddb-20240324.dist-info/top_level.txt,sha256=X9OTaub1c8S_JJ7g-f8JdkhhdiZ4x1j4eni1hdUCwE4,12
26
+ dfindexeddb-20240324.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.42.0)
2
+ Generator: bdist_wheel (0.43.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ dfindexeddb = dfindexeddb.indexeddb.cli:App
3
+ dfleveldb = dfindexeddb.leveldb.cli:App