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.
Files changed (94) hide show
  1. package/README.md +64 -31
  2. package/dist/admin/api.d.ts +8 -1
  3. package/dist/admin/api.js +94 -1
  4. package/dist/admin/api.js.map +1 -1
  5. package/dist/admin/ui.d.ts +1 -1
  6. package/dist/admin/ui.js +16 -16
  7. package/dist/auth/handler.d.ts +13 -1
  8. package/dist/auth/handler.js +173 -6
  9. package/dist/auth/handler.js.map +1 -1
  10. package/dist/auth/inbox.d.ts +31 -0
  11. package/dist/auth/inbox.js +112 -0
  12. package/dist/auth/inbox.js.map +1 -0
  13. package/dist/auth/oauth.d.ts +59 -0
  14. package/dist/auth/oauth.js +209 -0
  15. package/dist/auth/oauth.js.map +1 -0
  16. package/dist/auth/qr.d.ts +8 -0
  17. package/dist/auth/qr.js +376 -0
  18. package/dist/auth/qr.js.map +1 -0
  19. package/dist/auth/totp.d.ts +19 -0
  20. package/dist/auth/totp.js +101 -0
  21. package/dist/auth/totp.js.map +1 -0
  22. package/dist/cli.js +168 -10
  23. package/dist/cli.js.map +1 -1
  24. package/dist/cron/service.d.ts +29 -0
  25. package/dist/cron/service.js +134 -0
  26. package/dist/cron/service.js.map +1 -0
  27. package/dist/db/bootstrap.d.ts +8 -1
  28. package/dist/db/bootstrap.js +240 -1
  29. package/dist/db/bootstrap.js.map +1 -1
  30. package/dist/db/database.d.ts +24 -0
  31. package/dist/db/database.js +61 -2
  32. package/dist/db/database.js.map +1 -1
  33. package/dist/db/emulated.d.ts +12 -0
  34. package/dist/db/emulated.js +156 -0
  35. package/dist/db/emulated.js.map +1 -0
  36. package/dist/db/engine.d.ts +2 -0
  37. package/dist/db/engine.js.map +1 -1
  38. package/dist/db/inspect.d.ts +13 -0
  39. package/dist/db/inspect.js +22 -0
  40. package/dist/db/inspect.js.map +1 -0
  41. package/dist/db/pgmem-engine.d.ts +22 -0
  42. package/dist/db/pgmem-engine.js +135 -0
  43. package/dist/db/pgmem-engine.js.map +1 -0
  44. package/dist/db/schema-diff.d.ts +23 -0
  45. package/dist/db/schema-diff.js +182 -0
  46. package/dist/db/schema-diff.js.map +1 -0
  47. package/dist/functions/deno-shim.d.ts +24 -0
  48. package/dist/functions/deno-shim.js +47 -0
  49. package/dist/functions/deno-shim.js.map +1 -0
  50. package/dist/functions/handler.d.ts +3 -1
  51. package/dist/functions/handler.js.map +1 -1
  52. package/dist/gen-types.d.ts +17 -0
  53. package/dist/gen-types.js +222 -0
  54. package/dist/gen-types.js.map +1 -0
  55. package/dist/index.d.ts +20 -0
  56. package/dist/index.js +83 -15
  57. package/dist/index.js.map +1 -1
  58. package/dist/jwt.d.ts +7 -0
  59. package/dist/jwt.js.map +1 -1
  60. package/dist/log-buffer.d.ts +21 -0
  61. package/dist/log-buffer.js +20 -0
  62. package/dist/log-buffer.js.map +1 -0
  63. package/dist/node/bundle-function.d.ts +7 -0
  64. package/dist/node/bundle-function.js +90 -0
  65. package/dist/node/bundle-function.js.map +1 -0
  66. package/dist/node/db-diff.d.ts +34 -0
  67. package/dist/node/db-diff.js +82 -0
  68. package/dist/node/db-diff.js.map +1 -0
  69. package/dist/node/index.d.ts +4 -1
  70. package/dist/node/index.js +4 -1
  71. package/dist/node/index.js.map +1 -1
  72. package/dist/node/load-functions.d.ts +6 -0
  73. package/dist/node/load-functions.js +60 -8
  74. package/dist/node/load-functions.js.map +1 -1
  75. package/dist/node/load-oauth.d.ts +4 -0
  76. package/dist/node/load-oauth.js +126 -0
  77. package/dist/node/load-oauth.js.map +1 -0
  78. package/dist/node/native/engine.d.ts +1 -1
  79. package/dist/node/native/engine.js +45 -14
  80. package/dist/node/native/engine.js.map +1 -1
  81. package/dist/realtime/engine.d.ts +24 -1
  82. package/dist/realtime/engine.js +154 -2
  83. package/dist/realtime/engine.js.map +1 -1
  84. package/dist/rest/build.d.ts +5 -1
  85. package/dist/rest/build.js +17 -8
  86. package/dist/rest/build.js.map +1 -1
  87. package/dist/rest/handler.js +22 -9
  88. package/dist/rest/handler.js.map +1 -1
  89. package/dist/types.d.ts +10 -0
  90. package/dist/types.js.map +1 -1
  91. package/dist/webhooks/service.d.ts +47 -0
  92. package/dist/webhooks/service.js +86 -0
  93. package/dist/webhooks/service.js.map +1 -0
  94. 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":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AACrC,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AACnD,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;AAiBD;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAmB;IAClD,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,QAAQ,CAAA;IACtC,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;KAC5B,CAAC,CAAA;IACF,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;IACF,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,WAAW,CAAC,MAAM,cAAc,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,EAAE,MAAM,cAAc,CAAC,IAAI,CAAC,EAAE,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,CAAA;QAC/G,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAA;QAE/D,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;QACvF,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,eAAe,CAAA;QACzC,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,CAAA;QACpC,IAAI,IAAI,GAAkB,IAAI,CAAA;QAC9B,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACvB,MAAM,KAAK,CAAC,IAAI,CAAC,aAAa,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;YACpD,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,GAAG,KAAK,IAAI,IAAI,MAAM,CAAC,CAAA;YACvD,MAAM,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;QAC7B,CAAC;QACD,sEAAsE;QACtE,MAAM,IAAI,CAAC,EAAE,CAAC,KAAK,CACjB;4DACsD,EACtD,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CACpC,CAAA;QACD,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAA;IACtC,CAAC;YAAS,CAAC;QACT,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;QACpB,MAAM,IAAI,CAAC,KAAK,EAAE,CAAA;IACpB,CAAC;AACH,CAAC"}
