<commit_before>"use strict"
if (typeof define !== 'function') {
    var define = require('amdefine')(module)
}

define([ 'underscore', 'q', './jscr-api', './test-utils' ],

function(_, Q, API, Utils) {
    return function(newConnection) {
        var trace = Utils.trace;
        describe('API.Project', function() {
            var projectName = "test";
            var project;
            var promise;
            var testPromise;

            beforeEach(function() {
                testPromise = new Utils.TestPromise();
                var connection = newConnection();
                promise = connection.connect().then(function(workspace) {
                    return workspace.loadProject(projectName, {
                        create : true
                    });
                }).then(function(prj) {
                    project = prj;
                });
            });
            afterEach(function() {
                testPromise.waitsForFinish();
                testPromise = null;
            })

            it('should have all methods defined by the API', function() {
                testPromise.test(promise.then(function() {
                    return Utils.checkMethods(project, 'getProjectKey',
                            'loadResource', 'loadResources', 'deleteResource',
                            'storeResource', 'loadModifiedResources',
                            'loadResourceHistory', 'searchResources');
                }));
            });

            it('should be able to return an empty resource', function() {
                var path = API.normalizeKey('README.txt');
                testPromise.test(promise.then(function() {
                    return project.loadResource(path, {
                        create : true
                    });
                }).then(function(resource) {
                    expect(resource).not.toEqual(null);
                    expect(resource.getPath()).toEqual(path);
                    expect(resource.getPath()).toEqual(path);
                }));
            });

            it('should be able to load child resources', function() {
                function testChildResources(project, parentPath, childPaths) {
                    childPaths = _.map(childPaths, function(p) {
                        return API.normalizeKey(p);
                    })
                    var p = project.loadChildResources(parentPath)
                    //
                    .then(function(children) {
                        // expect(_.keys(children).length).toEqual(childPaths.length)
                        _.each(childPaths, function(path) {
                            var child = children[path];
                            expect(child).not.toEqual(null);
                            expect(child.getPath()).toEqual(path);
                        });
                    });
                    Utils.testPromise(p);
                }

                var list = [ 'about', 'about/team', 'about/news', 'docs',
                        'docs/help', 'docs/help/introduction',
                        '/path/to/resource' ];

                testPromise.test(promise.then(function() {
                    return project.loadResources(list, {
                        create : true
                    });
                }).then(
                        function(resources) {
                            _.each(list, function(name) {
                                name = API.normalizeKey(name);
                                var resource = resources[name];
                                var path = resource.getPath();
                                expect(path).toEqual(name);
                            })
                            testChildResources(project, 'about', [
                                    'about/team', 'about/news' ]);
                            testChildResources(project, '', [ 'about', 'docs',
                                    'path' ]);
                            return true;
                        }));
            });

            it('should be able to create a new resource', function() {
                var path = API.normalizeKey('README.txt');
                testPromise.test(promise.then(function() {
                    return project.loadResource(path, {
                        create : true
                    });
                }).then(function(resource) {
                    expect(resource).not.toEqual(null);
                    var properties = resource.getProperties();
                    expect(resource.getPath()).toEqual(path);
                    expect(properties).not.toEqual(null);
                    var sys = resource.getProperties();
                    expect(sys).not.toEqual(null);

                    var updated = resource.getUpdated();
                    expect(updated).not.toEqual(null);

                    var created = resource.getCreated();
                    expect(created).not.toEqual(null);
                    expect(created).toEqual(updated);
                }));
            })

            function checkHistory(resource, len) {
                var history = null;
                var path = resource.getPath();
                return project.loadResourceHistory(path).then(function(h) {
                    history = h;
                    expect(history.length).toEqual(len);
                    return project.loadResourceRevisions(path, {
                        versions : history
                    });
                }).then(function(revisions) {
                    expect(revisions.length).toEqual(len);
                    var pos = 0;
                    _.each(revisions, function(revision) {
                        var version = revision.getUpdated();
                        var expected = history[pos++];
                        expect(version).toEqual(expected);
                    })
                });
            }

            // }
            it('should be able to show resource history', function() {
                var path = API.normalizeKey('README.txt');
                var resource = null;
                testPromise.test(promise.then(function() {
                    return project.loadResource(path, {
                        create : true
                    });
                }).then(function(r) {
                    resource = r;
                    return checkHistory(resource, 1);
                }).then(function() {
                    var copy = resource.getCopy();
                    var props = copy.getProperties();
                    props.label = 'Hello, world!';
                    props.description = 'This is a short description';
                    return project.storeResource(copy);
                }).then(function() {
                    return checkHistory(resource, 2);
                }));
            });
        });
    }
});<commit_msg>Compare only some version fields in test.<commit_after>"use strict"
if (typeof define !== 'function') {
    var define = require('amdefine')(module)
}

define([ 'underscore', 'q', './jscr-api', './test-utils' ],

function(_, Q, API, Utils) {
    return function(newConnection) {
        var trace = Utils.trace;
        describe('API.Project', function() {
            var projectName = "test";
            var project;
            var promise;
            var testPromise;

            beforeEach(function() {
                testPromise = new Utils.TestPromise();
                var connection = newConnection();
                promise = connection.connect().then(function(workspace) {
                    return workspace.loadProject(projectName, {
                        create : true
                    });
                }).then(function(prj) {
                    project = prj;
                });
            });
            afterEach(function() {
                testPromise.waitsForFinish();
                testPromise = null;
            })

            it('should have all methods defined by the API', function() {
                testPromise.test(promise.then(function() {
                    return Utils.checkMethods(project, 'getProjectKey',
                            'loadResource', 'loadResources', 'deleteResource',
                            'storeResource', 'loadModifiedResources',
                            'loadResourceHistory', 'searchResources');
                }));
            });

            it('should be able to return an empty resource', function() {
                var path = API.normalizeKey('README.txt');
                testPromise.test(promise.then(function() {
                    return project.loadResource(path, {
                        create : true
                    });
                }).then(function(resource) {
                    expect(resource).not.toEqual(null);
                    expect(resource.getPath()).toEqual(path);
                    expect(resource.getPath()).toEqual(path);
                }));
            });

            it('should be able to load child resources', function() {
                function testChildResources(project, parentPath, childPaths) {
                    childPaths = _.map(childPaths, function(p) {
                        return API.normalizeKey(p);
                    })
                    var p = project.loadChildResources(parentPath)
                    //
                    .then(function(children) {
                        // expect(_.keys(children).length).toEqual(childPaths.length)
                        _.each(childPaths, function(path) {
                            var child = children[path];
                            expect(child).not.toEqual(null);
                            expect(child.getPath()).toEqual(path);
                        });
                    });
                    Utils.testPromise(p);
                }

                var list = [ 'about', 'about/team', 'about/news', 'docs',
                        'docs/help', 'docs/help/introduction',
                        '/path/to/resource' ];

                testPromise.test(promise.then(function() {
                    return project.loadResources(list, {
                        create : true
                    });
                }).then(
                        function(resources) {
                            _.each(list, function(name) {
                                name = API.normalizeKey(name);
                                var resource = resources[name];
                                var path = resource.getPath();
                                expect(path).toEqual(name);
                            })
                            testChildResources(project, 'about', [
                                    'about/team', 'about/news' ]);
                            testChildResources(project, '', [ 'about', 'docs',
                                    'path' ]);
                            return true;
                        }));
            });

            it('should be able to create a new resource', function() {
                var path = API.normalizeKey('README.txt');
                testPromise.test(promise.then(function() {
                    return project.loadResource(path, {
                        create : true
                    });
                }).then(function(resource) {
                    expect(resource).not.toEqual(null);
                    var properties = resource.getProperties();
                    expect(resource.getPath()).toEqual(path);
                    expect(properties).not.toEqual(null);
                    var sys = resource.getProperties();
                    expect(sys).not.toEqual(null);

                    var updated = resource.getUpdated();
                    expect(updated).not.toEqual(null);

                    var created = resource.getCreated();
                    expect(created).not.toEqual(null);
                    expect(created).toEqual(updated);
                }));
            })

            function checkHistory(resource, len) {
                var history = null;
                var path = resource.getPath();
                return project.loadResourceHistory(path).then(function(h) {
                    history = h;
                    expect(history.length).toEqual(len);
                    return project.loadResourceRevisions(path, {
                        versions : history
                    });
                }).then(
                        function(revisions) {
                            expect(revisions.length).toEqual(len);
                            var pos = 0;
                            _.each(revisions, function(revision) {
                                var version = revision.getUpdated();
                                var expected = history[pos++];
                                _.each([ 'versionId', 'timestamp', 'author' ],
                                        function(key) {
                                            expect(version[key]).toEqual(
                                                    expected[key]);
                                        })
                            })
                        });
            }

            // }
            it('should be able to show resource history', function() {
                var path = API.normalizeKey('README.txt');
                var resource = null;
                testPromise.test(promise.then(function() {
                    return project.loadResource(path, {
                        create : true
                    });
                }).then(function(r) {
                    resource = r;
                    return checkHistory(resource, 1);
                }).then(function() {
                    var copy = resource.getCopy();
                    var props = copy.getProperties();
                    props.label = 'Hello, world!';
                    props.description = 'This is a short description';
                    return project.storeResource(copy);
                }).then(function() {
                    return checkHistory(resource, 2);
                }));
            });
        });
    }
});<|endoftext|>


