@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
package/lib/Group.js ADDED
@@ -0,0 +1,171 @@
1
+ import * as turso from "@distilled.cloud/turso/turso";
2
+ import { Unowned } from "alchemy/AdoptPolicy";
3
+ import { deepEqual, isResolved } from "alchemy/Diff";
4
+ import { createPhysicalName } from "alchemy/PhysicalName";
5
+ import * as Provider from "alchemy/Provider";
6
+ import { Resource } from "alchemy/Resource";
7
+ import * as Data from "effect/Data";
8
+ import * as Effect from "effect/Effect";
9
+ import { organization } from "./Credentials.js";
10
+ export class GroupUnexpectedResponse extends Data.TaggedError("GroupUnexpectedResponse") {
11
+ }
12
+ /**
13
+ * A Turso group. Every Turso database belongs to one group, and the group
14
+ * decides where its databases run.
15
+ * @see https://docs.turso.tech/concepts#groups
16
+ *
17
+ * ### Creating a Group
18
+ * **Example:** Group in a single location
19
+ * ```typescript
20
+ * const group = yield* Turso.Group("Group", {
21
+ * location: "aws-us-east-1",
22
+ * });
23
+ * ```
24
+ *
25
+ * **Example:** Protected group with all extensions enabled
26
+ * ```typescript
27
+ * const group = yield* Turso.Group("Group", {
28
+ * name: "production",
29
+ * location: "aws-eu-west-1",
30
+ * extensions: "all",
31
+ * deleteProtection: true,
32
+ * });
33
+ * ```
34
+ *
35
+ * ### Adding databases
36
+ * **Example:** Create a database in the group
37
+ * ```typescript
38
+ * const db = yield* Turso.Database("Db", { group: group.name });
39
+ * ```
40
+ *
41
+ * @resource
42
+ * @product Group
43
+ */
44
+ export const Group = Resource("Turso.Group");
45
+ const DEFAULT_LOCATION = "aws-us-east-1";
46
+ export const GroupProvider = () => Provider.effect(Group, Effect.gen(function* () {
47
+ const groupName = (id, name) => name
48
+ ? Effect.succeed(name)
49
+ : createPhysicalName({ id, lowercase: true, maxLength: 32, delimiter: "-" });
50
+ const observe = (org, name) => turso.getGroup({ organizationSlug: org, groupName: name }).pipe(Effect.map((res) => res.group), Effect.catchTag("NotFound", () => Effect.undefined));
51
+ const toAttrs = (org, group, deleteProtection) => ({
52
+ name: group.name,
53
+ uuid: group.uuid ?? "",
54
+ organization: org,
55
+ location: group.primary ?? "",
56
+ locations: group.locations ?? [],
57
+ version: group.version ?? "",
58
+ deleteProtection,
59
+ });
60
+ return {
61
+ stables: ["name", "uuid", "organization", "location"],
62
+ diff: Effect.fn(function* ({ id, olds, news, output }) {
63
+ if (!isResolved(news))
64
+ return undefined;
65
+ const name = output?.name ?? (yield* groupName(id, olds?.name));
66
+ if (news.name !== undefined && news.name !== name) {
67
+ return { action: "replace" };
68
+ }
69
+ if ((news.location ?? DEFAULT_LOCATION) !== (olds?.location ?? DEFAULT_LOCATION)) {
70
+ return { action: "replace", deleteFirst: news.name !== undefined };
71
+ }
72
+ if (!deepEqual(news.extensions, olds?.extensions)) {
73
+ return { action: "replace", deleteFirst: news.name !== undefined };
74
+ }
75
+ }),
76
+ read: Effect.fn(function* ({ id, olds, output, previous }) {
77
+ const org = output?.organization ?? (yield* organization);
78
+ const name = output?.name ?? (yield* groupName(id, olds?.name));
79
+ const group = yield* observe(org, name);
80
+ if (!group)
81
+ return undefined;
82
+ const attrs = toAttrs(org, group, group.delete_protection ?? false);
83
+ // Turso has no tags: a group found under an explicit, user-chosen
84
+ // name without prior state may belong to someone else — unless it
85
+ // is the generation a delete-first replacement is about to free.
86
+ const ours = output !== undefined ||
87
+ olds?.name === undefined ||
88
+ (previous?.organization === org && previous.name === name);
89
+ return ours ? attrs : Unowned(attrs);
90
+ }),
91
+ reconcile: Effect.fn(function* ({ id, news, output, session }) {
92
+ const org = output?.organization ?? (yield* organization);
93
+ const name = output?.name ?? (yield* groupName(id, news.name));
94
+ const location = news.location ?? DEFAULT_LOCATION;
95
+ // Observe
96
+ let group = yield* observe(org, name);
97
+ // Ensure
98
+ if (!group) {
99
+ yield* session.note(`Creating group ${name}...`);
100
+ group = yield* turso
101
+ .createGroup({
102
+ organizationSlug: org,
103
+ name,
104
+ location,
105
+ extensions: news.extensions,
106
+ })
107
+ .pipe(Effect.map((res) => res.group), Effect.catchTag("Conflict", () => observe(org, name)));
108
+ if (!group) {
109
+ return yield* new GroupUnexpectedResponse({
110
+ message: `Turso group ${name} vanished after create`,
111
+ });
112
+ }
113
+ }
114
+ // Sync replica locations against the observed set.
115
+ const primary = group.primary ?? location;
116
+ const desired = new Set([primary, ...(news.replicas ?? [])]);
117
+ const observed = new Set(group.locations ?? [primary]);
118
+ for (const loc of desired) {
119
+ if (!observed.has(loc)) {
120
+ yield* session.note(`Adding location ${loc}...`);
121
+ group =
122
+ (yield* turso.addLocationToGroup({
123
+ organizationSlug: org,
124
+ groupName: name,
125
+ location: loc,
126
+ })).group ?? group;
127
+ }
128
+ }
129
+ for (const loc of observed) {
130
+ if (!desired.has(loc)) {
131
+ yield* session.note(`Removing location ${loc}...`);
132
+ group =
133
+ (yield* turso.removeLocationFromGroup({
134
+ organizationSlug: org,
135
+ groupName: name,
136
+ location: loc,
137
+ })).group ?? group;
138
+ }
139
+ }
140
+ // Sync configuration.
141
+ const config = yield* turso.getGroupConfiguration({
142
+ organizationSlug: org,
143
+ groupName: name,
144
+ });
145
+ let deleteProtection = config.delete_protection ?? false;
146
+ const desiredProtection = news.deleteProtection ?? false;
147
+ if (deleteProtection !== desiredProtection) {
148
+ const updated = yield* turso.updateGroupConfiguration({
149
+ organizationSlug: org,
150
+ groupName: name,
151
+ delete_protection: desiredProtection,
152
+ });
153
+ deleteProtection = updated.delete_protection ?? desiredProtection;
154
+ }
155
+ return toAttrs(org, group, deleteProtection);
156
+ }),
157
+ delete: Effect.fn(function* ({ output }) {
158
+ yield* turso
159
+ .deleteGroup({ organizationSlug: output.organization, groupName: output.name })
160
+ .pipe(Effect.catchTag("NotFound", () => Effect.void));
161
+ }),
162
+ list: Effect.fn(function* () {
163
+ const org = yield* organization;
164
+ const { groups } = yield* turso.listGroups({ organizationSlug: org });
165
+ return (groups ?? [])
166
+ .filter((group) => group.name !== undefined)
167
+ .map((group) => toAttrs(org, group, group.delete_protection ?? false));
168
+ }),
169
+ };
170
+ }));
171
+ //# sourceMappingURL=Group.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Group.js","sourceRoot":"","sources":["../src/Group.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,57 @@
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 SqlTokenAttributes, type SqlTokenProps } from "./DatabaseToken.ts";
5
+ import type { Providers } from "./Providers.ts";
6
+ declare const GroupTokenUnexpectedResponse_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 & {
7
+ readonly _tag: "GroupTokenUnexpectedResponse";
8
+ } & Readonly<A>;
9
+ export declare class GroupTokenUnexpectedResponse extends GroupTokenUnexpectedResponse_base<{
10
+ readonly message: string;
11
+ readonly cause?: unknown;
12
+ }> {
13
+ }
14
+ export interface GroupTokenProps extends SqlTokenProps {
15
+ /** Name of the group whose databases the token grants access to. */
16
+ group: string;
17
+ }
18
+ export interface GroupTokenAttributes extends SqlTokenAttributes {
19
+ /** Name of the group the token grants access to. */
20
+ group: string;
21
+ /** UUID of the group the token was minted for. */
22
+ groupId: string;
23
+ }
24
+ export type GroupToken = Resource<"Turso.GroupToken", GroupTokenProps, GroupTokenAttributes, never, Providers>;
25
+ /**
26
+ * A token that lets an app run SQL on every database in a Turso
27
+ * {@link Group}, even databases created later. Useful when each customer
28
+ * has their own database.
29
+ *
30
+ * Like {@link DatabaseToken}, you can't cancel a single group token. Changing
31
+ * a prop creates a new token, but the old one keeps working until it expires.
32
+ * @see https://docs.turso.tech/api-reference/groups/create-token
33
+ *
34
+ * ### Creating a Token
35
+ * **Example:** Full-access token for every database in a group
36
+ * ```typescript
37
+ * const token = yield* Turso.GroupToken("Token", {
38
+ * group: group.name,
39
+ * });
40
+ * ```
41
+ *
42
+ * **Example:** Read-only token that expires after a week
43
+ * ```typescript
44
+ * const token = yield* Turso.GroupToken("ReadOnly", {
45
+ * group: group.name,
46
+ * authorization: "read-only",
47
+ * expiration: "7d",
48
+ * });
49
+ * ```
50
+ *
51
+ * @resource
52
+ * @product Group
53
+ */
54
+ export declare const GroupToken: import("alchemy/Resource").ResourceClass<GroupToken>;
55
+ export declare const GroupTokenProvider: () => import("effect/Layer").Layer<Provider.Provider<GroupToken>, never, import("./Credentials.ts").Organization | turso.TursoOpContext>;
56
+ export {};
57
+ //# sourceMappingURL=GroupToken.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GroupToken.d.ts","sourceRoot":"","sources":["../src/GroupToken.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,8BAA8B,CAAC;AACtD,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAI5C,OAAO,EAIL,KAAK,kBAAkB,EACvB,KAAK,aAAa,EACnB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;;;;AAEhD,qBAAa,4BAA6B,SAAQ,kCAAiD;IACjG,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;CAC1B,CAAC;CAAG;AAEL,MAAM,WAAW,eAAgB,SAAQ,aAAa;IACpD,oEAAoE;IACpE,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,oBAAqB,SAAQ,kBAAkB;IAC9D,oDAAoD;IACpD,KAAK,EAAE,MAAM,CAAC;IACd,kDAAkD;IAClD,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,UAAU,GAAG,QAAQ,CAC/B,kBAAkB,EAClB,eAAe,EACf,oBAAoB,EACpB,KAAK,EACL,SAAS,CACV,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,UAAU,sDAA2C,CAAC;AAEnE,eAAO,MAAM,kBAAkB,0IAuD5B,CAAC"}
@@ -0,0 +1,88 @@
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 Data from "effect/Data";
5
+ import * as Effect from "effect/Effect";
6
+ import { organization } from "./Credentials.js";
7
+ import { diffSqlToken, isTokenCurrent, tokenExpiry, } from "./DatabaseToken.js";
8
+ export class GroupTokenUnexpectedResponse extends Data.TaggedError("GroupTokenUnexpectedResponse") {
9
+ }
10
+ /**
11
+ * A token that lets an app run SQL on every database in a Turso
12
+ * {@link Group}, even databases created later. Useful when each customer
13
+ * has their own database.
14
+ *
15
+ * Like {@link DatabaseToken}, you can't cancel a single group token. Changing
16
+ * a prop creates a new token, but the old one keeps working until it expires.
17
+ * @see https://docs.turso.tech/api-reference/groups/create-token
18
+ *
19
+ * ### Creating a Token
20
+ * **Example:** Full-access token for every database in a group
21
+ * ```typescript
22
+ * const token = yield* Turso.GroupToken("Token", {
23
+ * group: group.name,
24
+ * });
25
+ * ```
26
+ *
27
+ * **Example:** Read-only token that expires after a week
28
+ * ```typescript
29
+ * const token = yield* Turso.GroupToken("ReadOnly", {
30
+ * group: group.name,
31
+ * authorization: "read-only",
32
+ * expiration: "7d",
33
+ * });
34
+ * ```
35
+ *
36
+ * @resource
37
+ * @product Group
38
+ */
39
+ export const GroupToken = Resource("Turso.GroupToken");
40
+ export const GroupTokenProvider = () => Provider.effect(GroupToken, Effect.gen(function* () {
41
+ return {
42
+ stables: ["organization"],
43
+ diff: diffSqlToken,
44
+ reconcile: Effect.fn(function* ({ news, olds, output }) {
45
+ const org = output?.organization ?? (yield* organization);
46
+ // Observe — the token is bound to the group's UUID, so a group
47
+ // replaced under the same name invalidates it.
48
+ const { group } = yield* turso.getGroup({
49
+ organizationSlug: org,
50
+ groupName: news.group,
51
+ });
52
+ const groupId = group?.uuid ?? "";
53
+ if (output !== undefined &&
54
+ output.group === news.group &&
55
+ output.groupId === groupId &&
56
+ (yield* isTokenCurrent(output, olds, news))) {
57
+ return output;
58
+ }
59
+ // Ensure — mint.
60
+ const authorization = news.authorization ?? "full-access";
61
+ const { jwt } = yield* turso.createGroupToken({
62
+ organizationSlug: org,
63
+ groupName: news.group,
64
+ authorization,
65
+ expiration: news.expiration,
66
+ permissions: news.readAttach
67
+ ? { read_attach: { databases: news.readAttach } }
68
+ : undefined,
69
+ });
70
+ if (!jwt) {
71
+ return yield* new GroupTokenUnexpectedResponse({
72
+ message: `Turso returned no token for group ${news.group}`,
73
+ });
74
+ }
75
+ return {
76
+ token: jwt,
77
+ organization: org,
78
+ authorization,
79
+ expiresAt: tokenExpiry(jwt),
80
+ group: news.group,
81
+ groupId,
82
+ };
83
+ }),
84
+ // Turso cannot revoke a single token; it simply stops being tracked.
85
+ delete: () => Effect.void,
86
+ };
87
+ }));
88
+ //# sourceMappingURL=GroupToken.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GroupToken.js","sourceRoot":"","sources":["../src/GroupToken.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,8BAA8B,CAAC;AACtD,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,KAAK,IAAI,MAAM,aAAa,CAAC;AACpC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EACL,YAAY,EACZ,cAAc,EACd,WAAW,GAGZ,MAAM,oBAAoB,CAAC;AAG5B,MAAM,OAAO,4BAA6B,SAAQ,IAAI,CAAC,WAAW,CAAC,8BAA8B,CAG/F;CAAG;AAsBL;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,QAAQ,CAAa,kBAAkB,CAAC,CAAC;AAEnE,MAAM,CAAC,MAAM,kBAAkB,GAAG,GAAG,EAAE,CACrC,QAAQ,CAAC,MAAM,CACb,UAAU,EACV,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,OAAO;QACL,OAAO,EAAE,CAAC,cAAc,CAAC;QACzB,IAAI,EAAE,YAAY;QAClB,SAAS,EAAE,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE;YACpD,MAAM,GAAG,GAAG,MAAM,EAAE,YAAY,IAAI,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC;YAE1D,+DAA+D;YAC/D,+CAA+C;YAC/C,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC;gBACtC,gBAAgB,EAAE,GAAG;gBACrB,SAAS,EAAE,IAAI,CAAC,KAAK;aACtB,CAAC,CAAC;YACH,MAAM,OAAO,GAAG,KAAK,EAAE,IAAI,IAAI,EAAE,CAAC;YAClC,IACE,MAAM,KAAK,SAAS;gBACpB,MAAM,CAAC,KAAK,KAAK,IAAI,CAAC,KAAK;gBAC3B,MAAM,CAAC,OAAO,KAAK,OAAO;gBAC1B,CAAC,KAAK,CAAC,CAAC,cAAc,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,EAC3C,CAAC;gBACD,OAAO,MAAM,CAAC;YAChB,CAAC;YAED,iBAAiB;YACjB,MAAM,aAAa,GAAG,IAAI,CAAC,aAAa,IAAI,aAAa,CAAC;YAC1D,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,CAAC,CAAC,KAAK,CAAC,gBAAgB,CAAC;gBAC5C,gBAAgB,EAAE,GAAG;gBACrB,SAAS,EAAE,IAAI,CAAC,KAAK;gBACrB,aAAa;gBACb,UAAU,EAAE,IAAI,CAAC,UAAU;gBAC3B,WAAW,EAAE,IAAI,CAAC,UAAU;oBAC1B,CAAC,CAAC,EAAE,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,UAAU,EAAE,EAAE;oBACjD,CAAC,CAAC,SAAS;aACd,CAAC,CAAC;YACH,IAAI,CAAC,GAAG,EAAE,CAAC;gBACT,OAAO,KAAK,CAAC,CAAC,IAAI,4BAA4B,CAAC;oBAC7C,OAAO,EAAE,qCAAqC,IAAI,CAAC,KAAK,EAAE;iBAC3D,CAAC,CAAC;YACL,CAAC;YACD,OAAO;gBACL,KAAK,EAAE,GAAG;gBACV,YAAY,EAAE,GAAG;gBACjB,aAAa;gBACb,SAAS,EAAE,WAAW,CAAC,GAAG,CAAC;gBAC3B,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,OAAO;aACR,CAAC;QACJ,CAAC,CAAC;QACF,qEAAqE;QACrE,MAAM,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI;KAC1B,CAAC;AACJ,CAAC,CAAC,CACH,CAAC"}
package/lib/Hrana.d.ts ADDED
@@ -0,0 +1,42 @@
1
+ import { type SqlExecutor } from "@alchemy.run/sql/Migrations";
2
+ import * as Effect from "effect/Effect";
3
+ import * as HttpClient from "effect/http/HttpClient";
4
+ import * as Redacted from "effect/Redacted";
5
+ declare const HranaError_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: "Turso::HranaError";
7
+ } & Readonly<A>;
8
+ /**
9
+ * A minimal Hrana-over-HTTP client (`POST /v2/pipeline`) — the SQL wire
10
+ * protocol every Turso database speaks. Deploy-time work (migrations, SQL
11
+ * imports) uses it instead of `@libsql/client`, so the provider needs no
12
+ * driver and runs anywhere `HttpClient` does.
13
+ *
14
+ * @see https://github.com/tursodatabase/libsql/blob/main/docs/HRANA_3_SPEC.md
15
+ */
16
+ export declare class HranaError extends HranaError_base<{
17
+ message: string;
18
+ code?: string;
19
+ cause?: unknown;
20
+ }> {
21
+ }
22
+ /** Connection details for one Turso database. */
23
+ export interface HranaConnection {
24
+ /** `https://<hostname>` of the database. */
25
+ url: string;
26
+ /** A database auth token (JWT). */
27
+ authToken: Redacted.Redacted<string>;
28
+ }
29
+ /** Execute one statement and return its rows as objects. */
30
+ export declare const query: (connection: HranaConnection, sql: string, params?: ReadonlyArray<unknown>) => Effect.Effect<Array<Record<string, unknown>>, HranaError, HttpClient.HttpClient>;
31
+ /**
32
+ * Execute statements atomically. Each entry may hold several
33
+ * `;`-separated statements (a whole migration file or seed script), so they
34
+ * run as one Hrana `sequence` wrapped in `BEGIN`/`COMMIT`. A failing
35
+ * statement stops the sequence before `COMMIT`, and closing the stream
36
+ * rolls the open transaction back.
37
+ */
38
+ export declare const transaction: (connection: HranaConnection, statements: ReadonlyArray<string>) => Effect.Effect<void, HranaError, HttpClient.HttpClient>;
39
+ /** Adapt a Turso database connection into the migration registry's executor. */
40
+ export declare const makeMigrationExecutor: (connection: HranaConnection, client: HttpClient.HttpClient) => SqlExecutor;
41
+ export {};
42
+ //# sourceMappingURL=Hrana.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Hrana.d.ts","sourceRoot":"","sources":["../src/Hrana.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAG/E,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,UAAU,MAAM,wBAAwB,CAAC;AAErD,OAAO,KAAK,QAAQ,MAAM,iBAAiB,CAAC;;;;AAE5C;;;;;;;GAOG;AACH,qBAAa,UAAW,SAAQ,gBAAsC;IACpE,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;CAAG;AAsEL,iDAAiD;AACjD,MAAM,WAAW,eAAe;IAC9B,4CAA4C;IAC5C,GAAG,EAAE,MAAM,CAAC;IACZ,mCAAmC;IACnC,SAAS,EAAE,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;CACtC;AAyBD,4DAA4D;AAC5D,eAAO,MAAM,KAAK,eACJ,eAAe,OACtB,MAAM,WACF,aAAa,CAAC,OAAO,CAAC,KAC9B,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAAE,UAAU,EAAE,UAAU,CAAC,UAAU,CAW9E,CAAC;AAEL;;;;;;GAMG;AACH,eAAO,MAAM,WAAW,eACV,eAAe,cACf,aAAa,CAAC,MAAM,CAAC,KAChC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,UAAU,CAAC,UAAU,CAYpD,CAAC;AAEL,gFAAgF;AAChF,eAAO,MAAM,qBAAqB,eACpB,eAAe,UACnB,UAAU,CAAC,UAAU,KAC5B,WAgBF,CAAC"}
package/lib/Hrana.js ADDED
@@ -0,0 +1,109 @@
1
+ import { MigrationError } from "@alchemy.run/sql/Migrations";
2
+ import { splitSqlStatements } from "@alchemy.run/sql/SqlFile";
3
+ import * as Data from "effect/Data";
4
+ import * as Effect from "effect/Effect";
5
+ import * as HttpClient from "effect/http/HttpClient";
6
+ import * as HttpClientRequest from "effect/http/HttpClientRequest";
7
+ import * as Redacted from "effect/Redacted";
8
+ /**
9
+ * A minimal Hrana-over-HTTP client (`POST /v2/pipeline`) — the SQL wire
10
+ * protocol every Turso database speaks. Deploy-time work (migrations, SQL
11
+ * imports) uses it instead of `@libsql/client`, so the provider needs no
12
+ * driver and runs anywhere `HttpClient` does.
13
+ *
14
+ * @see https://github.com/tursodatabase/libsql/blob/main/docs/HRANA_3_SPEC.md
15
+ */
16
+ export class HranaError extends Data.TaggedError("Turso::HranaError") {
17
+ }
18
+ const toHranaValue = (value) => {
19
+ if (value === null || value === undefined)
20
+ return { type: "null" };
21
+ if (typeof value === "bigint")
22
+ return { type: "integer", value: value.toString() };
23
+ if (typeof value === "number") {
24
+ return Number.isInteger(value)
25
+ ? { type: "integer", value: value.toString() }
26
+ : { type: "float", value };
27
+ }
28
+ if (typeof value === "boolean")
29
+ return { type: "integer", value: value ? "1" : "0" };
30
+ if (value instanceof Uint8Array) {
31
+ return { type: "blob", base64: Buffer.from(value).toString("base64") };
32
+ }
33
+ if (typeof value === "string")
34
+ return { type: "text", value };
35
+ if (value instanceof Date)
36
+ return { type: "text", value: value.toISOString() };
37
+ return { type: "text", value: JSON.stringify(value) };
38
+ };
39
+ const fromHranaValue = (value) => {
40
+ switch (value.type) {
41
+ case "null":
42
+ return null;
43
+ case "integer":
44
+ return Number(value.value);
45
+ case "float":
46
+ return value.value;
47
+ case "text":
48
+ return value.value;
49
+ case "blob":
50
+ return Buffer.from(value.base64, "base64");
51
+ }
52
+ };
53
+ const toRows = (result) => result.rows.map((row) => Object.fromEntries(result.cols.map((col, i) => [col.name ?? String(i), fromHranaValue(row[i])])));
54
+ const pipeline = (connection, requests) => Effect.gen(function* () {
55
+ const client = yield* HttpClient.HttpClient;
56
+ const response = yield* client
57
+ .execute(HttpClientRequest.post(`${connection.url.replace(/\/$/, "")}/v2/pipeline`).pipe(HttpClientRequest.bearerToken(Redacted.value(connection.authToken)), HttpClientRequest.bodyJsonUnsafe({ requests: [...requests, { type: "close" }] })))
58
+ .pipe(Effect.mapError((cause) => new HranaError({ message: String(cause), cause })));
59
+ if (response.status < 200 || response.status >= 300) {
60
+ const text = yield* response.text.pipe(Effect.orElseSucceed(() => ""));
61
+ return yield* new HranaError({
62
+ message: `Turso SQL request failed with HTTP ${response.status}: ${text}`,
63
+ });
64
+ }
65
+ const body = (yield* response.json.pipe(Effect.mapError((cause) => new HranaError({ message: String(cause), cause }))));
66
+ return body.results.slice(0, requests.length);
67
+ });
68
+ /** Execute one statement and return its rows as objects. */
69
+ export const query = (connection, sql, params) => Effect.gen(function* () {
70
+ const stmt = { sql, args: (params ?? []).map(toHranaValue) };
71
+ const [result] = yield* pipeline(connection, [{ type: "execute", stmt }]);
72
+ if (result === undefined) {
73
+ return yield* new HranaError({ message: "Turso returned no result" });
74
+ }
75
+ if (result.type === "error") {
76
+ return yield* new HranaError({ message: result.error.message, code: result.error.code });
77
+ }
78
+ return toRows(result.response.result);
79
+ });
80
+ /**
81
+ * Execute statements atomically. Each entry may hold several
82
+ * `;`-separated statements (a whole migration file or seed script), so they
83
+ * run as one Hrana `sequence` wrapped in `BEGIN`/`COMMIT`. A failing
84
+ * statement stops the sequence before `COMMIT`, and closing the stream
85
+ * rolls the open transaction back.
86
+ */
87
+ export const transaction = (connection, statements) => Effect.gen(function* () {
88
+ const body = statements.flatMap(splitSqlStatements);
89
+ if (body.length === 0)
90
+ return;
91
+ const sql = ["BEGIN", ...body.map((s) => s.replace(/;\s*$/, "")), "COMMIT"].join(";\n");
92
+ const [result] = yield* pipeline(connection, [{ type: "sequence", sql }]);
93
+ if (result === undefined) {
94
+ return yield* new HranaError({ message: "Turso returned no result" });
95
+ }
96
+ if (result.type === "error") {
97
+ return yield* new HranaError({ message: result.error.message, code: result.error.code });
98
+ }
99
+ });
100
+ /** Adapt a Turso database connection into the migration registry's executor. */
101
+ export const makeMigrationExecutor = (connection, client) => {
102
+ const toMigrationError = (error) => new MigrationError({ message: `turso: ${error.message}`, cause: error });
103
+ return {
104
+ dialect: "sqlite",
105
+ query: (sql, params) => query(connection, sql, params).pipe(Effect.provideService(HttpClient.HttpClient, client), Effect.mapError(toMigrationError)),
106
+ batch: (statements) => transaction(connection, statements).pipe(Effect.provideService(HttpClient.HttpClient, client), Effect.mapError(toMigrationError)),
107
+ };
108
+ };
109
+ //# sourceMappingURL=Hrana.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Hrana.js","sourceRoot":"","sources":["../src/Hrana.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,73 @@
1
+ import * as turso from "@distilled.cloud/turso/turso";
2
+ import * as Binding from "alchemy/Binding";
3
+ import type { RuntimeContext } from "alchemy/RuntimeContext";
4
+ import * as Effect from "effect/Effect";
5
+ import * as Layer from "effect/Layer";
6
+ import type { ConnectClient } from "./Connect.ts";
7
+ import type { DatabaseSeed } from "./Database.ts";
8
+ import type { Group } from "./Group.ts";
9
+ /** A database in the managed group. */
10
+ export interface ManagedDatabase {
11
+ /** Database name. */
12
+ name: string;
13
+ /** Database UUID. */
14
+ dbId: string;
15
+ /** DNS hostname of the database. */
16
+ hostname: string;
17
+ /** libSQL URL (`libsql://<hostname>`). */
18
+ url: string;
19
+ }
20
+ export interface CreateManagedDatabaseOptions {
21
+ /** Seed the new database from an existing database (e.g. a template). */
22
+ seed?: DatabaseSeed;
23
+ /** Maximum size of the database, e.g. `"256mb"`. */
24
+ sizeLimit?: string;
25
+ }
26
+ export interface ManageDatabasesClient {
27
+ /** Create a database in the group. */
28
+ create(name: string, options?: CreateManagedDatabaseOptions): Effect.Effect<ManagedDatabase, turso.CreateDatabaseError, RuntimeContext>;
29
+ /** Look up a database by name; `undefined` when it doesn't exist. */
30
+ get(name: string): Effect.Effect<ManagedDatabase | undefined, turso.GetDatabaseError, RuntimeContext>;
31
+ /** List every database in the group. */
32
+ list(): Effect.Effect<ManagedDatabase[], turso.ListDatabasesError, RuntimeContext>;
33
+ /** Delete a database. Deleting a missing database succeeds. */
34
+ delete(name: string): Effect.Effect<void, turso.DeleteDatabaseError, RuntimeContext>;
35
+ /**
36
+ * libSQL connection accessors for a database in the group, authorized by
37
+ * a group-wide SQL token. Pass to `SQL.LibSQL` or `Drizzle.LibSQL`.
38
+ */
39
+ connect(name: string): ConnectClient;
40
+ }
41
+ /**
42
+ * Let a Worker create, list, delete, and query databases in one Turso
43
+ * {@link Group}. Use it to give each customer their own database.
44
+ *
45
+ * The Worker gets an {@link ApiToken} that only works on that group, and a
46
+ * {@link GroupToken} for running SQL. It can't touch databases in any other
47
+ * group, and your own Turso API token stays on your machine.
48
+ *
49
+ * ### Database per tenant
50
+ * **Example:** Create a tenant database on signup and query it
51
+ * ```typescript
52
+ * Effect.gen(function* () {
53
+ * const tenants = yield* Turso.ManageDatabases(Tenants);
54
+ * return {
55
+ * fetch: Effect.gen(function* () {
56
+ * const db = yield* tenants.create("acme");
57
+ * const sql = yield* SQL.LibSQL(tenants.connect(db.name));
58
+ * yield* sql`CREATE TABLE IF NOT EXISTS notes (body TEXT)`;
59
+ * return HttpServerResponse.text(db.url);
60
+ * }),
61
+ * };
62
+ * }).pipe(Effect.provide(Turso.ManageDatabasesHttp));
63
+ * ```
64
+ *
65
+ * @binding
66
+ * @product Group
67
+ */
68
+ export interface ManageDatabases extends Binding.Service<ManageDatabases, "Turso.ManageDatabases", (group: Group) => Effect.Effect<ManageDatabasesClient>> {
69
+ }
70
+ export declare const ManageDatabases: ManageDatabases;
71
+ /** Implementation over the Turso Platform API, usable from any Platform host. */
72
+ export declare const ManageDatabasesHttp: Layer.Layer<ManageDatabases, never, never>;
73
+ //# sourceMappingURL=ManageDatabases.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ManageDatabases.d.ts","sourceRoot":"","sources":["../src/ManageDatabases.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,8BAA8B,CAAC;AACtD,OAAO,KAAK,OAAO,MAAM,iBAAiB,CAAC;AAC3C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAGxC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AAEtC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAGxC,uCAAuC;AACvC,MAAM,WAAW,eAAe;IAC9B,qBAAqB;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,qBAAqB;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,oCAAoC;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,0CAA0C;IAC1C,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,4BAA4B;IAC3C,yEAAyE;IACzE,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,oDAAoD;IACpD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,qBAAqB;IACpC,sCAAsC;IACtC,MAAM,CACJ,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,4BAA4B,GACrC,MAAM,CAAC,MAAM,CAAC,eAAe,EAAE,KAAK,CAAC,mBAAmB,EAAE,cAAc,CAAC,CAAC;IAC7E,qEAAqE;IACrE,GAAG,CACD,IAAI,EAAE,MAAM,GACX,MAAM,CAAC,MAAM,CAAC,eAAe,GAAG,SAAS,EAAE,KAAK,CAAC,gBAAgB,EAAE,cAAc,CAAC,CAAC;IACtF,wCAAwC;IACxC,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,eAAe,EAAE,EAAE,KAAK,CAAC,kBAAkB,EAAE,cAAc,CAAC,CAAC;IACnF,+DAA+D;IAC/D,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,mBAAmB,EAAE,cAAc,CAAC,CAAC;IACrF;;;OAGG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,CAAC;CACtC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,WAAW,eAAgB,SAAQ,OAAO,CAAC,OAAO,CACtD,eAAe,EACf,uBAAuB,EACvB,CAAC,KAAK,EAAE,KAAK,KAAK,MAAM,CAAC,MAAM,CAAC,qBAAqB,CAAC,CACvD;CAAG;AAEJ,eAAO,MAAM,eAAe,iBAA4D,CAAC;AAEzF,iFAAiF;AACjF,eAAO,MAAM,mBAAmB,4CAwG/B,CAAC"}