better-supabase 0.0.0 → 0.1.0
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/LICENSE +21 -0
- package/README.md +88 -1
- package/bin/better-supabase.js +3 -0
- package/dist/casing-da0uRTqt.js +14 -0
- package/dist/cli/bin.d.ts +1 -0
- package/dist/cli/bin.js +15 -0
- package/dist/cli/index.d.ts +226 -0
- package/dist/cli/index.js +3 -0
- package/dist/cli-CoByWYja.js +6472 -0
- package/dist/client/index.d.ts +2 -0
- package/dist/client/index.js +112 -0
- package/dist/compiler-DL18ukPe.d.ts +14 -0
- package/dist/config/index.d.ts +3 -0
- package/dist/config/index.js +3 -0
- package/dist/config-D-aR2ZoN.js +276 -0
- package/dist/define-CTfWw-SS.d.ts +662 -0
- package/dist/define-QGkq04oe.js +1068 -0
- package/dist/edge/index.d.ts +43 -0
- package/dist/edge/index.js +103 -0
- package/dist/entitlements-BBg61DQZ.d.ts +20 -0
- package/dist/entitlements-BC_khwRy.js +17 -0
- package/dist/env/index.d.ts +2 -0
- package/dist/env/index.js +2 -0
- package/dist/env-BEToab0I.js +204 -0
- package/dist/errors-Bl3rkOzo.js +196 -0
- package/dist/events/index.d.ts +2 -0
- package/dist/events/index.js +2 -0
- package/dist/events-2OJsVG_d.d.ts +84 -0
- package/dist/events-BuzTgn2D.js +135 -0
- package/dist/executor-Bazhgsgq.d.ts +195 -0
- package/dist/executor-Bje4suQF.d.ts +25 -0
- package/dist/executor-Ceq60hPM.js +89 -0
- package/dist/hono/index.d.ts +45 -0
- package/dist/hono/index.js +70 -0
- package/dist/index-BD6wKthp.d.ts +597 -0
- package/dist/index-DQVvHzjW.d.ts +74 -0
- package/dist/index-DRid0qIv.d.ts +76 -0
- package/dist/index-Dup-nSe9.d.ts +117 -0
- package/dist/index-Dy6kuF0P.d.ts +71 -0
- package/dist/index-Hf8PP9sP.d.ts +116 -0
- package/dist/index-JXHBuS53.d.ts +144 -0
- package/dist/index-Nxo3TJcm.d.ts +208 -0
- package/dist/index-Sw43J8HG.d.ts +62 -0
- package/dist/index-cEeweTeM.d.ts +38 -0
- package/dist/index.d.ts +110 -0
- package/dist/index.js +42 -0
- package/dist/invalidate-DbxuL2jL.js +18 -0
- package/dist/jobs/index.d.ts +173 -0
- package/dist/jobs/index.js +477 -0
- package/dist/json-schema-BbwO2mZg.js +181 -0
- package/dist/lint/index.d.ts +75 -0
- package/dist/lint/index.js +167 -0
- package/dist/list/index.d.ts +2 -0
- package/dist/list/index.js +410 -0
- package/dist/live-6b9i72Ux.d.ts +180 -0
- package/dist/live-D8rV1Ldw.js +168 -0
- package/dist/mcp/index.d.ts +105 -0
- package/dist/mcp/index.js +330 -0
- package/dist/mfa-CpogP66z.js +521 -0
- package/dist/mfa-TVia554c.d.ts +44 -0
- package/dist/next/image/index.d.ts +34 -0
- package/dist/next/image/index.js +40 -0
- package/dist/next/index.d.ts +210 -0
- package/dist/next/index.js +426 -0
- package/dist/openapi/index.d.ts +2 -0
- package/dist/openapi/index.js +332 -0
- package/dist/orpc/index.d.ts +46 -0
- package/dist/orpc/index.js +50 -0
- package/dist/otel/index.d.ts +40 -0
- package/dist/otel/index.js +168 -0
- package/dist/path-Cv__-OTj.d.ts +17 -0
- package/dist/plugin-BqR6wKMB.js +23 -0
- package/dist/plugin-HvGqCurB.d.ts +118 -0
- package/dist/plugins/actor/index.d.ts +13 -0
- package/dist/plugins/actor/index.js +43 -0
- package/dist/plugins/rules/index.d.ts +88 -0
- package/dist/plugins/rules/index.js +174 -0
- package/dist/plugins/soft-delete/index.d.ts +38 -0
- package/dist/plugins/soft-delete/index.js +69 -0
- package/dist/plugins/tenant/index.d.ts +43 -0
- package/dist/plugins/tenant/index.js +73 -0
- package/dist/plugins/timestamps/index.d.ts +9 -0
- package/dist/plugins/timestamps/index.js +37 -0
- package/dist/plugins/validation/index.d.ts +24 -0
- package/dist/plugins/validation/index.js +59 -0
- package/dist/pool-BFeqzwQS.d.ts +37 -0
- package/dist/postgres/index.d.ts +28 -0
- package/dist/postgres/index.js +79 -0
- package/dist/postgrest-Dm-vhY6R.d.ts +43 -0
- package/dist/problem-DPu6Fh-t.js +97 -0
- package/dist/problem-DlgB0lYl.d.ts +47 -0
- package/dist/query/index.d.ts +2 -0
- package/dist/query/index.js +3 -0
- package/dist/query-ChvDbsDE.js +186 -0
- package/dist/react/index.d.ts +7 -0
- package/dist/react/index.js +5 -0
- package/dist/react/server.d.ts +13 -0
- package/dist/react/server.js +25 -0
- package/dist/react/session.d.ts +2 -0
- package/dist/react/session.js +3 -0
- package/dist/react-CrOvrnIP.js +241 -0
- package/dist/read-set-DhX2d04c.js +216 -0
- package/dist/realtime/index.d.ts +3 -0
- package/dist/realtime/index.js +253 -0
- package/dist/resolve-DW8h9NPm.d.ts +155 -0
- package/dist/resource-CYiu6Ygi.js +175 -0
- package/dist/resource-SkPTRJDD.d.ts +65 -0
- package/dist/respond-4-dhoV7x.js +380 -0
- package/dist/respond-PoTpy5DT.d.ts +169 -0
- package/dist/result-CTkCZ6JA.d.ts +198 -0
- package/dist/result-DWXatkd6.js +103 -0
- package/dist/scope-CDHcS7dE.js +90 -0
- package/dist/seed-CzecODcJ.js +101 -0
- package/dist/server/index.d.ts +42 -0
- package/dist/server/index.js +68 -0
- package/dist/session-C3osL21k.d.ts +21 -0
- package/dist/session-CD-3orO_.js +19 -0
- package/dist/shared-_6-uPL7E.js +57 -0
- package/dist/simplify-oHiDKRVQ.js +665 -0
- package/dist/spec-pins-CJPVghHy.d.ts +20 -0
- package/dist/spec-pins-t6sPaHvl.js +20 -0
- package/dist/sql-M8l3fnS0.js +260 -0
- package/dist/ssr/index.d.ts +5 -0
- package/dist/ssr/index.js +4 -0
- package/dist/standard-CyhRzctb.d.ts +7 -0
- package/dist/standard-FUi3Z3-7.js +21 -0
- package/dist/stats-3iy8u3_P.js +566 -0
- package/dist/storage/index.d.ts +4 -0
- package/dist/storage/index.js +2 -0
- package/dist/storage-BWXVUXGk.js +461 -0
- package/dist/tables-rZxRxNPW.js +59 -0
- package/dist/template-BeOGnynM.js +93 -0
- package/dist/template-mbvlysOo.d.ts +6 -0
- package/dist/testing/index.d.ts +294 -0
- package/dist/testing/index.js +751 -0
- package/dist/types-6cYK9pbJ.js +29 -0
- package/dist/types-C2fslP1z.d.ts +195 -0
- package/dist/version-Cqq1m3UY.js +4 -0
- package/dist/view-B0ltUmTi.d.ts +43 -0
- package/dist/view-B6F9QbG-.js +35 -0
- package/dist/webhooks/index.d.ts +152 -0
- package/dist/webhooks/index.js +2 -0
- package/dist/webhooks-DbMTJJX2.js +139 -0
- package/package.json +246 -17
- package/schemas/config-v1.json +328 -0
- package/schemas/doctor-report-v1.json +105 -0
- package/schemas/snapshot-v2.json +1002 -0
- package/skills/better-supabase/SKILL.md +84 -0
- package/skills/better-supabase/references/plugins.md +56 -0
- package/skills/better-supabase/references/troubleshooting.md +39 -0
- package/skills/better-supabase-api/SKILL.md +67 -0
- package/skills/better-supabase-api/references/adapters.md +108 -0
- package/skills/better-supabase-testing/SKILL.md +65 -0
- package/index.js +0 -1
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { a as parseEnv, n as EnvValidationError } from "../env-BEToab0I.js";
|
|
2
|
+
import { t as createQueries } from "../query-ChvDbsDE.js";
|
|
3
|
+
import { createBrowserClient } from "@supabase/ssr";
|
|
4
|
+
import { createClient } from "@supabase/supabase-js";
|
|
5
|
+
//#region src/client/index.ts
|
|
6
|
+
const LOADING = {
|
|
7
|
+
status: "loading",
|
|
8
|
+
user: null,
|
|
9
|
+
claims: null
|
|
10
|
+
};
|
|
11
|
+
const SIGNED_OUT = {
|
|
12
|
+
status: "signed-out",
|
|
13
|
+
user: null,
|
|
14
|
+
claims: null
|
|
15
|
+
};
|
|
16
|
+
function decodeClaims(token) {
|
|
17
|
+
const part = token.split(".")[1];
|
|
18
|
+
if (!part) return {};
|
|
19
|
+
try {
|
|
20
|
+
const bytes = Uint8Array.from(atob(part.replace(/-/g, "+").replace(/_/g, "/")), (char) => char.charCodeAt(0));
|
|
21
|
+
const parsed = JSON.parse(new TextDecoder().decode(bytes));
|
|
22
|
+
return typeof parsed === "object" && parsed !== null ? parsed : {};
|
|
23
|
+
} catch {
|
|
24
|
+
return {};
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function snapshotOf(session) {
|
|
28
|
+
if (!session) return SIGNED_OUT;
|
|
29
|
+
const claims = decodeClaims(session.access_token);
|
|
30
|
+
const role = typeof claims["role"] === "string" ? claims["role"] : session.user.role;
|
|
31
|
+
return {
|
|
32
|
+
status: "signed-in",
|
|
33
|
+
user: {
|
|
34
|
+
id: session.user.id,
|
|
35
|
+
...session.user.email ? { email: session.user.email } : {},
|
|
36
|
+
...role ? { role } : {}
|
|
37
|
+
},
|
|
38
|
+
claims
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function contextOf(snapshot) {
|
|
42
|
+
if (snapshot.status !== "signed-in") return {
|
|
43
|
+
actor: {
|
|
44
|
+
id: "anon",
|
|
45
|
+
kind: "anon",
|
|
46
|
+
role: "anon"
|
|
47
|
+
},
|
|
48
|
+
claims: { role: "anon" }
|
|
49
|
+
};
|
|
50
|
+
return {
|
|
51
|
+
actor: {
|
|
52
|
+
id: snapshot.user.id,
|
|
53
|
+
kind: "user",
|
|
54
|
+
...snapshot.user.role ? { role: snapshot.user.role } : {},
|
|
55
|
+
...snapshot.user.email ? { email: snapshot.user.email } : {}
|
|
56
|
+
},
|
|
57
|
+
claims: snapshot.claims
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function clientFor(options) {
|
|
61
|
+
if (options.client) return options.client;
|
|
62
|
+
if (!options.env) throw new TypeError("createBrowser needs `env` ({ url, publishableKey }) or `client`");
|
|
63
|
+
const checked = parseEnv({
|
|
64
|
+
SUPABASE_URL: options.env.url,
|
|
65
|
+
SUPABASE_PUBLISHABLE_KEY: options.env.publishableKey
|
|
66
|
+
});
|
|
67
|
+
if (!checked.ok) throw new EnvValidationError(checked.issues);
|
|
68
|
+
if (options.storage === "local") return createClient(checked.env.url, checked.env.publishableKey);
|
|
69
|
+
return createBrowserClient(checked.env.url, checked.env.publishableKey);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* The browser side: a supabase-js client, repositories that follow the
|
|
73
|
+
* session, TanStack Query options and an auth store for UI state.
|
|
74
|
+
*
|
|
75
|
+
* ```ts
|
|
76
|
+
* export const browser = createBrowser(sb, { env: { url, publishableKey } });
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
function createBrowser(sb, options = {}) {
|
|
80
|
+
const supabase = clientFor(options);
|
|
81
|
+
let snapshot = LOADING;
|
|
82
|
+
let db;
|
|
83
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
84
|
+
supabase.auth.onAuthStateChange((_event, session) => {
|
|
85
|
+
const next = snapshotOf(session);
|
|
86
|
+
if (!(next.status !== snapshot.status || next.user?.id !== snapshot.user?.id || next.status === "signed-in" && snapshot.status === "signed-in" && next.claims["exp"] !== snapshot.claims["exp"])) return;
|
|
87
|
+
snapshot = next;
|
|
88
|
+
db = void 0;
|
|
89
|
+
for (const listener of listeners) listener();
|
|
90
|
+
});
|
|
91
|
+
const browser = {
|
|
92
|
+
sb,
|
|
93
|
+
supabase,
|
|
94
|
+
get db() {
|
|
95
|
+
db ??= sb.connect(supabase, contextOf(snapshot));
|
|
96
|
+
return db;
|
|
97
|
+
},
|
|
98
|
+
queries: createQueries(sb, () => browser.db),
|
|
99
|
+
auth: {
|
|
100
|
+
current: () => snapshot,
|
|
101
|
+
subscribe: (listener) => {
|
|
102
|
+
listeners.add(listener);
|
|
103
|
+
return () => {
|
|
104
|
+
listeners.delete(listener);
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
return browser;
|
|
110
|
+
}
|
|
111
|
+
//#endregion
|
|
112
|
+
export { createBrowser };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { g as Operation } from "./executor-Bazhgsgq.js";
|
|
2
|
+
import { i as PostgrestPlan } from "./postgrest-Dm-vhY6R.js";
|
|
3
|
+
//#region src/core/compiler.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Turns an IR operation into something a backend runs. An `Executor` for a
|
|
6
|
+
* new backend usually pairs with a `Compiler` for it.
|
|
7
|
+
*/
|
|
8
|
+
interface Compiler<TTarget> {
|
|
9
|
+
readonly target: string;
|
|
10
|
+
compile(op: Operation): TTarget;
|
|
11
|
+
}
|
|
12
|
+
declare const postgrestCompiler: Compiler<PostgrestPlan>;
|
|
13
|
+
//#endregion
|
|
14
|
+
export { postgrestCompiler as n, Compiler as t };
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { A as resolveConfig, C as SqlConfig, D as VectorDistance, E as TimestampsConfig, F as valibot, I as JsonSchemaGeneratorOptions, L as buildJsonSchema, M as ZodGeneratorOptions, N as zod, O as VectorSearchConfig, P as ValibotGeneratorOptions, R as jsonSchema, S as SourceConfig, T as TenantConfig, _ as RealtimeConfig, a as CodecsConfig, b as SeedConfig, c as ExposeConfig, d as GeneratorInput, f as JsonSchemaSource, g as Privilege, h as PluginFlagsConfig, i as CONFIG_SCHEMA_URL, j as resolveJsonSchema, k as defineConfig, l as GeneratedFile, m as OpenApiConfig, n as BetterSupabaseConfig, o as DoctorConfig, p as JsonTypeConfig, r as BucketConfig, s as EntitlementsConfig, t as ActorConfig, u as Generator, v as ResolvedConfig, w as TableConfig, x as SoftDeleteConfig, y as ResolvedExpose } from "../index-BD6wKthp.js";
|
|
2
|
+
import { i as Casing } from "../types-C2fslP1z.js";
|
|
3
|
+
export { ActorConfig, BetterSupabaseConfig, BucketConfig, CONFIG_SCHEMA_URL, type Casing, CodecsConfig, DoctorConfig, EntitlementsConfig, ExposeConfig, GeneratedFile, Generator, GeneratorInput, type JsonSchemaGeneratorOptions, JsonSchemaSource, JsonTypeConfig, OpenApiConfig, PluginFlagsConfig, Privilege, RealtimeConfig, ResolvedConfig, ResolvedExpose, SeedConfig, SoftDeleteConfig, SourceConfig, SqlConfig, TableConfig, TenantConfig, TimestampsConfig, type ValibotGeneratorOptions, VectorDistance, VectorSearchConfig, type ZodGeneratorOptions, buildJsonSchema, defineConfig, jsonSchema, resolveConfig, resolveJsonSchema, valibot, zod };
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { n as jsonSchema, t as buildJsonSchema } from "../json-schema-BbwO2mZg.js";
|
|
2
|
+
import { a as zod, i as resolveJsonSchema, n as defineConfig, o as valibot, r as resolveConfig, t as CONFIG_SCHEMA_URL } from "../config-D-aR2ZoN.js";
|
|
3
|
+
export { CONFIG_SCHEMA_URL, buildJsonSchema, defineConfig, jsonSchema, resolveConfig, resolveJsonSchema, valibot, zod };
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import { a as parseImport, c as schemaName, d as variantsFor, i as fieldsFor, l as siblingPath, o as propertyKey, r as HEADER, s as rowType, u as tableEntries } from "./json-schema-BbwO2mZg.js";
|
|
2
|
+
//#region src/generators/valibot.ts
|
|
3
|
+
function scalar$1(kind) {
|
|
4
|
+
switch (kind.kind) {
|
|
5
|
+
case "uuid": return "v.pipe(v.string(), v.uuid())";
|
|
6
|
+
case "string": return "v.string()";
|
|
7
|
+
case "integer": return "v.pipe(v.number(), v.integer())";
|
|
8
|
+
case "number": return "v.number()";
|
|
9
|
+
case "boolean": return "v.boolean()";
|
|
10
|
+
case "datetime": return "v.pipe(v.string(), v.isoTimestamp())";
|
|
11
|
+
case "date": return "v.pipe(v.string(), v.isoDate())";
|
|
12
|
+
case "dateObject": return "v.date()";
|
|
13
|
+
case "bigint": return "v.bigint()";
|
|
14
|
+
case "json": return "json";
|
|
15
|
+
case "enum": return `v.picklist([${kind.values.map((value) => JSON.stringify(value)).join(", ")}])`;
|
|
16
|
+
case "unknown": return "v.unknown()";
|
|
17
|
+
default: return kind;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function field$1(table, plan, jsonImports) {
|
|
21
|
+
const imported = jsonImports.get(`${table}.${plan.column.db}`);
|
|
22
|
+
let expr = imported ? imported : plan.customJson ? `v.custom<NonNullable<RowOf<'${table}'>['${plan.name}']>>((value) => value !== undefined)` : plan.column.storage !== void 0 ? `(v.string() as unknown as v.GenericSchema<${rowType(table, plan)}>)` : scalar$1(plan.scalar);
|
|
23
|
+
if (plan.column.array) expr = `v.array(${expr})`;
|
|
24
|
+
if (plan.nullable) expr = `v.nullable(${expr})`;
|
|
25
|
+
if (plan.optional) expr = `v.exactOptional(${expr})`;
|
|
26
|
+
return ` ${propertyKey(plan.name)}: ${expr},`;
|
|
27
|
+
}
|
|
28
|
+
/** Valibot schemas for every table's Row, Insert and Update shape. */
|
|
29
|
+
function valibot(options = {}) {
|
|
30
|
+
return {
|
|
31
|
+
name: "valibot",
|
|
32
|
+
generate(input) {
|
|
33
|
+
const path = options.output ?? siblingPath(input.config.output, "valibot.ts");
|
|
34
|
+
const imports = /* @__PURE__ */ new Map();
|
|
35
|
+
const jsonImports = /* @__PURE__ */ new Map();
|
|
36
|
+
const tableNames = new Map(Object.values(input.meta.tables).map((t) => [t.name, t.key]));
|
|
37
|
+
for (const [key, spec] of Object.entries(options.json ?? {})) {
|
|
38
|
+
const [tableName, column] = key.split(".");
|
|
39
|
+
const { from, name } = parseImport(spec);
|
|
40
|
+
const specifier = from.startsWith(".") ? input.importPath(path, from) : from;
|
|
41
|
+
imports.set(specifier, [...imports.get(specifier) ?? [], name]);
|
|
42
|
+
jsonImports.set(`${tableNames.get(tableName ?? "") ?? tableName}.${column}`, name);
|
|
43
|
+
}
|
|
44
|
+
const lines = [
|
|
45
|
+
HEADER,
|
|
46
|
+
"import * as v from \"valibot\";",
|
|
47
|
+
""
|
|
48
|
+
];
|
|
49
|
+
lines.push(`import type { InsertOf, Json, RowOf, UpdateOf } from ${JSON.stringify(input.importPath(path, input.config.output))};`);
|
|
50
|
+
for (const [from, names] of imports) lines.push(`import { ${[...new Set(names)].sort().join(", ")} } from ${JSON.stringify(from)};`);
|
|
51
|
+
lines.push("", "const json: v.GenericSchema<Json> = v.lazy(() =>", " v.union([v.string(), v.number(), v.boolean(), v.null(), v.array(json), v.record(v.string(), v.optional(json))]),", ");");
|
|
52
|
+
const validators = [];
|
|
53
|
+
const validatorTypes = [];
|
|
54
|
+
for (const [key, table] of tableEntries(input)) {
|
|
55
|
+
lines.push("");
|
|
56
|
+
const variants = variantsFor(table);
|
|
57
|
+
for (const variant of variants) {
|
|
58
|
+
const name = schemaName(key, variant);
|
|
59
|
+
lines.push(`export const ${name}: v.GenericSchema<${variant}Of<'${key}'>> = v.object({`);
|
|
60
|
+
for (const plan of fieldsFor(table, variant, input)) lines.push(field$1(key, plan, jsonImports));
|
|
61
|
+
lines.push("});");
|
|
62
|
+
}
|
|
63
|
+
if (variants.includes("Insert")) {
|
|
64
|
+
const insert = schemaName(key, "Insert");
|
|
65
|
+
const update = schemaName(key, "Update");
|
|
66
|
+
validators.push(` ${propertyKey(key)}: { insert: ${insert}, update: ${update} },`);
|
|
67
|
+
validatorTypes.push(` readonly ${propertyKey(key)}: { readonly insert: typeof ${insert}; readonly update: typeof ${update} };`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
lines.push("", "/** Write validators for the validation plugin. */");
|
|
71
|
+
lines.push("export const validators: {", ...validatorTypes, "} = {", ...validators, "};", "");
|
|
72
|
+
return [{
|
|
73
|
+
path,
|
|
74
|
+
contents: lines.join("\n")
|
|
75
|
+
}];
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
//#endregion
|
|
80
|
+
//#region src/generators/zod.ts
|
|
81
|
+
function scalar(kind) {
|
|
82
|
+
switch (kind.kind) {
|
|
83
|
+
case "uuid": return "z.guid()";
|
|
84
|
+
case "string": return "z.string()";
|
|
85
|
+
case "integer": return "z.int()";
|
|
86
|
+
case "number": return "z.number()";
|
|
87
|
+
case "boolean": return "z.boolean()";
|
|
88
|
+
case "datetime": return "z.iso.datetime({ offset: true })";
|
|
89
|
+
case "date": return "z.iso.date()";
|
|
90
|
+
case "dateObject": return "z.date()";
|
|
91
|
+
case "bigint": return "z.bigint()";
|
|
92
|
+
case "json": return "z.json()";
|
|
93
|
+
case "enum": return `z.enum([${kind.values.map((value) => JSON.stringify(value)).join(", ")}])`;
|
|
94
|
+
case "unknown": return "z.unknown()";
|
|
95
|
+
default: return kind;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
function field(table, plan, jsonImports) {
|
|
99
|
+
const imported = jsonImports.get(`${table}.${plan.column.db}`);
|
|
100
|
+
let expr = imported ? imported : plan.customJson ? `z.custom<NonNullable<RowOf<'${table}'>['${plan.name}']>>((value) => value !== undefined)` : plan.column.storage !== void 0 ? `(z.string() as unknown as z.ZodType<${rowType(table, plan)}>)` : scalar(plan.scalar);
|
|
101
|
+
if (plan.column.array) expr = `z.array(${expr})`;
|
|
102
|
+
if (plan.nullable) expr = `${expr}.nullable()`;
|
|
103
|
+
if (plan.optional) expr = `${expr}.exactOptional()`;
|
|
104
|
+
return ` ${propertyKey(plan.name)}: ${expr},`;
|
|
105
|
+
}
|
|
106
|
+
/** Zod 4 schemas for every table's Row, Insert and Update shape. */
|
|
107
|
+
function zod(options = {}) {
|
|
108
|
+
return {
|
|
109
|
+
name: "zod",
|
|
110
|
+
generate(input) {
|
|
111
|
+
const path = options.output ?? siblingPath(input.config.output, "zod.ts");
|
|
112
|
+
const imports = /* @__PURE__ */ new Map();
|
|
113
|
+
const jsonImports = /* @__PURE__ */ new Map();
|
|
114
|
+
const tableNames = new Map(Object.values(input.meta.tables).map((t) => [t.name, t.key]));
|
|
115
|
+
for (const [key, spec] of Object.entries(options.json ?? {})) {
|
|
116
|
+
const [tableName, column] = key.split(".");
|
|
117
|
+
const { from, name } = parseImport(spec);
|
|
118
|
+
const specifier = from.startsWith(".") ? input.importPath(path, from) : from;
|
|
119
|
+
imports.set(specifier, [...imports.get(specifier) ?? [], name]);
|
|
120
|
+
jsonImports.set(`${tableNames.get(tableName ?? "") ?? tableName}.${column}`, name);
|
|
121
|
+
}
|
|
122
|
+
const lines = [
|
|
123
|
+
HEADER,
|
|
124
|
+
"import { z } from \"zod\";",
|
|
125
|
+
""
|
|
126
|
+
];
|
|
127
|
+
lines.push(`import type { InsertOf, RowOf, UpdateOf } from ${JSON.stringify(input.importPath(path, input.config.output))};`);
|
|
128
|
+
for (const [from, names] of imports) lines.push(`import { ${[...new Set(names)].sort().join(", ")} } from ${JSON.stringify(from)};`);
|
|
129
|
+
const validators = [];
|
|
130
|
+
for (const [key, table] of tableEntries(input)) {
|
|
131
|
+
lines.push("");
|
|
132
|
+
const variants = variantsFor(table);
|
|
133
|
+
for (const variant of variants) {
|
|
134
|
+
const name = schemaName(key, variant);
|
|
135
|
+
lines.push(`export const ${name}: z.ZodType<${variant}Of<'${key}'>> = z.object({`);
|
|
136
|
+
for (const plan of fieldsFor(table, variant, input)) lines.push(field(key, plan, jsonImports));
|
|
137
|
+
lines.push("});");
|
|
138
|
+
}
|
|
139
|
+
if (variants.includes("Insert")) validators.push(` ${propertyKey(key)}: { insert: ${schemaName(key, "Insert")}, update: ${schemaName(key, "Update")} },`);
|
|
140
|
+
}
|
|
141
|
+
lines.push("", "/** Write validators for the validation plugin. */");
|
|
142
|
+
lines.push("export const validators: {");
|
|
143
|
+
for (const [key, table] of tableEntries(input)) {
|
|
144
|
+
if (!variantsFor(table).includes("Insert")) continue;
|
|
145
|
+
lines.push(` readonly ${propertyKey(key)}: { readonly insert: typeof ${schemaName(key, "Insert")}; readonly update: typeof ${schemaName(key, "Update")} };`);
|
|
146
|
+
}
|
|
147
|
+
lines.push("} = {", ...validators, "};", "");
|
|
148
|
+
return [{
|
|
149
|
+
path,
|
|
150
|
+
contents: lines.join("\n")
|
|
151
|
+
}];
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
//#endregion
|
|
156
|
+
//#region src/config/index.ts
|
|
157
|
+
/** The JSON Schema document for a `json[...].schema` entry. */
|
|
158
|
+
function resolveJsonSchema(source) {
|
|
159
|
+
const standard = source["~standard"];
|
|
160
|
+
if (standard === void 0) return source;
|
|
161
|
+
const jsonSchema = standard.jsonSchema;
|
|
162
|
+
if (typeof jsonSchema?.input !== "function") throw new TypeError("json[...].schema: this Standard Schema library does not implement Standard JSON Schema; pass a JSON Schema object instead");
|
|
163
|
+
return jsonSchema.input({ target: "draft-2020-12" });
|
|
164
|
+
}
|
|
165
|
+
function entitlementsOf(config = {}) {
|
|
166
|
+
const customer = config.customer ?? "organizations.stripe_customer_id";
|
|
167
|
+
const dot = customer.lastIndexOf(".");
|
|
168
|
+
if (dot <= 0) throw new TypeError(`entitlements.customer must be "table.column", got "${customer}"`);
|
|
169
|
+
return {
|
|
170
|
+
table: customer.slice(0, dot),
|
|
171
|
+
column: customer.slice(dot + 1),
|
|
172
|
+
key: config.key ?? "id"
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
function vectorSearchOf(config = {}) {
|
|
176
|
+
return Object.entries(config).map(([table, entry]) => typeof entry === "string" ? {
|
|
177
|
+
table,
|
|
178
|
+
column: entry,
|
|
179
|
+
distance: "cosine"
|
|
180
|
+
} : {
|
|
181
|
+
table,
|
|
182
|
+
column: entry.column,
|
|
183
|
+
distance: entry.distance ?? "cosine"
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
const CONFIG_SCHEMA_URL = "https://unpkg.com/better-supabase/schemas/config-v1.json";
|
|
187
|
+
/** Identity helper that types `better-supabase.config.ts`. */
|
|
188
|
+
function defineConfig(config) {
|
|
189
|
+
return config;
|
|
190
|
+
}
|
|
191
|
+
function pick(value, defaults) {
|
|
192
|
+
if (value === void 0 || value === false) return void 0;
|
|
193
|
+
if (value === true) return defaults;
|
|
194
|
+
return {
|
|
195
|
+
...defaults,
|
|
196
|
+
...value
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
function resolveExpose(entry) {
|
|
200
|
+
if (Array.isArray(entry)) return {
|
|
201
|
+
anon: [],
|
|
202
|
+
authenticated: entry
|
|
203
|
+
};
|
|
204
|
+
const roles = entry;
|
|
205
|
+
return {
|
|
206
|
+
anon: roles.anon ?? [],
|
|
207
|
+
authenticated: roles.authenticated ?? []
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
/** Applies defaults. Paths stay relative to `root`. */
|
|
211
|
+
function resolveConfig(config, root) {
|
|
212
|
+
const output = config.output ?? "src/lib/supabase/generated.ts";
|
|
213
|
+
return {
|
|
214
|
+
root,
|
|
215
|
+
source: config.source ?? {},
|
|
216
|
+
schemas: config.schemas ?? ["public"],
|
|
217
|
+
casing: config.casing ?? "snake",
|
|
218
|
+
tables: config.tables ?? {},
|
|
219
|
+
output,
|
|
220
|
+
databaseTypesOutput: output.replace(/[^/]+$/, "database.types.ts"),
|
|
221
|
+
postgrestVersion: config.postgrestVersion ?? "13",
|
|
222
|
+
json: config.json ?? {},
|
|
223
|
+
codecs: {
|
|
224
|
+
timestamptz: config.codecs?.timestamptz ?? "string",
|
|
225
|
+
int8: config.codecs?.int8 ?? "number",
|
|
226
|
+
numeric: config.codecs?.numeric ?? "number"
|
|
227
|
+
},
|
|
228
|
+
sensitive: config.sensitive ?? [],
|
|
229
|
+
storagePaths: config.storagePaths ?? {},
|
|
230
|
+
expose: Object.fromEntries(Object.entries(config.expose ?? {}).map(([table, entry]) => [table, resolveExpose(entry)])),
|
|
231
|
+
readSets: config.readSets ?? [],
|
|
232
|
+
generators: config.generators ?? [],
|
|
233
|
+
plugins: {
|
|
234
|
+
timestamps: pick(config.plugins?.timestamps, {
|
|
235
|
+
createdAt: "created_at",
|
|
236
|
+
updatedAt: "updated_at"
|
|
237
|
+
}),
|
|
238
|
+
softDelete: pick(config.plugins?.softDelete, { column: "deleted_at" }),
|
|
239
|
+
tenant: pick(config.plugins?.tenant, {
|
|
240
|
+
column: "organization_id",
|
|
241
|
+
claim: "org_id"
|
|
242
|
+
}),
|
|
243
|
+
actor: pick(config.plugins?.actor, {
|
|
244
|
+
createdBy: "created_by",
|
|
245
|
+
updatedBy: "updated_by"
|
|
246
|
+
})
|
|
247
|
+
},
|
|
248
|
+
buckets: config.buckets ?? {},
|
|
249
|
+
topics: config.topics ?? {},
|
|
250
|
+
realtime: { tables: config.realtime?.tables ?? [] },
|
|
251
|
+
entitlements: entitlementsOf(config.entitlements),
|
|
252
|
+
vectorSearch: vectorSearchOf(config.vectorSearch),
|
|
253
|
+
sql: {
|
|
254
|
+
dir: config.sql?.dir ?? "supabase/schemas",
|
|
255
|
+
prefix: config.sql?.prefix ?? "900_better_supabase",
|
|
256
|
+
testsDir: config.sql?.testsDir ?? "supabase/tests",
|
|
257
|
+
kit: config.sql?.kit ?? []
|
|
258
|
+
},
|
|
259
|
+
seed: {
|
|
260
|
+
entry: config.seed?.entry ?? "supabase/seed.ts",
|
|
261
|
+
output: config.seed?.output ?? "supabase/seeds/000_better_supabase.sql"
|
|
262
|
+
},
|
|
263
|
+
openapi: {
|
|
264
|
+
entry: config.openapi?.entry ?? "src/lib/openapi.ts",
|
|
265
|
+
output: config.openapi?.output ?? "openapi.json"
|
|
266
|
+
},
|
|
267
|
+
doctor: {
|
|
268
|
+
ignore: config.doctor?.ignore ?? [],
|
|
269
|
+
strict: config.doctor?.strict ?? false,
|
|
270
|
+
sources: config.doctor?.sources ?? ["src/**/*.{ts,tsx}"],
|
|
271
|
+
policyHelperLimit: config.doctor?.policyHelperLimit ?? 5
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
//#endregion
|
|
276
|
+
export { zod as a, resolveJsonSchema as i, defineConfig as n, valibot as o, resolveConfig as r, CONFIG_SCHEMA_URL as t };
|