npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@chcaa/strapi-utils

v0.5.0

Published

Various strapi utils

Downloads

13

Readme

@chcaa/strapi-utils

A set of utility modules to make it easier to work with strapi.

Installation

Run npm install @chcaa/strapi-utils

Modules

@chcaa/strapi-utils contains the following modules:

lifecycle/CollectionChangedHook

Strapi's native collection lifecycle hooks are fine when you only care about changes to the collection itself, but if you also need to know when relations have changed, e.g. to update a slug field, it can become a complex task to handle when only using the native hooks.

The @chcaa/strapi-utils/lifecycle.CollectionChangedHook makes it easy to be notified whenever a collection or it's relations has changed simply by registering the paths of interest.

Create an instance using let cch = new CollectionChangedHook(). This would typically be done in strapi's bootstrap() startup hook, but could be done anywhere.

The constructor can take an optional options object to configure the registered listeners (can be overwritten pr. listener). The following options can be set:

options:object

  • onSelf:object
    • emitBeforeCreate:boolean default=false should listeners observing their own collection receive beforeCreate events?
    • emitAfterCreate:boolean default=true should listeners observing their own collection receive afterCreate events?
    • emitBeforeUpdate:boolean default=false should listeners observing their own collection receive beforeUpdate events?
    • emitAfterUpdate:boolean default=true should listeners observing their own collection receive afterUpdate events?
    • emitBeforeDelete:boolean default=false should listeners observing their own collection receive beforeDelete events?
    • emitAfterDelete:boolean default=false should listeners observing their own collection receive afterDelete events?
  • causedBy:object
    • includeBeforeState:boolean default=false include before state of changed entries on afterUpdate events. To e.g. create diffs
    • includeAfterState:boolean default=false include after state of changed entries on afterUpdate events. To e.g. create diffs in combination with includeBeforeState
    • calculateAttributesChanged:boolean default=false calculate and include which attributes did change on afterUpdate events. For each entity changed an object with properties matching the entities will be included with true|false if the attribute value was changed or not. If not set explicitly to false includeBeforeState and includeAfterState will be included as well
    • includeInferredAfterState:boolean default=false include inferred after state of changed entries on beforeUpdate events on listeners observing the collection itself. The state is inferred using the current state and then strapiEvent.params.data object.
    • calculateInferredAttributesChanged:boolean, default=false calculate and include in beforeUpdate events on listeners observing the collection itself which inferred attributes would change. The changes are based on comparing the current state with the inferred after state.

      NOTE includeInferredAfterState and calculateInferredAttributesChanged cannot infer changes to component data due to insufficient info in the strapiEvent, for components use the afterState and calculateAttributesChanges in afterXXX events.

    HINT ids, beforeState, afterState and changedAttributes arrays are all ordered in the same way so to find the matching object of beforeState, afterState and changedAttributes can be done as:

    for (let i = 0; i < ids.length; i ++) {
        let beforeEntity = causedBy.beforeState[i];
        let afterEntity = causedBy.afterState[i];
        let attributesChanged = causedBy.attributesChanged[i];
    }

Register listeners for each collection to be notified about changes to using:

register(collectionUid, relationsPaths, listener, options)

  • collectionUid:string the uid of the collection to be notified about changes to
  • relationPaths:string[] one or more relation paths to track for changes.
  • listener:function(event:object) the function to call when changes occur
    • event.idsAffected:number[] the ids of the collection objects affected by the change
    • event.causedBy:object an object with information about the changes
      • relationPath:string one of the registered paths which resulted in the change event
      • collectionUid:string the uid of the collection which changed
      • ids:number[]: the ids of the collection which changed
      • eventType:string describes what happened to the affected path in form of one of "afterCreate"|"afterUpdate"|"afterDelete" and equivalent "beforeXXX" if enabled in options
      • strapiEvent:object the strapi-event that triggered this event
    • options:object [optional] a set of options to configure the listener (overwrites the default options set in the constructor). See the constructor above for details

All relational paths must point to a collection but are allowed to have intermediate "components" in their path.

To get notifications about the collection itself register an empty string ("") along with the other paths of interest.

In the example below we want to be notified about changes to movies when the movie itself ("") changes, directors of the movie ("directors") changes or an actor in the cast ("cast.actor") changes.

const { CollectionChangedHook } = require('@chcaa/strapi-utils/lifecycle');

let cch = new CollectionChangedHook({ onSelf: { emitBeforeEvents: true }});

cch.register("api::movie.movie", ["", "directors", "cast.actor"], ({ idsAffected, causedBy }) => {
    if (causedBy.eventType.startsWith('after')) { // we are also listening for beforeEvents on the path "", but for now we only react on afterXxx
      for (let idChunk of _.chunk(idsAffected, 100)) { // use lodash to chunk the ids into sub-arrays of 100 ids each
        let entries = await strapi.entityService.findMany('api::movie.movie', {
          filters: {
            $and: [{ id: { $in: idChunk } }]
          },
          populate: {
            genres: true,
            cast: { populate: { actor: true } }
          }
        });
        // do something with the entries
      }
    }
});

Warning! Prevent Infinite Loops

  1. Do only modify data of paths not listened to or objects and relations attached to the owning collection identified by idsAffected.

Performance Note

Nested paths, especially ones including "components" can cause performance issues on larger datasets as they require a lot of joins for strapi to resolve the relations from the database.

To unregister use: unregister(collectionUid, relationaths, listener)

  • collectionUid:string the uid of the collection to NOT be notified about changes to anymore. E.g. "api::movie.movie"
  • relationPaths:string[] the relational paths to stop listening to
  • listener:function the listener to unregister (must be the same function which was passed to register()

If the listener was registered for the relationPaths ["a", "b"] but only unregisters for ["a"] it will still be notified about changes to "b".