<commit_before>module.exports = {
  rules: {
    // enforce spacing inside array brackets
    'array-bracket-spacing': ['error', 'never'],

    // enforce spacing inside single-line blocks
    // http://eslint.org/docs/rules/block-spacing
    'block-spacing': ['error', 'always'],

    // enforce one true brace style
    'brace-style': ['error', '1tbs', { allowSingleLine: true }],

    // require camel case names
    camelcase: ['error', { properties: 'never' }],

    // enforce spacing before and after comma
    'comma-spacing': ['error', { before: false, after: true }],

    // enforce one true comma style
    'comma-style': ['error', 'last'],

    // disallow padding inside computed properties
    'computed-property-spacing': ['error', 'never'],

    // enforces consistent naming when capturing the current execution context
    'consistent-this': 'off',

    // enforce newline at the end of file, with no multiple empty lines
    'eol-last': 'error',

    // enforce spacing between functions and their invocations
    // http://eslint.org/docs/rules/func-call-spacing
    'func-call-spacing': ['error', 'never'],

    // require function expressions to have a name
    'func-names': 'warn',

    // enforces use of function declarations or expressions
    'func-style': 'off',

    // Blacklist certain identifiers to prevent them being used
    // http://eslint.org/docs/rules/id-blacklist
    'id-blacklist': 'off',

    // this option enforces minimum and maximum identifier lengths
    // (variable names, property names etc.)
    'id-length': 'off',

    // require identifiers to match the provided regular expression
    'id-match': 'off',

    // this option sets a specific tab width for your code
    // http://eslint.org/docs/rules/indent
    indent: ['error', 2, { SwitchCase: 1, VariableDeclarator: 1, outerIIFEBody: 1 }],

    // specify whether double or single quotes should be used in JSX attributes
    // http://eslint.org/docs/rules/jsx-quotes
    'jsx-quotes': ['off', 'prefer-double'],

    // enforces spacing between keys and values in object literal properties
    'key-spacing': ['error', { beforeColon: false, afterColon: true }],

    // require a space before & after certain keywords
    'keyword-spacing': ['error', {
      before: true,
      after: true,
      overrides: {
        return: { after: true },
        throw: { after: true },
        case: { after: true }
      }
    }],

    // disallow mixed 'LF' and 'CRLF' as linebreaks
    // http://eslint.org/docs/rules/linebreak-style
    'linebreak-style': ['error', 'unix'],

    // enforces empty lines around comments
    'lines-around-comment': 'off',

    // specify the maximum depth that blocks can be nested
    'max-depth': ['off', 4],

    // specify the maximum length of a line in your program
    // http://eslint.org/docs/rules/max-len
    'max-len': ['error', 100, 2, {
      ignoreUrls: true,
      ignoreComments: false
    }],

    // specify the max number of lines in a file
    // http://eslint.org/docs/rules/max-lines
    'max-lines': ['off', {
      max: 300,
      skipBlankLines: true,
      skipComments: true
    }],

    // specify the maximum depth callbacks can be nested
    'max-nested-callbacks': 'off',

    // limits the number of parameters that can be used in the function declaration.
    'max-params': ['off', 3],

    // specify the maximum number of statement allowed in a function
    'max-statements': ['off', 10],

    // restrict the number of statements per line
    // http://eslint.org/docs/rules/max-statements-per-line
    'max-statements-per-line': ['off', { max: 1 }],

    // require multiline ternary
    // http://eslint.org/docs/rules/multiline-ternary
    'multiline-ternary': 'off',

    // require a capital letter for constructors
    'new-cap': ['error', { newIsCap: true }],

    // disallow the omission of parentheses when invoking a constructor with no arguments
    // http://eslint.org/docs/rules/new-parens
    'new-parens': 'error',

    // allow/disallow an empty newline after var statement
    'newline-after-var': 'off',

    // http://eslint.org/docs/rules/newline-before-return
    'newline-before-return': 'off',

    // enforces new line after each method call in the chain to make it
    // more readable and easy to maintain
    // http://eslint.org/docs/rules/newline-per-chained-call
    'newline-per-chained-call': ['error', { ignoreChainWithDepth: 4 }],

    // disallow use of the Array constructor
    'no-array-constructor': 'error',

    // disallow use of bitwise operators
    // http://eslint.org/docs/rules/no-bitwise
    // TODO: enable
    'no-bitwise': 'off',

    // disallow use of the continue statement
    // http://eslint.org/docs/rules/no-continue
    'no-continue': 'error',

    // disallow comments inline after code
    'no-inline-comments': 'off',

    // disallow if as the only statement in an else block
    // http://eslint.org/docs/rules/no-lonely-if
    'no-lonely-if': 'error',

    // disallow un-paren'd mixes of different operators
    // http://eslint.org/docs/rules/no-mixed-operators
    'no-mixed-operators': ['error', {
      groups: [
        ['+', '-', '*', '/', '%', '**'],
        ['&', '|', '^', '~', '<<', '>>', '>>>'],
        ['==', '!=', '===', '!==', '>', '>=', '<', '<='],
        ['&&', '||'],
        ['in', 'instanceof']
      ],
      allowSamePrecedence: false
    }],

    // disallow mixed spaces and tabs for indentation
    'no-mixed-spaces-and-tabs': 'error',

    // disallow multiple empty lines and only one newline at the end
    'no-multiple-empty-lines': ['error', { max: 2, maxEOF: 1 }],

    // disallow negated conditions
    // http://eslint.org/docs/rules/no-negated-condition
    'no-negated-condition': 'off',

    // disallow nested ternary expressions
    'no-nested-ternary': 'error',

    // disallow use of the Object constructor
    'no-new-object': 'error',

    // disallow use of unary operators, ++ and --
    'no-plusplus': 'off',

    // disallow certain syntax forms
    // http://eslint.org/docs/rules/no-restricted-syntax
    'no-restricted-syntax': [
      'error',
      'ForInStatement',
      'LabeledStatement',
      'WithStatement',
    ],

    // disallow space between function identifier and application
    'no-spaced-func': 'error',

    // disallow tab characters entirely
    'no-tabs': 'error',

    // disallow the use of ternary operators
    'no-ternary': 'off',

    // disallow trailing whitespace at the end of lines
    'no-trailing-spaces': 'error',

    // disallow dangling underscores in identifiers
    'no-underscore-dangle': ['error', { allowAfterThis: false }],

    // disallow the use of Boolean literals in conditional expressions
    // also, prefer `a || b` over `a ? a : b`
    // http://eslint.org/docs/rules/no-unneeded-ternary
    'no-unneeded-ternary': ['error', { defaultAssignment: false }],

    // disallow whitespace before properties
    // http://eslint.org/docs/rules/no-whitespace-before-property
    'no-whitespace-before-property': 'error',

    // require padding inside curly braces
    'object-curly-spacing': ['error', 'always'],

    // enforce line breaks between braces
    // http://eslint.org/docs/rules/object-curly-newline
    // TODO: enable once https://github.com/eslint/eslint/issues/6488 is resolved
    'object-curly-newline': ['off', {
      ObjectExpression: { minProperties: 0, multiline: true },
      ObjectPattern: { minProperties: 0, multiline: true }
    }],

    // enforce "same line" or "multiple line" on object properties.
    // http://eslint.org/docs/rules/object-property-newline
    'object-property-newline': ['error', {
      allowMultiplePropertiesPerLine: true,
    }],

    // allow just one var statement per function
    'one-var': ['error', 'never'],

    // require a newline around variable declaration
    // http://eslint.org/docs/rules/one-var-declaration-per-line
    'one-var-declaration-per-line': ['error', 'always'],

    // require assignment operator shorthand where possible or prohibit it entirely
    // http://eslint.org/docs/rules/operator-assignment
    'operator-assignment': ['error', 'always'],

    // enforce operators to be placed before or after line breaks
    'operator-linebreak': 'off',

    // enforce padding within blocks
    'padded-blocks': ['error', 'never'],

    // require quotes around object literal property names
    // http://eslint.org/docs/rules/quote-props.html
    'quote-props': ['error', 'as-needed', { keywords: false, unnecessary: true, numbers: false }],

    // specify whether double or single quotes should be used
    quotes: ['error', 'single', { avoidEscape: true }],

    // do not require jsdoc
    // http://eslint.org/docs/rules/require-jsdoc
    'require-jsdoc': 'off',

    // require or disallow use of semicolons instead of ASI
    semi: ['error', 'always'],

    // enforce spacing before and after semicolons
    'semi-spacing': ['error', { before: false, after: true }],

    // requires object keys to be sorted
    'sort-keys': ['off', 'asc', { caseSensitive: false, natural: true }],

    // sort variables within the same declaration block
    'sort-vars': 'off',

    // require or disallow space before blocks
    'space-before-blocks': 'error',

    // require or disallow space before function opening parenthesis
    // http://eslint.org/docs/rules/space-before-function-paren
    'space-before-function-paren': ['error', { anonymous: 'always', named: 'never' }],

    // require or disallow spaces inside parentheses
    'space-in-parens': ['error', 'never'],

    // require spaces around operators
    'space-infix-ops': 'error',

    // Require or disallow spaces before/after unary operators
    // http://eslint.org/docs/rules/space-unary-ops
    'space-unary-ops': ['error', {
      words: true,
      nonwords: false,
      overrides: {
      },
    }],

    // require or disallow a space immediately following the // or /* in a comment
    'spaced-comment': ['error', 'always', {
      exceptions: ['-', '+'],
      markers: ['=', '!']           // space here to support sprockets directives
    }],

    // require or disallow the Unicode Byte Order Mark
    // http://eslint.org/docs/rules/unicode-bom
    'unicode-bom': ['error', 'never'],

    // require regex literals to be wrapped in parentheses
    'wrap-regex': 'off'
  }
};
<commit_msg>[eslint config] [base] [breaking] enable `no-bitwise`<commit_after>module.exports = {
  rules: {
    // enforce spacing inside array brackets
    'array-bracket-spacing': ['error', 'never'],

    // enforce spacing inside single-line blocks
    // http://eslint.org/docs/rules/block-spacing
    'block-spacing': ['error', 'always'],

    // enforce one true brace style
    'brace-style': ['error', '1tbs', { allowSingleLine: true }],

    // require camel case names
    camelcase: ['error', { properties: 'never' }],

    // enforce spacing before and after comma
    'comma-spacing': ['error', { before: false, after: true }],

    // enforce one true comma style
    'comma-style': ['error', 'last'],

    // disallow padding inside computed properties
    'computed-property-spacing': ['error', 'never'],

    // enforces consistent naming when capturing the current execution context
    'consistent-this': 'off',

    // enforce newline at the end of file, with no multiple empty lines
    'eol-last': 'error',

    // enforce spacing between functions and their invocations
    // http://eslint.org/docs/rules/func-call-spacing
    'func-call-spacing': ['error', 'never'],

    // require function expressions to have a name
    'func-names': 'warn',

    // enforces use of function declarations or expressions
    'func-style': 'off',

    // Blacklist certain identifiers to prevent them being used
    // http://eslint.org/docs/rules/id-blacklist
    'id-blacklist': 'off',

    // this option enforces minimum and maximum identifier lengths
    // (variable names, property names etc.)
    'id-length': 'off',

    // require identifiers to match the provided regular expression
    'id-match': 'off',

    // this option sets a specific tab width for your code
    // http://eslint.org/docs/rules/indent
    indent: ['error', 2, { SwitchCase: 1, VariableDeclarator: 1, outerIIFEBody: 1 }],

    // specify whether double or single quotes should be used in JSX attributes
    // http://eslint.org/docs/rules/jsx-quotes
    'jsx-quotes': ['off', 'prefer-double'],

    // enforces spacing between keys and values in object literal properties
    'key-spacing': ['error', { beforeColon: false, afterColon: true }],

    // require a space before & after certain keywords
    'keyword-spacing': ['error', {
      before: true,
      after: true,
      overrides: {
        return: { after: true },
        throw: { after: true },
        case: { after: true }
      }
    }],

    // disallow mixed 'LF' and 'CRLF' as linebreaks
    // http://eslint.org/docs/rules/linebreak-style
    'linebreak-style': ['error', 'unix'],

    // enforces empty lines around comments
    'lines-around-comment': 'off',

    // specify the maximum depth that blocks can be nested
    'max-depth': ['off', 4],

    // specify the maximum length of a line in your program
    // http://eslint.org/docs/rules/max-len
    'max-len': ['error', 100, 2, {
      ignoreUrls: true,
      ignoreComments: false
    }],

    // specify the max number of lines in a file
    // http://eslint.org/docs/rules/max-lines
    'max-lines': ['off', {
      max: 300,
      skipBlankLines: true,
      skipComments: true
    }],

    // specify the maximum depth callbacks can be nested
    'max-nested-callbacks': 'off',

    // limits the number of parameters that can be used in the function declaration.
    'max-params': ['off', 3],

    // specify the maximum number of statement allowed in a function
    'max-statements': ['off', 10],

    // restrict the number of statements per line
    // http://eslint.org/docs/rules/max-statements-per-line
    'max-statements-per-line': ['off', { max: 1 }],

    // require multiline ternary
    // http://eslint.org/docs/rules/multiline-ternary
    'multiline-ternary': 'off',

    // require a capital letter for constructors
    'new-cap': ['error', { newIsCap: true }],

    // disallow the omission of parentheses when invoking a constructor with no arguments
    // http://eslint.org/docs/rules/new-parens
    'new-parens': 'error',

    // allow/disallow an empty newline after var statement
    'newline-after-var': 'off',

    // http://eslint.org/docs/rules/newline-before-return
    'newline-before-return': 'off',

    // enforces new line after each method call in the chain to make it
    // more readable and easy to maintain
    // http://eslint.org/docs/rules/newline-per-chained-call
    'newline-per-chained-call': ['error', { ignoreChainWithDepth: 4 }],

    // disallow use of the Array constructor
    'no-array-constructor': 'error',

    // disallow use of bitwise operators
    // http://eslint.org/docs/rules/no-bitwise
    'no-bitwise': 'error',

    // disallow use of the continue statement
    // http://eslint.org/docs/rules/no-continue
    'no-continue': 'error',

    // disallow comments inline after code
    'no-inline-comments': 'off',

    // disallow if as the only statement in an else block
    // http://eslint.org/docs/rules/no-lonely-if
    'no-lonely-if': 'error',

    // disallow un-paren'd mixes of different operators
    // http://eslint.org/docs/rules/no-mixed-operators
    'no-mixed-operators': ['error', {
      groups: [
        ['+', '-', '*', '/', '%', '**'],
        ['&', '|', '^', '~', '<<', '>>', '>>>'],
        ['==', '!=', '===', '!==', '>', '>=', '<', '<='],
        ['&&', '||'],
        ['in', 'instanceof']
      ],
      allowSamePrecedence: false
    }],

    // disallow mixed spaces and tabs for indentation
    'no-mixed-spaces-and-tabs': 'error',

    // disallow multiple empty lines and only one newline at the end
    'no-multiple-empty-lines': ['error', { max: 2, maxEOF: 1 }],

    // disallow negated conditions
    // http://eslint.org/docs/rules/no-negated-condition
    'no-negated-condition': 'off',

    // disallow nested ternary expressions
    'no-nested-ternary': 'error',

    // disallow use of the Object constructor
    'no-new-object': 'error',

    // disallow use of unary operators, ++ and --
    'no-plusplus': 'off',

    // disallow certain syntax forms
    // http://eslint.org/docs/rules/no-restricted-syntax
    'no-restricted-syntax': [
      'error',
      'ForInStatement',
      'LabeledStatement',
      'WithStatement',
    ],

    // disallow space between function identifier and application
    'no-spaced-func': 'error',

    // disallow tab characters entirely
    'no-tabs': 'error',

    // disallow the use of ternary operators
    'no-ternary': 'off',

    // disallow trailing whitespace at the end of lines
    'no-trailing-spaces': 'error',

    // disallow dangling underscores in identifiers
    'no-underscore-dangle': ['error', { allowAfterThis: false }],

    // disallow the use of Boolean literals in conditional expressions
    // also, prefer `a || b` over `a ? a : b`
    // http://eslint.org/docs/rules/no-unneeded-ternary
    'no-unneeded-ternary': ['error', { defaultAssignment: false }],

    // disallow whitespace before properties
    // http://eslint.org/docs/rules/no-whitespace-before-property
    'no-whitespace-before-property': 'error',

    // require padding inside curly braces
    'object-curly-spacing': ['error', 'always'],

    // enforce line breaks between braces
    // http://eslint.org/docs/rules/object-curly-newline
    // TODO: enable once https://github.com/eslint/eslint/issues/6488 is resolved
    'object-curly-newline': ['off', {
      ObjectExpression: { minProperties: 0, multiline: true },
      ObjectPattern: { minProperties: 0, multiline: true }
    }],

    // enforce "same line" or "multiple line" on object properties.
    // http://eslint.org/docs/rules/object-property-newline
    'object-property-newline': ['error', {
      allowMultiplePropertiesPerLine: true,
    }],

    // allow just one var statement per function
    'one-var': ['error', 'never'],

    // require a newline around variable declaration
    // http://eslint.org/docs/rules/one-var-declaration-per-line
    'one-var-declaration-per-line': ['error', 'always'],

    // require assignment operator shorthand where possible or prohibit it entirely
    // http://eslint.org/docs/rules/operator-assignment
    'operator-assignment': ['error', 'always'],

    // enforce operators to be placed before or after line breaks
    'operator-linebreak': 'off',

    // enforce padding within blocks
    'padded-blocks': ['error', 'never'],

    // require quotes around object literal property names
    // http://eslint.org/docs/rules/quote-props.html
    'quote-props': ['error', 'as-needed', { keywords: false, unnecessary: true, numbers: false }],

    // specify whether double or single quotes should be used
    quotes: ['error', 'single', { avoidEscape: true }],

    // do not require jsdoc
    // http://eslint.org/docs/rules/require-jsdoc
    'require-jsdoc': 'off',

    // require or disallow use of semicolons instead of ASI
    semi: ['error', 'always'],

    // enforce spacing before and after semicolons
    'semi-spacing': ['error', { before: false, after: true }],

    // requires object keys to be sorted
    'sort-keys': ['off', 'asc', { caseSensitive: false, natural: true }],

    // sort variables within the same declaration block
    'sort-vars': 'off',

    // require or disallow space before blocks
    'space-before-blocks': 'error',

    // require or disallow space before function opening parenthesis
    // http://eslint.org/docs/rules/space-before-function-paren
    'space-before-function-paren': ['error', { anonymous: 'always', named: 'never' }],

    // require or disallow spaces inside parentheses
    'space-in-parens': ['error', 'never'],

    // require spaces around operators
    'space-infix-ops': 'error',

    // Require or disallow spaces before/after unary operators
    // http://eslint.org/docs/rules/space-unary-ops
    'space-unary-ops': ['error', {
      words: true,
      nonwords: false,
      overrides: {
      },
    }],

    // require or disallow a space immediately following the // or /* in a comment
    'spaced-comment': ['error', 'always', {
      exceptions: ['-', '+'],
      markers: ['=', '!']           // space here to support sprockets directives
    }],

    // require or disallow the Unicode Byte Order Mark
    // http://eslint.org/docs/rules/unicode-bom
    'unicode-bom': ['error', 'never'],

    // require regex literals to be wrapped in parentheses
    'wrap-regex': 'off'
  }
};
<|endoftext|>


