This repository has been archived by the owner on Aug 13, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfaction.js
146 lines (125 loc) · 4.55 KB
/
faction.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
'use strict'
var utils = require('./lib/utils')
/**
* Validate an action creator's params with a schema declared in faction.create()
*
* @private
* @arg {object} paramsHash - The parameters passed in to an action creator
* @arg {object} spec - A hash of name -> {validator function} pairs
* @return {void}
* @throws {ActionParamError} - Thrown if any parameter fails validation
*/
function _validate(paramsHash, spec) {
var specKeys = Object.keys(spec)
specKeys.forEach(function(k) {
var validator = spec[k]
// TODO: assign validated values to a cloned object?
try {
paramsHash[k] = validator.exec(paramsHash[k]) // may throw ActionParamError
} catch (err) {
var msg = 'Validation failed for "' + k + '": ' + err.message
throw new utils.ActionParamError(msg)
}
})
}
/**
* Return an action creator function
*
* If a service function is given, the action's payload will be a Promise, which
* is returned from the service function.
*
* Otherwise the action creator is fully synchronous, and the Object literal
* passed in to it will be the action payload.
*
* @private
* @arg {string} type - The action's type constant
* @arg {object} options - Options object
* @arg {object} options.validators - Hash of validators to apply to inputs
* @arg {function} options.service - Promise-returning function used for async logic
* @return {function} - An action creator function
*/
function _makeActionCreator(type, options) {
return function actionCreator(inputParams) {
var action = { type: type, meta: {} } // payload is set below
// If action argument is an error object, follow the FSA conventions:
if (inputParams instanceof Error) {
action.error = true
action.payload = inputParams
return action
}
var paramsHash = inputParams || {}
// This will throw if validation fails:
if (options.validators) _validate(paramsHash, options.validators)
action.meta.inputs = JSON.parse(JSON.stringify(paramsHash))
if (typeof options.service !== 'function') {
action.payload = paramsHash
} else {
action.meta.factionAsync = true
action.meta._successCb = options._successCb
action.meta._errorCb = options._errorCb
action.meta._deferredHandler = options.service
}
return utils.addTimestamp(action)
}
}
/**
* Register a service function for handling asynchronous actions.
* Returns an object that createFaction() will use to make the appropriate
* Promise-returning action creator
*
* @arg {function} service - Async handler function (should return a Promise)
* @arg {object} validators - Optional hash of validators
* @return {object} - A config object for _makeActionCreator()
*/
function launch(service, validators) {
if (typeof service !== 'function') {
throw new Error('First arg must be a function, got: ' + JSON.stringify(service))
}
return {
service: service,
validators: validators,
_executeInMiddleware: true,
_successCb: null,
_errorCb: null,
chain: function(cb) {
if (typeof cb !== 'function') throw new Error('chain() takes a function')
this._successCb = cb
return this
},
onError: function(cb) {
if (typeof cb !== 'function') throw new Error('onError takes a function')
this._errorCb = cb
return this
}
}
}
function createFaction(definitionCallback) {
var types = {}
var creators = {}
// Inject an object of helpers into the provided action definition callback:
var actionSpecs = definitionCallback({
launch: launch,
v: require('./lib/validators').validators
})
for (var key in actionSpecs) {
types[key] = key
var spec = actionSpecs[key]
if (!spec) break
else if (spec.service) {
creators[key] = _makeActionCreator(key, spec)
} else creators[key] = _makeActionCreator(key, { validators: spec })
}
return {
types: Object.freeze(types),
creators: Object.freeze(creators)
}
}
var exports = {
create: createFaction,
makeMiddleware: require('./lib/middleware'),
makeValidator: require('./lib/validators').makeValidator,
ActionParamError: utils.ActionParamError
}
// Export for CommonJS, or else add a global faction variable:
if (typeof module !== 'undefined') module.exports = exports
else if (typeof window === 'object') window.faction = exports