<commit_before>
module.exports = {
    w3cmemes: {
        origin:         "W3CMemes"
    ,   name:           "W3CMemes"
    ,   description:    "The official news agency for the W3C community"
    }
,   w3cnews: {
        origin:         "W3C News"
    ,   name:           "W3C News"
    ,   description:    "The party line"
    }
,   "html-wg":  {
        origin:         "github"
    ,   name:           "HTML WG"
    ,   description:    "HTML Working Group work stream"
    ,   repository:     [
                            "w3c/html"
                        ,   "w3c/charter-html"
                        ,   "w3c/test-results"
                        ,   "w3c/dom"
                        ,   "w3c/aria-in-html"
                        ]
    }
,   wpt:  {
        origin:         "github"
    ,   name:           "WPT"
    ,   description:    "Web Platform Tests work stream"
    ,   repository:     [
                            "w3c/web-platform-tests"
                        ,   "w3c/wpt-runner"
                        ,   "w3c/test-results"
                        ,   "w3c/wpt-tools"
                        ,   "w3c/testharness.js"
                        ]
    }
,   "modern-tooling":   {
        origin:         "github"
    ,   name:           "Modern Tooling"
    ,   description:    "Work stream for the Modern Tooling project"
    ,   repository:     [
                            "w3c/echidna"
                        ,   "w3c/modern-tooling"
                        ,   "w3c/pheme"
                        ,   "w3c/midgard"
                        ,   "w3c/ash-nazg"
                        ,   "w3c/specberus"
                        ,   "w3c/respec"
                        ,   "w3c/respec-docs"
                        ,   "w3c/w3c-api"
                        ,   "w3c/developers"
                        ,   "w3c/third-party-resources-checker"
                        ,   "w3c/tr-design"
                        ]
    }
,   "html-future":   {
        origin:         "github"
    ,   name:           "Future of HTML"
    ,   description:    "Discussion on the future of HTML"
    ,   repository:     [
                            "w3c/charter-html"
                        ]
    }
,   "webrtc":   {
        origin:         "github"
    ,   name:           "WebRTC specs"
    ,   description:    "Github activity on WebRTC specs"
    ,   repository:     [
                            "w3c/webrtc-pc"
                        ,   "w3c/webrtc-identity"
                        ,   "w3c/webrtc-quic"
                        ,   "w3c/webrtc-ice"
                        ,   "w3c/webrtc-priority"
                        ,   "w3c/webrtc-nv-use-cases"
                        ,   "w3c/mediacapture-main"
                        ,   "w3c/mediacapture-record"
                        ,   "w3c/mediacapture-image"
                        ,   "w3c/mediacapture-depth"
                        ,   "w3c/mediacapture-fromelement"
                        ,   "w3c/mediacapture-output"
                        ,   "w3c/webrtc-stats"
                        ,   "w3c/mediacapture-screen-share"
                        ,   "w3c/webrtc-charter"
                        ,   "w3c/webrtc-respec-ci"
                        ,   "w3c/mst-content-hint"
      ,   "w3c/webrtc-interop-reports"
      ,   "w3c/webrtc-svc"
      ,   "w3c/webrtc-extensions"
      ,   "w3c/mediacapture-extensions"
      ,   "w3c/webrtc-provisional-stats",
      "w3c/webrtc-encoded-transform", "w3c/mediacapture-automation", "w3c/mediacapture-transform", "w3c/mediacapture-region"
    ]
    }
,   "csvw":   {
        origin:         "github"
    ,   name:           "CSVW Specs"
    ,   description:    "Github activity on the CSV on the Web specs"
    ,   repository:     [
                            "w3c/csvw"
                        ]
    }
,   "dpub":   {
        origin:         "github"
    ,   name:           "DPUB documents"
    ,   description:    "Github activity around digital publishing"
    ,   repository:     [
                            "w3c/dpub",
                            "w3c/dpub-accessibility",
                            "w3c/dpub-annotation",
                            "w3c/dpub-content-and-markup",
                            "w3c/dpub-metadata",
                            "w3c/dpub-stem",
                            "w3c/dpub-validation",
                            "w3c/dpub-pwp",
                            "w3c/dpub-pwp-loc",
                            "w3c/dpub-pwp-arch",
                            "w3c/dpub-pwp-ucr"
                        ]
    }
,   "publ": {
        origin:      "github"
    ,   name:        "Publishing Working Group documents"
    ,   description: "Github activities around the Publishing on the Web WG specs"
    ,   repository:  [
                         "w3c/publ-wg",
                         "w3c/wpub",
                         "w3c/pwpub",
                         "w3c/epub4",
                         "w3c/dpub-aria-2.0",
                         "w3c/publ-a11y"
                     ]
    }
,   "poe": {
        origin:      "github"
    ,   name:        "POE Working Group documents"
    ,   description: "Github activity around the Permissions and Obligations Expression Working Group"
    ,   repository:  [
                         "w3c/poe"
                     ]
    }
,   "annotation":   {
        origin:         "github"
    ,   name:           "Web Annotations specs"
    ,   description:    "Github activity on Web Annotation specs"
    ,   repository:     [
                            "w3c/web-annotation",
                            "w3c/findtext"
                        ]
    }
,   "das":   {
        origin:         "github"
    ,   name:           "Device & Sensors specs"
    ,   description:    "Github activity on Device & Sensors Working Group specs"
    ,   repository:     [
                            "w3c/sensors"
                        ,   "w3c/ambient-light"
                        ,   "w3c/proximity"
                        ,   "w3c/vibration"
                        ,   "w3c/battery"
                        ,   "w3c/wake-lock"
                        ,   "w3c/html-media-capture"
                        ,   "w3c/devicesensors-wg"
                        ,   "w3c/gyroscope"
                        ,   "w3c/accelerometer"
                        ]
    }
,   "immersiveweb":   {
        origin:         "github"
    ,   name:           "Immersive Web Repositories"
    ,   description:    "Github activity on Immersive Web CG & WG"
    ,   repository:     [
                            "immersive-web/webxr"
                        ,   "immersive-web/administrativia"
                        ,   "immersive-web/homepage"
                        ,   "immersive-web/spatial-favicons"
                        ,   "immersive-web/lighting-estimation"
                        ,   "immersive-web/proposals"
                        ,   "immersive-web/privacy-and-security"
                        ,   "immersive-web/geo-alignment"
                        ,   "immersive-web/hit-test"
                        ,   "immersive-web/anchors"
                        ,   "immersive-web/community-resources"
                        ]
    }
,   "machinelearning":   {
        origin:         "github"
    ,   name:           "Web Machine Learning Repositories"
    ,   description:    "Github activity on WebML WG"
    ,   repository:     [
                            "webmachinelearning/webnn"
                        ,   "webmachinelearning/model-loader"
                        ,   "webmachinelearning/meetings"
                        ,   "webmachinelearning/proposals"
                        ,   "webmachinelearning/ethical-webmachinelearning"
    ]
    }
,   "web-networks":   {
        origin:         "github"
    ,   name:           "Web & Networks IG Repositories"
    ,   description:    "Github activity on Web & Networks Interest Group"
  ,   repository:     [
    "w3c/edge-computing-web-exploration",
    "w3c/network-emulation",
    "w3c/web-networks"
  ]
}
};
<commit_msg>Add new webrtc repo<commit_after>
module.exports = {
    w3cmemes: {
        origin:         "W3CMemes"
    ,   name:           "W3CMemes"
    ,   description:    "The official news agency for the W3C community"
    }
,   w3cnews: {
        origin:         "W3C News"
    ,   name:           "W3C News"
    ,   description:    "The party line"
    }
,   "html-wg":  {
        origin:         "github"
    ,   name:           "HTML WG"
    ,   description:    "HTML Working Group work stream"
    ,   repository:     [
                            "w3c/html"
                        ,   "w3c/charter-html"
                        ,   "w3c/test-results"
                        ,   "w3c/dom"
                        ,   "w3c/aria-in-html"
                        ]
    }
,   wpt:  {
        origin:         "github"
    ,   name:           "WPT"
    ,   description:    "Web Platform Tests work stream"
    ,   repository:     [
                            "w3c/web-platform-tests"
                        ,   "w3c/wpt-runner"
                        ,   "w3c/test-results"
                        ,   "w3c/wpt-tools"
                        ,   "w3c/testharness.js"
                        ]
    }
,   "modern-tooling":   {
        origin:         "github"
    ,   name:           "Modern Tooling"
    ,   description:    "Work stream for the Modern Tooling project"
    ,   repository:     [
                            "w3c/echidna"
                        ,   "w3c/modern-tooling"
                        ,   "w3c/pheme"
                        ,   "w3c/midgard"
                        ,   "w3c/ash-nazg"
                        ,   "w3c/specberus"
                        ,   "w3c/respec"
                        ,   "w3c/respec-docs"
                        ,   "w3c/w3c-api"
                        ,   "w3c/developers"
                        ,   "w3c/third-party-resources-checker"
                        ,   "w3c/tr-design"
                        ]
    }
,   "html-future":   {
        origin:         "github"
    ,   name:           "Future of HTML"
    ,   description:    "Discussion on the future of HTML"
    ,   repository:     [
                            "w3c/charter-html"
                        ]
    }
,   "webrtc":   {
        origin:         "github"
    ,   name:           "WebRTC specs"
    ,   description:    "Github activity on WebRTC specs"
    ,   repository:     [
                            "w3c/webrtc-pc"
                        ,   "w3c/webrtc-identity"
                        ,   "w3c/webrtc-quic"
                        ,   "w3c/webrtc-ice"
                        ,   "w3c/webrtc-priority"
                        ,   "w3c/webrtc-nv-use-cases"
                        ,   "w3c/mediacapture-main"
                        ,   "w3c/mediacapture-record"
                        ,   "w3c/mediacapture-image"
                        ,   "w3c/mediacapture-depth"
                        ,   "w3c/mediacapture-fromelement"
                        ,   "w3c/mediacapture-output"
                        ,   "w3c/webrtc-stats"
                        ,   "w3c/mediacapture-screen-share"
                        ,   "w3c/webrtc-charter"
                        ,   "w3c/webrtc-respec-ci"
                        ,   "w3c/mst-content-hint"
      ,   "w3c/webrtc-interop-reports"
      ,   "w3c/webrtc-svc"
      ,   "w3c/webrtc-extensions"
      ,   "w3c/mediacapture-extensions"
      ,   "w3c/webrtc-provisional-stats",
      "w3c/webrtc-encoded-transform", "w3c/mediacapture-automation", "w3c/mediacapture-transform", "w3c/mediacapture-region", "w3c/mediacapture-handle"
    ]
    }
,   "csvw":   {
        origin:         "github"
    ,   name:           "CSVW Specs"
    ,   description:    "Github activity on the CSV on the Web specs"
    ,   repository:     [
                            "w3c/csvw"
                        ]
    }
,   "dpub":   {
        origin:         "github"
    ,   name:           "DPUB documents"
    ,   description:    "Github activity around digital publishing"
    ,   repository:     [
                            "w3c/dpub",
                            "w3c/dpub-accessibility",
                            "w3c/dpub-annotation",
                            "w3c/dpub-content-and-markup",
                            "w3c/dpub-metadata",
                            "w3c/dpub-stem",
                            "w3c/dpub-validation",
                            "w3c/dpub-pwp",
                            "w3c/dpub-pwp-loc",
                            "w3c/dpub-pwp-arch",
                            "w3c/dpub-pwp-ucr"
                        ]
    }
,   "publ": {
        origin:      "github"
    ,   name:        "Publishing Working Group documents"
    ,   description: "Github activities around the Publishing on the Web WG specs"
    ,   repository:  [
                         "w3c/publ-wg",
                         "w3c/wpub",
                         "w3c/pwpub",
                         "w3c/epub4",
                         "w3c/dpub-aria-2.0",
                         "w3c/publ-a11y"
                     ]
    }
,   "poe": {
        origin:      "github"
    ,   name:        "POE Working Group documents"
    ,   description: "Github activity around the Permissions and Obligations Expression Working Group"
    ,   repository:  [
                         "w3c/poe"
                     ]
    }
,   "annotation":   {
        origin:         "github"
    ,   name:           "Web Annotations specs"
    ,   description:    "Github activity on Web Annotation specs"
    ,   repository:     [
                            "w3c/web-annotation",
                            "w3c/findtext"
                        ]
    }
,   "das":   {
        origin:         "github"
    ,   name:           "Device & Sensors specs"
    ,   description:    "Github activity on Device & Sensors Working Group specs"
    ,   repository:     [
                            "w3c/sensors"
                        ,   "w3c/ambient-light"
                        ,   "w3c/proximity"
                        ,   "w3c/vibration"
                        ,   "w3c/battery"
                        ,   "w3c/wake-lock"
                        ,   "w3c/html-media-capture"
                        ,   "w3c/devicesensors-wg"
                        ,   "w3c/gyroscope"
                        ,   "w3c/accelerometer"
                        ]
    }
,   "immersiveweb":   {
        origin:         "github"
    ,   name:           "Immersive Web Repositories"
    ,   description:    "Github activity on Immersive Web CG & WG"
    ,   repository:     [
                            "immersive-web/webxr"
                        ,   "immersive-web/administrativia"
                        ,   "immersive-web/homepage"
                        ,   "immersive-web/spatial-favicons"
                        ,   "immersive-web/lighting-estimation"
                        ,   "immersive-web/proposals"
                        ,   "immersive-web/privacy-and-security"
                        ,   "immersive-web/geo-alignment"
                        ,   "immersive-web/hit-test"
                        ,   "immersive-web/anchors"
                        ,   "immersive-web/community-resources"
                        ]
    }
,   "machinelearning":   {
        origin:         "github"
    ,   name:           "Web Machine Learning Repositories"
    ,   description:    "Github activity on WebML WG"
    ,   repository:     [
                            "webmachinelearning/webnn"
                        ,   "webmachinelearning/model-loader"
                        ,   "webmachinelearning/meetings"
                        ,   "webmachinelearning/proposals"
                        ,   "webmachinelearning/ethical-webmachinelearning"
    ]
    }
,   "web-networks":   {
        origin:         "github"
    ,   name:           "Web & Networks IG Repositories"
    ,   description:    "Github activity on Web & Networks Interest Group"
  ,   repository:     [
    "w3c/edge-computing-web-exploration",
    "w3c/network-emulation",
    "w3c/web-networks"
  ]
}
};
<|endoftext|>