<commit_before>"use strict";
/*
Progress JSDO DataSource for Angular: 5.0.0
Copyright 2017-2018 Progress Software Corporation and/or its subsidiaries or affiliates.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
    http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
progress.data.ng.ds.ts    Version: v5.0.0
Progress DataSource class for NativeScript, Angular. This will provide a seamless integration
between OpenEdge (Progress Data Object) with NativeScript.
Author(s): maura, anikumar, egarcia
*/
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
Object.defineProperty(exports, "__esModule", { value: true });
var core_1 = require("@angular/core");
var jsdo_core_1 = require("@progress/jsdo-core");
require("rxjs/add/observable/fromPromise");
require("rxjs/add/operator/catch");
var Observable_1 = require("rxjs/Observable");
var DataSourceOptions = /** @class */ (function () {
    function DataSourceOptions() {
    }
    return DataSourceOptions;
}());
exports.DataSourceOptions = DataSourceOptions;
// tslint:disable max-classes-per-file
var DataSource = /** @class */ (function () {
    function DataSource(options) {
        this.jsdo = undefined;
        this._data = null;
        this.jsdo = options.jsdo;
        this._options = options;
        // Turning off autoApplyChanges. Want to explicitly call jsdo.acceptChanges() and rejectChanges()
        this.jsdo.autoApplyChanges = false;
        if (!options.jsdo || !(options.jsdo instanceof jsdo_core_1.progress.data.JSDO)) {
            throw new Error("DataSource: jsdo property must be set to a JSDO instance.");
        }
        if (this._options.tableRef === undefined && this.jsdo.defaultTableRef) {
            this._options.tableRef = this.jsdo.defaultTableRef._name;
        }
        if (this._options.tableRef === undefined) {
            throw new Error("DataSource: A tableRef must be specified when using a multi-table DataSet.");
        }
        else if (this.jsdo[this._options.tableRef] === undefined) {
            throw new Error("DataSource: tableRef '"
                + this._options.tableRef + "' is not present in underlying JSDO definition.");
        }
        this._tableRef = this._options.tableRef;
    }
    /**
     * Calls the jsdo.fill() retrieving data from the backend service
     * @returns Observable<Array<object>>
     */
    DataSource.prototype.read = function (params) {
        var _this = this;
        var wrapperPromise;
        var obs;
        var filter = {};
        if (params) {
            filter = params;
        }
        else {
            // Initial read() where the params are empty and we are assigning the filter criteria
            filter.filter = this._options.filter;
            filter.sort = this._options.sort;
            filter.top = this._options.top;
            filter.skip = this._options.skip;
        }
        // tableRef required for multi-table DataSets
        filter.tableRef = this._tableRef;
        wrapperPromise = new Promise(function (resolve, reject) {
            _this.jsdo.fill(filter)
                .then(function (result) {
                var data = result.jsdo[_this._tableRef].getData();
                // Make copy of jsdo data for datasource
                _this._data = (data.length > 0 ? data.map(function (item) { return Object.assign({}, item); }) : []);
                resolve(_this._data);
            }).catch(function (result) {
                reject(new Error(_this.normalizeError(result, "read", "")));
            });
        });
        obs = Observable_1.Observable.fromPromise(wrapperPromise);
        obs.catch(function (e) {
            return [];
        });
        // return Observable.fromPromise(wrapperPromise);
        return obs;
    };
    /**
     * Returns array of record objects from JSDO local memory
     * @returns {object}
     */
    DataSource.prototype.getData = function () {
        return this._data;
    };
    /**
     * Calls the jsdo.add() method, creating a new record in JSDO memory
     * jsdo.add() will either return the new record, or throws an exception
     * @param data - Record to create is passed as an object
     * @returns - If successful, an object of the new record is returned
     */
    DataSource.prototype.create = function (data) {
        var jsRecord;
        var newRow = {};
        jsRecord = this.jsdo[this._tableRef].add(data);
        this._copyRecord(jsRecord.data, newRow);
        return newRow;
    };
    /**
     * Returns a copy of the record with the specified id.
     * Note: current implementation uses jsdo's internal _id as id.
     * @param id - id of record
     * @returns - copy of record with specified id, else null if no record found
     */
    DataSource.prototype.findById = function (id) {
        // For now, we are using _id as our id to find records..
        jsRecord = this.jsdo[this._options.tableRef].findById(id, false);
    };
    /**
     * Calls the jsdo.update() method, for updating a record in JSDO memory
     * jsdo.update() will either return the updated record, or throws an exception
     * @param data - Record to create is passed as an object
     * @returns - boolean. True if successful, false if there is any failure
     */
    DataSource.prototype.update = function (data) {
        if (!data && (data === undefined || null)) {
            throw new Error("Unexpected signature for update() operation.");
        }
        var id = (data && data._id) ? data._id : null;
        var jsRecord;
        var retVal = false;
        if (!id) {
            throw new Error("DataSource.update(): data missing _id property");
        }
        jsRecord = this.jsdo[this._tableRef].findById(id);
        if (jsRecord) {
            // Found a valid record. Lets update now
            retVal = jsRecord.assign(data);
        }
        else {
            throw new Error("DataSource.update(): Unable to find record with this id " + id);
        }
        return retVal;
    };
    /**
     * Deletes a record from JSDO memory by calling jsdo.remove() API. This accepts a record
     * with a valid _id without which an error is reported
     * @param data Provide valid record for deletion
     * @returns boolean - True if the operation succeeds, false otherwise
     */
    DataSource.prototype.remove = function (data) {
        var retVal = false;
        var id = (data && data._id) ? data._id : null;
        var jsRecord;
        if (!data && (data === undefined || null)) {
            throw new Error("Unexpected signature for remove() operation.");
        }
        if (!id) {
            throw new Error("DataSource.remove(): data missing _id property");
        }
        jsRecord = this.jsdo[this._tableRef].findById(id);
        if (jsRecord) {
            // Found a valid record. Lets delete the record
            retVal = jsRecord.remove(data);
        }
        else {
            throw new Error("DataSource.remove(): Unable to find record with this id " + id);
        }
        return retVal;
    };
    /**
     * Accepts any pending changes in the data source. This results in the removal of the
     * before-image data. It also clears out any error messages.
     */
    DataSource.prototype.acceptChanges = function () {
        this.jsdo[this._tableRef].acceptChanges();
    };
    /**
     * Cancels any pending changes in the data source. Deleted rows are restored,
     * new rows are removed and updated rows are restored to their initial state.
     */
    DataSource.prototype.cancelChanges = function () {
        this.jsdo[this._tableRef].rejectChanges();
    };
    /**
     * Returns true if the underlying jsdo has CUD support (create, update, delete operations).
     * If not, it returns false.
     */
    DataSource.prototype.hasCUDSupport = function () {
        return this.jsdo.hasCUDOperations;
    };
    /**
     * Returns true if the underlying jsdo has Submit support (submit operation).
     * If not, it returns false.
     */
    DataSource.prototype.hasSubmitSupport = function () {
        return this.jsdo.hasSubmitOperation;
    };
    /**
     * Synchronizes to the server all record changes (creates, updates, and deletes) pending in
     * JSDO memory for the current Data Object resource
     * If jsdo.hasSubmitOperation is false, all record modifications are sent to server individually.
     * When 'true', modifications are batched together and sent in single request
     * @returns {object} Observable
     */
    DataSource.prototype.saveChanges = function () {
        var _this = this;
        var promise;
        var obs;
        var promResponse = {};
        var tableRefVal;
        promise = new Promise(function (resolve, reject) {
            var responseData = {};
            _this.jsdo.saveChanges(_this.jsdo.hasSubmitOperation)
                .then(function (result) {
                tableRefVal = _this._tableRef;
                if (_this.jsdo.hasSubmitOperation) {
                    // Submit case
                    _this._copyRecord(result.request.xhr.response, responseData);
                    resolve(responseData);
                }
                else {
                    // Non-Submit case
                    if (result.info.batch.operations && result.info.batch.operations.length > 0) {
                        result.info.batch.operations.forEach(function (operation) {
                            _this._copyRecord(operation.response, responseData);
                            // In case of multiple operations we want to merge those records pertaining
                            // to different operations in a single dataset and is sent as part of the
                            // response object for the consumer of this API.
                            _this._buildResponse(responseData, promResponse);
                        });
                        resolve(promResponse);
                        // Scenario where the saveChanges is invoked directly without any Submit/Non-Submit
                        // service as the serviceURI. We will resolve with an empty object
                    }
                    else if (result.info.batch.operations.length === 0) {
                        resolve({});
                    }
                    else {
                        reject(new Error(_this.normalizeError(result, "saveChanges", "Errors occurred while saving Changes.")));
                    }
                }
            }).catch(function (result) {
                reject(new Error(_this.normalizeError(result, "saveChanges", "Errors occurred while saving Changes.")));
            });
        });
        obs = Observable_1.Observable.fromPromise(promise);
        obs.catch(function (e) {
            return [];
        });
        return obs;
    };
    /**
     * This method is called after an error has occurred on a jsdo operation, and is
     * used to get an error message.
     * @param {any} result Object containing error info returned after execution of jsdo operation
     * @param {string} operation String containing operation performed when error occurred
     * @param {string} genericMsg If multiple errors are found in result object, if specified,
     * this string will be returned. If not specified, first error string will be returned.
     * @returns A single error message
     */
    DataSource.prototype.normalizeError = function (result, operation, genericMsg) {
        var errorMsg = "";
        var lastErrors = null;
        try {
            if (result.info && result.info.errorObject) {
                errorMsg = result.info.errorObject.message;
            }
            else if (result.jsdo) {
                lastErrors = result.jsdo[this._tableRef].getErrors();
                if (lastErrors.length >= 1) {
                    // If generic message is provided, use that, else we'll just grab first message
                    if (lastErrors.length > 1 && genericMsg) {
                        errorMsg = genericMsg;
                    }
                    else {
                        errorMsg = lastErrors[0].error;
                    }
                }
            }
            else if (result.message) {
                errorMsg = result.message;
            }
            if (errorMsg === "") {
                errorMsg = "Unknown error occurred when calling " + operation + ".";
            }
        }
        catch (error) {
            errorMsg = error.message;
        }
        return errorMsg;
    };
    DataSource.prototype._copyRecord = function (source, target) {
        var field;
        var newObject;
        if (!target) {
            console.log("_copyRecord: target parameter is not defined");
            return;
        }
        for (field in source) {
            if (source.hasOwnProperty(field)) {
                // Ignore all internal fields, except _id
                if (source[field] === undefined || source[field] === null ||
                    (field.charAt(0) === "_" && field !== "_id") ||
                    field.startsWith("prods:")) {
                    continue;
                }
                if (source[field] instanceof Date) {
                    target[field] = source[field];
                }
                else if (typeof source[field] === "object") {
                    newObject = source[field] instanceof Array ? [] : {};
                    this._copyRecord(source[field], newObject);
                    target[field] = newObject;
                }
                else {
                    target[field] = source[field];
                }
            }
        }
    };
    /**
     * This method is responsible for building a valid responseObject when multiple records
     * are involved in transaction
     * @param source  Actual dataset/record to be merged
     * @param target  Resultant dataset with all records information
     */

    DataSource.prototype._buildResponse = function (source, target) {
        var newEntry = source;
        var firstKey = Object.keys(source)[0], secondKey = (firstKey) ? Object.keys(source[firstKey])[0] : undefined;
        // Delete's on no submit services return empty datasets so
        // don't add anything.
        if (typeof source[firstKey] !== "undefined"
            && typeof source[firstKey][secondKey] !== "undefined") {
            if (Object.keys(target).length === 0) {
                this._copyRecord(source, target);
            }
            else {
                firstKey = Object.keys(target)[0];
                // Delete's on no submit services return empty datasets so
                // don't add anything.
                if (firstKey && typeof target[firstKey][this._tableRef] !== "undefined") {
                    // Dataset usecase
                    if (firstKey !== this._tableRef) {
                        target[firstKey][this._tableRef].push(newEntry[firstKey][this._tableRef][0]);
                    }
                    else { // Temp-table usecase
                        target[this._tableRef].push(newEntry[this._tableRef][0]);
                    }
                    return target;
                }
            }
        }
    };
    DataSource = __decorate([
        core_1.Injectable()
    ], DataSource);
    return DataSource;
}());
exports.DataSource = DataSource;<commit_msg>forgot return<commit_after>"use strict";
/*
Progress JSDO DataSource for Angular: 5.0.0
Copyright 2017-2018 Progress Software Corporation and/or its subsidiaries or affiliates.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
    http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
progress.data.ng.ds.ts    Version: v5.0.0
Progress DataSource class for NativeScript, Angular. This will provide a seamless integration
between OpenEdge (Progress Data Object) with NativeScript.
Author(s): maura, anikumar, egarcia
*/
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
Object.defineProperty(exports, "__esModule", { value: true });
var core_1 = require("@angular/core");
var jsdo_core_1 = require("@progress/jsdo-core");
require("rxjs/add/observable/fromPromise");
require("rxjs/add/operator/catch");
var Observable_1 = require("rxjs/Observable");
var DataSourceOptions = /** @class */ (function () {
    function DataSourceOptions() {
    }
    return DataSourceOptions;
}());
exports.DataSourceOptions = DataSourceOptions;
// tslint:disable max-classes-per-file
var DataSource = /** @class */ (function () {
    function DataSource(options) {
        this.jsdo = undefined;
        this._data = null;
        this.jsdo = options.jsdo;
        this._options = options;
        // Turning off autoApplyChanges. Want to explicitly call jsdo.acceptChanges() and rejectChanges()
        this.jsdo.autoApplyChanges = false;
        if (!options.jsdo || !(options.jsdo instanceof jsdo_core_1.progress.data.JSDO)) {
            throw new Error("DataSource: jsdo property must be set to a JSDO instance.");
        }
        if (this._options.tableRef === undefined && this.jsdo.defaultTableRef) {
            this._options.tableRef = this.jsdo.defaultTableRef._name;
        }
        if (this._options.tableRef === undefined) {
            throw new Error("DataSource: A tableRef must be specified when using a multi-table DataSet.");
        }
        else if (this.jsdo[this._options.tableRef] === undefined) {
            throw new Error("DataSource: tableRef '"
                + this._options.tableRef + "' is not present in underlying JSDO definition.");
        }
        this._tableRef = this._options.tableRef;
    }
    /**
     * Calls the jsdo.fill() retrieving data from the backend service
     * @returns Observable<Array<object>>
     */
    DataSource.prototype.read = function (params) {
        var _this = this;
        var wrapperPromise;
        var obs;
        var filter = {};
        if (params) {
            filter = params;
        }
        else {
            // Initial read() where the params are empty and we are assigning the filter criteria
            filter.filter = this._options.filter;
            filter.sort = this._options.sort;
            filter.top = this._options.top;
            filter.skip = this._options.skip;
        }
        // tableRef required for multi-table DataSets
        filter.tableRef = this._tableRef;
        wrapperPromise = new Promise(function (resolve, reject) {
            _this.jsdo.fill(filter)
                .then(function (result) {
                var data = result.jsdo[_this._tableRef].getData();
                // Make copy of jsdo data for datasource
                _this._data = (data.length > 0 ? data.map(function (item) { return Object.assign({}, item); }) : []);
                resolve(_this._data);
            }).catch(function (result) {
                reject(new Error(_this.normalizeError(result, "read", "")));
            });
        });
        obs = Observable_1.Observable.fromPromise(wrapperPromise);
        obs.catch(function (e) {
            return [];
        });
        // return Observable.fromPromise(wrapperPromise);
        return obs;
    };
    /**
     * Returns array of record objects from JSDO local memory
     * @returns {object}
     */
    DataSource.prototype.getData = function () {
        return this._data;
    };
    /**
     * Calls the jsdo.add() method, creating a new record in JSDO memory
     * jsdo.add() will either return the new record, or throws an exception
     * @param data - Record to create is passed as an object
     * @returns - If successful, an object of the new record is returned
     */
    DataSource.prototype.create = function (data) {
        var jsRecord;
        var newRow = {};
        jsRecord = this.jsdo[this._tableRef].add(data);
        this._copyRecord(jsRecord.data, newRow);
        return newRow;
    };
    /**
     * Returns a copy of the record with the specified id.
     * Note: current implementation uses jsdo's internal _id as id.
     * @param id - id of record
     * @returns - copy of record with specified id, else null if no record found
     */
    DataSource.prototype.findById = function (id) {
        // For now, we are using _id as our id to find records..
        return this.jsdo[this._options.tableRef].findById(id, false);
    };
    /**
     * Calls the jsdo.update() method, for updating a record in JSDO memory
     * jsdo.update() will either return the updated record, or throws an exception
     * @param data - Record to create is passed as an object
     * @returns - boolean. True if successful, false if there is any failure
     */
    DataSource.prototype.update = function (data) {
        if (!data && (data === undefined || null)) {
            throw new Error("Unexpected signature for update() operation.");
        }
        var id = (data && data._id) ? data._id : null;
        var jsRecord;
        var retVal = false;
        if (!id) {
            throw new Error("DataSource.update(): data missing _id property");
        }
        jsRecord = this.jsdo[this._tableRef].findById(id);
        if (jsRecord) {
            // Found a valid record. Lets update now
            retVal = jsRecord.assign(data);
        }
        else {
            throw new Error("DataSource.update(): Unable to find record with this id " + id);
        }
        return retVal;
    };
    /**
     * Deletes a record from JSDO memory by calling jsdo.remove() API. This accepts a record
     * with a valid _id without which an error is reported
     * @param data Provide valid record for deletion
     * @returns boolean - True if the operation succeeds, false otherwise
     */
    DataSource.prototype.remove = function (data) {
        var retVal = false;
        var id = (data && data._id) ? data._id : null;
        var jsRecord;
        if (!data && (data === undefined || null)) {
            throw new Error("Unexpected signature for remove() operation.");
        }
        if (!id) {
            throw new Error("DataSource.remove(): data missing _id property");
        }
        jsRecord = this.jsdo[this._tableRef].findById(id);
        if (jsRecord) {
            // Found a valid record. Lets delete the record
            retVal = jsRecord.remove(data);
        }
        else {
            throw new Error("DataSource.remove(): Unable to find record with this id " + id);
        }
        return retVal;
    };
    /**
     * Accepts any pending changes in the data source. This results in the removal of the
     * before-image data. It also clears out any error messages.
     */
    DataSource.prototype.acceptChanges = function () {
        this.jsdo[this._tableRef].acceptChanges();
    };
    /**
     * Cancels any pending changes in the data source. Deleted rows are restored,
     * new rows are removed and updated rows are restored to their initial state.
     */
    DataSource.prototype.cancelChanges = function () {
        this.jsdo[this._tableRef].rejectChanges();
    };
    /**
     * Returns true if the underlying jsdo has CUD support (create, update, delete operations).
     * If not, it returns false.
     */
    DataSource.prototype.hasCUDSupport = function () {
        return this.jsdo.hasCUDOperations;
    };
    /**
     * Returns true if the underlying jsdo has Submit support (submit operation).
     * If not, it returns false.
     */
    DataSource.prototype.hasSubmitSupport = function () {
        return this.jsdo.hasSubmitOperation;
    };
    /**
     * Synchronizes to the server all record changes (creates, updates, and deletes) pending in
     * JSDO memory for the current Data Object resource
     * If jsdo.hasSubmitOperation is false, all record modifications are sent to server individually.
     * When 'true', modifications are batched together and sent in single request
     * @returns {object} Observable
     */
    DataSource.prototype.saveChanges = function () {
        var _this = this;
        var promise;
        var obs;
        var promResponse = {};
        var tableRefVal;
        promise = new Promise(function (resolve, reject) {
            var responseData = {};
            _this.jsdo.saveChanges(_this.jsdo.hasSubmitOperation)
                .then(function (result) {
                tableRefVal = _this._tableRef;
                if (_this.jsdo.hasSubmitOperation) {
                    // Submit case
                    _this._copyRecord(result.request.xhr.response, responseData);
                    resolve(responseData);
                }
                else {
                    // Non-Submit case
                    if (result.info.batch.operations && result.info.batch.operations.length > 0) {
                        result.info.batch.operations.forEach(function (operation) {
                            _this._copyRecord(operation.response, responseData);
                            // In case of multiple operations we want to merge those records pertaining
                            // to different operations in a single dataset and is sent as part of the
                            // response object for the consumer of this API.
                            _this._buildResponse(responseData, promResponse);
                        });
                        resolve(promResponse);
                        // Scenario where the saveChanges is invoked directly without any Submit/Non-Submit
                        // service as the serviceURI. We will resolve with an empty object
                    }
                    else if (result.info.batch.operations.length === 0) {
                        resolve({});
                    }
                    else {
                        reject(new Error(_this.normalizeError(result, "saveChanges", "Errors occurred while saving Changes.")));
                    }
                }
            }).catch(function (result) {
                reject(new Error(_this.normalizeError(result, "saveChanges", "Errors occurred while saving Changes.")));
            });
        });
        obs = Observable_1.Observable.fromPromise(promise);
        obs.catch(function (e) {
            return [];
        });
        return obs;
    };
    /**
     * This method is called after an error has occurred on a jsdo operation, and is
     * used to get an error message.
     * @param {any} result Object containing error info returned after execution of jsdo operation
     * @param {string} operation String containing operation performed when error occurred
     * @param {string} genericMsg If multiple errors are found in result object, if specified,
     * this string will be returned. If not specified, first error string will be returned.
     * @returns A single error message
     */
    DataSource.prototype.normalizeError = function (result, operation, genericMsg) {
        var errorMsg = "";
        var lastErrors = null;
        try {
            if (result.info && result.info.errorObject) {
                errorMsg = result.info.errorObject.message;
            }
            else if (result.jsdo) {
                lastErrors = result.jsdo[this._tableRef].getErrors();
                if (lastErrors.length >= 1) {
                    // If generic message is provided, use that, else we'll just grab first message
                    if (lastErrors.length > 1 && genericMsg) {
                        errorMsg = genericMsg;
                    }
                    else {
                        errorMsg = lastErrors[0].error;
                    }
                }
            }
            else if (result.message) {
                errorMsg = result.message;
            }
            if (errorMsg === "") {
                errorMsg = "Unknown error occurred when calling " + operation + ".";
            }
        }
        catch (error) {
            errorMsg = error.message;
        }
        return errorMsg;
    };
    DataSource.prototype._copyRecord = function (source, target) {
        var field;
        var newObject;
        if (!target) {
            console.log("_copyRecord: target parameter is not defined");
            return;
        }
        for (field in source) {
            if (source.hasOwnProperty(field)) {
                // Ignore all internal fields, except _id
                if (source[field] === undefined || source[field] === null ||
                    (field.charAt(0) === "_" && field !== "_id") ||
                    field.startsWith("prods:")) {
                    continue;
                }
                if (source[field] instanceof Date) {
                    target[field] = source[field];
                }
                else if (typeof source[field] === "object") {
                    newObject = source[field] instanceof Array ? [] : {};
                    this._copyRecord(source[field], newObject);
                    target[field] = newObject;
                }
                else {
                    target[field] = source[field];
                }
            }
        }
    };
    /**
     * This method is responsible for building a valid responseObject when multiple records
     * are involved in transaction
     * @param source  Actual dataset/record to be merged
     * @param target  Resultant dataset with all records information
     */

    DataSource.prototype._buildResponse = function (source, target) {
        var newEntry = source;
        var firstKey = Object.keys(source)[0], secondKey = (firstKey) ? Object.keys(source[firstKey])[0] : undefined;
        // Delete's on no submit services return empty datasets so
        // don't add anything.
        if (typeof source[firstKey] !== "undefined"
            && typeof source[firstKey][secondKey] !== "undefined") {
            if (Object.keys(target).length === 0) {
                this._copyRecord(source, target);
            }
            else {
                firstKey = Object.keys(target)[0];
                // Delete's on no submit services return empty datasets so
                // don't add anything.
                if (firstKey && typeof target[firstKey][this._tableRef] !== "undefined") {
                    // Dataset usecase
                    if (firstKey !== this._tableRef) {
                        target[firstKey][this._tableRef].push(newEntry[firstKey][this._tableRef][0]);
                    }
                    else { // Temp-table usecase
                        target[this._tableRef].push(newEntry[this._tableRef][0]);
                    }
                    return target;
                }
            }
        }
    };
    DataSource = __decorate([
        core_1.Injectable()
    ], DataSource);
    return DataSource;
}());
exports.DataSource = DataSource;<|endoftext|>


