@supatype/cli 0.1.0-alpha.6 → 0.1.0-alpha.8
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.
- package/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-test.log +208 -1
- package/.turbo/turbo-typecheck.log +1 -1
- package/dist/app/proxy-dev-app.d.ts +13 -0
- package/dist/app/proxy-dev-app.d.ts.map +1 -0
- package/dist/app/proxy-dev-app.js +53 -0
- package/dist/app/proxy-dev-app.js.map +1 -0
- package/dist/app-config.d.ts +7 -0
- package/dist/app-config.d.ts.map +1 -0
- package/dist/app-config.js +113 -0
- package/dist/app-config.js.map +1 -0
- package/dist/augmentation-generator.d.ts +2 -0
- package/dist/augmentation-generator.d.ts.map +1 -0
- package/dist/augmentation-generator.js +111 -0
- package/dist/augmentation-generator.js.map +1 -0
- package/dist/binary-cache.d.ts +94 -0
- package/dist/binary-cache.d.ts.map +1 -0
- package/dist/binary-cache.js +669 -0
- package/dist/binary-cache.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +13 -7
- package/dist/cli.js.map +1 -1
- package/dist/commands/admin.d.ts.map +1 -1
- package/dist/commands/admin.js +4 -3
- package/dist/commands/admin.js.map +1 -1
- package/dist/commands/app.d.ts.map +1 -1
- package/dist/commands/app.js +56 -209
- package/dist/commands/app.js.map +1 -1
- package/dist/commands/cache.d.ts +6 -0
- package/dist/commands/cache.d.ts.map +1 -0
- package/dist/commands/cache.js +105 -0
- package/dist/commands/cache.js.map +1 -0
- package/dist/commands/cloud.d.ts +20 -0
- package/dist/commands/cloud.d.ts.map +1 -1
- package/dist/commands/cloud.js +50 -52
- package/dist/commands/cloud.js.map +1 -1
- package/dist/commands/db.d.ts.map +1 -1
- package/dist/commands/db.js +47 -54
- package/dist/commands/db.js.map +1 -1
- package/dist/commands/deploy.d.ts +2 -1
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/deploy.js +79 -52
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/dev.d.ts +11 -0
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +759 -385
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/diff.d.ts.map +1 -1
- package/dist/commands/diff.js +30 -15
- package/dist/commands/diff.js.map +1 -1
- package/dist/commands/engine.d.ts +1 -3
- package/dist/commands/engine.d.ts.map +1 -1
- package/dist/commands/engine.js +13 -85
- package/dist/commands/engine.js.map +1 -1
- package/dist/commands/functions.d.ts.map +1 -1
- package/dist/commands/functions.js +92 -105
- package/dist/commands/functions.js.map +1 -1
- package/dist/commands/generate.d.ts.map +1 -1
- package/dist/commands/generate.js +22 -12
- package/dist/commands/generate.js.map +1 -1
- package/dist/commands/init.d.ts +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +137 -410
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/migrate-from-v1.d.ts +5 -0
- package/dist/commands/migrate-from-v1.d.ts.map +1 -0
- package/dist/commands/migrate-from-v1.js +125 -0
- package/dist/commands/migrate-from-v1.js.map +1 -0
- package/dist/commands/migrate.d.ts.map +1 -1
- package/dist/commands/migrate.js +27 -23
- package/dist/commands/migrate.js.map +1 -1
- package/dist/commands/pg.d.ts +8 -0
- package/dist/commands/pg.d.ts.map +1 -0
- package/dist/commands/pg.js +102 -0
- package/dist/commands/pg.js.map +1 -0
- package/dist/commands/pull.d.ts.map +1 -1
- package/dist/commands/pull.js +5 -66
- package/dist/commands/pull.js.map +1 -1
- package/dist/commands/push.d.ts.map +1 -1
- package/dist/commands/push.js +128 -38
- package/dist/commands/push.js.map +1 -1
- package/dist/commands/seed.d.ts +2 -0
- package/dist/commands/seed.d.ts.map +1 -1
- package/dist/commands/seed.js +44 -11
- package/dist/commands/seed.js.map +1 -1
- package/dist/commands/self-host.d.ts +7 -1
- package/dist/commands/self-host.d.ts.map +1 -1
- package/dist/commands/self-host.js +272 -758
- package/dist/commands/self-host.js.map +1 -1
- package/dist/commands/self-update.d.ts +9 -0
- package/dist/commands/self-update.d.ts.map +1 -0
- package/dist/commands/self-update.js +33 -0
- package/dist/commands/self-update.js.map +1 -0
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +4 -3
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/types.d.ts +3 -0
- package/dist/commands/types.d.ts.map +1 -0
- package/dist/commands/types.js +62 -0
- package/dist/commands/types.js.map +1 -0
- package/dist/commands/update.d.ts +7 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +93 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/components.d.ts +5 -0
- package/dist/components.d.ts.map +1 -0
- package/dist/components.js +3 -0
- package/dist/components.js.map +1 -0
- package/dist/config.d.ts +10 -51
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +101 -33
- package/dist/config.js.map +1 -1
- package/dist/dev-compose.d.ts +17 -0
- package/dist/dev-compose.d.ts.map +1 -0
- package/dist/dev-compose.js +374 -0
- package/dist/dev-compose.js.map +1 -0
- package/dist/diff-output.d.ts +4 -0
- package/dist/diff-output.d.ts.map +1 -0
- package/dist/diff-output.js +12 -0
- package/dist/diff-output.js.map +1 -0
- package/dist/docker-postgres.d.ts +57 -0
- package/dist/docker-postgres.d.ts.map +1 -0
- package/dist/docker-postgres.js +208 -0
- package/dist/docker-postgres.js.map +1 -0
- package/dist/engine-client.d.ts +69 -0
- package/dist/engine-client.d.ts.map +1 -0
- package/dist/engine-client.js +157 -0
- package/dist/engine-client.js.map +1 -0
- package/dist/ensure-binary.d.ts +7 -0
- package/dist/ensure-binary.d.ts.map +1 -0
- package/dist/ensure-binary.js +17 -0
- package/dist/ensure-binary.js.map +1 -0
- package/dist/functions-router-gen.d.ts +14 -0
- package/dist/functions-router-gen.d.ts.map +1 -0
- package/dist/functions-router-gen.js +199 -0
- package/dist/functions-router-gen.js.map +1 -0
- package/dist/index.d.ts +4 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -3
- package/dist/index.js.map +1 -1
- package/dist/kong-config.d.ts +25 -0
- package/dist/kong-config.d.ts.map +1 -0
- package/dist/kong-config.js +71 -0
- package/dist/kong-config.js.map +1 -0
- package/dist/local-gateway.d.ts +7 -0
- package/dist/local-gateway.d.ts.map +1 -0
- package/dist/local-gateway.js +9 -0
- package/dist/local-gateway.js.map +1 -0
- package/dist/local-storage.d.ts +8 -0
- package/dist/local-storage.d.ts.map +1 -0
- package/dist/local-storage.js +14 -0
- package/dist/local-storage.js.map +1 -0
- package/dist/pgbouncer-userlist.d.ts +5 -0
- package/dist/pgbouncer-userlist.d.ts.map +1 -0
- package/dist/pgbouncer-userlist.js +14 -0
- package/dist/pgbouncer-userlist.js.map +1 -0
- package/dist/postgres-ctl.d.ts +44 -0
- package/dist/postgres-ctl.d.ts.map +1 -0
- package/dist/postgres-ctl.js +137 -0
- package/dist/postgres-ctl.js.map +1 -0
- package/dist/process-manager.d.ts +43 -0
- package/dist/process-manager.d.ts.map +1 -0
- package/dist/process-manager.js +135 -0
- package/dist/process-manager.js.map +1 -0
- package/dist/project-config.d.ts +235 -0
- package/dist/project-config.d.ts.map +1 -0
- package/dist/project-config.js +160 -0
- package/dist/project-config.js.map +1 -0
- package/dist/pull-utils.d.ts +15 -0
- package/dist/pull-utils.d.ts.map +1 -1
- package/dist/pull-utils.js +12 -0
- package/dist/pull-utils.js.map +1 -1
- package/dist/release-pins.d.ts +7 -0
- package/dist/release-pins.d.ts.map +1 -0
- package/dist/release-pins.js +27 -0
- package/dist/release-pins.js.map +1 -0
- package/dist/release-public-key.d.ts +8 -0
- package/dist/release-public-key.d.ts.map +1 -0
- package/dist/release-public-key.js +13 -0
- package/dist/release-public-key.js.map +1 -0
- package/dist/runtime-routes.d.ts +34 -0
- package/dist/runtime-routes.d.ts.map +1 -0
- package/dist/runtime-routes.js +252 -0
- package/dist/runtime-routes.js.map +1 -0
- package/dist/schema-ast-v2.d.ts +127 -0
- package/dist/schema-ast-v2.d.ts.map +1 -0
- package/dist/schema-ast-v2.js +226 -0
- package/dist/schema-ast-v2.js.map +1 -0
- package/dist/scripts/postinstall.d.ts +5 -6
- package/dist/scripts/postinstall.d.ts.map +1 -1
- package/dist/scripts/postinstall.js +36 -20
- package/dist/scripts/postinstall.js.map +1 -1
- package/dist/self-host-compose.d.ts +22 -0
- package/dist/self-host-compose.d.ts.map +1 -0
- package/dist/self-host-compose.js +347 -0
- package/dist/self-host-compose.js.map +1 -0
- package/dist/storage-provision.d.ts +24 -0
- package/dist/storage-provision.d.ts.map +1 -0
- package/dist/storage-provision.js +44 -0
- package/dist/storage-provision.js.map +1 -0
- package/dist/studio-admin-roles.d.ts +7 -0
- package/dist/studio-admin-roles.d.ts.map +1 -0
- package/dist/studio-admin-roles.js +14 -0
- package/dist/studio-admin-roles.js.map +1 -0
- package/dist/studio-dev-server.d.ts +22 -0
- package/dist/studio-dev-server.d.ts.map +1 -0
- package/dist/studio-dev-server.js +28 -0
- package/dist/studio-dev-server.js.map +1 -0
- package/dist/systemd.d.ts +26 -0
- package/dist/systemd.d.ts.map +1 -0
- package/dist/systemd.js +102 -0
- package/dist/systemd.js.map +1 -0
- package/dist/tsx-runner.d.ts.map +1 -1
- package/dist/tsx-runner.js +9 -2
- package/dist/tsx-runner.js.map +1 -1
- package/dist/type-extractor.d.ts +4 -0
- package/dist/type-extractor.d.ts.map +1 -0
- package/dist/type-extractor.js +1213 -0
- package/dist/type-extractor.js.map +1 -0
- package/dist/type-resolver.d.ts +33 -0
- package/dist/type-resolver.d.ts.map +1 -0
- package/dist/type-resolver.js +338 -0
- package/dist/type-resolver.js.map +1 -0
- package/package.json +4 -3
- package/releases/deno/VERSION +1 -0
- package/scripts/mirror-deno-release.sh +76 -0
- package/src/TYPE-RESOLUTION.md +294 -0
- package/src/app/proxy-dev-app.ts +67 -0
- package/src/app-config.ts +128 -0
- package/src/augmentation-generator.ts +126 -0
- package/src/binary-cache.ts +822 -0
- package/src/cli.ts +13 -8
- package/src/commands/admin.ts +4 -3
- package/src/commands/app.ts +67 -231
- package/src/commands/cache.ts +117 -0
- package/src/commands/cloud.ts +63 -64
- package/src/commands/db.ts +54 -63
- package/src/commands/deploy.ts +96 -62
- package/src/commands/dev.ts +933 -405
- package/src/commands/diff.ts +31 -29
- package/src/commands/engine.ts +13 -116
- package/src/commands/functions.ts +97 -115
- package/src/commands/generate.ts +23 -10
- package/src/commands/init.ts +149 -414
- package/src/commands/migrate-from-v1.ts +131 -0
- package/src/commands/migrate.ts +27 -23
- package/src/commands/pg.ts +133 -0
- package/src/commands/pull.ts +6 -85
- package/src/commands/push.ts +161 -56
- package/src/commands/seed.ts +54 -12
- package/src/commands/self-host.ts +312 -880
- package/src/commands/self-update.ts +45 -0
- package/src/commands/status.ts +4 -3
- package/src/commands/types.ts +76 -0
- package/src/commands/update.ts +109 -0
- package/src/components.ts +6 -0
- package/src/config.ts +127 -94
- package/src/dev-compose.ts +455 -0
- package/src/diff-output.ts +12 -0
- package/src/docker-postgres.ts +295 -0
- package/src/engine-client.ts +236 -0
- package/src/ensure-binary.ts +28 -0
- package/src/functions-router-gen.ts +224 -0
- package/src/index.ts +4 -12
- package/src/kong-config.ts +93 -0
- package/src/local-gateway.ts +9 -0
- package/src/local-storage.ts +14 -0
- package/src/pgbouncer-userlist.ts +15 -0
- package/src/postgres-ctl.ts +171 -0
- package/src/process-manager.ts +168 -0
- package/src/project-config.ts +386 -0
- package/src/pull-utils.ts +24 -0
- package/src/release-pins.ts +31 -0
- package/src/release-public-key.ts +12 -0
- package/src/runtime-routes.ts +291 -0
- package/src/schema-ast-v2.ts +324 -0
- package/src/scripts/postinstall.ts +36 -25
- package/src/self-host-compose.ts +389 -0
- package/src/storage-provision.ts +58 -0
- package/src/studio-admin-roles.ts +16 -0
- package/src/studio-dev-server.ts +53 -0
- package/src/systemd.ts +137 -0
- package/src/tsx-runner.ts +11 -1
- package/src/type-extractor.ts +1479 -0
- package/src/type-resolver.ts +457 -0
- package/tests/app-command.test.ts +54 -0
- package/tests/augmentation-generator.test.ts +59 -0
- package/tests/binary-cache-cloud-overrides.test.ts +123 -0
- package/tests/cached-artifact-format.test.ts +84 -0
- package/tests/cli-help.test.ts +40 -14
- package/tests/config.test.ts +171 -37
- package/tests/docker-postgres.test.ts +39 -0
- package/tests/engine-distribution.test.ts +3 -3
- package/tests/ensure-binary.test.ts +59 -0
- package/tests/init.test.ts +28 -86
- package/tests/migrate-from-v1.test.ts +29 -0
- package/tests/normalize-admin-config.test.ts +48 -0
- package/tests/pg-spawn-env.test.ts +18 -0
- package/tests/postgres-archive-tag.test.ts +9 -0
- package/tests/proxy-dev-app.test.ts +33 -0
- package/tests/pull-utils.test.ts +36 -1
- package/tests/release-pins.test.ts +28 -0
- package/tests/runtime-contract.test.ts +351 -0
- package/tests/seed-discover.test.ts +31 -0
- package/tests/studio-admin-roles.test.ts +27 -0
- package/tests/tsconfig.json +9 -0
- package/tests/type-extractor.test.ts +985 -0
- package/tests/type-resolver.test.ts +59 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/vitest.config.ts +12 -0
- package/dist/engine/cache.d.ts +0 -37
- package/dist/engine/cache.d.ts.map +0 -1
- package/dist/engine/cache.js +0 -121
- package/dist/engine/cache.js.map +0 -1
- package/dist/engine/download.d.ts +0 -19
- package/dist/engine/download.d.ts.map +0 -1
- package/dist/engine/download.js +0 -108
- package/dist/engine/download.js.map +0 -1
- package/dist/engine/platform.d.ts +0 -24
- package/dist/engine/platform.d.ts.map +0 -1
- package/dist/engine/platform.js +0 -50
- package/dist/engine/platform.js.map +0 -1
- package/dist/engine/resolve.d.ts +0 -37
- package/dist/engine/resolve.d.ts.map +0 -1
- package/dist/engine/resolve.js +0 -133
- package/dist/engine/resolve.js.map +0 -1
- package/dist/engine/update-notify.d.ts +0 -11
- package/dist/engine/update-notify.d.ts.map +0 -1
- package/dist/engine/update-notify.js +0 -43
- package/dist/engine/update-notify.js.map +0 -1
- package/dist/engine/verify.d.ts +0 -50
- package/dist/engine/verify.d.ts.map +0 -1
- package/dist/engine/verify.js +0 -161
- package/dist/engine/verify.js.map +0 -1
- package/dist/engine-version.d.ts +0 -35
- package/dist/engine-version.d.ts.map +0 -1
- package/dist/engine-version.js +0 -35
- package/dist/engine-version.js.map +0 -1
- package/dist/engine.d.ts +0 -34
- package/dist/engine.d.ts.map +0 -1
- package/dist/engine.js +0 -76
- package/dist/engine.js.map +0 -1
- package/src/engine/cache.ts +0 -135
- package/src/engine/download.ts +0 -143
- package/src/engine/platform.ts +0 -66
- package/src/engine/resolve.ts +0 -197
- package/src/engine/update-notify.ts +0 -50
- package/src/engine/verify.ts +0 -206
- package/src/engine-version.ts +0 -39
- package/src/engine.ts +0 -99
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
# Type Resolution in the Schema Extractor
|
|
2
|
+
|
|
3
|
+
`type-extractor.ts` converts TypeScript type definitions into `ExtractedSchemaAst` —
|
|
4
|
+
the JSON handed to the engine binary for SQL generation and client type output.
|
|
5
|
+
|
|
6
|
+
This document explains how type names are resolved, what patterns are supported,
|
|
7
|
+
and how the three-tier fallback chain works.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## The Problem
|
|
12
|
+
|
|
13
|
+
The extractor uses the TypeScript **parser only** (`ts.createSourceFile`), not the
|
|
14
|
+
full type checker. This is fast and requires no `tsconfig.json`, but it means the
|
|
15
|
+
extractor only sees raw source text — it cannot evaluate what a type alias resolves to.
|
|
16
|
+
|
|
17
|
+
The consequence is that any indirection breaks resolution:
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
// Works — extractor sees "Optional" literally
|
|
21
|
+
type Post = Model<{ email: Optional<Email> }>
|
|
22
|
+
|
|
23
|
+
// Previously broken — extractor sees "Nullable", not "Optional"
|
|
24
|
+
type Nullable<T> = Optional<T>
|
|
25
|
+
type Post = Model<{ email: Nullable<Email> }>
|
|
26
|
+
|
|
27
|
+
// Previously broken — import rename
|
|
28
|
+
import { Optional as Maybe } from "@supatype/types"
|
|
29
|
+
type Post = Model<{ email: Maybe<Email> }>
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Failures were silent — unknown types fell through to `{ kind: "text", pgType: "TEXT" }`
|
|
33
|
+
instead of throwing an error.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Three-Tier Resolution
|
|
38
|
+
|
|
39
|
+
Every type name encountered in a field definition is resolved through three tiers
|
|
40
|
+
in order. The first tier to succeed wins. If all three fail, an error is thrown.
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
Tier 1 — syntactic switch instant inline primitives and modifiers by name
|
|
44
|
+
Tier 2 — alias registry instant user-defined type aliases, import renames
|
|
45
|
+
Tier 3 — TypeScript checker ~300ms† conditional types, mapped types
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
† Tier 3 is **lazy** — the `ts.Program` and `TypeChecker` are only created the first
|
|
49
|
+
time a conditional or mapped type is encountered. Schemas that use only tiers 1 and 2
|
|
50
|
+
pay no cost.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Tier 1 — Syntactic Switch
|
|
55
|
+
|
|
56
|
+
The existing behaviour. The extractor walks the type reference chain and matches
|
|
57
|
+
names exactly against a hardcoded switch:
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
switch (typeName) {
|
|
61
|
+
case "Optional": flags.required = false; unwrap(); continue
|
|
62
|
+
case "Unique": flags.unique = true; unwrap(); continue
|
|
63
|
+
case "PrimaryKey": flags.primaryKey = true; unwrap(); continue
|
|
64
|
+
case "UUID": return { kind: "uuid", pgType: "UUID" }
|
|
65
|
+
case "Email": return { kind: "email", pgType: "TEXT" }
|
|
66
|
+
// ... all @supatype/types primitives and modifiers
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
This covers all types used inline with their canonical names.
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## Tier 2 — Alias Registry
|
|
75
|
+
|
|
76
|
+
Built once at startup from all source files loaded by `loadSchemaSourceFiles`.
|
|
77
|
+
Covers two sub-cases:
|
|
78
|
+
|
|
79
|
+
### 2a — Type alias declarations
|
|
80
|
+
|
|
81
|
+
Any `type X = ...` that is not a `Model<>` declaration is indexed by name:
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
// These all become entries in the alias registry:
|
|
85
|
+
type Nullable<T> = Optional<T>
|
|
86
|
+
type UniqueSlug = Unique<Slug<"title">>
|
|
87
|
+
type AuditId = PrimaryKey<UUID>
|
|
88
|
+
type MyEnum = "draft" | "published" | "archived"
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
When the extractor encounters an unknown name, it looks it up in the registry,
|
|
92
|
+
substitutes any type parameters via text replacement, and re-enters tier 1 with
|
|
93
|
+
the resolved node.
|
|
94
|
+
|
|
95
|
+
Multi-hop aliases work because the resolution recurses:
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
type A = B
|
|
99
|
+
type B = Optional<Email>
|
|
100
|
+
// A → B → Optional<Email> → resolved by tier 1
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Cycle detection via a `resolving: Set<string>` guard prevents infinite loops and
|
|
104
|
+
throws a descriptive error instead.
|
|
105
|
+
|
|
106
|
+
### 2b — Import renames
|
|
107
|
+
|
|
108
|
+
Explicit `as` renames in import statements are indexed per file:
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
import { Optional as Maybe } from "@supatype/types"
|
|
112
|
+
import { Nullable as MaybeNull } from "./shared/field-types"
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Before the tier 1 switch runs, each name is checked against the rename map for
|
|
116
|
+
the current file. `Maybe` becomes `Optional`, `MaybeNull` becomes `Nullable`
|
|
117
|
+
(which is then resolved by 2a).
|
|
118
|
+
|
|
119
|
+
### File loading
|
|
120
|
+
|
|
121
|
+
`loadSchemaSourceFiles` follows both `export` declarations (existing) and local
|
|
122
|
+
`import` declarations (new), ensuring that files referenced via import are loaded
|
|
123
|
+
into the source file set and their aliases are available in the registry.
|
|
124
|
+
|
|
125
|
+
Only relative specifiers (`.`-prefixed) are followed. Bare specifiers and scoped
|
|
126
|
+
packages (`@supatype/types`, `node_modules/*`) are not loaded — their exported
|
|
127
|
+
names are already covered by the tier 1 switch.
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Tier 3 — TypeScript Checker
|
|
132
|
+
|
|
133
|
+
Required for types that cannot be evaluated syntactically:
|
|
134
|
+
|
|
135
|
+
- **Conditional types**: `T extends U ? A : B` — requires evaluating the constraint
|
|
136
|
+
- **Mapped types**: `{ [K in keyof T]: F<T[K]> }` — requires enumerating `keyof T`
|
|
137
|
+
|
|
138
|
+
### How it works
|
|
139
|
+
|
|
140
|
+
1. A lazy `ts.Program` is created from the already-loaded source files using a
|
|
141
|
+
custom `CompilerHost` that serves them from memory (no disk re-reads).
|
|
142
|
+
|
|
143
|
+
2. Because our lightweight parse tree and the Program's parse tree are different
|
|
144
|
+
objects (even for the same file), the node with the unresolvable type is located
|
|
145
|
+
in the Program's source file by matching `pos`/`end` character positions.
|
|
146
|
+
|
|
147
|
+
3. `checker.getTypeAtLocation(programNode)` resolves the type fully.
|
|
148
|
+
|
|
149
|
+
4. `checker.typeToString(type, ..., TypeFormatFlags.UseAliasDefinedOutsideCurrentScope)`
|
|
150
|
+
converts it back to a string with **alias names preserved** — so `Optional<Email>`
|
|
151
|
+
stays as `Optional<Email>` rather than expanding to the underlying branded
|
|
152
|
+
intersection type.
|
|
153
|
+
|
|
154
|
+
5. The string is re-parsed via `ts.createSourceFile` into a proper TypeNode with
|
|
155
|
+
valid `pos`/`end` values (so downstream `getText()` calls work).
|
|
156
|
+
|
|
157
|
+
6. The resolved TypeNode is fed back into the tier 1 switch.
|
|
158
|
+
|
|
159
|
+
### When tier 3 fires
|
|
160
|
+
|
|
161
|
+
- A field type is directly a conditional or mapped expression
|
|
162
|
+
- A tier 2 alias body contains a conditional or mapped type (detected by
|
|
163
|
+
`needsChecker()` before text substitution is attempted — the original node,
|
|
164
|
+
which has valid source positions, is passed to the checker instead)
|
|
165
|
+
- The `Model<>` fields argument is a mapped type alias:
|
|
166
|
+
```typescript
|
|
167
|
+
type AllOptional<T> = { [K in keyof T]: Optional<T[K]> }
|
|
168
|
+
type Post = Model<AllOptional<{ email: Email; name: Text }>>
|
|
169
|
+
```
|
|
170
|
+
This is handled in `unwrapModelFields`, which also participates in the
|
|
171
|
+
three-tier chain.
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## What Is Supported
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
// Tier 1 — inline, canonical names
|
|
179
|
+
email: Optional<Email>
|
|
180
|
+
slug: Unique<Slug<"title">>
|
|
181
|
+
id: PrimaryKey<UUID>
|
|
182
|
+
|
|
183
|
+
// Tier 2a — simple alias
|
|
184
|
+
type Nullable<T> = Optional<T>
|
|
185
|
+
type UniqueSlug = Unique<Slug<"title">>
|
|
186
|
+
email: Nullable<Email>
|
|
187
|
+
slug: UniqueSlug
|
|
188
|
+
|
|
189
|
+
// Tier 2a — multi-hop
|
|
190
|
+
type A = B
|
|
191
|
+
type B = Nullable<Email>
|
|
192
|
+
email: A
|
|
193
|
+
|
|
194
|
+
// Tier 2b — import rename of primitive
|
|
195
|
+
import { Optional as Maybe } from "@supatype/types"
|
|
196
|
+
email: Maybe<Email>
|
|
197
|
+
|
|
198
|
+
// Tier 2b — import rename of local alias
|
|
199
|
+
import { Nullable as MaybeNull } from "./field-types"
|
|
200
|
+
email: MaybeNull<Email>
|
|
201
|
+
|
|
202
|
+
// Tier 2b — cross-file alias (no rename)
|
|
203
|
+
// helpers.ts: export type Nullable<T> = Optional<T>
|
|
204
|
+
import { Nullable } from "./helpers"
|
|
205
|
+
email: Nullable<Email>
|
|
206
|
+
|
|
207
|
+
// Tier 3 — conditional type
|
|
208
|
+
type NullableStr<T> = T extends string ? Optional<T> : T
|
|
209
|
+
email: NullableStr<Email>
|
|
210
|
+
|
|
211
|
+
// Tier 3 — mapped type as fields object
|
|
212
|
+
type AllOptional<T> = { [K in keyof T]: Optional<T[K]> }
|
|
213
|
+
type Post = Model<AllOptional<{ email: Email; name: Text }>>
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## What Is Not Supported
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
// Imports from node_modules other than @supatype/types
|
|
222
|
+
import { SomeHelper } from "some-library"
|
|
223
|
+
|
|
224
|
+
// Conditional / mapped types in alias bodies that reference
|
|
225
|
+
// symbols only available in node_modules (other than @supatype/types)
|
|
226
|
+
|
|
227
|
+
// TypeScript utility types used as field types
|
|
228
|
+
email: NonNullable<string> // error — not a @supatype/types primitive
|
|
229
|
+
|
|
230
|
+
// Namespace-qualified names
|
|
231
|
+
email: Types.Optional<Email> // error — only identifier references are resolved
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## Error Behaviour
|
|
237
|
+
|
|
238
|
+
Unknown types now **throw** instead of silently falling back to
|
|
239
|
+
`{ kind: "text", pgType: "TEXT" }`:
|
|
240
|
+
|
|
241
|
+
```
|
|
242
|
+
Error: Unknown Supatype type "SomeType" in field "email".
|
|
243
|
+
If this is a type alias, confirm the file defining it is reachable
|
|
244
|
+
from your schema entry point.
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
Cycles in alias chains throw:
|
|
248
|
+
|
|
249
|
+
```
|
|
250
|
+
Error: Field "email": circular alias chain detected resolving "A".
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
Unresolvable conditional/mapped types throw:
|
|
254
|
+
|
|
255
|
+
```
|
|
256
|
+
Error: Field "email": could not resolve conditional/mapped type via type checker.
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## Data Structures
|
|
262
|
+
|
|
263
|
+
```typescript
|
|
264
|
+
// One entry per non-Model type alias declaration across all loaded source files
|
|
265
|
+
type AliasEntry = {
|
|
266
|
+
typeParams: string[] // ["T"] for Nullable<T> = Optional<T>
|
|
267
|
+
body: ts.TypeNode // the RHS of the declaration
|
|
268
|
+
sourceFile: ts.SourceFile // getText() context for body
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Rename map: sf.fileName → (localName → canonicalName)
|
|
272
|
+
// Only populated for explicit `import { X as Y }` renames
|
|
273
|
+
type ImportRenameMap = Map<string, Map<string, string>>
|
|
274
|
+
|
|
275
|
+
// Passed to every resolution function; checker is lazy
|
|
276
|
+
type ResolveContext = {
|
|
277
|
+
aliasRegistry: Map<string, AliasEntry>
|
|
278
|
+
renameMap: ImportRenameMap
|
|
279
|
+
getChecker: () => CheckerContext
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
type CheckerContext = {
|
|
283
|
+
program: ts.Program
|
|
284
|
+
checker: ts.TypeChecker
|
|
285
|
+
}
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
## Adding a New Tier 1 Primitive
|
|
291
|
+
|
|
292
|
+
When a new type is added to `@supatype/types`, add a `case` to the `switch` in
|
|
293
|
+
`parseScalarType`. No other changes are needed — tier 2 and tier 3 handle
|
|
294
|
+
aliases and compositions of it automatically.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spawn the frontend dev server during `supatype dev` when app.mode is proxy.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { existsSync, readFileSync } from "node:fs"
|
|
6
|
+
import { join } from "node:path"
|
|
7
|
+
import { detectPackageManager } from "./framework.js"
|
|
8
|
+
import { ProcessManager } from "../process-manager.js"
|
|
9
|
+
import { projectRootFromConfig, type SupatypeProjectConfig } from "../project-config.js"
|
|
10
|
+
|
|
11
|
+
const DEFAULT_PROXY_DEV_SCRIPT = "start"
|
|
12
|
+
|
|
13
|
+
/** package.json script name to run (only when app.mode is proxy). */
|
|
14
|
+
export function resolveProxyDevScript(config: SupatypeProjectConfig): string | null {
|
|
15
|
+
if (config.app?.mode !== "proxy") return null
|
|
16
|
+
const script = config.app.start?.trim()
|
|
17
|
+
return script && script.length > 0 ? script : DEFAULT_PROXY_DEV_SCRIPT
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Start the configured package.json script for proxy dev.
|
|
22
|
+
* Returns null when not in proxy mode or when the script is missing.
|
|
23
|
+
*/
|
|
24
|
+
export function startProxyDevApp(
|
|
25
|
+
cwd: string,
|
|
26
|
+
config: SupatypeProjectConfig,
|
|
27
|
+
pidDir: string,
|
|
28
|
+
): ProcessManager | null {
|
|
29
|
+
const script = resolveProxyDevScript(config)
|
|
30
|
+
if (!script) return null
|
|
31
|
+
|
|
32
|
+
const appDir = projectRootFromConfig(config, cwd)
|
|
33
|
+
const pkgPath = join(appDir, "package.json")
|
|
34
|
+
if (!existsSync(pkgPath)) {
|
|
35
|
+
console.warn(`[supatype] app.mode=proxy but no package.json at ${appDir}; skipping dev app`)
|
|
36
|
+
return null
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf8")) as {
|
|
40
|
+
scripts?: Record<string, string>
|
|
41
|
+
}
|
|
42
|
+
if (!pkg.scripts?.[script]) {
|
|
43
|
+
console.warn(
|
|
44
|
+
`[supatype] app.mode=proxy: package.json has no "${script}" script.\n` +
|
|
45
|
+
` Add "scripts.${script}" or set app.start in supatype.config.ts`,
|
|
46
|
+
)
|
|
47
|
+
return null
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const pm = detectPackageManager(appDir)
|
|
51
|
+
const bin = pm
|
|
52
|
+
const args = pm === "yarn" ? [script] : ["run", script]
|
|
53
|
+
// pnpm/npm/yarn are .cmd shims on Windows — spawn via shell so PATH resolution works.
|
|
54
|
+
const useShell = process.platform === "win32"
|
|
55
|
+
|
|
56
|
+
console.log(`[supatype] Proxy mode: running ${bin} ${args.join(" ")} (${appDir})`)
|
|
57
|
+
|
|
58
|
+
const manager = new ProcessManager(bin, args, {
|
|
59
|
+
label: "app",
|
|
60
|
+
pidDir,
|
|
61
|
+
cwd: appDir,
|
|
62
|
+
colour: "\x1b[33m",
|
|
63
|
+
shell: useShell,
|
|
64
|
+
})
|
|
65
|
+
manager.start()
|
|
66
|
+
return manager
|
|
67
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, existsSync } from "node:fs"
|
|
2
|
+
import { resolve } from "node:path"
|
|
3
|
+
import { Node, Project, QuoteKind, SyntaxKind, type ObjectLiteralExpression, type SourceFile } from "ts-morph"
|
|
4
|
+
|
|
5
|
+
export interface UpdateAppConfigInput {
|
|
6
|
+
mode: "none" | "static" | "proxy"
|
|
7
|
+
staticDir?: string
|
|
8
|
+
upstream?: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function updateAppConfigInProject(cwd: string, input: UpdateAppConfigInput): string {
|
|
12
|
+
const configPath = resolve(cwd, "supatype.config.ts")
|
|
13
|
+
if (!existsSync(configPath)) {
|
|
14
|
+
throw new Error("supatype.config.ts not found. Run: supatype init")
|
|
15
|
+
}
|
|
16
|
+
const next = updateAppConfigAst(configPath, input)
|
|
17
|
+
writeFileSync(configPath, next, "utf8")
|
|
18
|
+
return configPath
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function updateAppConfigAst(configPath: string, input: UpdateAppConfigInput): string {
|
|
22
|
+
const project = new Project({
|
|
23
|
+
useInMemoryFileSystem: false,
|
|
24
|
+
skipAddingFilesFromTsConfig: true,
|
|
25
|
+
manipulationSettings: {
|
|
26
|
+
quoteKind: QuoteKind.Double,
|
|
27
|
+
},
|
|
28
|
+
})
|
|
29
|
+
const srcText = readFileSync(configPath, "utf8")
|
|
30
|
+
const sourceFile = project.createSourceFile(configPath, srcText, { overwrite: true })
|
|
31
|
+
const rootObject = getRootConfigObject(sourceFile)
|
|
32
|
+
|
|
33
|
+
const appProperty = rootObject.getProperty("app")
|
|
34
|
+
|
|
35
|
+
if (appProperty === undefined) {
|
|
36
|
+
rootObject.addPropertyAssignment({
|
|
37
|
+
name: "app",
|
|
38
|
+
initializer: renderAppInitializer(input),
|
|
39
|
+
})
|
|
40
|
+
} else if (Node.isPropertyAssignment(appProperty)) {
|
|
41
|
+
const init = appProperty.getInitializer()
|
|
42
|
+
if (init && Node.isObjectLiteralExpression(init)) {
|
|
43
|
+
patchAppObject(init, input)
|
|
44
|
+
} else {
|
|
45
|
+
appProperty.setInitializer(renderAppInitializer(input))
|
|
46
|
+
}
|
|
47
|
+
} else {
|
|
48
|
+
appProperty.remove()
|
|
49
|
+
rootObject.addPropertyAssignment({
|
|
50
|
+
name: "app",
|
|
51
|
+
initializer: renderAppInitializer(input),
|
|
52
|
+
})
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return sourceFile.getFullText()
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function getRootConfigObject(sourceFile: SourceFile): ObjectLiteralExpression {
|
|
59
|
+
const exportAssignment = sourceFile.getFirstDescendantByKind(SyntaxKind.ExportAssignment)
|
|
60
|
+
if (!exportAssignment) {
|
|
61
|
+
throw new Error("Could not find default export in supatype.config.ts.")
|
|
62
|
+
}
|
|
63
|
+
const expr = exportAssignment.getExpression()
|
|
64
|
+
if (Node.isObjectLiteralExpression(expr)) {
|
|
65
|
+
return expr
|
|
66
|
+
}
|
|
67
|
+
if (Node.isCallExpression(expr)) {
|
|
68
|
+
const [firstArg] = expr.getArguments()
|
|
69
|
+
if (firstArg && Node.isObjectLiteralExpression(firstArg)) {
|
|
70
|
+
return firstArg
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
throw new Error(
|
|
74
|
+
"supatype.config.ts must export an object literal or defineConfig({...}).",
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function renderAppInitializer(input: UpdateAppConfigInput): string {
|
|
79
|
+
if (input.mode === "proxy") {
|
|
80
|
+
return `{
|
|
81
|
+
mode: "proxy",
|
|
82
|
+
upstream: "${input.upstream ?? "http://localhost:3000"}",
|
|
83
|
+
}`
|
|
84
|
+
}
|
|
85
|
+
if (input.mode === "static") {
|
|
86
|
+
return `{
|
|
87
|
+
mode: "static",
|
|
88
|
+
static_dir: "${input.staticDir ?? "./dist"}",
|
|
89
|
+
}`
|
|
90
|
+
}
|
|
91
|
+
return `{
|
|
92
|
+
mode: "none",
|
|
93
|
+
}`
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function patchAppObject(appObj: ObjectLiteralExpression, input: UpdateAppConfigInput): void {
|
|
97
|
+
upsertStringProperty(appObj, "mode", input.mode)
|
|
98
|
+
|
|
99
|
+
if (input.mode === "proxy") {
|
|
100
|
+
upsertStringProperty(appObj, "upstream", input.upstream ?? "http://localhost:3000")
|
|
101
|
+
removePropertyIfPresent(appObj, "static_dir")
|
|
102
|
+
return
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (input.mode === "static") {
|
|
106
|
+
upsertStringProperty(appObj, "static_dir", input.staticDir ?? "./dist")
|
|
107
|
+
removePropertyIfPresent(appObj, "upstream")
|
|
108
|
+
return
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
removePropertyIfPresent(appObj, "upstream")
|
|
112
|
+
removePropertyIfPresent(appObj, "static_dir")
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function upsertStringProperty(obj: ObjectLiteralExpression, key: string, value: string): void {
|
|
116
|
+
const existing = obj.getProperty(key)
|
|
117
|
+
if (existing && Node.isPropertyAssignment(existing)) {
|
|
118
|
+
existing.setInitializer(`"${value}"`)
|
|
119
|
+
return
|
|
120
|
+
}
|
|
121
|
+
if (existing) existing.remove()
|
|
122
|
+
obj.addPropertyAssignment({ name: key, initializer: `"${value}"` })
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function removePropertyIfPresent(obj: ObjectLiteralExpression, key: string): void {
|
|
126
|
+
const prop = obj.getProperty(key)
|
|
127
|
+
if (prop) prop.remove()
|
|
128
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
interface AstModel {
|
|
2
|
+
tableName: string
|
|
3
|
+
fields: Record<string, Record<string, unknown>>
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
interface AstShape {
|
|
7
|
+
models?: AstModel[]
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function generateClientAugmentation(ast: unknown): string {
|
|
11
|
+
const parsed = ast as AstShape
|
|
12
|
+
const models = [...(parsed.models ?? [])].sort((a, b) => a.tableName.localeCompare(b.tableName))
|
|
13
|
+
|
|
14
|
+
const lines: string[] = []
|
|
15
|
+
lines.push("// Generated by supatype CLI — do not edit manually.")
|
|
16
|
+
lines.push("")
|
|
17
|
+
lines.push("declare module \"@supatype/client\" {")
|
|
18
|
+
lines.push(" interface SupatypeModels {")
|
|
19
|
+
|
|
20
|
+
for (const model of models) {
|
|
21
|
+
lines.push(` ${quoteKey(model.tableName)}: {`)
|
|
22
|
+
lines.push(` Row: ${generateRowType(model.fields)}`)
|
|
23
|
+
lines.push(` Insert: ${generateInsertType(model.fields)}`)
|
|
24
|
+
lines.push(` Update: ${generateUpdateType(model.fields)}`)
|
|
25
|
+
lines.push(" }")
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
lines.push(" }")
|
|
29
|
+
lines.push("}")
|
|
30
|
+
lines.push("")
|
|
31
|
+
lines.push("export {}")
|
|
32
|
+
lines.push("")
|
|
33
|
+
|
|
34
|
+
return lines.join("\n")
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function generateRowType(fields: Record<string, Record<string, unknown>>): string {
|
|
38
|
+
const entries = Object.entries(fields).sort(([a], [b]) => a.localeCompare(b))
|
|
39
|
+
if (entries.length === 0) return "Record<string, unknown>"
|
|
40
|
+
const body = entries
|
|
41
|
+
.map(([name, meta]) => ` ${quoteKey(name)}: ${toTsType(meta)}`)
|
|
42
|
+
.join("\n")
|
|
43
|
+
return `{\n${body}\n}`
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function insertColumnOptionalOnInsert(meta: Record<string, unknown>): boolean {
|
|
47
|
+
return meta["default"] !== undefined || meta["serverGenerated"] === true
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function generateInsertType(fields: Record<string, Record<string, unknown>>): string {
|
|
51
|
+
const entries = Object.entries(fields).sort(([a], [b]) => a.localeCompare(b))
|
|
52
|
+
if (entries.length === 0) return "Record<string, unknown>"
|
|
53
|
+
const body = entries
|
|
54
|
+
.map(([name, meta]) => {
|
|
55
|
+
const required = meta["required"] === true && !insertColumnOptionalOnInsert(meta)
|
|
56
|
+
return ` ${quoteKey(name)}${required ? "" : "?"}: ${toTsType(meta)}`
|
|
57
|
+
})
|
|
58
|
+
.join("\n")
|
|
59
|
+
return `{\n${body}\n}`
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function generateUpdateType(fields: Record<string, Record<string, unknown>>): string {
|
|
63
|
+
const entries = Object.entries(fields).sort(([a], [b]) => a.localeCompare(b))
|
|
64
|
+
if (entries.length === 0) return "Record<string, unknown>"
|
|
65
|
+
const body = entries
|
|
66
|
+
.map(([name, meta]) => ` ${quoteKey(name)}?: ${toTsType(meta)}`)
|
|
67
|
+
.join("\n")
|
|
68
|
+
return `{\n${body}\n}`
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function toTsType(meta: Record<string, unknown>): string {
|
|
72
|
+
const kind = typeof meta["kind"] === "string" ? meta["kind"] : "json"
|
|
73
|
+
const required = meta["required"] === true
|
|
74
|
+
const base = (() => {
|
|
75
|
+
switch (kind) {
|
|
76
|
+
case "uuid":
|
|
77
|
+
case "slug":
|
|
78
|
+
case "text":
|
|
79
|
+
case "email":
|
|
80
|
+
case "url":
|
|
81
|
+
case "date":
|
|
82
|
+
case "timestamp":
|
|
83
|
+
case "datetime":
|
|
84
|
+
case "money":
|
|
85
|
+
case "xml":
|
|
86
|
+
case "interval":
|
|
87
|
+
return "string"
|
|
88
|
+
case "integer":
|
|
89
|
+
case "smallInt":
|
|
90
|
+
case "serial":
|
|
91
|
+
case "float":
|
|
92
|
+
case "decimal":
|
|
93
|
+
return "number"
|
|
94
|
+
case "bigSerial":
|
|
95
|
+
case "bigInt":
|
|
96
|
+
return "bigint"
|
|
97
|
+
case "boolean":
|
|
98
|
+
return "boolean"
|
|
99
|
+
case "json":
|
|
100
|
+
case "blocks":
|
|
101
|
+
case "geo":
|
|
102
|
+
case "vector":
|
|
103
|
+
case "relation":
|
|
104
|
+
case "array":
|
|
105
|
+
case "image":
|
|
106
|
+
case "file":
|
|
107
|
+
return "Record<string, unknown>"
|
|
108
|
+
case "richText":
|
|
109
|
+
return "(import(\"@supatype/types/lexical\").SerializedEditorState | string)"
|
|
110
|
+
case "enum": {
|
|
111
|
+
const values = Array.isArray(meta["values"]) ? meta["values"] as unknown[] : []
|
|
112
|
+
const quoted = values
|
|
113
|
+
.filter((v): v is string => typeof v === "string")
|
|
114
|
+
.map((v) => JSON.stringify(v))
|
|
115
|
+
return quoted.length > 0 ? quoted.join(" | ") : "string"
|
|
116
|
+
}
|
|
117
|
+
default:
|
|
118
|
+
return "unknown"
|
|
119
|
+
}
|
|
120
|
+
})()
|
|
121
|
+
return required ? base : `${base} | null`
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function quoteKey(key: string): string {
|
|
125
|
+
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key) ? key : JSON.stringify(key)
|
|
126
|
+
}
|