@affordant/react 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,8 +1,14 @@
1
1
  # @affordant/react
2
2
 
3
- React adapter for [Affordant](https://leroy-florian.github.io/Affordant/). Gate your UI on what the server offers, and invoke affordances with **either invoker** the vanilla Promise one or the Effect one. The framework axis (React) and the effect-system axis (Promise / Effect) stay orthogonal and composable.
3
+ React adapter for [Affordant](https://leroy-florian.github.io/Affordant/). Gate your UI on what the server offers, and follow affordances with hooks. No runtime dependency beyond React.
4
4
 
5
- ## Gating (pure, invoker-agnostic)
5
+ ## Install
6
+
7
+ ```sh
8
+ npm install @affordant/react react
9
+ ```
10
+
11
+ ## Gating
6
12
 
7
13
  ```tsx
8
14
  import { useAffordance } from '@affordant/react'
@@ -14,7 +20,7 @@ function CancelButton({ order }) {
14
20
  }
15
21
  ```
16
22
 
17
- ## Vanilla (Promise) invoker
23
+ ## Following
18
24
 
19
25
  ```tsx
20
26
  import { useAffordance, useFollow } from '@affordant/react'
@@ -27,20 +33,20 @@ const { run, running } = useFollow()
27
33
  </button>
28
34
  ```
29
35
 
30
- ## Effect invoker
36
+ `run` resolves with the raw `Response` and re-throws on failure, with `error` set.
31
37
 
32
- The Effect path composes [`@affordant/effect`](https://www.npmjs.com/package/@affordant/effect) with the [`effect-react-bridge`](https://www.npmjs.com/package/effect-react-bridge) runtime — no Effect coupling leaks into the vanilla path.
38
+ ## Using Effect
33
39
 
34
- ```ts
35
- import { makeEffectHooks } from 'effect-react-bridge'
36
- import { makeAffordanceHooks } from '@affordant/react/effect'
40
+ There is no Effect entry point, and none is needed: `run` (and the underlying `follow`) return a `Promise<Response>`, which drops into Effect with a one-line wrap — `Effect.tryPromise(() => follow(action, init))`. Affordant stays Effect-compatible without shipping an Effect dependency.
37
41
 
38
- const bridge = makeEffectHooks({ runtime })
39
- const { useFollow } = makeAffordanceHooks(bridge)
40
- // useFollow().run(action, init) is now an interruptible Effect with a typed error
41
- ```
42
+ ## API
43
+
44
+ | Export | Description |
45
+ |---|---|
46
+ | `useAffordance(resource, rel)` | `{ can, action }` — memoised, null-safe gating over `can` / `actionFor`. |
47
+ | `useFollow()` | `{ running, error, run }` — follow an action, tracking request state. |
42
48
 
43
- `react` is a peer dependency; `effect`, `effect-react-bridge` and `@affordant/effect` are optional peers, needed only for `@affordant/react/effect`.
49
+ `react` is a peer dependency.
44
50
 
45
51
  ## License
46
52
 
package/dist/index.d.ts CHANGED
@@ -27,9 +27,9 @@ export interface UseFollowResult extends FollowState {
27
27
  readonly run: (action: HateoasAction, init?: FollowInit) => Promise<Response>;
28
28
  }
29
29
  /**
30
- * The vanilla (Promise) invoker as a hook: tracks `running` / `error` around
31
- * the client's `follow`. This is one of Affordant's two interchangeable
32
- * invokers; for the Effect one, see `@affordant/react/effect`.
30
+ * Follow an affordance from a hook: tracks `running` / `error` around the
31
+ * client's `follow`. `run` resolves with the raw `Response` and re-throws on
32
+ * failure (with `error` set).
33
33
  */
34
34
  export declare function useFollow(): UseFollowResult;
35
35
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -13,9 +13,9 @@ export function useAffordance(resource, rel) {
13
13
  return useMemo(() => ({ can: can(resource, rel), action: actionFor(resource, rel) }), [resource, rel]);
14
14
  }
15
15
  /**
16
- * The vanilla (Promise) invoker as a hook: tracks `running` / `error` around
17
- * the client's `follow`. This is one of Affordant's two interchangeable
18
- * invokers; for the Effect one, see `@affordant/react/effect`.
16
+ * Follow an affordance from a hook: tracks `running` / `error` around the
17
+ * client's `follow`. `run` resolves with the raw `Response` and re-throws on
18
+ * failure (with `error` set).
19
19
  */
20
20
  export function useFollow() {
21
21
  const [state, setState] = useState({ running: false, error: null });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@affordant/react",
3
- "version": "0.2.0",
4
- "description": "React adapter for Affordant: gate UI on the server's affordances and invoke them with either invoker the vanilla Promise one or the Effect one.",
3
+ "version": "0.3.0",
4
+ "description": "React adapter for Affordant: gate UI on the server's affordances and follow them with hooks. Zero runtime dependencies beyond React.",
5
5
  "license": "MIT",
6
6
  "author": "Florian Leroy",
7
7
  "repository": {
@@ -26,10 +26,6 @@
26
26
  ".": {
27
27
  "types": "./dist/index.d.ts",
28
28
  "import": "./dist/index.js"
29
- },
30
- "./effect": {
31
- "types": "./dist/effect.d.ts",
32
- "import": "./dist/effect.js"
33
29
  }
34
30
  },
35
31
  "files": [
@@ -43,25 +39,7 @@
43
39
  "@affordant/contract": "^0.2.0"
44
40
  },
45
41
  "peerDependencies": {
46
- "react": ">=18",
47
- "effect": "^3.21.0",
48
- "effect-react-bridge": ">=0.1.0",
49
- "@affordant/effect": ">=0.1.0"
50
- },
51
- "peerDependenciesMeta": {
52
- "effect": {
53
- "optional": true
54
- },
55
- "effect-react-bridge": {
56
- "optional": true
57
- },
58
- "@affordant/effect": {
59
- "optional": true
60
- }
61
- },
62
- "devDependencies": {
63
- "@affordant/effect": "*",
64
- "effect-react-bridge": "*"
42
+ "react": ">=18"
65
43
  },
66
44
  "scripts": {
67
45
  "build": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.build.json",
package/dist/effect.d.ts DELETED
@@ -1,29 +0,0 @@
1
- import type { EffectHooks } from 'effect-react-bridge';
2
- import { type FollowError } from '@affordant/effect';
3
- import type { FollowInit } from 'affordant';
4
- import type { HateoasAction } from '@affordant/contract';
5
- export interface EffectFollowResult {
6
- readonly running: boolean;
7
- readonly error: FollowError | null;
8
- readonly run: (action: HateoasAction, init?: FollowInit) => Promise<Response>;
9
- }
10
- export interface AffordanceEffectHooks {
11
- /**
12
- * The Effect invoker as a hook, run through the supplied
13
- * `effect-react-bridge` runtime: tracks `running` / `error` and interrupts
14
- * the request when the component unmounts.
15
- */
16
- useFollow(): EffectFollowResult;
17
- }
18
- /**
19
- * Compose the Effect invoker (`@affordant/effect`) with an
20
- * `effect-react-bridge` runtime to get React hooks. The bridge stays
21
- * domain-agnostic; this is the thin Affordant-specific glue.
22
- *
23
- * ```ts
24
- * const { useEffectFn } = makeEffectHooks({ runtime })
25
- * const affordances = makeAffordanceHooks({ useEffectFn } as EffectHooks<never>)
26
- * ```
27
- */
28
- export declare function makeAffordanceHooks<R>(hooks: EffectHooks<R>): AffordanceEffectHooks;
29
- //# sourceMappingURL=effect.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"effect.d.ts","sourceRoot":"","sources":["../src/effect.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AACtD,OAAO,EAA0B,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAC5E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAC3C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAExD,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,CAAA;IAClC,QAAQ,CAAC,GAAG,EAAE,CAAC,MAAM,EAAE,aAAa,EAAE,IAAI,CAAC,EAAE,UAAU,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;CAC9E;AAED,MAAM,WAAW,qBAAqB;IACpC;;;;OAIG;IACH,SAAS,IAAI,kBAAkB,CAAA;CAChC;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,qBAAqB,CASnF"}
package/dist/effect.js DELETED
@@ -1,20 +0,0 @@
1
- import { follow as effectFollow } from '@affordant/effect';
2
- /**
3
- * Compose the Effect invoker (`@affordant/effect`) with an
4
- * `effect-react-bridge` runtime to get React hooks. The bridge stays
5
- * domain-agnostic; this is the thin Affordant-specific glue.
6
- *
7
- * ```ts
8
- * const { useEffectFn } = makeEffectHooks({ runtime })
9
- * const affordances = makeAffordanceHooks({ useEffectFn } as EffectHooks<never>)
10
- * ```
11
- */
12
- export function makeAffordanceHooks(hooks) {
13
- return {
14
- useFollow() {
15
- const { running, error, run } = hooks.useEffectFn((action, init) => effectFollow(action, init));
16
- return { running, error, run };
17
- },
18
- };
19
- }
20
- //# sourceMappingURL=effect.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"effect.js","sourceRoot":"","sources":["../src/effect.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,IAAI,YAAY,EAAoB,MAAM,mBAAmB,CAAA;AAmB5E;;;;;;;;;GASG;AACH,MAAM,UAAU,mBAAmB,CAAI,KAAqB;IAC1D,OAAO;QACL,SAAS;YACP,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,KAAK,CAAC,WAAW,CAC/C,CAAC,MAAqB,EAAE,IAAiB,EAAE,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,CACzE,CAAA;YACD,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,CAAA;QAChC,CAAC;KACF,CAAA;AACH,CAAC"}