@homeflare/alchemy 0.27.1 → 0.27.2

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 (71) 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 +2 -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/dist/cloudflare/client.d.ts +0 -45
  67. package/dist/cloudflare/client.d.ts.map +0 -1
  68. package/dist/forgejo/client.d.ts +0 -37
  69. package/dist/forgejo/client.d.ts.map +0 -1
  70. package/src/cloudflare/client.ts +0 -76
  71. package/src/forgejo/client.ts +0 -136
@@ -12,15 +12,18 @@
12
12
  * from the stack does not DELETE it unless the caller opts into `.pipe(RemovalPolicy.destroy())`.
13
13
  * That matches the script's never-delete posture while still allowing a deliberate destroy.
14
14
  *
15
- * ⚠️ THE WIRE PATH USES NUMERIC ID, NOT NAME — measured on live org labels: PATCH and DELETE are
16
- * `/orgs/{org}/labels/{id}`. `locate` lists by name; `wirePath` uses the id from that row.
15
+ * ⚠️ THE WIRE PATH USES NUMERIC ID, NOT NAME — measured on live org labels: edit and delete are
16
+ * `orgEditLabel`/`orgDeleteLabel({ org, id })`. `fetchLive` lists by name; the id comes off that
17
+ * row for `update`/`destroy`, the same seam the hand-rolled `wirePath` used.
17
18
  *
18
19
  * ⛔ TOKEN NEEDS `write:organization` FOR RECONCILE — the mcp-read token lacks it (POST 403
19
20
  * measured 2026-09-13). Plan/deploy needs a provision-scoped credential in `FORGEJO_TOKEN`.
20
21
  */
21
22
  import { Resource } from 'alchemy';
22
23
  import * as Provider from 'alchemy/Provider';
