tenanso 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +263 -0
  3. package/dist/__tests__/connection-pool.test.d.ts +2 -0
  4. package/dist/__tests__/connection-pool.test.d.ts.map +1 -0
  5. package/dist/__tests__/connection-pool.test.js +88 -0
  6. package/dist/__tests__/connection-pool.test.js.map +1 -0
  7. package/dist/__tests__/hono-middleware.test.d.ts +2 -0
  8. package/dist/__tests__/hono-middleware.test.d.ts.map +1 -0
  9. package/dist/__tests__/hono-middleware.test.js +121 -0
  10. package/dist/__tests__/hono-middleware.test.js.map +1 -0
  11. package/dist/__tests__/tenanso.test.d.ts +2 -0
  12. package/dist/__tests__/tenanso.test.d.ts.map +1 -0
  13. package/dist/__tests__/tenanso.test.js +105 -0
  14. package/dist/__tests__/tenanso.test.js.map +1 -0
  15. package/dist/__tests__/turso-api.test.d.ts +2 -0
  16. package/dist/__tests__/turso-api.test.d.ts.map +1 -0
  17. package/dist/__tests__/turso-api.test.js +103 -0
  18. package/dist/__tests__/turso-api.test.js.map +1 -0
  19. package/dist/connection-pool.d.ts +14 -0
  20. package/dist/connection-pool.d.ts.map +1 -0
  21. package/dist/connection-pool.js +57 -0
  22. package/dist/connection-pool.js.map +1 -0
  23. package/dist/index.d.ts +3 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +2 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/middleware/hono.d.ts +198 -0
  28. package/dist/middleware/hono.d.ts.map +1 -0
  29. package/dist/middleware/hono.js +123 -0
  30. package/dist/middleware/hono.js.map +1 -0
  31. package/dist/tenanso.d.ts +63 -0
  32. package/dist/tenanso.d.ts.map +1 -0
  33. package/dist/tenanso.js +92 -0
  34. package/dist/tenanso.js.map +1 -0
  35. package/dist/turso-api.d.ts +13 -0
  36. package/dist/turso-api.d.ts.map +1 -0
  37. package/dist/turso-api.js +83 -0
  38. package/dist/turso-api.js.map +1 -0
  39. package/dist/types.d.ts +349 -0
  40. package/dist/types.d.ts.map +1 -0
  41. package/dist/types.js +2 -0
  42. package/dist/types.js.map +1 -0
  43. package/package.json +72 -0
