sharetribe-flex-sdk 1.14.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.
Files changed (87) hide show
  1. package/.circleci/config.yml +22 -0
  2. package/.eslintignore +3 -0
  3. package/.eslintrc.js +19 -0
  4. package/CHANGELOG.md +231 -0
  5. package/LICENSE +201 -0
  6. package/README.md +76 -0
  7. package/build/sharetribe-flex-sdk-node.js +13592 -0
  8. package/build/sharetribe-flex-sdk-web.js +1 -0
  9. package/docs/README.md +20 -0
  10. package/docs/authentication.md +179 -0
  11. package/docs/calling-the-api.md +217 -0
  12. package/docs/configurations.md +95 -0
  13. package/docs/developing-sdk.md +131 -0
  14. package/docs/docpress.json +14 -0
  15. package/docs/features.md +14 -0
  16. package/docs/keep-alive.md +48 -0
  17. package/docs/object-query-parameters.md +36 -0
  18. package/docs/scripts.js +41 -0
  19. package/docs/serializing-types-to-json.md +40 -0
  20. package/docs/sharing-session-between-client-and-server.md +19 -0
  21. package/docs/styles.css +95 -0
  22. package/docs/token-store.md +114 -0
  23. package/docs/try-it-in-browser.md +32 -0
  24. package/docs/try-it-in-the-playground.md +153 -0
  25. package/docs/types.md +27 -0
  26. package/docs/writing-your-own-token-store.md +29 -0
  27. package/docs/your-own-types.md +61 -0
  28. package/examples/README.md +5 -0
  29. package/examples/getting-started-browser/README.md +22 -0
  30. package/examples/getting-started-browser/index.html +89 -0
  31. package/examples/getting-started-browser/index.js +156 -0
  32. package/examples/getting-started-browser/screenshots/screenshot1.png +0 -0
  33. package/examples/getting-started-browser/screenshots/screenshot2.png +0 -0
  34. package/examples/getting-started-node/README.md +23 -0
  35. package/examples/getting-started-node/index.js +139 -0
  36. package/examples/getting-started-node/screenshots/screenshot.png +0 -0
  37. package/package.json +83 -0
  38. package/playground.js +295 -0
  39. package/src/browser_cookie_store.js +26 -0
  40. package/src/context_runner.js +151 -0
  41. package/src/context_runner.test.js +185 -0
  42. package/src/detect.js +11 -0
  43. package/src/express_cookie_store.js +57 -0
  44. package/src/fake/adapter.js +130 -0
  45. package/src/fake/api.js +137 -0
  46. package/src/fake/auth.js +84 -0
  47. package/src/fake/token_store.js +231 -0
  48. package/src/index.js +25 -0
  49. package/src/interceptors/.eslintrc.js +5 -0
  50. package/src/interceptors/add_auth_header.js +32 -0
  51. package/src/interceptors/add_auth_header.test.js +50 -0
  52. package/src/interceptors/add_auth_token_response.js +16 -0
  53. package/src/interceptors/add_client_id_to_params.js +12 -0
  54. package/src/interceptors/add_client_secret_to_params.js +15 -0
  55. package/src/interceptors/add_grant_type_to_params.js +23 -0
  56. package/src/interceptors/add_idp_client_id_to_params.js +17 -0
  57. package/src/interceptors/add_idp_id_to_params.js +17 -0
  58. package/src/interceptors/add_idp_token_to_params.js +17 -0
  59. package/src/interceptors/add_scope_to_params.js +18 -0
  60. package/src/interceptors/add_subject_token_to_params.js +18 -0
  61. package/src/interceptors/add_token_exchange_grant_type_to_params.js +12 -0
  62. package/src/interceptors/auth_info.js +50 -0
  63. package/src/interceptors/clear_token_after_revoke.js +45 -0
  64. package/src/interceptors/default_params.js +12 -0
  65. package/src/interceptors/fetch_auth_token_from_api.js +33 -0
  66. package/src/interceptors/fetch_auth_token_from_store.js +27 -0
  67. package/src/interceptors/fetch_refresh_token_for_revoke.js +24 -0
  68. package/src/interceptors/multipart_request.js +35 -0
  69. package/src/interceptors/retry_with_anon_token.js +58 -0
  70. package/src/interceptors/retry_with_refresh_token.js +70 -0
  71. package/src/interceptors/save_token.js +20 -0
  72. package/src/interceptors/transit_request.js +27 -0
  73. package/src/interceptors/transit_request.test.js +58 -0
  74. package/src/interceptors/transit_response.js +27 -0
  75. package/src/memory_store.js +19 -0
  76. package/src/params_serializer.js +65 -0
  77. package/src/params_serializer.test.js +58 -0
  78. package/src/sdk.js +894 -0
  79. package/src/sdk.test.js +908 -0
  80. package/src/serializer.js +279 -0
  81. package/src/serializer.test.js +229 -0
  82. package/src/token_store.js +15 -0
  83. package/src/types.js +108 -0
  84. package/src/types.test.js +75 -0
  85. package/src/utils.js +68 -0
  86. package/src/utils.test.js +85 -0
  87. package/webpack.config.babel.js +47 -0