23
- import { type ForgejoRequirements } from './resource.ts';
24
+ import * as organization from '@distilled.cloud/forgejo/organization';
25
+ import * as Effect from 'effect/Effect';
26
+ import { type ForgejoRequirements, type ForgejoSpec } from './resource.ts';
24
27
  export interface OrgLabelProps {
25
28
  /** Organization login — `homeflare` / `HomeFlare` must match the live org. */
26
29
  org: string;
@@ -42,5 +45,29 @@ export interface OrgLabelAttributes {
42
45
  export interface ForgejoOrgLabel extends Resource<'Forgejo.OrgLabel', OrgLabelProps, OrgLabelAttributes, never, ForgejoRequirements> {
43
46
  }
44
47
  export declare const ForgejoOrgLabel: import("alchemy").ResourceClass<ForgejoOrgLabel>;
48
+ /** ★ EXPORTED for direct testing with an explicit fake `Credentials` layer — see repository.ts. */
49
+ export declare const spec: ForgejoSpec<OrgLabelProps, organization.Label, OrgLabelAttributes, organization.OrgCreateLabelError | organization.OrgEditLabelError | organization.OrgDeleteLabelError | organization.OrgListLabelsError>;
50
+ export declare const handlers: {
51
+ list: () => Effect.Effect<never[], never, never>;
52
+ read: ({ olds }: {
53
+ olds: OrgLabelProps;
54
+ }) => Effect.Effect<OrgLabelAttributes | undefined, import("@distilled.cloud/cloudflare/Errors").BadGateway | import("@distilled.cloud/cloudflare/Errors").ConfigError | import("@distilled.cloud/forgejo/Errors").ForgejoParseError | import("@distilled.cloud/cloudflare/Errors").GatewayTimeout | import("effect/unstable/http/HttpClientError").HttpClientError | import("@distilled.cloud/cloudflare/Errors").InternalServerError | organization.NotFound | import("@distilled.cloud/cloudflare/Errors").ServiceUnavailable | import("@distilled.cloud/cloudflare/Errors").TooManyRequests | import("@distilled.cloud/cloudflare/Errors").Unauthorized | import("@distilled.cloud/forgejo/Errors").UnknownForgejoError | organization.UnprocessableEntity, import("effect/unstable/http/HttpClient").HttpClient>;
55
+ diff: ({ news, output }: {
56
+ news: import("alchemy").Input<OrgLabelProps>;
57
+ output: OrgLabelAttributes | undefined;
58
+ }) => Effect.Effect<{
59
+ readonly action: 'update';
60
+ } | {
61
+ readonly action: 'noop';
62
+ } | {
63
+ readonly action: 'replace';
64
+ } | undefined, import("@distilled.cloud/cloudflare/Errors").BadGateway | import("@distilled.cloud/cloudflare/Errors").ConfigError | import("@distilled.cloud/forgejo/Errors").ForgejoParseError | import("@distilled.cloud/cloudflare/Errors").GatewayTimeout | import("effect/unstable/http/HttpClientError").HttpClientError | import("@distilled.cloud/cloudflare/Errors").InternalServerError | organization.NotFound | import("@distilled.cloud/cloudflare/Errors").ServiceUnavailable | import("@distilled.cloud/cloudflare/Errors").TooManyRequests | import("@distilled.cloud/cloudflare/Errors").Unauthorized | import("@distilled.cloud/forgejo/Errors").UnknownForgejoError | organization.UnprocessableEntity, import("effect/unstable/http/HttpClient").HttpClient>;
65
+ reconcile: ({ news }: {
66
+ news: OrgLabelProps;
67
+ }) => Effect.Effect<OrgLabelAttributes, import("@distilled.cloud/cloudflare/Errors").BadGateway | import("@distilled.cloud/cloudflare/Errors").ConfigError | import("@distilled.cloud/forgejo/Errors").ForgejoParseError | import("@distilled.cloud/cloudflare/Errors").GatewayTimeout | import("effect/unstable/http/HttpClientError").HttpClientError | import("@distilled.cloud/cloudflare/Errors").InternalServerError | organization.NotFound | import("@distilled.cloud/cloudflare/Errors").ServiceUnavailable | import("@distilled.cloud/cloudflare/Errors").TooManyRequests | import("@distilled.cloud/cloudflare/Errors").Unauthorized | import("@distilled.cloud/forgejo/Errors").UnknownForgejoError | organization.UnprocessableEntity, import("effect/unstable/http/HttpClient").HttpClient>;
68
+ delete: ({ olds }: {
69
+ olds: OrgLabelProps;
70
+ }) => Effect.Effect<void, import("@distilled.cloud/cloudflare/Errors").BadGateway | import("@distilled.cloud/cloudflare/Errors").ConfigError | import("@distilled.cloud/forgejo/Errors").ForgejoParseError | import("@distilled.cloud/cloudflare/Errors").GatewayTimeout | import("effect/unstable/http/HttpClientError").HttpClientError | import("@distilled.cloud/cloudflare/Errors").InternalServerError | organization.NotFound | import("@distilled.cloud/cloudflare/Errors").ServiceUnavailable | import("@distilled.cloud/cloudflare/Errors").TooManyRequests | import("@distilled.cloud/cloudflare/Errors").Unauthorized | import("@distilled.cloud/forgejo/Errors").UnknownForgejoError | organization.UnprocessableEntity, import("effect/unstable/http/HttpClient").HttpClient>;
71
+ };
45
72
  export declare const ForgejoOrgLabelProvider: () => import("effect/Layer").Layer<Provider.Provider<ForgejoOrgLabel>, never, import("effect/unstable/http/HttpClient").HttpClient>;
46
73
  //# sourceMappingURL=org-label.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"org-label.d.ts","sourceRoot":"","sources":["../../src/forgejo/org-label.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAG7C,OAAO,EAAE,KAAK,mBAAmB,EAAmB,MAAM,eAAe,CAAC;AAG1E,MAAM,WAAW,aAAa;IAC5B,8EAA8E;IAC9E,GAAG,EAAE,MAAM,CAAC;IACZ,gEAAgE;IAChE,IAAI,EAAE,MAAM,CAAC;IACb,0CAA0C;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,eAAgB,SAAQ,QAAQ,CAC/C,kBAAkB,EAClB,aAAa,EACb,kBAAkB,EAClB,KAAK,EACL,mBAAmB,CACpB;CAAG;AAEJ,eAAO,MAAM,eAAe,kDAE1B,CAAC;AAyCH,eAAO,MAAM,uBAAuB,qIACqD,CAAC"}
1
+ {"version":3,"file":"org-label.d.ts","sourceRoot":"","sources":["../../src/forgejo/org-label.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,YAAY,MAAM,uCAAuC,CAAC;AACtE,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,KAAK,mBAAmB,EAAE,KAAK,WAAW,EAAmB,MAAM,eAAe,CAAC;AAG5F,MAAM,WAAW,aAAa;IAC5B,8EAA8E;IAC9E,GAAG,EAAE,MAAM,CAAC;IACZ,gEAAgE;IAChE,IAAI,EAAE,MAAM,CAAC;IACb,0CAA0C;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,eAAgB,SAAQ,QAAQ,CAC/C,kBAAkB,EAClB,aAAa,EACb,kBAAkB,EAClB,KAAK,EACL,mBAAmB,CACpB;CAAG;AAEJ,eAAO,MAAM,eAAe,kDAE1B,CAAC;AAEH,mGAAmG;AACnG,eAAO,MAAM,IAAI,EAAE,WAAW,CAC5B,aAAa,EACb,YAAY,CAAC,KAAK,EAClB,kBAAkB,EAChB,YAAY,CAAC,mBAAmB,GAChC,YAAY,CAAC,iBAAiB,GAC9B,YAAY,CAAC,mBAAmB,GAChC,YAAY,CAAC,kBAAkB,CAoClC,CAAC;AAEF,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;CAAwB,CAAC;AAE9C,eAAO,MAAM,uBAAuB,qIACqD,CAAC"}
@@ -3,19 +3,28 @@
3
3
  *
4
4
  * ★ READ OFF `<estate>/mcp-servers/docs/api/upstream/forgejo.json` (Forgejo 16.x OpenAPI): create is
5
5
  * `POST /orgs/{org}/teams`; read/update/delete use numeric id at `/teams/{id}` — not the name.
6
+ * Now `organization.orgCreateTeam` / `orgListTeams` / `orgEditTeam` / `orgDeleteTeam`.
6
7
  *
7
- * ⚠️ PATCH AND DELETE USE `/teams/{id}` — same seam as org labels. `locate` lists by name;
8
- * `wirePath` carries the id from that row.
8
+ * ⚠️ `orgEditTeam`/`orgDeleteTeam` TAKE ONLY `{ id, ... }` — NO `org` FIELD. The package's schema
9
+ * confirms Forgejo's edit/delete routes are `/teams/{id}` with no org segment; `fetchLive` lists
10
+ * by name and `update`/`destroy` carry the id off that row, same seam as org labels.
11
+ *
12
+ * ⚠️ `live.permission` IS TYPED `"none" | "read" | "write" | "admin" | "owner"` IN THE PACKAGE —
13
+ * wider than this family's own `TeamPermission`. The guard below keeps the original behaviour:
14
+ * an out-of-range value (`none`/`owner`) folds `attributes` to `undefined` rather than lying
15
+ * about which of the three this family manages the team is at.
9
16
  *
10
17
  * ⛔ TOKEN NEEDS `write:organization` TO CREATE OR PATCH — read list/get needs `read:organization`.
11
18
  */
12
19
  import { Resource } from 'alchemy';
13
20
  import * as Provider from 'alchemy/Provider';
14
- import { type ForgejoRequirements } from './resource.ts';
21
+ import * as organization from '@distilled.cloud/forgejo/organization';
22
+ import * as Effect from 'effect/Effect';
23
+ import { type ForgejoRequirements, type ForgejoSpec } from './resource.ts';
15
24
  export type TeamPermission = 'read' | 'write' | 'admin';
16
25
  export interface OrgTeamProps {
17
26
  org: string;
18
- /** Team name — primary locate key; sent on every PATCH body. */
27
+ /** Team name — primary locate key; sent on every edit body. */
19
28
  name: string;
20
29
  description?: string;
21
30
  permission?: TeamPermission;
@@ -38,5 +47,29 @@ export interface OrgTeamAttributes {
38
47
  export interface ForgejoOrgTeam extends Resource<'Forgejo.OrgTeam', OrgTeamProps, OrgTeamAttributes, never, ForgejoRequirements> {
39
48
  }
40
49
  export declare const ForgejoOrgTeam: import("alchemy").ResourceClass<ForgejoOrgTeam>;
50
+ /** ★ EXPORTED for direct testing with an explicit fake `Credentials` layer — see repository.ts. */
51
+ export declare const spec: ForgejoSpec<OrgTeamProps, organization.Team, OrgTeamAttributes, organization.OrgCreateTeamError | organization.OrgEditTeamError | organization.OrgDeleteTeamError | organization.OrgListTeamsError>;
52
+ export declare const handlers: {
53
+ list: () => Effect.Effect<never[], never, never>;
54
+ read: ({ olds }: {
55
+ olds: OrgTeamProps;
56
+ }) => Effect.Effect<OrgTeamAttributes | undefined, import("@distilled.cloud/cloudflare/Errors").BadGateway | import("@distilled.cloud/cloudflare/Errors").ConfigError | organization.Forbidden | import("@distilled.cloud/forgejo/Errors").ForgejoParseError | import("@distilled.cloud/cloudflare/Errors").GatewayTimeout | import("effect/unstable/http/HttpClientError").HttpClientError | import("@distilled.cloud/cloudflare/Errors").InternalServerError | organization.NotFound | import("@distilled.cloud/cloudflare/Errors").ServiceUnavailable | import("@distilled.cloud/cloudflare/Errors").TooManyRequests | import("@distilled.cloud/cloudflare/Errors").Unauthorized | import("@distilled.cloud/forgejo/Errors").UnknownForgejoError | organization.UnprocessableEntity, import("effect/unstable/http/HttpClient").HttpClient>;
57
+ diff: ({ news, output }: {
58
+ news: import("alchemy").Input<OrgTeamProps>;
59
+ output: OrgTeamAttributes | undefined;
60
+ }) => Effect.Effect<{
61
+ readonly action: 'update';
62
+ } | {
63
+ readonly action: 'noop';
64
+ } | {
65
+ readonly action: 'replace';
66
+ } | undefined, import("@distilled.cloud/cloudflare/Errors").BadGateway | import("@distilled.cloud/cloudflare/Errors").ConfigError | organization.Forbidden | import("@distilled.cloud/forgejo/Errors").ForgejoParseError | import("@distilled.cloud/cloudflare/Errors").GatewayTimeout | import("effect/unstable/http/HttpClientError").HttpClientError | import("@distilled.cloud/cloudflare/Errors").InternalServerError | organization.NotFound | import("@distilled.cloud/cloudflare/Errors").ServiceUnavailable | import("@distilled.cloud/cloudflare/Errors").TooManyRequests | import("@distilled.cloud/cloudflare/Errors").Unauthorized | import("@distilled.cloud/forgejo/Errors").UnknownForgejoError | organization.UnprocessableEntity, import("effect/unstable/http/HttpClient").HttpClient>;
67
+ reconcile: ({ news }: {
68
+ news: OrgTeamProps;
69
+ }) => Effect.Effect<OrgTeamAttributes, import("@distilled.cloud/cloudflare/Errors").BadGateway | import("@distilled.cloud/cloudflare/Errors").ConfigError | organization.Forbidden | import("@distilled.cloud/forgejo/Errors").ForgejoParseError | import("@distilled.cloud/cloudflare/Errors").GatewayTimeout | import("effect/unstable/http/HttpClientError").HttpClientError | import("@distilled.cloud/cloudflare/Errors").InternalServerError | organization.NotFound | import("@distilled.cloud/cloudflare/Errors").ServiceUnavailable | import("@distilled.cloud/cloudflare/Errors").TooManyRequests | import("@distilled.cloud/cloudflare/Errors").Unauthorized | import("@distilled.cloud/forgejo/Errors").UnknownForgejoError | organization.UnprocessableEntity, import("effect/unstable/http/HttpClient").HttpClient>;
70
+ delete: ({ olds }: {
71
+ olds: OrgTeamProps;
72
+ }) => Effect.Effect<void, import("@distilled.cloud/cloudflare/Errors").BadGateway | import("@distilled.cloud/cloudflare/Errors").ConfigError | organization.Forbidden | import("@distilled.cloud/forgejo/Errors").ForgejoParseError | import("@distilled.cloud/cloudflare/Errors").GatewayTimeout | import("effect/unstable/http/HttpClientError").HttpClientError | import("@distilled.cloud/cloudflare/Errors").InternalServerError | organization.NotFound | import("@distilled.cloud/cloudflare/Errors").ServiceUnavailable | import("@distilled.cloud/cloudflare/Errors").TooManyRequests | import("@distilled.cloud/cloudflare/Errors").Unauthorized | import("@distilled.cloud/forgejo/Errors").UnknownForgejoError | organization.UnprocessableEntity, import("effect/unstable/http/HttpClient").HttpClient>;
73
+ };
41
74
  export declare const ForgejoOrgTeamProvider: () => import("effect/Layer").Layer<Provider.Provider<ForgejoOrgTeam>, never, import("effect/unstable/http/HttpClient").HttpClient>;
42
75
  //# sourceMappingURL=org-team.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"org-team.d.ts","sourceRoot":"","sources":["../../src/forgejo/org-team.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAG7C,OAAO,EAAE,KAAK,mBAAmB,EAAmB,MAAM,eAAe,CAAC;AAG1E,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;AAExD,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,gEAAgE;IAChE,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,cAAc,CAAC;IAC3B,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,uBAAuB,EAAE,OAAO,CAAC;IACjC,gBAAgB,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,cAAe,SAAQ,QAAQ,CAC9C,iBAAiB,EACjB,YAAY,EACZ,iBAAiB,EACjB,KAAK,EACL,mBAAmB,CACpB;CAAG;AAEJ,eAAO,MAAM,cAAc,iDAA8C,CAAC;AAmD1E,eAAO,MAAM,sBAAsB,oIACoD,CAAC"}
1
+ {"version":3,"file":"org-team.d.ts","sourceRoot":"","sources":["../../src/forgejo/org-team.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,YAAY,MAAM,uCAAuC,CAAC;AACtE,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,KAAK,mBAAmB,EAAE,KAAK,WAAW,EAAmB,MAAM,eAAe,CAAC;AAG5F,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;AAExD,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,+DAA+D;IAC/D,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,cAAc,CAAC;IAC3B,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,uBAAuB,EAAE,OAAO,CAAC;IACjC,gBAAgB,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,cAAe,SAAQ,QAAQ,CAC9C,iBAAiB,EACjB,YAAY,EACZ,iBAAiB,EACjB,KAAK,EACL,mBAAmB,CACpB;CAAG;AAEJ,eAAO,MAAM,cAAc,iDAA8C,CAAC;AAY1E,mGAAmG;AACnG,eAAO,MAAM,IAAI,EAAE,WAAW,CAC5B,YAAY,EACZ,YAAY,CAAC,IAAI,EACjB,iBAAiB,EACf,YAAY,CAAC,kBAAkB,GAC/B,YAAY,CAAC,gBAAgB,GAC7B,YAAY,CAAC,kBAAkB,GAC/B,YAAY,CAAC,iBAAiB,CAiCjC,CAAC;AAEF,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;CAAwB,CAAC;AAE9C,eAAO,MAAM,sBAAsB,oIACoD,CAAC"}
@@ -1,27 +1,18 @@
1
1
  /**
2
- * JSON bodies for `Forgejo.RepoWebhook` — split to keep repo-webhook.ts under the line cap.
2
+ * `config` bodies for `Forgejo.RepoWebhook` — split to keep repo-webhook.ts under the line cap.
3
3
  *
4
4
  * ⛔ `config.secret` IS NEVER A PROP. When present it is read from `FORGEJO_HOOK_SECRET_*` at
5
5
  * create/update time only (see hookSecretEnvKey in values.ts). Alchemy attributes are Postgres
6
6
  * plaintext — a webhook HMAC secret must not be stored there.
7
+ *
8
+ * ⛔ NO `name` FIELD ON EITHER SIDE. `@distilled.cloud/forgejo`'s `RepoCreateHookRequest` and
9
+ * `Hook` (the create input and the read/list output) have no `name` property — verified against
10
+ * the package's schema. The hand-rolled client sent `name: props.name` on create anyway; Gitea's
11
+ * JSON decoder silently dropped it, so `locate` never actually matched by name either (its
12
+ * `row['name']` read was always `undefined` on an untyped response) — matching was always by
13
+ * URL in practice. `props.name`/`RepoWebhookAttributes.name` stay as this family's own stable
14
+ * key; nothing on the wire carries it.
7
15
  */
8
16
  import type { RepoWebhookProps } from './repo-webhook.ts';
9
- export declare const hookUrl: (live: Record<string, unknown>) => string;
10
17
  export declare const hookConfigForm: (props: RepoWebhookProps, includeSecret: boolean) => Record<string, string>;
11
- export declare const createHookForm: (props: RepoWebhookProps) => {
12
- active: boolean;
13
- authorization_header?: string;
14
- branch_filter?: string;
15
- config: Record<string, string>;
16
- events: string[];
17
- name: string;
18
- type: import("./repo-webhook.ts").RepoWebhookType;
19
- };
20
- export declare const updateHookForm: (props: RepoWebhookProps) => {
21
- active: boolean;
22
- authorization_header?: string;
23
- branch_filter?: string;
24
- config: Record<string, string>;
25
- events: string[];
26
- };
27
18
  //# sourceMappingURL=repo-webhook-form.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"repo-webhook-form.d.ts","sourceRoot":"","sources":["../../src/forgejo/repo-webhook-form.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAG1D,eAAO,MAAM,OAAO,SAAU,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,WACW,CAAC;AAEjE,eAAO,MAAM,cAAc,UAAW,gBAAgB,iBAAiB,OAAO,2BAW7E,CAAC;AAEF,eAAO,MAAM,cAAc,UAAW,gBAAgB;;;;;;;;CAUpD,CAAC;AAEH,eAAO,MAAM,cAAc,UAAW,gBAAgB;;;;;;CAQpD,CAAC"}
1
+ {"version":3,"file":"repo-webhook-form.d.ts","sourceRoot":"","sources":["../../src/forgejo/repo-webhook-form.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAG1D,eAAO,MAAM,cAAc,UAAW,gBAAgB,iBAAiB,OAAO,2BAW7E,CAAC"}
@@ -2,7 +2,13 @@
2
2
  * `Forgejo.RepoWebhook` — one repository webhook under `/repos/{owner}/{repo}/hooks`.
3
3
  *
4
4
  * ★ READ OFF `<estate>/mcp-servers/docs/api/upstream/forgejo.json`: create is POST collection; read/update/delete
5
- * use numeric hook id at `/repos/{owner}/{repo}/hooks/{id}`. `locate` lists and matches on `name`.
5
+ * use numeric hook id at `/repos/{owner}/{repo}/hooks/{id}`. Now `repository.repoCreateHook` /
6
+ * `repoListHooks` / `repoEditHook` / `repoDeleteHook`. `fetchLive` lists and matches on `url` —
7
+ * see repo-webhook-form.ts for why name was never the real match key.
8
+ *
9
+ * ⚠️ `Hook.url` IS A REQUIRED STRING IN THE PACKAGE'S SCHEMA — the `config.url` fallback the
10
+ * hand-rolled client carried (`stringRecord(live['config'])['url']`) is gone; a response
11
+ * without `url` now fails the operation's decode.
6
12
  *
7
13
  * ⛔ NO WEBHOOK `secret` IN PROPS OR ATTRIBUTES. Optional HMAC material is env-only at write time
8
14
  * (`FORGEJO_HOOK_SECRET_*` — see repo-webhook-form.ts). `hookConfigPublic` strips `secret` on read.
@@ -11,12 +17,14 @@
11
17
  */
12
18
  import { Resource } from 'alchemy';
13
19
  import * as Provider from 'alchemy/Provider';
14
- import { type ForgejoRequirements } from './resource.ts';
20
+ import * as repository from '@distilled.cloud/forgejo/repository';
21
+ import * as Effect from 'effect/Effect';
22
+ import { type ForgejoRequirements, type ForgejoSpec } from './resource.ts';
15
23
  export type RepoWebhookType = 'dingtalk' | 'discord' | 'feishu' | 'forgejo' | 'gitea' | 'gogs' | 'msteams' | 'packagist' | 'slack' | 'telegram' | 'wechatwork';
16
24
  export interface RepoWebhookProps {
17
25
  owner: string;
18
26
  repo: string;
19
- /** Stable locate key — also sent as Hook.name on create. */
27
+ /** Stable locate key on this side only — Forgejo hooks have no `name` field (see the form file). */
20
28
  name: string;
21
29
  type: RepoWebhookType;
22
30
  url: string;
@@ -42,5 +50,29 @@ export interface RepoWebhookAttributes {
42
50
  export interface ForgejoRepoWebhook extends Resource<'Forgejo.RepoWebhook', RepoWebhookProps, RepoWebhookAttributes, never, ForgejoRequirements> {
43
51
  }
44
52
  export declare const ForgejoRepoWebhook: import("alchemy").ResourceClass<ForgejoRepoWebhook>;
53
+ /** ★ EXPORTED for direct testing with an explicit fake `Credentials` layer — see repository.ts. */
54
+ export declare const spec: ForgejoSpec<RepoWebhookProps, repository.Hook, RepoWebhookAttributes, repository.RepoCreateHookError | repository.RepoEditHookError | repository.RepoDeleteHookError | repository.RepoListHooksError>;
55
+ export declare const handlers: {
56
+ list: () => Effect.Effect<never[], never, never>;
57
+ read: ({ olds }: {
58
+ olds: RepoWebhookProps;
59
+ }) => Effect.Effect<RepoWebhookAttributes | undefined, import("@distilled.cloud/cloudflare/Errors").BadGateway | import("@distilled.cloud/cloudflare/Errors").ConfigError | repository.Forbidden | import("@distilled.cloud/forgejo/Errors").ForgejoParseError | import("@distilled.cloud/cloudflare/Errors").GatewayTimeout | import("effect/unstable/http/HttpClientError").HttpClientError | import("@distilled.cloud/cloudflare/Errors").InternalServerError | repository.NotFound | import("@distilled.cloud/cloudflare/Errors").ServiceUnavailable | import("@distilled.cloud/cloudflare/Errors").TooManyRequests | import("@distilled.cloud/cloudflare/Errors").Unauthorized | import("@distilled.cloud/forgejo/Errors").UnknownForgejoError, import("effect/unstable/http/HttpClient").HttpClient>;
60
+ diff: ({ news, output }: {
61
+ news: import("alchemy").Input<RepoWebhookProps>;
62
+ output: RepoWebhookAttributes | undefined;
63
+ }) => Effect.Effect<{
64
+ readonly action: 'update';
65
+ } | {
66
+ readonly action: 'noop';
67
+ } | {
68
+ readonly action: 'replace';
69
+ } | undefined, import("@distilled.cloud/cloudflare/Errors").BadGateway | import("@distilled.cloud/cloudflare/Errors").ConfigError | repository.Forbidden | import("@distilled.cloud/forgejo/Errors").ForgejoParseError | import("@distilled.cloud/cloudflare/Errors").GatewayTimeout | import("effect/unstable/http/HttpClientError").HttpClientError | import("@distilled.cloud/cloudflare/Errors").InternalServerError | repository.NotFound | import("@distilled.cloud/cloudflare/Errors").ServiceUnavailable | import("@distilled.cloud/cloudflare/Errors").TooManyRequests | import("@distilled.cloud/cloudflare/Errors").Unauthorized | import("@distilled.cloud/forgejo/Errors").UnknownForgejoError, import("effect/unstable/http/HttpClient").HttpClient>;
70
+ reconcile: ({ news }: {
71
+ news: RepoWebhookProps;
72
+ }) => Effect.Effect<RepoWebhookAttributes, import("@distilled.cloud/cloudflare/Errors").BadGateway | import("@distilled.cloud/cloudflare/Errors").ConfigError | repository.Forbidden | import("@distilled.cloud/forgejo/Errors").ForgejoParseError | import("@distilled.cloud/cloudflare/Errors").GatewayTimeout | import("effect/unstable/http/HttpClientError").HttpClientError | import("@distilled.cloud/cloudflare/Errors").InternalServerError | repository.NotFound | import("@distilled.cloud/cloudflare/Errors").ServiceUnavailable | import("@distilled.cloud/cloudflare/Errors").TooManyRequests | import("@distilled.cloud/cloudflare/Errors").Unauthorized | import("@distilled.cloud/forgejo/Errors").UnknownForgejoError, import("effect/unstable/http/HttpClient").HttpClient>;
73
+ delete: ({ olds }: {
74
+ olds: RepoWebhookProps;
75
+ }) => Effect.Effect<void, import("@distilled.cloud/cloudflare/Errors").BadGateway | import("@distilled.cloud/cloudflare/Errors").ConfigError | repository.Forbidden | import("@distilled.cloud/forgejo/Errors").ForgejoParseError | import("@distilled.cloud/cloudflare/Errors").GatewayTimeout | import("effect/unstable/http/HttpClientError").HttpClientError | import("@distilled.cloud/cloudflare/Errors").InternalServerError | repository.NotFound | import("@distilled.cloud/cloudflare/Errors").ServiceUnavailable | import("@distilled.cloud/cloudflare/Errors").TooManyRequests | import("@distilled.cloud/cloudflare/Errors").Unauthorized | import("@distilled.cloud/forgejo/Errors").UnknownForgejoError, import("effect/unstable/http/HttpClient").HttpClient>;
76
+ };
45
77
  export declare const ForgejoRepoWebhookProvider: () => import("effect/Layer").Layer<Provider.Provider<ForgejoRepoWebhook>, never, import("effect/unstable/http/HttpClient").HttpClient>;
46
78
  //# sourceMappingURL=repo-webhook.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"repo-webhook.d.ts","sourceRoot":"","sources":["../../src/forgejo/repo-webhook.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAI7C,OAAO,EAAE,KAAK,mBAAmB,EAAmB,MAAM,eAAe,CAAC;AAG1E,MAAM,MAAM,eAAe,GACvB,UAAU,GACV,SAAS,GACT,QAAQ,GACR,SAAS,GACT,OAAO,GACP,MAAM,GACN,SAAS,GACT,WAAW,GACX,OAAO,GACP,UAAU,GACV,YAAY,CAAC;AAEjB,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,4DAA4D;IAC5D,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,eAAe,CAAC;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC9B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,eAAe,CAAC;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,kBAAmB,SAAQ,QAAQ,CAClD,qBAAqB,EACrB,gBAAgB,EAChB,qBAAqB,EACrB,KAAK,EACL,mBAAmB,CACpB;CAAG;AAEJ,eAAO,MAAM,kBAAkB,qDAAsD,CAAC;AAgDtF,eAAO,MAAM,0BAA0B,wIACwD,CAAC"}
1
+ {"version":3,"file":"repo-webhook.d.ts","sourceRoot":"","sources":["../../src/forgejo/repo-webhook.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,UAAU,MAAM,qCAAqC,CAAC;AAClE,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,OAAO,EAAE,KAAK,mBAAmB,EAAE,KAAK,WAAW,EAAmB,MAAM,eAAe,CAAC;AAG5F,MAAM,MAAM,eAAe,GACvB,UAAU,GACV,SAAS,GACT,QAAQ,GACR,SAAS,GACT,OAAO,GACP,MAAM,GACN,SAAS,GACT,WAAW,GACX,OAAO,GACP,UAAU,GACV,YAAY,CAAC;AAEjB,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,oGAAoG;IACpG,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,eAAe,CAAC;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC9B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,eAAe,CAAC;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,kBAAmB,SAAQ,QAAQ,CAClD,qBAAqB,EACrB,gBAAgB,EAChB,qBAAqB,EACrB,KAAK,EACL,mBAAmB,CACpB;CAAG;AAEJ,eAAO,MAAM,kBAAkB,qDAAsD,CAAC;AAEtF,mGAAmG;AACnG,eAAO,MAAM,IAAI,EAAE,WAAW,CAC5B,gBAAgB,EAChB,UAAU,CAAC,IAAI,EACf,qBAAqB,EACnB,UAAU,CAAC,mBAAmB,GAC9B,UAAU,CAAC,iBAAiB,GAC5B,UAAU,CAAC,mBAAmB,GAC9B,UAAU,CAAC,kBAAkB,CA2DhC,CAAC;AAEF,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;CAAwB,CAAC;AAE9C,eAAO,MAAM,0BAA0B,wIACwD,CAAC"}
@@ -3,14 +3,27 @@
3
3
  *
4
4
  * ★ MEASURED 2026-09-13 against the live daemon (`GET /api/v1/repos/<org>/<repo>`):
5
5
  * responses are flat JSON with `private`, `has_issues`, `default_branch`, etc. Create is
6
- * `POST /orgs/{org}/repos`; read/update/delete use `/repos/{owner}/{repo}`.
6
+ * `POST /orgs/{org}/repos`; read/update/delete use `/repos/{owner}/{repo}`. Now called through
7
+ * `@distilled.cloud/forgejo`'s `organization.createOrgRepo` / `repository.{getRepo,editRepo,
8
+ * deleteRepo}` instead of the retired hand-rolled client (resource.ts).
9
+ *
10
+ * ⛔ `CreateOrgRepoRequest` HAS NO `has_issues` / `has_wiki` / `has_projects`. Verified against
11
+ * the package's typed schema: Forgejo's own `POST /orgs/{org}/repos` never accepted them either
12
+ * (the hand-rolled client sent them anyway; Gitea's JSON decoder silently drops unknown keys,
13
+ * so they were always ignored on create). `editRepo`'s schema does carry them, and
14
+ * `updateForm`/`matches` below still converge them on the first non-create reconcile — the same
15
+ * two-step behaviour the old client had, now provable at the type level instead of by reading
16
+ * Gitea's source.
7
17
  *
8
18
  * ⛔ `defaultRemovalPolicy: 'retain'` — history, issues and packages in a repo are irreplaceable.
9
19
  * Opt into deletion with `.pipe(RemovalPolicy.destroy())`; `delete` below is fully implemented.
10
20
  */
11
21
  import { Resource } from 'alchemy';
12
22
  import * as Provider from 'alchemy/Provider';
13
- import { type ForgejoRequirements } from './resource.ts';
23
+ import * as organization from '@distilled.cloud/forgejo/organization';
24
+ import * as repository from '@distilled.cloud/forgejo/repository';
25
+ import * as Effect from 'effect/Effect';
26
+ import { type ForgejoRequirements, type ForgejoSpec } from './resource.ts';
14
27
  export interface RepositoryProps {
15
28
  /** Organization login — the owner segment in `<org>/<repo>`. */
16
29
  org: string;
@@ -41,5 +54,37 @@ export interface RepositoryAttributes {
41
54
  export interface ForgejoRepository extends Resource<'Forgejo.Repository', RepositoryProps, RepositoryAttributes, never, ForgejoRequirements> {
42
55
  }
43
56
  export declare const ForgejoRepository: import("alchemy").ResourceClass<ForgejoRepository>;
57
+ /**
58
+ * ★ EXPORTED, NOT JUST PASSED INLINE TO `forgejoHandlers` BELOW — so a test can call
59
+ * `spec.fetchLive` / `spec.attributes` directly (repository.test.ts does), against an explicit
60
+ * fake `Credentials` layer, without touching `forgejoHandlers`'s baked-in `CredentialsFromEnv`.
61
+ * That layer resolves `FORGEJO_URL` / `FORGEJO_TOKEN` through Effect's `Config`, whose default
62
+ * provider snapshots `process.env` once — a test-time `process.env` mutation is invisible to it,
63
+ * so the only way to run this exact production code against a fake server is to call it with an
64
+ * explicit `Credentials` layer, bypassing the env read entirely.
65
+ */
66
+ export declare const spec: ForgejoSpec<RepositoryProps, repository.Repository, RepositoryAttributes, organization.CreateOrgRepoError | repository.GetRepoError | repository.EditRepoError | repository.DeleteRepoError>;
67
+ export declare const handlers: {
68
+ list: () => Effect.Effect<never[], never, never>;
69
+ read: ({ olds }: {
70
+ olds: RepositoryProps;
71
+ }) => Effect.Effect<RepositoryAttributes | undefined, import("@distilled.cloud/cloudflare/Errors").BadGateway | organization.BadRequest | import("@distilled.cloud/cloudflare/Errors").ConfigError | organization.Conflict | repository.Forbidden | organization.Forbidden | import("@distilled.cloud/forgejo/Errors").ForgejoParseError | import("@distilled.cloud/cloudflare/Errors").GatewayTimeout | import("effect/unstable/http/HttpClientError").HttpClientError | import("@distilled.cloud/cloudflare/Errors").InternalServerError | repository.NotFound | organization.NotFound | import("@distilled.cloud/cloudflare/Errors").ServiceUnavailable | import("@distilled.cloud/cloudflare/Errors").TooManyRequests | import("@distilled.cloud/cloudflare/Errors").Unauthorized | import("@distilled.cloud/forgejo/Errors").UnknownForgejoError | repository.UnprocessableEntity, import("effect/unstable/http/HttpClient").HttpClient>;
72
+ diff: ({ news, output }: {
73
+ news: import("alchemy").Input<RepositoryProps>;
74
+ output: RepositoryAttributes | undefined;
75
+ }) => Effect.Effect<{
76
+ readonly action: 'update';
77
+ } | {
78
+ readonly action: 'noop';
79
+ } | {
80
+ readonly action: 'replace';
81
+ } | undefined, import("@distilled.cloud/cloudflare/Errors").BadGateway | organization.BadRequest | import("@distilled.cloud/cloudflare/Errors").ConfigError | organization.Conflict | repository.Forbidden | organization.Forbidden | import("@distilled.cloud/forgejo/Errors").ForgejoParseError | import("@distilled.cloud/cloudflare/Errors").GatewayTimeout | import("effect/unstable/http/HttpClientError").HttpClientError | import("@distilled.cloud/cloudflare/Errors").InternalServerError | repository.NotFound | organization.NotFound | import("@distilled.cloud/cloudflare/Errors").ServiceUnavailable | import("@distilled.cloud/cloudflare/Errors").TooManyRequests | import("@distilled.cloud/cloudflare/Errors").Unauthorized | import("@distilled.cloud/forgejo/Errors").UnknownForgejoError | repository.UnprocessableEntity, import("effect/unstable/http/HttpClient").HttpClient>;
82
+ reconcile: ({ news }: {
83
+ news: RepositoryProps;
84
+ }) => Effect.Effect<RepositoryAttributes, import("@distilled.cloud/cloudflare/Errors").BadGateway | organization.BadRequest | import("@distilled.cloud/cloudflare/Errors").ConfigError | organization.Conflict | repository.Forbidden | organization.Forbidden | import("@distilled.cloud/forgejo/Errors").ForgejoParseError | import("@distilled.cloud/cloudflare/Errors").GatewayTimeout | import("effect/unstable/http/HttpClientError").HttpClientError | import("@distilled.cloud/cloudflare/Errors").InternalServerError | repository.NotFound | organization.NotFound | import("@distilled.cloud/cloudflare/Errors").ServiceUnavailable | import("@distilled.cloud/cloudflare/Errors").TooManyRequests | import("@distilled.cloud/cloudflare/Errors").Unauthorized | import("@distilled.cloud/forgejo/Errors").UnknownForgejoError | repository.UnprocessableEntity, import("effect/unstable/http/HttpClient").HttpClient>;
85
+ delete: ({ olds }: {
86
+ olds: RepositoryProps;
87
+ }) => Effect.Effect<void, import("@distilled.cloud/cloudflare/Errors").BadGateway | organization.BadRequest | import("@distilled.cloud/cloudflare/Errors").ConfigError | organization.Conflict | repository.Forbidden | organization.Forbidden | import("@distilled.cloud/forgejo/Errors").ForgejoParseError | import("@distilled.cloud/cloudflare/Errors").GatewayTimeout | import("effect/unstable/http/HttpClientError").HttpClientError | import("@distilled.cloud/cloudflare/Errors").InternalServerError | repository.NotFound | organization.NotFound | import("@distilled.cloud/cloudflare/Errors").ServiceUnavailable | import("@distilled.cloud/cloudflare/Errors").TooManyRequests | import("@distilled.cloud/cloudflare/Errors").Unauthorized | import("@distilled.cloud/forgejo/Errors").UnknownForgejoError | repository.UnprocessableEntity, import("effect/unstable/http/HttpClient").HttpClient>;
88
+ };
44
89
  export declare const ForgejoRepositoryProvider: () => import("effect/Layer").Layer<Provider.Provider<ForgejoRepository>, never, import("effect/unstable/http/HttpClient").HttpClient>;
45
90
  //# sourceMappingURL=repository.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"repository.d.ts","sourceRoot":"","sources":["../../src/forgejo/repository.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAE7C,OAAO,EAAE,KAAK,mBAAmB,EAAmB,MAAM,eAAe,CAAC;AAG1E,MAAM,WAAW,eAAe;IAC9B,gEAAgE;IAChE,GAAG,EAAE,MAAM,CAAC;IACZ,gEAAgE;IAChE,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,wEAAwE;IACxE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,yEAAyE;IACzE,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,oBAAoB;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,OAAO,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,iBAAkB,SAAQ,QAAQ,CACjD,oBAAoB,EACpB,eAAe,EACf,oBAAoB,EACpB,KAAK,EACL,mBAAmB,CACpB;CAAG;AAEJ,eAAO,MAAM,iBAAiB,oDAE5B,CAAC;AAqEH,eAAO,MAAM,yBAAyB,uIACuD,CAAC"}
1
+ {"version":3,"file":"repository.d.ts","sourceRoot":"","sources":["../../src/forgejo/repository.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,YAAY,MAAM,uCAAuC,CAAC;AACtE,OAAO,KAAK,UAAU,MAAM,qCAAqC,CAAC;AAClE,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,KAAK,mBAAmB,EAAE,KAAK,WAAW,EAAmB,MAAM,eAAe,CAAC;AAE5F,MAAM,WAAW,eAAe;IAC9B,gEAAgE;IAChE,GAAG,EAAE,MAAM,CAAC;IACZ,gEAAgE;IAChE,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,wEAAwE;IACxE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,yEAAyE;IACzE,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,oBAAoB;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,OAAO,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,iBAAkB,SAAQ,QAAQ,CACjD,oBAAoB,EACpB,eAAe,EACf,oBAAoB,EACpB,KAAK,EACL,mBAAmB,CACpB;CAAG;AAEJ,eAAO,MAAM,iBAAiB,oDAE5B,CAAC;AAWH;;;;;;;;GAQG;AACH,eAAO,MAAM,IAAI,EAAE,WAAW,CAC5B,eAAe,EACf,UAAU,CAAC,UAAU,EACrB,oBAAoB,EAClB,YAAY,CAAC,kBAAkB,GAC/B,UAAU,CAAC,YAAY,GACvB,UAAU,CAAC,aAAa,GACxB,UAAU,CAAC,eAAe,CA+C7B,CAAC;AAEF,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;CAAwB,CAAC;AAE9C,eAAO,MAAM,yBAAyB,uIACuD,CAAC"}
@@ -1,68 +1,68 @@
1
1
  import type { Input } from 'alchemy/Input';
2
+ import type { ForgejoOpContext } from '@distilled.cloud/forgejo/Protocol';
2
3
  import * as Effect from 'effect/Effect';
3
4
  import type * as HttpClient from 'effect/unstable/http/HttpClient';
4
- import { ForgejoError } from './client.ts';
5
5
  /**
6
- * ★ WHAT EVERY OPERATION HERE NEEDS FROM THE RUNTIME — and it is NOT what the first draft said.
7
- *
8
- * These resources declared `ChildProcessSpawner`, copied from <estate>/proxmox, where it is there
9
- * because a PVE credential is minted by shelling out to `bao`. Nothing in this package shells
10
- * out: the token comes from `FORGEJO_TOKEN` in the environment. Declaring a service you never
11
- * use is not harmless — it makes a stack provide a layer for nothing, and it hides the one
12
- * service you DO need.
13
- *
14
- * What is actually required is `HttpClient`, for the reason in client.ts: Alchemy's own
15
- * providers call through Effect's client rather than the global `fetch`.
6
+ * ★ WHAT EVERY HANDLER NEEDS FROM THE CALLER'S RUNTIME, after Credentials are provided below.
7
+ * `Forgejo.ForgejoOpContext` is `Credentials | HttpClient.HttpClient`; `forgejoHandlers` closes
8
+ * over `CredentialsFromEnv` itself (same `FORGEJO_URL` / `FORGEJO_TOKEN` names client.ts read,
9
+ * still resolved at call time — see credentials.ts's own note on that), so a consuming stack
10
+ * only has to provide `HttpClient.HttpClient`, exactly as it did before this migration.
16
11
  */
17
12
  export type ForgejoRequirements = HttpClient.HttpClient;
18
- export type ForgejoSpec<Props extends object, Attributes> = {
19
- /** `repos/HomeFlare/homeflare`, `orgs/homeflare/labels/40` — one object. */
20
- readonly path: (props: Props) => string;
21
- /** `orgs/HomeFlare/repos`, `orgs/homeflare/labels` — where a new one is POSTed. */
22
- readonly collection: (props: Props) => string;
23
- readonly attributes: (live: Record<string, unknown>, props: Props) => Attributes | undefined;
24
- /** JSON body on create. ⚠️ Gitea takes JSON, not form encoding. */
25
- readonly createForm: (props: Props) => Record<string, unknown>;
26
- readonly updateForm?: (props: Props) => Record<string, unknown>;
13
+ /**
14
+ * One Forgejo object's distilled calls. `Live` is whatever the SDK decodes (a `Repository`, a
15
+ * `Label`, a composite for team membership, …) — no more untyped `Record<string, unknown>`.
16
+ *
17
+ * `E` is left to each resource file to declare (the union of the distilled operations' error
18
+ * types it actually calls, e.g. `RepoCreateHookError | RepoEditHookError | ...`), so a call site's
19
+ * precise per-operation error union flows straight through instead of being widened by hand here.
20
+ */
21
+ export type ForgejoSpec<Props extends object, Live, Attributes, E> = {
22
+ /** Already folds the SDK's `NotFound` to `undefined` via its own `catchTag` — see the note above. */
23
+ readonly fetchLive: (props: Props) => Effect.Effect<Live | undefined, E, ForgejoOpContext>;
24
+ readonly attributes: (live: Live, props: Props) => Attributes | undefined;
27
25
  readonly matches: (attributes: Attributes, props: Props) => boolean;
28
- /**
29
- * When the stable key is not enough for the wire path — org labels are addressed by numeric id.
30
- * `locate` lists or searches; `wirePath` builds update/delete paths from the live row.
31
- */
32
- readonly locate?: (props: Props) => Effect.Effect<Record<string, unknown> | undefined, ForgejoError, ForgejoRequirements>;
33
- readonly wirePath?: (props: Props, live: Record<string, unknown>) => string;
26
+ /** Absent for the two existence-only families (`OrgSecret`, `TeamMember`), which use `upsert` instead. */
27
+ readonly create?: (props: Props) => Effect.Effect<unknown, E, ForgejoOpContext>;
28
+ readonly update?: (props: Props, live: Live) => Effect.Effect<unknown, E, ForgejoOpContext>;
34
29
  /**
35
30
  * When create is PUT upsert rather than POST — org actions secrets have no POST and no GET-by-name.
36
31
  * `before === undefined` means absent; the callback decides whether to write. Existence-only families
37
32
  * noop when `before` is set because the API never returns the value for comparison.
38
33
  */
39
- readonly upsert?: (props: Props, before: Record<string, unknown> | undefined) => Effect.Effect<void, ForgejoError, ForgejoRequirements>;
34
+ readonly upsert?: (props: Props, before: Live | undefined) => Effect.Effect<void, E, ForgejoOpContext>;
35
+ readonly destroy: (props: Props, live: Live) => Effect.Effect<unknown, E, ForgejoOpContext>;
40
36
  };
41
- export declare const forgejoOperations: <Props extends object, Attributes>(spec: ForgejoSpec<Props, Attributes>) => {
42
- read: (props: Props) => Effect.Effect<Attributes | undefined, never, HttpClient.HttpClient>;
37
+ export declare const forgejoOperations: <Props extends object, Live, Attributes, E>(spec: ForgejoSpec<Props, Live, Attributes, E>) => {
38
+ read: (props: Props) => Effect.Effect<Attributes | undefined, E, ForgejoOpContext>;
43
39
  diff: (news: Input<Props>, output: Attributes | undefined) => Effect.Effect<{
44
40
  readonly action: 'update';
45
41
  } | {
46
42
  readonly action: 'noop';
47
43
  } | {
48
44
  readonly action: 'replace';
49
- } | undefined, never, HttpClient.HttpClient>;
50
- reconcile: (news: Props) => Effect.Effect<Attributes & ({} | null), ForgejoError, HttpClient.HttpClient>;
51
- destroy: (olds: Props) => Effect.Effect<void, ForgejoError, HttpClient.HttpClient>;
45
+ } | undefined, E, ForgejoOpContext>;
46
+ reconcile: (news: Props) => Effect.Effect<Attributes & ({} | null), E, ForgejoOpContext>;
47
+ destroy: (olds: Props) => Effect.Effect<void, E, ForgejoOpContext>;
52
48
  };
53
49
  /**
54
50
  * The five provider handlers for a spec'd Forgejo object, wired once.
55
51
  *
52
+ * ★ CREDENTIALS ARE PROVIDED HERE, ONCE, NOT BY EVERY CALLER. `CredentialsFromEnv` resolves
53
+ * `FORGEJO_URL` / `FORGEJO_TOKEN` on the calling fiber per request (credentials.ts's own note),
54
+ * so this closure keeps that laziness — nothing is captured at module load.
55
+ *
56
56
  * ⛔ IT STOPS AT THE HANDLERS AND DOES NOT RETURN THE LAYER — same reasoning as pveHandlers in
57
57
  * <estate>/proxmox: wrapping `Provider.effect` here needs casts against Alchemy's `Props<R>`.
58
58
  *
59
59
  * ⚠️ `list` answers empty — Forgejo index endpoints return the whole org; adoption stays explicit.
60
60
  */
61
- export declare const forgejoHandlers: <Props extends object, Attributes extends object>(spec: ForgejoSpec<Props, Attributes>) => {
61
+ export declare const forgejoHandlers: <Props extends object, Live, Attributes extends object, E>(spec: ForgejoSpec<Props, Live, Attributes, E>) => {
62
62
  list: () => Effect.Effect<never[], never, never>;
63
63
  read: ({ olds }: {
64
64
  olds: Props;
65
- }) => Effect.Effect<Attributes | undefined, never, HttpClient.HttpClient>;
65
+ }) => Effect.Effect<Attributes | undefined, E, HttpClient.HttpClient>;
66
66
  diff: ({ news, output }: {
67
67
  news: Input<Props>;
68
68
  output: Attributes | undefined;
@@ -72,12 +72,12 @@ export declare const forgejoHandlers: <Props extends object, Attributes extends
72
72
  readonly action: 'noop';
73
73
  } | {
74
74
  readonly action: 'replace';
75
- } | undefined, never, HttpClient.HttpClient>;
75
+ } | undefined, E, HttpClient.HttpClient>;
76
76
  reconcile: ({ news }: {
77
77
  news: Props;
78
- }) => Effect.Effect<Attributes, ForgejoError, HttpClient.HttpClient>;
78
+ }) => Effect.Effect<Attributes, E, HttpClient.HttpClient>;
79
79
  delete: ({ olds }: {
80
80
  olds: Props;
81
- }) => Effect.Effect<void, ForgejoError, HttpClient.HttpClient>;
81
+ }) => Effect.Effect<void, E, HttpClient.HttpClient>;
82
82
  };
83
83
  //# sourceMappingURL=resource.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../../src/forgejo/resource.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,KAAK,UAAU,MAAM,iCAAiC,CAAC;AACnE,OAAO,EAAE,YAAY,EAAW,MAAM,aAAa,CAAC;AAEpD;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,mBAAmB,GAAG,UAAU,CAAC,UAAU,CAAC;AAExD,MAAM,MAAM,WAAW,CAAC,KAAK,SAAS,MAAM,EAAE,UAAU,IAAI;IAC1D,4EAA4E;IAC5E,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,MAAM,CAAC;IACxC,mFAAmF;IACnF,QAAQ,CAAC,UAAU,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,MAAM,CAAC;IAC9C,QAAQ,CAAC,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE,KAAK,KAAK,UAAU,GAAG,SAAS,CAAC;IAC7F,mEAAmE;IACnE,QAAQ,CAAC,UAAU,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/D,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChE,QAAQ,CAAC,OAAO,EAAE,CAAC,UAAU,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC;IACpE;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,CAChB,KAAK,EAAE,KAAK,KACT,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EAAE,YAAY,EAAE,mBAAmB,CAAC,CAAC;IAC3F,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,MAAM,CAAC;IAC5E;;;;OAIG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,CAChB,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,KACxC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,YAAY,EAAE,mBAAmB,CAAC,CAAC;CAC7D,CAAC;AAWF,eAAO,MAAM,iBAAiB,GAAI,KAAK,SAAS,MAAM,EAAE,UAAU,QAC1D,WAAW,CAAC,KAAK,EAAE,UAAU,CAAC;kBAYf,KAAK;iBAQX,KAAK,CAAC,KAAK,CAAC,UAAU,UAAU,GAAG,SAAS;yBAIZ,QAAQ;;yBACF,MAAM;;yBAEtC,SAAS;;sBAIV,KAAK;oBA8BP,KAAK;CAOxB,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,GAAI,KAAK,SAAS,MAAM,EAAE,UAAU,SAAS,MAAM,QACvE,WAAW,CAAC,KAAK,EAAE,UAAU,CAAC;IAIlC,IAAI;IACJ,IAAI,aAAa;QAAE,IAAI,EAAE,KAAK,CAAA;KAAE;IAChC,IAAI,qBAAqB;QAAE,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;QAAC,MAAM,EAAE,UAAU,GAAG,SAAS,CAAA;KAAE;yBA7DlC,QAAQ;;yBACF,MAAM;;yBAEtC,SAAS;;IA4D5B,SAAS,aAAa;QAAE,IAAI,EAAE,KAAK,CAAA;KAAE;IACrC,MAAM,aAAa;QAAE,IAAI,EAAE,KAAK,CAAA;KAAE;CAErC,CAAC"}
1
+ {"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../../src/forgejo/resource.ts"],"names":[],"mappings":"AAuCA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAE3C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAC1E,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,KAAK,UAAU,MAAM,iCAAiC,CAAC;AAEnE;;;;;;GAMG;AACH,MAAM,MAAM,mBAAmB,GAAG,UAAU,CAAC,UAAU,CAAC;AAExD;;;;;;;GAOG;AACH,MAAM,MAAM,WAAW,CAAC,KAAK,SAAS,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,IAAI;IACnE,qGAAqG;IACrG,QAAQ,CAAC,SAAS,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,GAAG,SAAS,EAAE,CAAC,EAAE,gBAAgB,CAAC,CAAC;IAC3F,QAAQ,CAAC,UAAU,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,KAAK,UAAU,GAAG,SAAS,CAAC;IAC1E,QAAQ,CAAC,OAAO,EAAE,CAAC,UAAU,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC;IACpE,0GAA0G;IAC1G,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,EAAE,gBAAgB,CAAC,CAAC;IAChF,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,KAAK,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,EAAE,gBAAgB,CAAC,CAAC;IAC5F;;;;OAIG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,CAChB,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,IAAI,GAAG,SAAS,KACrB,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,EAAE,gBAAgB,CAAC,CAAC;IAC9C,QAAQ,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,KAAK,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,EAAE,gBAAgB,CAAC,CAAC;CAC7F,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,KAAK,SAAS,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,QACnE,WAAW,CAAC,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC;kBAExB,KAAK;iBAQX,KAAK,CAAC,KAAK,CAAC,UAAU,UAAU,GAAG,SAAS;yBAIZ,QAAQ;;yBACF,MAAM;;yBAEtC,SAAS;;sBAIV,KAAK;oBAgCP,KAAK;CAOxB,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,eAAe,GAAI,KAAK,SAAS,MAAM,EAAE,IAAI,EAAE,UAAU,SAAS,MAAM,EAAE,CAAC,QAChF,WAAW,CAAC,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC;IAM3C,IAAI;IACJ,IAAI,aAAa;QAAE,IAAI,EAAE,KAAK,CAAA;KAAE;IAChC,IAAI,qBAAqB;QAAE,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;QAAC,MAAM,EAAE,UAAU,GAAG,SAAS,CAAA;KAAE;yBArElC,QAAQ;;yBACF,MAAM;;yBAEtC,SAAS;;IAoE5B,SAAS,aAAa;QAAE,IAAI,EAAE,KAAK,CAAA;KAAE;IACrC,MAAM,aAAa;QAAE,IAAI,EAAE,KAAK,CAAA;KAAE;CAErC,CAAC"}
@@ -6,20 +6,28 @@
6
6
  * scopes only cap it (<estate>/openbao/src/forgejo-bootstrap.ts).
7
7
  *
8
8
  * ★ READ OFF THE LIVE 16.0.3 SWAGGER (127.0.0.1:3000/swagger.v1.json, 2026-09-14): GET, PUT and
9
- * DELETE `/teams/{id}/members/{username}` answer 200, 204 and 204, and 404 when absent. The team
10
- * is addressed by numeric id, so it is found by name first — the same seam as org-team.ts.
9
+ * DELETE `/teams/{id}/members/{username}` answer 200, 204 and 204, and 404 when absent. Now
10
+ * `organization.orgListTeamMember` / `orgAddTeamMember` / `orgRemoveTeamMember`; the team is
11
+ * still addressed by numeric id, found by name first via `orgListTeams` — the same seam as
12
+ * org-team.ts.
11
13
  *
12
14
  * ⚠️ EXISTENCE IS THE WHOLE OBJECT. Nothing about a membership can be updated, so `matches` is
13
15
  * always true; a different team or user is a different resource id in alchemy.run.ts.
14
16
  *
15
17
  * ⚠️ THE TEAM ITSELF IS ONLY LOCATED HERE, NEVER MANAGED. A missing team fails the plan by name
16
- * instead of folding into "absent", because a PUT can add a member but cannot create a team.
18
+ * instead of folding into "absent" — `ForgejoTeamNotFoundError` is this family's own domain
19
+ * error, not one of the distilled SDK's typed HTTP errors, so the `catchTag('NotFound', ...)`
20
+ * at the end of `fetchLive` below (see resource.ts's header for why every `fetchLive` folds its
21
+ * own `NotFound` rather than one shared helper) never touches it. A PUT can add a member but
22
+ * cannot create a team.
17
23
  *
18
24
  * ⛔ TOKEN NEEDS `write:organization` TO ADD OR REMOVE, and an identity allowed to manage the team.
19
25
  */
20
26
  import { Resource } from 'alchemy';
21
27
  import * as Provider from 'alchemy/Provider';
22
- import { type ForgejoRequirements } from './resource.ts';
28
+ import * as organization from '@distilled.cloud/forgejo/organization';
29
+ import * as Effect from 'effect/Effect';
30
+ import { type ForgejoRequirements, type ForgejoSpec } from './resource.ts';
23
31
  export interface TeamMemberProps {
24
32
  org: string;
25
33
  /** Team name as Forgejo shows it, e.g. `Owners`. */
@@ -35,5 +43,41 @@ export interface TeamMemberAttributes {
35
43
  export interface ForgejoTeamMember extends Resource<'Forgejo.TeamMember', TeamMemberProps, TeamMemberAttributes, never, ForgejoRequirements> {
36
44
  }
37
45
  export declare const ForgejoTeamMember: import("alchemy").ResourceClass<ForgejoTeamMember>;
46
+ declare const ForgejoTeamNotFoundError_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 & {
47
+ readonly _tag: "ForgejoTeamNotFoundError";
48
+ } & Readonly<A>;
49
+ export declare class ForgejoTeamNotFoundError extends ForgejoTeamNotFoundError_base<{
50
+ readonly message: string;
51
+ }> {
52
+ }
53
+ interface TeamMemberLive {
54
+ readonly teamId: number;
55
+ readonly login: string;
56
+ }
57
+ /** ★ EXPORTED for direct testing with an explicit fake `Credentials` layer — see repository.ts. */
58
+ export declare const spec: ForgejoSpec<TeamMemberProps, TeamMemberLive, TeamMemberAttributes, organization.OrgAddTeamMemberError | organization.OrgRemoveTeamMemberError | organization.OrgListTeamMemberError | organization.OrgListTeamsError | ForgejoTeamNotFoundError>;
59
+ export declare const handlers: {
60
+ list: () => Effect.Effect<never[], never, never>;
61
+ read: ({ olds }: {
62
+ olds: TeamMemberProps;
63
+ }) => Effect.Effect<TeamMemberAttributes | undefined, import("@distilled.cloud/cloudflare/Errors").BadGateway | import("@distilled.cloud/cloudflare/Errors").ConfigError | organization.Forbidden | import("@distilled.cloud/forgejo/Errors").ForgejoParseError | ForgejoTeamNotFoundError | import("@distilled.cloud/cloudflare/Errors").GatewayTimeout | import("effect/unstable/http/HttpClientError").HttpClientError | import("@distilled.cloud/cloudflare/Errors").InternalServerError | organization.NotFound | import("@distilled.cloud/cloudflare/Errors").ServiceUnavailable | import("@distilled.cloud/cloudflare/Errors").TooManyRequests | import("@distilled.cloud/cloudflare/Errors").Unauthorized | import("@distilled.cloud/forgejo/Errors").UnknownForgejoError, import("effect/unstable/http/HttpClient").HttpClient>;
64
+ diff: ({ news, output }: {
65
+ news: import("alchemy").Input<TeamMemberProps>;
66
+ output: TeamMemberAttributes | undefined;
67
+ }) => Effect.Effect<{
68
+ readonly action: 'update';
69
+ } | {
70
+ readonly action: 'noop';
71
+ } | {
72
+ readonly action: 'replace';
73
+ } | undefined, import("@distilled.cloud/cloudflare/Errors").BadGateway | import("@distilled.cloud/cloudflare/Errors").ConfigError | organization.Forbidden | import("@distilled.cloud/forgejo/Errors").ForgejoParseError | ForgejoTeamNotFoundError | import("@distilled.cloud/cloudflare/Errors").GatewayTimeout | import("effect/unstable/http/HttpClientError").HttpClientError | import("@distilled.cloud/cloudflare/Errors").InternalServerError | organization.NotFound | import("@distilled.cloud/cloudflare/Errors").ServiceUnavailable | import("@distilled.cloud/cloudflare/Errors").TooManyRequests | import("@distilled.cloud/cloudflare/Errors").Unauthorized | import("@distilled.cloud/forgejo/Errors").UnknownForgejoError, import("effect/unstable/http/HttpClient").HttpClient>;
74
+ reconcile: ({ news }: {
75
+ news: TeamMemberProps;
76
+ }) => Effect.Effect<TeamMemberAttributes, import("@distilled.cloud/cloudflare/Errors").BadGateway | import("@distilled.cloud/cloudflare/Errors").ConfigError | organization.Forbidden | import("@distilled.cloud/forgejo/Errors").ForgejoParseError | ForgejoTeamNotFoundError | import("@distilled.cloud/cloudflare/Errors").GatewayTimeout | import("effect/unstable/http/HttpClientError").HttpClientError | import("@distilled.cloud/cloudflare/Errors").InternalServerError | organization.NotFound | import("@distilled.cloud/cloudflare/Errors").ServiceUnavailable | import("@distilled.cloud/cloudflare/Errors").TooManyRequests | import("@distilled.cloud/cloudflare/Errors").Unauthorized | import("@distilled.cloud/forgejo/Errors").UnknownForgejoError, import("effect/unstable/http/HttpClient").HttpClient>;
77
+ delete: ({ olds }: {
78
+ olds: TeamMemberProps;
79
+ }) => Effect.Effect<void, import("@distilled.cloud/cloudflare/Errors").BadGateway | import("@distilled.cloud/cloudflare/Errors").ConfigError | organization.Forbidden | import("@distilled.cloud/forgejo/Errors").ForgejoParseError | ForgejoTeamNotFoundError | import("@distilled.cloud/cloudflare/Errors").GatewayTimeout | import("effect/unstable/http/HttpClientError").HttpClientError | import("@distilled.cloud/cloudflare/Errors").InternalServerError | organization.NotFound | import("@distilled.cloud/cloudflare/Errors").ServiceUnavailable | import("@distilled.cloud/cloudflare/Errors").TooManyRequests | import("@distilled.cloud/cloudflare/Errors").Unauthorized | import("@distilled.cloud/forgejo/Errors").UnknownForgejoError, import("effect/unstable/http/HttpClient").HttpClient>;
80
+ };
38
81
  export declare const ForgejoTeamMemberProvider: () => import("effect/Layer").Layer<Provider.Provider<ForgejoTeamMember>, never, import("effect/unstable/http/HttpClient").HttpClient>;
82
+ export {};
39
83
  //# sourceMappingURL=team-member.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"team-member.d.ts","sourceRoot":"","sources":["../../src/forgejo/team-member.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAG7C,OAAO,EAAE,KAAK,mBAAmB,EAAmB,MAAM,eAAe,CAAC;AAE1E,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,oDAAoD;IACpD,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,iBAAkB,SAAQ,QAAQ,CACjD,oBAAoB,EACpB,eAAe,EACf,oBAAoB,EACpB,KAAK,EACL,mBAAmB,CACpB;CAAG;AAEJ,eAAO,MAAM,iBAAiB,oDAAoD,CAAC;AAgDnF,eAAO,MAAM,yBAAyB,uIACuD,CAAC"}
1
+ {"version":3,"file":"team-member.d.ts","sourceRoot":"","sources":["../../src/forgejo/team-member.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,YAAY,MAAM,uCAAuC,CAAC;AAEtE,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,KAAK,mBAAmB,EAAE,KAAK,WAAW,EAAmB,MAAM,eAAe,CAAC;AAE5F,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,oDAAoD;IACpD,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,iBAAkB,SAAQ,QAAQ,CACjD,oBAAoB,EACpB,eAAe,EACf,oBAAoB,EACpB,KAAK,EACL,mBAAmB,CACpB;CAAG;AAEJ,eAAO,MAAM,iBAAiB,oDAAoD,CAAC;;;;AAEnF,qBAAa,wBAAyB,SAAQ,8BAA6C;IACzF,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B,CAAC;CAAG;AAEL,UAAU,cAAc;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAgBD,mGAAmG;AACnG,eAAO,MAAM,IAAI,EAAE,WAAW,CAC5B,eAAe,EACf,cAAc,EACd,oBAAoB,EAClB,YAAY,CAAC,qBAAqB,GAClC,YAAY,CAAC,wBAAwB,GACrC,YAAY,CAAC,sBAAsB,GACnC,YAAY,CAAC,iBAAiB,GAC9B,wBAAwB,CAqB3B,CAAC;AAEF,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;CAAwB,CAAC;AAE9C,eAAO,MAAM,yBAAyB,uIACuD,CAAC"}