tinbase 0.2.2 → 0.6.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/README.md +64 -31
- package/dist/admin/api.d.ts +8 -1
- package/dist/admin/api.js +94 -1
- package/dist/admin/api.js.map +1 -1
- package/dist/admin/ui.d.ts +1 -1
- package/dist/admin/ui.js +16 -16
- package/dist/auth/handler.d.ts +13 -1
- package/dist/auth/handler.js +173 -6
- package/dist/auth/handler.js.map +1 -1
- package/dist/auth/inbox.d.ts +31 -0
- package/dist/auth/inbox.js +112 -0
- package/dist/auth/inbox.js.map +1 -0
- package/dist/auth/oauth.d.ts +59 -0
- package/dist/auth/oauth.js +209 -0
- package/dist/auth/oauth.js.map +1 -0
- package/dist/auth/qr.d.ts +8 -0
- package/dist/auth/qr.js +376 -0
- package/dist/auth/qr.js.map +1 -0
- package/dist/auth/totp.d.ts +19 -0
- package/dist/auth/totp.js +101 -0
- package/dist/auth/totp.js.map +1 -0
- package/dist/cli.js +168 -10
- package/dist/cli.js.map +1 -1
- package/dist/cron/service.d.ts +29 -0
- package/dist/cron/service.js +134 -0
- package/dist/cron/service.js.map +1 -0
- package/dist/db/bootstrap.d.ts +8 -1
- package/dist/db/bootstrap.js +240 -1
- package/dist/db/bootstrap.js.map +1 -1
- package/dist/db/database.d.ts +24 -0
- package/dist/db/database.js +61 -2
- package/dist/db/database.js.map +1 -1
- package/dist/db/emulated.d.ts +12 -0
- package/dist/db/emulated.js +156 -0
- package/dist/db/emulated.js.map +1 -0
- package/dist/db/engine.d.ts +2 -0
- package/dist/db/engine.js.map +1 -1
- package/dist/db/inspect.d.ts +13 -0
- package/dist/db/inspect.js +22 -0
- package/dist/db/inspect.js.map +1 -0
- package/dist/db/pgmem-engine.d.ts +22 -0
- package/dist/db/pgmem-engine.js +135 -0
- package/dist/db/pgmem-engine.js.map +1 -0
- package/dist/db/schema-diff.d.ts +23 -0
- package/dist/db/schema-diff.js +182 -0
- package/dist/db/schema-diff.js.map +1 -0
- package/dist/functions/deno-shim.d.ts +24 -0
- package/dist/functions/deno-shim.js +47 -0
- package/dist/functions/deno-shim.js.map +1 -0
- package/dist/functions/handler.d.ts +3 -1
- package/dist/functions/handler.js.map +1 -1
- package/dist/gen-types.d.ts +17 -0
- package/dist/gen-types.js +222 -0
- package/dist/gen-types.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +83 -15
- package/dist/index.js.map +1 -1
- package/dist/jwt.d.ts +7 -0
- package/dist/jwt.js.map +1 -1
- package/dist/log-buffer.d.ts +21 -0
- package/dist/log-buffer.js +20 -0
- package/dist/log-buffer.js.map +1 -0
- package/dist/node/bundle-function.d.ts +7 -0
- package/dist/node/bundle-function.js +90 -0
- package/dist/node/bundle-function.js.map +1 -0
- package/dist/node/db-diff.d.ts +34 -0
- package/dist/node/db-diff.js +82 -0
- package/dist/node/db-diff.js.map +1 -0
- package/dist/node/index.d.ts +4 -1
- package/dist/node/index.js +4 -1
- package/dist/node/index.js.map +1 -1
- package/dist/node/load-functions.d.ts +6 -0
- package/dist/node/load-functions.js +60 -8
- package/dist/node/load-functions.js.map +1 -1
- package/dist/node/load-oauth.d.ts +4 -0
- package/dist/node/load-oauth.js +126 -0
- package/dist/node/load-oauth.js.map +1 -0
- package/dist/node/native/engine.d.ts +1 -1
- package/dist/node/native/engine.js +45 -14
- package/dist/node/native/engine.js.map +1 -1
- package/dist/realtime/engine.d.ts +24 -1
- package/dist/realtime/engine.js +154 -2
- package/dist/realtime/engine.js.map +1 -1
- package/dist/rest/build.d.ts +5 -1
- package/dist/rest/build.js +17 -8
- package/dist/rest/build.js.map +1 -1
- package/dist/rest/handler.js +22 -9
- package/dist/rest/handler.js.map +1 -1
- package/dist/types.d.ts +10 -0
- package/dist/types.js.map +1 -1
- package/dist/webhooks/service.d.ts +47 -0
- package/dist/webhooks/service.js +86 -0
- package/dist/webhooks/service.js.map +1 -0
- package/package.json +7 -2
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure-SQL emulations of Supabase's queue and cron extensions, so migrations
|
|
3
|
+
* and app code that call pgmq.* / cron.* work with no C extension on either
|
|
4
|
+
* engine. pgmq is fully self-contained SQL. cron records jobs here; the
|
|
5
|
+
* in-process CronService (src/cron/service.ts) executes them.
|
|
6
|
+
*
|
|
7
|
+
* These match the real extensions' function signatures closely enough that
|
|
8
|
+
* `pgmq.send(...)`, `pgmq.read(...)`, `select cron.schedule(...)` etc. behave
|
|
9
|
+
* the same from the client's and a migration's point of view.
|
|
10
|
+
*/
|
|
11
|
+
// ── pgmq (message queue) ────────────────────────────────────────────────────
|
|
12
|
+
export const PGMQ_SQL = `
|
|
13
|
+
create schema if not exists pgmq;
|
|
14
|
+
grant usage on schema pgmq to anon, authenticated, service_role;
|
|
15
|
+
|
|
16
|
+
do $$ begin
|
|
17
|
+
if not exists (select 1 from pg_type t join pg_namespace n on n.oid = t.typnamespace
|
|
18
|
+
where t.typname = 'message_record' and n.nspname = 'pgmq') then
|
|
19
|
+
create type pgmq.message_record as (
|
|
20
|
+
msg_id bigint, read_ct integer, enqueued_at timestamptz, vt timestamptz, message jsonb
|
|
21
|
+
);
|
|
22
|
+
end if;
|
|
23
|
+
end $$;
|
|
24
|
+
|
|
25
|
+
create or replace function pgmq.create(queue_name text) returns void language plpgsql security definer set search_path = pgmq, pg_catalog, public as $pgmq$
|
|
26
|
+
begin
|
|
27
|
+
execute format('create table if not exists pgmq.%I (
|
|
28
|
+
msg_id bigint generated always as identity primary key,
|
|
29
|
+
read_ct integer not null default 0,
|
|
30
|
+
enqueued_at timestamptz not null default now(),
|
|
31
|
+
vt timestamptz not null default now(),
|
|
32
|
+
message jsonb)', 'q_' || queue_name);
|
|
33
|
+
execute format('create table if not exists pgmq.%I (
|
|
34
|
+
msg_id bigint primary key, read_ct integer not null default 0,
|
|
35
|
+
enqueued_at timestamptz not null, archived_at timestamptz not null default now(),
|
|
36
|
+
vt timestamptz, message jsonb)', 'a_' || queue_name);
|
|
37
|
+
end $pgmq$;
|
|
38
|
+
|
|
39
|
+
create or replace function pgmq.send(queue_name text, msg jsonb, delay integer default 0)
|
|
40
|
+
returns bigint language plpgsql security definer set search_path = pgmq, pg_catalog, public as $pgmq$
|
|
41
|
+
declare id bigint;
|
|
42
|
+
begin
|
|
43
|
+
perform pgmq.create(queue_name);
|
|
44
|
+
execute format('insert into pgmq.%I (vt, message) values (now() + make_interval(secs => $1), $2) returning msg_id', 'q_' || queue_name)
|
|
45
|
+
into id using delay, msg;
|
|
46
|
+
return id;
|
|
47
|
+
end $pgmq$;
|
|
48
|
+
|
|
49
|
+
create or replace function pgmq.read(queue_name text, vt integer, qty integer)
|
|
50
|
+
returns setof pgmq.message_record language plpgsql security definer set search_path = pgmq, pg_catalog, public as $pgmq$
|
|
51
|
+
begin
|
|
52
|
+
return query execute format($fmt$
|
|
53
|
+
with cte as (
|
|
54
|
+
select msg_id from pgmq.%I where vt <= now() order by msg_id limit $1 for update skip locked
|
|
55
|
+
)
|
|
56
|
+
update pgmq.%I m set vt = now() + make_interval(secs => $2), read_ct = read_ct + 1
|
|
57
|
+
from cte where m.msg_id = cte.msg_id
|
|
58
|
+
returning m.msg_id, m.read_ct, m.enqueued_at, m.vt, m.message
|
|
59
|
+
$fmt$, 'q_' || queue_name, 'q_' || queue_name) using qty, vt;
|
|
60
|
+
end $pgmq$;
|
|
61
|
+
|
|
62
|
+
create or replace function pgmq.pop(queue_name text)
|
|
63
|
+
returns setof pgmq.message_record language plpgsql security definer set search_path = pgmq, pg_catalog, public as $pgmq$
|
|
64
|
+
begin
|
|
65
|
+
return query execute format($fmt$
|
|
66
|
+
with cte as (select msg_id from pgmq.%I where vt <= now() order by msg_id limit 1 for update skip locked)
|
|
67
|
+
delete from pgmq.%I m using cte where m.msg_id = cte.msg_id
|
|
68
|
+
returning m.msg_id, m.read_ct, m.enqueued_at, m.vt, m.message
|
|
69
|
+
$fmt$, 'q_' || queue_name, 'q_' || queue_name);
|
|
70
|
+
end $pgmq$;
|
|
71
|
+
|
|
72
|
+
create or replace function pgmq.delete(queue_name text, msg_id bigint)
|
|
73
|
+
returns boolean language plpgsql security definer set search_path = pgmq, pg_catalog, public as $pgmq$
|
|
74
|
+
declare n integer;
|
|
75
|
+
begin
|
|
76
|
+
execute format('delete from pgmq.%I where msg_id = $1', 'q_' || queue_name) using msg_id;
|
|
77
|
+
get diagnostics n = row_count;
|
|
78
|
+
return n > 0;
|
|
79
|
+
end $pgmq$;
|
|
80
|
+
|
|
81
|
+
create or replace function pgmq.archive(queue_name text, msg_id bigint)
|
|
82
|
+
returns boolean language plpgsql security definer set search_path = pgmq, pg_catalog, public as $pgmq$
|
|
83
|
+
declare n integer;
|
|
84
|
+
begin
|
|
85
|
+
execute format($fmt$
|
|
86
|
+
with del as (delete from pgmq.%I where msg_id = $1 returning *)
|
|
87
|
+
insert into pgmq.%I (msg_id, read_ct, enqueued_at, vt, message)
|
|
88
|
+
select msg_id, read_ct, enqueued_at, vt, message from del
|
|
89
|
+
$fmt$, 'q_' || queue_name, 'a_' || queue_name) using msg_id;
|
|
90
|
+
get diagnostics n = row_count;
|
|
91
|
+
return n > 0;
|
|
92
|
+
end $pgmq$;
|
|
93
|
+
|
|
94
|
+
grant execute on all functions in schema pgmq to anon, authenticated, service_role;
|
|
95
|
+
`;
|
|
96
|
+
// ── cron (scheduled jobs) ────────────────────────────────────────────────────
|
|
97
|
+
export const CRON_SQL = `
|
|
98
|
+
create schema if not exists cron;
|
|
99
|
+
grant usage on schema cron to service_role, authenticated;
|
|
100
|
+
|
|
101
|
+
create table if not exists cron.job (
|
|
102
|
+
jobid bigint generated always as identity primary key,
|
|
103
|
+
schedule text not null,
|
|
104
|
+
command text not null,
|
|
105
|
+
jobname text unique,
|
|
106
|
+
active boolean not null default true,
|
|
107
|
+
created_at timestamptz not null default now()
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
create table if not exists cron.job_run_details (
|
|
111
|
+
runid bigint generated always as identity primary key,
|
|
112
|
+
jobid bigint,
|
|
113
|
+
command text,
|
|
114
|
+
status text,
|
|
115
|
+
return_message text,
|
|
116
|
+
start_time timestamptz,
|
|
117
|
+
end_time timestamptz
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
create or replace function cron.schedule(job_name text, schedule text, command text)
|
|
121
|
+
returns bigint language plpgsql security definer set search_path = cron, pg_catalog, public as $cron$
|
|
122
|
+
declare id bigint;
|
|
123
|
+
begin
|
|
124
|
+
insert into cron.job (jobname, schedule, command) values (job_name, schedule, command)
|
|
125
|
+
on conflict (jobname) do update set schedule = excluded.schedule, command = excluded.command, active = true
|
|
126
|
+
returning jobid into id;
|
|
127
|
+
return id;
|
|
128
|
+
end $cron$;
|
|
129
|
+
|
|
130
|
+
create or replace function cron.schedule(schedule text, command text)
|
|
131
|
+
returns bigint language plpgsql security definer set search_path = cron, pg_catalog, public as $cron$
|
|
132
|
+
begin
|
|
133
|
+
return cron.schedule('job_' || floor(extract(epoch from clock_timestamp()) * 1000)::bigint::text, schedule, command);
|
|
134
|
+
end $cron$;
|
|
135
|
+
|
|
136
|
+
create or replace function cron.unschedule(job_name text)
|
|
137
|
+
returns boolean language plpgsql security definer set search_path = cron, pg_catalog, public as $cron$
|
|
138
|
+
declare n integer;
|
|
139
|
+
begin
|
|
140
|
+
delete from cron.job where jobname = job_name;
|
|
141
|
+
get diagnostics n = row_count;
|
|
142
|
+
return n > 0;
|
|
143
|
+
end $cron$;
|
|
144
|
+
|
|
145
|
+
create or replace function cron.unschedule(job_id bigint)
|
|
146
|
+
returns boolean language plpgsql security definer set search_path = cron, pg_catalog, public as $cron$
|
|
147
|
+
declare n integer;
|
|
148
|
+
begin
|
|
149
|
+
delete from cron.job where jobid = job_id;
|
|
150
|
+
get diagnostics n = row_count;
|
|
151
|
+
return n > 0;
|
|
152
|
+
end $cron$;
|
|
153
|
+
|
|
154
|
+
grant execute on function cron.schedule(text,text,text), cron.schedule(text,text), cron.unschedule(text), cron.unschedule(bigint) to service_role, authenticated;
|
|
155
|
+
`;
|
|
156
|
+
//# sourceMappingURL=emulated.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"emulated.js","sourceRoot":"","sources":["../../src/db/emulated.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,+EAA+E;AAC/E,MAAM,CAAC,MAAM,QAAQ,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmFvB,CAAA;AAED,gFAAgF;AAChF,MAAM,CAAC,MAAM,QAAQ,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0DvB,CAAA"}
|
package/dist/db/engine.d.ts
CHANGED
|
@@ -13,6 +13,8 @@ export interface EngineTx {
|
|
|
13
13
|
exec(sql: string): Promise<void>;
|
|
14
14
|
}
|
|
15
15
|
export interface DbEngine {
|
|
16
|
+
/** true for subset engines (pg-mem) that can't run the full plpgsql/RLS bootstrap */
|
|
17
|
+
minimalBootstrap?: boolean;
|
|
16
18
|
query<T = any>(sql: string, params?: unknown[]): Promise<EngineResults<T>>;
|
|
17
19
|
/** Run multiple SQL statements (no params). */
|
|
18
20
|
exec(sql: string): Promise<void>;
|
package/dist/db/engine.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../../src/db/engine.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../../src/db/engine.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAyBH,yEAAyE;AACzE,MAAM,OAAO,KAAK;IACR,IAAI,GAAkB,OAAO,CAAC,OAAO,EAAE,CAAA;IAE/C,KAAK,CAAC,IAAI;QACR,IAAI,OAAoB,CAAA;QACxB,MAAM,IAAI,GAAG,IAAI,OAAO,CAAO,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,CAAA;QACpD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAA;QACtB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAA;QACtC,MAAM,IAAI,CAAA;QACV,OAAO,OAAO,CAAA;IAChB,CAAC;IAED,KAAK,CAAC,GAAG,CAAI,EAAoB;QAC/B,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAA;QACjC,IAAI,CAAC;YACH,OAAO,MAAM,EAAE,EAAE,CAAA;QACnB,CAAC;gBAAS,CAAC;YACT,OAAO,EAAE,CAAA;QACX,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `tinbase inspect` core: a lightweight database report — per-table row counts
|
|
3
|
+
* and on-disk size for a schema. Like a tiny `supabase inspect db`, enough to
|
|
4
|
+
* eyeball what's in a local project. (Uses pg_total_relation_size, so it runs
|
|
5
|
+
* on the real-Postgres engines, not the pg-mem subset.)
|
|
6
|
+
*/
|
|
7
|
+
import { type Database } from './database.js';
|
|
8
|
+
export interface TableInfo {
|
|
9
|
+
table: string;
|
|
10
|
+
rows: number;
|
|
11
|
+
size: string;
|
|
12
|
+
}
|
|
13
|
+
export declare function inspectDb(db: Database, schema?: string): Promise<TableInfo[]>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `tinbase inspect` core: a lightweight database report — per-table row counts
|
|
3
|
+
* and on-disk size for a schema. Like a tiny `supabase inspect db`, enough to
|
|
4
|
+
* eyeball what's in a local project. (Uses pg_total_relation_size, so it runs
|
|
5
|
+
* on the real-Postgres engines, not the pg-mem subset.)
|
|
6
|
+
*/
|
|
7
|
+
import { quoteIdent } from './database.js';
|
|
8
|
+
export async function inspectDb(db, schema = 'public') {
|
|
9
|
+
const tables = await db.query(`select c.relname as table,
|
|
10
|
+
pg_size_pretty(pg_total_relation_size(c.oid)) as size
|
|
11
|
+
from pg_class c
|
|
12
|
+
join pg_namespace n on n.oid = c.relnamespace
|
|
13
|
+
where n.nspname = $1 and c.relkind = 'r'
|
|
14
|
+
order by pg_total_relation_size(c.oid) desc, c.relname`, [schema]);
|
|
15
|
+
const out = [];
|
|
16
|
+
for (const t of tables.rows) {
|
|
17
|
+
const c = await db.query(`select count(*)::int as n from ${quoteIdent(schema)}.${quoteIdent(t.table)}`);
|
|
18
|
+
out.push({ table: t.table, rows: c.rows[0]?.n ?? 0, size: t.size });
|
|
19
|
+
}
|
|
20
|
+
return out;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=inspect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inspect.js","sourceRoot":"","sources":["../../src/db/inspect.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,UAAU,EAAiB,MAAM,eAAe,CAAA;AAQzD,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,EAAY,EAAE,MAAM,GAAG,QAAQ;IAC7D,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,KAAK,CAC3B;;;;;4DAKwD,EACxD,CAAC,MAAM,CAAC,CACT,CAAA;IACD,MAAM,GAAG,GAAgB,EAAE,CAAA;IAC3B,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;QAC5B,MAAM,CAAC,GAAG,MAAM,EAAE,CAAC,KAAK,CACtB,kCAAkC,UAAU,CAAC,MAAM,CAAC,IAAI,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAC9E,CAAA;QACD,GAAG,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAA;IACrE,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pg-mem engine — an ultralight, pure-JS, in-memory Postgres *subset* for
|
|
3
|
+
* local dev / previews (RapidNative) where footprint matters more than full
|
|
4
|
+
* fidelity. A ~3.6 MB install with no WASM, vs PGlite's ~600 MB heap.
|
|
5
|
+
*
|
|
6
|
+
* What works: the REST CRUD surface (select/insert/update/delete, filters,
|
|
7
|
+
* embeds, count), email/password auth, edge functions, realtime
|
|
8
|
+
* (broadcast/presence + postgres_changes), and database webhooks. Because
|
|
9
|
+
* pg-mem has no triggers/LISTEN/NOTIFY, change events for realtime and webhooks
|
|
10
|
+
* are synthesized in JS by the REST layer (see Database.emitCdc) — every write
|
|
11
|
+
* goes through it in-process.
|
|
12
|
+
*
|
|
13
|
+
* What's explicitly NOT here (it is a subset, not a faithful Postgres):
|
|
14
|
+
* - NO row-level security (policies are skipped; RLS is not enforced) — so
|
|
15
|
+
* realtime/webhook events are delivered UNFILTERED, not per-subscriber
|
|
16
|
+
* - NO PL/pgSQL functions, pgmq, or cron
|
|
17
|
+
* - reduced introspection; UPDATE change events carry no old_record
|
|
18
|
+
*
|
|
19
|
+
* Use it for throwaway local data and previews, never production.
|
|
20
|
+
*/
|
|
21
|
+
import type { DbEngine } from './engine.js';
|
|
22
|
+
export declare function createPgmemEngine(): Promise<DbEngine>;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { Mutex } from './engine.js';
|
|
2
|
+
/** Split SQL into statements, respecting $tag$…$tag$ dollar-quoted blocks. */
|
|
3
|
+
function splitStatements(sql) {
|
|
4
|
+
const out = [];
|
|
5
|
+
let cur = '';
|
|
6
|
+
let i = 0;
|
|
7
|
+
let dollarTag = null;
|
|
8
|
+
while (i < sql.length) {
|
|
9
|
+
const ch = sql[i];
|
|
10
|
+
if (dollarTag) {
|
|
11
|
+
if (sql.startsWith(dollarTag, i)) {
|
|
12
|
+
cur += dollarTag;
|
|
13
|
+
i += dollarTag.length;
|
|
14
|
+
dollarTag = null;
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
cur += ch;
|
|
18
|
+
i++;
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
const m = ch === '$' ? sql.slice(i).match(/^\$[a-zA-Z_]*\$/) : null;
|
|
22
|
+
if (m) {
|
|
23
|
+
dollarTag = m[0];
|
|
24
|
+
cur += dollarTag;
|
|
25
|
+
i += dollarTag.length;
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
if (ch === ';') {
|
|
29
|
+
if (cur.trim())
|
|
30
|
+
out.push(cur.trim());
|
|
31
|
+
cur = '';
|
|
32
|
+
i++;
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
cur += ch;
|
|
36
|
+
i++;
|
|
37
|
+
}
|
|
38
|
+
if (cur.trim())
|
|
39
|
+
out.push(cur.trim());
|
|
40
|
+
return out;
|
|
41
|
+
}
|
|
42
|
+
/** Statements a real Supabase migration may contain that pg-mem can't run. */
|
|
43
|
+
function isSkippable(stmt) {
|
|
44
|
+
const s = stmt.toLowerCase().replace(/\s+/g, ' ').trim();
|
|
45
|
+
return (/^create( or replace)? policy/.test(s) ||
|
|
46
|
+
/^drop policy/.test(s) ||
|
|
47
|
+
/row level security/.test(s) ||
|
|
48
|
+
/^create extension/.test(s) ||
|
|
49
|
+
/^create( or replace)? trigger/.test(s) ||
|
|
50
|
+
/^create( or replace)? function/.test(s) && /language\s+plpgsql/.test(s) ||
|
|
51
|
+
/^do \$/.test(s) ||
|
|
52
|
+
/^grant /.test(s) ||
|
|
53
|
+
/^revoke /.test(s) ||
|
|
54
|
+
/^comment on/.test(s) ||
|
|
55
|
+
/^create publication/.test(s) ||
|
|
56
|
+
/^alter publication/.test(s));
|
|
57
|
+
}
|
|
58
|
+
export async function createPgmemEngine() {
|
|
59
|
+
let newDb, DataType;
|
|
60
|
+
try {
|
|
61
|
+
;
|
|
62
|
+
({ newDb, DataType } = await import('pg-mem'));
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
throw new Error('the pg-mem engine requires the optional `pg-mem` dependency — run `npm install pg-mem`');
|
|
66
|
+
}
|
|
67
|
+
const db = newDb({ autoCreateForeignKeyIndices: true });
|
|
68
|
+
// functions our REST/bootstrap SQL needs that pg-mem lacks
|
|
69
|
+
// impure: true so pg-mem evaluates them per row (not once per statement) —
|
|
70
|
+
// otherwise every row in a multi-row insert gets the same UUID → PK collision
|
|
71
|
+
db.public.registerFunction({ name: 'gen_random_uuid', returns: DataType.uuid, impure: true, implementation: () => crypto.randomUUID() });
|
|
72
|
+
db.public.registerFunction({ name: 'uuid_generate_v4', returns: DataType.uuid, impure: true, implementation: () => crypto.randomUUID() });
|
|
73
|
+
db.public.registerFunction({
|
|
74
|
+
name: 'row_to_json',
|
|
75
|
+
args: [DataType.record],
|
|
76
|
+
returns: DataType.jsonb,
|
|
77
|
+
implementation: ((r) => r),
|
|
78
|
+
});
|
|
79
|
+
// set_config/current_setting exist in pg-mem; RLS context is a harmless no-op here
|
|
80
|
+
const { Client } = db.adapters.createPg();
|
|
81
|
+
const client = new Client();
|
|
82
|
+
await client.connect();
|
|
83
|
+
const mutex = new Mutex();
|
|
84
|
+
const run = async (sql, params) => {
|
|
85
|
+
const res = await client.query(params && params.length ? { text: sql, values: params } : sql);
|
|
86
|
+
return { rows: (res.rows ?? []), affectedRows: res.rowCount ?? undefined };
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* Tolerant exec: run statements one at a time and skip the ones a real
|
|
90
|
+
* Supabase migration contains that pg-mem can't do — RLS policies, plpgsql
|
|
91
|
+
* functions/triggers, extensions, grants. Genuine errors on supported DDL
|
|
92
|
+
* still throw. Skipped statements are logged so nothing silently vanishes.
|
|
93
|
+
*/
|
|
94
|
+
const exec = async (sql) => {
|
|
95
|
+
for (const stmt of splitStatements(sql)) {
|
|
96
|
+
try {
|
|
97
|
+
await client.query(stmt);
|
|
98
|
+
}
|
|
99
|
+
catch (e) {
|
|
100
|
+
if (isSkippable(stmt)) {
|
|
101
|
+
console.warn(` [pgmem] skipped unsupported statement: ${stmt.replace(/\s+/g, ' ').slice(0, 70)}…`);
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
throw e;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
const tx = {
|
|
109
|
+
query: (sql, params) => run(sql, params),
|
|
110
|
+
exec,
|
|
111
|
+
};
|
|
112
|
+
return {
|
|
113
|
+
minimalBootstrap: true,
|
|
114
|
+
query: (sql, params) => mutex.run(() => run(sql, params)),
|
|
115
|
+
exec: (sql) => mutex.run(() => exec(sql)),
|
|
116
|
+
transaction: (fn) => mutex.run(async () => {
|
|
117
|
+
// pg-mem is single-connection in-memory; serialize and use a snapshot so
|
|
118
|
+
// a thrown fn rolls back
|
|
119
|
+
const restore = db.backup();
|
|
120
|
+
try {
|
|
121
|
+
return await fn(tx);
|
|
122
|
+
}
|
|
123
|
+
catch (e) {
|
|
124
|
+
restore.restore();
|
|
125
|
+
throw e;
|
|
126
|
+
}
|
|
127
|
+
}),
|
|
128
|
+
// no LISTEN/NOTIFY in pg-mem — realtime/webhooks simply don't fire
|
|
129
|
+
listen: async () => () => { },
|
|
130
|
+
close: async () => {
|
|
131
|
+
await client.end();
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=pgmem-engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pgmem-engine.js","sourceRoot":"","sources":["../../src/db/pgmem-engine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Database } from './database.js';
|
|
2
|
+
interface ColumnSnap {
|
|
3
|
+
name: string;
|
|
4
|
+
type: string;
|
|
5
|
+
nullable: boolean;
|
|
6
|
+
default: string | null;
|
|
7
|
+
}
|
|
8
|
+
interface TableSnap {
|
|
9
|
+
name: string;
|
|
10
|
+
columns: Map<string, ColumnSnap>;
|
|
11
|
+
order: string[];
|
|
12
|
+
}
|
|
13
|
+
export interface SchemaSnapshot {
|
|
14
|
+
tables: Map<string, TableSnap>;
|
|
15
|
+
/** constraint/index name → definition, keyed by table */
|
|
16
|
+
constraints: Map<string, Map<string, string>>;
|
|
17
|
+
indexes: Map<string, Map<string, string>>;
|
|
18
|
+
enums: Map<string, string[]>;
|
|
19
|
+
}
|
|
20
|
+
export declare function snapshotSchema(db: Database, schema?: string): Promise<SchemaSnapshot>;
|
|
21
|
+
/** DDL to turn `from` into `to`, scoped to one schema. Empty array = no changes. */
|
|
22
|
+
export declare function diffSchemas(from: SchemaSnapshot, to: SchemaSnapshot, schema?: string): string[];
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema snapshot + diff — the engine behind `tinbase db diff`.
|
|
3
|
+
*
|
|
4
|
+
* Snapshots a schema (tables, columns, constraints, indexes, enums) into a
|
|
5
|
+
* structured form, then emits the DDL to turn one snapshot into another. Used
|
|
6
|
+
* to capture changes made outside migrations (e.g. in the Studio SQL editor)
|
|
7
|
+
* into a new migration, the way `supabase db diff` (migra) does.
|
|
8
|
+
*
|
|
9
|
+
* Covered: enums (create + add value), tables (create/drop), columns
|
|
10
|
+
* (add/drop/alter type/nullability/default), and named constraints + indexes
|
|
11
|
+
* (add/drop by definition). Not yet diffed: functions, triggers, policies,
|
|
12
|
+
* views — noted so the output never silently claims completeness.
|
|
13
|
+
*/
|
|
14
|
+
import { quoteIdent } from './database.js';
|
|
15
|
+
export async function snapshotSchema(db, schema = 'public') {
|
|
16
|
+
const cols = await db.query(`select c.relname as table, a.attname as column,
|
|
17
|
+
format_type(a.atttypid, a.atttypmod) as type,
|
|
18
|
+
not a.attnotnull as nullable,
|
|
19
|
+
pg_get_expr(d.adbin, d.adrelid) as default
|
|
20
|
+
from pg_attribute a
|
|
21
|
+
join pg_class c on c.oid = a.attrelid
|
|
22
|
+
join pg_namespace n on n.oid = c.relnamespace
|
|
23
|
+
left join pg_attrdef d on d.adrelid = c.oid and d.adnum = a.attnum
|
|
24
|
+
where n.nspname = $1 and c.relkind = 'r' and a.attnum > 0 and not a.attisdropped
|
|
25
|
+
order by c.relname, a.attnum`, [schema]);
|
|
26
|
+
const tables = new Map();
|
|
27
|
+
for (const c of cols.rows) {
|
|
28
|
+
let t = tables.get(c.table);
|
|
29
|
+
if (!t) {
|
|
30
|
+
t = { name: c.table, columns: new Map(), order: [] };
|
|
31
|
+
tables.set(c.table, t);
|
|
32
|
+
}
|
|
33
|
+
t.columns.set(c.column, { name: c.column, type: c.type, nullable: c.nullable, default: c.default });
|
|
34
|
+
t.order.push(c.column);
|
|
35
|
+
}
|
|
36
|
+
const cons = await db.query(`select c.relname as table, con.conname as name, pg_get_constraintdef(con.oid) as def, con.conindid::int as conindid
|
|
37
|
+
from pg_constraint con
|
|
38
|
+
join pg_class c on c.oid = con.conrelid
|
|
39
|
+
join pg_namespace n on n.oid = c.relnamespace
|
|
40
|
+
where n.nspname = $1 and con.contype in ('p','u','f','c')`, [schema]);
|
|
41
|
+
const constraints = new Map();
|
|
42
|
+
const constraintIndexOids = new Set();
|
|
43
|
+
for (const c of cons.rows) {
|
|
44
|
+
if (!constraints.has(c.table))
|
|
45
|
+
constraints.set(c.table, new Map());
|
|
46
|
+
constraints.get(c.table).set(c.name, c.def);
|
|
47
|
+
if (c.conindid)
|
|
48
|
+
constraintIndexOids.add(c.conindid);
|
|
49
|
+
}
|
|
50
|
+
// indexes not backing a constraint (those are emitted via the constraint)
|
|
51
|
+
const idx = await db.query(`select c.relname as table, ic.relname as name, pg_get_indexdef(i.indexrelid) as def, i.indexrelid::int as indexrelid
|
|
52
|
+
from pg_index i
|
|
53
|
+
join pg_class ic on ic.oid = i.indexrelid
|
|
54
|
+
join pg_class c on c.oid = i.indrelid
|
|
55
|
+
join pg_namespace n on n.oid = c.relnamespace
|
|
56
|
+
where n.nspname = $1`, [schema]);
|
|
57
|
+
const indexes = new Map();
|
|
58
|
+
for (const r of idx.rows) {
|
|
59
|
+
if (constraintIndexOids.has(r.indexrelid))
|
|
60
|
+
continue;
|
|
61
|
+
if (!indexes.has(r.table))
|
|
62
|
+
indexes.set(r.table, new Map());
|
|
63
|
+
indexes.get(r.table).set(r.name, r.def);
|
|
64
|
+
}
|
|
65
|
+
const en = await db.query(`select t.typname as name, array_agg(e.enumlabel order by e.enumsortorder) as labels
|
|
66
|
+
from pg_type t join pg_enum e on e.enumtypid = t.oid
|
|
67
|
+
join pg_namespace n on n.oid = t.typnamespace
|
|
68
|
+
where n.nspname = $1 group by t.typname`, [schema]);
|
|
69
|
+
const enums = new Map();
|
|
70
|
+
for (const r of en.rows)
|
|
71
|
+
enums.set(r.name, r.labels);
|
|
72
|
+
return { tables, constraints, indexes, enums };
|
|
73
|
+
}
|
|
74
|
+
/** DDL to turn `from` into `to`, scoped to one schema. Empty array = no changes. */
|
|
75
|
+
export function diffSchemas(from, to, schema = 'public') {
|
|
76
|
+
const out = [];
|
|
77
|
+
const q = (n) => quoteIdent(n);
|
|
78
|
+
const tbl = (t) => `${q(schema)}.${q(t)}`;
|
|
79
|
+
// ── enums ──
|
|
80
|
+
for (const [name, labels] of to.enums) {
|
|
81
|
+
if (!from.enums.has(name)) {
|
|
82
|
+
out.push(`create type ${q(schema)}.${q(name)} as enum (${labels.map((l) => `'${l.replace(/'/g, "''")}'`).join(', ')});`);
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
const before = from.enums.get(name);
|
|
86
|
+
for (const label of labels) {
|
|
87
|
+
if (!before.includes(label))
|
|
88
|
+
out.push(`alter type ${q(schema)}.${q(name)} add value '${label.replace(/'/g, "''")}';`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
// ── new tables ──
|
|
93
|
+
for (const [name, t] of to.tables) {
|
|
94
|
+
if (from.tables.has(name))
|
|
95
|
+
continue;
|
|
96
|
+
const colDefs = t.order.map((cn) => columnClause(t.columns.get(cn), q));
|
|
97
|
+
out.push(`create table ${tbl(name)} (\n${colDefs.map((c) => ` ${c}`).join(',\n')}\n);`);
|
|
98
|
+
}
|
|
99
|
+
// ── column-level diffs on shared tables ──
|
|
100
|
+
for (const [name, toT] of to.tables) {
|
|
101
|
+
const fromT = from.tables.get(name);
|
|
102
|
+
if (!fromT)
|
|
103
|
+
continue;
|
|
104
|
+
for (const cn of toT.order) {
|
|
105
|
+
const toC = toT.columns.get(cn);
|
|
106
|
+
const fromC = fromT.columns.get(cn);
|
|
107
|
+
if (!fromC) {
|
|
108
|
+
out.push(`alter table ${tbl(name)} add column ${columnClause(toC, q)};`);
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
if (fromC.type !== toC.type)
|
|
112
|
+
out.push(`alter table ${tbl(name)} alter column ${q(cn)} type ${toC.type};`);
|
|
113
|
+
if (fromC.nullable !== toC.nullable)
|
|
114
|
+
out.push(`alter table ${tbl(name)} alter column ${q(cn)} ${toC.nullable ? 'drop not null' : 'set not null'};`);
|
|
115
|
+
if ((fromC.default ?? null) !== (toC.default ?? null)) {
|
|
116
|
+
out.push(toC.default === null
|
|
117
|
+
? `alter table ${tbl(name)} alter column ${q(cn)} drop default;`
|
|
118
|
+
: `alter table ${tbl(name)} alter column ${q(cn)} set default ${toC.default};`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
for (const cn of fromT.order) {
|
|
122
|
+
if (!toT.columns.has(cn))
|
|
123
|
+
out.push(`alter table ${tbl(name)} drop column ${q(cn)};`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
// ── dropped tables ──
|
|
127
|
+
for (const name of from.tables.keys()) {
|
|
128
|
+
if (!to.tables.has(name))
|
|
129
|
+
out.push(`drop table ${tbl(name)};`);
|
|
130
|
+
}
|
|
131
|
+
// ── constraints (drop changed/removed, then add new) ──
|
|
132
|
+
diffNamed(from.constraints, to.constraints, {
|
|
133
|
+
drop: (table, cname) => out.push(`alter table ${tbl(table)} drop constraint ${q(cname)};`),
|
|
134
|
+
add: (table, cname, def) => out.push(`alter table ${tbl(table)} add constraint ${q(cname)} ${def};`),
|
|
135
|
+
});
|
|
136
|
+
// ── indexes (drop changed/removed, then add new) ──
|
|
137
|
+
diffNamed(from.indexes, to.indexes, {
|
|
138
|
+
drop: (_table, iname) => out.push(`drop index ${q(schema)}.${q(iname)};`),
|
|
139
|
+
add: (_table, _iname, def) => out.push(`${def};`),
|
|
140
|
+
});
|
|
141
|
+
return out;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Render a column definition. A `serial`-style column introspects as an integer
|
|
145
|
+
* with a `nextval('..._seq')` default and an owned sequence; re-emit it as
|
|
146
|
+
* serial/bigserial/smallserial so the sequence is recreated with the table
|
|
147
|
+
* (a bare `default nextval(...)` would reference a sequence that doesn't exist yet).
|
|
148
|
+
*/
|
|
149
|
+
function columnClause(c, q) {
|
|
150
|
+
const isSerialDefault = c.default !== null && /^nextval\(/i.test(c.default);
|
|
151
|
+
if (isSerialDefault) {
|
|
152
|
+
const serial = c.type === 'bigint' ? 'bigserial' : c.type === 'smallint' ? 'smallserial' : 'serial';
|
|
153
|
+
return `${q(c.name)} ${serial}` + (c.nullable ? '' : ' not null');
|
|
154
|
+
}
|
|
155
|
+
let s = `${q(c.name)} ${c.type}`;
|
|
156
|
+
if (!c.nullable)
|
|
157
|
+
s += ' not null';
|
|
158
|
+
if (c.default !== null)
|
|
159
|
+
s += ` default ${c.default}`;
|
|
160
|
+
return s;
|
|
161
|
+
}
|
|
162
|
+
function diffNamed(from, to, ops) {
|
|
163
|
+
// drops (and changed → drop first)
|
|
164
|
+
for (const [table, defs] of from) {
|
|
165
|
+
const toDefs = to.get(table) ?? new Map();
|
|
166
|
+
for (const [name, def] of defs) {
|
|
167
|
+
const toDef = toDefs.get(name);
|
|
168
|
+
if (toDef === undefined || toDef !== def)
|
|
169
|
+
ops.drop(table, name, def);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
// adds (and changed → re-add)
|
|
173
|
+
for (const [table, defs] of to) {
|
|
174
|
+
const fromDefs = from.get(table) ?? new Map();
|
|
175
|
+
for (const [name, def] of defs) {
|
|
176
|
+
const fromDef = fromDefs.get(name);
|
|
177
|
+
if (fromDef === undefined || fromDef !== def)
|
|
178
|
+
ops.add(table, name, def);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=schema-diff.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-diff.js","sourceRoot":"","sources":["../../src/db/schema-diff.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal `Deno` global shim so Supabase-style edge functions run unchanged
|
|
3
|
+
* under Node/Bun. Supabase functions are written for Deno:
|
|
4
|
+
*
|
|
5
|
+
* Deno.serve((req) => new Response(...))
|
|
6
|
+
* const url = Deno.env.get('SUPABASE_URL')
|
|
7
|
+
*
|
|
8
|
+
* Instead of starting a server, our `Deno.serve` captures the handler so the
|
|
9
|
+
* FunctionsHandler can invoke it per request; `Deno.env` reads the injected
|
|
10
|
+
* SUPABASE_* vars, function secrets, and process.env.
|
|
11
|
+
*
|
|
12
|
+
* Not resolved: `npm:` / `jsr:` / URL import specifiers and the Deno std lib —
|
|
13
|
+
* a function using those needs a bundling step. Functions that stick to Web
|
|
14
|
+
* APIs (fetch/Request/Response) + Deno.serve/Deno.env work as-is.
|
|
15
|
+
*/
|
|
16
|
+
type DenoHandler = (req: Request) => Response | Promise<Response>;
|
|
17
|
+
/** Merge variables the shim's Deno.env should expose (SUPABASE_* keys, secrets). */
|
|
18
|
+
export declare function setDenoEnv(env: Record<string, string>): void;
|
|
19
|
+
/** Install globalThis.Deno if we're not already running under a Deno-like runtime. */
|
|
20
|
+
export declare function installDenoShim(): void;
|
|
21
|
+
/** Return and clear the handler captured by the most recent Deno.serve() call. */
|
|
22
|
+
export declare function takeCapturedHandler(): DenoHandler | undefined;
|
|
23
|
+
export declare function resetCapturedHandler(): void;
|
|
24
|
+
export {};
|