@@ -0,0 +1,103 @@
1
+ import { describe, it, expect, vi, beforeEach } from "vitest";
2
+ import { TursoApi } from "../turso-api.js";
3
+ const tursoConfig = {
4
+ organizationSlug: "test-org",
5
+ apiToken: "test-api-token",
6
+ group: "default",
7
+ };
8
+ describe("TursoApi", () => {
9
+ beforeEach(() => {
10
+ vi.restoreAllMocks();
11
+ });
12
+ describe("createDatabase", () => {
13
+ it("sends POST to Turso API without seed", async () => {
14
+ const fetchSpy = vi.spyOn(globalThis, "fetch").mockResolvedValue(new Response(JSON.stringify({ database: { Name: "my-tenant" } }), {
15
+ status: 200,
16
+ }));
17
+ const api = new TursoApi(tursoConfig, undefined);
18
+ await api.createDatabase("my-tenant");
19
+ expect(fetchSpy).toHaveBeenCalledOnce();
20
+ const [url, opts] = fetchSpy.mock.calls[0];
21
+ expect(url).toBe("https://api.turso.tech/v1/organizations/test-org/databases");
22
+ expect(opts?.method).toBe("POST");
23
+ const body = JSON.parse(opts?.body);
24
+ expect(body).toEqual({ name: "my-tenant", group: "default" });
25
+ });
26
+ it("includes seed when configured", async () => {
27
+ const fetchSpy = vi.spyOn(globalThis, "fetch").mockResolvedValue(new Response("{}", { status: 200 }));
28
+ const api = new TursoApi(tursoConfig, { database: "seed-db" });
29
+ await api.createDatabase("my-tenant");
30
+ const body = JSON.parse(fetchSpy.mock.calls[0][1]?.body);
31
+ expect(body).toEqual({
32
+ name: "my-tenant",
33
+ group: "default",
34
+ seed: {
35
+ type: "database",
36
+ name: "seed-db",
37
+ },
38
+ });
39
+ });
40
+ it("rejects invalid tenant names", async () => {
41
+ const api = new TursoApi(tursoConfig, undefined);
42
+ await expect(api.createDatabase("")).rejects.toThrow("Invalid tenant name");
43
+ await expect(api.createDatabase("My Tenant")).rejects.toThrow("Invalid tenant name");
44
+ await expect(api.createDatabase("-starts-with-hyphen")).rejects.toThrow("Invalid tenant name");
45
+ await expect(api.createDatabase("has/slash")).rejects.toThrow("Invalid tenant name");
46
+ await expect(api.createDatabase("has space")).rejects.toThrow("Invalid tenant name");
47
+ });
48
+ it("accepts valid tenant names", async () => {
49
+ vi.spyOn(globalThis, "fetch").mockResolvedValue(new Response("{}", { status: 200 }));
50
+ const api = new TursoApi(tursoConfig, undefined);
51
+ await expect(api.createDatabase("valid-name")).resolves.not.toThrow();
52
+ await expect(api.createDatabase("tenant123")).resolves.not.toThrow();
53
+ await expect(api.createDatabase("a")).resolves.not.toThrow();
54
+ });
55
+ it("throws on API error", async () => {
56
+ vi.spyOn(globalThis, "fetch").mockResolvedValue(new Response("quota exceeded", { status: 429 }));
57
+ const api = new TursoApi(tursoConfig, undefined);
58
+ await expect(api.createDatabase("my-tenant")).rejects.toThrow('Failed to create database "my-tenant": 429');
59
+ });
60
+ });
61
+ describe("deleteDatabase", () => {
62
+ it("sends DELETE to Turso API", async () => {
63
+ const fetchSpy = vi.spyOn(globalThis, "fetch").mockResolvedValue(new Response("{}", { status: 200 }));
64
+ const api = new TursoApi(tursoConfig, undefined);
65
+ await api.deleteDatabase("my-tenant");
66
+ const [url, opts] = fetchSpy.mock.calls[0];
67
+ expect(url).toBe("https://api.turso.tech/v1/organizations/test-org/databases/my-tenant");
68
+ expect(opts?.method).toBe("DELETE");
69
+ });
70
+ });
71
+ describe("listDatabases", () => {
72
+ it("returns database names", async () => {
73
+ vi.spyOn(globalThis, "fetch").mockResolvedValue(new Response(JSON.stringify({
74
+ databases: [{ Name: "tenant-a" }, { Name: "tenant-b" }],
75
+ }), { status: 200 }));
76
+ const api = new TursoApi(tursoConfig, undefined);
77
+ const result = await api.listDatabases();
78
+ expect(result).toEqual(["tenant-a", "tenant-b"]);
79
+ });
80
+ });
81
+ describe("databaseExists", () => {
82
+ it("returns true when database exists", async () => {
83
+ vi.spyOn(globalThis, "fetch").mockResolvedValue(new Response(JSON.stringify({ database: { Name: "my-tenant" } }), {
84
+ status: 200,
85
+ }));
86
+ const api = new TursoApi(tursoConfig, undefined);
87
+ expect(await api.databaseExists("my-tenant")).toBe(true);
88
+ const [url] = vi.mocked(fetch).mock.calls[0];
89
+ expect(url).toBe("https://api.turso.tech/v1/organizations/test-org/databases/my-tenant");
90
+ });
91
+ it("returns false when database does not exist", async () => {
92
+ vi.spyOn(globalThis, "fetch").mockResolvedValue(new Response("not found", { status: 404 }));
93
+ const api = new TursoApi(tursoConfig, undefined);
94
+ expect(await api.databaseExists("nonexistent")).toBe(false);
95
+ });
96
+ it("throws on non-404 errors", async () => {
97
+ vi.spyOn(globalThis, "fetch").mockResolvedValue(new Response("unauthorized", { status: 401 }));
98
+ const api = new TursoApi(tursoConfig, undefined);
99
+ await expect(api.databaseExists("my-tenant")).rejects.toThrow('Failed to check database "my-tenant": 401');
100
+ });
101
+ });
102
+ });
103
+ //# sourceMappingURL=turso-api.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"turso-api.test.js","sourceRoot":"","sources":["../../src/__tests__/turso-api.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { DrizzleDb, TenansoConfig } from "./types.js";
2
+ export declare class ConnectionPool {
3
+ private readonly cache;
4
+ private readonly databaseUrl;
5
+ private readonly authToken;
6
+ private readonly schema;
7
+ private readonly maxConnections;
8
+ constructor(config: TenansoConfig);
9
+ getDb(tenant: string): DrizzleDb;
10
+ remove(tenant: string): void;
11
+ get size(): number;
12
+ private evictIfNeeded;
13
+ }
14
+ //# sourceMappingURL=connection-pool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection-pool.d.ts","sourceRoot":"","sources":["../src/connection-pool.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAQ3D,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAgC;IACtD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAqB;IAC/C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA0B;IACjD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;gBAE5B,MAAM,EAAE,aAAa;IAOjC,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS;IAoBhC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAQ5B,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,OAAO,CAAC,aAAa;CAiBtB"}
@@ -0,0 +1,57 @@
1
+ import { drizzle } from "drizzle-orm/libsql";
2
+ import { createClient } from "@libsql/client";
3
+ export class ConnectionPool {
4
+ cache = new Map();
5
+ databaseUrl;
6
+ authToken;
7
+ schema;
8
+ maxConnections;
9
+ constructor(config) {
10
+ this.databaseUrl = config.databaseUrl;
11
+ this.authToken = config.authToken || undefined;
12
+ this.schema = config.schema;
13
+ this.maxConnections = config.maxConnections ?? 50;
14
+ }
15
+ getDb(tenant) {
16
+ const existing = this.cache.get(tenant);
17
+ if (existing) {
18
+ existing.lastUsed = Date.now();
19
+ return existing.db;
20
+ }
21
+ this.evictIfNeeded();
22
+ const url = this.databaseUrl.replace("{tenant}", tenant);
23
+ const client = createClient({
24
+ url,
25
+ ...(this.authToken ? { authToken: this.authToken } : {}),
26
+ });
27
+ const db = drizzle(client, { schema: this.schema });
28
+ this.cache.set(tenant, { client, db, lastUsed: Date.now() });
29
+ return db;
30
+ }
31
+ remove(tenant) {
32
+ const entry = this.cache.get(tenant);
33
+ if (entry) {
34
+ entry.client.close();
35
+ this.cache.delete(tenant);
36
+ }
37
+ }
38
+ get size() {
39
+ return this.cache.size;
40
+ }
41
+ evictIfNeeded() {
42
+ if (this.cache.size < this.maxConnections)
43
+ return;
44
+ let oldestKey;
45
+ let oldestTime = Infinity;
46
+ for (const [key, entry] of this.cache) {
47
+ if (entry.lastUsed < oldestTime) {
48
+ oldestTime = entry.lastUsed;
49
+ oldestKey = key;
50
+ }
51
+ }
52
+ if (oldestKey) {
53
+ this.remove(oldestKey);
54
+ }
55
+ }
56
+ }
57
+ //# sourceMappingURL=connection-pool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection-pool.js","sourceRoot":"","sources":["../src/connection-pool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAe,MAAM,gBAAgB,CAAC;AAS3D,MAAM,OAAO,cAAc;IACR,KAAK,GAAG,IAAI,GAAG,EAAqB,CAAC;IACrC,WAAW,CAAS;IACpB,SAAS,CAAqB;IAC9B,MAAM,CAA0B;IAChC,cAAc,CAAS;IAExC,YAAY,MAAqB;QAC/B,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC;QACtC,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,SAAS,IAAI,SAAS,CAAC;QAC/C,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;QAC5B,IAAI,CAAC,cAAc,GAAG,MAAM,CAAC,cAAc,IAAI,EAAE,CAAC;IACpD,CAAC;IAED,KAAK,CAAC,MAAc;QAClB,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACxC,IAAI,QAAQ,EAAE,CAAC;YACb,QAAQ,CAAC,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YAC/B,OAAO,QAAQ,CAAC,EAAE,CAAC;QACrB,CAAC;QAED,IAAI,CAAC,aAAa,EAAE,CAAC;QAErB,MAAM,GAAG,GAAG,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QACzD,MAAM,MAAM,GAAG,YAAY,CAAC;YAC1B,GAAG;YACH,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACzD,CAAC,CAAC;QACH,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;QAEpD,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QAC7D,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,CAAC,MAAc;QACnB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACrC,IAAI,KAAK,EAAE,CAAC;YACV,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;YACrB,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;IAED,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;IACzB,CAAC;IAEO,aAAa;QACnB,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,cAAc;YAAE,OAAO;QAElD,IAAI,SAA6B,CAAC;QAClC,IAAI,UAAU,GAAG,QAAQ,CAAC;QAE1B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACtC,IAAI,KAAK,CAAC,QAAQ,GAAG,UAAU,EAAE,CAAC;gBAChC,UAAU,GAAG,KAAK,CAAC,QAAQ,CAAC;gBAC5B,SAAS,GAAG,GAAG,CAAC;YAClB,CAAC;QACH,CAAC;QAED,IAAI,SAAS,EAAE,CAAC;YACd,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,3 @@
1
+ export { createTenanso } from "./tenanso.js";
2
+ export type { TenansoConfig, TenansoInstance, TursoConfig, SeedConfig, DrizzleDb, } from "./types.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,YAAY,EACV,aAAa,EACb,eAAe,EACf,WAAW,EACX,UAAU,EACV,SAAS,GACV,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { createTenanso } from "./tenanso.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,198 @@
1
+ import type { TenansoInstance, DrizzleDb } from "../types.js";
2
+ /**
3
+ * Hono environment type for tenanso middleware.
4
+ *
5
+ * Pass this as the generic to `new Hono<TenansoEnv>()` to get type-safe
6
+ * access to `c.var.tenant` and `c.var.db` in your route handlers.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * import { Hono } from "hono";
11
+ * import type { TenansoEnv } from "tenanso/hono";
12
+ *
13
+ * const app = new Hono<TenansoEnv>();
14
+ *
15
+ * app.get("/api/users", async (c) => {
16
+ * const db = c.var.db; // DrizzleDb — fully typed
17
+ * const tenant = c.var.tenant; // string
18
+ * });
19
+ * ```
20
+ */
21
+ export type TenansoEnv = {
22
+ Variables: {
23
+ /** Current tenant name, as resolved by {@link tenantMiddleware} */
24
+ tenant: string;
25
+ /** Drizzle db instance scoped to the current tenant */
26
+ db: DrizzleDb;
27
+ };
28
+ };
29
+ /**
30
+ * Options for {@link tenantMiddleware}.
31
+ */
32
+ export interface TenantMiddlewareOptions {
33
+ /**
34
+ * Resolve the tenant identifier from the request context.
35
+ *
36
+ * This function is called on every request. It should extract the tenant
37
+ * identifier from a trusted source — typically a verified JWT claim,
38
+ * an auth provider's organization ID, or an API key lookup.
39
+ *
40
+ * Return `undefined` to reject the request with a 400 response.
41
+ *
42
+ * **Security note:** Never trust raw client-supplied values without
43
+ * authentication. The tenant should come from a verified source.
44
+ *
45
+ * @param c - A subset of the Hono context with request accessors.
46
+ * @returns The tenant identifier, or `undefined` to reject.
47
+ *
48
+ * @example From a request header
49
+ * ```typescript
50
+ * resolve: (c) => c.req.header("x-tenant-id")
51
+ * ```
52
+ *
53
+ * @example From a verified JWT payload (recommended)
54
+ * ```typescript
55
+ * resolve: (c) => {
56
+ * const payload = c.get("jwtPayload") as { tenant: string };
57
+ * return payload.tenant;
58
+ * }
59
+ * ```
60
+ *
61
+ * @example From a URL path parameter
62
+ * ```typescript
63
+ * // Route: /t/:tenantId/*
64
+ * resolve: (c) => c.req.param("tenantId")
65
+ * ```
66
+ *
67
+ * @example From a subdomain
68
+ * ```typescript
69
+ * resolve: (c) => {
70
+ * const url = new URL(c.req.url);
71
+ * const subdomain = url.hostname.split(".")[0];
72
+ * return subdomain === "www" ? undefined : subdomain;
73
+ * }
74
+ * ```
75
+ *
76
+ * @example Async resolution (e.g., API key lookup)
77
+ * ```typescript
78
+ * resolve: async (c) => {
79
+ * const apiKey = c.req.header("Authorization")?.slice(7);
80
+ * return apiKey ? await lookupTenantByApiKey(apiKey) : undefined;
81
+ * }
82
+ * ```
83
+ */
84
+ resolve: (c: {
85
+ req: {
86
+ header: (name: string) => string | undefined;
87
+ param: (name: string) => string | undefined;
88
+ url: string;
89
+ };
90
+ get: (key: string) => unknown;
91
+ }) => string | undefined | Promise<string | undefined>;
92
+ }
93
+ /**
94
+ * Hono middleware that resolves the tenant from each request and sets
95
+ * `c.var.db` and `c.var.tenant`.
96
+ *
97
+ * This middleware:
98
+ * 1. Calls `options.resolve(c)` to extract the tenant identifier from the request
99
+ * 2. If `undefined`, responds with `400 { error: "Tenant not specified" }`
100
+ * 3. Otherwise, gets a Drizzle db instance via `tenanso.dbFor(tenant)`
101
+ * 4. Sets `c.var.tenant` and `c.var.db` for downstream handlers
102
+ *
103
+ * Combine with Hono's `contextStorage()` middleware to enable
104
+ * {@link getTenantDb} and {@link getTenantName} outside of handlers.
105
+ *
106
+ * @param tenanso - The tenanso instance created by `createTenanso()`.
107
+ * @param options - Middleware options. See {@link TenantMiddlewareOptions}.
108
+ * @returns A Hono middleware function.
109
+ *
110
+ * @example Basic usage
111
+ * ```typescript
112
+ * import { Hono } from "hono";
113
+ * import { contextStorage } from "hono/context-storage";
114
+ * import { createTenanso } from "tenanso";
115
+ * import { tenantMiddleware, type TenansoEnv } from "tenanso/hono";
116
+ *
117
+ * const tenanso = createTenanso({ ... });
118
+ * const app = new Hono<TenansoEnv>();
119
+ *
120
+ * app.use(contextStorage());
121
+ * app.use("/api/*", tenantMiddleware(tenanso, {
122
+ * resolve: (c) => c.req.header("x-tenant-id"),
123
+ * }));
124
+ *
125
+ * app.get("/api/users", async (c) => {
126
+ * const users = await c.var.db.select().from(usersTable);
127
+ * return c.json(users);
128
+ * });
129
+ * ```
130
+ *
131
+ * @example Scoped to specific routes
132
+ * ```typescript
133
+ * const app = new Hono();
134
+ *
135
+ * // No tenant needed
136
+ * app.get("/health", (c) => c.json({ status: "ok" }));
137
+ *
138
+ * // Tenant-scoped routes
139
+ * const api = new Hono<TenansoEnv>();
140
+ * api.use("*", tenantMiddleware(tenanso, {
141
+ * resolve: (c) => c.get("jwtPayload").tenant,
142
+ * }));
143
+ * api.get("/users", async (c) => {
144
+ * return c.json({ tenant: c.var.tenant });
145
+ * });
146
+ * app.route("/api", api);
147
+ * ```
148
+ */
149
+ export declare function tenantMiddleware(tenanso: TenansoInstance, options: TenantMiddlewareOptions): import("hono").MiddlewareHandler<TenansoEnv, string, {}, Response>;
150
+ /**
151
+ * Get the current tenant's Drizzle db from Hono's `contextStorage()`.
152
+ *
153
+ * This allows you to access the tenant-scoped database from anywhere
154
+ * in the async call stack — not just inside Hono route handlers.
155
+ * Requires Hono's `contextStorage()` middleware to be active.
156
+ *
157
+ * @returns The Drizzle database instance for the current tenant.
158
+ * @throws If called outside of a request context or without `contextStorage()` middleware.
159
+ *
160
+ * @example
161
+ * ```typescript
162
+ * import { getTenantDb } from "tenanso/hono";
163
+ *
164
+ * // Can be called from any async function during request handling
165
+ * async function getActiveUserCount(): Promise<number> {
166
+ * const db = getTenantDb();
167
+ * const users = await db.select().from(usersTable);
168
+ * return users.length;
169
+ * }
170
+ *
171
+ * app.get("/api/stats", async (c) => {
172
+ * const count = await getActiveUserCount();
173
+ * return c.json({ activeUsers: count });
174
+ * });
175
+ * ```
176
+ */
177
+ export declare function getTenantDb(): DrizzleDb;
178
+ /**
179
+ * Get the current tenant name from Hono's `contextStorage()`.
180
+ *
181
+ * This allows you to access the current tenant identifier from anywhere
182
+ * in the async call stack — not just inside Hono route handlers.
183
+ * Requires Hono's `contextStorage()` middleware to be active.
184
+ *
185
+ * @returns The current tenant name as a string.
186
+ * @throws If called outside of a request context or without `contextStorage()` middleware.
187
+ *
188
+ * @example
189
+ * ```typescript
190
+ * import { getTenantName } from "tenanso/hono";
191
+ *
192
+ * function logAction(action: string) {
193
+ * console.log(`[${getTenantName()}] ${action}`);
194
+ * }
195
+ * ```
196
+ */
197
+ export declare function getTenantName(): string;
198
+ //# sourceMappingURL=hono.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hono.d.ts","sourceRoot":"","sources":["../../src/middleware/hono.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE9D;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB,SAAS,EAAE;QACT,mEAAmE;QACnE,MAAM,EAAE,MAAM,CAAC;QACf,uDAAuD;QACvD,EAAE,EAAE,SAAS,CAAC;KACf,CAAC;CACH,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAkDG;IACH,OAAO,EAAE,CAAC,CAAC,EAAE;QACX,GAAG,EAAE;YACH,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;YAC7C,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;YAC5C,GAAG,EAAE,MAAM,CAAC;SACb,CAAC;QACF,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;KAC/B,KAAK,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;CACxD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,eAAe,EACxB,OAAO,EAAE,uBAAuB,sEAajC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,WAAW,IAAI,SAAS,CAEvC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAEtC"}
@@ -0,0 +1,123 @@
1
+ import { createMiddleware } from "hono/factory";
2
+ import { getContext } from "hono/context-storage";
3
+ /**
4
+ * Hono middleware that resolves the tenant from each request and sets
5
+ * `c.var.db` and `c.var.tenant`.
6
+ *
7
+ * This middleware:
8
+ * 1. Calls `options.resolve(c)` to extract the tenant identifier from the request
9
+ * 2. If `undefined`, responds with `400 { error: "Tenant not specified" }`
10
+ * 3. Otherwise, gets a Drizzle db instance via `tenanso.dbFor(tenant)`
11
+ * 4. Sets `c.var.tenant` and `c.var.db` for downstream handlers
12
+ *
13
+ * Combine with Hono's `contextStorage()` middleware to enable
14
+ * {@link getTenantDb} and {@link getTenantName} outside of handlers.
15
+ *
16
+ * @param tenanso - The tenanso instance created by `createTenanso()`.
17
+ * @param options - Middleware options. See {@link TenantMiddlewareOptions}.
18
+ * @returns A Hono middleware function.
19
+ *
20
+ * @example Basic usage
21
+ * ```typescript
22
+ * import { Hono } from "hono";
23
+ * import { contextStorage } from "hono/context-storage";
24
+ * import { createTenanso } from "tenanso";
25
+ * import { tenantMiddleware, type TenansoEnv } from "tenanso/hono";
26
+ *
27
+ * const tenanso = createTenanso({ ... });
28
+ * const app = new Hono<TenansoEnv>();
29
+ *
30
+ * app.use(contextStorage());
31
+ * app.use("/api/*", tenantMiddleware(tenanso, {
32
+ * resolve: (c) => c.req.header("x-tenant-id"),
33
+ * }));
34
+ *
35
+ * app.get("/api/users", async (c) => {
36
+ * const users = await c.var.db.select().from(usersTable);
37
+ * return c.json(users);
38
+ * });
39
+ * ```
40
+ *
41
+ * @example Scoped to specific routes
42
+ * ```typescript
43
+ * const app = new Hono();
44
+ *
45
+ * // No tenant needed
46
+ * app.get("/health", (c) => c.json({ status: "ok" }));
47
+ *
48
+ * // Tenant-scoped routes
49
+ * const api = new Hono<TenansoEnv>();
50
+ * api.use("*", tenantMiddleware(tenanso, {
51
+ * resolve: (c) => c.get("jwtPayload").tenant,
52
+ * }));
53
+ * api.get("/users", async (c) => {
54
+ * return c.json({ tenant: c.var.tenant });
55
+ * });
56
+ * app.route("/api", api);
57
+ * ```
58
+ */
59
+ export function tenantMiddleware(tenanso, options) {
60
+ return createMiddleware(async (c, next) => {
61
+ const tenantName = await options.resolve(c);
62
+ if (!tenantName) {
63
+ return c.json({ error: "Tenant not specified" }, 400);
64
+ }
65
+ const db = tenanso.dbFor(tenantName);
66
+ c.set("tenant", tenantName);
67
+ c.set("db", db);
68
+ await next();
69
+ });
70
+ }
71
+ /**
72
+ * Get the current tenant's Drizzle db from Hono's `contextStorage()`.
73
+ *
74
+ * This allows you to access the tenant-scoped database from anywhere
75
+ * in the async call stack — not just inside Hono route handlers.
76
+ * Requires Hono's `contextStorage()` middleware to be active.
77
+ *
78
+ * @returns The Drizzle database instance for the current tenant.
79
+ * @throws If called outside of a request context or without `contextStorage()` middleware.
80
+ *
81
+ * @example
82
+ * ```typescript
83
+ * import { getTenantDb } from "tenanso/hono";
84
+ *
85
+ * // Can be called from any async function during request handling
86
+ * async function getActiveUserCount(): Promise<number> {
87
+ * const db = getTenantDb();
88
+ * const users = await db.select().from(usersTable);
89
+ * return users.length;
90
+ * }
91
+ *
92
+ * app.get("/api/stats", async (c) => {
93
+ * const count = await getActiveUserCount();
94
+ * return c.json({ activeUsers: count });
95
+ * });
96
+ * ```
97
+ */
98
+ export function getTenantDb() {
99
+ return getContext().var.db;
100
+ }
101
+ /**
102
+ * Get the current tenant name from Hono's `contextStorage()`.
103
+ *
104
+ * This allows you to access the current tenant identifier from anywhere
105
+ * in the async call stack — not just inside Hono route handlers.
106
+ * Requires Hono's `contextStorage()` middleware to be active.
107
+ *
108
+ * @returns The current tenant name as a string.
109
+ * @throws If called outside of a request context or without `contextStorage()` middleware.
110
+ *
111
+ * @example
112
+ * ```typescript
113
+ * import { getTenantName } from "tenanso/hono";
114
+ *
115
+ * function logAction(action: string) {
116
+ * console.log(`[${getTenantName()}] ${action}`);
117
+ * }
118
+ * ```
119
+ */
120
+ export function getTenantName() {
121
+ return getContext().var.tenant;
122
+ }
123
+ //# sourceMappingURL=hono.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hono.js","sourceRoot":"","sources":["../../src/middleware/hono.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAgGlD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AACH,MAAM,UAAU,gBAAgB,CAC9B,OAAwB,EACxB,OAAgC;IAEhC,OAAO,gBAAgB,CAAa,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE;QACpD,MAAM,UAAU,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAC5C,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,sBAAsB,EAAE,EAAE,GAAG,CAAC,CAAC;QACxD,CAAC;QAED,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QACrC,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;QAC5B,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAChB,MAAM,IAAI,EAAE,CAAC;IACf,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,UAAU,WAAW;IACzB,OAAO,UAAU,EAAc,CAAC,GAAG,CAAC,EAAE,CAAC;AACzC,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,aAAa;IAC3B,OAAO,UAAU,EAAc,CAAC,GAAG,CAAC,MAAM,CAAC;AAC7C,CAAC"}
@@ -0,0 +1,63 @@
1
+ import type { TenansoConfig, TenansoInstance } from "./types.js";
2
+ /**
3
+ * Create a tenanso instance for multi-tenant database management.
4
+ *
5
+ * This is the main entry point for the library. It returns a {@link TenansoInstance}
6
+ * that provides tenant lifecycle management and tenant-scoped database access.
7
+ *
8
+ * Internally, it creates:
9
+ * - A {@link ConnectionPool} that caches Drizzle instances per tenant with LRU eviction
10
+ * - A Turso Platform API client for creating, deleting, and listing tenant databases
11
+ *
12
+ * @param config - Configuration options. See {@link TenansoConfig}.
13
+ * @returns A {@link TenansoInstance} for managing tenants and accessing their databases.
14
+ *
15
+ * @example Basic setup
16
+ * ```typescript
17
+ * import { createTenanso } from "tenanso";
18
+ * import * as schema from "./db/schema.js";
19
+ *
20
+ * const tenanso = createTenanso({
21
+ * turso: {
22
+ * organizationSlug: "my-org",
23
+ * apiToken: process.env.TURSO_API_TOKEN!,
24
+ * group: "my-app",
25
+ * },
26
+ * databaseUrl: "libsql://{tenant}-my-app-my-account.turso.io",
27
+ * authToken: process.env.TURSO_GROUP_AUTH_TOKEN!,
28
+ * schema,
29
+ * seed: { database: "seed-db" },
30
+ * });
31
+ * ```
32
+ *
33
+ * @example Tenant lifecycle
34
+ * ```typescript
35
+ * // Create a tenant (cloned from seed-db)
36
+ * await tenanso.createTenant("acme");
37
+ *
38
+ * // Query tenant's database
39
+ * await tenanso.withTenant("acme", async (db) => {
40
+ * const users = await db.select().from(usersTable);
41
+ * });
42
+ *
43
+ * // Or get the db directly
44
+ * const db = tenanso.dbFor("acme");
45
+ * ```
46
+ *
47
+ * @example With Hono middleware
48
+ * ```typescript
49
+ * import { tenantMiddleware, type TenansoEnv } from "tenanso/hono";
50
+ *
51
+ * const app = new Hono<TenansoEnv>();
52
+ * app.use("/api/*", tenantMiddleware(tenanso, {
53
+ * resolve: (c) => c.get("jwtPayload").tenant,
54
+ * }));
55
+ *
56
+ * app.get("/api/users", async (c) => {
57
+ * const users = await c.var.db.select().from(usersTable);
58
+ * return c.json(users);
59
+ * });
60
+ * ```
61
+ */
62
+ export declare function createTenanso(config: TenansoConfig): TenansoInstance;
63
+ //# sourceMappingURL=tenanso.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tenanso.d.ts","sourceRoot":"","sources":["../src/tenanso.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAa,MAAM,YAAY,CAAC;AAE5E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2DG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,aAAa,GAAG,eAAe,CAwCpE"}