<commit_before>// Filename: formurlencoded.js
// Timestamp: 2017.07.04-19:19:11 (last modified)
// Author(s): Bumblehead (www.bumblehead.com), JBlashill (james@blashill.com), Jumper423 (jump.e.r@yandex.ru)

export default (data, opts = {}) => {
    const sorted = Boolean(opts.sorted),
        skipIndex = Boolean(opts.skipIndex),
        ignorenull = Boolean(opts.ignorenull),
        skipBracket = Boolean(opts.skipBracket),

        encode = value => String(value)
            .replace(/[^ !'()~\*]/gu, encodeURIComponent)
            .replace(/ /g, '+')
            .replace(/[!'()~\*]/g, ch =>
                '%' + ch.charCodeAt().toString(16).slice(-2).toUpperCase()),

        keys = (obj, keyarr = Object.keys(obj)) =>
            sorted ? keyarr.sort() : keyarr,

        filterjoin = arr => arr.filter(e => e).join('&'),

        objnest = (name, obj) =>
            filterjoin(keys(obj).map(key =>
                nest(name + '[' + key + ']', obj[key]))),

        arrnest = (name, arr, brackets = skipBracket ? '' : '[]') => arr.length
            ? filterjoin(arr.map((elem, index) => skipIndex
                ? nest(name + brackets, elem)
                : nest(name + '[' + index + ']', elem)))
            : encode(name + brackets),

        nest = (name, value, type = typeof value, f = null) => {
            if (value === f)
                f = ignorenull ? f : encode(name) + '=' + f;
            else if (/string|number|boolean/.test(type))
                f = encode(name) + '=' + encode(value);
            else if (Array.isArray(value))
                f = arrnest(name, value);
            else if (type === 'object')
                f = objnest(name, value);

            return f;
        };

    return data && filterjoin(keys(data).map(key => nest(key, data[key])));
};
<commit_msg>Add option to use a Set to simulate checkbox forms<commit_after>// Filename: formurlencoded.js
// Timestamp: 2017.07.04-19:19:11 (last modified)
// Author(s): Bumblehead (www.bumblehead.com), JBlashill (james@blashill.com), Jumper423 (jump.e.r@yandex.ru)

export default (data, opts = {}) => {
    const sorted = Boolean(opts.sorted),
    skipIndex = Boolean(opts.skipIndex),
    ignorenull = Boolean(opts.ignorenull),
    skipBracket = Boolean(opts.skipBracket),

    encode = value => String(value)
        .replace(/[^ !'()~\*]/gu, encodeURIComponent)
        .replace(/ /g, '+')
        .replace(/[!'()~\*]/g, ch =>
            '%' + ch.charCodeAt().toString(16).slice(-2).toUpperCase()),

    keys = (obj, keyarr = Object.keys(obj)) =>
        sorted ? keyarr.sort() : keyarr,

    filterjoin = arr => arr.filter(e => e).join('&'),

    objnest = (name, obj) =>
        filterjoin(keys(obj).map(key =>
            nest(name + '[' + key + ']', obj[key]))),

    arrnest = (name, arr, brackets = skipBracket ? '' : '[]') => arr.length
        ? filterjoin(arr.map((elem, index) => skipIndex
            ? nest(name + brackets, elem)
            : nest(name + '[' + index + ']', elem)))
        : encode(name + brackets),

    setnest = (name, set, arr=[]) => {
        set.forEach(elem => {
          arr.push(nest(name, elem))
        })
        return filterjoin(arr);
    }

    nest = (name, value, type = typeof value, f = null) => {
        if (value === f)
            f = ignorenull ? f : encode(name) + '=' + f;
        else if (/string|number|boolean/.test(type))
            f = encode(name) + '=' + encode(value);
        else if (Array.isArray(value))
            f = arrnest(name, value);
        else if (value instanceof Set)
          f = setnest(name, value);
        else if (type === 'object')
            f = objnest(name, value);

        return f;
    };

return data && filterjoin(keys(data).map(key => nest(key, data[key])));
};
<|endoftext|>


<commit_before>'use strict';
const debug = require('debug')('brunch:asset');
const sysPath = require('universal-path');
const readFromCache = require('fcache').readFile;
const processAsset = require('./pipeline').processAsset;
const BrunchError = require('../utils/error');
const isIgnored = require('./is_ignored');

// Returns all parent directories:
// < parentDirs('app/assets/thing/index.html')
// > ['app/', 'app/assets/', 'app/assets/thing/']
const parentDirs = path => {
  return path.split('/').map((part, index, parts) => {
    return parts.slice(0, index).concat(part, '').join('/');
  }).slice(0, -1);
};

class Asset {
  constructor(path, publicPath, assetsConvention) {
    debug(`Init asset ${path}`);

    this.destinationPath = this.path = path;
    this.compiled = '';
    this.removed = this.disposed = false;

    this._publicPath = publicPath;
    this._assetsConvention = assetsConvention;
    this._wasProcessed = false;
    this._rawSource = new Buffer(0);

    this.error = null;
    this.dependencies = [];
    this.compilationTime = 0;

    // Disallow adding new properties and changing descriptors.
    Object.seal(this);
  }

  get source() {
    return `${this._rawSource}`;
  }

  get copyTime() {
    return this.compilationTime;
  }

  compile() {
    const path = this.path;
    if (this.disposed) {
      throw new BrunchError('ALREADY_DISPOSED', {path});
    }

    if (isIgnored(path)) return Promise.resolve(this);

    return readFromCache(path)
      .then(data => {
        const asset = this;
        asset._wasProcessed = false;
        asset._rawSource = data;

        return {
          path,
          get data() {
            asset._wasProcessed = true;
            return `${data}`;
          },
        };
      })
      .then(processAsset)
      .then(file => this._updateCache(file))
      .catch(error => {
        this.error = error;
        return this;
      });
  }

  _updateCache(file) {
    const path = sysPath.normalize(file.path);
    const assetsDir = parentDirs(path).find(this._assetsConvention);
    if (!assetsDir) {
      throw new BrunchError('MOVED_ASSET', {path});
    }

    const rel = sysPath.relative(assetsDir, path);

    this.destinationPath = sysPath.join(this._publicPath, rel);
    this.compiled = this._wasProcessed ? file.data : this.source;
    this.dependencies = file.dependencies || [];
    this.compilationTime = Date.now();
  }

  dispose() {
    debug(`Disposing asset ${this.path}`);

    this.path = '';
    this._rawSource = new Buffer(0);
    this.compiled = '';
    this.disposed = true;
    this.error = null;
    this.dependencies = Object.freeze([]);

    // You're frozen when your heart's not open.
    Object.freeze(this);
  }
}

module.exports = Asset;
<commit_msg>Fix copying binary files<commit_after>'use strict';
const debug = require('debug')('brunch:asset');
const sysPath = require('universal-path');
const readFromCache = require('fcache').readFile;
const processAsset = require('./pipeline').processAsset;
const BrunchError = require('../utils/error');
const isIgnored = require('./is_ignored');

// Returns all parent directories:
// < parentDirs('app/assets/thing/index.html')
// > ['app/', 'app/assets/', 'app/assets/thing/']
const parentDirs = path => {
  return path.split('/').map((part, index, parts) => {
    return parts.slice(0, index).concat(part, '').join('/');
  }).slice(0, -1);
};

class Asset {
  constructor(path, publicPath, assetsConvention) {
    debug(`Init asset ${path}`);

    this.destinationPath = this.path = path;
    this.compiled = '';
    this.removed = this.disposed = false;

    this._publicPath = publicPath;
    this._assetsConvention = assetsConvention;
    this._wasProcessed = false;
    this._rawSource = new Buffer(0);

    this.error = null;
    this.dependencies = [];
    this.compilationTime = 0;

    // Disallow adding new properties and changing descriptors.
    Object.seal(this);
  }

  get source() {
    return `${this._rawSource}`;
  }

  get copyTime() {
    return this.compilationTime;
  }

  compile() {
    const path = this.path;
    if (this.disposed) {
      throw new BrunchError('ALREADY_DISPOSED', {path});
    }

    if (isIgnored(path)) return Promise.resolve(this);

    return readFromCache(path)
      .then(data => {
        const asset = this;
        asset._wasProcessed = false;
        asset._rawSource = data;

        return {
          path,
          get data() {
            asset._wasProcessed = true;
            return `${data}`;
          },
        };
      })
      .then(processAsset)
      .then(file => this._updateCache(file))
      .catch(error => {
        this.error = error;
        return this;
      });
  }

  _updateCache(file) {
    const path = sysPath.normalize(file.path);
    const assetsDir = parentDirs(path).find(this._assetsConvention);
    if (!assetsDir) {
      throw new BrunchError('MOVED_ASSET', {path});
    }

    const rel = sysPath.relative(assetsDir, path);

    this.destinationPath = sysPath.join(this._publicPath, rel);
    this.compiled = this._wasProcessed ? file.data : this._rawSource;
    this.dependencies = file.dependencies || [];
    this.compilationTime = Date.now();
  }

  dispose() {
    debug(`Disposing asset ${this.path}`);

    this.path = '';
    this._rawSource = new Buffer(0);
    this.compiled = '';
    this.disposed = true;
    this.error = null;
    this.dependencies = Object.freeze([]);

    // You're frozen when your heart's not open.
    Object.freeze(this);
  }
}

module.exports = Asset;
<|endoftext|>


<commit_before>'use strict';
const debug = require('debug')('brunch:write');
const each = require('async-each');
const sysPath = require('path');
const generate = require('./generate');
const _helpers = require('../helpers');
const formatError = _helpers.formatError;
const logger = require('loggy');
const anymatch = require('anymatch');

// For use in `.filter()`.
const changedSince = startTime => {
  return generated => {
    return generated.sourceFiles.some(sourceFile => {
      return sourceFile.compilationTime >= startTime ||
             sourceFile.removed;
    });
  };
};

const formatWriteError = sourceFile => {
  return formatError(sourceFile.error, sourceFile.path);
};

const getPaths = (sourceFile, joinConfig) => {
  const sourceFileJoinConfig = joinConfig[sourceFile.type + 's'] || {};
  const hlprs = sourceFileJoinConfig.pluginHelpers;
  return Object.keys(sourceFileJoinConfig)
  .filter(key => {
    return key !== 'pluginHelpers';
  })
  .filter(generatedFilePath => {
    if (sourceFile.isHelper) {
      return hlprs.indexOf(generatedFilePath) >= 0;
    } else {
      const checker = sourceFileJoinConfig[generatedFilePath];
      return checker(sourceFile.path);
    }
  });
};

const getFiles = (fileList, config, joinConfig, startTime) => {
  const map = {};
  const anyJoinTo = {};
  const checkAnyJoinTo = file => {
    const ftype = file.type;
    if (anyJoinTo[ftype] == null) {
      anyJoinTo[ftype] = Object.keys(config.overrides)
        .map(_ => config.overrides[_].files)
        .map(spec => {
          const key = file.type + "s";
          const item = spec && spec[key];
          return item && item.joinTo;
        })
        .filter(spec => spec);
    }
    const joinSpecs = anyJoinTo[ftype];

    /* config.files was copied to config.overrides._default.files */

    /*.concat [config.files] */
    if (typeof joinSpecs === 'function') {
      return joinSpecs(file.path);
    } else if (joinSpecs.some(spec => typeof spec === 'string')) {
      return anyJoinTo[file.type] = () => true;
    } else if (!joinSpecs.length) {
      anyJoinTo[file.type] = () => false;
      return false;
    } else {
      anyJoinTo[file.type] = anymatch(joinSpecs.reduce((flat, aJoinTo) => {
        return flat.concat(Object.keys(aJoinTo).map(_ => {
          return aJoinTo[_];
        }));
      }, []));
      return anyJoinTo[file.type](file.path);
    }
  };
  fileList.files.forEach(file => {
    if (file.error == null && file.data == null) return;
    const paths = getPaths(file, joinConfig);
    paths.forEach(path => {
      if (map[path] == null) {
        map[path] = [];
      }
      return map[path].push(file);
    });
    if (!paths.length) {
      if (file.error) {
        logger.error(formatWriteError(file));
      }
      if (file.data && file.compilationTime >= startTime) {
        if (!checkAnyJoinTo(file)) {
          return logger.warn("'" + file.path + "' compiled, but not written. Check your " + file.type + "s.joinTo config.");
        }
      }
    }
  });
  return Object.keys(map).map(generatedFilePath => {
    const sourceFiles = map[generatedFilePath];
    const fullPath = sysPath.join(config.paths["public"], generatedFilePath);
    return {
      sourceFiles: sourceFiles,
      path: fullPath
    };
  });
};

const write = module.exports = (fileList, config, joinConfig, optimizers, startTime, callback) => {
  const files = getFiles(fileList, config, joinConfig, startTime);
  const errors = files
    .map(generated => {
      return generated.sourceFiles
        .filter(f => f.error != null)
        .map(formatWriteError);
    })
    .reduce( ((a, b) => a.concat(b)), []);
  if (errors.length > 0) return callback(errors.join(' ; '));
  const changed = files.filter(changedSince(startTime));
  debug(`Writing ${changed.length}/${files.length} files`);

  /* Remove files marked as such and dispose them, clean memory. */
  const disposed = {
    generated: [],
    sourcePaths: []
  };
  changed.forEach(generated => {
    const sourceFiles = generated.sourceFiles;
    sourceFiles
      .filter(file => {
        return file.removed;
      })
      .forEach(file => {
        disposed.generated.push(generated);
        disposed.sourcePaths.push(sysPath.basename(file.path));
        file.dispose();
        sourceFiles.splice(sourceFiles.indexOf(file), 1);
      });
  });
  const gen = (file, next) => {
    generate(file.path, file.sourceFiles, config, optimizers, next);
  };
  return each(changed, gen, error => {
    if (error != null) return callback(error);
    callback(null, changed, disposed);
  });
};
<commit_msg>Style fixes.<commit_after>'use strict';
const debug = require('debug')('brunch:write');
const each = require('async-each');
const sysPath = require('path');
const generate = require('./generate');
const _helpers = require('../helpers');
const formatError = _helpers.formatError;
const logger = require('loggy');
const anymatch = require('anymatch');

// For use in `.filter()`.
const changedSince = startTime => generated => {
  const g = generated;
  return g.sourceFiles.some(f => f.compilationTime >= startTime || f.removed);
};

const formatWriteError = sourceFile => {
  return formatError(sourceFile.error, sourceFile.path);
};

const getPaths = (sourceFile, joinConfig) => {
  const sourceFileJoinConfig = joinConfig[sourceFile.type + 's'] || {};
  const hlprs = sourceFileJoinConfig.pluginHelpers;
  return Object.keys(sourceFileJoinConfig)
  .filter(key => {
    return key !== 'pluginHelpers';
  })
  .filter(generatedFilePath => {
    if (sourceFile.isHelper) {
      return hlprs.indexOf(generatedFilePath) >= 0;
    } else {
      const checker = sourceFileJoinConfig[generatedFilePath];
      return checker(sourceFile.path);
    }
  });
};

const getFiles = (fileList, config, joinConfig, startTime) => {
  const map = {};
  const anyJoinTo = {};
  const checkAnyJoinTo = file => {
    const ftype = file.type;
    if (anyJoinTo[ftype] == null) {
      anyJoinTo[ftype] = Object.keys(config.overrides)
        .map(key => config.overrides[key].files)
        .map(spec => {
          const key = file.type + "s";
          const item = spec && spec[key];
          return item && item.joinTo;
        })
        .filter(spec => spec);
    }
    const joinSpecs = anyJoinTo[ftype];

    /* config.files was copied to config.overrides._default.files */

    /*.concat [config.files] */
    if (typeof joinSpecs === 'function') {
      return joinSpecs(file.path);
    } else if (joinSpecs.some(spec => typeof spec === 'string')) {
      return anyJoinTo[file.type] = () => true;
    } else if (!joinSpecs.length) {
      anyJoinTo[file.type] = () => false;
      return false;
    } else {
      anyJoinTo[file.type] = anymatch(joinSpecs.reduce((flat, aJoinTo) => {
        return flat.concat(Object.keys(aJoinTo).map(_ => {
          return aJoinTo[_];
        }));
      }, []));
      return anyJoinTo[file.type](file.path);
    }
  };
  fileList.files.forEach(file => {
    if (file.error == null && file.data == null) return;
    const paths = getPaths(file, joinConfig);
    paths.forEach(path => {
      if (map[path] == null) map[path] = [];
      map[path].push(file);
    });
    if (!paths.length) {
      if (file.error) logger.error(formatWriteError(file));
      if (file.data && file.compilationTime >= startTime) {
        if (!checkAnyJoinTo(file)) {
          logger.warn("'" + file.path + "' compiled, but not written. Check your " + file.type + "s.joinTo config.");
        }
      }
    }
  });
  return Object.keys(map).map(generatedFilePath => {
    const sourceFiles = map[generatedFilePath];
    const fullPath = sysPath.join(config.paths["public"], generatedFilePath);
    return {
      sourceFiles: sourceFiles,
      path: fullPath
    };
  });
};

const write = module.exports = (fileList, config, joinConfig, optimizers, startTime, callback) => {
  const files = getFiles(fileList, config, joinConfig, startTime);
  const errors = files
    .map(generated => {
      return generated.sourceFiles
        .filter(f => f.error != null)
        .map(formatWriteError);
    })
    .reduce( ((a, b) => a.concat(b)), []);
  if (errors.length > 0) return callback(errors.join(' ; '));
  const changed = files.filter(changedSince(startTime));
  debug(`Writing ${changed.length}/${files.length} files`);

  /* Remove files marked as such and dispose them, clean memory. */
  const disposed = {
    generated: [],
    sourcePaths: []
  };
  changed.forEach(generated => {
    const sourceFiles = generated.sourceFiles;
    sourceFiles
      .filter(file => {
        return file.removed;
      })
      .forEach(file => {
        disposed.generated.push(generated);
        disposed.sourcePaths.push(sysPath.basename(file.path));
        file.dispose();
        sourceFiles.splice(sourceFiles.indexOf(file), 1);
      });
  });
  const gen = (file, next) => {
    generate(file.path, file.sourceFiles, config, optimizers, next);
  };
  return each(changed, gen, error => {
    if (error != null) return callback(error);
    callback(null, changed, disposed);
  });
};
<|endoftext|>


<commit_before>// Core routines for event emitters
//

var lib = require('./lib')
  , url = require('url')
  , util = require('util')
  , request = require('request')
  , querystring = require('querystring')
  ;

var DEFAULT_HEARTBEAT = 30000;
var HEARTBEAT_TIMEOUT_COEFFICIENT = 1.25; // E.g. heartbeat 1000ms would trigger a timeout after 1250ms of no heartbeat.
var DEFAULT_MAX_RETRY_SECONDS     = 60 * 60;
var INITIAL_RETRY_DELAY           = 1000;

var FEED_PARAMETERS   = ['since', 'limit', 'feed', 'heartbeat', 'filter', 'include_docs'];

var SUPER_CLASS = require('events').EventEmitter;
//var SUPER_CLASS = require('stream').Stream;

function Feed (db) {
  var self = this;
  SUPER_CLASS.call(self);

  self.db = db;

  self.feed = 'continuous';
  self.heartbeat         = DEFAULT_HEARTBEAT;
  self.max_retry_seconds = DEFAULT_MAX_RETRY_SECONDS;

  self.headers = {};
  self.request = {}; // Extra options for potentially future versions of request. The caller can supply them.

  self.since = 0;

  self.retry_delay = INITIAL_RETRY_DELAY; // ms

  self.pending = { request     : null
                 , activity_at : null
                 , data        : null
                 };

  // XXX
  var events = ['drain', 'error', 'close', 'pipe', 'data', 'end', 'fd'];

  events.forEach(function(ev) {
    //self.on(ev, function() { return self['on_' + ev].apply(self, arguments) });
    self.on(ev, function() {
      var args = Array.prototype.slice.call(arguments);
      self.log.debug('EVENT ' + ev + ' ' + lib.JS(args));
    })
  })
} // Feed
util.inherits(Feed, SUPER_CLASS);

Feed.prototype.start =
Feed.prototype.follow = function follow_feed(db) {
  var self = this;

  if(db)
    self.db = db;

  if(!self.db)
    throw new Error('Database URL required');

  if(self.feed !== 'continuous')
    throw new Error('The only valid feed option is "continuous"');

  if(typeof self.heartbeat !== 'number')
    throw new Error('Required "heartbeat" value');

  var parsed = url.parse(self.db);
  self.log = lib.log4js().getLogger(parsed.hostname + parsed.pathname);
  self.log.setLevel(process.env.changes_level || "info");

  return self.confirm();
}

Feed.prototype.confirm = function confirm_feed() {
  var self = this;

  self.db_safe = lib.scrub_creds(self.db);

  self.log.debug('Checking database: ' + self.db_safe);
  self.emit('confirm');

  var confirm_timeout = self.heartbeat * 3; // Give it time to look up the name, connect, etc.
  var timeout_id = setTimeout(function() {
    return self.die(new Error('Timeout confirming database: ' + self.db_safe));
  }, confirm_timeout);

  var headers = lib.JP(lib.JS(self.headers));
  headers.accept = 'application/json';

  request({uri:self.db, headers:headers}, function(er, resp, body) {
    clearTimeout(timeout_id);

    if(er)
      return self.die(er);

    var db;
    try {
      db = JSON.parse(body)
    } catch(json_er) {
      return self.emit('error', json_er)
    }

    if(!db.db_name || !db.instance_start_time)
      return self.emit('error', new Error('Bad DB response: ' + body));

    self.log.debug('Confirmed db: ' + self.db_safe);
    return self.query();
  })
}

Feed.prototype.query = function query_feed() {
  var self = this;

  var query_params = {};
  FEED_PARAMETERS.forEach(function(key) {
    if(key in self)
      query_params[key] = self[key];
  })

  if(typeof query_params.filter !== 'string')
    delete query_params.filter;

  var feed_url = self.db + '/_changes?' + querystring.stringify(query_params);

  self.headers.accept = self.headers.accept || 'application/json';
  var req = { method : 'GET'
            , uri    : feed_url
            , headers: self.headers
            , encoding: 'utf-8'
            }

  req.changes_query = query_params;
  Object.keys(self.request).forEach(function(key) {
    req[key] = self.request[key];
  })

  var now = new Date;
  self.log.debug('Query at ' + lib.JP(lib.JS(now)) + ': ' + lib.scrub_creds(feed_url));

  var timeout_id, timed_out = false;
  var in_flight, timeout_id, timed_out = false;

  var timed_out = false;
  function on_timeout() {
    self.log.debug('Request timeout: ' + in_flight.id());
    timed_out = true;
    return self.retry();
  }

  function on_response(er, resp) {
    clearTimeout(timeout_id);

    if(timed_out) {
      self.log.debug('Ignoring late response: ' + in_flight.id());
      return destroy_response(resp);
    }

    if(er) {
      self.log.debug('Request error ' + in_flight.id() + ': ' + er.stack);
      destroy_response(resp);
      return self.retry();
    }

    if(resp.statusCode !== 200) {
      self.log.debug('Bad changes response' + in_flight.id() + ': ' + resp.statusCode);
      destroy_response(resp);
      return self.retry();
    }

    self.log.debug('Good response: ' + in_flight.id());
    self.retry_delay = INITIAL_RETRY_DELAY;
    return self.prep(in_flight);
  }

  req.onResponse = on_response;
  timeout_id = setTimeout(on_timeout, self.heartbeat);
  in_flight = request(req);
  in_flight.created_at = now;
  in_flight.id = function() { return lib.JP(lib.JS(this.created_at)) };

  // Shorten the timestamp, used for debugging.
  //in_flight.id = function() { return /\.(\d\d\d)Z$/.exec(lib.JP(lib.JS(this.created_at)))[1] };

  return self.emit('query');
}

Feed.prototype.prep = function prep_request(req) {
  var self = this;

  var now = new Date;
  self.pending.request = req;
  self.pending.activity_at = now;
  self.pending.data        = "";
  self.pending.wait_timer  = null;

  function handler_for(ev) {
    var name = 'on_couch_' + ev;
    var inner_handler = self[name];

    function handle_confirmed_req_event() {
      if(self.pending.request === req)
        return inner_handler.apply(self, arguments);

      if(!req.created_at)
        return self.die(new Error("Received data from unknown request")); // Pretty sure this is impossible.

      var s_to_now = (new Date() - req.created_at) / 1000;
      var s_to_req = '[no req]';
      if(self.pending.request)
        s_to_req = (self.pending.request.created_at - req.created_at) / 1000;

      var msg = req.id() + ': to_req=' + s_to_req + 's, to_now=' + s_to_now + 's';

      if(ev === 'end') {
        return self.log.debug('Old END ' + msg);
        return destroy_req(req);
      }

      if(ev === 'data') {
        self.log.debug('Old DATA ' + msg);
        return destroy_req(req);
      }

      self.log.warn('Received "' + ev + '" from old request started ' + s_to_req + 's before current and ' + s_to_now + 's before now');
    }

    return handle_confirmed_req_event;
  }

  var handlers = ['data', 'end', 'error'];
  handlers.forEach(function(ev) {
    req.on(ev, handler_for(ev));
  })

  return self.wait();
}

Feed.prototype.wait = function wait_for_event() {
  var self = this;
  self.emit('wait');

  if(self.pending.wait_timer)
    return self.die(new Error('wait() called but there is already a wait_timer: ' + self.pending.wait_timer));

  var timeout_ms = self.heartbeat * HEARTBEAT_TIMEOUT_COEFFICIENT;

  self.log.debug('Req ' + self.pending.request.id() + ' waiting ' + timeout_ms + 'ms for data: ' + self.db_safe);
  self.pending.wait_timer = setTimeout(function() { self.on_timeout() }, timeout_ms);
}

Feed.prototype.on_couch_data = function on_couch_data(data, req) {
  var self = this;

  if(! self.pending.wait_timer)
    return self.die(new Error('Cannot find timeout timer during incoming data'));

  clearTimeout(self.pending.wait_timer);
  self.pending.wait_timer = null;
  self.pending.activity_at = new Date;

  self.log.debug('Data from ' + self.pending.request.id());

  // Buf could have 0, 1, or many JSON objects in it.
  var buf = self.pending.data + data;
  var offset, json, change;

  while((offset = buf.indexOf("\n")) >= 0) {
    json = buf.substr(0, offset);
    buf = buf.substr(offset + 1);

    if(json == '') {
      self.log.debug('Heartbeat: ' + self.pending.request.id());
    } else {
      //self.log.debug('JSON: ' + json);
      try {
        change = JSON.parse(json);
      } catch(er) {
        return self.die(er);
      }

      //self.log.debug('Object:\n' + util.inspect(change));

      seq = change.seq;
      if(!seq)
        return self.die(new Error('Change has no .seq field: ' + json));

      self.on_change(change);
    }
  }

  self.pending.data = buf;
  self.wait();
}

Feed.prototype.on_timeout = function on_timeout() {
  var self = this;

  var now = new Date;
  var elapsed_ms = now - self.pending.activity_at;
  self.log.warn('Closing req ' + self.pending.request.id() + ' for timeout after ' + elapsed_ms + 'ms; heartbeat=' + self.heartbeat);

  return destroy_req(self.pending.request);
  //return self.retry();
}

Feed.prototype.retry = function retry() {
  var self = this;

  clearTimeout(self.pending.wait_timer);
  self.pending.wait_timer = null;

  self.log.info('Retrying since=' + self.since + ' after ' + self.retry_delay + 'ms: ' + self.db_safe);
  self.emit('retry');

  setTimeout(function() { self.query() }, self.retry_delay);

  var max_retry_ms = self.max_retry_seconds * 1000;
  self.retry_delay *= 2;
  if(self.retry_delay > max_retry_ms)
    self.retry_delay = max_retry_ms;
}

Feed.prototype.on_couch_end = function on_couch_end() {
  var self = this;

  self.log.debug('Changes feed ended ' + self.pending.request.id());
  self.pending.request = null;
  return self.retry();
}

Feed.prototype.on_couch_error = function on_couch_error(er) {
  var self = this;

  self.log.debug('Changes query eror: ' + lib.JS(er.stack));
  return self.retry();
}

Feed.prototype.die = function(er) {
  var self = this;

  self.log.fatal('Fatal error: ' + er.stack);

  self.emit('error', er);

  if(self.pending && self.pending.request) {
    self.log.debug('Destroying req ' + self.pending.request.id());
    destroy_request(self.pending.request);
  }

  //throw er;
}

Feed.prototype.on_change = function on_change(change) {
  var self = this;

  if(!change.seq)
    return self.die(new Error('No seq value in change: ' + lib.JS(change)));

  if(change.seq <= self.since)
    return self.die(new Error('Bad seq value ' + change.seq + ' but since=' + self.since + ' : ' + lib.JS(change)));

  if(typeof self.filter !== 'function')
    return self.on_good_change(change);

  var req = { 'query': lib.JDUP(self.pending.request.changes_query) };
  var f_change = lib.JDUP(change); // Don't let the filter mutate the real data.
  var result = self.filter.apply(null, [f_change, req]);
  result = (result && true) || false;
  if(result) {
    self.log.debug('Builtin filter PASS for change: ' + change.seq);
    return self.on_good_change(change);
  } else
    self.log.debug('Builtin filter FAIL for change: ' + change.seq);
}

Feed.prototype.on_good_change = function on_good_change(change) {
  var self = this;

  self.since = change.seq;
  self.emit('change', change);
}

module.exports = { "Feed" : Feed
                 };

/*
 * Utilities
 */

function destroy_req(req) {
  if(req)
    return destroy_response(req.response);
}

function destroy_response(response) {
  if(!response)
    return;

  response.connection.end();
  response.connection.destroy();
}
<commit_msg>Retry on an old seq value<commit_after>// Core routines for event emitters
//

var lib = require('./lib')
  , url = require('url')
  , util = require('util')
  , request = require('request')
  , querystring = require('querystring')
  ;

var DEFAULT_HEARTBEAT = 30000;
var HEARTBEAT_TIMEOUT_COEFFICIENT = 1.25; // E.g. heartbeat 1000ms would trigger a timeout after 1250ms of no heartbeat.
var DEFAULT_MAX_RETRY_SECONDS     = 60 * 60;
var INITIAL_RETRY_DELAY           = 1000;

var FEED_PARAMETERS   = ['since', 'limit', 'feed', 'heartbeat', 'filter', 'include_docs'];

var SUPER_CLASS = require('events').EventEmitter;
//var SUPER_CLASS = require('stream').Stream;

function Feed (db) {
  var self = this;
  SUPER_CLASS.call(self);

  self.db = db;

  self.feed = 'continuous';
  self.heartbeat         = DEFAULT_HEARTBEAT;
  self.max_retry_seconds = DEFAULT_MAX_RETRY_SECONDS;

  self.headers = {};
  self.request = {}; // Extra options for potentially future versions of request. The caller can supply them.

  self.since = 0;

  self.retry_delay = INITIAL_RETRY_DELAY; // ms

  self.pending = { request     : null
                 , activity_at : null
                 , data        : null
                 };

  // XXX
  var events = ['drain', 'error', 'close', 'pipe', 'data', 'end', 'fd'];

  events.forEach(function(ev) {
    //self.on(ev, function() { return self['on_' + ev].apply(self, arguments) });
    self.on(ev, function() {
      var args = Array.prototype.slice.call(arguments);
      self.log.debug('EVENT ' + ev + ' ' + lib.JS(args));
    })
  })
} // Feed
util.inherits(Feed, SUPER_CLASS);

Feed.prototype.start =
Feed.prototype.follow = function follow_feed(db) {
  var self = this;

  if(db)
    self.db = db;

  if(!self.db)
    throw new Error('Database URL required');

  if(self.feed !== 'continuous')
    throw new Error('The only valid feed option is "continuous"');

  if(typeof self.heartbeat !== 'number')
    throw new Error('Required "heartbeat" value');

  var parsed = url.parse(self.db);
  self.log = lib.log4js().getLogger(parsed.hostname + parsed.pathname);
  self.log.setLevel(process.env.changes_level || "info");

  return self.confirm();
}

Feed.prototype.confirm = function confirm_feed() {
  var self = this;

  self.db_safe = lib.scrub_creds(self.db);

  self.log.debug('Checking database: ' + self.db_safe);
  self.emit('confirm');

  var confirm_timeout = self.heartbeat * 3; // Give it time to look up the name, connect, etc.
  var timeout_id = setTimeout(function() {
    return self.die(new Error('Timeout confirming database: ' + self.db_safe));
  }, confirm_timeout);

  var headers = lib.JP(lib.JS(self.headers));
  headers.accept = 'application/json';

  request({uri:self.db, headers:headers}, function(er, resp, body) {
    clearTimeout(timeout_id);

    if(er)
      return self.die(er);

    var db;
    try {
      db = JSON.parse(body)
    } catch(json_er) {
      return self.emit('error', json_er)
    }

    if(!db.db_name || !db.instance_start_time)
      return self.emit('error', new Error('Bad DB response: ' + body));

    self.log.debug('Confirmed db: ' + self.db_safe);
    return self.query();
  })
}

Feed.prototype.query = function query_feed() {
  var self = this;

  var query_params = {};
  FEED_PARAMETERS.forEach(function(key) {
    if(key in self)
      query_params[key] = self[key];
  })

  if(typeof query_params.filter !== 'string')
    delete query_params.filter;

  var feed_url = self.db + '/_changes?' + querystring.stringify(query_params);

  self.headers.accept = self.headers.accept || 'application/json';
  var req = { method : 'GET'
            , uri    : feed_url
            , headers: self.headers
            , encoding: 'utf-8'
            }

  req.changes_query = query_params;
  Object.keys(self.request).forEach(function(key) {
    req[key] = self.request[key];
  })

  var now = new Date;
  self.log.debug('Query at ' + lib.JP(lib.JS(now)) + ': ' + lib.scrub_creds(feed_url));

  var timeout_id, timed_out = false;
  var in_flight, timeout_id, timed_out = false;

  var timed_out = false;
  function on_timeout() {
    self.log.debug('Request timeout: ' + in_flight.id());
    timed_out = true;
    return self.retry();
  }

  function on_response(er, resp) {
    clearTimeout(timeout_id);

    if(timed_out) {
      self.log.debug('Ignoring late response: ' + in_flight.id());
      return destroy_response(resp);
    }

    if(er) {
      self.log.debug('Request error ' + in_flight.id() + ': ' + er.stack);
      destroy_response(resp);
      return self.retry();
    }

    if(resp.statusCode !== 200) {
      self.log.debug('Bad changes response' + in_flight.id() + ': ' + resp.statusCode);
      destroy_response(resp);
      return self.retry();
    }

    self.log.debug('Good response: ' + in_flight.id());
    self.retry_delay = INITIAL_RETRY_DELAY;
    return self.prep(in_flight);
  }

  req.onResponse = on_response;
  timeout_id = setTimeout(on_timeout, self.heartbeat);
  in_flight = request(req);
  in_flight.created_at = now;
  in_flight.id = function() { return lib.JP(lib.JS(this.created_at)) };

  // Shorten the timestamp, used for debugging.
  //in_flight.id = function() { return /\.(\d\d\d)Z$/.exec(lib.JP(lib.JS(this.created_at)))[1] };

  return self.emit('query');
}

Feed.prototype.prep = function prep_request(req) {
  var self = this;

  var now = new Date;
  self.pending.request = req;
  self.pending.activity_at = now;
  self.pending.data        = "";
  self.pending.wait_timer  = null;

  function handler_for(ev) {
    var name = 'on_couch_' + ev;
    var inner_handler = self[name];

    function handle_confirmed_req_event() {
      if(self.pending.request === req)
        return inner_handler.apply(self, arguments);

      if(!req.created_at)
        return self.die(new Error("Received data from unknown request")); // Pretty sure this is impossible.

      var s_to_now = (new Date() - req.created_at) / 1000;
      var s_to_req = '[no req]';
      if(self.pending.request)
        s_to_req = (self.pending.request.created_at - req.created_at) / 1000;

      var msg = req.id() + ': to_req=' + s_to_req + 's, to_now=' + s_to_now + 's';

      if(ev === 'end') {
        return self.log.debug('Old END ' + msg);
        return destroy_req(req);
      }

      if(ev === 'data') {
        self.log.debug('Old DATA ' + msg);
        return destroy_req(req);
      }

      self.log.warn('Received "' + ev + '" from old request started ' + s_to_req + 's before current and ' + s_to_now + 's before now');
    }

    return handle_confirmed_req_event;
  }

  var handlers = ['data', 'end', 'error'];
  handlers.forEach(function(ev) {
    req.on(ev, handler_for(ev));
  })

  return self.wait();
}

Feed.prototype.wait = function wait_for_event() {
  var self = this;
  self.emit('wait');

  if(self.pending.wait_timer)
    return self.die(new Error('wait() called but there is already a wait_timer: ' + self.pending.wait_timer));

  var timeout_ms = self.heartbeat * HEARTBEAT_TIMEOUT_COEFFICIENT;

  self.log.debug('Req ' + self.pending.request.id() + ' waiting ' + timeout_ms + 'ms for data: ' + self.db_safe);
  self.pending.wait_timer = setTimeout(function() { self.on_timeout() }, timeout_ms);
}

Feed.prototype.on_couch_data = function on_couch_data(data, req) {
  var self = this;

  if(! self.pending.wait_timer)
    return self.die(new Error('Cannot find timeout timer during incoming data'));

  clearTimeout(self.pending.wait_timer);
  self.pending.wait_timer = null;
  self.pending.activity_at = new Date;

  self.log.debug('Data from ' + self.pending.request.id());

  // Buf could have 0, 1, or many JSON objects in it.
  var buf = self.pending.data + data;
  var offset, json, change;

  while((offset = buf.indexOf("\n")) >= 0) {
    json = buf.substr(0, offset);
    buf = buf.substr(offset + 1);

    if(json == '') {
      self.log.debug('Heartbeat: ' + self.pending.request.id());
    } else {
      //self.log.debug('JSON: ' + json);
      try {
        change = JSON.parse(json);
      } catch(er) {
        return self.die(er);
      }

      //self.log.debug('Object:\n' + util.inspect(change));

      seq = change.seq;
      if(!seq)
        return self.die(new Error('Change has no .seq field: ' + json));

      self.on_change(change);
    }
  }

  self.pending.data = buf;
  self.wait();
}

Feed.prototype.on_timeout = function on_timeout() {
  var self = this;

  var now = new Date;
  var elapsed_ms = now - self.pending.activity_at;
  self.log.warn('Closing req ' + self.pending.request.id() + ' for timeout after ' + elapsed_ms + 'ms; heartbeat=' + self.heartbeat);

  return destroy_req(self.pending.request);
  //return self.retry();
}

Feed.prototype.retry = function retry() {
  var self = this;

  clearTimeout(self.pending.wait_timer);
  self.pending.wait_timer = null;

  self.log.info('Retrying since=' + self.since + ' after ' + self.retry_delay + 'ms: ' + self.db_safe);
  self.emit('retry');

  setTimeout(function() { self.query() }, self.retry_delay);

  var max_retry_ms = self.max_retry_seconds * 1000;
  self.retry_delay *= 2;
  if(self.retry_delay > max_retry_ms)
    self.retry_delay = max_retry_ms;
}

Feed.prototype.on_couch_end = function on_couch_end() {
  var self = this;

  self.log.debug('Changes feed ended ' + self.pending.request.id());
  self.pending.request = null;
  return self.retry();
}

Feed.prototype.on_couch_error = function on_couch_error(er) {
  var self = this;

  self.log.debug('Changes query eror: ' + lib.JS(er.stack));
  return self.retry();
}

Feed.prototype.die = function(er) {
  var self = this;

  self.log.fatal('Fatal error: ' + er.stack);

  self.emit('error', er);

  if(self.pending && self.pending.request) {
    self.log.debug('Destroying req ' + self.pending.request.id());
    destroy_request(self.pending.request);
  }

  //throw er;
}

Feed.prototype.on_change = function on_change(change) {
  var self = this;

  if(!change.seq)
    return self.die(new Error('No seq value in change: ' + lib.JS(change)));

  if(change.seq <= self.since) {
    self.log.debug('Bad seq value ' + change.seq + ' since=' + self.since);
    return destroy_req(self.pending.request);
  }

  if(typeof self.filter !== 'function')
    return self.on_good_change(change);

  var req = { 'query': lib.JDUP(self.pending.request.changes_query) };
  var f_change = lib.JDUP(change); // Don't let the filter mutate the real data.
  var result = self.filter.apply(null, [f_change, req]);
  result = (result && true) || false;
  if(result) {
    self.log.debug('Builtin filter PASS for change: ' + change.seq);
    return self.on_good_change(change);
  } else
    self.log.debug('Builtin filter FAIL for change: ' + change.seq);
}

Feed.prototype.on_good_change = function on_good_change(change) {
  var self = this;

  self.since = change.seq;
  self.emit('change', change);
}

module.exports = { "Feed" : Feed
                 };

/*
 * Utilities
 */

function destroy_req(req) {
  if(req)
    return destroy_response(req.response);
}

function destroy_response(response) {
  if(!response)
    return;

  response.connection.end();
  response.connection.destroy();
}
<|endoftext|>


<commit_before>var shortener = require('./shortener');
var redis = require('redis').createClient();
var Url;

exports.setup = function(app, db) {
    Url = db.model('Url');
};

exports.index = function (req, res) {
    var context = {
        'title': "Homepage",
    };

    res.render('index', context);
};

exports.submit = function (req, res) {
    var sourceUrl = req.body.url;
    var message = "success";

    console.log('processing ' + sourceUrl);
    Url.findOne({sourceUrl: sourceUrl}, function(err, url) {
        console.log('submit Url.findOne url = ' + url + ' err = ' + JSON.stringify(err));
        if (err) {
            console.log('err getting url');
            res.status(500);
            return res.end();
        }
        if (url) {
            return res.redirect(url.destinationUrl);
        } else {
            console.log('about to shorten.shorten on sourceUrl = ' + sourceUrl);
            shortener.shorten(sourceUrl, function(destinationUrl, err) {
                if (!destinationUrl) {
                    console.log('destinationUrl is null!');
                    res.status(500);
                    return res.end();
                } else {
                    var newUrl = new Url({
                        sourceUrl: sourceUrl,
                        destinationUrl: destinationUrl
                    });
                    newUrl.save(function(err) {
                        if (err) {
                            console.log('error saving to mongodb' + JSON.stringify(err));
                            res.status(500);
                            return res.end();
                        } else {
                            return res.json(200, {
                                message: message,
                                url: destinationUrl
                            });
                        }
                    });
                }
            });
        }
    });
};

exports.redirect = function (req, res) {
    var destinationUrl = req.params.destinationUrl;
    console.log('destinationUrl = ' + destinationUrl);
    Url.findOne({destinationUrl: destinationUrl}, function(err, url) {
        if (err) {
        } else {
            if (url) {
                return res.json(200, {
                    message: 'success',
                    url: destinationUrl
                });
            }
        }
    });
};
<commit_msg>fix redirect not working. need to test more<commit_after>var shortener = require('./shortener');
var redis = require('redis').createClient();
var Url;

exports.setup = function(app, db) {
    Url = db.model('Url');
};

exports.index = function (req, res) {
    var context = {
        'title': "Homepage",
    };

    res.render('index', context);
};

exports.submit = function (req, res) {
    var sourceUrl = req.body.url;
    var message = "success";

    console.log('processing ' + sourceUrl);
    Url.findOne({sourceUrl: sourceUrl}, function(err, url) {
        if (err) {
            console.log('err getting url');
            res.status(500);
            return res.end();
        }
        if (url) {
            return res.json(200, {
                url: url.destinationUrl,
                message: 'success'
            });
        } else {
            shortener.shorten(sourceUrl, function(destinationUrl, err) {
                if (!destinationUrl) {
                    console.log('destinationUrl is null!');
                    res.status(500);
                    return res.end();
                } else {
                    var newUrl = new Url({
                        sourceUrl: sourceUrl,
                        destinationUrl: destinationUrl
                    });
                    newUrl.save(function(err) {
                        if (err) {
                            console.log('error saving to mongodb' + JSON.stringify(err));
                            res.status(500);
                            return res.end();
                        } else {
                            return res.json(200, {
                                message: message,
                                url: destinationUrl
                            });
                        }
                    });
                }
            });
        }
    });
};

exports.redirect = function (req, res) {
    var destinationUrl = req.params.destinationUrl;
    console.log('destinationUrl = ' + destinationUrl);
    Url.findOne({destinationUrl: destinationUrl}, function(err, url) {
        console.log('redirect findOne err = ' + err + ' url = ' + JSON.stringify(url));
        if (url) {
            return res.redirect(url.sourceUrl);
        } else {
            return res.json(500, {});
        }
    });
};
<|endoftext|>


<commit_before>var tape = require("tape"),
    jsdom = require("jsdom"),
    selection = require("../");

tape("selection.namespace returns the expected values for built-in namespaces", function(test) {
  test.deepEqual(selection.namespace("svg:g"), {space: "http://www.w3.org/2000/svg", local: "g"});
  test.deepEqual(selection.namespace("xhtml:b"), {space: "http://www.w3.org/1999/xhtml", local: "b"});
  test.deepEqual(selection.namespace("xlink:href"), {space: "http://www.w3.org/1999/xlink", local: "href"});
  test.deepEqual(selection.namespace("xml:lang"), {space: "http://www.w3.org/XML/1998/namespace", local: "lang"});
  test.end();
});

tape("selection.namespace(\"xmlns:…\") treats the whole name as the local name", function(test) {
  test.deepEqual(selection.namespace("xmlns:xlink"), {space: "http://www.w3.org/2000/xmlns/", local: "xmlns:xlink"});
  test.end();
});
<commit_msg>Better namespace tests.<commit_after>var tape = require("tape"),
    jsdom = require("jsdom"),
    d3 = require("../");

tape("d3.namespace(name) returns name if there is no namespace prefix", function(test) {
  test.equal(d3.namespace("foo"), "foo");
  test.end();
});

tape("d3.namespace(name) coerces name to a string", function(test) {
  test.equal(d3.namespace({toString: function() { return "foo"; }}), "foo");
  test.deepEqual(d3.namespace({toString: function() { return "svg"; }}), {space: "http://www.w3.org/2000/svg", local: "svg"});
  test.end();
});

tape("d3.namespace(name) returns the expected values for built-in namespaces", function(test) {
  test.deepEqual(d3.namespace("svg"), {space: "http://www.w3.org/2000/svg", local: "svg"});
  test.deepEqual(d3.namespace("xhtml"), {space: "http://www.w3.org/1999/xhtml", local: "xhtml"});
  test.deepEqual(d3.namespace("xlink"), {space: "http://www.w3.org/1999/xlink", local: "xlink"});
  test.deepEqual(d3.namespace("xml"), {space: "http://www.w3.org/XML/1998/namespace", local: "xml"});
  test.deepEqual(d3.namespace("svg:g"), {space: "http://www.w3.org/2000/svg", local: "g"});
  test.deepEqual(d3.namespace("xhtml:b"), {space: "http://www.w3.org/1999/xhtml", local: "b"});
  test.deepEqual(d3.namespace("xlink:href"), {space: "http://www.w3.org/1999/xlink", local: "href"});
  test.deepEqual(d3.namespace("xml:lang"), {space: "http://www.w3.org/XML/1998/namespace", local: "lang"});
  test.end();
});

tape("d3.namespace(\"xmlns:…\") treats the whole name as the local name", function(test) {
  test.deepEqual(d3.namespace("xmlns:xlink"), {space: "http://www.w3.org/2000/xmlns/", local: "xmlns:xlink"});
  test.end();
});
<|endoftext|>


<commit_before>const Bacon = require("baconjs")

ffux.run = run
export default ffux

function ffux({initialState, actions}) {
  return function (initState) {
    const initial = arguments.length > 0 ? initState
      : typeof initialState !== "undefined" ? initialState
      : undefined

    const triggers = Object.keys(actions).map(name => {
      const bus = new Bacon.Bus()
      const fn  = function() { bus.push.call(bus, {args: argArray(arguments)}) }
      const cb  = actions[name]
      return {name, fn, bus, cb}
    })

    const patterns = triggers.reduce((memo, {bus, cb}) => [...memo, ...[[bus], step(cb)]], [])

    return {
      stateP: Bacon.update.apply(Bacon, [initial, ...patterns]),
      actions: zipObject(triggers.map(({name, fn}) => [name, fn]))
    }
  }
}

function run(stores, onValueCallback) {
  const stateTemplate = zipObject(Object.keys(stores).map(name => {
    const {stateP} = stores[name]
    return [name, stateP]
  }))

  const actions = zipObject(Object.keys(stores).map(name => {
    const {actions} = stores[name]
    return [name, actions]
  }))

  const appStateP = Bacon.combineTemplate(stateTemplate)
  appStateP.onValue((state) => {
    onValueCallback({state, actions})
  })
}

function step(actionCallback) {
  return (state, {args}) => {
    return actionCallback.apply(null, [state, ...args])
  }
}

function argArray(args) {
  return Array.prototype.slice.call(args)
}

function zipObject(pairs) {
  const obj = {}
  pairs.forEach(([k, v]) => obj[k] = v)
  return obj
}
<commit_msg>Remove Bacon and add store dependencies<commit_after>const Bacon = require("baconjs")

export default ffux

ffux.run    = run
ffux.pure   = pureAction
ffux.impure = impureAction

function ffux(actions) {
  return function (initialState, deps) {
    const state = {value: initialState}
    const stateChangeHandlers = []

    const storeActions = Object.keys(actions).map(name => {
      const handler  = actions[name]
      return {name, handler}
    })

    let actionInterfaces
    actionInterfaces = zipObject(storeActions.map(({name, handler}) => {
      function actionInterface() {
        const args = argArray(arguments)
        if (isPure(handler)) {
          state.value = handler.apply(null, [{state: state.value, deps}, ...args])
          notifyChanged(state.value)
        } else {
          handler.apply(null, [{state: state.value, deps, self: actionInterfaces}, ...args])
        }
      }
      return [name, actionInterface]
    }))

    return {
      initialState,
      stateChangeHandlers,
      actionInterfaces,
      state: () => state.value
    }

    function isPure(fn) {
      return fn && fn.__ffux_pure === true
    }

    function notifyChanged(newState) {
      stateChangeHandlers.forEach(h => h(newState))
    }
  }
}

function run(stores, onValueCallback) {
  const state = zipObject(Object.keys(stores).map(name => {
    const {initialState} = stores[name]
    return [name, initialState]
  }))

  const actions = zipObject(Object.keys(stores).map(name => {
    const {actionInterfaces} = stores[name]
    return [name, actionInterfaces]
  }))

  Object.keys(stores).forEach(name => {
    const {stateChangeHandlers} = stores[name]
    stateChangeHandlers.push((storeState) => {
      state[name] = storeState
      onValueCallback({state, actions})
    })
  })
  onValueCallback({state, actions})
}

function pureAction(fn) {
  fn.__ffux_pure = true
  return fn
}

function impureAction(fn) {
  fn.__ffux_pure = false
  return fn
}

function argArray(args) {
  return Array.prototype.slice.call(args)
}

function zipObject(pairs) {
  const obj = {}
  pairs.forEach(([k, v]) => obj[k] = v)
  return obj
}
<|endoftext|>