TypeDAL 4.7.2__tar.gz → 4.8.0__tar.gz

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.
Files changed (75) hide show
  1. {typedal-4.7.2 → typedal-4.8.0}/CHANGELOG.md +11 -0
  2. {typedal-4.7.2 → typedal-4.8.0}/PKG-INFO +19 -4
  3. {typedal-4.7.2 → typedal-4.8.0}/README.md +1 -0
  4. {typedal-4.7.2 → typedal-4.8.0}/docs/10_advanced_apis.md +53 -0
  5. {typedal-4.7.2 → typedal-4.8.0}/docs/6_migrations.md +12 -0
  6. {typedal-4.7.2 → typedal-4.8.0}/docs/7_configuration.md +2 -0
  7. {typedal-4.7.2 → typedal-4.8.0}/pyproject.toml +29 -5
  8. {typedal-4.7.2 → typedal-4.8.0}/src/typedal/__about__.py +1 -1
  9. {typedal-4.7.2 → typedal-4.8.0}/src/typedal/__init__.py +2 -1
  10. {typedal-4.7.2 → typedal-4.8.0}/src/typedal/cli.py +105 -5
  11. {typedal-4.7.2 → typedal-4.8.0}/src/typedal/config.py +3 -2
  12. {typedal-4.7.2 → typedal-4.8.0}/src/typedal/core.py +27 -0
  13. {typedal-4.7.2 → typedal-4.8.0}/src/typedal/define.py +3 -2
  14. {typedal-4.7.2 → typedal-4.8.0}/src/typedal/enum_helpers.py +6 -0
  15. {typedal-4.7.2 → typedal-4.8.0}/src/typedal/fields.py +2 -4
  16. {typedal-4.7.2 → typedal-4.8.0}/src/typedal/mixins.py +29 -90
  17. typedal-4.8.0/src/typedal/serializers/typescript.py +79 -0
  18. {typedal-4.7.2 → typedal-4.8.0}/src/typedal/tables.py +216 -5
  19. {typedal-4.7.2 → typedal-4.8.0}/tests/test_cli.py +42 -4
  20. {typedal-4.7.2 → typedal-4.8.0}/tests/test_config.py +27 -1
  21. {typedal-4.7.2 → typedal-4.8.0}/tests/test_main.py +6 -2
  22. {typedal-4.7.2 → typedal-4.8.0}/tests/test_mypy.py +14 -14
  23. {typedal-4.7.2 → typedal-4.8.0}/tests/test_query_builder.py +1 -4
  24. {typedal-4.7.2 → typedal-4.8.0}/tests/test_table.py +1 -1
  25. typedal-4.8.0/tests/test_typescript.py +190 -0
  26. typedal-4.7.2/.crush/.gitignore +0 -1
  27. typedal-4.7.2/.crush/init +0 -0
  28. typedal-4.7.2/.crush/logs/crush.log +0 -207
  29. {typedal-4.7.2 → typedal-4.8.0}/.github/workflows/su6.yml +0 -0
  30. {typedal-4.7.2 → typedal-4.8.0}/.gitignore +0 -0
  31. {typedal-4.7.2 → typedal-4.8.0}/.readthedocs.yml +0 -0
  32. {typedal-4.7.2 → typedal-4.8.0}/coverage.svg +0 -0
  33. {typedal-4.7.2 → typedal-4.8.0}/docs/1_getting_started.md +0 -0
  34. {typedal-4.7.2 → typedal-4.8.0}/docs/2_defining_tables.md +0 -0
  35. {typedal-4.7.2 → typedal-4.8.0}/docs/3_building_queries.md +0 -0
  36. {typedal-4.7.2 → typedal-4.8.0}/docs/4_relationships.md +0 -0
  37. {typedal-4.7.2 → typedal-4.8.0}/docs/5_py4web.md +0 -0
  38. {typedal-4.7.2 → typedal-4.8.0}/docs/8_mixins.md +0 -0
  39. {typedal-4.7.2 → typedal-4.8.0}/docs/9_memoization.md +0 -0
  40. {typedal-4.7.2 → typedal-4.8.0}/docs/css/code_blocks.css +0 -0
  41. {typedal-4.7.2 → typedal-4.8.0}/docs/index.md +0 -0
  42. {typedal-4.7.2 → typedal-4.8.0}/docs/requirements.txt +0 -0
  43. {typedal-4.7.2 → typedal-4.8.0}/example_new.py +0 -0
  44. {typedal-4.7.2 → typedal-4.8.0}/example_old.py +0 -0
  45. {typedal-4.7.2 → typedal-4.8.0}/mkdocs.yml +0 -0
  46. {typedal-4.7.2 → typedal-4.8.0}/src/typedal/caching.py +0 -0
  47. {typedal-4.7.2 → typedal-4.8.0}/src/typedal/constants.py +0 -0
  48. {typedal-4.7.2 → typedal-4.8.0}/src/typedal/for_py4web.py +0 -0
  49. {typedal-4.7.2 → typedal-4.8.0}/src/typedal/for_web2py.py +0 -0
  50. {typedal-4.7.2 → typedal-4.8.0}/src/typedal/helpers.py +0 -0
  51. {typedal-4.7.2 → typedal-4.8.0}/src/typedal/py.typed +0 -0
  52. {typedal-4.7.2 → typedal-4.8.0}/src/typedal/query_builder.py +0 -0
  53. {typedal-4.7.2 → typedal-4.8.0}/src/typedal/relationships.py +0 -0
  54. {typedal-4.7.2 → typedal-4.8.0}/src/typedal/rows.py +0 -0
  55. {typedal-4.7.2 → typedal-4.8.0}/src/typedal/serializers/as_json.py +0 -0
  56. {typedal-4.7.2 → typedal-4.8.0}/src/typedal/types.py +0 -0
  57. {typedal-4.7.2 → typedal-4.8.0}/src/typedal/web2py_py4web_shared.py +0 -0
  58. {typedal-4.7.2 → typedal-4.8.0}/tasks.py +0 -0
  59. {typedal-4.7.2 → typedal-4.8.0}/tests/__init__.py +0 -0
  60. {typedal-4.7.2 → typedal-4.8.0}/tests/configs/simple.toml +0 -0
  61. {typedal-4.7.2 → typedal-4.8.0}/tests/configs/valid.env +0 -0
  62. {typedal-4.7.2 → typedal-4.8.0}/tests/configs/valid.toml +0 -0
  63. {typedal-4.7.2 → typedal-4.8.0}/tests/py314_tests.py +0 -0
  64. {typedal-4.7.2 → typedal-4.8.0}/tests/test_docs_examples.py +0 -0
  65. {typedal-4.7.2 → typedal-4.8.0}/tests/test_helpers.py +0 -0
  66. {typedal-4.7.2 → typedal-4.8.0}/tests/test_json.py +0 -0
  67. {typedal-4.7.2 → typedal-4.8.0}/tests/test_mixins.py +0 -0
  68. {typedal-4.7.2 → typedal-4.8.0}/tests/test_orm.py +0 -0
  69. {typedal-4.7.2 → typedal-4.8.0}/tests/test_py4web.py +0 -0
  70. {typedal-4.7.2 → typedal-4.8.0}/tests/test_relationships.py +0 -0
  71. {typedal-4.7.2 → typedal-4.8.0}/tests/test_row.py +0 -0
  72. {typedal-4.7.2 → typedal-4.8.0}/tests/test_stats.py +0 -0
  73. {typedal-4.7.2 → typedal-4.8.0}/tests/test_web2py.py +0 -0
  74. {typedal-4.7.2 → typedal-4.8.0}/tests/test_xx_others.py +0 -0
  75. {typedal-4.7.2 → typedal-4.8.0}/tests/timings.py +0 -0