@@ -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';
@@ -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
@@ -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
- const mod = (await import(pathToFileURL(path).href));
31
- if (typeof mod.default === 'function') {
32
- functions.set(name, mod.default);
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 (file === 'index.ts' && /Unknown file extension|Cannot find module/.test(msg)) {
41
- console.warn(` warning: function "${name}" is TypeScript — run tinbase with tsx or bun, or compile it to .js`);
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;AAChD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAGxC,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,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,UAAU,EAAE,WAAW,CAAC,EAAE,CAAC;YACzD,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,MAAM,GAAG,GAAG,CAAC,MAAM,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAA+B,CAAA;gBAClF,IAAI,OAAO,GAAG,CAAC,OAAO,KAAK,UAAU,EAAE,CAAC;oBACtC,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,OAAO,CAAC,CAAA;gBAClC,CAAC;qBAAM,CAAC;oBACN,OAAO,CAAC,IAAI,CAAC,wBAAwB,IAAI,kCAAkC,CAAC,CAAA;gBAC9E,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,IAAI,KAAK,UAAU,IAAI,2CAA2C,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;oBACjF,OAAO,CAAC,IAAI,CAAC,wBAAwB,IAAI,qEAAqE,CAAC,CAAA;gBACjH,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"}
1
+ {"version":3,"file":"load-functions.js","sourceRoot":"","sources":["../../src/node/load-functions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { OAuthProviderConfig } from '../auth/oauth.js';
2
+ type Providers = Record<string, OAuthProviderConfig>;
3
+ export declare function loadOAuthProviders(projectDir: string, env?: NodeJS.ProcessEnv): Providers;
4
+ export {};
@@ -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>;