@supatype/cli 0.1.5 → 0.1.7
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 +143 -93
- package/.turbo/turbo-typecheck.log +1 -1
- package/deno.d.ts +52 -0
- package/dist/augmentation-generator.d.ts +22 -0
- package/dist/augmentation-generator.d.ts.map +1 -1
- package/dist/augmentation-generator.js +43 -7
- package/dist/augmentation-generator.js.map +1 -1
- package/dist/binary-cache.d.ts +14 -0
- package/dist/binary-cache.d.ts.map +1 -1
- package/dist/binary-cache.js +33 -13
- package/dist/binary-cache.js.map +1 -1
- package/dist/cache-clean.d.ts +15 -0
- package/dist/cache-clean.d.ts.map +1 -0
- package/dist/cache-clean.js +76 -0
- package/dist/cache-clean.js.map +1 -0
- package/dist/cache-pins.d.ts +51 -0
- package/dist/cache-pins.d.ts.map +1 -0
- package/dist/cache-pins.js +184 -0
- package/dist/cache-pins.js.map +1 -0
- package/dist/cli-install-method.d.ts +19 -0
- package/dist/cli-install-method.d.ts.map +1 -0
- package/dist/cli-install-method.js +60 -0
- package/dist/cli-install-method.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +6 -0
- package/dist/cli.js.map +1 -1
- package/dist/cloud-credentials.d.ts +34 -0
- package/dist/cloud-credentials.d.ts.map +1 -0
- package/dist/cloud-credentials.js +139 -0
- package/dist/cloud-credentials.js.map +1 -0
- package/dist/commands/admin.d.ts +11 -0
- package/dist/commands/admin.d.ts.map +1 -1
- package/dist/commands/admin.js +131 -22
- package/dist/commands/admin.js.map +1 -1
- package/dist/commands/cache.d.ts.map +1 -1
- package/dist/commands/cache.js +67 -71
- package/dist/commands/cache.js.map +1 -1
- package/dist/commands/cloud.d.ts.map +1 -1
- package/dist/commands/cloud.js +32 -8
- package/dist/commands/cloud.js.map +1 -1
- package/dist/commands/db.d.ts +1 -0
- package/dist/commands/db.d.ts.map +1 -1
- package/dist/commands/db.js +147 -1
- package/dist/commands/db.js.map +1 -1
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +50 -10
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/doctor.d.ts +28 -0
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +60 -3
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/functions.d.ts +7 -0
- package/dist/commands/functions.d.ts.map +1 -1
- package/dist/commands/functions.js +40 -6
- package/dist/commands/functions.js.map +1 -1
- package/dist/commands/generate.d.ts.map +1 -1
- package/dist/commands/generate.js +5 -1
- package/dist/commands/generate.js.map +1 -1
- package/dist/commands/hooks.d.ts +12 -0
- package/dist/commands/hooks.d.ts.map +1 -0
- package/dist/commands/hooks.js +118 -0
- package/dist/commands/hooks.js.map +1 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +41 -7
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/internal.d.ts +6 -0
- package/dist/commands/internal.d.ts.map +1 -0
- package/dist/commands/internal.js +22 -0
- package/dist/commands/internal.js.map +1 -0
- package/dist/commands/keys.d.ts.map +1 -1
- package/dist/commands/keys.js +2 -0
- package/dist/commands/keys.js.map +1 -1
- package/dist/commands/link-helpers.d.ts.map +1 -1
- package/dist/commands/link-helpers.js +8 -2
- package/dist/commands/link-helpers.js.map +1 -1
- package/dist/commands/login.d.ts +7 -0
- package/dist/commands/login.d.ts.map +1 -0
- package/dist/commands/login.js +110 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/push.d.ts.map +1 -1
- package/dist/commands/push.js +110 -7
- package/dist/commands/push.js.map +1 -1
- package/dist/commands/self-update.d.ts +7 -0
- package/dist/commands/self-update.d.ts.map +1 -1
- package/dist/commands/self-update.js +18 -11
- package/dist/commands/self-update.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +18 -3
- package/dist/config.js.map +1 -1
- package/dist/db-preflight.d.ts +78 -0
- package/dist/db-preflight.d.ts.map +1 -0
- package/dist/db-preflight.js +452 -0
- package/dist/db-preflight.js.map +1 -0
- package/dist/dev-compose.d.ts +1 -1
- package/dist/dev-compose.d.ts.map +1 -1
- package/dist/dev-compose.js +129 -72
- package/dist/dev-compose.js.map +1 -1
- package/dist/env-file.d.ts +40 -2
- package/dist/env-file.d.ts.map +1 -1
- package/dist/env-file.js +132 -11
- package/dist/env-file.js.map +1 -1
- package/dist/field-masking-tier.d.ts +50 -0
- package/dist/field-masking-tier.d.ts.map +1 -0
- package/dist/field-masking-tier.js +91 -0
- package/dist/field-masking-tier.js.map +1 -0
- package/dist/functions-deno-types.d.ts +24 -0
- package/dist/functions-deno-types.d.ts.map +1 -0
- package/dist/functions-deno-types.js +155 -0
- package/dist/functions-deno-types.js.map +1 -0
- package/dist/hook-upload.d.ts +30 -0
- package/dist/hook-upload.d.ts.map +1 -0
- package/dist/hook-upload.js +86 -0
- package/dist/hook-upload.js.map +1 -0
- package/dist/hooks-generator.d.ts +8 -0
- package/dist/hooks-generator.d.ts.map +1 -0
- package/dist/hooks-generator.js +332 -0
- package/dist/hooks-generator.js.map +1 -0
- package/dist/link.d.ts +4 -0
- package/dist/link.d.ts.map +1 -1
- package/dist/link.js +2 -0
- package/dist/link.js.map +1 -1
- package/dist/local-secrets.d.ts +71 -0
- package/dist/local-secrets.d.ts.map +1 -0
- package/dist/local-secrets.js +100 -0
- package/dist/local-secrets.js.map +1 -0
- package/dist/model-hooks.d.ts +73 -0
- package/dist/model-hooks.d.ts.map +1 -0
- package/dist/model-hooks.js +211 -0
- package/dist/model-hooks.js.map +1 -0
- package/dist/postgres-ctl.d.ts +11 -0
- package/dist/postgres-ctl.d.ts.map +1 -1
- package/dist/postgres-ctl.js +25 -2
- package/dist/postgres-ctl.js.map +1 -1
- package/dist/project-config.d.ts +121 -1
- package/dist/project-config.d.ts.map +1 -1
- package/dist/project-config.js +150 -1
- package/dist/project-config.js.map +1 -1
- package/dist/resolve-target.d.ts +4 -0
- package/dist/resolve-target.d.ts.map +1 -1
- package/dist/resolve-target.js +69 -73
- package/dist/resolve-target.js.map +1 -1
- package/dist/schema-ast-v2.d.ts +2 -1
- package/dist/schema-ast-v2.d.ts.map +1 -1
- package/dist/schema-ast-v2.js +4 -2
- package/dist/schema-ast-v2.js.map +1 -1
- package/dist/schema-push-lock.d.ts.map +1 -1
- package/dist/schema-push-lock.js +43 -16
- package/dist/schema-push-lock.js.map +1 -1
- package/dist/self-host-compose.d.ts +24 -0
- package/dist/self-host-compose.d.ts.map +1 -1
- package/dist/self-host-compose.js +301 -69
- package/dist/self-host-compose.js.map +1 -1
- package/dist/service-role-check.d.ts +21 -0
- package/dist/service-role-check.d.ts.map +1 -0
- package/dist/service-role-check.js +85 -0
- package/dist/service-role-check.js.map +1 -0
- package/dist/target-client.d.ts +17 -0
- package/dist/target-client.d.ts.map +1 -1
- package/dist/target-client.js +69 -17
- package/dist/target-client.js.map +1 -1
- package/dist/type-extractor.d.ts.map +1 -1
- package/dist/type-extractor.js +736 -32
- package/dist/type-extractor.js.map +1 -1
- package/dist/ui/runtime/command-chrome.js +1 -1
- package/dist/ui/runtime/command-chrome.js.map +1 -1
- package/dist/ui/runtime/stdin-after-ink.d.ts.map +1 -1
- package/dist/ui/runtime/stdin-after-ink.js +3 -1
- package/dist/ui/runtime/stdin-after-ink.js.map +1 -1
- package/package.json +4 -1
- package/scripts/generate-homebrew-formula.mjs +121 -0
- package/src/augmentation-generator.ts +46 -9
- package/src/binary-cache.ts +47 -15
- package/src/cache-clean.ts +81 -0
- package/src/cache-pins.ts +243 -0
- package/src/cli-install-method.ts +67 -0
- package/src/cli.ts +6 -0
- package/src/cloud-credentials.ts +183 -0
- package/src/commands/admin.ts +153 -28
- package/src/commands/cache.ts +90 -75
- package/src/commands/cloud.ts +32 -8
- package/src/commands/db.ts +162 -1
- package/src/commands/dev.ts +57 -8
- package/src/commands/doctor.ts +69 -3
- package/src/commands/functions.ts +42 -5
- package/src/commands/generate.ts +5 -1
- package/src/commands/hooks.ts +138 -0
- package/src/commands/init.ts +42 -8
- package/src/commands/internal.ts +23 -0
- package/src/commands/keys.ts +2 -0
- package/src/commands/link-helpers.ts +9 -2
- package/src/commands/login.ts +127 -0
- package/src/commands/push.ts +124 -7
- package/src/commands/self-update.ts +85 -13
- package/src/config.ts +22 -3
- package/src/db-preflight.ts +575 -0
- package/src/dev-compose.ts +144 -73
- package/src/env-file.ts +157 -11
- package/src/field-masking-tier.ts +107 -0
- package/src/functions-deno-types.ts +188 -0
- package/src/hook-upload.ts +107 -0
- package/src/hooks-generator.ts +352 -0
- package/src/link.ts +7 -0
- package/src/local-secrets.ts +104 -0
- package/src/model-hooks.ts +260 -0
- package/src/postgres-ctl.ts +29 -2
- package/src/project-config.ts +236 -2
- package/src/resolve-target.ts +113 -68
- package/src/schema-ast-v2.ts +5 -2
- package/src/schema-push-lock.ts +56 -17
- package/src/self-host-compose.ts +344 -68
- package/src/service-role-check.ts +111 -0
- package/src/target-client.ts +105 -18
- package/src/type-extractor.ts +883 -28
- package/src/ui/runtime/command-chrome.ts +1 -1
- package/src/ui/runtime/stdin-after-ink.ts +3 -1
- package/tests/access-composition.test.ts +667 -0
- package/tests/admin-studio-membership.test.ts +121 -0
- package/tests/api-schemas.test.ts +83 -0
- package/tests/archive-name-alias.test.ts +42 -0
- package/tests/augmentation-generator.test.ts +72 -4
- package/tests/cache-clean.test.ts +206 -0
- package/tests/cli-help.test.ts +3 -0
- package/tests/cli-install-method.test.ts +66 -0
- package/tests/command-chrome-coverage.test.ts +31 -26
- package/tests/command-chrome-wrap.test.ts +1 -0
- package/tests/db-preflight.test.ts +72 -0
- package/tests/doctor-render.test.ts +62 -0
- package/tests/doctor-service-role.test.ts +55 -0
- package/tests/env-file.test.ts +121 -0
- package/tests/external-database-compose.test.ts +184 -0
- package/tests/external-database-config.test.ts +154 -0
- package/tests/field-masking-tier.test.ts +184 -0
- package/tests/functions-deno-types.test.ts +197 -0
- package/tests/homebrew-formula.test.ts +58 -0
- package/tests/hook-upload.test.ts +125 -0
- package/tests/hooks-generator.test.ts +212 -0
- package/tests/init.test.ts +3 -0
- package/tests/local-secrets.test.ts +92 -0
- package/tests/model-hooks.test.ts +322 -0
- package/tests/runtime-contract.test.ts +113 -1
- package/tests/service-role-check.test.ts +109 -0
- package/tests/service-role-routes.test.ts +51 -0
- package/tests/target-client-refresh.test.ts +94 -0
- package/tests/type-extractor.test.ts +187 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/tests/engine-distribution.test.ts +0 -418
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { describe, expect, it, vi, afterEach } from "vitest"
|
|
2
|
+
import { targetFetch, TargetApiError } from "../src/target-client.js"
|
|
3
|
+
|
|
4
|
+
describe("targetFetch cloud auth refresh", () => {
|
|
5
|
+
afterEach(() => {
|
|
6
|
+
vi.unstubAllGlobals()
|
|
7
|
+
vi.restoreAllMocks()
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
it("refreshes once on TOKEN_EXPIRED then retries", async () => {
|
|
11
|
+
const fetchMock = vi
|
|
12
|
+
.fn()
|
|
13
|
+
.mockResolvedValueOnce({
|
|
14
|
+
ok: false,
|
|
15
|
+
status: 401,
|
|
16
|
+
json: async () => ({ message: "Token expired", code: "TOKEN_EXPIRED" }),
|
|
17
|
+
})
|
|
18
|
+
.mockResolvedValueOnce({
|
|
19
|
+
ok: true,
|
|
20
|
+
status: 200,
|
|
21
|
+
json: async () => ({
|
|
22
|
+
access_token: "new-access",
|
|
23
|
+
refresh_token: "new-refresh",
|
|
24
|
+
expires_in: 3600,
|
|
25
|
+
}),
|
|
26
|
+
})
|
|
27
|
+
.mockResolvedValueOnce({
|
|
28
|
+
ok: true,
|
|
29
|
+
status: 200,
|
|
30
|
+
json: async () => ({ data: { ok: true } }),
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
vi.stubGlobal("fetch", fetchMock)
|
|
34
|
+
|
|
35
|
+
const onRefreshed = vi.fn()
|
|
36
|
+
const result = await targetFetch<{ ok: boolean }>("http://api.test", "/api/v1", {
|
|
37
|
+
method: "GET",
|
|
38
|
+
path: "/projects/x/status",
|
|
39
|
+
token: "old-access",
|
|
40
|
+
authRefresh: {
|
|
41
|
+
cloudApiUrl: "http://api.test",
|
|
42
|
+
refreshToken: "old-refresh",
|
|
43
|
+
onRefreshed,
|
|
44
|
+
},
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
expect(result).toEqual({ ok: true })
|
|
48
|
+
expect(onRefreshed).toHaveBeenCalledWith({
|
|
49
|
+
accessToken: "new-access",
|
|
50
|
+
refreshToken: "new-refresh",
|
|
51
|
+
})
|
|
52
|
+
expect(fetchMock).toHaveBeenCalledTimes(3)
|
|
53
|
+
const retryAuth = (fetchMock.mock.calls[2]![1] as RequestInit).headers as Record<string, string>
|
|
54
|
+
expect(retryAuth.Authorization).toBe("Bearer new-access")
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it("points to supatype login when expired without refresh token", async () => {
|
|
58
|
+
vi.stubGlobal(
|
|
59
|
+
"fetch",
|
|
60
|
+
vi.fn().mockResolvedValue({
|
|
61
|
+
ok: false,
|
|
62
|
+
status: 401,
|
|
63
|
+
json: async () => ({ message: "Token expired", code: "TOKEN_EXPIRED" }),
|
|
64
|
+
}),
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
await expect(
|
|
68
|
+
targetFetch("http://api.test", "/api/v1", {
|
|
69
|
+
method: "GET",
|
|
70
|
+
path: "/projects/x/status",
|
|
71
|
+
token: "old",
|
|
72
|
+
}),
|
|
73
|
+
).rejects.toThrow(/supatype login/)
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
it("throws TargetApiError with status", async () => {
|
|
77
|
+
vi.stubGlobal(
|
|
78
|
+
"fetch",
|
|
79
|
+
vi.fn().mockResolvedValue({
|
|
80
|
+
ok: false,
|
|
81
|
+
status: 500,
|
|
82
|
+
json: async () => ({ message: "boom" }),
|
|
83
|
+
}),
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
await expect(
|
|
87
|
+
targetFetch("http://api.test", "/api/v1", {
|
|
88
|
+
method: "GET",
|
|
89
|
+
path: "/x",
|
|
90
|
+
token: "t",
|
|
91
|
+
}),
|
|
92
|
+
).rejects.toMatchObject({ name: "TargetApiError", status: 500 } satisfies Partial<TargetApiError>)
|
|
93
|
+
})
|
|
94
|
+
})
|
|
@@ -1135,4 +1135,191 @@ export type User = Model<{ id: UUID; email: NonNullable<string> }>
|
|
|
1135
1135
|
|
|
1136
1136
|
expect(() => extractSchemaAstFromTypes(schemaPath, dir)).toThrow(/Unknown Supatype type "NonNullable"/)
|
|
1137
1137
|
})
|
|
1138
|
+
// Regression: access factored into a shared type alias silently produced NO
|
|
1139
|
+
// rules, so every model using the pattern was published without RLS.
|
|
1140
|
+
it("resolves model access from a shared type alias in another file", () => {
|
|
1141
|
+
const dir = mkdtempSync(join(tmpdir(), "supatype-access-alias-"))
|
|
1142
|
+
dirs.push(dir)
|
|
1143
|
+
writeFileSync(
|
|
1144
|
+
join(dir, "shared.ts"),
|
|
1145
|
+
`
|
|
1146
|
+
import type { Public, Role } from "@supatype/types"
|
|
1147
|
+
|
|
1148
|
+
export type CmsPublicReadAdminWrite = {
|
|
1149
|
+
read: Public
|
|
1150
|
+
create: Role<"admin">
|
|
1151
|
+
update: Role<"admin">
|
|
1152
|
+
delete: Role<"admin">
|
|
1153
|
+
}
|
|
1154
|
+
`,
|
|
1155
|
+
"utf8",
|
|
1156
|
+
)
|
|
1157
|
+
const schemaPath = join(dir, "schema.ts")
|
|
1158
|
+
writeFileSync(
|
|
1159
|
+
schemaPath,
|
|
1160
|
+
`
|
|
1161
|
+
import type { Model, UUID } from "@supatype/types"
|
|
1162
|
+
import type { CmsPublicReadAdminWrite } from "./shared.js"
|
|
1163
|
+
|
|
1164
|
+
export type Article = Model<{ id: UUID; title: string }, {
|
|
1165
|
+
tableName: "articles"
|
|
1166
|
+
access: CmsPublicReadAdminWrite
|
|
1167
|
+
}>
|
|
1168
|
+
`,
|
|
1169
|
+
"utf8",
|
|
1170
|
+
)
|
|
1171
|
+
|
|
1172
|
+
const ast = extractSchemaAstFromTypes(schemaPath, dir)
|
|
1173
|
+
const access = modelAccess(ast?.models.find((m) => m.name === "Article"))
|
|
1174
|
+
expect(access).toEqual({
|
|
1175
|
+
read: { type: "public" },
|
|
1176
|
+
create: { type: "role", roles: ["admin"] },
|
|
1177
|
+
update: { type: "role", roles: ["admin"] },
|
|
1178
|
+
delete: { type: "role", roles: ["admin"] },
|
|
1179
|
+
})
|
|
1180
|
+
})
|
|
1181
|
+
|
|
1182
|
+
it("resolves model access from an intersection of aliases", () => {
|
|
1183
|
+
const dir = mkdtempSync(join(tmpdir(), "supatype-access-intersection-"))
|
|
1184
|
+
dirs.push(dir)
|
|
1185
|
+
const schemaPath = join(dir, "schema.ts")
|
|
1186
|
+
writeFileSync(
|
|
1187
|
+
schemaPath,
|
|
1188
|
+
`
|
|
1189
|
+
import type { Model, UUID, Public, Role } from "@supatype/types"
|
|
1190
|
+
|
|
1191
|
+
type PublicRead = { read: Public }
|
|
1192
|
+
type AdminWrite = { create: Role<"admin">; delete: Role<"admin"> }
|
|
1193
|
+
|
|
1194
|
+
export type Article = Model<{ id: UUID; title: string }, {
|
|
1195
|
+
access: PublicRead & AdminWrite
|
|
1196
|
+
}>
|
|
1197
|
+
`,
|
|
1198
|
+
"utf8",
|
|
1199
|
+
)
|
|
1200
|
+
|
|
1201
|
+
const ast = extractSchemaAstFromTypes(schemaPath, dir)
|
|
1202
|
+
const access = modelAccess(ast?.models.find((m) => m.name === "Article"))
|
|
1203
|
+
expect(Object.keys(access).sort()).toEqual(["create", "delete", "read"])
|
|
1204
|
+
})
|
|
1205
|
+
|
|
1206
|
+
// The dangerous case: `const … as const` cannot express access (the rule names
|
|
1207
|
+
// are types), and silently ignoring it published an unprotected table.
|
|
1208
|
+
it("throws when model access cannot be resolved instead of dropping the rules", () => {
|
|
1209
|
+
const dir = mkdtempSync(join(tmpdir(), "supatype-access-unresolvable-"))
|
|
1210
|
+
dirs.push(dir)
|
|
1211
|
+
const schemaPath = join(dir, "schema.ts")
|
|
1212
|
+
writeFileSync(
|
|
1213
|
+
schemaPath,
|
|
1214
|
+
`
|
|
1215
|
+
import type { Model, UUID } from "@supatype/types"
|
|
1216
|
+
|
|
1217
|
+
export type Article = Model<{ id: UUID; title: string }, {
|
|
1218
|
+
access: typeof someRuntimeValue
|
|
1219
|
+
}>
|
|
1220
|
+
`,
|
|
1221
|
+
"utf8",
|
|
1222
|
+
)
|
|
1223
|
+
|
|
1224
|
+
expect(() => extractSchemaAstFromTypes(schemaPath, dir)).toThrow(
|
|
1225
|
+
/Model "Article": could not resolve its `access` rules/,
|
|
1226
|
+
)
|
|
1227
|
+
})
|
|
1228
|
+
|
|
1229
|
+
it("carries Decimal precision and scale through to the AST", () => {
|
|
1230
|
+
// `Decimal<10, 2>` used to extract as an unbounded NUMERIC: the engine renders `NUMERIC(p, s)`
|
|
1231
|
+
// from these two facts, and nothing read the type arguments. A money column silently lost its
|
|
1232
|
+
// constraint, which no error surfaced and no test caught.
|
|
1233
|
+
const dir = mkdtempSync(join(tmpdir(), "supatype-decimal-"))
|
|
1234
|
+
dirs.push(dir)
|
|
1235
|
+
const schemaPath = join(dir, "schema.ts")
|
|
1236
|
+
writeFileSync(
|
|
1237
|
+
schemaPath,
|
|
1238
|
+
`
|
|
1239
|
+
import type { Decimal, Model, UUID } from "@supatype/types"
|
|
1240
|
+
|
|
1241
|
+
export type Invoice = Model<{
|
|
1242
|
+
id: UUID
|
|
1243
|
+
total: Decimal<10, 2>
|
|
1244
|
+
rate: Decimal<5, 4>
|
|
1245
|
+
}>
|
|
1246
|
+
`,
|
|
1247
|
+
"utf8",
|
|
1248
|
+
)
|
|
1249
|
+
|
|
1250
|
+
const ast = extractSchemaAstFromTypes(schemaPath, dir)
|
|
1251
|
+
const invoice = ast?.models.find((m) => m.name === "Invoice")
|
|
1252
|
+
expect(invoice?.fields["total"]).toMatchObject({ kind: "decimal", precision: 10, scale: 2 })
|
|
1253
|
+
expect(invoice?.fields["rate"]).toMatchObject({ kind: "decimal", precision: 5, scale: 4 })
|
|
1254
|
+
})
|
|
1255
|
+
|
|
1256
|
+
it("extracts Code and Currency as JSONB carrying their declared shape", () => {
|
|
1257
|
+
// Both were exported from `@supatype/types` and rejected by the extractor, so a schema using
|
|
1258
|
+
// either failed the push with "unknown type". Each carries two values — a language with its
|
|
1259
|
+
// source, an amount with its currency — so JSONB is the column that does not drop one.
|
|
1260
|
+
const dir = mkdtempSync(join(tmpdir(), "supatype-code-currency-"))
|
|
1261
|
+
dirs.push(dir)
|
|
1262
|
+
const schemaPath = join(dir, "schema.ts")
|
|
1263
|
+
writeFileSync(
|
|
1264
|
+
schemaPath,
|
|
1265
|
+
`
|
|
1266
|
+
import type { Code, Currency, Model, UUID } from "@supatype/types"
|
|
1267
|
+
|
|
1268
|
+
export type Snippet = Model<{
|
|
1269
|
+
id: UUID
|
|
1270
|
+
body: Code<"sql">
|
|
1271
|
+
anyLang: Code
|
|
1272
|
+
price: Currency<"USD">
|
|
1273
|
+
anyCurrency: Currency
|
|
1274
|
+
}>
|
|
1275
|
+
`,
|
|
1276
|
+
"utf8",
|
|
1277
|
+
)
|
|
1278
|
+
|
|
1279
|
+
const ast = extractSchemaAstFromTypes(schemaPath, dir)
|
|
1280
|
+
const snippet = ast?.models.find((m) => m.name === "Snippet")
|
|
1281
|
+
|
|
1282
|
+
expect(snippet?.fields["body"]).toMatchObject({
|
|
1283
|
+
kind: "json",
|
|
1284
|
+
annotations: { db: { pgType: "JSONB" } },
|
|
1285
|
+
tsType: '{ lang: "sql"; source: string }',
|
|
1286
|
+
})
|
|
1287
|
+
expect(snippet?.fields["price"]).toMatchObject({
|
|
1288
|
+
kind: "json",
|
|
1289
|
+
annotations: { db: { pgType: "JSONB" } },
|
|
1290
|
+
tsType: '{ amount: string; code: "USD" }',
|
|
1291
|
+
})
|
|
1292
|
+
|
|
1293
|
+
// Without a literal argument the currency is not known statically, so the row carries it.
|
|
1294
|
+
expect(snippet?.fields["anyLang"]).toMatchObject({ tsType: "{ lang: string; source: string }" })
|
|
1295
|
+
expect(snippet?.fields["anyCurrency"]).toMatchObject({ tsType: "{ amount: string; code: string }" })
|
|
1296
|
+
|
|
1297
|
+
// Both are JSONB, so the column kind cannot tell Studio which editor to show. The `editor`
|
|
1298
|
+
// annotation is what separates a code snippet from a money amount from a raw JSON blob.
|
|
1299
|
+
expect(snippet?.fields["body"]).toMatchObject({ annotations: { platform: { editor: "code" } } })
|
|
1300
|
+
expect(snippet?.fields["price"]).toMatchObject({
|
|
1301
|
+
annotations: { platform: { editor: "currency" } },
|
|
1302
|
+
})
|
|
1303
|
+
})
|
|
1304
|
+
|
|
1305
|
+
it("throws when bucket access cannot be resolved", () => {
|
|
1306
|
+
const dir = mkdtempSync(join(tmpdir(), "supatype-bucket-access-"))
|
|
1307
|
+
dirs.push(dir)
|
|
1308
|
+
const schemaPath = join(dir, "schema.ts")
|
|
1309
|
+
writeFileSync(
|
|
1310
|
+
schemaPath,
|
|
1311
|
+
`
|
|
1312
|
+
import type { Bucket } from "@supatype/types"
|
|
1313
|
+
|
|
1314
|
+
export type Media = Bucket<"media", {
|
|
1315
|
+
access: typeof someRuntimeValue
|
|
1316
|
+
}>
|
|
1317
|
+
`,
|
|
1318
|
+
"utf8",
|
|
1319
|
+
)
|
|
1320
|
+
|
|
1321
|
+
expect(() => extractSchemaAstFromTypes(schemaPath, dir)).toThrow(
|
|
1322
|
+
/Bucket "media": could not resolve its `access` rules/,
|
|
1323
|
+
)
|
|
1324
|
+
})
|
|
1138
1325
|
})
|