@alchemy.run/turso 0.0.0 → 2.0.0-beta.85

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 (83) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +68 -4
  3. package/lib/ApiToken.d.ts +82 -0
  4. package/lib/ApiToken.d.ts.map +1 -0
  5. package/lib/ApiToken.js +128 -0
  6. package/lib/ApiToken.js.map +1 -0
  7. package/lib/AuthProvider.d.ts +36 -0
  8. package/lib/AuthProvider.d.ts.map +1 -0
  9. package/lib/AuthProvider.js +85 -0
  10. package/lib/AuthProvider.js.map +1 -0
  11. package/lib/Connect.d.ts +79 -0
  12. package/lib/Connect.d.ts.map +1 -0
  13. package/lib/Connect.js +22 -0
  14. package/lib/Connect.js.map +1 -0
  15. package/lib/Contributions.d.ts +7 -0
  16. package/lib/Contributions.d.ts.map +1 -0
  17. package/lib/Contributions.js +6 -0
  18. package/lib/Contributions.js.map +1 -0
  19. package/lib/Credentials.d.ts +39 -0
  20. package/lib/Credentials.d.ts.map +1 -0
  21. package/lib/Credentials.js +76 -0
  22. package/lib/Credentials.js.map +1 -0
  23. package/lib/Database.d.ts +226 -0
  24. package/lib/Database.d.ts.map +1 -0
  25. package/lib/Database.js +277 -0
  26. package/lib/Database.js.map +1 -0
  27. package/lib/DatabaseToken.d.ts +104 -0
  28. package/lib/DatabaseToken.d.ts.map +1 -0
  29. package/lib/DatabaseToken.js +133 -0
  30. package/lib/DatabaseToken.js.map +1 -0
  31. package/lib/Group.d.ts +102 -0
  32. package/lib/Group.d.ts.map +1 -0
  33. package/lib/Group.js +171 -0
  34. package/lib/Group.js.map +1 -0
  35. package/lib/GroupToken.d.ts +57 -0
  36. package/lib/GroupToken.d.ts.map +1 -0
  37. package/lib/GroupToken.js +88 -0
  38. package/lib/GroupToken.js.map +1 -0
  39. package/lib/Hrana.d.ts +42 -0
  40. package/lib/Hrana.d.ts.map +1 -0
  41. package/lib/Hrana.js +109 -0
  42. package/lib/Hrana.js.map +1 -0
  43. package/lib/ManageDatabases.d.ts +73 -0
  44. package/lib/ManageDatabases.d.ts.map +1 -0
  45. package/lib/ManageDatabases.js +81 -0
  46. package/lib/ManageDatabases.js.map +1 -0
  47. package/lib/OrganizationInvite.d.ts +46 -0
  48. package/lib/OrganizationInvite.d.ts.map +1 -0
  49. package/lib/OrganizationInvite.js +72 -0
  50. package/lib/OrganizationInvite.js.map +1 -0
  51. package/lib/OrganizationMember.d.ts +47 -0
  52. package/lib/OrganizationMember.d.ts.map +1 -0
  53. package/lib/OrganizationMember.js +81 -0
  54. package/lib/OrganizationMember.js.map +1 -0
  55. package/lib/OrganizationSettings.d.ts +52 -0
  56. package/lib/OrganizationSettings.d.ts.map +1 -0
  57. package/lib/OrganizationSettings.js +93 -0
  58. package/lib/OrganizationSettings.js.map +1 -0
  59. package/lib/Providers.d.ts +24 -0
  60. package/lib/Providers.d.ts.map +1 -0
  61. package/lib/Providers.js +42 -0
  62. package/lib/Providers.js.map +1 -0
  63. package/lib/index.d.ts +14 -0
  64. package/lib/index.d.ts.map +1 -0
  65. package/lib/index.js +14 -0
  66. package/lib/index.js.map +1 -0
  67. package/package.json +54 -3
  68. package/src/ApiToken.ts +207 -0
  69. package/src/AuthProvider.ts +113 -0
  70. package/src/Connect.ts +103 -0
  71. package/src/Contributions.ts +9 -0
  72. package/src/Credentials.ts +112 -0
  73. package/src/Database.ts +519 -0
  74. package/src/DatabaseToken.ts +213 -0
  75. package/src/Group.ts +269 -0
  76. package/src/GroupToken.ts +127 -0
  77. package/src/Hrana.ts +184 -0
  78. package/src/ManageDatabases.ts +195 -0
  79. package/src/OrganizationInvite.ts +118 -0
  80. package/src/OrganizationMember.ts +122 -0
  81. package/src/OrganizationSettings.ts +145 -0
  82. package/src/Providers.ts +67 -0
  83. package/src/index.ts +20 -0
