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,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory ring buffer of recent server log lines, surfaced in the Studio
|
|
3
|
+
* "Logs" pane (and available as backend.logs). Captures request lines and the
|
|
4
|
+
* internal log() output (migrations, mail, webhooks, cron). Dev convenience —
|
|
5
|
+
* bounded, never persisted.
|
|
6
|
+
*/
|
|
7
|
+
export type LogLevel = 'info' | 'warn' | 'error';
|
|
8
|
+
export interface LogEntry {
|
|
9
|
+
ts: string;
|
|
10
|
+
level: LogLevel;
|
|
11
|
+
msg: string;
|
|
12
|
+
}
|
|
13
|
+
export declare class LogBuffer {
|
|
14
|
+
private cap;
|
|
15
|
+
private entries;
|
|
16
|
+
constructor(cap?: number);
|
|
17
|
+
push(msg: string, level?: LogLevel): void;
|
|
18
|
+
/** Most recent first. */
|
|
19
|
+
list(limit?: number): LogEntry[];
|
|
20
|
+
clear(): void;
|
|
21
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export class LogBuffer {
|
|
2
|
+
cap;
|
|
3
|
+
entries = [];
|
|
4
|
+
constructor(cap = 500) {
|
|
5
|
+
this.cap = cap;
|
|
6
|
+
}
|
|
7
|
+
push(msg, level = 'info') {
|
|
8
|
+
this.entries.push({ ts: new Date().toISOString(), level, msg });
|
|
9
|
+
if (this.entries.length > this.cap)
|
|
10
|
+
this.entries.splice(0, this.entries.length - this.cap);
|
|
11
|
+
}
|
|
12
|
+
/** Most recent first. */
|
|
13
|
+
list(limit = 200) {
|
|
14
|
+
return this.entries.slice(-limit).reverse();
|
|
15
|
+
}
|
|
16
|
+
clear() {
|
|
17
|
+
this.entries = [];
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=log-buffer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log-buffer.js","sourceRoot":"","sources":["../src/log-buffer.ts"],"names":[],"mappings":"AAcA,MAAM,OAAO,SAAS;IAEA;IADZ,OAAO,GAAe,EAAE,CAAA;IAChC,YAAoB,MAAM,GAAG;QAAT,QAAG,GAAH,GAAG,CAAM;IAAG,CAAC;IAEjC,IAAI,CAAC,GAAW,EAAE,QAAkB,MAAM;QACxC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAA;QAC/D,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG;YAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAA;IAC5F,CAAC;IAED,yBAAyB;IACzB,IAAI,CAAC,KAAK,GAAG,GAAG;QACd,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,CAAA;IAC7C,CAAC;IAED,KAAK;QACH,IAAI,CAAC,OAAO,GAAG,EAAE,CAAA;IACnB,CAAC;CACF"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Map a Deno-style specifier to a fetchable URL. Pure — unit tested. */
|
|
2
|
+
export declare function rewriteRemoteSpecifier(spec: string): string;
|
|
3
|
+
/**
|
|
4
|
+
* Bundle `entryPath` to a single ESM file and return its path (written under
|
|
5
|
+
* the OS temp dir). Throws if esbuild is not installed.
|
|
6
|
+
*/
|
|
7
|
+
export declare function bundleFunction(entryPath: string, name: string): Promise<string>;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bundle an edge function with esbuild so it runs under Node like it would on
|
|
3
|
+
* Supabase's Deno edge runtime. This resolves what a plain `import()` can't:
|
|
4
|
+
* - TypeScript and multi-file functions (relative imports)
|
|
5
|
+
* - `npm:` / `jsr:` specifiers → rewritten to esm.sh and fetched
|
|
6
|
+
* - `https://` URL imports → fetched (and their transitive deps)
|
|
7
|
+
*
|
|
8
|
+
* Remote modules are fetched at bundle time (first run) and disk-cached, the
|
|
9
|
+
* same trade Deno makes. esbuild is an optional dependency; when it's missing
|
|
10
|
+
* the loader falls back to a plain import (Web-API/Deno.serve functions still
|
|
11
|
+
* work, just not remote/TS imports).
|
|
12
|
+
*/
|
|
13
|
+
import { createHash } from 'node:crypto';
|
|
14
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
15
|
+
import { existsSync } from 'node:fs';
|
|
16
|
+
import { tmpdir } from 'node:os';
|
|
17
|
+
import { join } from 'node:path';
|
|
18
|
+
/** Map a Deno-style specifier to a fetchable URL. Pure — unit tested. */
|
|
19
|
+
export function rewriteRemoteSpecifier(spec) {
|
|
20
|
+
if (spec.startsWith('npm:'))
|
|
21
|
+
return `https://esm.sh/${spec.slice(4)}`;
|
|
22
|
+
if (spec.startsWith('jsr:'))
|
|
23
|
+
return `https://esm.sh/jsr/${spec.slice(4)}`;
|
|
24
|
+
return spec; // already an http(s) URL
|
|
25
|
+
}
|
|
26
|
+
const HTTP_CACHE = join(tmpdir(), 'tinbase-fn-http');
|
|
27
|
+
async function fetchModule(url) {
|
|
28
|
+
const key = createHash('sha256').update(url).digest('hex');
|
|
29
|
+
const cached = join(HTTP_CACHE, key);
|
|
30
|
+
if (existsSync(cached))
|
|
31
|
+
return readFile(cached, 'utf8');
|
|
32
|
+
const res = await fetch(url, { redirect: 'follow' });
|
|
33
|
+
if (!res.ok)
|
|
34
|
+
throw new Error(`failed to fetch ${url}: HTTP ${res.status}`);
|
|
35
|
+
const body = await res.text();
|
|
36
|
+
await mkdir(HTTP_CACHE, { recursive: true });
|
|
37
|
+
await writeFile(cached, body);
|
|
38
|
+
return body;
|
|
39
|
+
}
|
|
40
|
+
/** esbuild plugin: resolve npm:/jsr:/https imports by fetching from esm.sh. */
|
|
41
|
+
function remotePlugin() {
|
|
42
|
+
return {
|
|
43
|
+
name: 'tinbase-remote',
|
|
44
|
+
setup(build) {
|
|
45
|
+
build.onResolve({ filter: /^(npm:|jsr:|https?:\/\/)/ }, (args) => ({
|
|
46
|
+
path: rewriteRemoteSpecifier(args.path),
|
|
47
|
+
namespace: 'http-url',
|
|
48
|
+
}));
|
|
49
|
+
// imports *inside* a fetched module resolve relative to its URL
|
|
50
|
+
build.onResolve({ filter: /.*/, namespace: 'http-url' }, (args) => ({
|
|
51
|
+
path: new URL(args.path, args.importer).href,
|
|
52
|
+
namespace: 'http-url',
|
|
53
|
+
}));
|
|
54
|
+
build.onLoad({ filter: /.*/, namespace: 'http-url' }, async (args) => ({
|
|
55
|
+
contents: await fetchModule(args.path),
|
|
56
|
+
loader: 'js',
|
|
57
|
+
}));
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Bundle `entryPath` to a single ESM file and return its path (written under
|
|
63
|
+
* the OS temp dir). Throws if esbuild is not installed.
|
|
64
|
+
*/
|
|
65
|
+
export async function bundleFunction(entryPath, name) {
|
|
66
|
+
let esbuild;
|
|
67
|
+
try {
|
|
68
|
+
esbuild = await import('esbuild');
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
throw new Error('esbuild-not-available');
|
|
72
|
+
}
|
|
73
|
+
const outDir = join(tmpdir(), 'tinbase-fn-bundle');
|
|
74
|
+
await mkdir(outDir, { recursive: true });
|
|
75
|
+
const result = await esbuild.build({
|
|
76
|
+
entryPoints: [entryPath],
|
|
77
|
+
bundle: true,
|
|
78
|
+
format: 'esm',
|
|
79
|
+
platform: 'node',
|
|
80
|
+
target: 'node18',
|
|
81
|
+
write: false,
|
|
82
|
+
logLevel: 'silent',
|
|
83
|
+
plugins: [remotePlugin()],
|
|
84
|
+
});
|
|
85
|
+
const code = result.outputFiles[0].text;
|
|
86
|
+
const out = join(outDir, `${name}-${createHash('sha256').update(entryPath).digest('hex').slice(0, 8)}.mjs`);
|
|
87
|
+
await writeFile(out, code);
|
|
88
|
+
return out;
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=bundle-function.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bundle-function.js","sourceRoot":"","sources":["../../src/node/bundle-function.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
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;
|
|
14
|
+
export interface DbPullOptions extends DbDiffOptions {
|
|
15
|
+
/** directory to write the migration into (usually supabase/migrations); omit to skip writing */
|
|
16
|
+
migrationsDir?: string;
|
|
17
|
+
/** migration name suffix (default 'remote_schema') */
|
|
18
|
+
name?: string;
|
|
19
|
+
/** timestamp version prefix; pass for determinism (default: now as YYYYMMDDHHMMSS) */
|
|
20
|
+
stamp?: string;
|
|
21
|
+
}
|
|
22
|
+
export interface DbPullResult {
|
|
23
|
+
ddl: string[];
|
|
24
|
+
version: string | null;
|
|
25
|
+
path: string | null;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* `tinbase db pull` core: like `db diff`, but writes the delta as a migration
|
|
29
|
+
* AND records it as already-applied on the live database — the schema is
|
|
30
|
+
* already there, so a subsequent `tinbase start` must not re-run it. This is
|
|
31
|
+
* how you bring an out-of-migration (or externally-created) schema under
|
|
32
|
+
* version control.
|
|
33
|
+
*/
|
|
34
|
+
export declare function pullSchema(opts: DbPullOptions): Promise<DbPullResult>;
|
|
@@ -0,0 +1,82 @@
|
|
|
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 { mkdir, writeFile } from 'node:fs/promises';
|
|
9
|
+
import { tmpdir } from 'node:os';
|
|
10
|
+
import { join } from 'node:path';
|
|
11
|
+
import { createBackend } from '../index.js';
|
|
12
|
+
import { snapshotSchema, diffSchemas } from '../db/schema-diff.js';
|
|
13
|
+
export async function computeDbDiff(opts) {
|
|
14
|
+
const schema = opts.schema ?? 'public';
|
|
15
|
+
// shadow = migrations only, fresh
|
|
16
|
+
const shadow = await createBackend({
|
|
17
|
+
engine: opts.makeShadowEngine ? await opts.makeShadowEngine() : undefined,
|
|
18
|
+
migrations: opts.migrations,
|
|
19
|
+
// no seed: seed is data, not schema
|
|
20
|
+
});
|
|
21
|
+
// live = current project db (createBackend only applies *pending* migrations, so
|
|
22
|
+
// this reflects the actual current schema including out-of-migration changes)
|
|
23
|
+
const live = await createBackend({
|
|
24
|
+
engine: opts.liveEngine,
|
|
25
|
+
dataDir: opts.liveEngine ? undefined : opts.liveDataDir,
|
|
26
|
+
migrations: opts.migrations,
|
|
27
|
+
});
|
|
28
|
+
try {
|
|
29
|
+
const shadowSnap = await snapshotSchema(shadow.db, schema);
|
|
30
|
+
const liveSnap = await snapshotSchema(live.db, schema);
|
|
31
|
+
return diffSchemas(shadowSnap, liveSnap, schema);
|
|
32
|
+
}
|
|
33
|
+
finally {
|
|
34
|
+
await shadow.close();
|
|
35
|
+
await live.close();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export function shadowNativeDataDir() {
|
|
39
|
+
return join(mkdtempSync(join(tmpdir(), 'tinbase-shadow-')), 'pg');
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* `tinbase db pull` core: like `db diff`, but writes the delta as a migration
|
|
43
|
+
* AND records it as already-applied on the live database — the schema is
|
|
44
|
+
* already there, so a subsequent `tinbase start` must not re-run it. This is
|
|
45
|
+
* how you bring an out-of-migration (or externally-created) schema under
|
|
46
|
+
* version control.
|
|
47
|
+
*/
|
|
48
|
+
export async function pullSchema(opts) {
|
|
49
|
+
const schema = opts.schema ?? 'public';
|
|
50
|
+
const shadow = await createBackend({
|
|
51
|
+
engine: opts.makeShadowEngine ? await opts.makeShadowEngine() : undefined,
|
|
52
|
+
migrations: opts.migrations,
|
|
53
|
+
});
|
|
54
|
+
const live = await createBackend({
|
|
55
|
+
engine: opts.liveEngine,
|
|
56
|
+
dataDir: opts.liveEngine ? undefined : opts.liveDataDir,
|
|
57
|
+
migrations: opts.migrations,
|
|
58
|
+
});
|
|
59
|
+
try {
|
|
60
|
+
const ddl = diffSchemas(await snapshotSchema(shadow.db, schema), await snapshotSchema(live.db, schema), schema);
|
|
61
|
+
if (ddl.length === 0)
|
|
62
|
+
return { ddl, version: null, path: null };
|
|
63
|
+
const stamp = opts.stamp ?? new Date().toISOString().replace(/[-:T]/g, '').slice(0, 14);
|
|
64
|
+
const name = opts.name ?? 'remote_schema';
|
|
65
|
+
const body = ddl.join('\n\n') + '\n';
|
|
66
|
+
let path = null;
|
|
67
|
+
if (opts.migrationsDir) {
|
|
68
|
+
await mkdir(opts.migrationsDir, { recursive: true });
|
|
69
|
+
path = join(opts.migrationsDir, `${stamp}_${name}.sql`);
|
|
70
|
+
await writeFile(path, body);
|
|
71
|
+
}
|
|
72
|
+
// record as already applied on the live DB so `start` won't re-run it
|
|
73
|
+
await live.db.query(`insert into supabase_migrations.schema_migrations (version, name, statements)
|
|
74
|
+
values ($1, $2, $3) on conflict (version) do nothing`, [stamp, `${stamp}_${name}`, [body]]);
|
|
75
|
+
return { ddl, version: stamp, path };
|
|
76
|
+
}
|
|
77
|
+
finally {
|
|
78
|
+
await shadow.close();
|
|
79
|
+
await live.close();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=db-diff.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db-diff.js","sourceRoot":"","sources":["../../src/node/db-diff.ts"],"names":[],"mappings":"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"}
|
package/dist/node/index.d.ts
CHANGED
|
@@ -5,4 +5,7 @@ export { loadSupabaseProject, type SupabaseProject } from './project.js';
|
|
|
5
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
|
-
export { loadFunctions } from './load-functions.js';
|
|
8
|
+
export { loadFunctions, loadFunctionEnv } from './load-functions.js';
|
|
9
|
+
export { bundleFunction, rewriteRemoteSpecifier } from './bundle-function.js';
|
|
10
|
+
export { loadOAuthProviders } from './load-oauth.js';
|
|
11
|
+
export { computeDbDiff, pullSchema, shadowNativeDataDir, type DbDiffOptions, type DbPullOptions, type DbPullResult } from './db-diff.js';
|
package/dist/node/index.js
CHANGED
|
@@ -5,5 +5,8 @@ export { loadSupabaseProject } from './project.js';
|
|
|
5
5
|
export { acceptWebSocket } from './ws.js';
|
|
6
6
|
export { createNativeEngine, ensurePostgres } from './native/engine.js';
|
|
7
7
|
export { PgWireClient, PgWireError } from './native/wire.js';
|
|
8
|
-
export { loadFunctions } from './load-functions.js';
|
|
8
|
+
export { loadFunctions, loadFunctionEnv } from './load-functions.js';
|
|
9
|
+
export { bundleFunction, rewriteRemoteSpecifier } from './bundle-function.js';
|
|
10
|
+
export { loadOAuthProviders } from './load-oauth.js';
|
|
11
|
+
export { computeDbDiff, pullSchema, shadowNativeDataDir } from './db-diff.js';
|
|
9
12
|
//# 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,eAAe,EAAE,MAAM,qBAAqB,CAAA;AACpE,OAAO,EAAE,cAAc,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAA;AAC7E,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AACpD,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,mBAAmB,EAA6D,MAAM,cAAc,CAAA"}
|
|
@@ -1,2 +1,8 @@
|
|
|
1
1
|
import type { EdgeFunction } from '../functions/handler.js';
|
|
2
|
+
/**
|
|
3
|
+
* Load edge-function secrets from supabase/functions/.env (KEY=VALUE lines,
|
|
4
|
+
* `#` comments). These are exposed to functions via Deno.env and ctx.env —
|
|
5
|
+
* the local equivalent of `supabase functions serve --env-file`.
|
|
6
|
+
*/
|
|
7
|
+
export declare function loadFunctionEnv(projectDir: string): Promise<Record<string, string>>;
|
|
2
8
|
export declare function loadFunctions(projectDir: string): Promise<Map<string, EdgeFunction>>;
|
|
@@ -1,7 +1,40 @@
|
|
|
1
1
|
/** Loads edge functions from supabase/functions/<name>/index.{ts,js,mjs} (Node only). */
|
|
2
|
-
import { readdir, stat } from 'node:fs/promises';
|
|
2
|
+
import { readdir, readFile, 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';
|
|
6
|
+
import { bundleFunction } from './bundle-function.js';
|
|
7
|
+
/**
|
|
8
|
+
* Load edge-function secrets from supabase/functions/.env (KEY=VALUE lines,
|
|
9
|
+
* `#` comments). These are exposed to functions via Deno.env and ctx.env —
|
|
10
|
+
* the local equivalent of `supabase functions serve --env-file`.
|
|
11
|
+
*/
|
|
12
|
+
export async function loadFunctionEnv(projectDir) {
|
|
13
|
+
let text;
|
|
14
|
+
try {
|
|
15
|
+
text = await readFile(join(projectDir, 'supabase', 'functions', '.env'), 'utf8');
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return {};
|
|
19
|
+
}
|
|
20
|
+
const env = {};
|
|
21
|
+
for (const raw of text.split('\n')) {
|
|
22
|
+
const line = raw.trim();
|
|
23
|
+
if (!line || line.startsWith('#'))
|
|
24
|
+
continue;
|
|
25
|
+
const eq = line.indexOf('=');
|
|
26
|
+
if (eq === -1)
|
|
27
|
+
continue;
|
|
28
|
+
const key = line.slice(0, eq).trim();
|
|
29
|
+
let val = line.slice(eq + 1).trim();
|
|
30
|
+
if ((val.startsWith('"') && val.endsWith('"')) || (val.startsWith("'") && val.endsWith("'"))) {
|
|
31
|
+
val = val.slice(1, -1);
|
|
32
|
+
}
|
|
33
|
+
if (key)
|
|
34
|
+
env[key] = val;
|
|
35
|
+
}
|
|
36
|
+
return env;
|
|
37
|
+
}
|
|
5
38
|
export async function loadFunctions(projectDir) {
|
|
6
39
|
const functions = new Map();
|
|
7
40
|
const root = join(projectDir, 'supabase', 'functions');
|
|
@@ -12,13 +45,15 @@ export async function loadFunctions(projectDir) {
|
|
|
12
45
|
catch {
|
|
13
46
|
return functions;
|
|
14
47
|
}
|
|
48
|
+
// so Supabase-style `Deno.serve(handler)` functions run unchanged
|
|
49
|
+
installDenoShim();
|
|
15
50
|
for (const name of entries) {
|
|
16
51
|
if (name.startsWith('_') || name.startsWith('.'))
|
|
17
52
|
continue;
|
|
18
53
|
const dir = join(root, name);
|
|
19
54
|
if (!(await stat(dir)).isDirectory())
|
|
20
55
|
continue;
|
|
21
|
-
for (const file of ['index.ts', 'index.js', 'index.mjs']) {
|
|
56
|
+
for (const file of ['index.ts', 'index.tsx', 'index.js', 'index.mjs']) {
|
|
22
57
|
const path = join(dir, file);
|
|
23
58
|
try {
|
|
24
59
|
await stat(path);
|
|
@@ -27,18 +62,35 @@ export async function loadFunctions(projectDir) {
|
|
|
27
62
|
continue;
|
|
28
63
|
}
|
|
29
64
|
try {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
65
|
+
// Bundle with esbuild so TS, relative imports, and npm:/jsr:/URL
|
|
66
|
+
// specifiers resolve. If esbuild isn't installed, import the file
|
|
67
|
+
// directly (Web-API / Deno.serve functions still work).
|
|
68
|
+
let importUrl;
|
|
69
|
+
try {
|
|
70
|
+
importUrl = pathToFileURL(await bundleFunction(path, name)).href;
|
|
71
|
+
}
|
|
72
|
+
catch (e) {
|
|
73
|
+
if (e.message !== 'esbuild-not-available')
|
|
74
|
+
throw e;
|
|
75
|
+
importUrl = pathToFileURL(path).href;
|
|
76
|
+
}
|
|
77
|
+
resetCapturedHandler();
|
|
78
|
+
const mod = (await import(importUrl));
|
|
79
|
+
// prefer an explicit default export; otherwise use the handler a
|
|
80
|
+
// Deno.serve() call captured during import
|
|
81
|
+
const denoHandler = takeCapturedHandler();
|
|
82
|
+
const handler = typeof mod.default === 'function' ? mod.default : denoHandler ? (req) => denoHandler(req) : undefined;
|
|
83
|
+
if (handler) {
|
|
84
|
+
functions.set(name, handler);
|
|
33
85
|
}
|
|
34
86
|
else {
|
|
35
|
-
console.warn(` warning: function "${name}" has no default export, skipped`);
|
|
87
|
+
console.warn(` warning: function "${name}" has no default export or Deno.serve() handler, skipped`);
|
|
36
88
|
}
|
|
37
89
|
}
|
|
38
90
|
catch (e) {
|
|
39
91
|
const msg = e instanceof Error ? e.message : String(e);
|
|
40
|
-
if (
|
|
41
|
-
console.warn(` warning: function "${name}"
|
|
92
|
+
if (/Unknown file extension|Cannot find module/.test(msg)) {
|
|
93
|
+
console.warn(` warning: function "${name}" needs bundling — install esbuild, or run tinbase with tsx/bun`);
|
|
42
94
|
}
|
|
43
95
|
else {
|
|
44
96
|
console.warn(` warning: failed to load function "${name}": ${msg}`);
|
|
@@ -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;
|
|
1
|
+
{"version":3,"file":"load-functions.js","sourceRoot":"","sources":["../../src/node/load-functions.ts"],"names":[],"mappings":"AAAA,yFAAyF;AACzF,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAA;AAC1D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAExC,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAA;AACtG,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAErD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,UAAkB;IACtD,IAAI,IAAY,CAAA;IAChB,IAAI,CAAC;QACH,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,CAAA;IAClF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAA;IACX,CAAC;IACD,MAAM,GAAG,GAA2B,EAAE,CAAA;IACtC,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,EAAE,CAAA;QACvB,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAQ;QAC3C,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;QAC5B,IAAI,EAAE,KAAK,CAAC,CAAC;YAAE,SAAQ;QACvB,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;QACpC,IAAI,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;QACnC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;YAC7F,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;QACxB,CAAC;QACD,IAAI,GAAG;YAAE,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,CAAA;IACzB,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,UAAkB;IACpD,MAAM,SAAS,GAAG,IAAI,GAAG,EAAwB,CAAA;IACjD,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,EAAE,UAAU,EAAE,WAAW,CAAC,CAAA;IAEtD,IAAI,OAAO,GAAa,EAAE,CAAA;IAC1B,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAA;IAClB,CAAC;IAED,kEAAkE;IAClE,eAAe,EAAE,CAAA;IAEjB,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;QAC3B,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAQ;QAC1D,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;QAC5B,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,WAAW,EAAE;YAAE,SAAQ;QAC9C,KAAK,MAAM,IAAI,IAAI,CAAC,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,CAAC,EAAE,CAAC;YACtE,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;YAC5B,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,IAAI,CAAC,CAAA;YAClB,CAAC;YAAC,MAAM,CAAC;gBACP,SAAQ;YACV,CAAC;YACD,IAAI,CAAC;gBACH,iEAAiE;gBACjE,kEAAkE;gBAClE,wDAAwD;gBACxD,IAAI,SAAiB,CAAA;gBACrB,IAAI,CAAC;oBACH,SAAS,GAAG,aAAa,CAAC,MAAM,cAAc,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAA;gBAClE,CAAC;gBAAC,OAAO,CAAC,EAAE,CAAC;oBACX,IAAK,CAAW,CAAC,OAAO,KAAK,uBAAuB;wBAAE,MAAM,CAAC,CAAA;oBAC7D,SAAS,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC,IAAI,CAAA;gBACtC,CAAC;gBACD,oBAAoB,EAAE,CAAA;gBACtB,MAAM,GAAG,GAAG,CAAC,MAAM,MAAM,CAAC,SAAS,CAAC,CAA+B,CAAA;gBACnE,iEAAiE;gBACjE,2CAA2C;gBAC3C,MAAM,WAAW,GAAG,mBAAmB,EAAE,CAAA;gBACzC,MAAM,OAAO,GAAG,OAAO,GAAG,CAAC,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,GAAY,EAAE,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;gBAC9H,IAAI,OAAO,EAAE,CAAC;oBACZ,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,OAAuB,CAAC,CAAA;gBAC9C,CAAC;qBAAM,CAAC;oBACN,OAAO,CAAC,IAAI,CAAC,wBAAwB,IAAI,0DAA0D,CAAC,CAAA;gBACtG,CAAC;YACH,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,MAAM,GAAG,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;gBACtD,IAAI,2CAA2C,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;oBAC1D,OAAO,CAAC,IAAI,CAAC,wBAAwB,IAAI,iEAAiE,CAAC,CAAA;gBAC7G,CAAC;qBAAM,CAAC;oBACN,OAAO,CAAC,IAAI,CAAC,uCAAuC,IAAI,MAAM,GAAG,EAAE,CAAC,CAAA;gBACtE,CAAC;YACH,CAAC;YACD,MAAK;QACP,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC"}
|
|
@@ -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>;
|