@homeflare/alchemy 0.27.1 → 0.27.3

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 (72) hide show
  1. package/README.md +3 -2
  2. package/dist/cloudflare/fake-r2-lock.d.ts +53 -0
  3. package/dist/cloudflare/fake-r2-lock.d.ts.map +1 -0
  4. package/dist/cloudflare/index.d.ts +4 -5
  5. package/dist/cloudflare/index.d.ts.map +1 -1
  6. package/dist/cloudflare/index.js +79 -103
  7. package/dist/cloudflare/index.js.map +6 -7
  8. package/dist/cloudflare/mesh-node-api.d.ts +3 -2
  9. package/dist/cloudflare/mesh-node-api.d.ts.map +1 -1
  10. package/dist/cloudflare/providers.d.ts +1 -1
  11. package/dist/cloudflare/providers.d.ts.map +1 -1
  12. package/dist/cloudflare/r2-bucket-lock.d.ts +28 -21
  13. package/dist/cloudflare/r2-bucket-lock.d.ts.map +1 -1
  14. package/dist/forgejo/branch-protection-form.d.ts +16 -5
  15. package/dist/forgejo/branch-protection-form.d.ts.map +1 -1
  16. package/dist/forgejo/branch-protection.d.ts +34 -3
  17. package/dist/forgejo/branch-protection.d.ts.map +1 -1
  18. package/dist/forgejo/fake-forgejo.d.ts +41 -0
  19. package/dist/forgejo/fake-forgejo.d.ts.map +1 -0
  20. package/dist/forgejo/index.js +220 -284
  21. package/dist/forgejo/index.js.map +14 -15
  22. package/dist/forgejo/org-actions-secrets.d.ts +42 -12
  23. package/dist/forgejo/org-actions-secrets.d.ts.map +1 -1
  24. package/dist/forgejo/org-label.d.ts +30 -3
  25. package/dist/forgejo/org-label.d.ts.map +1 -1
  26. package/dist/forgejo/org-team.d.ts +37 -4
  27. package/dist/forgejo/org-team.d.ts.map +1 -1
  28. package/dist/forgejo/repo-webhook-form.d.ts +9 -18
  29. package/dist/forgejo/repo-webhook-form.d.ts.map +1 -1
  30. package/dist/forgejo/repo-webhook.d.ts +35 -3
  31. package/dist/forgejo/repo-webhook.d.ts.map +1 -1
  32. package/dist/forgejo/repository.d.ts +47 -2
  33. package/dist/forgejo/repository.d.ts.map +1 -1
  34. package/dist/forgejo/resource.d.ts +37 -37
  35. package/dist/forgejo/resource.d.ts.map +1 -1
  36. package/dist/forgejo/team-member.d.ts +48 -4
  37. package/dist/forgejo/team-member.d.ts.map +1 -1
  38. package/dist/forgejo/values.d.ts +9 -5
  39. package/dist/forgejo/values.d.ts.map +1 -1
  40. package/docs/credentials.md +6 -0
  41. package/docs/distilled-interim.md +151 -0
  42. package/docs/peers.md +15 -6
  43. package/docs/upstream-conformance.md +46 -22
  44. package/package.json +5 -2
  45. package/src/cloudflare/fake-r2-lock.ts +120 -0
  46. package/src/cloudflare/index.ts +4 -5
  47. package/src/cloudflare/mesh-node-api.ts +3 -2
  48. package/src/cloudflare/providers.ts +13 -16
  49. package/src/cloudflare/r2-bucket-lock.test.ts +139 -0
  50. package/src/cloudflare/r2-bucket-lock.ts +59 -51
  51. package/src/forgejo/branch-protection-form.ts +12 -5
  52. package/src/forgejo/branch-protection.ts +58 -26
  53. package/src/forgejo/fake-forgejo.ts +54 -0
  54. package/src/forgejo/org-actions-secrets.ts +44 -36
  55. package/src/forgejo/org-label.test.ts +100 -0
  56. package/src/forgejo/org-label.ts +46 -36
  57. package/src/forgejo/org-team.ts +40 -27
  58. package/src/forgejo/repo-webhook-form.ts +10 -27
  59. package/src/forgejo/repo-webhook.ts +66 -35
  60. package/src/forgejo/repository.test.ts +125 -0
  61. package/src/forgejo/repository.ts +72 -52
  62. package/src/forgejo/resource.ts +80 -79
  63. package/src/forgejo/team-member.test.ts +97 -0
  64. package/src/forgejo/team-member.ts +56 -38
  65. package/src/forgejo/values.ts +9 -17
  66. package/src/proxmox/distilled-task-await.test.ts +124 -0
  67. package/dist/cloudflare/client.d.ts +0 -45
  68. package/dist/cloudflare/client.d.ts.map +0 -1
  69. package/dist/forgejo/client.d.ts +0 -37
  70. package/dist/forgejo/client.d.ts.map +0 -1
  71. package/src/cloudflare/client.ts +0 -76
  72. package/src/forgejo/client.ts +0 -136
@@ -1,10 +1,11 @@
1
1
  // src/forgejo/branch-protection.ts
2
2
  import { Resource } from "alchemy";
3
3
  import * as Provider from "alchemy/Provider";
4
- import * as Effect3 from "effect/Effect";
4
+ import * as repository from "@distilled.cloud/forgejo/repository";
5
+ import * as Effect2 from "effect/Effect";
5
6
 
6
7
  // src/forgejo/branch-protection-form.ts
