uplid 1.0.0__py3-none-any.whl → 1.0.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,296 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: uplid
|
|
3
|
+
Version: 1.0.1
|
|
4
|
+
Summary: Universal Prefixed Literal IDs - type-safe, human-readable identifiers
|
|
5
|
+
Keywords: uuid,id,identifier,pydantic,type-safe,uuid7
|
|
6
|
+
Author: ZVS
|
|
7
|
+
Author-email: ZVS <zvs@daswolf.dev>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
10
|
+
Classifier: Framework :: Pydantic :: 2
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
15
|
+
Classifier: Typing :: Typed
|
|
16
|
+
Requires-Dist: pydantic>=2.10
|
|
17
|
+
Requires-Python: >=3.14
|
|
18
|
+
Project-URL: Homepage, https://github.com/zvsdev/uplid
|
|
19
|
+
Project-URL: Repository, https://github.com/zvsdev/uplid
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
|
|
22
|
+
# UPLID
|
|
23
|
+
|
|
24
|
+
Universal Prefixed Literal IDs - type-safe, human-readable identifiers for Python 3.14+.
|
|
25
|
+
|
|
26
|
+
[](https://github.com/zvsdev/uplid/actions/workflows/ci.yml)
|
|
27
|
+
[](https://pypi.org/project/uplid/)
|
|
28
|
+
[](https://pypi.org/project/uplid/)
|
|
29
|
+
|
|
30
|
+
## Features
|
|
31
|
+
|
|
32
|
+
- **Type-safe prefixes**: `UPLID[Literal["usr"]]` prevents mixing user IDs with org IDs at compile time
|
|
33
|
+
- **Human-readable**: `usr_0M3xL9kQ7vR2nP5wY1jZ4c` (Stripe-style prefixed IDs)
|
|
34
|
+
- **Time-sortable**: Built on UUIDv7 (RFC 9562) for natural chronological ordering
|
|
35
|
+
- **Compact**: 22-character base62 encoding (URL-safe, no special characters)
|
|
36
|
+
- **Stdlib UUIDs**: Uses Python 3.14's native `uuid7()` - no external UUID libraries
|
|
37
|
+
- **Pydantic 2 native**: Full validation and serialization support
|
|
38
|
+
- **Thread-safe**: ID generation is safe for concurrent use
|
|
39
|
+
|
|
40
|
+
## Installation
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
pip install uplid
|
|
44
|
+
# or
|
|
45
|
+
uv add uplid
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Requires Python 3.14+ and Pydantic 2.10+.
|
|
49
|
+
|
|
50
|
+
## Quick Start
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
from typing import Literal
|
|
54
|
+
from pydantic import BaseModel, Field
|
|
55
|
+
from uplid import UPLID, factory
|
|
56
|
+
|
|
57
|
+
# Define typed aliases and factories
|
|
58
|
+
UserId = UPLID[Literal["usr"]]
|
|
59
|
+
OrgId = UPLID[Literal["org"]]
|
|
60
|
+
UserIdFactory = factory(UserId)
|
|
61
|
+
|
|
62
|
+
# Use in Pydantic models
|
|
63
|
+
class User(BaseModel):
|
|
64
|
+
id: UserId = Field(default_factory=UserIdFactory)
|
|
65
|
+
org_id: OrgId
|
|
66
|
+
|
|
67
|
+
# Generate IDs
|
|
68
|
+
user_id = UPLID.generate("usr")
|
|
69
|
+
print(user_id) # usr_0M3xL9kQ7vR2nP5wY1jZ4c
|
|
70
|
+
|
|
71
|
+
# Parse from string
|
|
72
|
+
parsed = UPLID.from_string("usr_0M3xL9kQ7vR2nP5wY1jZ4c", "usr")
|
|
73
|
+
|
|
74
|
+
# Access properties
|
|
75
|
+
print(parsed.datetime) # 2026-01-30 12:34:56.789000+00:00
|
|
76
|
+
print(parsed.timestamp) # 1738240496.789
|
|
77
|
+
|
|
78
|
+
# Type safety - these are compile-time errors:
|
|
79
|
+
# user.org_id = user_id # Error: UserId is not compatible with OrgId
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Pydantic Serialization
|
|
83
|
+
|
|
84
|
+
UPLIDs serialize to strings and deserialize with validation:
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
from pydantic import BaseModel, Field
|
|
88
|
+
from uplid import UPLID, factory
|
|
89
|
+
|
|
90
|
+
UserId = UPLID[Literal["usr"]]
|
|
91
|
+
UserIdFactory = factory(UserId)
|
|
92
|
+
|
|
93
|
+
class User(BaseModel):
|
|
94
|
+
id: UserId = Field(default_factory=UserIdFactory)
|
|
95
|
+
name: str
|
|
96
|
+
|
|
97
|
+
user = User(name="Alice")
|
|
98
|
+
|
|
99
|
+
# Serialize to dict - ID becomes string
|
|
100
|
+
user.model_dump()
|
|
101
|
+
# {"id": "usr_0M3xL9kQ7vR2nP5wY1jZ4c", "name": "Alice"}
|
|
102
|
+
|
|
103
|
+
# Serialize to JSON
|
|
104
|
+
json_str = user.model_dump_json()
|
|
105
|
+
|
|
106
|
+
# Deserialize - validates UPLID format and prefix
|
|
107
|
+
restored = User.model_validate_json(json_str)
|
|
108
|
+
assert restored.id == user.id
|
|
109
|
+
|
|
110
|
+
# Wrong prefix raises ValidationError
|
|
111
|
+
User(id="org_xxx...", name="Bad") # ValidationError
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## FastAPI Integration
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
from typing import Annotated, Literal
|
|
118
|
+
from fastapi import Cookie, Depends, FastAPI, Header, HTTPException
|
|
119
|
+
from pydantic import BaseModel, Field
|
|
120
|
+
from uplid import UPLID, UPLIDError, factory, parse
|
|
121
|
+
|
|
122
|
+
UserId = UPLID[Literal["usr"]]
|
|
123
|
+
UserIdFactory = factory(UserId)
|
|
124
|
+
parse_user_id = parse(UserId)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class User(BaseModel):
|
|
128
|
+
id: UserId = Field(default_factory=UserIdFactory)
|
|
129
|
+
name: str
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
app = FastAPI()
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
# Dependency for validating path/query/header/cookie parameters
|
|
136
|
+
def get_user_id(user_id: str) -> UserId:
|
|
137
|
+
try:
|
|
138
|
+
return parse_user_id(user_id)
|
|
139
|
+
except UPLIDError as e:
|
|
140
|
+
raise HTTPException(422, f"Invalid user ID: {e}") from None
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
# Path parameter validation
|
|
144
|
+
@app.get("/users/{user_id}")
|
|
145
|
+
def get_user(user_id: Annotated[UserId, Depends(get_user_id)]) -> User:
|
|
146
|
+
...
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
# JSON body - Pydantic validates UPLID fields automatically
|
|
150
|
+
@app.post("/users")
|
|
151
|
+
def create_user(user: User) -> User:
|
|
152
|
+
# user.id validated as UserId, wrong prefix returns 422
|
|
153
|
+
return user
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
# Header validation
|
|
157
|
+
def get_user_id_from_header(x_user_id: Annotated[str, Header()]) -> UserId:
|
|
158
|
+
try:
|
|
159
|
+
return parse_user_id(x_user_id)
|
|
160
|
+
except UPLIDError as e:
|
|
161
|
+
raise HTTPException(422, f"Invalid X-User-Id header: {e}") from None
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
@app.get("/me")
|
|
165
|
+
def get_current_user(user_id: Annotated[UserId, Depends(get_user_id_from_header)]) -> User:
|
|
166
|
+
...
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
# Cookie validation
|
|
170
|
+
def get_session_user(session_user_id: Annotated[str, Cookie()]) -> UserId:
|
|
171
|
+
try:
|
|
172
|
+
return parse_user_id(session_user_id)
|
|
173
|
+
except UPLIDError as e:
|
|
174
|
+
raise HTTPException(422, f"Invalid session cookie: {e}") from None
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
@app.get("/session")
|
|
178
|
+
def get_session(user_id: Annotated[UserId, Depends(get_session_user)]) -> User:
|
|
179
|
+
...
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## Database Storage
|
|
183
|
+
|
|
184
|
+
UPLIDs serialize to strings. Store as `VARCHAR(87)` (64 char prefix + 1 underscore + 22 char base62):
|
|
185
|
+
|
|
186
|
+
```python
|
|
187
|
+
from typing import Literal
|
|
188
|
+
from sqlalchemy import String, create_engine
|
|
189
|
+
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, Session
|
|
190
|
+
from uplid import UPLID, factory
|
|
191
|
+
|
|
192
|
+
UserId = UPLID[Literal["usr"]]
|
|
193
|
+
UserIdFactory = factory(UserId)
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
class Base(DeclarativeBase):
|
|
197
|
+
pass
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
class UserRow(Base):
|
|
201
|
+
__tablename__ = "users"
|
|
202
|
+
|
|
203
|
+
id: Mapped[str] = mapped_column(String(87), primary_key=True)
|
|
204
|
+
name: Mapped[str] = mapped_column(String(100))
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
# Create with UPLID, store as string
|
|
208
|
+
engine = create_engine("sqlite:///:memory:")
|
|
209
|
+
Base.metadata.create_all(engine)
|
|
210
|
+
|
|
211
|
+
with Session(engine) as session:
|
|
212
|
+
user = UserRow(id=str(UPLID.generate("usr")), name="Alice")
|
|
213
|
+
session.add(user)
|
|
214
|
+
session.commit()
|
|
215
|
+
|
|
216
|
+
# Query and parse back to UPLID
|
|
217
|
+
row = session.query(UserRow).first()
|
|
218
|
+
user_id = UPLID.from_string(row.id, "usr")
|
|
219
|
+
print(user_id.datetime) # When the ID was created
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## Prefix Rules
|
|
223
|
+
|
|
224
|
+
Prefixes must be snake_case:
|
|
225
|
+
- Lowercase letters and single underscores only
|
|
226
|
+
- Cannot start or end with underscore
|
|
227
|
+
- Maximum 64 characters
|
|
228
|
+
- Examples: `usr`, `api_key`, `org_member`
|
|
229
|
+
|
|
230
|
+
## API Reference
|
|
231
|
+
|
|
232
|
+
### `UPLID[PREFIX]`
|
|
233
|
+
|
|
234
|
+
Generic class for prefixed IDs.
|
|
235
|
+
|
|
236
|
+
```python
|
|
237
|
+
# Generate new ID
|
|
238
|
+
uid = UPLID.generate("usr")
|
|
239
|
+
|
|
240
|
+
# Parse from string
|
|
241
|
+
uid = UPLID.from_string("usr_0M3xL9kQ7vR2nP5wY1jZ4c", "usr")
|
|
242
|
+
|
|
243
|
+
# Properties
|
|
244
|
+
uid.prefix # str: "usr"
|
|
245
|
+
uid.uid # UUID: underlying UUIDv7
|
|
246
|
+
uid.base62_uid # str: 22-char base62 encoding
|
|
247
|
+
uid.datetime # datetime: UTC timestamp from UUIDv7
|
|
248
|
+
uid.timestamp # float: Unix timestamp in seconds
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### `factory(UPLIDType)`
|
|
252
|
+
|
|
253
|
+
Creates a factory function for Pydantic's `default_factory`.
|
|
254
|
+
|
|
255
|
+
```python
|
|
256
|
+
UserId = UPLID[Literal["usr"]]
|
|
257
|
+
UserIdFactory = factory(UserId)
|
|
258
|
+
|
|
259
|
+
class User(BaseModel):
|
|
260
|
+
id: UserId = Field(default_factory=UserIdFactory)
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### `parse(UPLIDType)`
|
|
264
|
+
|
|
265
|
+
Creates a parser function that raises `UPLIDError` on invalid input.
|
|
266
|
+
|
|
267
|
+
```python
|
|
268
|
+
from uplid import UPLID, parse, UPLIDError
|
|
269
|
+
|
|
270
|
+
UserId = UPLID[Literal["usr"]]
|
|
271
|
+
parse_user_id = parse(UserId)
|
|
272
|
+
|
|
273
|
+
try:
|
|
274
|
+
uid = parse_user_id("usr_0M3xL9kQ7vR2nP5wY1jZ4c")
|
|
275
|
+
except UPLIDError as e:
|
|
276
|
+
print(e)
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### `UPLIDType`
|
|
280
|
+
|
|
281
|
+
Protocol for generic functions accepting any UPLID:
|
|
282
|
+
|
|
283
|
+
```python
|
|
284
|
+
from uplid import UPLIDType
|
|
285
|
+
|
|
286
|
+
def log_entity(id: UPLIDType) -> None:
|
|
287
|
+
print(f"{id.prefix} created at {id.datetime}")
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### `UPLIDError`
|
|
291
|
+
|
|
292
|
+
Exception raised for invalid IDs. Subclasses `ValueError`.
|
|
293
|
+
|
|
294
|
+
## License
|
|
295
|
+
|
|
296
|
+
MIT
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
uplid/__init__.py,sha256=96PZKnozPOjHfFbWko0jGzynDNcrr6Sm6jPhwAGnQxU,253
|
|
2
2
|
uplid/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
3
|
uplid/uplid.py,sha256=krCFV7eFOcFn7KV8R1ko6B9lK37AzcrEJlciMmNvgmA,15108
|
|
4
|
-
uplid-1.0.
|
|
5
|
-
uplid-1.0.
|
|
6
|
-
uplid-1.0.
|
|
4
|
+
uplid-1.0.1.dist-info/WHEEL,sha256=fAguSjoiATBe7TNBkJwOjyL1Tt4wwiaQGtNtjRPNMQA,80
|
|
5
|
+
uplid-1.0.1.dist-info/METADATA,sha256=OBp_290PWT_NA2dGPyD5TwBcT60QA5Gdb2myQhHaOG8,7704
|
|
6
|
+
uplid-1.0.1.dist-info/RECORD,,
|
uplid-1.0.0.dist-info/METADATA
DELETED
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: uplid
|
|
3
|
-
Version: 1.0.0
|
|
4
|
-
Summary: Universal Prefixed Literal IDs - type-safe, human-readable identifiers
|
|
5
|
-
Keywords: uuid,id,identifier,pydantic,type-safe,uuid7
|
|
6
|
-
Author: ZVS
|
|
7
|
-
Author-email: ZVS <zvs@daswolf.dev>
|
|
8
|
-
License-Expression: MIT
|
|
9
|
-
Classifier: Development Status :: 5 - Production/Stable
|
|
10
|
-
Classifier: Framework :: Pydantic :: 2
|
|
11
|
-
Classifier: Intended Audience :: Developers
|
|
12
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
-
Classifier: Operating System :: OS Independent
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.14
|
|
15
|
-
Classifier: Typing :: Typed
|
|
16
|
-
Requires-Dist: pydantic>=2.10
|
|
17
|
-
Requires-Python: >=3.14
|
|
18
|
-
Project-URL: Homepage, https://github.com/zvsdev/uplid
|
|
19
|
-
Project-URL: Repository, https://github.com/zvsdev/uplid
|
|
20
|
-
Description-Content-Type: text/markdown
|
|
21
|
-
|
|
22
|
-
# UPLID
|
|
23
|
-
|
|
24
|
-
Universal Prefixed Literal IDs - type-safe, human-readable identifiers for Python 3.14+.
|
|
25
|
-
|
|
26
|
-
[](https://github.com/zvsdev/uplid/actions/workflows/ci.yml)
|
|
27
|
-
[](https://pypi.org/project/uplid/)
|
|
28
|
-
[](https://pypi.org/project/uplid/)
|
|
29
|
-
|
|
30
|
-
## Features
|
|
31
|
-
|
|
32
|
-
- **Type-safe prefixes**: `UPLID[Literal["usr"]]` prevents mixing user IDs with org IDs
|
|
33
|
-
- **Human-readable**: `usr_4mJ9k2L8nP3qR7sT1vW5xY` (Stripe-style)
|
|
34
|
-
- **Time-sortable**: Built on UUIDv7 for natural ordering
|
|
35
|
-
- **Compact**: 22-character base62 encoding
|
|
36
|
-
- **Zero external deps**: Uses Python 3.14's stdlib `uuid7()`
|
|
37
|
-
- **Pydantic 2 native**: Full validation and serialization support
|
|
38
|
-
|
|
39
|
-
## Installation
|
|
40
|
-
|
|
41
|
-
```bash
|
|
42
|
-
pip install uplid
|
|
43
|
-
# or
|
|
44
|
-
uv add uplid
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
Requires Python 3.14+.
|
|
48
|
-
|
|
49
|
-
## Quick Start
|
|
50
|
-
|
|
51
|
-
```python
|
|
52
|
-
from typing import Literal
|
|
53
|
-
from pydantic import BaseModel, Field
|
|
54
|
-
from uplid import UPLID, factory
|
|
55
|
-
|
|
56
|
-
# Define typed ID aliases
|
|
57
|
-
UserId = UPLID[Literal["usr"]]
|
|
58
|
-
OrgId = UPLID[Literal["org"]]
|
|
59
|
-
|
|
60
|
-
# Use in Pydantic models
|
|
61
|
-
class User(BaseModel):
|
|
62
|
-
id: UserId = Field(default_factory=factory(UserId))
|
|
63
|
-
org_id: OrgId
|
|
64
|
-
|
|
65
|
-
# Generate IDs
|
|
66
|
-
user_id = UPLID.generate("usr")
|
|
67
|
-
print(user_id) # usr_4mJ9k2L8nP3qR7sT1vW5xY
|
|
68
|
-
|
|
69
|
-
# Parse from string
|
|
70
|
-
parsed = UPLID.from_string("usr_4mJ9k2L8nP3qR7sT1vW5xY", "usr")
|
|
71
|
-
|
|
72
|
-
# Type safety - these are compile-time errors with ty/mypy:
|
|
73
|
-
# user.org_id = user_id # Error: UserId != OrgId
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
## Prefix Rules
|
|
77
|
-
|
|
78
|
-
Prefixes must be snake_case:
|
|
79
|
-
- Lowercase letters and underscores only
|
|
80
|
-
- Cannot start or end with underscore
|
|
81
|
-
- Examples: `usr`, `api_key`, `org_member`
|
|
82
|
-
|
|
83
|
-
## API Reference
|
|
84
|
-
|
|
85
|
-
### `UPLID[PREFIX]`
|
|
86
|
-
|
|
87
|
-
Generic class for prefixed IDs.
|
|
88
|
-
|
|
89
|
-
```python
|
|
90
|
-
# Generate new ID
|
|
91
|
-
uid = UPLID.generate("usr")
|
|
92
|
-
|
|
93
|
-
# Parse from string
|
|
94
|
-
uid = UPLID.from_string("usr_abc123...", "usr")
|
|
95
|
-
|
|
96
|
-
# Properties
|
|
97
|
-
uid.prefix # "usr"
|
|
98
|
-
uid.uid # UUID object
|
|
99
|
-
uid.datetime # datetime from UUIDv7
|
|
100
|
-
uid.timestamp # float (Unix timestamp)
|
|
101
|
-
uid.base62_uid # "abc123..." (22 chars)
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
### `factory(UPLIDType)`
|
|
105
|
-
|
|
106
|
-
Creates a factory function for Pydantic's `default_factory`.
|
|
107
|
-
|
|
108
|
-
```python
|
|
109
|
-
UserId = UPLID[Literal["usr"]]
|
|
110
|
-
|
|
111
|
-
class User(BaseModel):
|
|
112
|
-
id: UserId = Field(default_factory=factory(UserId))
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
### `parse(UPLIDType)`
|
|
116
|
-
|
|
117
|
-
Creates a parser function that raises `UPLIDError` on invalid input.
|
|
118
|
-
|
|
119
|
-
```python
|
|
120
|
-
from uplid import UPLID, parse, UPLIDError
|
|
121
|
-
|
|
122
|
-
UserId = UPLID[Literal["usr"]]
|
|
123
|
-
parse_user_id = parse(UserId)
|
|
124
|
-
|
|
125
|
-
try:
|
|
126
|
-
uid = parse_user_id("usr_abc123...")
|
|
127
|
-
except UPLIDError as e:
|
|
128
|
-
print(e)
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
### `UPLIDType`
|
|
132
|
-
|
|
133
|
-
Protocol for generic functions accepting any UPLID:
|
|
134
|
-
|
|
135
|
-
```python
|
|
136
|
-
from uplid import UPLIDType
|
|
137
|
-
|
|
138
|
-
def log_entity(id: UPLIDType) -> None:
|
|
139
|
-
print(f"{id.prefix} created at {id.datetime}")
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
### `UPLIDError`
|
|
143
|
-
|
|
144
|
-
Exception raised for invalid IDs. Subclasses `ValueError`.
|
|
145
|
-
|
|
146
|
-
```python
|
|
147
|
-
from uplid import UPLIDError
|
|
148
|
-
|
|
149
|
-
try:
|
|
150
|
-
UPLID.from_string("invalid", "usr")
|
|
151
|
-
except UPLIDError as e:
|
|
152
|
-
print(e)
|
|
153
|
-
except ValueError: # Also works
|
|
154
|
-
pass
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
## License
|
|
158
|
-
|
|
159
|
-
MIT
|
|
File without changes
|