refactor: the great hook renaming

This commit is contained in:
cha0s 2022-03-08 16:03:06 -06:00
parent 218145bcc9
commit 8667d32c14
74 changed files with 535 additions and 228 deletions

View File

@ -137,7 +137,7 @@ import {Hooks} from '@flecks/core';
export default {
[Hooks]: {
'@flecks/core/starting': () => {
'@flecks/core.starting': () => {
console.log('hello, gorgeous');
},
},
@ -149,14 +149,14 @@ you the way you deserve to be treated.
Just to give you an idea of the power of hooks, some will be listed here:
- `@flecks/core/config`:
- `@flecks/core.config`:
> Define default configuration.
- `@flecks/docker/containers`:
- `@flecks/docker.containers`:
> Define [Docker](https://www.docker.com/) containers to run alongside your application to
develop e.g. DB models, redis commands, etc. without having to worry about installing stuff.
- `@flecks/http/server/request.route`:
- `@flecks/http/server.request.route`:
> Define [Express](http://expressjs.com/) middleware that runs when an HTTP route is hit.
- `@flecks/server/up`:
- `@flecks/server.up`:
> Do things when server comes up (e.g. DB connection, HTTP listener, make you coffee, etc).
...and so many more.

View File

@ -22,4 +22,4 @@
- [ ] autogenerated config dox page
- [x] remove `invokeParallel()`
- [x] Specialize `invokeReduce()` with `invokeMerge()`.
- [ ] Rename all hooks to dot-first notation; rewrite `lookupFlecks()`.
- [x] Rename all hooks to dot-first notation; rewrite `lookupFlecks()`.

View File

@ -0,0 +1,325 @@
# Hooks
Hooks are how everything happens in flecks. There are many hooks and the hooks provided by flecks are documented at the [hooks reference page](ADDME).
To define hooks (and turn your plain ol' boring JS modules into beautiful interesting flecks), you only have to import the `Hooks` symbol and key your default export:
```javascript
import {Hooks} from '@flecks/core';
export default {
[Hooks]: {
'@flecks/core.starting': () => {
console.log('hello, gorgeous');
},
},
};
```
**Note:** All hooks recieve an extra final argument, which is the flecks instance.
## Types
 
### `invoke(hook, ...args)`
Invokes all hook implementations and returns the results keyed by the implementing flecks' paths.
 
### `invokeComposed(hook, initial, ...args)`
### `invokeComposedAsync(hook, initial, ...args)`
See: [function composition](https://www.educative.io/edpresso/function-composition-in-javascript).
`initial` is passed to the first implementation, which returns a result which is passed to the second implementation, which returns a result which is passed to the third implementation, etc.
Composed hooks are ordered.
 
### `invokeFlat(hook, ...args)`
Invokes all hook implementations and returns the results as an array.
 
### `invokeFleck(hook, fleck, ...args)`
Invoke a single fleck's hook implementation and return the result.
 
### `invokeMerge(hook, ...args)`
### `invokeMergeAsync(hook, ...args)`
Invokes all hook implementations and returns the result of merging all implementations' returned objects together.
 
### `invokeReduce(hook, reducer, initial, ...args)`
### `invokeReduceAsync(hook, reducer, initial, ...args)`
See: [Array.prototype.reduce()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce)
Invokes hook implementations one at a time, their results being passed to the reducer as `currentValue`. Returns the final reduction.
 
### `invokeSequential(Async)?(hook, ...args)`
Invokes all hook implementations, one after another. In the async variant, each implementation's result is `await`ed before invoking the next implementation.
Sequential hooks are ordered.
 
## Idioms
### `flecks.gather(hook, options)`
Gathering is useful when your fleck defines some sort of specification, and then expects its sibling flecks to actually implement it. Examples of this in flecks would be:
- Models, defined through `@flecks/db/server.models`.
- Packets, defined through `@flecks/socket.packets`.
One constraint of using `flecks.gather()` is that whatever you are gathering must be able to be extended as a class. You can't `flecks.gather()` plain objects, numbers, strings... you get the idea.
The most basic usage:
```javascript
const Gathered = flecks.gather('my-gather-hook');
```
Suppose `my-gather-hook` above resulted in gathering two classes, `Foo` and `Bar`. In this case, `Gathered` would be such as:
```javascript
import {ById, ByType} from '@flecks/core';
const Gathered = {
1: Bar,
2: Foo,
'Bar': Bar,
'Foo': Foo,
[ById]: {
1: Bar,
2: Foo,
},
[ByType]: {
'Bar': Bar,
'Foo': Foo,
},
};
```
`flecks.gather()` gives each of your classes a numeric (nonzero) ID as well as a type name. It also merges all numeric keys and type labels together into the result, so `Gathered[1] === Gathered.Bar` would evaluate to `true` in the example above.
The symbol keys `ById` and `ByType` are useful if you need to iterate over *either* all IDs or all types. Since the numeric IDs and types are merged, iterating over the entire `Gathered` object would otherwise result in duplicates.
Each class gathered by `flecks.gather()` will be extended with two properties by default: `id` and `type`. These correspond to the ID and type referenced above, and are useful for e.g. serialization.
Following from the example above:
```javascript
const foo = new Gathered.Foo();
assert(foo.id === 2);
assert(foo.type === 'Foo);
```
`flecks.gather()` also supports some options:
```javascript
{
// The property added when extending the class to return the numeric ID.
idAttribute = 'id',
// The property added when extending the class to return the type.
typeAttribute = 'type',
// A function called with the `Gathered` object to allow checking validity.
check = () => {},
}
```
As an example, when `@flecks/db/server` gathers models, `typeAttribute` is set to `name`, because Sequelize requires its model classes to have a unique `name` property.
**Note:** the numeric IDs are useful for efficient serialization between the client and server, but **if you are using this property, ensure that `flecks.gather()` is called equivalently on both the client and the server**. As a rule of thumb, if you have serializable `Gathered`s, they should be invoked and defined in `your-fleck`, and not in `your-fleck/[platform]`, so that they are invoked for every platform.
#### `Flecks.provide(context, options)`
Complementary to gather hooks above, `Flecks.provide()` allows you to ergonomically provide your flecks' implementations to a gather hook.
Here's an example of how you could manually provide `@flecks/db/server.models` in your own fleck:
```javascript
import {Hooks} foom '@flecks/core';
import SomeModel from './models/some-model';
import AnotherModel from './models/another-model';
export default {
[Hooks]: {
'@flecks/db/server.models': () => ({
SomeModel,
AnotherModel,
}),
},
};
```
If you think about the example above, you might realize that it will become a lot of typing to keep adding new models over time. Provider hooks exist to reduce this maintenance burden for you.
Webpack provides an API called [require.context](https://v4.webpack.js.org/guides/dependency-management/#requirecontext), and the flecks provider is optimized to work with this API.
Supposing our fleck is structured like so:
```
index.js
models/
├─ some-model.js
└─ another-model.js
```
then, this `index.js`:
```javascript
import {Flecks, Hooks} from '@flecks/core';
export default {
[Hooks]: {
'@flecks/db/server.models': Flecks.provide(require.context('./models', false, /\.js$/)),
},
};
```
is *exactly equivalent* to the gather example above. By default, `Flecks.provide()` *CamelCase*s the paths, so `some-model` becomes `SomeModel`, just as in the example above.
`Flecks.provide()` also supports some options:
```javascript
{
// The transformation used on the class path.
transformer = camelCase,
}
```
**Note:** There is no requirement to use `Flecks.provide()`, it is merely a convenience.
### Decorator hooks
When a Model (or any other) is gathered as above, an implicit hook is called: `${hook}.decorate`. This allows other flecks to decorate whatever has been gathered:
```javascript
import {Hooks} from '@flecks/core';
export default {
[Hooks]: {
'@flecks/db/server.models.decorate': (Models) => {
return {
...Models,
User: class extends Models.User {
// Let's mix in some logging...
constructor(...args) {
super(...args);
console.log ('Another user decorated!');
}
},
};
},
},
};
```
#### `Flecks.decorate(context, options)`
As with above, there exists an API for making the maintenance of decorators more ergonomic.
Supposing our fleck is structured like so:
```
index.js
models/
└─ decorators/
└─ user.js
```
and supposing that `./models/decorators/user.js` is written like so:
```javascript
export default (User) => {
return class extends User {
// Let's mix in some logging...
constructor(...args) {
super(...args);
console.log ('Another user decorated!');
}
};
};
```
then, this `index.js`:
```javascript
import {Flecks, Hooks} from '@flecks/core';
export default {
[Hooks]: {
'@flecks/db/server.models.decorate': Flecks.decorate(require.context('./models/decorators', false, /\.js$/)),
},
};
```
is *exactly equivalent* to the decorator example above.
`Flecks.decorate()` also supports some options:
```javascript
{
// The transformation used on the class path.
transformer = camelCase,
}
```
Decorator hooks are ordered.
## Ordered hooks
In many of the instances above, reference was made to the fact that certain hook types are "ordered".
Suppose we are composing an application and we have HTTP session state using cookies. When a user hits a route, we need to load their session and subsequently read a value from said session to determine if the user prefers dark mode. Clearly, we will have to ensure that the session reification happens first. This is one function of ordered hooks.
Flecks uses the name of the hook as a configuration key in order to determine the ordering of a hook. Let's take the hook we alluded to earlier as an example, `@flecks/http/server.request.route`:
Our `flecks.yml` could be configured like so:
```yaml
'@flecks/http/server':
'request.route':
- '@flecks/user/session'
- 'my-cool-fleck'
```
In this application, when `@flecks/http/server.request.route` is invoked, `@flecks/user/session`'s implementation is invoked (which reifies the user's session from cookies), followed by `my-cool-fleck`'s (which, we assume, does some kind of very cool dark mode check).
It may not always be ergonomic to configure the order of every single implementation, but enough to specify which implementations must run first (or last).
For example, suppose we have multiple implementations that require there to have been a reified user session, but which order those implementations run might not be a concern. For this, flecks provides you with the ellipses entry:
```yaml
'@flecks/http/server':
'request.route':
- '@flecks/user/session'
- '...'
- 'some-final-fleck'
```
In this application, we first reify the user session as before, but instead of listing `my-cool-fleck` immediately after, we specify ellipses. After the ellipses we specify `some-final-fleck` to, we assume, do some finalization work.
Ellipses essentially translate to: "every implementing fleck which has not already been explicitly listed in the ordering configuration".
Using more than one ellipses entry in an ordering configuration is ambiguous and will throw an error.
The default ordering configuration for any ordered hook is: `['...']` which translates to all implementations in an undefined order.

View File

@ -7,7 +7,7 @@ export default {
* @param {string} target The build target; e.g. `server`.
* @param {Object} config The neutrino configuration.
*/
'@flecks/core/build': (target, config) => {
'@flecks/core.build': (target, config) => {
if ('something' === target) {
config[target].use.push(someNeutrinoMiddleware);
}
@ -17,7 +17,7 @@ export default {
* Alter build configurations after they have been hooked.
* @param {Object} configs The neutrino configurations.
*/
'@flecks/core/build/alter': (configs) => {
'@flecks/core.build.alter': (configs) => {
// Maybe we want to do something if a config exists..?
if (configs.something) {
// Do something...
@ -29,7 +29,7 @@ export default {
/**
* Define CLI commands.
*/
'@flecks/core/commands': (program) => ({
'@flecks/core.commands': (program) => ({
// So this could be invoked like:
// npx flecks something -t --blow-up blah
something: {
@ -50,51 +50,51 @@ export default {
/**
* Define configuration.
*/
'@flecks/core/config': () => ({
'@flecks/core.config': () => ({
whatever: 'configuration',
your: 1337,
fleck: 'needs',
though: 'you should keep the values serializable',
}),
/**
* Invoked when a gathered class is HMR'd.
* @param {constructor} Class The class.
* @param {string} hook The gather hook; e.g. `@flecks/db/server/models`.
*/
'@flecks/core/gathered/hmr': (Class, hook) => {
// Do something with Class...
},
/**
* Invoked when a fleck is HMR'd
* @param {string} path The path of the fleck
* @param {Module} updatedFleck The updated fleck module.
*/
'@flecks/core/hmr': (path, updatedFleck) => {
'@flecks/core.hmr': (path, updatedFleck) => {
if ('my-fleck' === path) {
updatedFleck.doSomething();
}
},
/**
* Invoked when a gathered class is HMR'd.
* @param {constructor} Class The class.
* @param {string} hook The gather hook; e.g. `@flecks/db/server.models`.
*/
'@flecks/core.hmr.gathered': (Class, hook) => {
// Do something with Class...
},
/**
* Invoked when the application is starting. Use for order-independent initialization tasks.
*/
'@flecks/core/starting': (flecks) => {
'@flecks/core.starting': (flecks) => {
flecks.set('$my-fleck/value', initializeMyValue());
},
/**
* Define neutrino build targets.
*/
'@flecks/core/targets': () => ['sometarget'],
'@flecks/core.targets': () => ['sometarget'],
/**
* Hook into webpack configuration.
* @param {string} target The build target; e.g. `server`.
* @param {Object} config The neutrino configuration.
*/
'@flecks/core/webpack': (target, config) => {
'@flecks/core.webpack': (target, config) => {
if ('something' === target) {
config.stats = 'verbose';
}

View File

@ -29,7 +29,7 @@ module.exports = {
},
},
],
pluginId: '@flecks/core/copy',
pluginId: '@flecks/core.copy',
}),
autoentry(),
fleck(),

View File

@ -16,5 +16,5 @@ const flecks = Flecks.bootstrap();
debug('bootstrapped');
const config = R(process.env[targetNeutrino(FLECKS_CORE_BUILD_TARGET)]);
flecks.invokeFlat('@flecks/core/build', FLECKS_CORE_BUILD_TARGET, config);
flecks.invokeFlat('@flecks/core.build', FLECKS_CORE_BUILD_TARGET, config);
module.exports = neutrino(config).eslintrc();

View File

@ -35,7 +35,7 @@ export default (async () => {
debug('bootstrapped');
debug('gathering configs');
let targets = flatten(flecks.invokeFlat('@flecks/core/targets'));
let targets = flatten(flecks.invokeFlat('@flecks/core.targets'));
if (buildList.length > 0) {
targets = intersection(targets, buildList);
}
@ -52,16 +52,16 @@ export default (async () => {
));
await Promise.all(
entries.map(async ([target, config]) => (
flecks.invokeFlat('@flecks/core/build', target, config)
flecks.invokeFlat('@flecks/core.build', target, config)
)),
);
const neutrinoConfigs = Object.fromEntries(entries);
await Promise.all(flecks.invokeFlat('@flecks/core/build/alter', neutrinoConfigs));
await Promise.all(flecks.invokeFlat('@flecks/core.build.alter', neutrinoConfigs));
const webpackConfigs = await Promise.all(
Object.entries(neutrinoConfigs)
.map(async ([target, config]) => {
const webpackConfig = neutrino(config).webpack();
await flecks.invokeFlat('@flecks/core/webpack', target, webpackConfig);
await flecks.invokeFlat('@flecks/core.webpack', target, webpackConfig);
return webpackConfig;
}),
);

View File

@ -87,7 +87,7 @@ else {
const flecks = Flecks.bootstrap();
debug('bootstrapped');
// Register commands.
const commands = flecks.invokeMerge('@flecks/core/commands', program);
const commands = flecks.invokeMerge('@flecks/core.commands', program);
const keys = Object.keys(commands).sort();
for (let i = 0; i < keys.length; ++i) {
const {

View File

@ -15,9 +15,9 @@ import Middleware from './middleware';
const debug = D('@flecks/core/flecks');
export const ById = Symbol.for('@flecks/core/byId');
export const ByType = Symbol.for('@flecks/core/byType');
export const Hooks = Symbol.for('@flecks/core/hooks');
export const ById = Symbol.for('@flecks/core.byId');
export const ByType = Symbol.for('@flecks/core.byType');
export const Hooks = Symbol.for('@flecks/core.hooks');
const capitalize = (string) => string.substring(0, 1).toUpperCase() + string.substring(1);
@ -67,13 +67,13 @@ export default class Flecks {
configureFleck(fleck) {
this.config[fleck] = {
...this.invokeFleck('@flecks/core/config', fleck),
...this.invokeFleck('@flecks/core.config', fleck),
...this.config[fleck],
};
}
configureFlecks() {
const defaultConfig = this.invoke('@flecks/core/config');
const defaultConfig = this.invoke('@flecks/core.config');
const flecks = Object.keys(defaultConfig);
for (let i = 0; i < flecks.length; i++) {
this.configureFleck(flecks[i]);
@ -330,9 +330,11 @@ export default class Flecks {
}
lookupFlecks(hook) {
const parts = hook.split('/');
const key = parts.pop();
return this.config[parts.join('/')]?.[key]?.concat() || [];
const index = hook.indexOf('.');
if (-1 === index) {
return ['...'];
}
return this.get([hook.slice(0, index), hook.slice(index + 1)], ['...']);
}
makeMiddleware(hook) {
@ -448,7 +450,7 @@ export default class Flecks {
}
async up(hook) {
await Promise.all(this.invokeFlat('@flecks/core/starting'));
await Promise.all(this.invokeFlat('@flecks/core.starting'));
await this.invokeSequentialAsync(hook);
}
@ -474,7 +476,7 @@ export default class Flecks {
const Subclass = wrapperClass(Class, id, idAttribute, type, typeAttribute);
// eslint-disable-next-line no-multi-assign
gathered[type] = gathered[id] = gathered[ById][id] = gathered[ByType][type] = Subclass;
this.invoke('@flecks/core/gathered/hmr', Subclass, hook);
this.invoke('@flecks/core.hmr.gathered', Subclass, hook);
}
}
}

View File

@ -17,7 +17,7 @@ export {
export default {
[Hooks]: {
'@flecks/core/config': () => ({
'@flecks/core.config': () => ({
'eslint.exclude': [],
id: 'flecks',
}),

View File

@ -44,7 +44,7 @@ export const targetNeutrino = (target) => (
);
export const targetNeutrinos = (flecks) => {
const entries = Object.entries(flecks.invoke('@flecks/core/targets'));
const entries = Object.entries(flecks.invoke('@flecks/core.targets'));
const targetNeutrinos = {};
for (let i = 0; i < entries.length; ++i) {
const [fleck, targets] = entries[i];
@ -86,7 +86,7 @@ export default (program, flecks) => {
],
},
};
const targets = flatten(flecks.invokeFlat('@flecks/core/targets'));
const targets = flatten(flecks.invokeFlat('@flecks/core.targets'));
if (targets.length > 0) {
commands.build = {
args: [

View File

@ -18,7 +18,7 @@ export {JsonStream, transform} from './stream';
export default {
[Hooks]: {
'@flecks/core/build': (target, config, flecks) => {
'@flecks/core.build': (target, config, flecks) => {
const {'eslint.exclude': exclude} = flecks.get('@flecks/core');
if (-1 !== exclude.indexOf(target)) {
return;
@ -43,6 +43,6 @@ export default {
}),
);
},
'@flecks/core/commands': commands,
'@flecks/core.commands': commands,
},
};

View File

@ -1,4 +1,4 @@
// eslint-disable-next-line import/no-unresolved
// eslint-disable-next-line import/no-extraneous-dependencies, import/no-unresolved
import {Flecks, Hooks} from '@flecks/core';
export const testNodespace = () => [
@ -10,9 +10,8 @@ export const testNodespace = () => [
export default {
[Hooks]: {
'@flecks/core/config': () => ({
'@flecks/core.config': () => ({
foo: 'bar',
'test-gather.decorate': ['...'],
}),
'@flecks/core/one/test-gather': (
Flecks.provide(require.context('./things', false, /\.js$/))

View File

@ -1,4 +1,4 @@
// eslint-disable-next-line import/no-unresolved
// eslint-disable-next-line import/no-extraneous-dependencies, import/no-unresolved
import {Flecks, Hooks} from '@flecks/core';
export default {

View File

@ -9,7 +9,7 @@ export default {
* defined in its own file.
* See: https://github.com/cha0s/flecks/tree/master/packages/user/src/server/models
*/
'@flecks/db/server/models': Flecks.provide(require.context('./models', false, /\.js$/)),
'@flecks/db/server.models': Flecks.provide(require.context('./models', false, /\.js$/)),
/**
* Decorate database models.
@ -20,7 +20,7 @@ export default {
*
* @param {constructor} Model The model to decorate.
*/
'@flecks/db/server/models.decorate': (
'@flecks/db/server.models.decorate': (
Flecks.decorate(require.context('./models/decorators', false, /\.js$/))
),
},

View File

@ -11,26 +11,25 @@ export {createDatabaseConnection};
export default {
[Hooks]: {
'@flecks/core/config': () => ({
'@flecks/core.config': () => ({
database: ':memory:',
dialect: 'sqlite',
host: undefined,
'models.decorate': ['...'],
password: undefined,
port: undefined,
username: undefined,
}),
'@flecks/core/starting': (flecks) => {
'@flecks/core.starting': (flecks) => {
flecks.set('$flecks/db.models', flecks.gather(
'@flecks/db/server/models',
'@flecks/db/server.models',
{typeAttribute: 'name'},
));
},
'@flecks/docker/containers': containers,
'@flecks/server/up': async (flecks) => {
'@flecks/docker.containers': containers,
'@flecks/server.up': async (flecks) => {
flecks.set('$flecks/db/sequelize', await createDatabaseConnection(flecks));
},
'@flecks/repl/context': (flecks) => ({
'@flecks/repl.context': (flecks) => ({
Models: flecks.get('$flecks/db.models'),
sequelize: flecks.get('$flecks/db/sequelize'),
}),

View File

@ -8,7 +8,7 @@ export default {
* Beware: the user running the server must have Docker privileges.
* See: https://docs.docker.com/engine/install/linux-postinstall/#manage-docker-as-a-non-root-user
*/
'@flecks/docker/containers': () => ({
'@flecks/docker.containers': () => ({
someContainer: {
// Environment variables.
environment: {

View File

@ -47,7 +47,7 @@ export default (program, flecks) => {
],
},
};
const containers = flecks.invoke('@flecks/docker/containers');
const containers = flecks.invoke('@flecks/docker.containers');
(
await Promise.all(
Object.entries(containers)

View File

@ -5,15 +5,15 @@ import startContainer from './start-container';
export default {
[Hooks]: {
'@flecks/core/config': () => ({
'@flecks/core.config': () => ({
enabled: true,
}),
'@flecks/core/commands': commands,
'@flecks/server/up': async (flecks) => {
'@flecks/core.commands': commands,
'@flecks/server.up': async (flecks) => {
if (!flecks.get('@flecks/docker/server.enabled')) {
return;
}
const containers = await flecks.invokeMergeAsync('@flecks/docker/containers');
const containers = await flecks.invokeMergeAsync('@flecks/docker.containers');
await Promise.all(
Object.entries(containers)
.map(([key, config]) => startContainer(flecks, key, config)),

View File

@ -82,7 +82,7 @@ const FlecksInvocations = (state, filename) => ({
path.node.loc,
);
state.addInvocation(
'@flecks/core/starting',
'@flecks/core.starting',
'invokeFlat',
filename,
path.node.loc,

View File

@ -4,8 +4,8 @@ import commands from './commands';
export default {
[Hooks]: {
'@flecks/core/commands': commands,
'@flecks/core/config': () => ({
'@flecks/core.commands': commands,
'@flecks/core.config': () => ({
filenameRewriters: {},
}),
},

View File

@ -20,7 +20,7 @@ module.exports = (async () => {
debug('bootstrapped');
const compiler = flecks.invokeFleck(
'@flecks/fleck/compiler',
'@flecks/fleck.compiler',
flecks.get('@flecks/fleck.compiler'),
);
if (compiler) {

View File

@ -10,7 +10,7 @@ import {
spawnWith,
} from '@flecks/core/server';
const debug = D('@flecks/core/commands');
const debug = D('@flecks/core.commands');
const {
FLECKS_CORE_ROOT = process.cwd(),

View File

@ -4,7 +4,7 @@ import commands from './commands';
export default {
[Hooks]: {
'@flecks/core/commands': commands,
'@flecks/core/targets': () => ['fleck'],
'@flecks/core.commands': commands,
'@flecks/core.targets': () => ['fleck'],
},
};

View File

@ -7,7 +7,7 @@ export {default as createLimiter} from './limiter';
export default {
[Hooks]: {
'@flecks/core/config': () => ({
'@flecks/core.config': () => ({
keys: ['ip'],
http: {
keys: ['ip'],
@ -22,8 +22,8 @@ export default {
ttl: 30,
},
}),
'@flecks/db/server/models': Flecks.provide(require.context('./models', false, /\.js$/)),
'@flecks/http/server/request.route': (flecks) => {
'@flecks/db/server.models': Flecks.provide(require.context('./models', false, /\.js$/)),
'@flecks/http/server.request.route': (flecks) => {
const {http} = flecks.get('@flecks/governor/server');
const limiter = flecks.get('$flecks/governor.http.limiter');
return async (req, res, next) => {
@ -52,7 +52,7 @@ export default {
}
};
},
'@flecks/server/up': async (flecks) => {
'@flecks/server.up': async (flecks) => {
if (flecks.fleck('@flecks/http/server')) {
const {http} = flecks.get('@flecks/governor/server');
const limiter = await createLimiter(
@ -93,7 +93,7 @@ export default {
flecks.set('$flecks/governor.socket.limiter', limiter);
}
},
'@flecks/socket/server/request.socket': (flecks) => {
'@flecks/socket/server.request.socket': (flecks) => {
const limiter = flecks.get('$flecks/governor.socket.limiter');
return async (socket, next) => {
const {handshake: req} = socket;
@ -120,7 +120,7 @@ export default {
}
};
},
'@flecks/socket/packets.decorate': (Packets, flecks) => (
'@flecks/socket.packets.decorate': (Packets, flecks) => (
Object.fromEntries(
Object.entries(Packets).map(([keyPrefix, Packet]) => [
keyPrefix,

View File

@ -5,14 +5,14 @@ export default {
/**
* Define sequential actions to run when the client comes up.
*/
'@flecks/http/client/up': async () => {
'@flecks/http/client.up': async () => {
await youCanDoAsyncThingsHere();
},
/**
* Override flecks configuration sent to client flecks.
* @param {http.ClientRequest} req The HTTP request object.
*/
'@flecks/http/config': (req) => ({
'@flecks/http.config': (req) => ({
someClientFleck: {
someConfig: req.someConfig,
},
@ -20,7 +20,7 @@ export default {
/**
* Define HTTP routes.
*/
'@flecks/http/routes': () => [
'@flecks/http.routes': () => [
{
method: 'get',
path: '/some-path',
@ -33,20 +33,20 @@ export default {
/**
* Define neutrino compilation middleware (e.g. @neutrinojs/react).
*/
'@flecks/http/server/compiler': () => {
'@flecks/http/server.compiler': () => {
return require('@neutrinojs/node');
},
/**
* Define middleware to run when a route is matched.
*/
'@flecks/http/server/request.route': () => (req, res, next) => {
'@flecks/http/server.request.route': () => (req, res, next) => {
// Express-style route middleware...
next();
},
/**
* Define middleware to run when an HTTP socket connection is established.
*/
'@flecks/http/server/request.socket': () => (req, res, next) => {
'@flecks/http/server.request.socket': () => (req, res, next) => {
// Express-style route middleware...
next();
},
@ -55,13 +55,13 @@ export default {
* @param {stream.Readable} stream The HTML stream.
* @param {http.ClientRequest} req The HTTP request object.
*/
'@flecks/http/server/stream.html': (stream, req) => {
'@flecks/http/server.stream.html': (stream, req) => {
return stream.pipe(myTransformStream);
},
/**
* Define sequential actions to run when the HTTP server comes up.
*/
'@flecks/http/server/up': async () => {
'@flecks/http/server.up': async () => {
await youCanDoAsyncThingsHere();
},
},

View File

@ -20,8 +20,6 @@
"files": [
"build",
"build/template.ejs",
"client.js",
"client.js.map",
"client/tests.js",
"client/tests.js.map",
"entry.js",

View File

@ -1,9 +0,0 @@
import {Hooks} from '@flecks/core';
export default {
[Hooks]: {
'@flecks/core/config': () => ({
up: ['...'],
}),
},
};

View File

@ -11,7 +11,7 @@ const progress = new Progress(window);
(async () => {
// eslint-disable-next-line no-console
console.log(`flecks client v${version} loading runtime...`);
const config = window[Symbol.for('@flecks/http/config')];
const config = window[Symbol.for('@flecks/http.config')];
const debug = D(config['@flecks/core']?.id || 'flecks');
debug('loading runtime...');
const {default: loader} = await __non_webpack_import__(
@ -24,7 +24,7 @@ const progress = new Progress(window);
const flecks = new Flecks(runtime);
window.flecks = flecks;
try {
await flecks.up('@flecks/http/client/up');
await flecks.up('@flecks/http/client.up');
debug('up!');
}
catch (error) {

View File

@ -37,7 +37,7 @@ module.exports = (async () => {
};
// Compile code.
const compiler = flecks.invokeFleck(
'@flecks/http/server/compiler',
'@flecks/http/server.compiler',
flecks.get('@flecks/http/server.compiler'),
);
if (compiler) {

View File

@ -24,7 +24,7 @@ module.exports = async (flecks) => {
const paths = Object.entries(resolver);
const source = [
'module.exports = (update) => (async () => ({',
" config: window[Symbol.for('@flecks/http/config')],",
" config: window[Symbol.for('@flecks/http.config')],",
' flecks: Object.fromEntries(await Promise.all([',
paths
.map(([path]) => [
@ -45,7 +45,7 @@ module.exports = async (flecks) => {
source.push(` module.hot.accept('${path}', async () => {`);
source.push(` const updatedFleck = require('${path}');`);
source.push(` window.flecks.refresh('${path}', updatedFleck);`);
source.push(` window.flecks.invoke('@flecks/core/hmr', '${path}', updatedFleck);`);
source.push(` window.flecks.invoke('@flecks/core.hmr', '${path}', updatedFleck);`);
source.push(' });');
});
source.push('}');

View File

@ -1,7 +1,7 @@
import {Transform} from 'stream';
const config = async (flecks, req) => {
const httpConfig = await flecks.invokeMergeAsync('@flecks/http/config', req);
const httpConfig = await flecks.invokeMergeAsync('@flecks/http.config', req);
const config = {};
const {resolver} = flecks.get('$flecks/http.flecks');
const keys = Object.keys(resolver);
@ -24,7 +24,7 @@ const config = async (flecks, req) => {
export const configSource = async (flecks, req) => {
const codedConfig = encodeURIComponent(JSON.stringify(await config(flecks, req)));
return `window[Symbol.for('@flecks/http/config')] = JSON.parse(decodeURIComponent("${
return `window[Symbol.for('@flecks/http.config')] = JSON.parse(decodeURIComponent("${
codedConfig
}"));`;
};

View File

@ -16,7 +16,7 @@ const {
const debug = D('@flecks/http/server/http');
const deliverHtmlStream = (stream, flecks, req, res) => {
flecks.invokeComposed('@flecks/http/server/stream.html', stream, req).pipe(res);
flecks.invokeComposed('@flecks/http/server.stream.html', stream, req).pipe(res);
};
export const createHttpServer = async (flecks) => {
@ -36,10 +36,10 @@ export const createHttpServer = async (flecks) => {
// Compression. heheh
app.use(compression({level: 'production' === NODE_ENV ? 6 : 9}));
// Socket connection.
app.use(flecks.makeMiddleware('@flecks/http/server/request.socket'));
app.use(flecks.makeMiddleware('@flecks/http/server.request.socket'));
// Routes.
const routeMiddleware = flecks.makeMiddleware('@flecks/http/server/request.route');
const routes = flatten(flecks.invokeFlat('@flecks/http/routes'));
const routeMiddleware = flecks.makeMiddleware('@flecks/http/server.request.route');
const routes = flatten(flecks.invokeFlat('@flecks/http.routes'));
routes.forEach(({method, path, middleware}) => app[method](path, routeMiddleware, middleware));
// In development mode, create a proxy to the webpack-dev-server.
if ('production' !== NODE_ENV) {
@ -117,7 +117,7 @@ export const createHttpServer = async (flecks) => {
reject(error);
return;
}
await Promise.all(flecks.invokeFlat('@flecks/http/server/up', httpServer));
await Promise.all(flecks.invokeFlat('@flecks/http/server.up', httpServer));
debug('HTTP server up @ %s!', [host, port].filter((e) => !!e).join(':'));
resolve();
});

View File

@ -8,7 +8,7 @@ const debug = D('@flecks/http/server');
export default {
[Hooks]: {
'@flecks/core/build/alter': (neutrinoConfigs, flecks) => {
'@flecks/core.build.alter': (neutrinoConfigs, flecks) => {
// Bail if there's no http build.
if (!neutrinoConfigs.http) {
return;
@ -36,7 +36,7 @@ export default {
// eslint-disable-next-line no-param-reassign
delete neutrinoConfigs.http;
},
'@flecks/core/config': () => ({
'@flecks/core.config': () => ({
devHost: 'localhost',
devPort: undefined,
devPublic: undefined,
@ -44,20 +44,18 @@ export default {
host: '0.0.0.0',
output: 'http',
port: 32340,
'stream.html': ['...'],
'request.route': [],
'request.socket': [],
trust: false,
up: ['...'],
}),
'@flecks/core/starting': (flecks) => {
'@flecks/core.starting': (flecks) => {
debug('bootstrapping flecks...');
const httpFlecks = Flecks.bootstrap({platforms: ['client'], without: ['server']});
debug('bootstrapped');
flecks.set('$flecks/http.flecks', httpFlecks);
},
'@flecks/core/targets': () => ['http'],
'@flecks/http/routes': (flecks) => [
'@flecks/core.targets': () => ['http'],
'@flecks/http.routes': (flecks) => [
{
method: 'get',
path: '/flecks.config.js',
@ -67,9 +65,9 @@ export default {
},
},
],
'@flecks/http/server/stream.html': inlineConfig,
'@flecks/server/up': (flecks) => createHttpServer(flecks),
'@flecks/repl/context': (flecks) => ({
'@flecks/http/server.stream.html': inlineConfig,
'@flecks/server.up': (flecks) => createHttpServer(flecks),
'@flecks/repl.context': (flecks) => ({
httpServer: flecks.get('$flecks/http/server.instance'),
}),
},

View File

@ -8,7 +8,7 @@ export default {
* Note: `req` will be only be defined when server-side rendering.
* @param {http.ClientRequest} req The HTTP request object.
*/
'@flecks/react/providers': (req) => {
'@flecks/react.providers': (req) => {
// Generally it makes more sense to separate client and server concerns using platform
// naming conventions, but this is just a small contrived example.
return req ? serverSideProvider(req) : clientSideProvider();
@ -18,7 +18,7 @@ export default {
* Note: `req` will be only be defined when server-side rendering.
* @param {http.ClientRequest} req The HTTP request object.
*/
'@flecks/react/roots': (req) => {
'@flecks/react.roots': (req) => {
// Note that we're not returning `<Component />`, but `Component`.
return Component;
},

View File

@ -12,7 +12,7 @@ export {FlecksContext};
export default {
[Hooks]: {
'@flecks/http/client/up': async (flecks) => {
'@flecks/http/client.up': async (flecks) => {
const {ssr} = flecks.get('@flecks/react');
debug('%sing...', ssr ? 'hydrat' : 'render');
(ssr ? hydrate : render)(

View File

@ -7,10 +7,10 @@ export default () => {
const flecks = useContext(FlecksContext);
// Hack... force flecks update on HMR.
useEffect(() => {
if (!flecks.hooks['@flecks/core/hmr']) {
flecks.hooks['@flecks/core/hmr'] = [];
if (!flecks.hooks['@flecks/core.hmr']) {
flecks.hooks['@flecks/core.hmr'] = [];
}
flecks.hooks['@flecks/core/hmr'].push({
flecks.hooks['@flecks/core.hmr'].push({
plugin: '@flecks/react/hmr',
fn: () => {
setId(Math.random());

View File

@ -15,8 +15,7 @@ export {default as usePrevious} from './hooks/use-previous';
export default {
[Hooks]: {
'@flecks/core/config': () => ({
providers: ['...'],
'@flecks/core.config': () => ({
ssr: true,
}),
},

View File

@ -7,9 +7,9 @@ import FlecksContext from '@flecks/react/context';
const debug = D('@flecks/react/root');
export default async (flecks, req) => {
const Roots = flecks.invoke('@flecks/react/roots', req);
const Roots = flecks.invoke('@flecks/react.roots', req);
debug('roots: %O', Object.keys(Roots));
const Providers = await flecks.invokeSequentialAsync('@flecks/react/providers', req);
const Providers = await flecks.invokeSequentialAsync('@flecks/react.providers', req);
const FlattenedProviders = [];
for (let i = 0; i < Providers.length; i++) {
const Provider = Providers[i];

View File

@ -6,7 +6,7 @@ import {HistoryRouter as ReduxHistoryRouter} from 'redux-first-history/rr6';
export default {
[Hooks]: {
'@flecks/react/providers': (req, flecks) => (
'@flecks/react.providers': (req, flecks) => (
flecks.fleck('@flecks/redux')
? [ReduxHistoryRouter, {history: createReduxHistory(flecks.get('$flecks/redux/store'))}]
: [HistoryRouter, {history}]

View File

@ -7,10 +7,10 @@ export * from 'redux-first-history';
export default {
[Hooks]: {
'@flecks/redux/slices': () => ({
'@flecks/redux.slices': () => ({
router: routerReducer,
}),
'@flecks/redux/store': (options) => {
'@flecks/redux.store': (options) => {
options.middleware.push(routerMiddleware);
},
},

View File

@ -3,7 +3,7 @@ import {StaticRouter} from 'react-router-dom/server';
export default {
[Hooks]: {
'@flecks/react/providers': (req, flecks) => (
'@flecks/react.providers': (req, flecks) => (
flecks.get('@flecks/react.ssr') ? [StaticRouter, {location: req.url}] : []
),
},

View File

@ -5,7 +5,7 @@ import ssr from './ssr';
export default {
[Hooks]: {
'@flecks/http/server/compiler': (flecks) => (
'@flecks/http/server.compiler': (flecks) => (
react({
clean: false,
hot: false,
@ -23,7 +23,7 @@ export default {
},
})
),
'@flecks/http/server/stream.html': (stream, req, flecks) => (
'@flecks/http/server.stream.html': (stream, req, flecks) => (
flecks.get('@flecks/react.ssr') ? ssr(stream, req, flecks) : stream
),
},

View File

@ -29,12 +29,12 @@ export const keys = (client, pattern) => safeKeys(client, pattern, 0);
export default {
[Hooks]: {
'@flecks/core/config': () => ({
'@flecks/core.config': () => ({
host: 'localhost',
port: 6379,
}),
'@flecks/docker/containers': containers,
'@flecks/repl/context': (flecks) => ({
'@flecks/docker.containers': containers,
'@flecks/repl.context': (flecks) => ({
redisClient: createClient(flecks),
}),
},

View File

@ -11,14 +11,14 @@ const RedisStore = ConnectRedis(session);
export default {
[Hooks]: {
'@flecks/user/session': async (flecks) => {
'@flecks/user.session': async (flecks) => {
const client = createClient(flecks, {legacyMode: true});
await client.connect();
return {
store: new RedisStore({client}),
};
},
'@flecks/socket/server': async (flecks) => {
'@flecks/socket.server': async (flecks) => {
const pubClient = createClient(flecks);
const subClient = createClient(flecks);
await Promise.all([pubClient.connect(), subClient.connect()]);

View File

@ -5,7 +5,7 @@ export default {
/**
* Define side-effects to run against Redux actions.
*/
'@flecks/redux/effects': () => ({
'@flecks/redux.effects': () => ({
someActionName: (store, action, flecks) => {
// Runs when `someActionName` actions are dispatched.
},
@ -13,7 +13,7 @@ export default {
/**
* Define root-level reducers for the Redux store.
*/
'@flecks/redux/reducers': () => {
'@flecks/redux.reducers': () => {
return (state, action) => {
// Whatever you'd like.
return state;
@ -24,7 +24,7 @@ export default {
*
* See: https://redux-toolkit.js.org/api/createSlice
*/
'@flecks/redux/slices': () => {
'@flecks/redux.slices': () => {
const something = createSlice(
// ...
);
@ -36,7 +36,7 @@ export default {
* Modify Redux store configuration.
* @param {Object} options A mutable object with keys for enhancers and middleware.
*/
'@flecks/redux/store': (options) => {
'@flecks/redux.store': (options) => {
options.enhancers.splice(someIndex, 1);
options.middleware.push(mySpecialMiddleware);
},

View File

@ -4,4 +4,4 @@ export const hydrateServer = createAction('@flecks/redux/hydrate.server');
export const hydrateLocalStorage = createAction('@flecks/redux/hydrate.localStorage');
export const storeLocalStorage = createAction('@flecks/redux/store.localStorage');
export const storeLocalStorage = createAction('@flecks/redux.store.localStorage');

View File

@ -6,8 +6,8 @@ import localStorageEnhancer from './local-storage';
export default {
[Hooks]: {
'@flecks/react/providers': async (req, flecks) => {
const slices = await ensureUniqueReduction(flecks, '@flecks/redux/slices');
'@flecks/react.providers': async (req, flecks) => {
const slices = await ensureUniqueReduction(flecks, '@flecks/redux.slices');
const reducer = createReducer(flecks, slices);
// Hydrate from server.
const {preloadedState} = flecks.get('@flecks/redux/client');
@ -15,11 +15,11 @@ export default {
flecks.set('$flecks/redux/store', store);
return [Provider, {store}];
},
'@flecks/redux/store': ({enhancers}) => {
'@flecks/redux.store': ({enhancers}) => {
// Hydrate from and subscribe to localStorage.
enhancers.push(localStorageEnhancer);
},
'@flecks/socket/packets.decorate': (
'@flecks/socket.packets.decorate': (
Flecks.decorate(require.context('./packets/decorators', false, /\.js$/))
),
},

View File

@ -7,6 +7,6 @@ export * from './actions';
export default {
[Hooks]: {
'@flecks/socket/packets': Flecks.provide(require.context('./packets', false, /\.js$/)),
'@flecks/socket.packets': Flecks.provide(require.context('./packets', false, /\.js$/)),
},
};

View File

@ -9,8 +9,8 @@ const debug = D('@flecks/redux/server');
export default {
[Hooks]: {
'@flecks/http/server/request.route': (flecks) => async (req, res, next) => {
const slices = await ensureUniqueReduction(flecks, '@flecks/redux/slices');
'@flecks/http/server.request.route': (flecks) => async (req, res, next) => {
const slices = await ensureUniqueReduction(flecks, '@flecks/redux.slices');
const reducer = createReducer(flecks, slices);
const preloadedState = reducer(undefined, hydrateServer({flecks, req}));
debug(
@ -21,11 +21,11 @@ export default {
req.redux = await configureStore(flecks, reducer, {preloadedState});
next();
},
'@flecks/http/config': async (req) => ({
'@flecks/http.config': async (req) => ({
'@flecks/redux/client': {
preloadedState: req.redux.getState(),
},
}),
'@flecks/react/providers': (req) => [Provider, {store: req.redux}],
'@flecks/react.providers': (req) => [Provider, {store: req.redux}],
},
};

View File

@ -2,7 +2,7 @@ import {combineReducers} from '@reduxjs/toolkit';
import reduceReducers from 'reduce-reducers';
export default (flecks, slices) => {
let reducers = flecks.invokeFlat('@flecks/redux/reducers');
let reducers = flecks.invokeFlat('@flecks/redux.reducers');
if (Object.keys(slices).length > 0) {
reducers = reducers.concat(combineReducers(slices));
}

View File

@ -14,7 +14,7 @@ export default async function configureStore(flecks, reducer, {preloadedState})
effectsMiddleware(flecks),
],
};
flecks.invokeFlat('@flecks/redux/store', options);
flecks.invokeFlat('@flecks/redux.store', options);
return configureStoreR({
enhancers: (defaultEnhancers) => {
const index = options.enhancers.indexOf('@flecks/redux/defaultEnhancers');

View File

@ -1,5 +1,5 @@
export default (flecks) => {
const effects = flecks.invokeFlat('@flecks/redux/effects');
const effects = flecks.invokeFlat('@flecks/redux.effects');
const effect = (store, action) => {
effects.forEach((map) => {
if (map[action.type]) {

View File

@ -7,7 +7,7 @@ export default {
*
* Note: commands will be prefixed with a period in the Node REPL.
*/
'@flecks/repl/commands': () => ({
'@flecks/repl.commands': () => ({
someCommand: (...args) => {
// args are passed from the Node REPL. So, you could invoke it like:
// .someCommand foo bar
@ -17,7 +17,7 @@ export default {
/**
* Provide global context to the REPL.
*/
'@flecks/repl/context': () => {
'@flecks/repl.context': () => {
// Now you'd be able to do like:
// `node> someValue;`
// and the REPL would evaluate it to `'foobar'`.

View File

@ -10,7 +10,7 @@ const debug = D('@flecks/repl');
export async function createReplServer(flecks) {
const {id} = flecks.get('@flecks/core');
const context = flecks.invokeFlat('@flecks/repl/context')
const context = flecks.invokeFlat('@flecks/repl.context')
.reduce((r, vars) => ({...r, ...vars}), {flecks});
debug(
'context = %O',
@ -18,7 +18,7 @@ export async function createReplServer(flecks) {
);
const commands = {};
Object.entries(
flecks.invokeFlat('@flecks/repl/commands').reduce((r, commands) => ({...r, ...commands}), {}),
flecks.invokeFlat('@flecks/repl.commands').reduce((r, commands) => ({...r, ...commands}), {}),
).forEach(([key, value]) => {
commands[key] = value;
debug('registered command: %s', key);

View File

@ -5,7 +5,7 @@ import {createReplServer} from './repl';
export default {
[Hooks]: {
'@flecks/core/commands': commands,
'@flecks/server/up': (flecks) => createReplServer(flecks),
'@flecks/core.commands': commands,
'@flecks/server.up': (flecks) => createReplServer(flecks),
},
};

View File

@ -5,13 +5,13 @@ export default {
/**
* Define neutrino compilation middleware (e.g. @neutrinojs/react).
*/
'@flecks/server/compiler': () => {
'@flecks/server.compiler': () => {
return require('@neutrinojs/node');
},
/**
* Define sequential actions to run when the server comes up.
*/
'@flecks/server/up': async () => {
'@flecks/server.up': async () => {
await youCanDoAsyncThingsHere();
},
},

View File

@ -24,7 +24,7 @@ const {version} = require('../package.json');
const flecks = new Flecks(runtime);
global.flecks = flecks;
try {
await flecks.up('@flecks/server/up');
await flecks.up('@flecks/server.up');
debug('up!');
}
catch (error) {

View File

@ -2,12 +2,11 @@ import {Hooks} from '@flecks/core';
export default {
[Hooks]: {
'@flecks/core/config': () => ({
'@flecks/core.config': () => ({
hot: false,
inspect: false,
profile: false,
start: false,
up: ['...'],
}),
},
};

View File

@ -44,7 +44,7 @@ module.exports = async (flecks) => {
paths.forEach((path) => {
source.push(` module.hot.accept('${path}', async () => {`);
source.push(` global.flecks.refresh('${path}', require('${path}'));`);
source.push(` global.flecks.invoke('@flecks/core/hmr', '${path}');`);
source.push(` global.flecks.invoke('@flecks/core.hmr', '${path}');`);
source.push(' });');
});
source.push('}');

View File

@ -64,7 +64,7 @@ module.exports = (async () => {
};
const compiler = flecks.invokeFleck(
'@flecks/server/compiler',
'@flecks/server.compiler',
flecks.get('@flecks/server.compiler'),
);

View File

@ -2,6 +2,6 @@ import {Hooks} from '@flecks/core';
export default {
[Hooks]: {
'@flecks/core/targets': () => ['server'],
'@flecks/core.targets': () => ['server'],
},
};

View File

@ -7,13 +7,13 @@ export default {
*
* See: https://socket.io/docs/v4/client-options/
*/
'@flecks/socket/client': () => ({
'@flecks/socket.client': () => ({
timeout: Infinity,
}),
/**
* Define server-side intercom channels.
*/
'@flecks/socket/intercom': (req) => ({
'@flecks/socket.intercom': (req) => ({
// This would have been called like:
// `const result = await req.intercom('someChannel', payload)`.
// `result` will be an `n`-length array, where `n` is the number of server instances. Each
@ -33,7 +33,7 @@ export default {
* See: https://github.com/cha0s/flecks/tree/master/packages/socket/src/packet/packet.js
* See: https://github.com/cha0s/flecks/tree/master/packages/socket/src/packet/redirect.js
*/
'@flecks/socket/packets': Flecks.provide(require.context('./packets', false, /\.js$/)),
'@flecks/socket.packets': Flecks.provide(require.context('./packets', false, /\.js$/)),
/**
* Decorate database models.
*
@ -41,7 +41,7 @@ export default {
* decorator would be defined in its own file.
* @param {constructor} Packet The packet to decorate.
*/
'@flecks/socket/packets.decorate': (
'@flecks/socket.packets.decorate': (
Flecks.decorate(require.context('./packets/decorators', false, /\.js$/))
),
@ -50,13 +50,13 @@ export default {
*
* See: https://socket.io/docs/v4/server-options/
*/
'@flecks/socket/server': () => ({
'@flecks/socket.server': () => ({
pingTimeout: Infinity,
}),
/**
* Define middleware to run when a socket connection is established.
*/
'@flecks/socket/server/request.socket': () => (socket, next) => {
'@flecks/socket/server.request.socket': () => (socket, next) => {
// Express-style route middleware...
next();
},

View File

@ -4,13 +4,13 @@ import SocketClient from './socket';
export default {
[Hooks]: {
'@flecks/http/client/up': (flecks) => {
'@flecks/http/client.up': (flecks) => {
const socket = new SocketClient(flecks);
flecks.set('$flecks/socket.socket', socket);
socket.connect();
socket.listen();
},
'@flecks/socket/client': ({config: {'@flecks/core': {id}}}) => ({
'@flecks/socket.client': ({config: {'@flecks/core': {id}}}) => ({
cors: {
origin: false,
},

View File

@ -30,7 +30,7 @@ export default class SocketClient extends decorate(Socket) {
{
reconnectionDelay: 'production' === process.env.NODE_ENV ? 1000 : 100,
reconnectionDelayMax: 'production' === process.env.NODE_ENV ? 5000 : 500,
...this.flecks.invokeMerge('@flecks/socket/client'),
...this.flecks.invokeMerge('@flecks/socket.client'),
},
);
this.socket.emitPromise = promisify(this.socket.emit.bind(this.socket));

View File

@ -10,16 +10,13 @@ export {default as Packet, Packer, ValidationError} from './packet';
export default {
[Hooks]: {
'@flecks/core/config': () => ({
'packets.decorate': ['...'],
}),
'@flecks/core/starting': (flecks) => {
'@flecks/core.starting': (flecks) => {
flecks.set('$flecks/socket.packets', flecks.gather(
'@flecks/socket/packets',
'@flecks/socket.packets',
{check: badPacketsCheck},
));
},
'@flecks/http/config': async (
'@flecks/http.config': async (
req,
{config: {'@flecks/socket': {'packets.decorate': decorators}}},
) => ({
@ -29,7 +26,7 @@ export default {
),
},
}),
'@flecks/socket/packets': (flecks) => ({
'@flecks/socket.packets': (flecks) => ({
Bundle: Bundle(flecks),
Redirect,
Refresh,

View File

@ -7,7 +7,7 @@ export default function createIntercom(sockets, transport) {
debug('@flecks/socket.s: %s(%o)', transport, type, payload);
const responses = await new Promise((resolve, reject) => {
sockets.io.serverSideEmit(
'@flecks/socket/intercom',
'@flecks/socket.intercom',
{payload, type},
(error, responses) => (error ? reject(error) : resolve(responses)),
);

View File

@ -5,24 +5,24 @@ import Sockets from './sockets';
export default {
[Hooks]: {
'@flecks/core/config': () => ({
'@flecks/core.config': () => ({
connect: [],
'request.socket': [],
}),
'@flecks/http/server/request.socket': ({config: {'$flecks/socket.sockets': sockets}}) => (req, res, next) => {
'@flecks/http/server.request.socket': ({config: {'$flecks/socket.sockets': sockets}}) => (req, res, next) => {
req.intercom = createIntercom(sockets, 'http');
next();
},
'@flecks/http/server/up': async (httpServer, flecks) => {
'@flecks/http/server.up': async (httpServer, flecks) => {
const sockets = new Sockets(httpServer, flecks);
await sockets.connect();
flecks.set('$flecks/socket.sockets', sockets);
},
'@flecks/repl/context': (flecks) => ({
'@flecks/repl.context': (flecks) => ({
Packets: flecks.get('$flecks/socket.packets'),
sockets: flecks.get('$flecks/socket.sockets'),
}),
'@flecks/socket/server': ({config: {'@flecks/core': {id}}}) => ({
'@flecks/socket.server': ({config: {'@flecks/core': {id}}}) => ({
path: `/${id}/socket.io`,
}),
},

View File

@ -14,7 +14,7 @@ export default class SocketServer {
this.onConnect = this.onConnect.bind(this);
this.flecks = flecks;
this.httpServer = httpServer;
const hooks = flecks.invokeMerge('@flecks/socket/intercom');
const hooks = flecks.invokeMerge('@flecks/socket.intercom');
debug('intercom hooks(%O)', hooks);
this.localIntercom = async ({payload, type}, fn) => {
debug('customHook: %s(%o)', type, payload);
@ -31,13 +31,13 @@ export default class SocketServer {
async connect() {
this.io = SocketIoServer(this.httpServer, {
...await this.flecks.invokeMergeAsync('@flecks/socket/server'),
...await this.flecks.invokeMergeAsync('@flecks/socket.server'),
serveClient: false,
});
this.flecks.set('$flecks/socket.io', this.io);
this.io.use(this.makeSocketMiddleware());
this.io.on('@flecks/socket/intercom', this.localIntercom);
this.flecks.invoke('@flecks/socket/server.io', this);
this.io.on('@flecks/socket.intercom', this.localIntercom);
this.flecks.invoke('@flecks/socket.server.io', this);
this.io.on('connect', this.onConnect);
}
@ -47,7 +47,7 @@ export default class SocketServer {
makeSocketMiddleware() {
const {app} = this.httpServer;
const middleware = this.flecks.makeMiddleware('@flecks/socket/server/request.socket');
const middleware = this.flecks.makeMiddleware('@flecks/socket/server.request.socket');
return async (socket, next) => {
Object.defineProperty(socket.handshake, 'ip', {
configurable: true,
@ -72,7 +72,7 @@ export default class SocketServer {
req.flecks = this.flecks;
req.intercom = createIntercom(this, 'socket');
req.sockets = this;
this.flecks.invokeSequentialAsync('@flecks/socket/server/connect', serverSocket);
this.flecks.invokeSequentialAsync('@flecks/socket/server.connect', serverSocket);
}
static send(flecks, nsp, packetOrDehydrated) {

View File

@ -7,7 +7,7 @@ export default {
*
* See: https://www.npmjs.com/package/express-session
*/
'@flecks/user/session': () => ({
'@flecks/user.session': () => ({
saveUninitialized: true,
}),
},

View File

@ -10,11 +10,11 @@ export * from './state/users';
export default {
[Hooks]: {
'@flecks/redux/slices': () => ({
'@flecks/redux.slices': () => ({
user,
users,
}),
'@flecks/socket/packets': (flecks) => ({
'@flecks/socket.packets': (flecks) => ({
Logout: Logout(flecks),
}),
},

View File

@ -6,14 +6,14 @@ import LocalStrategy from 'passport-local';
export default {
[Hooks]: {
'@flecks/core/config': () => ({
'@flecks/core.config': () => ({
failureRedirect: '/',
successRedirect: '/',
}),
'@flecks/db/server/models.decorate': (
'@flecks/db/server.models.decorate': (
Flecks.decorate(require.context('./models/decorators', false, /\.js$/))
),
'@flecks/http/routes': (flecks) => {
'@flecks/http.routes': (flecks) => {
const {failureRedirect, successRedirect} = flecks.get('@flecks/user/local/server');
return [
{
@ -23,7 +23,7 @@ export default {
},
];
},
'@flecks/repl/commands': (flecks) => {
'@flecks/repl.commands': (flecks) => {
const {User} = flecks.get('$flecks/db.models');
return {
createUser: async (spec) => {
@ -45,7 +45,7 @@ export default {
},
};
},
'@flecks/server/up': (flecks) => {
'@flecks/server.up': (flecks) => {
passport.use(new LocalStrategy(
{usernameField: 'email'},
async (email, password, fn) => {

View File

@ -6,11 +6,11 @@ const debug = D('@flecks/user/passport');
export default {
[Hooks]: {
'@flecks/db/server/models': Flecks.provide(require.context('./models', false, /\.js$/)),
'@flecks/http/server/request.route': (flecks) => (req, res, next) => {
debug('@flecks/http/server/request.route: passport.initialize()');
'@flecks/db/server.models': Flecks.provide(require.context('./models', false, /\.js$/)),
'@flecks/http/server.request.route': (flecks) => (req, res, next) => {
debug('@flecks/http/server.request.route: passport.initialize()');
passport.initialize()(req, res, () => {
debug('@flecks/http/server/request.route: passport.session()');
debug('@flecks/http/server.request.route: passport.session()');
passport.session()(req, res, () => {
if (!req.user) {
const {User} = flecks.get('$flecks/db.models');
@ -21,7 +21,7 @@ export default {
});
});
},
'@flecks/http/routes': () => [
'@flecks/http.routes': () => [
{
method: 'get',
path: '/auth/logout',
@ -31,7 +31,7 @@ export default {
},
},
],
'@flecks/server/up': (flecks) => {
'@flecks/server.up': (flecks) => {
passport.serializeUser((user, fn) => fn(null, user.id));
passport.deserializeUser(async (id, fn) => {
const {User} = flecks.get('$flecks/db.models');
@ -43,7 +43,7 @@ export default {
}
});
},
'@flecks/socket/intercom': () => ({
'@flecks/socket.intercom': () => ({
'@flecks/user/users': async (sids, server) => {
const sockets = await server.sockets();
return sids
@ -57,10 +57,10 @@ export default {
);
},
}),
'@flecks/socket/server/request.socket': (flecks) => (socket, next) => {
debug('@flecks/socket/server/request.socket: passport.initialize()');
'@flecks/socket/server.request.socket': (flecks) => (socket, next) => {
debug('@flecks/socket/server.request.socket: passport.initialize()');
passport.initialize()(socket.handshake, undefined, () => {
debug('@flecks/socket/server/request.socket: passport.session()');
debug('@flecks/socket/server.request.socket: passport.session()');
passport.session()(socket.handshake, undefined, async () => {
/* eslint-disable no-param-reassign */
if (!socket.handshake.user) {

View File

@ -6,21 +6,21 @@ const debug = D('@flecks/user/session');
export default {
[Hooks]: {
'@flecks/core/config': () => ({
'@flecks/core.config': () => ({
cookieSecret: (
'Set the FLECKS_ENV_FLECKS_USER_SESSION_SERVER_cookieSecret environment variable!'
),
}),
'@flecks/http/server/request.route': (flecks) => {
'@flecks/http/server.request.route': (flecks) => {
const urle = express.urlencoded({extended: true});
return (req, res, next) => {
debug('@flecks/http/server/request.route: express.urlencoded()');
debug('@flecks/http/server.request.route: express.urlencoded()');
urle(req, res, (error) => {
if (error) {
next(error);
return;
}
debug('@flecks/http/server/request.route: session()');
debug('@flecks/http/server.request.route: session()');
flecks.get('$flecks/user.session')(req, res, (error) => {
if (error) {
next(error);
@ -32,17 +32,17 @@ export default {
});
};
},
'@flecks/server/up': async (flecks) => {
'@flecks/server.up': async (flecks) => {
flecks.set('$flecks/user.session', expressSession({
resave: false,
sameSite: true,
saveUninitialized: false,
secret: flecks.get('@flecks/user/session/server.cookieSecret'),
...await flecks.invokeMergeAsync('@flecks/user/session'),
...await flecks.invokeMergeAsync('@flecks/user.session'),
}));
},
'@flecks/socket/server/request.socket': (flecks) => (socket, next) => {
debug('@flecks/socket/server/request.socket: session()');
'@flecks/socket/server.request.socket': (flecks) => (socket, next) => {
debug('@flecks/socket/server.request.socket: session()');
flecks.get('$flecks/user.session')(socket.handshake, {}, () => {
const id = socket.handshake.session?.id;
socket.join(id);