7
- var branchProtectionForm = (props) => ({
8
+ var shared = (props) => ({
8
9
  ...props.applyToAdmins === undefined ? {} : { apply_to_admins: props.applyToAdmins },
9
10
  ...props.dismissStaleApprovals === undefined ? {} : { dismiss_stale_approvals: props.dismissStaleApprovals },
10
11
  ...props.enablePush === undefined ? {} : { enable_push: props.enablePush },
@@ -12,70 +13,23 @@ var branchProtectionForm = (props) => ({
12
13
  ...props.ignoreStaleApprovals === undefined ? {} : { ignore_stale_approvals: props.ignoreStaleApprovals },
13
14
  ...props.requireSignedCommits === undefined ? {} : { require_signed_commits: props.requireSignedCommits },
14
15
  ...props.requiredApprovals === undefined ? {} : { required_approvals: props.requiredApprovals },
15
- ...props.statusCheckContexts === undefined ? {} : { status_check_contexts: props.statusCheckContexts },
16
- rule_name: props.branchName
16
+ ...props.statusCheckContexts === undefined ? {} : { status_check_contexts: props.statusCheckContexts }
17
17
  });
18
+ var createBranchProtectionForm = (props) => ({
19
+ ...shared(props),
20
+ branch_name: props.branchName
21
+ });
22
+ var editBranchProtectionForm = (props) => shared(props);
18
23
 
19
24
  // src/forgejo/resource.ts
20
25
  import { isResolved } from "alchemy/Diff";
21
- import * as Effect2 from "effect/Effect";
22
-
23
- // src/forgejo/client.ts
26
+ import { CredentialsFromEnv } from "@distilled.cloud/forgejo/Credentials";
24
27
  import * as Effect from "effect/Effect";
25
- import * as HttpClient from "effect/unstable/http/HttpClient";
26
- import * as HttpClientRequest from "effect/unstable/http/HttpClientRequest";
27
- var PATH_PREFIX = "/api/v1";
28
- var apiBase = () => {
29
- const raw = process.env["FORGEJO_URL"];
30
- if (raw === undefined || raw.trim() === "") {
31
- throw new Error("FORGEJO_URL is not set. Point it at your Forgejo instance, e.g. https://git.example.com");
32
- }
33
- return raw.trim().replace(/\/$/, "");
34
- };
35
- var token = () => Effect.gen(function* () {
36
- const raw = process.env["FORGEJO_TOKEN"];
37
- if (raw === undefined || raw.trim() === "") {
38
- return yield* Effect.fail(new ForgejoError(0, "GET", "", "FORGEJO_TOKEN is unset. Mint a scoped token and export it — never store it in Alchemy props."));
39
- }
40
- return raw.trim();
41
- });
42
-
43
- class ForgejoError extends Error {
44
- status;
45
- method;
46
- path;
47
- constructor(status, method, path, detail) {
48
- super(`Forgejo ${method} ${path} -> ${String(status)}: ${detail}`);
49
- this.status = status;
50
- this.method = method;
51
- this.path = path;
52
- this.name = "ForgejoError";
53
- }
54
- }
55
- var authorization = (value) => ({ Authorization: `token ${value}` });
56
- var forgejo = (method, path, body) => Effect.gen(function* () {
57
- const credential = yield* token();
58
- const url = `${apiBase()}${PATH_PREFIX}/${path.replace(/^\//, "")}`;
59
- const client = yield* HttpClient.HttpClient;
60
- const request = HttpClientRequest.make(method)(url).pipe(HttpClientRequest.setHeaders({ ...authorization(credential), Accept: "application/json" }), body === undefined ? (self) => self : HttpClientRequest.bodyText(JSON.stringify(body), "application/json"));
61
- const response = yield* client.execute(request).pipe(Effect.mapError((cause) => new ForgejoError(0, method, path, String(cause))));
62
- if (response.status < 200 || response.status >= 300) {
63
- const detail = yield* response.text.pipe(Effect.orElseSucceed(() => ""));
64
- return yield* Effect.fail(new ForgejoError(response.status, method, path, detail.slice(0, 300)));
65
- }
66
- const text = yield* response.text.pipe(Effect.mapError((cause) => new ForgejoError(0, method, path, String(cause))));
67
- return text === "" ? undefined : JSON.parse(text);
68
- });
69
-
70
- // src/forgejo/resource.ts
71
- var absentOn404 = (io) => io.pipe(Effect2.catchIf((cause) => cause instanceof ForgejoError && cause.status === 404, () => Effect2.succeed(undefined)));
72
28
  var forgejoOperations = (spec) => {
73
- const fetchLive = (props) => absentOn404(spec.locate !== undefined ? spec.locate(props) : forgejo("GET", spec.path(props)));
74
- const objectPath = (props, live) => spec.wirePath === undefined ? spec.path(props) : spec.wirePath(props, live);
75
- const read = (props) => fetchLive(props).pipe(Effect2.map((data) => data === undefined ? undefined : spec.attributes(data, props)));
29
+ const read = (props) => spec.fetchLive(props).pipe(Effect.map((live) => live === undefined ? undefined : spec.attributes(live, props)));
76
30
  return {
77
31
  read,
78
- diff: (news, output) => Effect2.gen(function* () {
32
+ diff: (news, output) => Effect.gen(function* () {
79
33
  if (output === undefined || !isResolved(news))
80
34
  return;
81
35
  const live = yield* read(news);
@@ -83,62 +37,51 @@ var forgejoOperations = (spec) => {
83
37
  return { action: "update" };
84
38
  if (spec.matches(live, news))
85
39
  return { action: "noop" };
86
- return spec.updateForm === undefined ? { action: "replace" } : { action: "update" };
40
+ return spec.update === undefined ? { action: "replace" } : { action: "update" };
87
41
  }),
88
- reconcile: (news) => Effect2.gen(function* () {
89
- const before = yield* fetchLive(news);
42
+ reconcile: (news) => Effect.gen(function* () {
43
+ const before = yield* spec.fetchLive(news);
90
44
  if (spec.upsert !== undefined) {
91
45
  yield* spec.upsert(news, before);
92
46
  } else if (before === undefined) {
93
- yield* forgejo("POST", spec.collection(news), spec.createForm(news));
94
- } else if (spec.updateForm !== undefined) {
47
+ if (spec.create === undefined) {
48
+ return yield* Effect.die(new Error("forgejoOperations: spec declares neither create nor upsert"));
49
+ }
50
+ yield* spec.create(news);
51
+ } else if (spec.update !== undefined) {
95
52
  const mapped = spec.attributes(before, news);
96
53
  if (mapped === undefined || spec.matches(mapped, news)) {} else {
97
- const form = spec.updateForm(news);
98
- if (Object.keys(form).length > 0) {
99
- yield* forgejo("PATCH", objectPath(news, before), form);
100
- }
54
+ yield* spec.update(news, before);
101
55
  }
102
56
  }
103
57
  const after = yield* read(news);
104
58
  if (after === undefined) {
105
- return yield* Effect2.die(new Error(`${spec.path(news)}: the write returned no error but the object is still absent. ` + "Gitea returns the object directly — read back rather than trusting the status code."));
59
+ return yield* Effect.die(new Error("the write returned no error but the object is still absent. " + "Gitea returns the object directly — read back rather than trusting the status code."));
106
60
  }
107
61
  return after;
108
62
  }),
109
- destroy: (olds) => Effect2.gen(function* () {
110
- const live = yield* fetchLive(olds);
63
+ destroy: (olds) => Effect.gen(function* () {
64
+ const live = yield* spec.fetchLive(olds);
111
65
  if (live === undefined)
112
66
  return;
113
- yield* forgejo("DELETE", objectPath(olds, live));
67
+ yield* spec.destroy(olds, live);
114
68
  })
115
69
  };
116
70
  };
117
71
  var forgejoHandlers = (spec) => {
118
72
  const ops = forgejoOperations(spec);
73
+ const withCredentials = (effect) => Effect.provide(effect, CredentialsFromEnv);
119
74
  return {
120
- list: () => Effect2.succeed([]),
121
- read: ({ olds }) => ops.read(olds),
122
- diff: ({ news, output }) => ops.diff(news, output),
123
- reconcile: ({ news }) => ops.reconcile(news),
124
- delete: ({ olds }) => ops.destroy(olds)
75
+ list: () => Effect.succeed([]),
76
+ read: ({ olds }) => withCredentials(ops.read(olds)),
77
+ diff: ({ news, output }) => withCredentials(ops.diff(news, output)),
78
+ reconcile: ({ news }) => withCredentials(ops.reconcile(news)),
79
+ delete: ({ olds }) => withCredentials(ops.destroy(olds))
125
80
  };
126
81
  };
127
82
 
128
83
  // src/forgejo/values.ts
129
- var text = (value, fallback = "") => typeof value === "string" ? value : fallback;
130
- var color = (value) => text(value).replace(/^#/, "").toLowerCase();
131
- var bool = (value, fallback = false) => value === undefined || value === null ? fallback : value === true || value === 1 || value === "1";
132
- var int = (value, fallback = 0) => {
133
- if (typeof value === "number" && Number.isFinite(value))
134
- return Math.trunc(value);
135
- if (typeof value === "string" && value.trim() !== "") {
136
- const parsed = Number.parseInt(value, 10);
137
- if (Number.isFinite(parsed))
138
- return parsed;
139
- }
140
- return fallback;
141
- };
84
+ var color = (value) => value.replace(/^#/, "").toLowerCase();
142
85
  var stringArray = (value) => Array.isArray(value) ? value.filter((entry) => typeof entry === "string").toSorted() : [];
143
86
  var stringRecord = (value) => {
144
87
  if (typeof value !== "object" || value === null || Array.isArray(value))
@@ -167,107 +110,116 @@ var hookSecretEnvKey = (owner, repo, name) => `FORGEJO_HOOK_SECRET_${owner}_${re
167
110
 
168
111
  // src/forgejo/branch-protection.ts
169
112
  var ForgejoBranchProtection = Resource("Forgejo.BranchProtection", { defaultRemovalPolicy: "retain" });
170
- var handlers = forgejoHandlers({
171
- attributes: (live, props) => {
172
- const ruleName = text(live["rule_name"], text(live["branch_name"]));
173
- if (ruleName === "")
174
- return;
175
- return {
176
- applyToAdmins: bool(live["apply_to_admins"]),
177
- branchName: props.branchName,
178
- dismissStaleApprovals: bool(live["dismiss_stale_approvals"]),
179
- enablePush: bool(live["enable_push"], true),
180
- enableStatusCheck: bool(live["enable_status_check"]),
181
- ignoreStaleApprovals: bool(live["ignore_stale_approvals"]),
182
- owner: props.owner,
183
- repo: props.repo,
184
- requireSignedCommits: bool(live["require_signed_commits"]),
185
- requiredApprovals: int(live["required_approvals"]),
186
- statusCheckContexts: stringArray(live["status_check_contexts"])
187
- };
188
- },
189
- collection: (props) => `repos/${props.owner}/${props.repo}/branch_protections`,
190
- createForm: branchProtectionForm,
113
+ var spec = {
114
+ attributes: (live, props) => ({
115
+ applyToAdmins: live.apply_to_admins ?? false,
116
+ branchName: props.branchName,
117
+ dismissStaleApprovals: live.dismiss_stale_approvals ?? false,
118
+ enablePush: live.enable_push ?? true,
119
+ enableStatusCheck: live.enable_status_check ?? false,
120
+ ignoreStaleApprovals: live.ignore_stale_approvals ?? false,
121
+ owner: props.owner,
122
+ repo: props.repo,
123
+ requireSignedCommits: live.require_signed_commits ?? false,
124
+ requiredApprovals: live.required_approvals ?? 0,
125
+ statusCheckContexts: stringArray(live.status_check_contexts ?? [])
126
+ }),
127
+ create: (props) => repository.repoCreateBranchProtection({
128
+ owner: props.owner,
129
+ repo: props.repo,
130
+ ...createBranchProtectionForm(props)
131
+ }),
132
+ destroy: (props) => repository.repoDeleteBranchProtection({
133
+ owner: props.owner,
134
+ repo: props.repo,
135
+ name: props.branchName
136
+ }),
137
+ fetchLive: (props) => repository.repoGetBranchProtection({ owner: props.owner, repo: props.repo, name: props.branchName }).pipe(Effect2.catchTag("NotFound", () => Effect2.succeed(undefined))),
191
138
  matches: (attributes, props) => attributes.requiredApprovals === (props.requiredApprovals ?? 0) && attributes.enablePush === (props.enablePush ?? true) && attributes.enableStatusCheck === (props.enableStatusCheck ?? false) && attributes.requireSignedCommits === (props.requireSignedCommits ?? false) && attributes.dismissStaleApprovals === (props.dismissStaleApprovals ?? false) && attributes.ignoreStaleApprovals === (props.ignoreStaleApprovals ?? false) && attributes.applyToAdmins === (props.applyToAdmins ?? false) && (props.statusCheckContexts === undefined || stringArray(props.statusCheckContexts).join("\x00") === attributes.statusCheckContexts.join("\x00")),
192
- path: (props) => `repos/${props.owner}/${props.repo}/branch_protections/${props.branchName}`,
193
- updateForm: branchProtectionForm
194
- });
195
- var ForgejoBranchProtectionProvider = () => Provider.effect(ForgejoBranchProtection, Effect3.succeed(ForgejoBranchProtection.Provider.of(handlers)));
139
+ update: (props) => repository.repoEditBranchProtection({
140
+ owner: props.owner,
141
+ repo: props.repo,
142
+ name: props.branchName,
143
+ ...editBranchProtectionForm(props)
144
+ })
145
+ };
146
+ var handlers = forgejoHandlers(spec);
147
+ var ForgejoBranchProtectionProvider = () => Provider.effect(ForgejoBranchProtection, Effect2.succeed(ForgejoBranchProtection.Provider.of(handlers)));
196
148
  // src/forgejo/org-actions-secrets.ts
197
149
  import { Resource as Resource2 } from "alchemy";
198
150
  import * as Provider2 from "alchemy/Provider";
199
- import * as Effect4 from "effect/Effect";
151
+ import * as organization from "@distilled.cloud/forgejo/organization";
152
+ import * as Data from "effect/Data";
153
+ import * as Effect3 from "effect/Effect";
200
154
  var ForgejoOrgSecret = Resource2("Forgejo.OrgSecret");
201
- var secretData = (org, name) => Effect4.gen(function* () {
155
+
156
+ class ForgejoSecretEnvUnsetError extends Data.TaggedError("ForgejoSecretEnvUnsetError") {
157
+ }
158
+ var secretData = (org, name) => Effect3.gen(function* () {
202
159
  const key = orgSecretEnvKey(org, name);
203
160
  const raw = process.env[key];
204
161
  if (raw === undefined || raw.trim() === "") {
205
- return yield* Effect4.fail(new ForgejoError(0, "PUT", `orgs/${org}/actions/secrets/${name}`, `${key} is unset. Export the secret value there — never as an Alchemy prop.`));
162
+ return yield* Effect3.fail(new ForgejoSecretEnvUnsetError({
163
+ message: `${key} is unset. Export the secret value there — never as an Alchemy prop.`
164
+ }));
206
165
  }
207
166
  return raw.trim();
208
167
  });
209
- var handlers2 = forgejoHandlers({
168
+ var spec2 = {
210
169
  attributes: (live, props) => ({
211
- createdAt: text(live["created_at"]),
170
+ createdAt: live.created_at,
212
171
  name: props.name,
213
172
  org: props.org
214
173
  }),
215
- collection: (props) => `orgs/${props.org}/actions/secrets/${props.name}`,
216
- createForm: () => ({}),
217
- locate: (props) => forgejo("GET", `orgs/${props.org}/actions/secrets?limit=200`).pipe(Effect4.map((rows) => Array.isArray(rows) ? rows.find((row) => row["name"] === props.name) : undefined)),
174
+ destroy: (props) => organization.deleteOrgSecret({ org: props.org, secretname: props.name }),
175
+ fetchLive: (props) => organization.orgListActionsSecrets({ org: props.org, limit: 200 }).pipe(Effect3.map((rows) => rows.find((row) => row.name === props.name)), Effect3.catchTag("NotFound", () => Effect3.succeed(undefined))),
218
176
  matches: () => true,
219
- path: (props) => `orgs/${props.org}/actions/secrets/${props.name}`,
220
- upsert: (props, before) => Effect4.gen(function* () {
221
- if (before !== undefined)
222
- return;
223
- const data = yield* secretData(props.org, props.name);
224
- yield* forgejo("PUT", `orgs/${props.org}/actions/secrets/${props.name}`, { data });
225
- })
226
- });
227
- var ForgejoOrgSecretProvider = () => Provider2.effect(ForgejoOrgSecret, Effect4.succeed(ForgejoOrgSecret.Provider.of(handlers2)));
177
+ upsert: (props, before) => before !== undefined ? Effect3.void : Effect3.flatMap(secretData(props.org, props.name), (data) => Effect3.asVoid(organization.updateOrgSecret({ data, org: props.org, secretname: props.name })))
178
+ };
179
+ var handlers2 = forgejoHandlers(spec2);
180
+ var ForgejoOrgSecretProvider = () => Provider2.effect(ForgejoOrgSecret, Effect3.succeed(ForgejoOrgSecret.Provider.of(handlers2)));
228
181
  // src/forgejo/org-label.ts
229
182
  import { Resource as Resource3 } from "alchemy";
230
183
  import * as Provider3 from "alchemy/Provider";
231
- import * as Effect5 from "effect/Effect";
184
+ import * as organization2 from "@distilled.cloud/forgejo/organization";
185
+ import * as Effect4 from "effect/Effect";
232
186
  var ForgejoOrgLabel = Resource3("Forgejo.OrgLabel", {
233
187
  defaultRemovalPolicy: "retain"
234
188
  });
235
- var handlers3 = forgejoHandlers({
236
- attributes: (live, props) => {
237
- const id = live["id"];
238
- if (typeof id !== "number")
239
- return;
240
- return {
241
- color: color(live["color"]),
242
- description: text(live["description"]),
243
- exclusive: bool(live["exclusive"]),
244
- labelId: id,
245
- name: props.name,
246
- org: props.org
247
- };
248
- },
249
- collection: (props) => `orgs/${props.org}/labels`,
250
- createForm: (props) => ({
189
+ var spec3 = {
190
+ attributes: (live, props) => ({
191
+ color: color(live.color),
192
+ description: live.description ?? "",
193
+ exclusive: live.exclusive ?? false,
194
+ labelId: live.id,
195
+ name: props.name,
196
+ org: props.org
197
+ }),
198
+ create: (props) => organization2.orgCreateLabel({
251
199
  color: color(props.color),
252
200
  description: props.description ?? "",
253
201
  exclusive: props.exclusive ?? false,
254
- name: props.name
202
+ name: props.name,
203
+ org: props.org
255
204
  }),
256
- locate: (props) => forgejo("GET", `orgs/${props.org}/labels?limit=200`).pipe(Effect5.map((rows) => Array.isArray(rows) ? rows.find((row) => row["name"] === props.name) : undefined)),
205
+ destroy: (props, live) => organization2.orgDeleteLabel({ org: props.org, id: live.id }),
206
+ fetchLive: (props) => organization2.orgListLabels({ org: props.org, limit: 200 }).pipe(Effect4.map((rows) => rows.find((row) => row.name === props.name)), Effect4.catchTag("NotFound", () => Effect4.succeed(undefined))),
257
207
  matches: (attributes, props) => attributes.color === color(props.color) && attributes.description === (props.description ?? "") && attributes.exclusive === (props.exclusive ?? false),
258
- path: (props) => `orgs/${props.org}/labels/${props.name}`,
259
- updateForm: (props) => ({
208
+ update: (props, live) => organization2.orgEditLabel({
260
209
  color: color(props.color),
261
210
  description: props.description ?? "",
262
- exclusive: props.exclusive ?? false
263
- }),
264
- wirePath: (props, live) => `orgs/${props.org}/labels/${String(live["id"])}`
265
- });
266
- var ForgejoOrgLabelProvider = () => Provider3.effect(ForgejoOrgLabel, Effect5.succeed(ForgejoOrgLabel.Provider.of(handlers3)));
211
+ exclusive: props.exclusive ?? false,
212
+ id: live.id,
213
+ org: props.org
214
+ })
215
+ };
216
+ var handlers3 = forgejoHandlers(spec3);
217
+ var ForgejoOrgLabelProvider = () => Provider3.effect(ForgejoOrgLabel, Effect4.succeed(ForgejoOrgLabel.Provider.of(handlers3)));
267
218
  // src/forgejo/org-team.ts
268
219
  import { Resource as Resource4 } from "alchemy";
269
220
  import * as Provider4 from "alchemy/Provider";
270
- import * as Effect6 from "effect/Effect";
221
+ import * as organization3 from "@distilled.cloud/forgejo/organization";
222
+ import * as Effect5 from "effect/Effect";
271
223
  var ForgejoOrgTeam = Resource4("Forgejo.OrgTeam");
272
224
  var teamForm = (props) => ({
273
225
  can_create_org_repo: props.canCreateOrgRepo ?? false,
@@ -278,42 +230,38 @@ var teamForm = (props) => ({
278
230
  ...props.units === undefined ? {} : { units: props.units },
279
231
  ...props.unitsMap === undefined ? {} : { units_map: props.unitsMap }
280
232
  });
281
- var handlers4 = forgejoHandlers({
233
+ var spec4 = {
282
234
  attributes: (live, props) => {
283
- const id = live["id"];
284
- if (typeof id !== "number")
285
- return;
286
- const permission = text(live["permission"], "read");
235
+ const permission = live.permission ?? "read";
287
236
  if (permission !== "read" && permission !== "write" && permission !== "admin")
288
237
  return;
289
238
  return {
290
- canCreateOrgRepo: bool(live["can_create_org_repo"]),
291
- description: text(live["description"]),
292
- includesAllRepositories: bool(live["includes_all_repositories"]),
239
+ canCreateOrgRepo: live.can_create_org_repo ?? false,
240
+ description: live.description ?? "",
241
+ includesAllRepositories: live.includes_all_repositories ?? false,
293
242
  name: props.name,
294
243
  org: props.org,
295
244
  permission,
296
- teamId: id,
297
- units: stringArray(live["units"]),
298
- unitsMap: stringRecord(live["units_map"])
245
+ teamId: live.id,
246
+ units: stringArray(live.units ?? []),
247
+ unitsMap: stringRecord(live.units_map ?? {})
299
248
  };
300
249
  },
301
- collection: (props) => `orgs/${props.org}/teams`,
302
- createForm: teamForm,
303
- locate: (props) => forgejo("GET", `orgs/${props.org}/teams?limit=200`).pipe(Effect6.map((rows) => Array.isArray(rows) ? rows.find((row) => row["name"] === props.name) : undefined)),
250
+ create: (props) => organization3.orgCreateTeam({ org: props.org, ...teamForm(props) }),
251
+ destroy: (_props, live) => organization3.orgDeleteTeam({ id: live.id }),
252
+ fetchLive: (props) => organization3.orgListTeams({ org: props.org, limit: 200 }).pipe(Effect5.map((rows) => rows.find((row) => row.name === props.name)), Effect5.catchTag("NotFound", () => Effect5.succeed(undefined))),
304
253
  matches: (attributes, props) => attributes.description === (props.description ?? "") && attributes.permission === (props.permission ?? "read") && attributes.includesAllRepositories === (props.includesAllRepositories ?? false) && attributes.canCreateOrgRepo === (props.canCreateOrgRepo ?? false) && (props.units === undefined || stringArray(props.units).join("\x00") === attributes.units.join("\x00")) && (props.unitsMap === undefined || recordEqual(attributes.unitsMap, props.unitsMap)),
305
- path: (props) => `orgs/${props.org}/teams/${props.name}`,
306
- updateForm: teamForm,
307
- wirePath: (_props, live) => `teams/${String(live["id"])}`
308
- });
309
- var ForgejoOrgTeamProvider = () => Provider4.effect(ForgejoOrgTeam, Effect6.succeed(ForgejoOrgTeam.Provider.of(handlers4)));
254
+ update: (props, live) => organization3.orgEditTeam({ id: live.id, ...teamForm(props) })
255
+ };
256
+ var handlers4 = forgejoHandlers(spec4);
257
+ var ForgejoOrgTeamProvider = () => Provider4.effect(ForgejoOrgTeam, Effect5.succeed(ForgejoOrgTeam.Provider.of(handlers4)));
310
258
  // src/forgejo/repo-webhook.ts
311
259
  import { Resource as Resource5 } from "alchemy";
312
260
  import * as Provider5 from "alchemy/Provider";
313
- import * as Effect7 from "effect/Effect";
261
+ import * as repository2 from "@distilled.cloud/forgejo/repository";
262
+ import * as Effect6 from "effect/Effect";
314
263
 
315
264
  // src/forgejo/repo-webhook-form.ts
316
- var hookUrl = (live) => text(live["url"]) || text(stringRecord(live["config"])["url"]);
317
265
  var hookConfigForm = (props, includeSecret) => {
318
266
  const config = {
319
267
  content_type: props.contentType ?? "json",
@@ -327,141 +275,129 @@ var hookConfigForm = (props, includeSecret) => {
327
275
  }
328
276
  return config;
329
277
  };
330
- var createHookForm = (props) => ({
331
- active: props.active ?? true,
332
- ...props.authorizationHeader === undefined ? {} : { authorization_header: props.authorizationHeader },
333
- ...props.branchFilter === undefined ? {} : { branch_filter: props.branchFilter },
334
- config: hookConfigForm(props, true),
335
- events: props.events,
336
- name: props.name,
337
- type: props.type
338
- });
339
- var updateHookForm = (props) => ({
340
- active: props.active ?? true,
341
- ...props.authorizationHeader === undefined ? {} : { authorization_header: props.authorizationHeader },
342
- ...props.branchFilter === undefined ? {} : { branch_filter: props.branchFilter },
343
- config: hookConfigForm(props, false),
344
- events: props.events
345
- });
346
278
 
347
279
  // src/forgejo/repo-webhook.ts
348
280
  var ForgejoRepoWebhook = Resource5("Forgejo.RepoWebhook");
349
- var handlers5 = forgejoHandlers({
281
+ var spec5 = {
350
282
  attributes: (live, props) => {
351
- const id = live["id"];
352
- if (typeof id !== "number")
353
- return;
354
- const config = hookConfigPublic(live["config"]);
355
- const type = text(live["type"]);
283
+ const config = hookConfigPublic(live.config);
356
284
  return {
357
- active: bool(live["active"], true),
358
- authorizationHeader: text(live["authorization_header"]),
359
- branchFilter: text(live["branch_filter"]),
360
- contentType: text(config["content_type"], text(live["content_type"], "json")),
361
- events: stringArray(live["events"]),
362
- hookId: id,
285
+ active: live.active ?? true,
286
+ authorizationHeader: live.authorization_header ?? "",
287
+ branchFilter: live.branch_filter ?? "",
288
+ contentType: config["content_type"] ?? live.content_type ?? "json",
289
+ events: stringArray(live.events ?? []),
290
+ hookId: live.id,
363
291
  name: props.name,
364
292
  owner: props.owner,
365
293
  repo: props.repo,
366
- type,
367
- url: hookUrl(live)
294
+ type: live.type,
295
+ url: live.url
368
296
  };
369
297
  },
370
- collection: (props) => `repos/${props.owner}/${props.repo}/hooks`,
371
- createForm: createHookForm,
372
- locate: (props) => forgejo("GET", `repos/${props.owner}/${props.repo}/hooks?limit=200`).pipe(Effect7.map((rows) => Array.isArray(rows) ? rows.find((row) => text(row["name"]) === props.name || hookUrl(row) === props.url) : undefined)),
298
+ create: (props) => repository2.repoCreateHook({
299
+ active: props.active ?? true,
300
+ ...props.authorizationHeader === undefined ? {} : { authorization_header: props.authorizationHeader },
301
+ ...props.branchFilter === undefined ? {} : { branch_filter: props.branchFilter },
302
+ config: hookConfigForm(props, true),
303
+ events: props.events,
304
+ owner: props.owner,
305
+ repo: props.repo,
306
+ type: props.type
307
+ }),
308
+ destroy: (props, live) => repository2.repoDeleteHook({ owner: props.owner, repo: props.repo, id: live.id }),
309
+ fetchLive: (props) => repository2.repoListHooks({ owner: props.owner, repo: props.repo, limit: 200 }).pipe(Effect6.map((rows) => rows.find((row) => row.url === props.url)), Effect6.catchTag("NotFound", () => Effect6.succeed(undefined))),
373
310
  matches: (attributes, props) => attributes.type === props.type && attributes.url === props.url && attributes.active === (props.active ?? true) && attributes.contentType === (props.contentType ?? "json") && attributes.branchFilter === (props.branchFilter ?? "") && attributes.authorizationHeader === (props.authorizationHeader ?? "") && stringArray(props.events).join("\x00") === attributes.events.join("\x00"),
374
- path: (props) => `repos/${props.owner}/${props.repo}/hooks/${props.name}`,
375
- updateForm: updateHookForm,
376
- wirePath: (props, live) => `repos/${props.owner}/${props.repo}/hooks/${String(live["id"])}`
377
- });
378
- var ForgejoRepoWebhookProvider = () => Provider5.effect(ForgejoRepoWebhook, Effect7.succeed(ForgejoRepoWebhook.Provider.of(handlers5)));
311
+ update: (props, live) => repository2.repoEditHook({
312
+ active: props.active ?? true,
313
+ ...props.authorizationHeader === undefined ? {} : { authorization_header: props.authorizationHeader },
314
+ ...props.branchFilter === undefined ? {} : { branch_filter: props.branchFilter },
315
+ config: hookConfigForm(props, false),
316
+ events: props.events,
317
+ id: live.id,
318
+ owner: props.owner,
319
+ repo: props.repo
320
+ })
321
+ };
322
+ var handlers5 = forgejoHandlers(spec5);
323
+ var ForgejoRepoWebhookProvider = () => Provider5.effect(ForgejoRepoWebhook, Effect6.succeed(ForgejoRepoWebhook.Provider.of(handlers5)));
379
324
  // src/forgejo/repository.ts
380
325
  import { Resource as Resource6 } from "alchemy";
381
326
  import * as Provider6 from "alchemy/Provider";
382
- import * as Effect8 from "effect/Effect";
327
+ import * as organization4 from "@distilled.cloud/forgejo/organization";
328
+ import * as repository3 from "@distilled.cloud/forgejo/repository";
329
+ import * as Effect7 from "effect/Effect";
383
330
  var ForgejoRepository = Resource6("Forgejo.Repository", {
384
331
  defaultRemovalPolicy: "retain"
385
332
  });
386
- var handlers6 = forgejoHandlers({
387
- attributes: (live, props) => {
388
- const id = live["id"];
389
- if (typeof id !== "number")
390
- return;
391
- return {
392
- defaultBranch: text(live["default_branch"], "main"),
393
- description: text(live["description"]),
394
- empty: bool(live["empty"]),
395
- hasIssues: bool(live["has_issues"], true),
396
- hasProjects: bool(live["has_projects"], true),
397
- hasWiki: bool(live["has_wiki"], true),
398
- name: props.name,
399
- org: props.org,
400
- private: bool(live["private"], true),
401
- repoId: id
402
- };
403
- },
404
- collection: (props) => `orgs/${props.org}/repos`,
405
- createForm: (props) => ({
333
+ var editForm = (props) => ({
334
+ ...props.defaultBranch === undefined ? {} : { default_branch: props.defaultBranch },
335
+ ...props.description === undefined ? {} : { description: props.description },
336
+ ...props.hasIssues === undefined ? {} : { has_issues: props.hasIssues },
337
+ ...props.hasProjects === undefined ? {} : { has_projects: props.hasProjects },
338
+ ...props.hasWiki === undefined ? {} : { has_wiki: props.hasWiki },
339
+ ...props.private === undefined ? {} : { private: props.private }
340
+ });
341
+ var spec6 = {
342
+ attributes: (live, props) => ({
343
+ defaultBranch: live.default_branch,
344
+ description: live.description ?? "",
345
+ empty: live.empty ?? false,
346
+ hasIssues: live.has_issues ?? true,
347
+ hasProjects: live.has_projects ?? true,
348
+ hasWiki: live.has_wiki ?? true,
349
+ name: props.name,
350
+ org: props.org,
351
+ private: live.private ?? true,
352
+ repoId: live.id
353
+ }),
354
+ create: (props) => organization4.createOrgRepo({
406
355
  auto_init: props.autoInit ?? false,
407
356
  description: props.description ?? "",
408
- has_issues: props.hasIssues ?? true,
409
- has_projects: props.hasProjects ?? true,
410
- has_wiki: props.hasWiki ?? true,
411
357
  name: props.name,
358
+ org: props.org,
412
359
  private: props.private ?? true
413
360
  }),
361
+ destroy: (props) => repository3.deleteRepo({ owner: props.org, repo: props.name }),
362
+ fetchLive: (props) => repository3.getRepo({ owner: props.org, repo: props.name }).pipe(Effect7.catchTag("NotFound", () => Effect7.succeed(undefined))),
414
363
  matches: (attributes, props) => (props.description === undefined || attributes.description === props.description) && (props.private === undefined || attributes.private === props.private) && (props.hasIssues === undefined || attributes.hasIssues === props.hasIssues) && (props.hasWiki === undefined || attributes.hasWiki === props.hasWiki) && (props.hasProjects === undefined || attributes.hasProjects === props.hasProjects) && (props.defaultBranch === undefined || attributes.defaultBranch === props.defaultBranch),
415
- path: (props) => `repos/${props.org}/${props.name}`,
416
- updateForm: (props) => ({
417
- ...props.defaultBranch === undefined ? {} : { default_branch: props.defaultBranch },
418
- ...props.description === undefined ? {} : { description: props.description },
419
- ...props.hasIssues === undefined ? {} : { has_issues: props.hasIssues },
420
- ...props.hasProjects === undefined ? {} : { has_projects: props.hasProjects },
421
- ...props.hasWiki === undefined ? {} : { has_wiki: props.hasWiki },
422
- ...props.private === undefined ? {} : { private: props.private }
423
- })
424
- });
425
- var ForgejoRepositoryProvider = () => Provider6.effect(ForgejoRepository, Effect8.succeed(ForgejoRepository.Provider.of(handlers6)));
364
+ update: (props) => repository3.editRepo({ owner: props.org, repo: props.name, ...editForm(props) })
365
+ };
366
+ var handlers6 = forgejoHandlers(spec6);
367
+ var ForgejoRepositoryProvider = () => Provider6.effect(ForgejoRepository, Effect7.succeed(ForgejoRepository.Provider.of(handlers6)));
426
368
  // src/forgejo/team-member.ts
427
369
  import { Resource as Resource7 } from "alchemy";
428
370
  import * as Provider7 from "alchemy/Provider";
429
- import * as Effect9 from "effect/Effect";
371
+ import * as organization5 from "@distilled.cloud/forgejo/organization";
372
+ import * as Data2 from "effect/Data";
373
+ import * as Effect8 from "effect/Effect";
430
374
  var ForgejoTeamMember = Resource7("Forgejo.TeamMember");
431
- var TEAM_ID = "__team_id";
432
- var teamIdOf = (props) => Effect9.gen(function* () {
433
- const path = `orgs/${props.org}/teams?limit=200`;
434
- const rows = yield* forgejo("GET", path);
435
- const id = (Array.isArray(rows) ? rows : []).find((row) => row["name"] === props.team)?.["id"];
436
- if (typeof id !== "number") {
437
- return yield* Effect9.fail(new ForgejoError(0, "GET", path, `no team named ${props.team} in org ${props.org}`));
438
- }
439
- return id;
440
- });
441
- var memberPath = (id, props) => `teams/${String(id)}/members/${props.username}`;
442
- var handlers7 = forgejoHandlers({
375
+
376
+ class ForgejoTeamNotFoundError extends Data2.TaggedError("ForgejoTeamNotFoundError") {
377
+ }
378
+ var teamIdOf = (props) => organization5.orgListTeams({ org: props.org, limit: 200 }).pipe(Effect8.flatMap((rows) => {
379
+ const id = rows.find((row) => row.name === props.team)?.id;
380
+ return id === undefined ? Effect8.fail(new ForgejoTeamNotFoundError({
381
+ message: `no team named ${props.team} in org ${props.org}`
382
+ })) : Effect8.succeed(id);
383
+ }));
384
+ var spec7 = {
443
385
  attributes: (live, props) => {
444
- const id = live[TEAM_ID];
445
- const login = live["login"];
446
- if (typeof id !== "number" || typeof login !== "string")
447
- return;
448
- if (login.toLowerCase() !== props.username.toLowerCase())
386
+ if (live.login.toLowerCase() !== props.username.toLowerCase())
449
387
  return;
450
- return { org: props.org, team: props.team, teamId: id, username: props.username };
388
+ return { org: props.org, team: props.team, teamId: live.teamId, username: props.username };
451
389
  },
452
- collection: (props) => `orgs/${props.org}/teams`,
453
- createForm: () => ({}),
454
- locate: (props) => Effect9.gen(function* () {
455
- const id = yield* teamIdOf(props);
456
- const user = yield* forgejo("GET", memberPath(id, props));
457
- return user === undefined ? undefined : { ...user, [TEAM_ID]: id };
458
- }),
390
+ destroy: (props, live) => organization5.orgRemoveTeamMember({ id: live.teamId, username: props.username }),
391
+ fetchLive: (props) => Effect8.gen(function* () {
392
+ const teamId = yield* teamIdOf(props);
393
+ const user = yield* organization5.orgListTeamMember({ id: teamId, username: props.username });
394
+ return { login: user.login, teamId };
395
+ }).pipe(Effect8.catchTag("NotFound", () => Effect8.succeed(undefined))),
459
396
  matches: () => true,
460
- path: (props) => `orgs/${props.org}/teams/${props.team}/members/${props.username}`,
461
- upsert: (props, before) => before !== undefined ? Effect9.void : Effect9.flatMap(teamIdOf(props), (id) => Effect9.asVoid(forgejo("PUT", memberPath(id, props)))),
462
- wirePath: (props, live) => memberPath(Number(live[TEAM_ID]), props)
463
- });
464
- var ForgejoTeamMemberProvider = () => Provider7.effect(ForgejoTeamMember, Effect9.succeed(ForgejoTeamMember.Provider.of(handlers7)));
397
+ upsert: (props, before) => before !== undefined ? Effect8.void : Effect8.flatMap(teamIdOf(props), (id) => Effect8.asVoid(organization5.orgAddTeamMember({ id, username: props.username })))
398
+ };
399
+ var handlers7 = forgejoHandlers(spec7);
400
+ var ForgejoTeamMemberProvider = () => Provider7.effect(ForgejoTeamMember, Effect8.succeed(ForgejoTeamMember.Provider.of(handlers7)));
465
401
  export {
466
402
  ForgejoBranchProtectionProvider,
467
403
  ForgejoOrgLabel,
@@ -475,5 +411,5 @@ export {
475
411
  ForgejoTeamMemberProvider
476
412
  };
477
413
 
478
- //# debugId=09F62B41760BE1CD64756E2164756E21
414
+ //# debugId=D0F244891AADE98C64756E2164756E21
479
415
  //# sourceMappingURL=index.js.map