<commit_before>'use strict'

import leftPad from 'left-pad'

const DOC_STORE = 'ÿdocument-storeÿ'
const DOC_STORE_LENGTH = DOC_STORE.length
const META_STORE = 'ÿmeta-storeÿ'
const META_STORE_LENGTH = META_STORE.length
const ATTACHMENT_STORE = 'ÿattach-storeÿ'
const SEQUENCE_STORE = 'ÿby-sequenceÿ'
const SEQUENCE_STORE_LENGTH = SEQUENCE_STORE.length

export const forDocument = id => `${DOC_STORE}${id}`
export const forAttachment = id => `${ATTACHMENT_STORE}${id}`
export const forMeta = id => `${META_STORE}${id}`
export const forSequence = seq => `${SEQUENCE_STORE}${leftPad(seq, 16, 0)}`

export const sliceDocument = id => id.slice(DOC_STORE_LENGTH)
export const sliceMeta = id => id.slice(META_STORE_LENGTH)

export const toDocumentKeys = list => list.map(forDocument)
export const toMetaKeys = list => list.map(forMeta)
export const toSequenceKeys = list => list.map(forSequence)

export const getDocumentKeys = list => list
  .filter(key => key.startsWith(DOC_STORE))
  .map(key => key.slice(DOC_STORE_LENGTH))

