tinbase 0.2.1 → 0.5.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 +38 -8
- package/dist/admin/api.d.ts +5 -0
- package/dist/admin/api.js +85 -0
- 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 +7 -0
- package/dist/auth/handler.js +25 -0
- package/dist/auth/handler.js.map +1 -1
- 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/cli.js +105 -2
- 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 +1 -1
- package/dist/db/bootstrap.js +43 -1
- package/dist/db/bootstrap.js.map +1 -1
- package/dist/db/database.d.ts +3 -0
- package/dist/db/database.js +19 -0
- 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/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/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 +9 -0
- package/dist/index.js +35 -6
- package/dist/index.js.map +1 -1
- package/dist/node/db-diff.d.ts +13 -0
- package/dist/node/db-diff.js +40 -0
- package/dist/node/db-diff.js.map +1 -0
- package/dist/node/index.d.ts +2 -0
- package/dist/node/index.js +2 -0
- package/dist/node/index.js.map +1 -1
- package/dist/node/load-functions.js +11 -3
- 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 +98 -22
- package/dist/node/native/engine.js.map +1 -1
- package/dist/realtime/engine.d.ts +14 -1
- package/dist/realtime/engine.js +65 -2
- package/dist/realtime/engine.js.map +1 -1
- package/dist/types.d.ts +8 -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 +3 -2
package/dist/index.js
CHANGED
|
@@ -11,12 +11,15 @@ import { AdminApi } from './admin/api.js';
|
|
|
11
11
|
import { ADMIN_HTML } from './admin/ui.js';
|
|
12
12
|
import { AuthHandler } from './auth/handler.js';
|
|
13
13
|
import { FunctionsHandler } from './functions/handler.js';
|
|
14
|
+
import { installDenoShim, setDenoEnv } from './functions/deno-shim.js';
|
|
14
15
|
import { Database } from './db/database.js';
|
|
15
16
|
import { signJwt, verifyJwt } from './jwt.js';
|
|
16
17
|
import { RealtimeEngine } from './realtime/engine.js';
|
|
17
18
|
import { RestHandler } from './rest/handler.js';
|
|
18
19
|
import { MemoryStorageDriver } from './storage/driver.js';
|
|
19
20
|
import { StorageHandler } from './storage/handler.js';
|
|
21
|
+
import { WebhooksService } from './webhooks/service.js';
|
|
22
|
+
import { CronService } from './cron/service.js';
|
|
20
23
|
import { DEFAULT_JWT_SECRET } from './types.js';
|
|
21
24
|
export * from './types.js';
|
|
22
25
|
export { Database } from './db/database.js';
|
|
@@ -24,6 +27,11 @@ export { MemoryStorageDriver } from './storage/driver.js';
|
|
|
24
27
|
export { RealtimeEngine } from './realtime/engine.js';
|
|
25
28
|
export { signJwt, verifyJwt, decodeJwt } from './jwt.js';
|
|
26
29
|
export { FunctionsHandler } from './functions/handler.js';
|
|
30
|
+
export { generateTypes } from './gen-types.js';
|
|
31
|
+
export { installDenoShim, setDenoEnv } from './functions/deno-shim.js';
|
|
32
|
+
export { WebhooksService } from './webhooks/service.js';
|
|
33
|
+
export { CronService, cronMatches } from './cron/service.js';
|
|
34
|
+
export { snapshotSchema, diffSchemas } from './db/schema-diff.js';
|
|
27
35
|
const CORS_HEADERS = {
|
|
28
36
|
'access-control-allow-origin': '*',
|
|
29
37
|
'access-control-allow-methods': 'GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD',
|
|
@@ -52,19 +60,35 @@ export async function createBackend(config = {}) {
|
|
|
52
60
|
log(`[mail] to=${msg.to} subject="${msg.subject}"\n${msg.text}`);
|
|
53
61
|
},
|
|
54
62
|
};
|
|
55
|
-
const auth = new AuthHandler(db, {
|
|
63
|
+
const auth = new AuthHandler(db, {
|
|
64
|
+
jwtSecret,
|
|
65
|
+
siteUrl,
|
|
66
|
+
jwtExpiry,
|
|
67
|
+
mailer,
|
|
68
|
+
oauthProviders: config.oauthProviders,
|
|
69
|
+
oauthFetch: config.oauthFetch,
|
|
70
|
+
});
|
|
56
71
|
const storage = new StorageHandler(db, config.storageDriver ?? new MemoryStorageDriver(), { jwtSecret });
|
|
57
|
-
const realtime = new RealtimeEngine(db);
|
|
72
|
+
const realtime = new RealtimeEngine(db, jwtSecret);
|
|
58
73
|
await realtime.start();
|
|
74
|
+
const webhooks = new WebhooksService(db, config.webhookFetch, (d) => config.log?.(`[webhook] ${d.event.type} ${d.event.schema}.${d.event.table} -> ${d.webhook.url} ${d.ok ? d.status : 'FAILED ' + (d.error ?? '')}`));
|
|
75
|
+
if (config.webhooks?.length)
|
|
76
|
+
await webhooks.start(config.webhooks);
|
|
77
|
+
const cron = new CronService(db);
|
|
78
|
+
cron.start();
|
|
59
79
|
const admin = new AdminApi(db);
|
|
60
80
|
const fnMap = config.functions instanceof Map
|
|
61
81
|
? config.functions
|
|
62
82
|
: new Map(Object.entries(config.functions ?? {}));
|
|
63
|
-
const
|
|
83
|
+
const fnEnv = {
|
|
64
84
|
SUPABASE_URL: siteUrl,
|
|
65
85
|
SUPABASE_ANON_KEY: anonKey,
|
|
66
86
|
SUPABASE_SERVICE_ROLE_KEY: serviceRoleKey,
|
|
67
|
-
}
|
|
87
|
+
};
|
|
88
|
+
// make these visible to Deno.serve-style functions via the shim's Deno.env
|
|
89
|
+
installDenoShim();
|
|
90
|
+
setDenoEnv(fnEnv);
|
|
91
|
+
const functions = new FunctionsHandler(fnMap, fnEnv);
|
|
68
92
|
async function resolveContext(req, url) {
|
|
69
93
|
const authz = req.headers.get('authorization');
|
|
70
94
|
const bearer = authz?.toLowerCase().startsWith('bearer ') ? authz.slice(7) : null;
|
|
@@ -112,8 +136,9 @@ export async function createBackend(config = {}) {
|
|
|
112
136
|
return withCors(await storage.handle(req, { role: 'anon', claims: null }, url));
|
|
113
137
|
}
|
|
114
138
|
}
|
|
115
|
-
if (path === '/auth/v1/verify'
|
|
116
|
-
|
|
139
|
+
if ((path === '/auth/v1/verify' || path === '/auth/v1/authorize' || path === '/auth/v1/callback') &&
|
|
140
|
+
(req.method === 'GET' || req.method === 'POST')) {
|
|
141
|
+
// email-link clicks and OAuth provider redirects arrive without an apikey
|
|
117
142
|
return withCors(await auth.handle(req, { role: 'anon', claims: null }, url));
|
|
118
143
|
}
|
|
119
144
|
if (path.startsWith('/auth/v1/')) {
|
|
@@ -156,11 +181,15 @@ export async function createBackend(config = {}) {
|
|
|
156
181
|
db,
|
|
157
182
|
realtime,
|
|
158
183
|
functions,
|
|
184
|
+
webhooks,
|
|
185
|
+
cron,
|
|
159
186
|
anonKey,
|
|
160
187
|
serviceRoleKey,
|
|
161
188
|
jwtSecret,
|
|
162
189
|
migrate: (migrations, seedSql) => db.runMigrations(migrations, seedSql),
|
|
163
190
|
close: async () => {
|
|
191
|
+
cron.stop();
|
|
192
|
+
webhooks.stopService();
|
|
164
193
|
realtime.stop();
|
|
165
194
|
await db.close();
|
|
166
195
|
},
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,EAAE,gBAAgB,EAAqB,MAAM,wBAAwB,CAAA;AAC5E,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC3C,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AACrD,OAAO,EAAE,kBAAkB,EAA+D,MAAM,YAAY,CAAA;AAE5G,cAAc,YAAY,CAAA;AAC1B,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC3C,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AACzD,OAAO,EAAE,cAAc,EAA2B,MAAM,sBAAsB,CAAA;AAC9E,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AACxD,OAAO,EAAE,gBAAgB,EAA2C,MAAM,wBAAwB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { MigrationFile } from '../types.js';
|
|
2
|
+
export interface DbDiffOptions {
|
|
3
|
+
/** the live project's data dir (wasm) or undefined when a native engine is passed */
|
|
4
|
+
liveDataDir?: string;
|
|
5
|
+
liveEngine?: import('../db/engine.js').DbEngine;
|
|
6
|
+
migrations: MigrationFile[];
|
|
7
|
+
seedSql?: string;
|
|
8
|
+
schema?: string;
|
|
9
|
+
/** factory for the shadow engine (native mode); omit for wasm/in-memory shadow */
|
|
10
|
+
makeShadowEngine?: () => Promise<import('../db/engine.js').DbEngine>;
|
|
11
|
+
}
|
|
12
|
+
export declare function computeDbDiff(opts: DbDiffOptions): Promise<string[]>;
|
|
13
|
+
export declare function shadowNativeDataDir(): string;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `tinbase db diff` core: diff the live project database (which may contain
|
|
3
|
+
* changes made outside migrations) against a fresh "shadow" database that has
|
|
4
|
+
* only the migrations applied. The emitted DDL is the delta you'd save as a
|
|
5
|
+
* new migration.
|
|
6
|
+
*/
|
|
7
|
+
import { mkdtempSync } from 'node:fs';
|
|
8
|
+
import { tmpdir } from 'node:os';
|
|
9
|
+
import { join } from 'node:path';
|
|
10
|
+
import { createBackend } from '../index.js';
|
|
11
|
+
import { snapshotSchema, diffSchemas } from '../db/schema-diff.js';
|
|
12
|
+
export async function computeDbDiff(opts) {
|
|
13
|
+
const schema = opts.schema ?? 'public';
|
|
14
|
+
// shadow = migrations only, fresh
|
|
15
|
+
const shadow = await createBackend({
|
|
16
|
+
engine: opts.makeShadowEngine ? await opts.makeShadowEngine() : undefined,
|
|
17
|
+
migrations: opts.migrations,
|
|
18
|
+
// no seed: seed is data, not schema
|
|
19
|
+
});
|
|
20
|
+
// live = current project db (createBackend only applies *pending* migrations, so
|
|
21
|
+
// this reflects the actual current schema including out-of-migration changes)
|
|
22
|
+
const live = await createBackend({
|
|
23
|
+
engine: opts.liveEngine,
|
|
24
|
+
dataDir: opts.liveEngine ? undefined : opts.liveDataDir,
|
|
25
|
+
migrations: opts.migrations,
|
|
26
|
+
});
|
|
27
|
+
try {
|
|
28
|
+
const shadowSnap = await snapshotSchema(shadow.db, schema);
|
|
29
|
+
const liveSnap = await snapshotSchema(live.db, schema);
|
|
30
|
+
return diffSchemas(shadowSnap, liveSnap, schema);
|
|
31
|
+
}
|
|
32
|
+
finally {
|
|
33
|
+
await shadow.close();
|
|
34
|
+
await live.close();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export function shadowNativeDataDir() {
|
|
38
|
+
return join(mkdtempSync(join(tmpdir(), 'tinbase-shadow-')), 'pg');
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=db-diff.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db-diff.js","sourceRoot":"","sources":["../../src/node/db-diff.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AACrC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,aAAa,EAAuB,MAAM,aAAa,CAAA;AAChE,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AAclE,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAAmB;IACrD,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,QAAQ,CAAA;IAEtC,kCAAkC;IAClC,MAAM,MAAM,GAAmB,MAAM,aAAa,CAAC;QACjD,MAAM,EAAE,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,gBAAgB,EAAE,CAAC,CAAC,CAAC,SAAS;QACzE,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,oCAAoC;KACrC,CAAC,CAAA;IACF,iFAAiF;IACjF,8EAA8E;IAC9E,MAAM,IAAI,GAAmB,MAAM,aAAa,CAAC;QAC/C,MAAM,EAAE,IAAI,CAAC,UAAU;QACvB,OAAO,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW;QACvD,UAAU,EAAE,IAAI,CAAC,UAAU;KAC5B,CAAC,CAAA;IAEF,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,MAAM,cAAc,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,CAAA;QAC1D,MAAM,QAAQ,GAAG,MAAM,cAAc,CAAC,IAAI,CAAC,EAAE,EAAE,MAAM,CAAC,CAAA;QACtD,OAAO,WAAW,CAAC,UAAU,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAA;IAClD,CAAC;YAAS,CAAC;QACT,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;QACpB,MAAM,IAAI,CAAC,KAAK,EAAE,CAAA;IACpB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,mBAAmB;IACjC,OAAO,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,iBAAiB,CAAC,CAAC,EAAE,IAAI,CAAC,CAAA;AACnE,CAAC"}
|
package/dist/node/index.d.ts
CHANGED
|
@@ -6,3 +6,5 @@ export { acceptWebSocket, type WsConnection } from './ws.js';
|
|
|
6
6
|
export { createNativeEngine, ensurePostgres, type NativeEngineOptions } from './native/engine.js';
|
|
7
7
|
export { PgWireClient, PgWireError } from './native/wire.js';
|
|
8
8
|
export { loadFunctions } from './load-functions.js';
|
|
9
|
+
export { loadOAuthProviders } from './load-oauth.js';
|
|
10
|
+
export { computeDbDiff, shadowNativeDataDir, type DbDiffOptions } from './db-diff.js';
|
package/dist/node/index.js
CHANGED
|
@@ -6,4 +6,6 @@ export { acceptWebSocket } from './ws.js';
|
|
|
6
6
|
export { createNativeEngine, ensurePostgres } from './native/engine.js';
|
|
7
7
|
export { PgWireClient, PgWireError } from './native/wire.js';
|
|
8
8
|
export { loadFunctions } from './load-functions.js';
|
|
9
|
+
export { loadOAuthProviders } from './load-oauth.js';
|
|
10
|
+
export { computeDbDiff, shadowNativeDataDir } from './db-diff.js';
|
|
9
11
|
//# sourceMappingURL=index.js.map
|
package/dist/node/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/node/index.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,OAAO,EAAE,KAAK,EAAyC,MAAM,aAAa,CAAA;AAC1E,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAChD,OAAO,EAAE,mBAAmB,EAAwB,MAAM,cAAc,CAAA;AACxE,OAAO,EAAE,eAAe,EAAqB,MAAM,SAAS,CAAA;AAC5D,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAA4B,MAAM,oBAAoB,CAAA;AACjG,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/node/index.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,OAAO,EAAE,KAAK,EAAyC,MAAM,aAAa,CAAA;AAC1E,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAChD,OAAO,EAAE,mBAAmB,EAAwB,MAAM,cAAc,CAAA;AACxE,OAAO,EAAE,eAAe,EAAqB,MAAM,SAAS,CAAA;AAC5D,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAA4B,MAAM,oBAAoB,CAAA;AACjG,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AACpD,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAsB,MAAM,cAAc,CAAA"}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { readdir, stat } from 'node:fs/promises';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
import { pathToFileURL } from 'node:url';
|
|
5
|
+
import { installDenoShim, resetCapturedHandler, takeCapturedHandler } from '../functions/deno-shim.js';
|
|
5
6
|
export async function loadFunctions(projectDir) {
|
|
6
7
|
const functions = new Map();
|
|
7
8
|
const root = join(projectDir, 'supabase', 'functions');
|
|
@@ -12,6 +13,8 @@ export async function loadFunctions(projectDir) {
|
|
|
12
13
|
catch {
|
|
13
14
|
return functions;
|
|
14
15
|
}
|
|
16
|
+
// so Supabase-style `Deno.serve(handler)` functions run unchanged
|
|
17
|
+
installDenoShim();
|
|
15
18
|
for (const name of entries) {
|
|
16
19
|
if (name.startsWith('_') || name.startsWith('.'))
|
|
17
20
|
continue;
|
|
@@ -27,12 +30,17 @@ export async function loadFunctions(projectDir) {
|
|
|
27
30
|
continue;
|
|
28
31
|
}
|
|
29
32
|
try {
|
|
33
|
+
resetCapturedHandler();
|
|
30
34
|
const mod = (await import(pathToFileURL(path).href));
|
|
31
|
-
|
|
32
|
-
|
|
35
|
+
// prefer an explicit default export; otherwise use the handler a
|
|
36
|
+
// Deno.serve() call captured during import
|
|
37
|
+
const denoHandler = takeCapturedHandler();
|
|
38
|
+
const handler = typeof mod.default === 'function' ? mod.default : denoHandler ? (req) => denoHandler(req) : undefined;
|
|
39
|
+
if (handler) {
|
|
40
|
+
functions.set(name, handler);
|
|
33
41
|
}
|
|
34
42
|
else {
|
|
35
|
-
console.warn(` warning: function "${name}" has no default export, skipped`);
|
|
43
|
+
console.warn(` warning: function "${name}" has no default export or Deno.serve() handler, skipped`);
|
|
36
44
|
}
|
|
37
45
|
}
|
|
38
46
|
catch (e) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"load-functions.js","sourceRoot":"","sources":["../../src/node/load-functions.ts"],"names":[],"mappings":"AAAA,yFAAyF;AACzF,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAA;AAChD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"load-functions.js","sourceRoot":"","sources":["../../src/node/load-functions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve OAuth providers the way an existing Supabase project already declares
|
|
3
|
+
* them, so no new config is needed:
|
|
4
|
+
*
|
|
5
|
+
* 1. supabase/config.toml [auth.external.<provider>] (enabled/client_id/secret/url/redirect_uri),
|
|
6
|
+
* resolving `env(VAR)` references — this is a real Supabase project's source of truth.
|
|
7
|
+
* 2. GOTRUE_EXTERNAL_<PROVIDER>_CLIENT_ID / _SECRET / _ENABLED (what GoTrue runs on).
|
|
8
|
+
* 3. TINBASE_OAUTH_<PROVIDER>_CLIENT_ID / _SECRET (alias, lowest precedence).
|
|
9
|
+
*/
|
|
10
|
+
import { readFileSync } from 'node:fs';
|
|
11
|
+
import { join } from 'node:path';
|
|
12
|
+
export function loadOAuthProviders(projectDir, env = process.env) {
|
|
13
|
+
const providers = {};
|
|
14
|
+
fromConfigToml(projectDir, env, providers);
|
|
15
|
+
fromGotrueEnv(env, providers);
|
|
16
|
+
fromTinbaseEnv(env, providers);
|
|
17
|
+
return providers;
|
|
18
|
+
}
|
|
19
|
+
/** Resolve a config.toml value: strip quotes, expand `env(VAR)`. */
|
|
20
|
+
function resolveValue(raw, env) {
|
|
21
|
+
let v = raw.trim().replace(/^["']|["']$/g, '');
|
|
22
|
+
const m = v.match(/^env\(\s*"?([A-Za-z0-9_]+)"?\s*\)$/);
|
|
23
|
+
if (m)
|
|
24
|
+
return env[m[1]];
|
|
25
|
+
return v;
|
|
26
|
+
}
|
|
27
|
+
function fromConfigToml(projectDir, env, out) {
|
|
28
|
+
let text;
|
|
29
|
+
try {
|
|
30
|
+
text = readFileSync(join(projectDir, 'supabase', 'config.toml'), 'utf8');
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
// minimal TOML section scan: [auth.external.<provider>] key = value
|
|
36
|
+
const lines = text.split('\n');
|
|
37
|
+
let provider = null;
|
|
38
|
+
let acc = {};
|
|
39
|
+
const flush = () => {
|
|
40
|
+
if (!provider)
|
|
41
|
+
return;
|
|
42
|
+
const enabled = acc.enabled === undefined ? true : acc.enabled === 'true';
|
|
43
|
+
const clientId = acc.client_id;
|
|
44
|
+
const secret = acc.secret;
|
|
45
|
+
if (enabled && clientId && secret) {
|
|
46
|
+
// google/github get endpoints from presets; `url` (custom OIDC) or explicit
|
|
47
|
+
// *_url keys override. Only pass overrides we actually have.
|
|
48
|
+
out[provider] = {
|
|
49
|
+
clientId,
|
|
50
|
+
clientSecret: secret,
|
|
51
|
+
authorizeUrl: acc.authorize_url ?? (acc.url ? `${acc.url}/authorize` : undefined),
|
|
52
|
+
tokenUrl: acc.token_url ?? (acc.url ? `${acc.url}/token` : undefined),
|
|
53
|
+
userInfoUrl: acc.userinfo_url ?? (acc.url ? `${acc.url}/userinfo` : undefined),
|
|
54
|
+
scopes: acc.scopes,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
provider = null;
|
|
58
|
+
acc = {};
|
|
59
|
+
};
|
|
60
|
+
for (const line of lines) {
|
|
61
|
+
const trimmed = line.trim();
|
|
62
|
+
const section = trimmed.match(/^\[auth\.external\.([a-z0-9_]+)\]$/i);
|
|
63
|
+
if (section) {
|
|
64
|
+
flush();
|
|
65
|
+
provider = section[1].toLowerCase();
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (trimmed.startsWith('[')) {
|
|
69
|
+
flush();
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (!provider)
|
|
73
|
+
continue;
|
|
74
|
+
const kv = trimmed.match(/^([a-z_]+)\s*=\s*(.+)$/i);
|
|
75
|
+
if (kv) {
|
|
76
|
+
const val = resolveValue(kv[2], env);
|
|
77
|
+
if (val !== undefined)
|
|
78
|
+
acc[kv[1]] = val;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
flush();
|
|
82
|
+
}
|
|
83
|
+
function fromGotrueEnv(env, out) {
|
|
84
|
+
collectEnv(env, out, 'GOTRUE_EXTERNAL_', {
|
|
85
|
+
id: 'CLIENT_ID',
|
|
86
|
+
secret: 'SECRET',
|
|
87
|
+
enabled: 'ENABLED',
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
function fromTinbaseEnv(env, out) {
|
|
91
|
+
collectEnv(env, out, 'TINBASE_OAUTH_', {
|
|
92
|
+
id: 'CLIENT_ID',
|
|
93
|
+
secret: 'CLIENT_SECRET',
|
|
94
|
+
enabled: 'ENABLED',
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
function collectEnv(env, out, prefix, keys) {
|
|
98
|
+
const names = new Set();
|
|
99
|
+
for (const key of Object.keys(env)) {
|
|
100
|
+
if (key.startsWith(prefix) && key.endsWith(`_${keys.id}`)) {
|
|
101
|
+
names.add(key.slice(prefix.length, -`_${keys.id}`.length));
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
for (const upper of names) {
|
|
105
|
+
const name = upper.toLowerCase();
|
|
106
|
+
if (out[name])
|
|
107
|
+
continue; // higher-precedence source already set it
|
|
108
|
+
const g = (s) => env[`${prefix}${upper}_${s}`];
|
|
109
|
+
const clientId = g(keys.id);
|
|
110
|
+
const clientSecret = g(keys.secret);
|
|
111
|
+
const enabled = g(keys.enabled);
|
|
112
|
+
if (!clientId || !clientSecret)
|
|
113
|
+
continue;
|
|
114
|
+
if (enabled !== undefined && enabled !== 'true')
|
|
115
|
+
continue;
|
|
116
|
+
out[name] = {
|
|
117
|
+
clientId,
|
|
118
|
+
clientSecret,
|
|
119
|
+
authorizeUrl: g('AUTHORIZE_URL'),
|
|
120
|
+
tokenUrl: g('TOKEN_URL'),
|
|
121
|
+
userInfoUrl: g('USERINFO_URL'),
|
|
122
|
+
scopes: g('SCOPES'),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=load-oauth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"load-oauth.js","sourceRoot":"","sources":["../../src/node/load-oauth.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,6 +8,6 @@ export interface NativeEngineOptions {
|
|
|
8
8
|
cacheDir?: string;
|
|
9
9
|
log?: (msg: string) => void;
|
|
10
10
|
}
|
|
11
|
-
/** Download + unpack Postgres binaries if not already cached. Returns the install dir. */
|
|
11
|
+
/** Download + unpack Postgres binaries if not already cached (concurrency-safe). Returns the install dir. */
|
|
12
12
|
export declare function ensurePostgres(version?: string, cacheDir?: string, log?: (m: string) => void): Promise<string>;
|
|
13
13
|
export declare function createNativeEngine(opts: NativeEngineOptions): Promise<DbEngine>;
|
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
* socket directory (0700), never TCP.
|
|
7
7
|
*/
|
|
8
8
|
import { execFileSync, spawn } from 'node:child_process';
|
|
9
|
-
import {
|
|
9
|
+
import { randomBytes } from 'node:crypto';
|
|
10
|
+
import { chmodSync, existsSync, mkdirSync, mkdtempSync, readFileSync, renameSync, rmSync, appendFileSync } from 'node:fs';
|
|
10
11
|
import { writeFile } from 'node:fs/promises';
|
|
11
12
|
import { homedir, tmpdir } from 'node:os';
|
|
12
13
|
import { join } from 'node:path';
|
|
@@ -23,25 +24,55 @@ function target() {
|
|
|
23
24
|
return `${arch}-unknown-linux-gnu`;
|
|
24
25
|
throw new Error(`unsupported platform for native engine: ${process.platform} (use the default PGlite engine)`);
|
|
25
26
|
}
|
|
26
|
-
/**
|
|
27
|
+
/** A binary install is only usable if both the server and its catalog seed are present. */
|
|
28
|
+
function isCompleteInstall(dir) {
|
|
29
|
+
return existsSync(join(dir, 'bin', 'postgres')) && existsSync(join(dir, 'share', 'postgres.bki'));
|
|
30
|
+
}
|
|
31
|
+
/** Download + unpack Postgres binaries if not already cached (concurrency-safe). Returns the install dir. */
|
|
27
32
|
export async function ensurePostgres(version = DEFAULT_PG_VERSION, cacheDir, log) {
|
|
28
33
|
const t = target();
|
|
29
34
|
const root = cacheDir ?? join(homedir(), '.cache', 'tinbase');
|
|
30
35
|
const dir = join(root, `postgresql-${version}-${t}`);
|
|
31
|
-
if (
|
|
36
|
+
if (isCompleteInstall(dir))
|
|
32
37
|
return dir;
|
|
38
|
+
// Concurrency-safe: multiple test workers / processes may call this at once on
|
|
39
|
+
// a cold cache. Each downloads + extracts to unique temp paths, then atomically
|
|
40
|
+
// renames into place — so no worker ever sees a half-written tarball or a
|
|
41
|
+
// partially-extracted install dir.
|
|
33
42
|
const url = `https://github.com/theseus-rs/postgresql-binaries/releases/download/${version}/postgresql-${version}-${t}.tar.gz`;
|
|
34
|
-
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
mkdirSync(root, { recursive: true });
|
|
44
|
+
const uniq = `${process.pid}-${randomBytes(6).toString('hex')}`;
|
|
45
|
+
const tarball = join(root, `pg-${version}-${uniq}.tar.gz`);
|
|
46
|
+
const tmpDir = join(root, `.tmp-${version}-${t}-${uniq}`);
|
|
47
|
+
try {
|
|
48
|
+
if (isCompleteInstall(dir))
|
|
49
|
+
return dir; // another worker finished while we started
|
|
50
|
+
log?.(`downloading postgres ${version} (${t})…`);
|
|
51
|
+
const res = await fetch(url);
|
|
52
|
+
if (!res.ok)
|
|
53
|
+
throw new Error(`failed to download ${url}: HTTP ${res.status}`);
|
|
54
|
+
await writeFile(tarball, Buffer.from(await res.arrayBuffer()));
|
|
55
|
+
mkdirSync(tmpDir, { recursive: true });
|
|
56
|
+
execFileSync('tar', ['xzf', tarball, '-C', tmpDir, '--strip-components=1']);
|
|
57
|
+
if (!isCompleteInstall(tmpDir))
|
|
58
|
+
throw new Error('postgres archive extracted incompletely');
|
|
59
|
+
// publish atomically; if another worker already did (or a stale dir exists), reconcile
|
|
60
|
+
try {
|
|
61
|
+
renameSync(tmpDir, dir);
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
if (!isCompleteInstall(dir)) {
|
|
65
|
+
rmSync(dir, { recursive: true, force: true });
|
|
66
|
+
renameSync(tmpDir, dir);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
log?.(`postgres installed to ${dir}`);
|
|
70
|
+
return dir;
|
|
71
|
+
}
|
|
72
|
+
finally {
|
|
73
|
+
rmSync(tarball, { force: true });
|
|
74
|
+
rmSync(tmpDir, { recursive: true, force: true });
|
|
75
|
+
}
|
|
45
76
|
}
|
|
46
77
|
const TUNED_CONF = `
|
|
47
78
|
# tinbase: memory-lean settings for an embedded, single-app Postgres
|
|
@@ -60,19 +91,33 @@ export async function createNativeEngine(opts) {
|
|
|
60
91
|
// initdb on first boot
|
|
61
92
|
if (!existsSync(join(opts.dataDir, 'PG_VERSION'))) {
|
|
62
93
|
mkdirSync(opts.dataDir, { recursive: true });
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
94
|
+
try {
|
|
95
|
+
execFileSync(bin('initdb'), ['-U', 'postgres', '-A', 'trust', '-E', 'UTF8', '-D', opts.dataDir], {
|
|
96
|
+
stdio: 'pipe',
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
catch (e) {
|
|
100
|
+
const stderr = e.stderr?.toString() ?? '';
|
|
101
|
+
throw new Error(`initdb failed:\n${stderr || e.message}`);
|
|
102
|
+
}
|
|
66
103
|
appendFileSync(join(opts.dataDir, 'postgresql.conf'), TUNED_CONF);
|
|
67
104
|
}
|
|
68
|
-
//
|
|
69
|
-
|
|
105
|
+
// A stale postmaster.pid (from a crashed run) makes postgres refuse to start.
|
|
106
|
+
// Remove it if the process it names is no longer alive.
|
|
107
|
+
removeStalePidFile(join(opts.dataDir, 'postmaster.pid'));
|
|
108
|
+
// private socket dir — trust auth is safe because only this user can reach it.
|
|
109
|
+
// Keep the path short: macOS caps unix socket paths at ~104 chars.
|
|
110
|
+
const sockDir = mkdtempSync(join(tmpdir(), 'tb-'));
|
|
70
111
|
chmodSync(sockDir, 0o700);
|
|
71
112
|
const child = spawn(bin('postgres'), ['-D', opts.dataDir, '-k', sockDir], {
|
|
72
|
-
stdio: 'ignore',
|
|
113
|
+
stdio: ['ignore', 'ignore', 'pipe'],
|
|
73
114
|
detached: false,
|
|
74
115
|
});
|
|
75
116
|
let childExited = false;
|
|
117
|
+
let stderr = '';
|
|
118
|
+
child.stderr?.on('data', (d) => {
|
|
119
|
+
stderr = (stderr + d.toString()).slice(-4000);
|
|
120
|
+
});
|
|
76
121
|
child.on('exit', () => (childExited = true));
|
|
77
122
|
const socketPath = join(sockDir, '.s.PGSQL.5432');
|
|
78
123
|
const connect = async () => {
|
|
@@ -82,8 +127,12 @@ export async function createNativeEngine(opts) {
|
|
|
82
127
|
return await PgWireClient.connect({ socketPath, user: 'postgres', database: 'postgres' });
|
|
83
128
|
}
|
|
84
129
|
catch (e) {
|
|
85
|
-
if (childExited)
|
|
86
|
-
|
|
130
|
+
if (childExited) {
|
|
131
|
+
const detail = stderr.trim();
|
|
132
|
+
throw new Error(`embedded postgres failed to start${detail ? `:\n${detail}` : ' (no output)'}\n\n` +
|
|
133
|
+
`data dir: ${opts.dataDir}\n` +
|
|
134
|
+
`If a previous run is still holding it, stop it; or delete the data dir to start fresh.`);
|
|
135
|
+
}
|
|
87
136
|
if (Date.now() > deadline)
|
|
88
137
|
throw e;
|
|
89
138
|
await new Promise((r) => setTimeout(r, 150));
|
|
@@ -156,6 +205,33 @@ export async function createNativeEngine(opts) {
|
|
|
156
205
|
},
|
|
157
206
|
};
|
|
158
207
|
}
|
|
208
|
+
/**
|
|
209
|
+
* Postgres refuses to boot if a postmaster.pid names a live process. When the
|
|
210
|
+
* previous run crashed the pid is stale; postgres usually clears it, but if the
|
|
211
|
+
* data dir moved or the boot ID differs it may not — remove it when the named
|
|
212
|
+
* pid is dead so a fresh start succeeds.
|
|
213
|
+
*/
|
|
214
|
+
function removeStalePidFile(pidPath) {
|
|
215
|
+
if (!existsSync(pidPath))
|
|
216
|
+
return;
|
|
217
|
+
try {
|
|
218
|
+
const pid = parseInt(readFileSync(pidPath, 'utf8').split('\n')[0]?.trim() ?? '', 10);
|
|
219
|
+
if (!pid) {
|
|
220
|
+
rmSync(pidPath, { force: true });
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
try {
|
|
224
|
+
process.kill(pid, 0); // throws if the process does not exist
|
|
225
|
+
// process is alive — leave the pid file; postgres will report the conflict
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
rmSync(pidPath, { force: true });
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
catch {
|
|
232
|
+
// unreadable pid file — let postgres decide
|
|
233
|
+
}
|
|
234
|
+
}
|
|
159
235
|
/** Match PGlite's param serialization: arrays → pg literals, objects → JSON. */
|
|
160
236
|
function normalizeParams(params) {
|
|
161
237
|
return params?.map((p) => {
|