package/playground.js ADDED
@@ -0,0 +1,295 @@
1
+ /* eslint-env node */
2
+ /* eslint-disable no-console */
3
+
4
+ // Don't show an error when devDependencies (i.e. colors) is imported
5
+ /* eslint import/no-extraneous-dependencies: ["error", {"devDependencies": true}] */
6
+
7
+ const colors = require('colors');
8
+ const repl = require('repl');
9
+ const commandLineArgs = require('command-line-args');
10
+ const commandLineUsage = require('command-line-usage');
11
+ const fs = require('fs');
12
+ const open = require('open');
13
+ const util = require('util');
14
+ const sharetribeSdk = require('./src/index');
15
+ const {LatLng, LatLgnBounds, UUID, Money, BigDecimal} = require('./src/types');
16
+
17
+ // Welcome message when Playground starts
18
+ const printWelcomeMessage = raw => {
19
+ colors.setTheme({
20
+ h1: 'yellow',
21
+ h2: 'yellow',
22
+ inline: 'gray',
23
+ block: 'gray',
24
+ });
25
+
26
+ if (raw) {
27
+ console.log('');
28
+ console.log(' # Playground (raw mode)'.h1);
29
+ console.log(' ');
30
+ console.log(' With the Marketplace API Playground you can test and try out the SDK with real results from the API.');
31
+ console.log(' ');
32
+ console.log(' ## Globals'.h2);
33
+ console.log(' ');
34
+ console.log(' The following globals are available:');
35
+ console.log(' ');
36
+ console.log(` - ${'`sharetribeSdk`'.inline}: The SDK module`);
37
+ console.log(` - ${'`printResponse`'.inline}: Helper function for pretty printing the API response.`);
38
+ console.log(` - ${'`pr`'.inline}: Helper function for pretty printing the API response from the last command that ran (presumably an API call).`);
39
+ console.log(` - ${'`apiDocs`'.inline}: Open the Marketplace API documentation in your browser.`);
40
+ console.log(' ');
41
+ console.log(' ## Example usage'.h2);
42
+ console.log(' ');
43
+ console.log(' Create new SDK instance:');
44
+ console.log(' ');
45
+ console.log(' ```'.block);
46
+ console.log(' const clientId = "<your clientId here>";'.block);
47
+ console.log(' const sdk = sharetribeSdk.createInstance({'.block);
48
+ console.log(' clientId,'.block);
49
+ console.log(' tokenStore: sharetribeSdk.tokenStore.memoryStore()'.block);
50
+ console.log(' });'.block);
51
+ console.log(' ```'.block);
52
+ console.log(' ');
53
+ console.log(' Print marketplace information:');
54
+ console.log(' ');
55
+ console.log(' sdk.marketplace.show().then(printResponse);'.block);
56
+ console.log(' ');
57
+ console.log(' Alternative version using pr():');
58
+ console.log(' ');
59
+ console.log(' sdk.marketplace.show();'.block);
60
+ console.log(' pr();'.block);
61
+ console.log(' ');
62
+ console.log(' Fetch 10 listings:');
63
+ console.log(' ');
64
+ console.log(' sdk.listings.query({per_page: 10}).then(response => {'.block);
65
+ console.log(' console.log("Fetched " + response.data.data.length + " listings.");'.block);
66
+ console.log(' response.data.data.forEach(listing => {'.block);
67
+ console.log(' console.log(listing.attributes.title);'.block);
68
+ console.log(' });'.block);
69
+ console.log(' });'.block);
70
+ console.log(' ');
71
+ console.log(` Type ${'`.exit`'.inline} when you want to exit the Playground`);
72
+ } else {
73
+ console.log('');
74
+ console.log(' # Playground'.h1);
75
+ console.log(' ');
76
+ console.log(' With the Marketplace API Playground you can test and try out the SDK with real results from the API.');
77
+ console.log(' ');
78
+ console.log(' ## Globals'.h2);
79
+ console.log(' ');
80
+ console.log(' The following globals are available:');
81
+ console.log(' ');
82
+ console.log(` - ${'`sharetribeSdk`'.inline}: The SDK module`);
83
+ console.log(` - ${'`sdk`'.inline}: An SDK instance initialized with your client ID`);
84
+ console.log(` - ${'`printResponse`'.inline}: Helper function for pretty printing the API response.`);
85
+ console.log(` - ${'`apiDocs`'.inline}: Open the Marketplace API documentation in your browser.`);
86
+ console.log(' ');
87
+ console.log(' ## Example usage'.h2);
88
+ console.log(' ');
89
+ console.log(' Print marketplace information:');
90
+ console.log(' ');
91
+ console.log(' sdk.marketplace.show().then(printResponse);'.block);
92
+ console.log(' ');
93
+ console.log(' Alternative version using pr():');
94
+ console.log(' ');
95
+ console.log(' sdk.marketplace.show();'.block);
96
+ console.log(' pr();'.block);
97
+ console.log(' ');
98
+ console.log(' Fetch 10 listings:');
99
+ console.log(' ');
100
+ console.log(' ```'.block);
101
+ console.log(' sdk.listings.query({per_page: 10}).then(response => {'.block);
102
+ console.log(' console.log("Fetched " + response.data.data.length + " listings.");'.block);
103
+ console.log(' response.data.data.forEach(listing => {'.block);
104
+ console.log(' console.log(listing.attributes.title);'.block);
105
+ console.log(' });'.block);
106
+ console.log(' });'.block);
107
+ console.log(' ```'.block);
108
+ console.log(' ');
109
+ console.log(` Type ${'`.exit`'.inline} when you want to exit the Playground`);
110
+ }
111
+ };
112
+
113
+ // CLI Usage information
114
+ const optionDefinitions = [
115
+ {
116
+ name: 'help',
117
+ description: 'Display this usage guide.',
118
+ alias: 'h',
119
+ type: Boolean
120
+ },
121
+ {
122
+ name: 'clientid',
123
+ description: 'Your Marketplace API Client ID to initialize the SDK with. You create your Client ID in Flex Console: {underline https://flex-console.sharetribe.com/applications}.',
124
+ alias: 'c',
125
+ typeLabel: '{underline ID}',
126
+ type: String
127
+ },
128
+ {
129
+ name: 'user',
130
+ description: 'The email of the user to log in with. The SDK is initialized in an authenticated state.',
131
+ alias: 'u',
132
+ typeLabel: '{underline email}',
133
+ type: String
134
+ },
135
+ {
136
+ name: 'password',
137
+ description: 'The password of the user to log in with. The SDK is initialized in an authenticated state.',
138
+ alias: 's',
139
+ typeLabel: '{underline password}',
140
+ type: String
141
+ },
142
+ {
143
+ name: 'raw',
144
+ description: 'Start the playground without initializing the SDK.',
145
+ type: Boolean
146
+ },
147
+ {
148
+ name: 'script',
149
+ description: 'Execute a playground script, i.e. a file that contains one or more playground commands.',
150
+ typeLabel: '{underline script_file.js}',
151
+ type: String
152
+ },
153
+ {
154
+ name: 'apidocs',
155
+ description: 'Open the Marketplace API reference using default browser.',
156
+ type: Boolean
157
+ }
158
+ ];
159
+
160
+ const printUsage = () => {
161
+ console.log(commandLineUsage([
162
+ {
163
+ header: 'Marketplace API Playground',
164
+ content: 'The easiest way to call the Flex Marketplace API using the JS SDK to try things out, to learn the APIs and to test your ideas.'
165
+ },
166
+ {
167
+ header: 'Options',
168
+ optionList: optionDefinitions
169
+ }
170
+ ]));
171
+ };
172
+
173
+ const printResponse = response => {
174
+ console.log(JSON.stringify(response.data, null, 4));
175
+ return response;
176
+ }
177
+
178
+ const printLastResponse = (ctx) =>
179
+ () => ctx._.then(printResponse);
180
+
181
+ const apiDocs = () =>
182
+ open('https://www.sharetribe.com/api-reference/marketplace.html?javascript#marketplace-api')
183
+ .then(() => '');
184
+
185
+ const setupContext = (ctx, sdk) => {
186
+ ctx.sharetribeSdk = sharetribeSdk;
187
+ if (sdk) {
188
+ ctx.sdk = sdk;
189
+ }
190
+ ctx.printResponse = printResponse;
191
+ ctx.pr = printLastResponse(ctx);
192
+ ctx.apiDocs = apiDocs;
193
+ ctx.LatLng = LatLng;
194
+ ctx.LatLngBounds = LatLgnBounds;
195
+ ctx.UUID = UUID;
196
+ ctx.Money = Money;
197
+ ctx.BigDecimal = BigDecimal;
198
+ }
199
+
200
+ const startRepl = (sdk, rawMode, src) =>
201
+ () => {
202
+ if (src) {
203
+ // Setup context
204
+ setupContext(global, sdk);
205
+
206
+ // Start REPL
207
+ console.log('Executing script...');
208
+ console.log();
209
+ repl.start({
210
+ prompt: '> ',
211
+ input: src,
212
+ output: process.stdout,
213
+ useGlobal: true,
214
+ writer: result => result instanceof Promise ? '' : util.inspect(result)
215
+ });
216
+ } else {
217
+ printWelcomeMessage(rawMode);
218
+ // Start REPL
219
+ const replInstance = repl.start('> ');
220
+
221
+ // Attach history
222
+ // Node versions prior to 10.11 don't support setupHistory
223
+ if (replInstance.setupHistory) {
224
+ replInstance.setupHistory('./.repl_history', () => null);
225
+ }
226
+
227
+ // Setup context
228
+ const ctx = replInstance.context;
229
+ setupContext(ctx, sdk);
230
+ }
231
+ }
232
+
233
+ const exitOnFailure = msg =>
234
+ () => {
235
+ console.error(msg)
236
+ process.exit(1);
237
+ };
238
+
239
+ // Parse command line args
240
+ //
241
+ const options = commandLineArgs(optionDefinitions);
242
+
243
+ if (options.apidocs) {
244
+ apiDocs();
245
+ process.exit();
246
+ }
247
+
248
+ if (options.help || (!options.raw && options.clientid == null)) {
249
+ printUsage();
250
+ process.exit();
251
+ };
252
+
253
+ let scriptSrc;
254
+ if (options.script) {
255
+ scriptSrc = fs.createReadStream(options.script);
256
+ } else {
257
+ scriptSrc = null;
258
+ }
259
+
260
+ // If client ID is provided on start, instantiate and expose sdk
261
+ if (options.clientid) {
262
+ const sdk = sharetribeSdk.createInstance({
263
+ clientId: options.clientid,
264
+ tokenStore: sharetribeSdk.tokenStore.memoryStore()
265
+ });
266
+
267
+ // If user auth info is given too, log in with the user.
268
+ if (options.user && options.password) {
269
+ console.log(`Initializing SDK instance with Client ID: ${options.clientid}...`)
270
+ sdk.marketplace.show()
271
+ .then(result => console.log(`Successfully connected to ${result.data.data.attributes.name} marketplace.`))
272
+ .catch(exitOnFailure(`Unable to access the Marketplace API with the given Client ID: ${options.clientid}.`))
273
+ .then(() => {
274
+ console.log(`Logging in user ${options.user}...`);
275
+ return sdk.login({
276
+ username: options.user,
277
+ password: options.password
278
+ });
279
+ })
280
+ .catch(exitOnFailure(`Unable to log in with the email: ${options.user} and password you gave.`))
281
+ .then(startRepl(sdk, false, scriptSrc));
282
+
283
+ // No user auth, just Client ID
284
+ } else {
285
+ console.log(`Initializing SDK instance with Client ID: ${options.clientid}...`)
286
+ sdk.marketplace.show()
287
+ .then(result => console.log(`Successfully connected to ${result.data.data.attributes.name} marketplace.`))
288
+ .catch(exitOnFailure(`Unable to access the Marketplace API with the given Client ID: ${options.clientid}.`))
289
+ .then(startRepl(sdk, false, scriptSrc));
290
+
291
+ }
292
+ // Raw mode
293
+ } else {
294
+ startRepl(null, true, scriptSrc)();
295
+ }
@@ -0,0 +1,26 @@
1
+ import Cookies from 'js-cookie';
2
+
3
+ const generateKey = (clientId, namespace) => `${namespace}-${clientId}-token`;
4
+
5
+ const createStore = ({ clientId, secure }) => {
6
+ const expiration = 30; // 30 days
7
+ const namespace = 'st';
8
+ const key = generateKey(clientId, namespace);
9
+
10
+ const getToken = () => Cookies.getJSON(key);
11
+ const setToken = tokenData => {
12
+ const secureFlag = secure ? { secure: true } : {};
13
+ Cookies.set(key, tokenData, { expires: expiration, ...secureFlag });
14
+ };
15
+ const removeToken = () => {
16
+ Cookies.remove(key);
17
+ };
18
+
19
+ return {
20
+ getToken,
21
+ setToken,
22
+ removeToken,
23
+ };
24
+ };
25
+
26
+ export default createStore;
@@ -0,0 +1,151 @@
1
+ const DEBUG = false;
2
+
3
+ const resolve = ctx => Promise.resolve(ctx);
4
+
5
+ const buildCtx = (params = {}, middleware) => ({
6
+ ...params,
7
+ enterQueue: [...middleware].reverse(),
8
+ leaveStack: [],
9
+ });
10
+
11
+ const tryExecuteMw = (ctx, mw, stage) => {
12
+ /* eslint-disable no-console */
13
+ /* eslint-disable no-undef */
14
+ if (DEBUG) {
15
+ if (mw[stage]) {
16
+ console.log(`Executing ${mw.constructor.name}#${stage}`);
17
+ }
18
+ }
19
+ /* eslint-enable no-console */
20
+ /* eslint-enable no-undef */
21
+
22
+ return resolve(ctx)
23
+ .then(mw[stage] || resolve)
24
+ .catch(error => {
25
+ const errorCtx = error.ctx || ctx;
26
+ return Promise.resolve({
27
+ ...errorCtx,
28
+ error,
29
+ errorMiddleware: mw.constructor.name,
30
+ errorStage: stage,
31
+ });
32
+ });
33
+ };
34
+
35
+ const nextMw = ctx => {
36
+ const leaveStack = [...ctx.leaveStack];
37
+ const enterQueue = [...ctx.enterQueue];
38
+ let mw;
39
+ let type;
40
+
41
+ if (ctx.error) {
42
+ mw = leaveStack.shift();
43
+ type = 'error';
44
+ } else if (enterQueue.length) {
45
+ mw = enterQueue.pop();
46
+ leaveStack.unshift(mw);
47
+ type = 'enter';
48
+ } else {
49
+ mw = leaveStack.shift();
50
+ type = 'leave';
51
+ }
52
+ return [{ ...ctx, enterQueue, leaveStack }, mw, type];
53
+ };
54
+
55
+ const executeCtx = ctx => {
56
+ const [newCtx, mw, type] = nextMw(ctx);
57
+
58
+ if (mw) {
59
+ return tryExecuteMw(newCtx, mw, type).then(executeCtx);
60
+ }
61
+
62
+ if (newCtx.error) {
63
+ const { error, ...errorCtx } = newCtx;
64
+ error.ctx = errorCtx;
65
+ return Promise.reject(error);
66
+ }
67
+
68
+ return Promise.resolve(newCtx);
69
+ };
70
+
71
+ /**
72
+
73
+ ## contextRunner([interceptors]) => (ctx: Object) => Promise
74
+
75
+ `contextRunner` takes an array of interceptor objects and returns a
76
+ runnable function that takes context map (i.e. `ctx`) as a
77
+ parameter, executes the interceptor chain and returns a Promise.
78
+
79
+ ## Return value
80
+
81
+ After running the function returned by the context runner, a
82
+ Promise will be returned.
83
+
84
+ - If the result is successful a `Promise.resolve(ctx)` is returned
85
+ - If the result is unsuccessful a `Promise.resolve(error)` is
86
+ - returned. The `error` MUST be instance of Error, and it MUST
87
+ - contain key `ctx`, which contains the context map.
88
+
89
+ ## Interceptor
90
+
91
+ Interceptor is an object with `enter`, `leave` and `exit`
92
+ functions. The object implement all `enter`, `leave` and `exit`
93
+ functions, or some or none of them.
94
+
95
+ It might be beneficial to define interceptors as classes. This
96
+ helps debugging, because instead of `[Object object]` you are able
97
+ to see the name of the interceptor. However, if you define the
98
+ interceptor as a class, you should NOT use `this` in the class
99
+ methods. The methods will be called in unbinded fashion.
100
+
101
+ ### enter(ctx) => ctx
102
+
103
+ `enter` function will be called when the context runner is
104
+ executing the interceptor chain in the `enter` stage. The function
105
+ takes context map as a parameter and returns the context map. If
106
+ the interceptor does async operations, it can also return a
107
+ Promise. If an error occures during the execution, an Error can be
108
+ thrown.
109
+
110
+ ### leave(ctx) => ctx
111
+
112
+ `leave` function will be called when the context runner is
113
+ executing the interceptor chain in the `leave` stage. The function
114
+ takes context map as a parameter and returns the context map. If
115
+ the interceptor does async operations, it can also return a
116
+ Promise. If an error occures during the execution, an Error can be
117
+ thrown.
118
+
119
+ ### error(ctx) => ctx
120
+
121
+ `error` function will be called when the context runner is
122
+ executing the interceptor chain in the `error` stage. The runner
123
+ goes to an `error` stage, if a previously executed interceptor
124
+ threw an error or returned a rejected Promise.
125
+
126
+ In `error` stage, the context map will have `ctx.error` assigned to
127
+ the error that was thrown.
128
+
129
+ The error can be rescued by returning a context map where
130
+ `ctx.error` is falsy.
131
+
132
+ ## Example usage:
133
+
134
+ ```
135
+ class LocalSumNumbers {
136
+ enter({ numbers, ...ctx }) {
137
+ return { ...ctx, sum: numbers.reduce((a, b) => a + b, 0) }
138
+ }
139
+ }
140
+
141
+ contextRunner([
142
+ new LocalSumNumbers()
143
+ ])({ numbers: [1, 2, 3, 4 ]}).then((ctx) => {
144
+ console.log(ctx.sym) // prints 10
145
+ });
146
+ ```
147
+
148
+ */
149
+ const contextRunner = middleware => params => executeCtx(buildCtx(params, middleware));
150
+
151
+ export default contextRunner;
@@ -0,0 +1,185 @@
1
+ import contextRunner from './context_runner';
2
+
3
+ describe('context runner', () => {
4
+ const createEnterMW = name => ctx => {
5
+ const { enters = [], ...newCtx } = ctx;
6
+ return { ...newCtx, enters: [...enters, name] };
7
+ };
8
+
9
+ const createLeaveMW = name => ctx => {
10
+ const { leaves = [], ...newCtx } = ctx;
11
+ return { ...newCtx, leaves: [...leaves, name] };
12
+ };
13
+
14
+ const createErrorRaiseMW = name => ctx => {
15
+ const { errors = [], ...newCtx } = ctx;
16
+ return { ...newCtx, errors: [...errors, name] };
17
+ };
18
+
19
+ const createErrorResolveMW = name => ctx => {
20
+ const { errors = [], ...newCtx } = ctx;
21
+ return { ...newCtx, errors: [...errors, name], error: null };
22
+ };
23
+
24
+ const enterRaiseMW = () => {
25
+ throw new Error('middleware enter failed');
26
+ };
27
+
28
+ const leaveRaiseMW = () => {
29
+ throw new Error('middleware leave failed');
30
+ };
31
+
32
+ const createTestMiddlewareEL = name => ({
33
+ enter: createEnterMW(name),
34
+ leave: createLeaveMW(name),
35
+ });
36
+
37
+ const createTestMiddlewareE = name => ({
38
+ enter: createEnterMW(name),
39
+ });
40
+
41
+ const createTestMiddlewareL = name => ({
42
+ leave: createLeaveMW(name),
43
+ });
44
+
45
+ const createTestMiddlewareELERaise = name => ({
46
+ enter: createEnterMW(name),
47
+ leave: createLeaveMW(name),
48
+ error: createErrorRaiseMW(name),
49
+ });
50
+
51
+ const createTestMiddlewareRaise = () => ({
52
+ enter: enterRaiseMW,
53
+ });
54
+
55
+ const createTestMiddlewareELEResolve = name => ({
56
+ enter: createEnterMW(name),
57
+ leave: createLeaveMW(name),
58
+ error: createErrorResolveMW(name),
59
+ });
60
+
61
+ const createTestMiddlewareELRaise = name => ({
62
+ enter: createEnterMW(name),
63
+ leave: leaveRaiseMW,
64
+ });
65
+
66
+ const createEnterTimeoutMW = name => ctx => {
67
+ const { enters = [], ...newCtx } = ctx;
68
+
69
+ return new Promise(resolve => {
70
+ // eslint-disable-next-line no-undef
71
+ setTimeout(() => {
72
+ resolve({ ...newCtx, enters: [...enters, name] });
73
+ }, 10);
74
+ });
75
+ };
76
+
77
+ const createLeaveTimeoutMW = name => ctx => {
78
+ const { leaves = [], ...newCtx } = ctx;
79
+
80
+ return new Promise(resolve => {
81
+ // eslint-disable-next-line no-undef
82
+ setTimeout(() => {
83
+ resolve({ ...newCtx, leaves: [...leaves, name] });
84
+ }, 10);
85
+ });
86
+ };
87
+
88
+ const createTestMiddlewareTimeoutEL = name => ({
89
+ enter: createEnterTimeoutMW(name),
90
+ leave: createLeaveTimeoutMW(name),
91
+ });
92
+
93
+ it('exists', () => {
94
+ expect(contextRunner).not.toBeNull();
95
+ expect(contextRunner).not.toBeUndefined();
96
+ });
97
+
98
+ it('executes enter and leave phases of middleware', () => {
99
+ const mw = createTestMiddlewareEL('one');
100
+ const runner = contextRunner([mw]);
101
+
102
+ return runner().then(ctx => {
103
+ expect(ctx.enters).toEqual(['one']);
104
+ expect(ctx.leaves).toEqual(['one']);
105
+ });
106
+ });
107
+
108
+ it('executes enters in given orders and leaves in reverse', () => {
109
+ const mw1 = createTestMiddlewareEL('one');
110
+ const mw2 = createTestMiddlewareEL('two');
111
+ const mw3 = createTestMiddlewareEL('three');
112
+ const runner = contextRunner([mw1, mw2, mw3]);
113
+
114
+ return runner().then(ctx => {
115
+ expect(ctx.enters).toEqual(['one', 'two', 'three']);
116
+ expect(ctx.leaves).toEqual(['three', 'two', 'one']);
117
+ });
118
+ });
119
+
120
+ it('calls enter and leave only on middleware that define them', () => {
121
+ const mw1 = createTestMiddlewareE('one');
122
+ const mw2 = createTestMiddlewareEL('two');
123
+ const mw3 = createTestMiddlewareL('three');
124
+ const runner = contextRunner([mw1, mw2, mw3]);
125
+
126
+ return runner().then(ctx => {
127
+ expect(ctx.enters).toEqual(['one', 'two']);
128
+ expect(ctx.leaves).toEqual(['three', 'two']);
129
+ });
130
+ });
131
+
132
+ it('calls error on all middlewares in stack', () => {
133
+ const mw1 = createTestMiddlewareELERaise('one');
134
+ const mw2 = createTestMiddlewareELERaise('two');
135
+ const mw3 = createTestMiddlewareRaise();
136
+ const runner = contextRunner([mw1, mw2, mw3]);
137
+
138
+ return runner().catch(error => {
139
+ const { ctx } = error;
140
+ expect(ctx.enters).toEqual(['one', 'two']);
141
+ expect(ctx.leaves).toBeUndefined();
142
+ expect(ctx.errors).toEqual(['two', 'one']);
143
+ });
144
+ });
145
+
146
+ it('middleware can resolve an error which leads to remaining leaves being called', () => {
147
+ const mw1 = createTestMiddlewareEL('one');
148
+ const mw2 = createTestMiddlewareELEResolve('two');
149
+ const mw3 = createTestMiddlewareRaise();
150
+ const runner = contextRunner([mw1, mw2, mw3]);
151
+
152
+ return runner().then(ctx => {
153
+ expect(ctx.enters).toEqual(['one', 'two']);
154
+ expect(ctx.leaves).toEqual(['one']);
155
+ expect(ctx.errors).toEqual(['two']);
156
+ });
157
+ });
158
+
159
+ it('leave raising trigger the error flow', () => {
160
+ const mw1 = createTestMiddlewareELERaise('one');
161
+ const mw2 = createTestMiddlewareELRaise('two');
162
+ const mw3 = createTestMiddlewareEL('three');
163
+ const runner = contextRunner([mw1, mw2, mw3]);
164
+
165
+ return runner().catch(error => {
166
+ const { ctx } = error;
167
+
168
+ expect(ctx.enters).toEqual(['one', 'two', 'three']);
169
+ expect(ctx.leaves).toEqual(['three']);
170
+ expect(ctx.errors).toEqual(['one']);
171
+ });
172
+ });
173
+
174
+ it('supports async operations', () => {
175
+ const mw1 = createTestMiddlewareTimeoutEL('one');
176
+ const mw2 = createTestMiddlewareTimeoutEL('two');
177
+ const mw3 = createTestMiddlewareTimeoutEL('three');
178
+ const runner = contextRunner([mw1, mw2, mw3]);
179
+
180
+ return runner().then(ctx => {
181
+ expect(ctx.enters).toEqual(['one', 'two', 'three']);
182
+ expect(ctx.leaves).toEqual(['three', 'two', 'one']);
183
+ });
184
+ });
185
+ });
package/src/detect.js ADDED
@@ -0,0 +1,11 @@
1
+ /**
2
+
3
+ Collection of functions for detecting browser/server capabilities.
4
+
5
+ */
6
+
7
+ /* eslint-disable import/prefer-default-export */
8
+ /* eslint-disable no-undef */
9
+
10
+ export const hasBrowserCookies = () =>
11
+ typeof document === 'object' && typeof document.cookie === 'string';