export const getSequenceKeys = list => list
  .filter(key => key.startsWith(SEQUENCE_STORE))
  .map(key => parseInt(key.slice(SEQUENCE_STORE_LENGTH), 10))
<commit_msg>prepare keys for attachments<commit_after>'use strict'

import leftPad from 'left-pad'

const DOC_STORE = 'ÿdocument-storeÿ'
const DOC_STORE_LENGTH = DOC_STORE.length
const META_STORE = 'ÿmeta-storeÿ'
const META_STORE_LENGTH = META_STORE.length
const ATTACHMENT_STORE = 'ÿattach-storeÿ'
const ATTACHMENT_BINARY_STORE = 'ÿattach-binary-storeÿ'
const SEQUENCE_STORE = 'ÿby-sequenceÿ'
const SEQUENCE_STORE_LENGTH = SEQUENCE_STORE.length

export const forDocument = id => `${DOC_STORE}${id}`
export const forAttachment = id => `${ATTACHMENT_STORE}${id}`
export const forBinaryAttachment = id => `${ATTACHMENT_BINARY_STORE}${id}`
export const forMeta = id => `${META_STORE}${id}`
export const forSequence = seq => `${SEQUENCE_STORE}${leftPad(seq, 16, 0)}`

export const sliceDocument = id => id.slice(DOC_STORE_LENGTH)
export const sliceMeta = id => id.slice(META_STORE_LENGTH)