@@ -2,6 +2,17 @@
2
2
 
3
3
  <!--next-version-placeholder-->
4
4
 
5
+ ## v4.8.0 (2026-04-23)
6
+
7
+ ### Feature
8
+
9
+ * TypeDAL 4.8: type... script? ([#9](https://github.com/trialandsuccess/TypeDAL/issues/9)) ([`9a688c0`](https://github.com/trialandsuccess/TypeDAL/commit/9a688c012ddcb77207b5879b7c66c6c2fee547c0))
10
+
11
+ ### Fix
12
+
13
+ * **uuid:** Don't try to store NULL uuid as 'None' ([`29f8606`](https://github.com/trialandsuccess/TypeDAL/commit/29f8606972110b5472a42806c6c0e5b66f50095a))
14
+ * Ensure `InvalidEnumValue` is falsey ([`21fea08`](https://github.com/trialandsuccess/TypeDAL/commit/21fea086e3219d47615f7b9539caf4d5f5c48327))
15
+
5
16
  ## v4.7.2 (2026-04-20)
6
17
 
7
18
  ### Fix
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: TypeDAL
3
- Version: 4.7.2
3
+ Version: 4.8.0
4
4
  Summary: Typing support for PyDAL
5
5
  Project-URL: Documentation, https://typedal.readthedocs.io/
6
6
  Project-URL: Issues, https://github.com/trialandsuccess/TypeDAL/issues
@@ -15,7 +15,7 @@ Classifier: Programming Language :: Python :: Implementation :: CPython
15
15
  Classifier: Programming Language :: Python :: Implementation :: PyPy
16
16
  Requires-Python: >=3.12
17
17
  Requires-Dist: configurable-json<2
18
- Requires-Dist: configuraptor<3,>=2.0.1
18
+ Requires-Dist: configuraptor<3,>=2.1.0
19
19
  Requires-Dist: dill<1
20
20
  Requires-Dist: legacy-cgi; python_version >= '3.13'
21
21
  Requires-Dist: pydal>=20251012.3
@@ -24,12 +24,19 @@ Requires-Dist: python-slugify<9
24
24
  Provides-Extra: all
25
25
  Requires-Dist: edwh-migrate[full]>=0.8.0; extra == 'all'
26
26
  Requires-Dist: py4web; extra == 'all'
27
- Requires-Dist: pydal2sql[all]>=1.2.0; extra == 'all'
27
+ Requires-Dist: pydal2sql[all]>=1.3.5; extra == 'all'
28
28
  Requires-Dist: pydantic<3; extra == 'all'
29
29
  Requires-Dist: questionary; extra == 'all'
30
30
  Requires-Dist: tabulate; extra == 'all'
31
31
  Requires-Dist: tomlkit; extra == 'all'
32
32
  Requires-Dist: typer<0.19,>=0.18; extra == 'all'
33
+ Requires-Dist: typtyp<1; extra == 'all'
34
+ Provides-Extra: cli
35
+ Requires-Dist: pydal2sql>=1.3.5; extra == 'cli'
36
+ Requires-Dist: questionary; extra == 'cli'
37
+ Requires-Dist: tabulate; extra == 'cli'
38
+ Requires-Dist: tomlkit; extra == 'cli'
39
+ Requires-Dist: typer<0.19,>=0.18; extra == 'cli'
33
40
  Provides-Extra: dev
34
41
  Requires-Dist: contextlib-chdir; extra == 'dev'
35
42
  Requires-Dist: ewok; extra == 'dev'
@@ -47,13 +54,20 @@ Requires-Dist: types-requests; extra == 'dev'
47
54
  Requires-Dist: types-tabulate; extra == 'dev'
48
55
  Provides-Extra: migrations
49
56
  Requires-Dist: edwh-migrate>=0.8.0; extra == 'migrations'
50
- Requires-Dist: pydal2sql>=1.2.0; extra == 'migrations'
57
+ Requires-Dist: pydal2sql>=1.3.5; extra == 'migrations'
51
58
  Requires-Dist: questionary; extra == 'migrations'
52
59
  Requires-Dist: tabulate; extra == 'migrations'
53
60
  Requires-Dist: tomlkit; extra == 'migrations'
54
61
  Requires-Dist: typer<0.19,>=0.18; extra == 'migrations'
55
62
  Provides-Extra: py4web
56
63
  Requires-Dist: py4web; extra == 'py4web'
64
+ Provides-Extra: typescript
65
+ Requires-Dist: pydal2sql>=1.3.5; extra == 'typescript'
66
+ Requires-Dist: questionary; extra == 'typescript'
67
+ Requires-Dist: tabulate; extra == 'typescript'
68
+ Requires-Dist: tomlkit; extra == 'typescript'
69
+ Requires-Dist: typer<0.19,>=0.18; extra == 'typescript'
70
+ Requires-Dist: typtyp<1; extra == 'typescript'
57
71
  Description-Content-Type: text/markdown
58
72
 
59
73
  # TypeDAL
@@ -149,6 +163,7 @@ typedal --help
149
163
  - `migrations.fake`: Mark one or more migrations as completed in the database without executing the SQL code.
150
164
  - `migrations.generate`: Run `pydal2sql` based on the TypeDAL configuration.
151
165
  - `migrations.run`: Run `edwh-migrate` based on the TypeDAL configuration.
166
+ - `typescript.generate`: Generate TypeScript interfaces from TypeDAL models.
152
167
  - `setup`: Interactively setup a `[tool.typedal]` entry in the local `pyproject.toml`.
153
168
 
154
169
  ### Configuration
@@ -91,6 +91,7 @@ typedal --help
91
91
  - `migrations.fake`: Mark one or more migrations as completed in the database without executing the SQL code.
92
92
  - `migrations.generate`: Run `pydal2sql` based on the TypeDAL configuration.
93
93
  - `migrations.run`: Run `edwh-migrate` based on the TypeDAL configuration.
94
+ - `typescript.generate`: Generate TypeScript interfaces from TypeDAL models.
94
95
  - `setup`: Interactively setup a `[tool.typedal]` entry in the local `pyproject.toml`.
95
96
 
96
97
  ### Configuration
@@ -74,3 +74,56 @@ MyTable.reorder_fields(MyTable.id, MyTable.name, keep_others=False)
74
74
  ```
75
75
 
76
76
  This is useful when you want deterministic field order for SQL generation, inspection, or exports.
77
+
78
+ ## TypeScript schema generation
79
+
80
+ TypeDAL can generate TypeScript types from your models.
81
+
82
+ Install the optional dependency first:
83
+
84
+ ```bash
85
+ uv pip install TypeDAL[typescript] # or typedal[all]
86
+ ```
87
+
88
+ ### From Python APIs
89
+
90
+ Generate schema for one model:
91
+
92
+ ```python
93
+ ts = User.as_typescript()
94
+ print(ts)
95
+ ```
96
+
97
+ Generate schema for all currently defined models on a database instance:
98
+
99
+ ```python
100
+ ts = db.as_typescript()
101
+ print(ts)
102
+ ```
103
+
104
+ Generate schema for a subset of models:
105
+
106
+ ```python
107
+ ts = db.as_typescript("User", "Post")
108
+ # or:
109
+ ts = db.as_typescript(User, Post)
110
+ ```
111
+
112
+ ### From the CLI
113
+
114
+ Generate TypeScript from your configured table definitions:
115
+
116
+ ```bash
117
+ typedal typescript.generate
118
+ ```
119
+
120
+ Useful variants:
121
+
122
+ ```bash
123
+ typedal typescript.generate path/to/models.py
124
+ typedal typescript.generate --tables User --tables Post
125
+ typedal typescript.generate --output-file src/types/typedal.ts
126
+ ```
127
+
128
+ Configuration details for `typescript.generate` (including `typescript_output`) are documented in
129
+ [7. Configuration](./7_configuration.md).
@@ -88,3 +88,15 @@ You can however overwrite the behavior as defined in the config. See all options
88
88
  ```bash
89
89
  typedal migrations.run --help
90
90
  ```
91
+
92
+ ## Related: TypeScript generation
93
+
94
+ If you also want TypeScript types from your TypeDAL models, use:
95
+
96
+ ```bash
97
+ typedal typescript.generate
98
+ ```
99
+
100
+ This command is separate from migrations, but uses the same style of TypeDAL config and model discovery.
101
+ See [10. Advanced APIs](./10_advanced_apis.md#typescript-schema-generation) for usage,
102
+ and [7. Configuration](./7_configuration.md) for config options such as `typescript_output`.
@@ -50,6 +50,7 @@ fake_migrate = false
50
50
  database_to_restore = "data/backup.sql"
51
51
  tables = ["users", "posts"]
52
52
  noop = false
53
+ typescript_output = "src/types/typedal.ts"
53
54
  ```
54
55
 
55
56
  ### Generating Migrations (pydal2sql)
@@ -62,6 +63,7 @@ noop = false
62
63
  - **`function`**: Function name containing your `db.define()` calls (default: `"define_tables"`)
63
64
  - **`tables`**: Specific tables to generate migrations for (optional; usually set via CLI instead)
64
65
  - **`noop`**: Don't write to output (usually set via CLI)
66
+ - **`typescript_output`**: Output path used by `typedal typescript.generate` (optional)
65
67
 
66
68
  ### Running Migrations (edwh-migrate)
67
69
 
@@ -28,7 +28,7 @@ classifiers = [
28
28
  dependencies = [
29
29
  "pydal >= 20251012.3", # core
30
30
  "dill < 1", # caching
31
- "configuraptor >= 2.0.1, < 3", # config
31
+ "configuraptor >= 2.1.0, < 3", # config
32
32
  "Configurable-JSON < 2", # json dumping
33
33
  "python-slugify < 9",
34
34
  "legacy-cgi; python_version >= '3.13'",
@@ -40,24 +40,48 @@ py4web = [
40
40
  "py4web",
41
41
  ]
42
42
 
43
- migrations = [
43
+ cli = [
44
44
  "typer >=0.18, <0.19",
45
45
  "tabulate",
46
- "pydal2sql>=1.2.0",
46
+ "questionary",
47
+ "tomlkit",
48
+ "pydal2sql>=1.3.5",
49
+ ]
50
+
51
+ migrations = [
52
+ "tabulate",
53
+ "pydal2sql>=1.3.5",
47
54
  "edwh-migrate>=0.8.0",
55
+ # cli:
56
+ "typer >=0.18, <0.19",
57
+ "tabulate",
48
58
  "questionary",
49
59
  "tomlkit",
60
+ "pydal2sql>=1.3.5",
61
+ ]
62
+
63
+ typescript = [
64
+ "typtyp < 1",
65
+ # cli:
66
+ "typer >=0.18, <0.19",
67
+ "tabulate",
68
+ "questionary",
69
+ "tomlkit",
70
+ "pydal2sql>=1.3.5",
50
71
  ]
51
72
 
52
73
  all = [
53
74
  "py4web",
54
- "typer >=0.18, <0.19",
75
+ "typtyp < 1",
55
76
  "tabulate",
56
- "pydal2sql[all]>=1.2.0",
57
77
  "edwh-migrate[full]>=0.8.0",
58
78
  "pydantic < 3",
79
+ # cli:
80
+ "typer >=0.18, <0.19",
81
+ "tabulate",
59
82
  "questionary",
60
83
  "tomlkit",
84
+ "pydal2sql[all]>=1.3.5",
61
85
  ]
62
86
 
63
87
  dev = [
@@ -5,4 +5,4 @@ This file contains the Version info for this package.
5
5
  # SPDX-FileCopyrightText: 2023-present Robin van der Noord <robinvandernoord@gmail.com>
6
6
  #
7
7
  # SPDX-License-Identifier: MIT
8
- __version__ = "4.7.2"
8
+ __version__ = "4.8.0"
@@ -6,7 +6,7 @@ from .core import TypeDAL
6
6
  from .fields import TypedField
7
7
  from .helpers import sql_expression
8
8
  from .query_builder import QueryBuilder
9
- from .relationships import Relationship, relationship
9
+ from .relationships import Ref, Relationship, relationship
10
10
  from .rows import PaginatedRows, TypedRows
11
11
  from .tables import TypedTable
12
12
 
@@ -20,6 +20,7 @@ except ImportError: # pragma: no cover
20
20
  __all__ = [
21
21
  "PaginatedRows",
22
22
  "QueryBuilder",
23
+ "Ref",
23
24
  "Relationship",
24
25
  "TypeDAL",
25
26
  "TypedField",
@@ -17,7 +17,6 @@ from .helpers import match_strings
17
17
  from .types import AnyDict
18
18
 
19
19
  try:
20
- import edwh_migrate
21
20
  import pydal2sql # noqa: F401
22
21
  import questionary
23
22
  import rich
@@ -27,12 +26,16 @@ try:
27
26
  except ImportError as e:
28
27
  # ImportWarning is hidden by default
29
28
  warnings.warn(
30
- "`migrations` extra not installed. Please run `pip install typedal[migrations]` to fix this.",
29
+ "`cli` extra not installed. Please run `pip install typedal[cli]` "
30
+ "(or `typedal[migrations]`, `typedal[typescript]`, `typedal[all]`) "
31
+ "to fix this.",
31
32
  source=e,
32
33
  category=RuntimeWarning,
33
34
  )
34
35
  exit(127) # command not found
35
36
 
37
+ from typing import Never
38
+
36
39
  from pydal2sql.typer_support import IS_DEBUG, with_exit_code
37
40
  from pydal2sql.types import (
38
41
  DBType_Option,
@@ -41,7 +44,11 @@ from pydal2sql.types import (
41
44
  Tables_Option,
42
45
  )
43
46
  from pydal2sql_core import core_alter, core_create, core_stub
44
- from typing_extensions import Never
47
+ from pydal2sql_core.cli_support import (
48
+ RenderContext,
49
+ find_file_contents,
50
+ render_schema_from_code,
51
+ )
45
52
 
46
53
  from . import caching
47
54
  from .__about__ import __version__
@@ -86,6 +93,11 @@ questionary_types: dict[typing.Hashable, Optional[AnyDict]] = {
86
93
  "message": "Python file where migrations will be written to.",
87
94
  "file_filter": lambda file: "." not in file or file.endswith(".py"),
88
95
  },
96
+ "typescript_output": {
97
+ "type": "path",
98
+ "message": "File where generated TypeScript will be written to.",
99
+ "file_filter": lambda file: "." not in file or file.endswith(".ts"),
100
+ },
89
101
  # disabled props:
90
102
  "pyproject": None, # internal
91
103
  "noop": None, # only for debugging
@@ -315,6 +327,16 @@ def run_migrations(
315
327
  """
316
328
  Run edwh-migrate based on the typedal config.
317
329
  """
330
+ try:
331
+ import edwh_migrate
332
+ except ImportError:
333
+ rich.print(
334
+ r"[red]missing dependency![/red] "
335
+ r"Please install [blue]`typedal\[migrations]`[/blue] "
336
+ r"to use [blue]`typedal migrations.*`[/blue]."
337
+ )
338
+ raise typer.Exit(code=127)
339
+
318
340
  # 1. build migrate Config from TypeDAL config
319
341
  # 2. import right file
320
342
  # 3. `activate_migrations`
@@ -360,6 +382,16 @@ def fake_migrations(
360
382
 
361
383
  glob is supported in 'names'
362
384
  """
385
+ try:
386
+ import edwh_migrate
387
+ except ImportError:
388
+ rich.print(
389
+ r"[red]missing dependency![/red] "
390
+ r"Please install [blue]`typedal\[migrations]`[/blue] "
391
+ r"to use [blue]`typedal migrations.*`[/blue]."
392
+ )
393
+ raise typer.Exit(code=127)
394
+
363
395
  if not (names or all):
364
396
  rich.print("Please provide one or more migration names, or pass --all to fake all.")
365
397
  return 1
@@ -385,13 +417,16 @@ def fake_migrations(
385
417
  db = edwh_migrate.setup_db(config=migrate_config)
386
418
  except edwh_migrate.migrate.DatabaseNotYetInitialized:
387
419
  db = edwh_migrate.setup_db(
388
- config=migrate_config, migrate=True, migrate_enabled=True, remove_migrate_tablefile=True
420
+ config=migrate_config,
421
+ migrate=True,
422
+ migrate_enabled=True,
423
+ remove_migrate_tablefile=True,
389
424
  )
390
425
 
391
426
  previously_migrated = (
392
427
  db(
393
428
  db.ewh_implemented_features.name.belongs(to_fake)
394
- & (db.ewh_implemented_features.installed == True) # noqa E712
429
+ & (db.ewh_implemented_features.installed == True), # noqa E712
395
430
  )
396
431
  .select(db.ewh_implemented_features.name)
397
432
  .column("name")
@@ -447,6 +482,71 @@ def migrations_stub(
447
482
  return 0
448
483
 
449
484
 
485
+ @app.command(name="typescript.generate")
486
+ @with_exit_code(hide_tb=IS_DEBUG)
487
+ def generate_typescript(
488
+ connection: typing.Annotated[str, typer.Option("--connection", "-c")] = None,
489
+ filename: OptionalArgument[str] = None,
490
+ tables: Tables_Option = None,
491
+ magic: Optional[bool] = None,
492
+ function: Optional[str] = None,
493
+ output_file: Optional[str] = None,
494
+ ) -> bool:
495
+ """
496
+ Generate TypeScript interfaces from TypeDAL table definitions.
497
+ """
498
+ from .serializers.typescript import is_supported
499
+
500
+ if not is_supported(): # pragma: no cover
501
+ rich.print(
502
+ r"[red]missing dependency![/red] "
503
+ r"Please install [blue]`typedal\[typescript]`[/blue] "
504
+ r"to use [blue]`typedal typescript.generate`[/blue]."
505
+ )
506
+ raise typer.Exit(code=127)
507
+
508
+ config = load_config(connection)
509
+ config.update(
510
+ input=filename,
511
+ tables=tables,
512
+ magic=magic,
513
+ function=function,
514
+ typescript_output=output_file,
515
+ _skip_none=True,
516
+ )
517
+
518
+ def typescript_renderer(context: RenderContext) -> str:
519
+ db: TypeDAL = context.db_new
520
+ return db.as_typescript(*context.tables)
521
+
522
+ mut_functions: list[str] = []
523
+ source_input = config.input
524
+ source_code = find_file_contents(
525
+ source_input,
526
+ config.function,
527
+ found_functions=mut_functions,
528
+ default_version="current" if source_input else "stdin",
529
+ )
530
+
531
+ success = render_schema_from_code(
532
+ source_code,
533
+ # output_file=raw_output,
534
+ output_file=config.typescript_output or None,
535
+ renderer=typescript_renderer,
536
+ db_type=config.dialect,
537
+ tables=config.tables,
538
+ magic=config.magic,
539
+ function_name=tuple(mut_functions),
540
+ use_typedal=True,
541
+ write_mode="w",
542
+ )
543
+
544
+ if not success: # pragma: no cover
545
+ rich.print("[red] something went wrong [/red]")
546
+
547
+ return success
548
+
549
+
450
550
  type AnyNestedDict = dict[str, AnyDict]
451
551
 
452
552
 
@@ -16,7 +16,7 @@ from .types import AnyDict
16
16
 
17
17
  if t.TYPE_CHECKING:
18
18
  from edwh_migrate import Config as MigrateConfig
19
- from pydal2sql.typer_support import Config as P2SConfig
19
+ from pydal2sql_core.state import Config as P2SConfig
20
20
 
21
21
  LazyPolicy = t.Literal["forbid", "warn", "ignore", "tolerate", "allow"]
22
22
 
@@ -39,6 +39,7 @@ class TypeDALConfig(TypedConfig):
39
39
  # pydal2sql:
40
40
  input: str = ""
41
41
  output: str = ""
42
+ typescript_output: str = ""
42
43
  noop: bool = False
43
44
  magic: bool = True
44
45
  tables: t.Optional[list[str]] = None
@@ -70,7 +71,7 @@ class TypeDALConfig(TypedConfig):
70
71
  """
71
72
  Convert the config to the format required by pydal2sql.
72
73
  """
73
- from pydal2sql.typer_support import Config, get_pydal2sql_config
74
+ from pydal2sql_core.state import Config, get_pydal2sql_config
74
75
 
75
76
  if self.pyproject: # pragma: no cover
76
77
  project = Path(self.pyproject).read_text()
@@ -22,6 +22,7 @@ from .helpers import (
22
22
  sql_expression,
23
23
  to_snake,
24
24
  )
25
+ from .serializers.typescript import TypedDictRegistry
25
26
 
26
27
  # noinspection PyUnusedImports
27
28
  from .types import CacheStatus, Field, Template
@@ -499,6 +500,32 @@ class TypeDAL(pydal.DAL):
499
500
  """
500
501
  return memoize(self, func, *args, key=key, ttl=ttl, **kwargs)
501
502
 
503
+ def as_typescript(self, *tables: str | type[TypedTable]) -> str:
504
+ """
505
+ Generate a TypeScript schema string for all currently defined typedal models.
506
+ """
507
+ TypedDictRegistry.clear() # clean registry in order to apply 'tables' filtering
508
+ registry = TypedDictRegistry()
509
+
510
+ do_filter: t.Callable[[str], bool]
511
+ if tables:
512
+ names = {
513
+ model.__name__
514
+ for table in tables
515
+ if (model := (self.find_model(table) if isinstance(table, str) else table))
516
+ }
517
+
518
+ do_filter = lambda name: name in names # noqa: E731
519
+ else:
520
+ do_filter = lambda name: not name.startswith("_") # noqa: E731
521
+
522
+ # Ensure all currently defined models are registered into the TypedDict registry/world.
523
+ for model in self._class_map.values():
524
+ if do_filter(model.__name__):
525
+ model.as_typeddict()
526
+
527
+ return registry.get_typescript("as_typescript")
528
+
502
529
 
503
530
  TypeDAL.representers.setdefault("rows_render", default_representer)
504
531
 
@@ -118,8 +118,9 @@ class TableDefinitionBuilder:
118
118
  table=table,
119
119
  relationships=t.cast(dict[str, Relationship[t.Any]], relationships),
120
120
  )
121
- self.class_map[str(table)] = cls
122
- self.class_map[table._rname] = cls
121
+ self.class_map[str(table)] = cls # tablename - pydal name
122
+ self.class_map[cls.__name__] = cls # TableName - typedal name
123
+ self.class_map[table._rname] = cls # table_model - sql name
123
124
  cls.__on_define__(self.db)
124
125
  else:
125
126
  warnings.warn("db.define used without inheriting TypedTable. This could lead to strange problems!")
@@ -11,6 +11,12 @@ class InvalidEnumValue:
11
11
  raw: t.Any
12
12
  value: None = None
13
13
 
14
+ def __bool__(self) -> bool:
15
+ """
16
+ Make this fake type falsey.
17
+ """
18
+ return False
19
+
14
20
 
15
21
  def enum_value_type(enum_type: type[enum.Enum]) -> type[t.Any]:
16
22
  values = [member.value for member in enum_type]
@@ -373,9 +373,7 @@ def UploadField(**kw: t.Unpack[FieldSettings]) -> TypedField[str]:
373
373
  Upload = UploadField
374
374
 
375
375
 
376
- def ReferenceField[
377
- T_subclass: (TypedTable, Table)
378
- ](
376
+ def ReferenceField[T_subclass: (TypedTable, Table)](
379
377
  other_table: str | t.Type[TypedTable] | TypedTable | Table | T_subclass,
380
378
  **kw: t.Unpack[FieldSettings],
381
379
  ) -> TypedField[int]:
@@ -559,7 +557,7 @@ def PointField(**kw: t.Unpack[FieldSettings]) -> TypedField[tuple[float, float]]
559
557
  NativeUUIDField = SQLCustomType(
560
558
  type="string",
561
559
  native="uuid",
562
- encoder=str,
560
+ encoder=lambda val: "" if val is None else str(val),
563
561
  decoder=lambda value: uuid.UUID(value) if value else None,
564
562
  )
565
563