@@ -0,0 +1,277 @@
1
+ import { diffMigrations, migrationsAttrs, migrationsInputOf, runMigrations, stampedOf, } from "@alchemy.run/sql/Migrations";
2
+ import { applySqlImports, hashImports, splitSqlStatements } from "@alchemy.run/sql/SqlFile";
3
+ import * as turso from "@distilled.cloud/turso/turso";
4
+ import { Unowned } from "alchemy/AdoptPolicy";
5
+ import { deepEqual, isResolved } from "alchemy/Diff";
6
+ import { createPhysicalName } from "alchemy/PhysicalName";
7
+ import * as Provider from "alchemy/Provider";
8
+ import { Resource } from "alchemy/Resource";
9
+ import * as Data from "effect/Data";
10
+ import * as Effect from "effect/Effect";
11
+ import * as HttpClient from "effect/http/HttpClient";
12
+ import * as Redacted from "effect/Redacted";
13
+ import { organization } from "./Credentials.js";
14
+ import * as Hrana from "./Hrana.js";
15
+ export class DatabaseUnexpectedResponse extends Data.TaggedError("DatabaseUnexpectedResponse") {
16
+ }
17
+ /**
18
+ * A Turso database: a SQLite database that lives in a {@link Group}.
19
+ * Connect to it from a Worker or Lambda with {@link Connect}, or create a
20
+ * {@link DatabaseToken} for other apps.
21
+ * @see https://docs.turso.tech/concepts#databases
22
+ *
23
+ * ### Creating a Database
24
+ * **Example:** Database in a group
25
+ * ```typescript
26
+ * const group = yield* Turso.Group("Group", { location: "aws-us-east-1" });
27
+ * const db = yield* Turso.Database("Db", { group: group.name });
28
+ * ```
29
+ *
30
+ * **Example:** Database with delete protection and an IP allowlist
31
+ * ```typescript
32
+ * const db = yield* Turso.Database("Db", {
33
+ * group: group.name,
34
+ * deleteProtection: true,
35
+ * allowedIps: ["203.0.113.0/24"],
36
+ * });
37
+ * ```
38
+ *
39
+ * ### Branching
40
+ * **Example:** Copy an existing database
41
+ * ```typescript
42
+ * const preview = yield* Turso.Database("Preview", {
43
+ * group: group.name,
44
+ * seed: { type: "database", name: db.name },
45
+ * });
46
+ * ```
47
+ *
48
+ * ### Migrations
49
+ * Point `migrations` at a folder of `.sql` files (or a `Drizzle.Schema`).
50
+ * Pending migrations are applied in order on each deploy.
51
+ *
52
+ * **Example:** Apply migrations from a directory
53
+ * ```typescript
54
+ * const db = yield* Turso.Database("Db", {
55
+ * group: group.name,
56
+ * migrations: "./migrations",
57
+ * });
58
+ * ```
59
+ *
60
+ * **Example:** Seed with SQL files
61
+ * ```typescript
62
+ * const db = yield* Turso.Database("Db", {
63
+ * group: group.name,
64
+ * importFiles: ["./seed/users.sql"],
65
+ * });
66
+ * ```
67
+ *
68
+ * ### Querying from a Worker
69
+ * **Example:** Drizzle over a Turso database
70
+ * ```typescript
71
+ * const conn = yield* Turso.Connect(Db);
72
+ * const db = yield* Drizzle.LibSQL(conn);
73
+ * ```
74
+ *
75
+ * @resource
76
+ * @product Database
77
+ */
78
+ export const Database = Resource("Turso.Database");
79
+ const databaseName = (id, name) => name
80
+ ? Effect.succeed(name)
81
+ : createPhysicalName({ id, lowercase: true, maxLength: 32, delimiter: "-" });
82
+ const rootDir = Effect.sync(() => process.cwd());
83
+ /**
84
+ * Run `use` against a short-lived (10 minute) full-access database token,
85
+ * for deploy-time SQL (migrations and imports).
86
+ */
87
+ const withConnection = (org, database, use) => Effect.gen(function* () {
88
+ const client = yield* HttpClient.HttpClient;
89
+ const { jwt } = yield* turso.createDatabaseToken({
90
+ organizationSlug: org,
91
+ databaseName: database.name,
92
+ expiration: "10m",
93
+ authorization: "full-access",
94
+ });
95
+ if (!jwt) {
96
+ return yield* new DatabaseUnexpectedResponse({
97
+ message: `Turso returned no token for ${database.name}`,
98
+ });
99
+ }
100
+ return yield* use({ url: `https://${database.hostname}`, authToken: jwt }, client);
101
+ });
102
+ export const DatabaseProvider = () => Provider.effect(Database, Effect.gen(function* () {
103
+ const observe = (org, name) => turso.getDatabase({ organizationSlug: org, databaseName: name }).pipe(Effect.map((res) => res.database), Effect.catchTag("NotFound", () => Effect.undefined));
104
+ const toAttrs = (org, db, config, rest) => ({
105
+ dbId: db.DbId ?? "",
106
+ name: db.Name,
107
+ organization: org,
108
+ group: db.group ?? "",
109
+ hostname: db.Hostname ?? "",
110
+ url: `libsql://${db.Hostname}`,
111
+ httpUrl: `https://${db.Hostname}`,
112
+ primaryRegion: db.primaryRegion ?? "",
113
+ regions: db.regions ?? [],
114
+ sizeLimit: config.size_limit || undefined,
115
+ allowAttach: config.allow_attach ?? false,
116
+ blockReads: config.block_reads ?? false,
117
+ blockWrites: config.block_writes ?? false,
118
+ deleteProtection: config.delete_protection ?? false,
119
+ allowedIps: config.allowed_ips ?? [],
120
+ allowedAwsVpcIds: config.allowed_aws_vpc_ids ?? [],
121
+ ...rest,
122
+ });
123
+ const getConfig = (org, name) => turso.getDatabaseConfiguration({ organizationSlug: org, databaseName: name });
124
+ return {
125
+ stables: ["dbId", "name", "organization", "group", "hostname", "url", "httpUrl"],
126
+ diff: Effect.fn(function* ({ id, olds, news, output }) {
127
+ if (!isResolved(news))
128
+ return undefined;
129
+ const name = output?.name ?? (yield* databaseName(id, olds?.name));
130
+ if (news.name !== undefined && news.name !== name) {
131
+ return { action: "replace" };
132
+ }
133
+ if (news.group !== (output?.group ?? olds?.group)) {
134
+ return { action: "replace", deleteFirst: news.name !== undefined };
135
+ }
136
+ if (news.encryption?.cipher !== olds?.encryption?.cipher ||
137
+ (news.encryption &&
138
+ olds?.encryption &&
139
+ Redacted.value(news.encryption.key) !== Redacted.value(olds.encryption.key))) {
140
+ return { action: "replace", deleteFirst: news.name !== undefined };
141
+ }
142
+ if (yield* diffMigrations({ news, output })) {
143
+ return { action: "update" };
144
+ }
145
+ if (news.importFiles?.length) {
146
+ const hashes = yield* hashImports(news.importFiles, yield* rootDir);
147
+ if (!deepEqual(hashes, output?.importHashes ?? {})) {
148
+ return { action: "update" };
149
+ }
150
+ }
151
+ }),
152
+ read: Effect.fn(function* ({ id, olds, output, previous }) {
153
+ const org = output?.organization ?? (yield* organization);
154
+ const name = output?.name ?? (yield* databaseName(id, olds?.name));
155
+ const db = yield* observe(org, name);
156
+ if (!db)
157
+ return undefined;
158
+ const attrs = toAttrs(org, db, yield* getConfig(org, name), {
159
+ migrationsDir: output?.migrationsDir,
160
+ migrationsTable: output?.migrationsTable,
161
+ migrationsHashes: output?.migrationsHashes ?? {},
162
+ importHashes: output?.importHashes ?? {},
163
+ });
164
+ // Turso has no tags: a database found under an explicit,
165
+ // user-chosen name without prior state may belong to someone else — unless it
166
+ // is the generation a delete-first replacement is about to free.
167
+ const ours = output !== undefined ||
168
+ olds?.name === undefined ||
169
+ (previous?.organization === org && previous.name === name);
170
+ return ours ? attrs : Unowned(attrs);
171
+ }),
172
+ reconcile: Effect.fn(function* ({ id, news, output, session }) {
173
+ const org = output?.organization ?? (yield* organization);
174
+ const name = output?.name ?? (yield* databaseName(id, news.name));
175
+ // Observe
176
+ let db = yield* observe(org, name);
177
+ // Ensure
178
+ if (!db) {
179
+ yield* session.note(`Creating database ${name}...`);
180
+ yield* turso
181
+ .createDatabase({
182
+ organizationSlug: org,
183
+ name,
184
+ group: news.group,
185
+ seed: news.seed,
186
+ size_limit: news.sizeLimit,
187
+ remote_encryption: news.encryption
188
+ ? {
189
+ encryption_key: Redacted.value(news.encryption.key),
190
+ encryption_cipher: news.encryption.cipher,
191
+ }
192
+ : undefined,
193
+ })
194
+ .pipe(Effect.catchTag("Conflict", () => Effect.void));
195
+ db = yield* observe(org, name);
196
+ if (!db) {
197
+ return yield* new DatabaseUnexpectedResponse({
198
+ message: `Turso database ${name} vanished after create`,
199
+ });
200
+ }
201
+ }
202
+ // Sync configuration — diff observed settings against desired
203
+ // and PATCH only the fields that differ.
204
+ let config = yield* getConfig(org, name);
205
+ const patch = {
206
+ databaseName: name,
207
+ };
208
+ let dirty = false;
209
+ const set = (key, observed, desired) => {
210
+ if (!deepEqual(observed, desired)) {
211
+ patch[key] = desired;
212
+ dirty = true;
213
+ }
214
+ };
215
+ if (news.sizeLimit !== undefined)
216
+ set("size_limit", config.size_limit, news.sizeLimit);
217
+ // AWS-hosted databases reject `allow_attach` outright, so it is
218
+ // only sent when explicitly configured.
219
+ if (news.allowAttach !== undefined) {
220
+ set("allow_attach", config.allow_attach ?? false, news.allowAttach);
221
+ }
222
+ set("block_reads", config.block_reads ?? false, news.blockReads ?? false);
223
+ set("block_writes", config.block_writes ?? false, news.blockWrites ?? false);
224
+ set("delete_protection", config.delete_protection ?? false, news.deleteProtection ?? false);
225
+ set("allowed_ips", config.allowed_ips ?? [], news.allowedIps ?? []);
226
+ set("allowed_aws_vpc_ids", config.allowed_aws_vpc_ids ?? [], news.allowedAwsVpcIds ?? []);
227
+ if (dirty) {
228
+ config = yield* turso.updateDatabaseConfiguration({ organizationSlug: org, ...patch });
229
+ }
230
+ // Sync schema — migrations, then SQL imports.
231
+ const target = { name, hostname: db.Hostname };
232
+ const migrationsInput = migrationsInputOf(news);
233
+ const migrations = migrationsInput
234
+ ? yield* runMigrations({
235
+ input: migrationsInput,
236
+ stamped: stampedOf(output),
237
+ withExecutor: (apply) => withConnection(org, target, (connection, client) => apply(Hrana.makeMigrationExecutor(connection, client))),
238
+ })
239
+ : undefined;
240
+ const importHashes = yield* applySqlImports({
241
+ importFiles: news.importFiles,
242
+ rootDir: yield* rootDir,
243
+ previous: output?.importHashes,
244
+ apply: (file) => session
245
+ .note(`Importing ${file.id}...`)
246
+ .pipe(Effect.andThen(withConnection(org, target, (connection, client) => Hrana.transaction(connection, splitSqlStatements(file.sql)).pipe(Effect.provideService(HttpClient.HttpClient, client))))),
247
+ });
248
+ return toAttrs(org, db, config, {
249
+ ...migrationsAttrs({ input: migrationsInput, run: migrations, output }),
250
+ importHashes,
251
+ });
252
+ }),
253
+ delete: Effect.fn(function* ({ output }) {
254
+ yield* turso
255
+ .deleteDatabase({ organizationSlug: output.organization, databaseName: output.name })
256
+ .pipe(Effect.catchTag("NotFound", () => Effect.void));
257
+ }),
258
+ list: Effect.fn(function* () {
259
+ const org = yield* organization;
260
+ const { databases } = yield* turso.listDatabases({ organizationSlug: org });
261
+ return (databases ?? [])
262
+ .filter((db) => db.Name !== undefined)
263
+ .map((db) => toAttrs(org, db, {
264
+ allow_attach: db.allow_attach,
265
+ block_reads: db.block_reads,
266
+ block_writes: db.block_writes,
267
+ delete_protection: db.delete_protection,
268
+ }, {
269
+ migrationsDir: undefined,
270
+ migrationsTable: undefined,
271
+ migrationsHashes: {},
272
+ importHashes: {},
273
+ }));
274
+ }),
275
+ };
276
+ }));
277
+ //# sourceMappingURL=Database.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Database.js","sourceRoot":"","sources":["../src/Database.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,104 @@
1
+ import * as turso from "@distilled.cloud/turso/turso";
2
+ import * as Provider from "alchemy/Provider";
3
+ import { Resource } from "alchemy/Resource";
4
+ import * as Effect from "effect/Effect";
5
+ import * as Redacted from "effect/Redacted";
6
+ import type { Providers } from "./Providers.ts";
7
+ declare const DatabaseTokenUnexpectedResponse_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
8
+ readonly _tag: "DatabaseTokenUnexpectedResponse";
9
+ } & Readonly<A>;
10
+ export declare class DatabaseTokenUnexpectedResponse extends DatabaseTokenUnexpectedResponse_base<{
11
+ readonly message: string;
12
+ readonly cause?: unknown;
13
+ }> {
14
+ }
15
+ /** Access level of a Turso SQL auth token. */
16
+ export type TokenAuthorization = "full-access" | "read-only";
17
+ /** Props shared by {@link DatabaseToken} and {@link GroupToken}. */
18
+ export interface SqlTokenProps {
19
+ /**
20
+ * Access level granted by the token.
21
+ * @default "full-access"
22
+ */
23
+ authorization?: TokenAuthorization;
24
+ /**
25
+ * Token lifetime, e.g. `"2w"`, `"7d"`, `"1h30m"`. Omit for a token that
26
+ * never expires. An expired token is re-minted on the next deploy.
27
+ */
28
+ expiration?: string;
29
+ /**
30
+ * Databases this token may `ATTACH` for reading (requires `allowAttach`
31
+ * on those databases). Not supported on AWS-hosted databases.
32
+ */
33
+ readAttach?: string[];
34
+ }
35
+ /** Attributes shared by {@link DatabaseToken} and {@link GroupToken}. */
36
+ export interface SqlTokenAttributes {
37
+ /** The token (a JWT), sent as `Authorization: Bearer <token>`. */
38
+ token: Redacted.Redacted<string>;
39
+ /** The organization slug the token belongs to. */
40
+ organization: string;
41
+ /** Access level granted by the token. */
42
+ authorization: TokenAuthorization;
43
+ /** Expiry as epoch milliseconds, or `undefined` for a non-expiring token. */
44
+ expiresAt: number | undefined;
45
+ }
46
+ export interface DatabaseTokenProps extends SqlTokenProps {
47
+ /** Name of the database the token grants access to (e.g. `db.name`). */
48
+ database: string;
49
+ }
50
+ export interface DatabaseTokenAttributes extends SqlTokenAttributes {
51
+ /** Name of the database the token grants access to. */
52
+ database: string;
53
+ /** UUID of the database the token was minted for. */
54
+ dbId: string;
55
+ }
56
+ export type DatabaseToken = Resource<"Turso.DatabaseToken", DatabaseTokenProps, DatabaseTokenAttributes, never, Providers>;
57
+ /**
58
+ * A token that lets an app run SQL on one Turso database. Workers and
59
+ * Lambdas should use {@link Connect}, which creates one for you. Use
60
+ * `DatabaseToken` for anything else, such as a container or a CI job.
61
+ *
62
+ * You can't cancel a single token. Changing a prop creates a new token, but
63
+ * the old one keeps working until it expires. Destroying the resource doesn't
64
+ * stop it either. Give tokens you share an `expiration`, or cancel every token
65
+ * for the database with `turso db tokens invalidate`.
66
+ * @see https://docs.turso.tech/api-reference/databases/create-token
67
+ *
68
+ * ### Creating a Token
69
+ * **Example:** Full-access token
70
+ * ```typescript
71
+ * const token = yield* Turso.DatabaseToken("Token", {
72
+ * database: db.name,
73
+ * });
74
+ * ```
75
+ *
76
+ * **Example:** Read-only token that expires after two weeks
77
+ * ```typescript
78
+ * const token = yield* Turso.DatabaseToken("ReadOnly", {
79
+ * database: db.name,
80
+ * authorization: "read-only",
81
+ * expiration: "2w",
82
+ * });
83
+ * ```
84
+ *
85
+ * @resource
86
+ * @product Database
87
+ */
88
+ export declare const DatabaseToken: import("alchemy/Resource").ResourceClass<DatabaseToken>;
89
+ /** Decode a Turso JWT's `exp` claim (seconds) to epoch milliseconds. */
90
+ export declare const tokenExpiry: (token: Redacted.Redacted<string>) => number | undefined;
91
+ /**
92
+ * `diff` for SQL tokens: an unchanged token that is about to expire still
93
+ * needs an update, so the deploy re-mints it.
94
+ */
95
+ export declare const diffSqlToken: (args_0: {
96
+ output: SqlTokenAttributes | undefined;
97
+ }) => Effect.Effect<{
98
+ readonly action: "update";
99
+ } | undefined, never, never>;
100
+ /** Whether a previously minted token can be kept as-is. */
101
+ export declare const isTokenCurrent: (output: SqlTokenAttributes | undefined, olds: SqlTokenProps | undefined, news: SqlTokenProps) => Effect.Effect<boolean, never, never>;
102
+ export declare const DatabaseTokenProvider: () => import("effect/Layer").Layer<Provider.Provider<DatabaseToken>, never, import("./Credentials.ts").Organization | turso.TursoOpContext>;
103
+ export {};
104
+ //# sourceMappingURL=DatabaseToken.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DatabaseToken.d.ts","sourceRoot":"","sources":["../src/DatabaseToken.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,8BAA8B,CAAC;AAEtD,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAG5C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,QAAQ,MAAM,iBAAiB,CAAC;AAE5C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;;;;AAEhD,qBAAa,+BAAgC,SAAQ,qCAEnD;IACA,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;CAC1B,CAAC;CAAG;AAEL,8CAA8C;AAC9C,MAAM,MAAM,kBAAkB,GAAG,aAAa,GAAG,WAAW,CAAC;AAE7D,oEAAoE;AACpE,MAAM,WAAW,aAAa;IAC5B;;;OAGG;IACH,aAAa,CAAC,EAAE,kBAAkB,CAAC;IACnC;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,yEAAyE;AACzE,MAAM,WAAW,kBAAkB;IACjC,kEAAkE;IAClE,KAAK,EAAE,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACjC,kDAAkD;IAClD,YAAY,EAAE,MAAM,CAAC;IACrB,yCAAyC;IACzC,aAAa,EAAE,kBAAkB,CAAC;IAClC,6EAA6E;IAC7E,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;CAC/B;AAED,MAAM,WAAW,kBAAmB,SAAQ,aAAa;IACvD,wEAAwE;IACxE,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,uBAAwB,SAAQ,kBAAkB;IACjE,uDAAuD;IACvD,QAAQ,EAAE,MAAM,CAAC;IACjB,qDAAqD;IACrD,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,MAAM,aAAa,GAAG,QAAQ,CAClC,qBAAqB,EACrB,kBAAkB,EAClB,uBAAuB,EACvB,KAAK,EACL,SAAS,CACV,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,eAAO,MAAM,aAAa,yDAAiD,CAAC;AAE5E,wEAAwE;AACxE,eAAO,MAAM,WAAW,UAAW,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAG,MAAM,GAAG,SAWvE,CAAC;AAKF;;;GAGG;AACH,eAAO,MAAM,YAAY;YAGf,kBAAkB,GAAG,SAAS;;qBAIwB,QAAQ;4BACtE,CAAC;AAEH,2DAA2D;AAC3D,eAAO,MAAM,cAAc,wIAkBzB,CAAC;AAEH,eAAO,MAAM,qBAAqB,6IAuD/B,CAAC"}
@@ -0,0 +1,133 @@
1
+ import * as turso from "@distilled.cloud/turso/turso";
2
+ import { deepEqual } from "alchemy/Diff";
3
+ import * as Provider from "alchemy/Provider";
4
+ import { Resource } from "alchemy/Resource";
5
+ import * as Clock from "effect/Clock";
6
+ import * as Data from "effect/Data";
7
+ import * as Effect from "effect/Effect";
8
+ import * as Redacted from "effect/Redacted";
9
+ import { organization } from "./Credentials.js";
10
+ export class DatabaseTokenUnexpectedResponse extends Data.TaggedError("DatabaseTokenUnexpectedResponse") {
11
+ }
12
+ /**
13
+ * A token that lets an app run SQL on one Turso database. Workers and
14
+ * Lambdas should use {@link Connect}, which creates one for you. Use
15
+ * `DatabaseToken` for anything else, such as a container or a CI job.
16
+ *
17
+ * You can't cancel a single token. Changing a prop creates a new token, but
18
+ * the old one keeps working until it expires. Destroying the resource doesn't
19
+ * stop it either. Give tokens you share an `expiration`, or cancel every token
20
+ * for the database with `turso db tokens invalidate`.
21
+ * @see https://docs.turso.tech/api-reference/databases/create-token
22
+ *
23
+ * ### Creating a Token
24
+ * **Example:** Full-access token
25
+ * ```typescript
26
+ * const token = yield* Turso.DatabaseToken("Token", {
27
+ * database: db.name,
28
+ * });
29
+ * ```
30
+ *
31
+ * **Example:** Read-only token that expires after two weeks
32
+ * ```typescript
33
+ * const token = yield* Turso.DatabaseToken("ReadOnly", {
34
+ * database: db.name,
35
+ * authorization: "read-only",
36
+ * expiration: "2w",
37
+ * });
38
+ * ```
39
+ *
40
+ * @resource
41
+ * @product Database
42
+ */
43
+ export const DatabaseToken = Resource("Turso.DatabaseToken");
44
+ /** Decode a Turso JWT's `exp` claim (seconds) to epoch milliseconds. */
45
+ export const tokenExpiry = (token) => {
46
+ const payload = Redacted.value(token).split(".")[1];
47
+ if (!payload)
48
+ return undefined;
49
+ try {
50
+ const claims = JSON.parse(Buffer.from(payload, "base64url").toString("utf8"));
51
+ return typeof claims.exp === "number" ? claims.exp * 1000 : undefined;
52
+ }
53
+ catch {
54
+ return undefined;
55
+ }
56
+ };
57
+ /** Re-mint a token that expires within the hour. */
58
+ const RENEW_WITHIN_MS = 60 * 60 * 1000;
59
+ /**
60
+ * `diff` for SQL tokens: an unchanged token that is about to expire still
61
+ * needs an update, so the deploy re-mints it.
62
+ */
63
+ export const diffSqlToken = Effect.fn(function* ({ output, }) {
64
+ if (output?.expiresAt === undefined)
65
+ return undefined;
66
+ const now = yield* Clock.currentTimeMillis;
67
+ return output.expiresAt - now <= RENEW_WITHIN_MS ? { action: "update" } : undefined;
68
+ });
69
+ /** Whether a previously minted token can be kept as-is. */
70
+ export const isTokenCurrent = Effect.fn(function* (output, olds, news) {
71
+ if (output === undefined || olds === undefined)
72
+ return false;
73
+ if ((news.authorization ?? "full-access") !== (olds.authorization ?? "full-access") ||
74
+ news.expiration !== olds.expiration ||
75
+ !deepEqual(news.readAttach ?? [], olds.readAttach ?? [])) {
76
+ return false;
77
+ }
78
+ if (output.expiresAt === undefined)
79
+ return true;
80
+ // Re-mint a token that expires within the hour, so a deploy never hands
81
+ // consumers a token about to lapse.
82
+ const now = yield* Clock.currentTimeMillis;
83
+ return output.expiresAt - now > RENEW_WITHIN_MS;
84
+ });
85
+ export const DatabaseTokenProvider = () => Provider.effect(DatabaseToken, Effect.gen(function* () {
86
+ return {
87
+ stables: ["organization"],
88
+ diff: diffSqlToken,
89
+ reconcile: Effect.fn(function* ({ news, olds, output }) {
90
+ const org = output?.organization ?? (yield* organization);
91
+ // Observe — the token is bound to the database's UUID, so a
92
+ // database replaced under the same name invalidates it.
93
+ const { database } = yield* turso.getDatabase({
94
+ organizationSlug: org,
95
+ databaseName: news.database,
96
+ });
97
+ const dbId = database?.DbId ?? "";
98
+ if (output !== undefined &&
99
+ output.database === news.database &&
100
+ output.dbId === dbId &&
101
+ (yield* isTokenCurrent(output, olds, news))) {
102
+ return output;
103
+ }
104
+ // Ensure — mint.
105
+ const authorization = news.authorization ?? "full-access";
106
+ const { jwt } = yield* turso.createDatabaseToken({
107
+ organizationSlug: org,
108
+ databaseName: news.database,
109
+ authorization,
110
+ expiration: news.expiration,
111
+ permissions: news.readAttach
112
+ ? { read_attach: { databases: news.readAttach } }
113
+ : undefined,
114
+ });
115
+ if (!jwt) {
116
+ return yield* new DatabaseTokenUnexpectedResponse({
117
+ message: `Turso returned no token for ${news.database}`,
118
+ });
119
+ }
120
+ return {
121
+ token: jwt,
122
+ organization: org,
123
+ authorization,
124
+ expiresAt: tokenExpiry(jwt),
125
+ database: news.database,
126
+ dbId,
127
+ };
128
+ }),
129
+ // Turso cannot revoke a single token; it simply stops being tracked.
130
+ delete: () => Effect.void,
131
+ };
132
+ }));
133
+ //# sourceMappingURL=DatabaseToken.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DatabaseToken.js","sourceRoot":"","sources":["../src/DatabaseToken.ts"],"names":[],"mappings":"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"}
package/lib/Group.d.ts ADDED
@@ -0,0 +1,102 @@
1
+ import * as turso from "@distilled.cloud/turso/turso";
2
+ import * as Provider from "alchemy/Provider";
3
+ import { Resource } from "alchemy/Resource";
4
+ import type { Providers } from "./Providers.ts";
5
+ declare const GroupUnexpectedResponse_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
6
+ readonly _tag: "GroupUnexpectedResponse";
7
+ } & Readonly<A>;
8
+ export declare class GroupUnexpectedResponse extends GroupUnexpectedResponse_base<{
9
+ readonly message: string;
10
+ readonly cause?: unknown;
11
+ }> {
12
+ }
13
+ /**
14
+ * A Turso location code, e.g. `"aws-us-east-1"`. List the locations
15
+ * available to your organization with `turso db locations`.
16
+ */
17
+ export type Location = "aws-ap-northeast-1" | "aws-ap-south-1" | "aws-ap-southeast-2" | "aws-ca-central-1" | "aws-eu-north-1" | "aws-eu-west-1" | "aws-sa-east-1" | "aws-us-east-1" | "aws-us-east-2" | "aws-us-west-2" | (string & {});
18
+ export interface GroupProps {
19
+ /**
20
+ * Group name, unique within the organization. Lowercase letters,
21
+ * numbers, and dashes. If omitted, a unique name is generated from the
22
+ * stack, stage, and logical ID. Changing it replaces the group.
23
+ */
24
+ name?: string;
25
+ /**
26
+ * Primary location of the group. Every database in the group lives here.
27
+ * Changing it replaces the group (and every database in it).
28
+ * @default "aws-us-east-1"
29
+ */
30
+ location?: Location;
31
+ /**
32
+ * Additional replica locations. Reconciled in place by adding and
33
+ * removing locations. AWS-hosted groups do not support replication, so
34
+ * this only applies to organizations with legacy Fly-hosted groups.
35
+ */
36
+ replicas?: Location[];
37
+ /**
38
+ * SQLite extensions enabled for the group's databases: `"all"`, or a list
39
+ * of extension names. Set at creation; changing it replaces the group.
40
+ */
41
+ extensions?: "all" | string[];
42
+ /**
43
+ * Prevent the group (and its databases) from being deleted. While
44
+ * enabled, destroying or replacing the group fails; set it to `false`
45
+ * and deploy before deleting.
46
+ * @default false
47
+ */
48
+ deleteProtection?: boolean;
49
+ }
50
+ export interface GroupAttributes {
51
+ /** Group name. */
52
+ name: string;
53
+ /** Group UUID. */
54
+ uuid: string;
55
+ /** The organization slug that owns the group. */
56
+ organization: string;
57
+ /** Primary location of the group. */
58
+ location: string;
59
+ /** Every location the group runs in (primary first). */
60
+ locations: string[];
61
+ /** The libSQL server version the group's databases run. */
62
+ version: string;
63
+ /** Whether delete protection is enabled. */
64
+ deleteProtection: boolean;
65
+ }
66
+ export type Group = Resource<"Turso.Group", GroupProps, GroupAttributes, never, Providers>;
67
+ /**
68
+ * A Turso group. Every Turso database belongs to one group, and the group
69
+ * decides where its databases run.
70
+ * @see https://docs.turso.tech/concepts#groups
71
+ *
72
+ * ### Creating a Group
73
+ * **Example:** Group in a single location
74
+ * ```typescript
75
+ * const group = yield* Turso.Group("Group", {
76
+ * location: "aws-us-east-1",
77
+ * });
78
+ * ```
79
+ *
80
+ * **Example:** Protected group with all extensions enabled
81
+ * ```typescript
82
+ * const group = yield* Turso.Group("Group", {
83
+ * name: "production",
84
+ * location: "aws-eu-west-1",
85
+ * extensions: "all",
86
+ * deleteProtection: true,
87
+ * });
88
+ * ```
89
+ *
90
+ * ### Adding databases
91
+ * **Example:** Create a database in the group
92
+ * ```typescript
93
+ * const db = yield* Turso.Database("Db", { group: group.name });
94
+ * ```
95
+ *
96
+ * @resource
97
+ * @product Group
98
+ */
99
+ export declare const Group: import("alchemy/Resource").ResourceClass<Group>;
100
+ export declare const GroupProvider: () => import("effect/Layer").Layer<Provider.Provider<Group>, never, import("./Credentials.ts").Organization | import("alchemy/Stack").Stack | import("alchemy/Stage").Stage | turso.TursoOpContext>;
101
+ export {};
102
+ //# sourceMappingURL=Group.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Group.d.ts","sourceRoot":"","sources":["../src/Group.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,8BAA8B,CAAC;AAItD,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAI5C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;;;;AAEhD,qBAAa,uBAAwB,SAAQ,6BAA4C;IACvF,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;CAC1B,CAAC;CAAG;AAEL;;;GAGG;AACH,MAAM,MAAM,QAAQ,GAChB,oBAAoB,GACpB,gBAAgB,GAChB,oBAAoB,GACpB,kBAAkB,GAClB,gBAAgB,GAChB,eAAe,GACf,eAAe,GACf,eAAe,GACf,eAAe,GACf,eAAe,GACf,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;AAElB,MAAM,WAAW,UAAU;IACzB;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,QAAQ,EAAE,CAAC;IACtB;;;OAGG;IACH,UAAU,CAAC,EAAE,KAAK,GAAG,MAAM,EAAE,CAAC;IAC9B;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,eAAe;IAC9B,kBAAkB;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,kBAAkB;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,iDAAiD;IACjD,YAAY,EAAE,MAAM,CAAC;IACrB,qCAAqC;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,wDAAwD;IACxD,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,2DAA2D;IAC3D,OAAO,EAAE,MAAM,CAAC;IAChB,4CAA4C;IAC5C,gBAAgB,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,MAAM,KAAK,GAAG,QAAQ,CAAC,aAAa,EAAE,UAAU,EAAE,eAAe,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;AAE3F;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,eAAO,MAAM,KAAK,iDAAiC,CAAC;AAIpD,eAAO,MAAM,aAAa,qMAmJvB,CAAC"}