export const toDocumentKeys = list => list.map(forDocument)
export const toMetaKeys = list => list.map(forMeta)
export const toSequenceKeys = list => list.map(forSequence)

export const getDocumentKeys = list => list
  .filter(key => key.startsWith(DOC_STORE))
  .map(key => key.slice(DOC_STORE_LENGTH))

export const getSequenceKeys = list => list
  .filter(key => key.startsWith(SEQUENCE_STORE))
  .map(key => parseInt(key.slice(SEQUENCE_STORE_LENGTH), 10))
<|endoftext|>


<commit_before>export default ({ assetPrefix = '', webpack, ...config } = {}) => ({
  ...config,
  assetPrefix,
  webpack(webpackConfig, { dev }) {
    let localIdentName, imageName;
    if (dev) {
      localIdentName = '[name]__[local]--[hash:base64:5]';
      imageName = '[name]';
    } else {
      localIdentName = null;
      imageName = '[name]-[hash]';
    }

    const rule = webpackConfig.module.rules.find(rule => (
      rule.loader === 'babel-loader' &&
      rule.test && rule.test.toString() === '/\\.js(\\?[^?]*)?$/'
    ));
    if (rule && !rule.options.babelrc) {
      rule.options.presets.push(require.resolve('../babel/preset'));
    }
    webpackConfig.module.rules.push(
      {
        test: [
          /\.(css|s(a|c)ss)$/,
          /\.(bmp|gif|jpe?g|png)$/,
        ],
        loader: 'emit-file-loader',
        options: {
          name: 'dist/[path][name].[ext]',
        },
      },
      {
        test: /\.mod(ule)?\.css$/,
        use: [
          'babel-loader',
          'styled-modules/loader',
          {
            loader: 'css-loader',
            options: {
              localIdentName,
              modules: true,
              sourceMap: dev,
              importLoaders: 1,
            },
          },
        ],
      },
      {
        test: /\.mod(ule)?\.s(a|c)ss$/,
        use: [
          'babel-loader',
          'styled-modules/loader',
          {
            loader: 'css-loader',
            options: {
              localIdentName,
              modules: true,
              sourceMap: dev,
              importLoaders: 2,
            },
          },
          'sass-loader',
        ],
      },
      {
        test: /\.css$/,
        exclude: /\.mod(ule)?\.css$/,
        use: [
          'babel-loader',
          'styled-modules/loader',
          {
            loader: 'css-loader',
            options: {
              localIdentName,
              sourceMap: dev,
              importLoaders: 1,
            },
          },
        ],
      },
      {
        test: /\.s(a|c)ss$/,
        exclude: /\.mod(ule)?\.s(a|c)ss$/,
        use: [
          'babel-loader',
          'styled-modules/loader',
          {
            loader: 'css-loader',
            options: {
              localIdentName,
              sourceMap: dev,
              importLoaders: 2,
            },
          },
          'sass-loader',
        ],
      },
      {
        test: /\.(bmp|gif|jpe?g|png|ttf|eot|woff2?|svg)$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: `dist/static/[path]${imageName}.[ext]`,
          publicPath: url => `${assetPrefix}/_soya/${url.replace('dist/static/', '')}`,
        },
      },
    );

    if (webpack) {
      webpack(webpackConfig, { dev });
    }

    return webpackConfig;
  },
});
<commit_msg>Minimize css on production env<commit_after>export default ({ assetPrefix = '', webpack, ...config } = {}) => ({
  ...config,
  assetPrefix,
  webpack(webpackConfig, { dev }) {
    let localIdentName, imageName;
    if (dev) {
      localIdentName = '[name]__[local]--[hash:base64:5]';
      imageName = '[name]';
    } else {
      localIdentName = null;
      imageName = '[name]-[hash]';
    }

    const cssLoaderOptions = {
      localIdentName,
      minimize: !dev,
      sourceMap: dev,
    };

    const rule = webpackConfig.module.rules.find(rule => (
      rule.loader === 'babel-loader' &&
      rule.test && rule.test.toString() === '/\\.js(\\?[^?]*)?$/'
    ));
    if (rule && !rule.options.babelrc) {
      rule.options.presets.push(require.resolve('../babel/preset'));
    }
    webpackConfig.module.rules.push(
      {
        test: [
          /\.(css|s(a|c)ss)$/,
          /\.(bmp|gif|jpe?g|png)$/,
        ],
        loader: 'emit-file-loader',
        options: {
          name: 'dist/[path][name].[ext]',
        },
      },
      {
        test: /\.mod(ule)?\.css$/,
        use: [
          'babel-loader',
          'styled-modules/loader',
          {
            loader: 'css-loader',
            options: {
              ...cssLoaderOptions,
              modules: true,
            },
          },
        ],
      },
      {
        test: /\.mod(ule)?\.s(a|c)ss$/,
        use: [
          'babel-loader',
          'styled-modules/loader',
          {
            loader: 'css-loader',
            options: {
              ...cssLoaderOptions,
              modules: true,
              importLoaders: 1,
            },
          },
          'sass-loader',
        ],
      },
      {
        test: /\.css$/,
        exclude: /\.mod(ule)?\.css$/,
        use: [
          'babel-loader',
          'styled-modules/loader',
          {
            loader: 'css-loader',
            options: cssLoaderOptions,
          },
        ],
      },
      {
        test: /\.s(a|c)ss$/,
        exclude: /\.mod(ule)?\.s(a|c)ss$/,
        use: [
          'babel-loader',
          'styled-modules/loader',
          {
            loader: 'css-loader',
            options: {
              ...cssLoaderOptions,
              importLoaders: 1,
            },
          },
          'sass-loader',
        ],
      },
      {
        test: /\.(bmp|gif|jpe?g|png|ttf|eot|woff2?|svg)$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: `dist/static/[path]${imageName}.[ext]`,
          publicPath: url => `${assetPrefix}/_soya/${url.replace('dist/static/', '')}`,
        },
      },
    );

    if (webpack) {
      webpack(webpackConfig, { dev });
    }

    return webpackConfig;
  },
});
<|endoftext|>


