Skip to content
This repository was archived by the owner on Oct 1, 2022. It is now read-only.
Merged
2 changes: 1 addition & 1 deletion docs/.vuepress/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ module.exports = {
'docs/state',
'docs/collections',
'docs/actions',
'docs/controllers',
// 'docs/controllers',
'docs/core',
'docs/route',
'docs/persisting-data',
Expand Down
11 changes: 9 additions & 2 deletions docs/.vuepress/styles/index.styl
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,15 @@ $pulseYellow = #FFD696;
$pulseWhite = #F7F8F8;
$pulseLightBlue = #B6DCFF;

table code
line-height: 2em !important;
table
tr, td, th
border-color: $pulseGrey;
tr:nth-child(2n)
background-color: $pulseCodeBackground;

code
line-height: 2em !important;


pre.vue-container
border-left-width: .5rem;
Expand Down
3 changes: 2 additions & 1 deletion docs/.vuepress/theme/styles/code.styl
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
padding 0.25rem 0.5rem
margin 0
font-size 0.85em
background-color rgba(27,31,35,0.05)
// background-color rgba(27,31,35,0.05)
background-color $codeBgColor
border-radius 3px
.token
&.deleted
Expand Down
3 changes: 2 additions & 1 deletion docs/.vuepress/theme/styles/custom-blocks.styl
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
$pulseCodeBackground = #282e3f;
.custom-block
.custom-block-title
font-weight 600
Expand Down Expand Up @@ -32,7 +33,7 @@
border-radius 2px
margin 1.6em 0
padding 1.6em
background-color #eee
background-color $pulseCodeBackground
h4
margin-top 0
figure, p
Expand Down
24 changes: 10 additions & 14 deletions docs/v4/docs/controllers.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,15 @@ core.accounts.myAction();

The first parameter of the Controller function is `ControllerConfig`

```js
const App = new Pulse();

const config = {
collection: App.Collection()(),
state: {
MY_STATE: App.State(),
MY_COMPUTED_STATE: App.Computed(() => true)
}
```ts
import {collection, state} from '@pulsejs/core'

const accounts = {
collection: collection(),
MY_STATE: state(),
MY_COMPUTED_STATE: state(() => true)
};
export const accounts = App.Controller(config);
export const accounts;
```

## Config Structure
Expand Down Expand Up @@ -60,7 +58,7 @@ For TypeScript users, the inferred types of the object you pass in will be prese

In some cases you will prefer to use more than the default Controller categories, you might want to spread actions to the root of the controller instance so they can be access like the following.

```js
```ts
accounts.myAction();
```

Expand All @@ -83,15 +81,13 @@ This is how a controller folder should be organized.
### `index.ts`

```ts
// import instance
import App from '../../app';
// import state
import { state, computed, collection } from './state';
// import actions, helpers and routes
import * as actions from './actions';

// init controller, merge state and computed state
const controller = App.Controller({ state: { ...state, ...computed }, collection }).root(actions);
const controller = { state: { ...state, ...computed }, collection };
```

The order of imports above is important, state/collections must be imported first to also allow them to be imported into `actions.ts` without creating a cyclic import. Sometimes this can cause `import * as ...` to return an empty object at runtime, following this structure will avoid that.
Expand Down
149 changes: 35 additions & 114 deletions docs/v4/docs/core.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,89 +23,36 @@ core.authentication.state.TOKEN.value;
## Definition

```ts
export const App = new Pulse();
import { setCore } from '@pulsejs/core'

const core = {
accounts,
authentication
};

export default App.Core(core);
setCore(core); // register your core and initialize computed states

export type ICore = typeof core;
export default core;
```

Imports for `accounts` and `authentication` were ommited for this example.

The Pulse instance is created first as `App`, followed by an object that forms the root of the core object, in this case we're passing in two arbitrary Controllers.

Now we register the core with `App.Core()` which snapshots the core object. It can now be accessed anywhere with the very same function, without any parameters. (See [Usage]())

> _In practice the initilization of App should be in a seperate file (eg: `app.ts`) as it must occur before the imports that require the `App` instance and TSLint doesn't like code above imports._
An object forms the root of the core object, in this case we're passing in two arbitrary Controllers.

See [Creating your core]() for the more detailed structure.

::: tip Why export the type?
We're unable to directly import the core into controllers, as it would create cyclic dependencies which can cause horrible compile issues, especially at scale. This is why we use `App.Core()` to get the core inside controllers, but it still wouldn't be type safe.

However, TypeScript types are immune to this paradox and can time travel. :crystal_ball: Once you declare them, they are able to be refrenced in code before and after declaration. This means we can import just the type of the finalized core into our individual controllers.

Now when making changes to one Controller you'll see full intelisense in the other—regardless of the order the controllers are initialized.
:::

## Usage

The core can be accessed from both outside and within itself, which means the syntax is slightly different for each. To demonstrate, we'll import and access a Controller named `accounts`.

> From **within** the core (this could be any file within)

```ts
import { App } from './app'; // instance
import { ICore } from './core'; // type from the future

const core = App.Core<ICore>();
```

This method ensures this Controller can access other Controllers, even ones that might not be initialized yet. We import our time-traveling type `ICore` and assign it to the Core functions' generic.

> From **outside** the core

```js
import core from './core';

core.accounts;
```

It's safe to use the default import here as we know everything has been initialized, this would be the easiest way to access the core in your UI components.
Now we register the core with `setCore()` which snapshots the core object.

### Caveats

#### 1) Destructuing imports

In an ideal world we'd be able to do this:

```ts
const { accounts } = App.Core<ICore>();
```

This would not work because at import-level accounts has not been defined yet, as assembly of the core happens last.

However if you import **without** destructuing, the constant you assign will be a direct reference to the core object within the App instance. So at runtime it will work.

```ts
const core = App.Core<ICore>();
```

#### 2) Using the core to supply initial state
#### 1) Using the core to supply initial state

A way to remember this rule, is to only use `core.` notation inside functions that are not **immediately called**. Such as Computed functions and actions.

```js
const core = App.Core<ICore>();
```ts
import core from './../core'

const state = {
noworks: App.State(core.accounts.state.HELLO.value), // compile error
works: App.Computed(() => {
noworks: state(core.accounts.state.HELLO.value), // compile error
works: state(() => {
return core.accounts.state.HELLO.value // no compile error
}),
},
Expand All @@ -123,73 +70,52 @@ Create a folder in your application named **_core_**.
In some cases you might want to create your core in a seperate repo or monorepo if you wish to use the same core in multiple projects.
:::

### New File: `app.ts`

This is where you create an instance of Pulse.

```ts
import React from 'react';
import Pulse from 'pulse-framework';

export const App = new Pulse({
framework: React
});
```

By this point your core should look something like this:
At this point, your core should look something like this:
::: vue
├── **core**
├── **/core**
│ ├── **index.ts**
│ ├── `app.ts`
:::

> Leave index.ts empty for now.

### New Directory: `controllers`

Create a folder for your conrollers. Pulse advocates splitting up your core into modules using the [Controller]() class to containerize the module. However this step is optional, you're free to structure your core however you'd like.
Create a folder for your conrollers. Pulse advocates splitting up your core into modules using an object; however, this step is optional. You're free to structure your core however you'd like.

> See [Controller]() documentation for more detail
<!-- > See [Controller]() documentation for more detail -->

```ts
import { App } from './app'; // instance
import { ICore } from './core'; // type from the future

const core = App.Core<ICore>(); // grab sister controller

export const accounts = App.Controller({
state: {
IS_NEW_ACCOUNT: App.State().type(Boolean)
}
actions: {
logout() {
App.reset(core.authentication.state)
}
}
import {state, action} from '@pulsejs/core';
import core from './core'; // type from the future

export const accounts = {
IS_NEW_ACCOUNT: state().type(Boolean),
logout: action(() => {
core.authentication.token.reset()
})

});
export default accounts;
```

### New File: `core.ts`

```ts
import { App } from './app';

import accounts from './controllers/accounts';
import authentication from './controllers/authentication';

export const core = App.Core({
export const core = setCore({
accounts,
authentication
});

export type ICore = typeof core;
export default core;
```

Everything comes together in `core.ts`, it handles importing the Pulse instance, followed by your controllers.

`App.Core()` declares the final core structure and saves it to the instance so that subsequent calls.

Finally the core is registered and exported and `ICore` is exported as a type declaration.
`setCore()` declares the final core structure and saves it to the instance so that subsequent calls.

### Export everything `index.ts`

Expand All @@ -200,26 +126,21 @@ export default core;

## Structure at scale

Pulse is flexible, so you are free to do you own thing, but you must ensure that at the very least instance creation comes first, core construction comes last.
Pulse is flexible, so you are free to do you own thing, but here is a core structure we recommend.

::: vue
**core**
├── .**index.ts**
├── .**app.ts** _Create Pulse instance_
│ ├── `controllers`
**/core**
├── **index.ts** _Export core and whatever else you want to expose to the application_
├── **core.ts** _Construct the core_
│ ├── `/controllers`
│ │ └── **accounts**
│ │ │ ├── **index.ts** _Create and export controller_
│ │ │ ├── **state.ts** _Define all State, Computed State & a Collection_
│ │ │ ├── **actions.ts** _All account actions as exported function_
│ │ │ ├── **interfaces.ts** _Typescript interfaces for accounts_
│ │ │ ├── **routes.ts** _api/socket endpoints for accounts_
│ ├── `api`
│ │ └── **index.ts**
│ │ └── **rest.service.ts** _For rest api users_
│ │ └── **socket.service.ts** _For websocket users_
│ ├── `utils`
│ ├── `/utils`
│ │ └── **index.ts**
│ ├── `data` _(Optional)_
│ │ ├── **lists.json**
└── .**core.ts** _Construct the core_
│ ├── `/data` _(Optional)_
│ │ ├── **lists.json**
:::
Loading