<commit_before>function TestResolver(resolver, source, search_path) {
  this.resolver = resolver;
  this.source = source;
  this.search_path = search_path;

  this.seen = [];
  this.require_cache = {};
  this.cache_on = true;
}

TestResolver.prototype.require = function(import_path) {
  if (this.cache_on && this.require_cache[import_path]) {
    return this.require_cache[import_path];
  }

  // Remember: This throws if not found.
  var result = this.resolver.require(import_path, this.search_path);

  this.require_cache[import_path] = result;

  return result;
};

TestResolver.prototype.resolve = function(import_path, imported_from, callback) {
  var self = this;
  this.source.resolve(import_path, function(err, result, resolved_path) {
    if (err) return callback(err);
    if (result) return callback(null, result, resolved_path);

    self.resolver.resolve(import_path, imported_from, callback);
  });
};

module.exports = TestResolver;
<commit_msg>allow import paths to be OS-independent<commit_after>const path = require("path");

function TestResolver(resolver, source, search_path) {
  this.resolver = resolver;
  this.source = source;
  this.search_path = search_path;

  this.seen = [];
  this.require_cache = {};
  this.cache_on = true;
}

TestResolver.prototype.require = function(import_path) {
  if (this.cache_on && this.require_cache[import_path]) {
    return this.require_cache[import_path];
  }

  // For Windows: Allow import paths to be either path separator ('\' or '/')
  // by converting all '/' to the default (path.sep);
  import_path = import_path.replace(/\//g, path.sep);

  // Remember: This throws if not found.
  var result = this.resolver.require(import_path, this.search_path);

  this.require_cache[import_path] = result;

  return result;
};

TestResolver.prototype.resolve = function(
  import_path,
  imported_from,
  callback
) {
  var self = this;
  this.source.resolve(import_path, function(err, result, resolved_path) {
    if (err) return callback(err);
    if (result) return callback(null, result, resolved_path);

    self.resolver.resolve(import_path, imported_from, callback);
  });
};

module.exports = TestResolver;
<|endoftext|>


<commit_before>import test from "ava";
import Browser from "../src/index.js";
import { rootContext } from "webmiddle";

test.beforeEach(t => {
  t.context.context = rootContext.extend({
    networkRetries: 3
  });
});

test("GET https page", async t => {
  const number = Math.round(Math.random() * 100);

  const output = await t.context.context.evaluate(
    <Browser
      name="virtual"
      contentType="application/json"
      method="GET"
      url={`https://httpbin.org/get?number=${encodeURIComponent(
        number
      )}&static=${encodeURIComponent("test this number")}`}
    />
  );

  const json = output.content;
  t.deepEqual(json.args, {
    number: number.toString(), // NOTE: type is lost with query data
    static: "test this number"
  });
});

test("GET xml document (infer resource contentType)", async t => {
  const number = Math.round(Math.random() * 100);

  const output = await t.context.context.evaluate(
    <Browser name="virtual" method="GET" url={`https://httpbin.org/xml`} />
  );

  t.is(output.contentType, "application/xml");
});

test("GET non-html document: should ignore waitFor", async t => {
  const number = Math.round(Math.random() * 100);

  const output = await t.context.context.evaluate(
    <Browser
      name="virtual"
      contentType="application/xml"
      method="GET"
      url={`https://httpbin.org/xml`}
      waitFor=".foo"
    />
  );

  t.is(output.contentType, "application/xml");
});

test("POST https page: form data as string (no content type header)", async t => {
  const number = Math.round(Math.random() * 100);

  const output = await t.context.context.evaluate(
    <Browser
      name="virtual"
      contentType="application/json"
      method="POST"
      url="https://httpbin.org/post"
      body={`number=${encodeURIComponent(number)}&static=${encodeURIComponent(
        "test this number"
      )}`}
    />
  );

  const json = output.content;
  t.deepEqual(json.form, {
    number: number.toString(), // NOTE: type is lost with form data
    static: "test this number"
  });
});

test("POST https page: form data as string (no content type header, case insensitive method)", async t => {
  const number = Math.round(Math.random() * 100);

  const output = await t.context.context.evaluate(
    <Browser
      name="virtual"
      contentType="application/json"
      method="pOsT"
      url="https://httpbin.org/post"
      body={`number=${encodeURIComponent(number)}&static=${encodeURIComponent(
        "test this number"
      )}`}
    />
  );

  const json = output.content;
  t.deepEqual(json.form, {
    number: number.toString(), // NOTE: type is lost with form data
    static: "test this number"
  });
});

test("POST https page: form data as object (no content type header)", async t => {
  const number = Math.round(Math.random() * 100);

  const output = await t.context.context.evaluate(
    <Browser
      name="virtual"
      contentType="application/json"
      method="POST"
      url="https://httpbin.org/post"
      body={{
        number,
        static: "test this number"
      }}
    />
  );

  const json = output.content;
  t.deepEqual(json.form, {
    number: number.toString(), // NOTE: type is lost with form data
    static: "test this number"
  });
});

test("POST https page: form data as object (with content type header)", async t => {
  const number = Math.round(Math.random() * 100);

  const output = await t.context.context.evaluate(
    <Browser
      name="virtual"
      contentType="application/json"
      method="POST"
      url="https://httpbin.org/post"
      body={{
        number,
        static: "test this number"
      }}
      httpHeaders={{
        "Content-Type": "application/x-www-form-urlencoded"
      }}
    />
  );

  const json = output.content;
  t.deepEqual(json.form, {
    number: number.toString(), // NOTE: type is lost with form data
    static: "test this number"
  });
});

test("POST https page: json data as string", async t => {
  const number = Math.round(Math.random() * 100);

  const output = await t.context.context.evaluate(
    <Browser
      name="virtual"
      contentType="application/json"
      method="POST"
      url="https://httpbin.org/post"
      body={JSON.stringify({
        number,
        static: "test this number"
      })}
      httpHeaders={{
        "Content-Type": "application/json"
      }}
    />
  );

  const json = output.content;
  t.deepEqual(json.json, {
    number,
    static: "test this number"
  });
});

test("POST https page: json data as string (case insensitive headers)", async t => {
  const number = Math.round(Math.random() * 100);

  const output = await t.context.context.evaluate(
    <Browser
      name="virtual"
      contentType="application/json"
      method="POST"
      url="https://httpbin.org/post"
      body={JSON.stringify({
        number,
        static: "test this number"
      })}
      httpHeaders={{
        "CoNTenT-TYpe": "application/json"
      }}
    />
  );

  const json = output.content;
  t.deepEqual(json.json, {
    number,
    static: "test this number"
  });
});

test("POST https page: json data as object", async t => {
  const number = Math.round(Math.random() * 100);

  const output = await t.context.context.evaluate(
    <Browser
      name="virtual"
      contentType="application/json"
      method="POST"
      url="https://httpbin.org/post"
      body={{
        number,
        static: "test this number"
      }}
      httpHeaders={{
        "Content-Type": "application/json"
      }}
    />
  );

  const json = output.content;
  t.deepEqual(json.json, {
    number,
    static: "test this number"
  });
});

test("httpHeaders", async t => {
  const output = await t.context.context.evaluate(
    <Browser
      name="virtual"
      contentType="application/json"
      method="GET"
      url="https://httpbin.org/get"
      httpHeaders={{
        "My-Custom-Webmiddle-Header": "Browser service test"
      }}
    />
  );

  // Note: uppercase letters inside a word are converted to lowercase by the server
  // e.g: My-CuStoM => My-Custom

  const json = output.content;
  t.is(json.headers["My-Custom-Webmiddle-Header"], "Browser service test");
});

test("waitFor", async t => {
  const output = await t.context.context.evaluate(
    <Browser
      name="virtual"
      contentType="text/html"
      method="GET"
      url="http://todomvc.com/examples/react/#/"
      waitFor=".new-todo"
    />
  );

  t.pass();
});

test("cookies", async t => {
  // save to jar

  let v2 = Math.floor(Math.random() * 100) + 1;
  let v1 = Math.floor(Math.random() * 100) + 1;

  await t.context.context.evaluate(
    <Browser
      name="virtual"
      contentType="application/json"
      method="GET"
      url={`https://httpbin.org/cookies/set?k2=${v2}&k1=${v1}`}
    />
  );

  const cookies = t.context.context.cookieManager.jar.getCookiesSync(
    "https://httpbin.org"
  );

  const cookieK2 = cookies.find(c => c.key === "k2");
  t.is(cookieK2.value, String(v2));

  const cookieK1 = cookies.find(c => c.key === "k1");
  t.is(cookieK1.value, String(v1));

  // read from jar

  v2 = Math.floor(Math.random() * 100) + 1;
  v1 = Math.floor(Math.random() * 100) + 1;

  t.context.context.cookieManager.jar.setCookieSync(
    t.context.context.cookieManager.Cookie.parse(`k2=${v2}; Path=/`, {
      loose: true
    }),
    "https://httpbin.org",
    {}
  );

  t.context.context.cookieManager.jar.setCookieSync(
    t.context.context.cookieManager.Cookie.parse(`k1=${v1}; Path=/`, {
      loose: true
    }),
    "https://httpbin.org",
    {}
  );

  const output = await t.context.context.evaluate(
    <Browser
      name="virtual"
      contentType="application/json"
      method="GET"
      url="https://httpbin.org/cookies"
    />
  );

  const json = output.content;
  t.deepEqual(json.cookies, {
    k2: String(v2),
    k1: String(v1)
  });
});

test("Should not throw when status code is between 200 and 299", async t => {
  await t.notThrows(
    t.context.context.evaluate(
      <Browser
        name="virtual"
        contentType="text/html"
        method="GET"
        url={`https://httpbin.org/status/201`}
      />
    )
  );

  await t.notThrows(
    t.context.context.evaluate(
      <Browser
        name="virtual"
        contentType="text/html"
        method="GET"
        url={`https://httpbin.org/status/299`}
      />
    )
  );
});

test("Should fail with correct status code", async t => {
  try {
    await t.context.context.evaluate(
      <Browser
        name="virtual"
        contentType="text/html"
        method="GET"
        url={`https://httpbin.org/status/499`}
      />
    );
  } catch (err) {
    t.is(err.statusCode, 499);
    t.is(err.message, "success");
  }
});
<commit_msg>#8 | Split save & read cookie tests, remove randomness<commit_after>import test from "ava";
import Browser from "../src/index.js";
import { rootContext } from "webmiddle";

test.beforeEach(t => {
  t.context.context = rootContext.extend({
    networkRetries: 3
  });
});

test("GET https page", async t => {
  const number = Math.round(Math.random() * 100);

  const output = await t.context.context.evaluate(
    <Browser
      name="virtual"
      contentType="application/json"
      method="GET"
      url={`https://httpbin.org/get?number=${encodeURIComponent(
        number
      )}&static=${encodeURIComponent("test this number")}`}
    />
  );

  const json = output.content;
  t.deepEqual(json.args, {
    number: number.toString(), // NOTE: type is lost with query data
    static: "test this number"
  });
});

test("GET xml document (infer resource contentType)", async t => {
  const number = Math.round(Math.random() * 100);

  const output = await t.context.context.evaluate(
    <Browser name="virtual" method="GET" url={`https://httpbin.org/xml`} />
  );

  t.is(output.contentType, "application/xml");
});

test("GET non-html document: should ignore waitFor", async t => {
  const number = Math.round(Math.random() * 100);

  const output = await t.context.context.evaluate(
    <Browser
      name="virtual"
      contentType="application/xml"
      method="GET"
      url={`https://httpbin.org/xml`}
      waitFor=".foo"
    />
  );

  t.is(output.contentType, "application/xml");
});

test("POST https page: form data as string (no content type header)", async t => {
  const number = Math.round(Math.random() * 100);

  const output = await t.context.context.evaluate(
    <Browser
      name="virtual"
      contentType="application/json"
      method="POST"
      url="https://httpbin.org/post"
      body={`number=${encodeURIComponent(number)}&static=${encodeURIComponent(
        "test this number"
      )}`}
    />
  );

  const json = output.content;
  t.deepEqual(json.form, {
    number: number.toString(), // NOTE: type is lost with form data
    static: "test this number"
  });
});

test("POST https page: form data as string (no content type header, case insensitive method)", async t => {
  const number = Math.round(Math.random() * 100);

  const output = await t.context.context.evaluate(
    <Browser
      name="virtual"
      contentType="application/json"
      method="pOsT"
      url="https://httpbin.org/post"
      body={`number=${encodeURIComponent(number)}&static=${encodeURIComponent(
        "test this number"
      )}`}
    />
  );

  const json = output.content;
  t.deepEqual(json.form, {
    number: number.toString(), // NOTE: type is lost with form data
    static: "test this number"
  });
});

test("POST https page: form data as object (no content type header)", async t => {
  const number = Math.round(Math.random() * 100);

  const output = await t.context.context.evaluate(
    <Browser
      name="virtual"
      contentType="application/json"
      method="POST"
      url="https://httpbin.org/post"
      body={{
        number,
        static: "test this number"
      }}
    />
  );

  const json = output.content;
  t.deepEqual(json.form, {
    number: number.toString(), // NOTE: type is lost with form data
    static: "test this number"
  });
});

test("POST https page: form data as object (with content type header)", async t => {
  const number = Math.round(Math.random() * 100);

  const output = await t.context.context.evaluate(
    <Browser
      name="virtual"
      contentType="application/json"
      method="POST"
      url="https://httpbin.org/post"
      body={{
        number,
        static: "test this number"
      }}
      httpHeaders={{
        "Content-Type": "application/x-www-form-urlencoded"
      }}
    />
  );

  const json = output.content;
  t.deepEqual(json.form, {
    number: number.toString(), // NOTE: type is lost with form data
    static: "test this number"
  });
});

test("POST https page: json data as string", async t => {
  const number = Math.round(Math.random() * 100);

  const output = await t.context.context.evaluate(
    <Browser
      name="virtual"
      contentType="application/json"
      method="POST"
      url="https://httpbin.org/post"
      body={JSON.stringify({
        number,
        static: "test this number"
      })}
      httpHeaders={{
        "Content-Type": "application/json"
      }}
    />
  );

  const json = output.content;
  t.deepEqual(json.json, {
    number,
    static: "test this number"
  });
});

test("POST https page: json data as string (case insensitive headers)", async t => {
  const number = Math.round(Math.random() * 100);

  const output = await t.context.context.evaluate(
    <Browser
      name="virtual"
      contentType="application/json"
      method="POST"
      url="https://httpbin.org/post"
      body={JSON.stringify({
        number,
        static: "test this number"
      })}
      httpHeaders={{
        "CoNTenT-TYpe": "application/json"
      }}
    />
  );

  const json = output.content;
  t.deepEqual(json.json, {
    number,
    static: "test this number"
  });
});

test("POST https page: json data as object", async t => {
  const number = Math.round(Math.random() * 100);

  const output = await t.context.context.evaluate(
    <Browser
      name="virtual"
      contentType="application/json"
      method="POST"
      url="https://httpbin.org/post"
      body={{
        number,
        static: "test this number"
      }}
      httpHeaders={{
        "Content-Type": "application/json"
      }}
    />
  );

  const json = output.content;
  t.deepEqual(json.json, {
    number,
    static: "test this number"
  });
});

test("httpHeaders", async t => {
  const output = await t.context.context.evaluate(
    <Browser
      name="virtual"
      contentType="application/json"
      method="GET"
      url="https://httpbin.org/get"
      httpHeaders={{
        "My-Custom-Webmiddle-Header": "Browser service test"
      }}
    />
  );

  // Note: uppercase letters inside a word are converted to lowercase by the server
  // e.g: My-CuStoM => My-Custom

  const json = output.content;
  t.is(json.headers["My-Custom-Webmiddle-Header"], "Browser service test");
});

test("waitFor", async t => {
  const output = await t.context.context.evaluate(
    <Browser
      name="virtual"
      contentType="text/html"
      method="GET"
      url="http://todomvc.com/examples/react/#/"
      waitFor=".new-todo"
    />
  );

  t.pass();
});

test("cookies: save to jar", async t => {
  const v1 = 10;
  const v2 = 20;

  await t.context.context.evaluate(
    <Browser
      name="virtual"
      contentType="application/json"
      method="GET"
      url={`https://httpbin.org/cookies/set?a1=${v1}&a2=${v2}`}
    />
  );

  const cookies = t.context.context.cookieManager.jar.getCookiesSync(
    "https://httpbin.org"
  );

  const cookieA1 = cookies.find(c => c.key === "a1");
  t.is(cookieA1.value, String(v1));

  const cookieA2 = cookies.find(c => c.key === "a2");
  t.is(cookieA2.value, String(v2));
});

test("cookies: read from jar", async t => {
  const v1 = 30;
  const v2 = 40;

  t.context.context.cookieManager.jar.setCookieSync(
    t.context.context.cookieManager.Cookie.parse(`b1=${v1}; Path=/`, {
      loose: true
    }),
    "https://httpbin.org",
    {}
  );

  t.context.context.cookieManager.jar.setCookieSync(
    t.context.context.cookieManager.Cookie.parse(`b2=${v2}; Path=/`, {
      loose: true
    }),
    "https://httpbin.org",
    {}
  );

  const output = await t.context.context.evaluate(
    <Browser
      name="virtual"
      contentType="application/json"
      method="GET"
      url="https://httpbin.org/cookies"
    />
  );

  const json = output.content;
  t.deepEqual(json.cookies, {
    b1: String(v1),
    b2: String(v2)
  });
});

test("Should not throw when status code is between 200 and 299", async t => {
  await t.notThrows(
    t.context.context.evaluate(
      <Browser
        name="virtual"
        contentType="text/html"
        method="GET"
        url={`https://httpbin.org/status/201`}
      />
    )
  );

  await t.notThrows(
    t.context.context.evaluate(
      <Browser
        name="virtual"
        contentType="text/html"
        method="GET"
        url={`https://httpbin.org/status/299`}
      />
    )
  );
});

test("Should fail with correct status code", async t => {
  try {
    await t.context.context.evaluate(
      <Browser
        name="virtual"
        contentType="text/html"
        method="GET"
        url={`https://httpbin.org/status/499`}
      />
    );
  } catch (err) {
    t.is(err.statusCode, 499);
    t.is(err.message, "success");
  }
});
<|endoftext|>


<commit_before>import uniqueId from 'lodash/uniqueId';
import startsWith from 'lodash/startsWith';
import isEmpty from 'lodash/isEmpty';

import { COMPONENT_GROUP_DESIGN_TYPE } from './design-type';

export function createGroup(name) {
  return {
    type: `${uniqueId(COMPONENT_GROUP_DESIGN_TYPE)}|${name}`,
    name
  };
}

export function isGroupComponent(component) {
  if (!component) {
    return false;
  }

  return startsWith(component.type, COMPONENT_GROUP_DESIGN_TYPE);
}

/**
 * Check if component array is grouped.
 *
 * A grouped component array MUST have a group component as its first element.
 * @param {Array} components
 */
export function isGrouped(components) {
  // Grouped components must have at least 2 elements
  if (!components || components.length < 2) {
    return false;
  }

  const possiblyGroup = components[0];
  return isGroupComponent(possiblyGroup);
}

/**
 * Split component array into an array of groups
 *
 * @param {Array} components
 */
export function splitGroup(components) {
  if (isEmpty(components)) {
    return [];
  }

  const lastIndex = components.length - 1;

  return components.reduce(
    (state, c, idx) => {
      const { groups, buffer, group } = state;
      const isGroup = isGroupComponent(c);

      if (!isGroup) {
        buffer.push(c);
      }

      // When processing the last component, ensure buffer is consumed
      if (isGroup || idx === lastIndex) {
        // Empty group is ignored
        if (group && !isEmpty(buffer)) {
          groups.push({
            group,
            components: buffer
          });
        }

        // Start a new group
        state.buffer = [];
        state.group = c;
      }

      return state;
    },
    { groups: [], buffer: [], g: null }
  ).groups;
}
<commit_msg>Fix design props warning<commit_after>import uniqueId from 'lodash/uniqueId';
import startsWith from 'lodash/startsWith';
import isEmpty from 'lodash/isEmpty';

import { COMPONENT_GROUP_DESIGN_TYPE } from './design-type';

function DummyComponent() {
  return null;
}

export function createGroup(name) {
  return {
    type: `${uniqueId(COMPONENT_GROUP_DESIGN_TYPE)}|${name}`,
    editor: DummyComponent,
    preview: DummyComponent,
    name
  };
}

export function isGroupComponent(component) {
  if (!component) {
    return false;
  }

  return startsWith(component.type, COMPONENT_GROUP_DESIGN_TYPE);
}

/**
 * Check if component array is grouped.
 *
 * A grouped component array MUST have a group component as its first element.
 * @param {Array} components
 */
export function isGrouped(components) {
  // Grouped components must have at least 2 elements
  if (!components || components.length < 2) {
    return false;
  }

  const possiblyGroup = components[0];
  return isGroupComponent(possiblyGroup);
}

/**
 * Split component array into an array of groups
 *
 * @param {Array} components
 */
export function splitGroup(components) {
  if (isEmpty(components)) {
    return [];
  }

  const lastIndex = components.length - 1;

  return components.reduce(
    (state, c, idx) => {
      const { groups, buffer, group } = state;
      const isGroup = isGroupComponent(c);

      if (!isGroup) {
        buffer.push(c);
      }

      // When processing the last component, ensure buffer is consumed
      if (isGroup || idx === lastIndex) {
        // Empty group is ignored
        if (group && !isEmpty(buffer)) {
          groups.push({
            group,
            components: buffer
          });
        }

        // Start a new group
        state.buffer = [];
        state.group = c;
      }

      return state;
    },
    { groups: [], buffer: [], g: null }
  ).groups;
}
<|endoftext|>