profile
viewpoint
Rick Waldron rwaldron Bocoup Boston, MA http://bocoup.com/ Director of Engineering @bocoup; Creator of Johnny-Five; Maintainer of @tc39/test262; He/him.

meltingice/CamanJS 3325

Javascript HTML5 (Ca)nvas (Man)ipulation

firmata/firmata.js 640

JavaScript implementation of the Firmata protocol

nebrius/raspi-io 497

An IO plugin for Johnny-Five that provides support for the Raspberry Pi

JSFoundation/standards 246

Giving web developers a voice in the standards process

bterlson/eshost-cli 152

Run ECMAScript code uniformly across any ECMAScript host

bterlson/eshost 103

A uniform wrapper around a multitude of ECMAScript hosts. CLI: https://github.com/bterlson/eshost-cli

gf3/WAT 102

LOLWAT?

jeresig/dromaeo 71

JavaScript Performance Test Suite

boazsender/jQuery-Twitter-Plugin 57

A jQuery plugin for putting twitter searches on websites.

bterlson/test262-harness 54

Experimental harness for test262

push eventrwaldron/web-platform-tests

Rick Waldron

commit sha 80161a1c971b27d7971f08515e27ace91e13e9cb

WeakRef/FinalizationRegistry testing

view details

push time in 4 days

create barnchrwaldron/web-platform-tests

branch : weakref-finalizationregistry

created branch time in 4 days

push eventrwaldron/web-platform-tests

Rick Waldron

commit sha ce0df1fef5eaedc5432cf125f133bccff27f9c66

WeakRef/FinalizationRegistry testing

view details

push time in 4 days

push eventrwaldron/web-platform-tests

Rick Waldron

commit sha dc3c63ab0a65efef3f626e42c6687dc51eda7209

WeakRef/FinalizationRegistry testing

view details

push time in 4 days

push eventbocoup/dcss

Rick Waldron

commit sha df82a5615f1cf9379e88a4d023cac52d63a52fb0

Server: components API

view details

Rick Waldron

commit sha 3610bb10fb3cfd46f9bdfbec117afa6a6ff32819

Layout: selected sortable selected

view details

push time in 5 days

push eventbocoup/dcss

Rick Waldron

commit sha 756e1d65a8f45885789b0c48d765ba38f07d1e2e

ScenarioAuthors: fix layout overflow

view details

push time in 5 days

push eventbocoup/dcss

Rick Waldron

commit sha ebfb78721d37dc07a8b8534988a5e067845fbc97

ScenarioAuthors: fix layout overflow

view details

push time in 5 days

push eventbocoup/dcss

Rick Waldron

commit sha dfbd6c0427fdfe91c98ac5bd7455bd22138ca76c

AudioPrompt: include legacy AudioResponse as export alias.

view details

Rick Waldron

commit sha efd14cf194d43283ae98f9944c363d621f6450db

Lint

view details

push time in 5 days

Pull request review commentAirtable/airtable.js

Translate source code to typescript

-'use strict';+ import exponentialBackoffWithJitter from './exponential_backoff_with_jitter';+import objectToQueryParamString from './object_to_query_param_string';+import packageVersion from './package_version';+import fetch from './fetch';+import AbortController from './abort-controller'; -var exponentialBackoffWithJitter = require('./exponential_backoff_with_jitter');-var objectToQueryParamString = require('./object_to_query_param_string');-var packageVersion = require('./package_version');-var fetch = require('./fetch');-var AbortController = require('./abort-controller');--var userAgent = 'Airtable.js/' + packageVersion;+const userAgent = `Airtable.js/${packageVersion}`;  function runAction(base, method, path, queryParams, bodyData, callback, numAttempts) {

Can this be an async function?

mzgoddard

comment created time in 6 days

Pull request review commentAirtable/airtable.js

Translate source code to typescript

+import forEach from 'lodash/forEach';+import get from 'lodash/get';+import assign from 'lodash/assign';+import isPlainObject from 'lodash/isPlainObject';+import fetch from './fetch';+import AbortController from './abort-controller';+import objectToQueryParamString from './object_to_query_param_string';+import AirtableError from './airtable_error';+import Table from './table';+import HttpHeaders from './http_headers';+import runAction from './run_action';+import packageVersion from './package_version';+import exponentialBackoffWithJitter from './exponential_backoff_with_jitter';+import type Airtable from './airtable';++const userAgent = `Airtable.js/${packageVersion}`;++type BaseRequestOptions = {+    method?: string;+    path?: string;+    qs?: any;+    headers?: any;+    body?: any;+    _numAttempts?: number;+};++class Base {+    readonly _airtable: Airtable;+    readonly _id: string;++    constructor(airtable: Airtable, baseId: string) {+        this._airtable = airtable;+        this._id = baseId;+    }++    table(tableName: string) {+        return new Table(this, null, tableName);+    }++    makeRequest(options: BaseRequestOptions) {+        const that = this;++        options = options || {};++        const method = get(options, 'method', 'GET').toUpperCase();++        const url = `${this._airtable._endpointUrl}/v${this._airtable._apiVersionMajor}/${+            this._id+        }${get(options, 'path', '/')}?${objectToQueryParamString(get(options, 'qs', {}))}`;++        const controller = new AbortController();++        const requestOptions: RequestInit = {+            method,+            headers: this._getRequestHeaders(get(options, 'headers', {})),+            signal: controller.signal,+        };++        if ('body' in options && _canRequestMethodIncludeBody(method)) {+            requestOptions.body = JSON.stringify(options.body);+        }++        const timeout = setTimeout(() => {+            controller.abort();+        }, this._airtable.requestTimeout);++        return new Promise((resolve, reject) => {+            fetch(url, requestOptions)+                .then((resp: Response & {statusCode: Response['status']}) => {

This might be an ignorant question: where does Response come from?

mzgoddard

comment created time in 6 days

Pull request review commentAirtable/airtable.js

Translate source code to typescript

+import includes from 'lodash/includes';+import isArray from 'lodash/isArray';

Array.isArray

mzgoddard

comment created time in 6 days

Pull request review commentAirtable/airtable.js

Translate source code to typescript

+import isFunction from 'lodash/isFunction';+import clone from 'lodash/clone';+import forEach from 'lodash/forEach';+import map from 'lodash/map';+import keys from 'lodash/keys';+import Record from './record';+import callbackToPromise from './callback_to_promise';+import has from './has';+import Table from './table';+import {paramValidators, QueryParams} from './query_params';++type PageCallback = (records: Record[], processNextPage: () => void) => void;+type RecordCollectionCallback = (error: any, records?: Record[]) => void;+type DoneCallback = (error: any) => void;++interface RecordCollectionRequestMethod {+    (): Promise<Record[]>;+    (done: RecordCollectionCallback): void;+}++interface RecordPageIteratationMethod {+    (pageCallback: PageCallback): Promise<void>;+    (pageCallback: PageCallback, done: DoneCallback): void;+}++/**+ * Builds a query object. Won't fetch until `firstPage` or+ * or `eachPage` is called.+ *+ * Params should be validated prior to being passed to Query+ * with `Query.validateParams`.+ */+class Query {+    readonly _table: Table;+    readonly _params: QueryParams;++    readonly firstPage: RecordCollectionRequestMethod;+    readonly eachPage: RecordPageIteratationMethod;+    readonly all: RecordCollectionRequestMethod;++    static paramValidators = paramValidators;++    constructor(table: Table, params: QueryParams) {+        this._table = table;+        this._params = params;++        this.firstPage = callbackToPromise(firstPage, this);+        this.eachPage = callbackToPromise(eachPage, this, 1);+        this.all = callbackToPromise(all, this);+    }++    /**+     * Validates the parameters for passing to the Query constructor.+     *+     * @params {object} params parameters to validate+     *+     * @return an object with two keys:+     *  validParams: the object that should be passed to the constructor.+     *  ignoredKeys: a list of keys that will be ignored.+     *  errors: a list of error messages.+     */+    static validateParams(params: any) {+        const validParams: QueryParams = {};+        const ignoredKeys = [];+        const errors = [];++        forEach(keys(params), key => {+            const value = params[key];+            if (has(Query.paramValidators, key)) {+                const validator = Query.paramValidators[key];+                const validationResult = validator(value);+                if (validationResult.pass) {+                    validParams[key] = value;+                } else {+                    errors.push(validationResult.error);+                }+            } else {+                ignoredKeys.push(key);+            }+        });++        return {+            validParams,+            ignoredKeys,+            errors,+        };+    }+}++/**+ * Fetches the first page of results for the query asynchronously,+ * then calls `done(error, records)`.+ */+function firstPage(this: Query, done: RecordCollectionCallback) {+    if (!isFunction(done)) {+        throw new Error('The first parameter to `firstPage` must be a function');+    }++    this.eachPage(+        records => {+            done(null, records);+        },+        error => {+            done(error, null);+        }+    );+}++/**+ * Fetches each page of results for the query asynchronously.+ *+ * Calls `pageCallback(records, fetchNextPage)` for each+ * page. You must call `fetchNextPage()` to fetch the next page of+ * results.+ *+ * After fetching all pages, or if there's an error, calls+ * `done(error)`.+ */+function eachPage(this: Query, pageCallback: PageCallback, done: DoneCallback) {+    if (!isFunction(pageCallback)) {+        throw new Error('The first parameter to `eachPage` must be a function');+    }++    if (!isFunction(done) && done !== void 0) {

This has the same behavior as the parameter I pointed out previously.

mzgoddard

comment created time in 6 days

Pull request review commentAirtable/airtable.js

Translate source code to typescript

+import isFunction from 'lodash/isFunction';+import clone from 'lodash/clone';+import forEach from 'lodash/forEach';+import map from 'lodash/map';+import keys from 'lodash/keys';+import Record from './record';+import callbackToPromise from './callback_to_promise';+import has from './has';+import Table from './table';+import {paramValidators, QueryParams} from './query_params';++type PageCallback = (records: Record[], processNextPage: () => void) => void;+type RecordCollectionCallback = (error: any, records?: Record[]) => void;+type DoneCallback = (error: any) => void;++interface RecordCollectionRequestMethod {+    (): Promise<Record[]>;+    (done: RecordCollectionCallback): void;+}++interface RecordPageIteratationMethod {+    (pageCallback: PageCallback): Promise<void>;+    (pageCallback: PageCallback, done: DoneCallback): void;+}++/**+ * Builds a query object. Won't fetch until `firstPage` or+ * or `eachPage` is called.+ *+ * Params should be validated prior to being passed to Query+ * with `Query.validateParams`.+ */+class Query {+    readonly _table: Table;+    readonly _params: QueryParams;++    readonly firstPage: RecordCollectionRequestMethod;+    readonly eachPage: RecordPageIteratationMethod;+    readonly all: RecordCollectionRequestMethod;++    static paramValidators = paramValidators;++    constructor(table: Table, params: QueryParams) {+        this._table = table;+        this._params = params;++        this.firstPage = callbackToPromise(firstPage, this);+        this.eachPage = callbackToPromise(eachPage, this, 1);+        this.all = callbackToPromise(all, this);+    }++    /**+     * Validates the parameters for passing to the Query constructor.+     *+     * @params {object} params parameters to validate+     *+     * @return an object with two keys:+     *  validParams: the object that should be passed to the constructor.+     *  ignoredKeys: a list of keys that will be ignored.+     *  errors: a list of error messages.+     */+    static validateParams(params: any) {+        const validParams: QueryParams = {};+        const ignoredKeys = [];+        const errors = [];++        forEach(keys(params), key => {+            const value = params[key];+            if (has(Query.paramValidators, key)) {+                const validator = Query.paramValidators[key];+                const validationResult = validator(value);+                if (validationResult.pass) {+                    validParams[key] = value;+                } else {+                    errors.push(validationResult.error);+                }+            } else {+                ignoredKeys.push(key);+            }+        });++        return {+            validParams,+            ignoredKeys,+            errors,+        };+    }+}++/**+ * Fetches the first page of results for the query asynchronously,+ * then calls `done(error, records)`.+ */+function firstPage(this: Query, done: RecordCollectionCallback) {+    if (!isFunction(done)) {+        throw new Error('The first parameter to `firstPage` must be a function');+    }++    this.eachPage(+        records => {+            done(null, records);+        },+        error => {+            done(error, null);+        }+    );+}++/**+ * Fetches each page of results for the query asynchronously.+ *+ * Calls `pageCallback(records, fetchNextPage)` for each+ * page. You must call `fetchNextPage()` to fetch the next page of+ * results.+ *+ * After fetching all pages, or if there's an error, calls+ * `done(error)`.+ */+function eachPage(this: Query, pageCallback: PageCallback, done: DoneCallback) {+    if (!isFunction(pageCallback)) {+        throw new Error('The first parameter to `eachPage` must be a function');+    }++    if (!isFunction(done) && done !== void 0) {+        throw new Error('The second parameter to `eachPage` must be a function or undefined');+    }++    const that = this;

Unnecessary when all occurrences of this are in arrow functions

mzgoddard

comment created time in 6 days

Pull request review commentAirtable/airtable.js

Translate source code to typescript

-'use strict';+ import exponentialBackoffWithJitter from './exponential_backoff_with_jitter';

Space

mzgoddard

comment created time in 6 days

Pull request review commentAirtable/airtable.js

Translate source code to typescript

+import isFunction from 'lodash/isFunction';+import clone from 'lodash/clone';+import forEach from 'lodash/forEach';+import map from 'lodash/map';+import keys from 'lodash/keys';+import Record from './record';+import callbackToPromise from './callback_to_promise';+import has from './has';+import Table from './table';+import {paramValidators, QueryParams} from './query_params';++type PageCallback = (records: Record[], processNextPage: () => void) => void;+type RecordCollectionCallback = (error: any, records?: Record[]) => void;+type DoneCallback = (error: any) => void;++interface RecordCollectionRequestMethod {+    (): Promise<Record[]>;+    (done: RecordCollectionCallback): void;+}++interface RecordPageIteratationMethod {+    (pageCallback: PageCallback): Promise<void>;+    (pageCallback: PageCallback, done: DoneCallback): void;+}++/**+ * Builds a query object. Won't fetch until `firstPage` or+ * or `eachPage` is called.+ *+ * Params should be validated prior to being passed to Query+ * with `Query.validateParams`.+ */+class Query {+    readonly _table: Table;+    readonly _params: QueryParams;++    readonly firstPage: RecordCollectionRequestMethod;+    readonly eachPage: RecordPageIteratationMethod;+    readonly all: RecordCollectionRequestMethod;++    static paramValidators = paramValidators;++    constructor(table: Table, params: QueryParams) {+        this._table = table;+        this._params = params;++        this.firstPage = callbackToPromise(firstPage, this);+        this.eachPage = callbackToPromise(eachPage, this, 1);+        this.all = callbackToPromise(all, this);+    }++    /**+     * Validates the parameters for passing to the Query constructor.+     *+     * @params {object} params parameters to validate+     *+     * @return an object with two keys:+     *  validParams: the object that should be passed to the constructor.+     *  ignoredKeys: a list of keys that will be ignored.+     *  errors: a list of error messages.+     */+    static validateParams(params: any) {+        const validParams: QueryParams = {};+        const ignoredKeys = [];+        const errors = [];++        forEach(keys(params), key => {+            const value = params[key];+            if (has(Query.paramValidators, key)) {+                const validator = Query.paramValidators[key];+                const validationResult = validator(value);+                if (validationResult.pass) {+                    validParams[key] = value;+                } else {+                    errors.push(validationResult.error);+                }+            } else {+                ignoredKeys.push(key);+            }+        });++        return {+            validParams,+            ignoredKeys,+            errors,+        };+    }+}

?

static validateParams(params: any) {
    return Object.entries(params).reduce((accum, [key, value]) => {
        if (has(Query.paramValidators, key)) {
            const validationResult = Query.paramValidators[key](value);
            if (validationResult.pass) {
                accum.validParams[key] = value;
            } else {
                accum.errors.push(validationResult.error);
            }
        } else {
            accum.ignoredKeys.push(key);
        }

        return accum;
    }, {
        vvalidParams: QueryParams = {},
        ignoredKeys: [],
        errors: [],
    })
}
mzgoddard

comment created time in 6 days

Pull request review commentAirtable/airtable.js

Translate source code to typescript

+import internalConfig from './internal_config.json';

Can you just import the two consts here?

mzgoddard

comment created time in 6 days

Pull request review commentAirtable/airtable.js

Translate source code to typescript

+import forEach from 'lodash/forEach';++const isBrowser = typeof window !== 'undefined';++class HttpHeaders {+    _headersByLowercasedKey;++    constructor() {+        this._headersByLowercasedKey = {};+    }++    set(headerKey, headerValue) {+        let lowercasedKey = headerKey.toLowerCase();++        if (lowercasedKey === 'x-airtable-user-agent') {+            lowercasedKey = 'user-agent';+            headerKey = 'User-Agent';+        }++        this._headersByLowercasedKey[lowercasedKey] = {+            headerKey,+            headerValue,+        };+    }++    toJSON() {+        const result = {};+        forEach(this._headersByLowercasedKey, (headerDefinition, lowercasedKey) => {

If you switch to:

Object.entries(this._headersByLowercasedKey).forEach(([lowercasedKey, headerDefinition]) => {

You will eliminate the dependency

mzgoddard

comment created time in 6 days

Pull request review commentAirtable/airtable.js

Translate source code to typescript

+import internalConfig from './internal_config.json';++// "Full Jitter" algorithm taken from https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/+function exponentialBackoffWithJitter(numberOfRetries: number) {+    const rawBackoffTimeMs =+        internalConfig.INITIAL_RETRY_DELAY_IF_RATE_LIMITED * 2 ** numberOfRetries;

Exponentiation operator—be still my beating heart!

mzgoddard

comment created time in 6 days

Pull request review commentAirtable/airtable.js

Translate source code to typescript

+import isFunction from 'lodash/isFunction';+import clone from 'lodash/clone';+import forEach from 'lodash/forEach';+import map from 'lodash/map';+import keys from 'lodash/keys';+import Record from './record';+import callbackToPromise from './callback_to_promise';+import has from './has';+import Table from './table';+import {paramValidators, QueryParams} from './query_params';++type PageCallback = (records: Record[], processNextPage: () => void) => void;+type RecordCollectionCallback = (error: any, records?: Record[]) => void;+type DoneCallback = (error: any) => void;++interface RecordCollectionRequestMethod {+    (): Promise<Record[]>;+    (done: RecordCollectionCallback): void;+}++interface RecordPageIteratationMethod {+    (pageCallback: PageCallback): Promise<void>;+    (pageCallback: PageCallback, done: DoneCallback): void;+}++/**+ * Builds a query object. Won't fetch until `firstPage` or+ * or `eachPage` is called.+ *+ * Params should be validated prior to being passed to Query+ * with `Query.validateParams`.+ */+class Query {+    readonly _table: Table;+    readonly _params: QueryParams;++    readonly firstPage: RecordCollectionRequestMethod;+    readonly eachPage: RecordPageIteratationMethod;+    readonly all: RecordCollectionRequestMethod;++    static paramValidators = paramValidators;++    constructor(table: Table, params: QueryParams) {+        this._table = table;+        this._params = params;++        this.firstPage = callbackToPromise(firstPage, this);+        this.eachPage = callbackToPromise(eachPage, this, 1);+        this.all = callbackToPromise(all, this);+    }++    /**+     * Validates the parameters for passing to the Query constructor.+     *+     * @params {object} params parameters to validate+     *+     * @return an object with two keys:+     *  validParams: the object that should be passed to the constructor.+     *  ignoredKeys: a list of keys that will be ignored.+     *  errors: a list of error messages.+     */+    static validateParams(params: any) {+        const validParams: QueryParams = {};+        const ignoredKeys = [];+        const errors = [];++        forEach(keys(params), key => {+            const value = params[key];
        forEach(keys(params), key => {
            const value = params[key];

Is just

Object.entries(params).forEach(([key, value]) => 
mzgoddard

comment created time in 6 days

Pull request review commentAirtable/airtable.js

Translate source code to typescript

 var didWarnForDeprecation = {};  * @return a wrapped function  */ function deprecate(fn, key, message) {-    return function() {+    return function(...args) {         if (!didWarnForDeprecation[key]) {             didWarnForDeprecation[key] = true;             console.warn(message);         }-        fn.apply(this, arguments);+        fn.apply(this, args);

What is this expected to be in here? I would just replace this line with fn(...args); and let the call function have this of its own.

mzgoddard

comment created time in 6 days

Pull request review commentAirtable/airtable.js

Translate source code to typescript

+import forEach from 'lodash/forEach';+import get from 'lodash/get';+import assign from 'lodash/assign';

Is this just Object.assign?

mzgoddard

comment created time in 6 days

Pull request review commentAirtable/airtable.js

Translate source code to typescript

+// istanbul ignore file+let AbortController: new () => AbortController;+if (typeof window === 'undefined') {+    AbortController = require('abort-controller');+} else {+    if ('signal' in new Request('')) {+        AbortController = window.AbortController;+    } else {+        const polyfill = require('abortcontroller-polyfill/dist/cjs-ponyfill');+        AbortController = polyfill.AbortController;+    }+}++export = AbortController;

Does all of this totally break if you use regular export ...?

mzgoddard

comment created time in 6 days

Pull request review commentAirtable/airtable.js

Translate source code to typescript

+import forEach from 'lodash/forEach';+import get from 'lodash/get';+import assign from 'lodash/assign';+import isPlainObject from 'lodash/isPlainObject';+import fetch from './fetch';+import AbortController from './abort-controller';+import objectToQueryParamString from './object_to_query_param_string';+import AirtableError from './airtable_error';+import Table from './table';+import HttpHeaders from './http_headers';+import runAction from './run_action';+import packageVersion from './package_version';+import exponentialBackoffWithJitter from './exponential_backoff_with_jitter';+import type Airtable from './airtable';++const userAgent = `Airtable.js/${packageVersion}`;++type BaseRequestOptions = {+    method?: string;+    path?: string;+    qs?: any;+    headers?: any;+    body?: any;+    _numAttempts?: number;+};++class Base {+    readonly _airtable: Airtable;+    readonly _id: string;++    constructor(airtable: Airtable, baseId: string) {+        this._airtable = airtable;+        this._id = baseId;+    }++    table(tableName: string) {+        return new Table(this, null, tableName);+    }++    makeRequest(options: BaseRequestOptions) {+        const that = this;++        options = options || {};++        const method = get(options, 'method', 'GET').toUpperCase();++        const url = `${this._airtable._endpointUrl}/v${this._airtable._apiVersionMajor}/${+            this._id+        }${get(options, 'path', '/')}?${objectToQueryParamString(get(options, 'qs', {}))}`;++        const controller = new AbortController();++        const requestOptions: RequestInit = {+            method,+            headers: this._getRequestHeaders(get(options, 'headers', {})),+            signal: controller.signal,+        };++        if ('body' in options && _canRequestMethodIncludeBody(method)) {+            requestOptions.body = JSON.stringify(options.body);+        }++        const timeout = setTimeout(() => {+            controller.abort();+        }, this._airtable.requestTimeout);++        return new Promise((resolve, reject) => {+            fetch(url, requestOptions)+                .then((resp: Response & {statusCode: Response['status']}) => {+                    clearTimeout(timeout);+                    resp.statusCode = resp.status;+                    if (resp.status === 429 && !that._airtable._noRetryIfRateLimited) {+                        const numAttempts = get(options, '_numAttempts', 0);+                        const backoffDelayMs = exponentialBackoffWithJitter(numAttempts);+                        setTimeout(() => {+                            const newOptions = assign({}, options, {+                                _numAttempts: numAttempts + 1,+                            });+                            that.makeRequest(newOptions)+                                .then(resolve)+                                .catch(reject);+                        }, backoffDelayMs);+                    } else {+                        resp.json()+                            .then(body => {+                                const err =+                                    that._checkStatusForError(resp.status, body) ||+                                    _getErrorForNonObjectBody(resp.status, body);++                                if (err) {+                                    reject(err);+                                } else {+                                    resolve({+                                        statusCode: resp.status,+                                        headers: resp.headers,+                                        body,+                                    });+                                }+                            })+                            .catch(() => {+                                const err = _getErrorForNonObjectBody(resp.status);+                                reject(err);+                            });+                    }+                })+                .catch(err => {+                    clearTimeout(timeout);+                    err = new AirtableError('CONNECTION_ERROR', err.message, null);+                    reject(err);+                });+        });+    }++    /**+     * @deprecated This method is deprecated.+     */+    runAction(method, path, queryParams, bodyData, callback) {+        runAction(this, method, path, queryParams, bodyData, callback, 0);+    }++    _getRequestHeaders(headers) {+        const result = new HttpHeaders();++        result.set('Authorization', `Bearer ${this._airtable._apiKey}`);+        result.set('User-Agent', userAgent);+        result.set('Content-Type', 'application/json');+        forEach(headers, (headerValue, headerKey) => {+            result.set(headerKey, headerValue);+        });++        return result.toJSON();+    }++    _checkStatusForError(statusCode, body) {+        if (statusCode === 401) {+            return new AirtableError(+                'AUTHENTICATION_REQUIRED',+                'You should provide valid api key to perform this operation',+                statusCode+            );+        } else if (statusCode === 403) {+            return new AirtableError(+                'NOT_AUTHORIZED',+                'You are not authorized to perform this operation',+                statusCode+            );+        } else if (statusCode === 404) {+            return (() => {+                const message =+                    body && body.error && body.error.message+                        ? body.error.message+                        : 'Could not find what you are looking for';+                return new AirtableError('NOT_FOUND', message, statusCode);+            })();

I don't understand what the IIAFE is for?

mzgoddard

comment created time in 6 days

Pull request review commentAirtable/airtable.js

Translate source code to typescript

+import forEach from 'lodash/forEach';+import get from 'lodash/get';+import assign from 'lodash/assign';+import isPlainObject from 'lodash/isPlainObject';+import fetch from './fetch';+import AbortController from './abort-controller';+import objectToQueryParamString from './object_to_query_param_string';+import AirtableError from './airtable_error';+import Table from './table';+import HttpHeaders from './http_headers';+import runAction from './run_action';+import packageVersion from './package_version';+import exponentialBackoffWithJitter from './exponential_backoff_with_jitter';+import type Airtable from './airtable';++const userAgent = `Airtable.js/${packageVersion}`;++type BaseRequestOptions = {+    method?: string;+    path?: string;+    qs?: any;+    headers?: any;+    body?: any;+    _numAttempts?: number;+};++class Base {+    readonly _airtable: Airtable;+    readonly _id: string;++    constructor(airtable: Airtable, baseId: string) {+        this._airtable = airtable;+        this._id = baseId;+    }++    table(tableName: string) {+        return new Table(this, null, tableName);+    }++    makeRequest(options: BaseRequestOptions) {+        const that = this;++        options = options || {};++        const method = get(options, 'method', 'GET').toUpperCase();++        const url = `${this._airtable._endpointUrl}/v${this._airtable._apiVersionMajor}/${+            this._id+        }${get(options, 'path', '/')}?${objectToQueryParamString(get(options, 'qs', {}))}`;++        const controller = new AbortController();++        const requestOptions: RequestInit = {+            method,+            headers: this._getRequestHeaders(get(options, 'headers', {})),+            signal: controller.signal,+        };++        if ('body' in options && _canRequestMethodIncludeBody(method)) {+            requestOptions.body = JSON.stringify(options.body);+        }++        const timeout = setTimeout(() => {+            controller.abort();+        }, this._airtable.requestTimeout);++        return new Promise((resolve, reject) => {+            fetch(url, requestOptions)+                .then((resp: Response & {statusCode: Response['status']}) => {+                    clearTimeout(timeout);+                    resp.statusCode = resp.status;+                    if (resp.status === 429 && !that._airtable._noRetryIfRateLimited) {+                        const numAttempts = get(options, '_numAttempts', 0);+                        const backoffDelayMs = exponentialBackoffWithJitter(numAttempts);+                        setTimeout(() => {+                            const newOptions = assign({}, options, {+                                _numAttempts: numAttempts + 1,+                            });+                            that.makeRequest(newOptions)+                                .then(resolve)+                                .catch(reject);+                        }, backoffDelayMs);+                    } else {+                        resp.json()+                            .then(body => {+                                const err =+                                    that._checkStatusForError(resp.status, body) ||+                                    _getErrorForNonObjectBody(resp.status, body);++                                if (err) {+                                    reject(err);+                                } else {+                                    resolve({+                                        statusCode: resp.status,+                                        headers: resp.headers,+                                        body,+                                    });+                                }+                            })+                            .catch(() => {+                                const err = _getErrorForNonObjectBody(resp.status);+                                reject(err);+                            });+                    }+                })+                .catch(err => {+                    clearTimeout(timeout);+                    err = new AirtableError('CONNECTION_ERROR', err.message, null);+                    reject(err);+                });+        });+    }++    /**+     * @deprecated This method is deprecated.+     */+    runAction(method, path, queryParams, bodyData, callback) {+        runAction(this, method, path, queryParams, bodyData, callback, 0);+    }++    _getRequestHeaders(headers) {+        const result = new HttpHeaders();++        result.set('Authorization', `Bearer ${this._airtable._apiKey}`);+        result.set('User-Agent', userAgent);+        result.set('Content-Type', 'application/json');+        forEach(headers, (headerValue, headerKey) => {+            result.set(headerKey, headerValue);+        });++        return result.toJSON();+    }++    _checkStatusForError(statusCode, body) {+        if (statusCode === 401) {+            return new AirtableError(+                'AUTHENTICATION_REQUIRED',+                'You should provide valid api key to perform this operation',+                statusCode+            );+        } else if (statusCode === 403) {+            return new AirtableError(+                'NOT_AUTHORIZED',+                'You are not authorized to perform this operation',+                statusCode+            );+        } else if (statusCode === 404) {+            return (() => {+                const message =+                    body && body.error && body.error.message+                        ? body.error.message+                        : 'Could not find what you are looking for';+                return new AirtableError('NOT_FOUND', message, statusCode);+            })();+        } else if (statusCode === 413) {+            return new AirtableError('REQUEST_TOO_LARGE', 'Request body is too large', statusCode);+        } else if (statusCode === 422) {+            return (() => {+                const type =+                    body && body.error && body.error.type+                        ? body.error.type+                        : 'UNPROCESSABLE_ENTITY';+                const message =+                    body && body.error && body.error.message+                        ? body.error.message+                        : 'The operation cannot be processed';+                return new AirtableError(type, message, statusCode);+            })();+        } else if (statusCode === 429) {+            return new AirtableError(+                'TOO_MANY_REQUESTS',+                'You have made too many requests in a short period of time. Please retry your request later',+                statusCode+            );+        } else if (statusCode === 500) {+            return new AirtableError(+                'SERVER_ERROR',+                'Try again. If the problem persists, contact support.',+                statusCode+            );+        } else if (statusCode === 503) {+            return new AirtableError(+                'SERVICE_UNAVAILABLE',+                'The service is temporarily unavailable. Please retry shortly.',+                statusCode+            );+        } else if (statusCode >= 400) {+            return (() => {+                const type =+                    body && body.error && body.error.type ? body.error.type : 'UNEXPECTED_ERROR';+                const message =+                    body && body.error && body.error.message+                        ? body.error.message+                        : 'An unexpected error occurred';+                return new AirtableError(type, message, statusCode);+            })();+        } else {+            return null;+        }+    }++    doCall(tableName) {+        return this.table(tableName);+    }++    getId() {+        return this._id;+    }++    static createFunctor(airtable: Airtable, baseId: string) {+        const base = new Base(airtable, baseId);+        const baseFn = tableName => {+            return base.doCall(tableName);+        };+        forEach(['table', 'makeRequest', 'runAction', 'getId'], baseMethod => {+            baseFn[baseMethod] = base[baseMethod].bind(base);+        });+        baseFn._base = base;+        baseFn.tables = base['tables'];+        return baseFn;

?

const base = new Base(airtable, baseId);
const baseFn = tableName => base.doCall(tableName);
baseFn._base = base;
baseFn.tables = base.tables;
return ['table', 'makeRequest', 'runAction', 'getId'].reduce((baseFn, method) => {
    baseFn[method] = base[method].bind(base);
    return baseFn;
}, baseFn);

(If you dig it, and the other suggestion, then you'll eliminate forEach as a dependency)

mzgoddard

comment created time in 6 days

Pull request review commentAirtable/airtable.js

Translate source code to typescript

-'use strict';--var isArray = require('lodash/isArray');-var forEach = require('lodash/forEach');-var isNil = require('lodash/isNil');+import isArray from 'lodash/isArray';

0_o Is this code expected to run in pre-ES5 environments?

mzgoddard

comment created time in 6 days

Pull request review commentAirtable/airtable.js

Translate source code to typescript

-'use strict';- /**  * Given a function fn that takes a callback as its last argument, returns  * a new version of the function that takes the callback optionally. If  * the function is not called with a callback for the last argument, the  * function will return a promise instead.  */-function callbackToPromise(fn, context, callbackArgIndex) {+function callbackToPromise(fn, context, callbackArgIndex = void 0): any {

This is just setting callbackArgIndex to undefined, if callbackArgIndex is not present or undefined... which is what it would be set to by default.

Instead, you can do something like...

const MISSING = Symbol('MISSING');
function callbackToPromise(fn, context, callbackArgIndex = MISSING) {
    return function() {
        let thisCallbackArgIndex;
        if (callbackArgIndex === MISSING) {

mzgoddard

comment created time in 6 days

Pull request review commentAirtable/airtable.js

Translate source code to typescript

+import forEach from 'lodash/forEach';+import get from 'lodash/get';+import assign from 'lodash/assign';+import isPlainObject from 'lodash/isPlainObject';+import fetch from './fetch';+import AbortController from './abort-controller';+import objectToQueryParamString from './object_to_query_param_string';+import AirtableError from './airtable_error';+import Table from './table';+import HttpHeaders from './http_headers';+import runAction from './run_action';+import packageVersion from './package_version';+import exponentialBackoffWithJitter from './exponential_backoff_with_jitter';+import type Airtable from './airtable';++const userAgent = `Airtable.js/${packageVersion}`;++type BaseRequestOptions = {+    method?: string;+    path?: string;+    qs?: any;+    headers?: any;+    body?: any;+    _numAttempts?: number;+};++class Base {+    readonly _airtable: Airtable;+    readonly _id: string;++    constructor(airtable: Airtable, baseId: string) {+        this._airtable = airtable;+        this._id = baseId;+    }++    table(tableName: string) {+        return new Table(this, null, tableName);+    }++    makeRequest(options: BaseRequestOptions) {+        const that = this;++        options = options || {};++        const method = get(options, 'method', 'GET').toUpperCase();++        const url = `${this._airtable._endpointUrl}/v${this._airtable._apiVersionMajor}/${+            this._id+        }${get(options, 'path', '/')}?${objectToQueryParamString(get(options, 'qs', {}))}`;++        const controller = new AbortController();++        const requestOptions: RequestInit = {+            method,+            headers: this._getRequestHeaders(get(options, 'headers', {})),+            signal: controller.signal,+        };++        if ('body' in options && _canRequestMethodIncludeBody(method)) {+            requestOptions.body = JSON.stringify(options.body);+        }++        const timeout = setTimeout(() => {+            controller.abort();+        }, this._airtable.requestTimeout);++        return new Promise((resolve, reject) => {+            fetch(url, requestOptions)+                .then((resp: Response & {statusCode: Response['status']}) => {+                    clearTimeout(timeout);+                    resp.statusCode = resp.status;+                    if (resp.status === 429 && !that._airtable._noRetryIfRateLimited) {+                        const numAttempts = get(options, '_numAttempts', 0);+                        const backoffDelayMs = exponentialBackoffWithJitter(numAttempts);+                        setTimeout(() => {+                            const newOptions = assign({}, options, {+                                _numAttempts: numAttempts + 1,+                            });+                            that.makeRequest(newOptions)+                                .then(resolve)+                                .catch(reject);+                        }, backoffDelayMs);+                    } else {+                        resp.json()+                            .then(body => {+                                const err =+                                    that._checkStatusForError(resp.status, body) ||+                                    _getErrorForNonObjectBody(resp.status, body);++                                if (err) {+                                    reject(err);+                                } else {+                                    resolve({+                                        statusCode: resp.status,+                                        headers: resp.headers,+                                        body,+                                    });+                                }+                            })+                            .catch(() => {+                                const err = _getErrorForNonObjectBody(resp.status);+                                reject(err);+                            });+                    }+                })+                .catch(err => {+                    clearTimeout(timeout);+                    err = new AirtableError('CONNECTION_ERROR', err.message, null);+                    reject(err);+                });+        });+    }++    /**+     * @deprecated This method is deprecated.+     */+    runAction(method, path, queryParams, bodyData, callback) {+        runAction(this, method, path, queryParams, bodyData, callback, 0);+    }++    _getRequestHeaders(headers) {+        const result = new HttpHeaders();++        result.set('Authorization', `Bearer ${this._airtable._apiKey}`);+        result.set('User-Agent', userAgent);+        result.set('Content-Type', 'application/json');+        forEach(headers, (headerValue, headerKey) => {+            result.set(headerKey, headerValue);+        });

?

Object.entries(headers).forEach(([key, value]) => result.set(key, value));
mzgoddard

comment created time in 6 days

Pull request review commentAirtable/airtable.js

Translate source code to typescript

+import forEach from 'lodash/forEach';+import get from 'lodash/get';+import assign from 'lodash/assign';+import isPlainObject from 'lodash/isPlainObject';+import fetch from './fetch';+import AbortController from './abort-controller';+import objectToQueryParamString from './object_to_query_param_string';+import AirtableError from './airtable_error';+import Table from './table';+import HttpHeaders from './http_headers';+import runAction from './run_action';+import packageVersion from './package_version';+import exponentialBackoffWithJitter from './exponential_backoff_with_jitter';+import type Airtable from './airtable';++const userAgent = `Airtable.js/${packageVersion}`;++type BaseRequestOptions = {+    method?: string;+    path?: string;+    qs?: any;+    headers?: any;+    body?: any;+    _numAttempts?: number;+};++class Base {+    readonly _airtable: Airtable;+    readonly _id: string;++    constructor(airtable: Airtable, baseId: string) {+        this._airtable = airtable;+        this._id = baseId;+    }++    table(tableName: string) {+        return new Table(this, null, tableName);+    }++    makeRequest(options: BaseRequestOptions) {+        const that = this;++        options = options || {};++        const method = get(options, 'method', 'GET').toUpperCase();++        const url = `${this._airtable._endpointUrl}/v${this._airtable._apiVersionMajor}/${+            this._id+        }${get(options, 'path', '/')}?${objectToQueryParamString(get(options, 'qs', {}))}`;++        const controller = new AbortController();++        const requestOptions: RequestInit = {+            method,+            headers: this._getRequestHeaders(get(options, 'headers', {})),+            signal: controller.signal,+        };++        if ('body' in options && _canRequestMethodIncludeBody(method)) {+            requestOptions.body = JSON.stringify(options.body);+        }++        const timeout = setTimeout(() => {+            controller.abort();+        }, this._airtable.requestTimeout);++        return new Promise((resolve, reject) => {+            fetch(url, requestOptions)+                .then((resp: Response & {statusCode: Response['status']}) => {+                    clearTimeout(timeout);+                    resp.statusCode = resp.status;+                    if (resp.status === 429 && !that._airtable._noRetryIfRateLimited) {+                        const numAttempts = get(options, '_numAttempts', 0);+                        const backoffDelayMs = exponentialBackoffWithJitter(numAttempts);+                        setTimeout(() => {+                            const newOptions = assign({}, options, {+                                _numAttempts: numAttempts + 1,+                            });+                            that.makeRequest(newOptions)+                                .then(resolve)+                                .catch(reject);+                        }, backoffDelayMs);+                    } else {+                        resp.json()+                            .then(body => {+                                const err =+                                    that._checkStatusForError(resp.status, body) ||+                                    _getErrorForNonObjectBody(resp.status, body);++                                if (err) {+                                    reject(err);+                                } else {+                                    resolve({+                                        statusCode: resp.status,+                                        headers: resp.headers,+                                        body,+                                    });+                                }+                            })+                            .catch(() => {+                                const err = _getErrorForNonObjectBody(resp.status);+                                reject(err);+                            });+                    }+                })+                .catch(err => {+                    clearTimeout(timeout);+                    err = new AirtableError('CONNECTION_ERROR', err.message, null);+                    reject(err);+                });+        });+    }++    /**+     * @deprecated This method is deprecated.+     */+    runAction(method, path, queryParams, bodyData, callback) {+        runAction(this, method, path, queryParams, bodyData, callback, 0);+    }++    _getRequestHeaders(headers) {+        const result = new HttpHeaders();++        result.set('Authorization', `Bearer ${this._airtable._apiKey}`);+        result.set('User-Agent', userAgent);+        result.set('Content-Type', 'application/json');+        forEach(headers, (headerValue, headerKey) => {+            result.set(headerKey, headerValue);+        });++        return result.toJSON();+    }++    _checkStatusForError(statusCode, body) {+        if (statusCode === 401) {+            return new AirtableError(+                'AUTHENTICATION_REQUIRED',+                'You should provide valid api key to perform this operation',+                statusCode+            );+        } else if (statusCode === 403) {+            return new AirtableError(+                'NOT_AUTHORIZED',+                'You are not authorized to perform this operation',+                statusCode+            );+        } else if (statusCode === 404) {+            return (() => {+                const message =+                    body && body.error && body.error.message+                        ? body.error.message+                        : 'Could not find what you are looking for';+                return new AirtableError('NOT_FOUND', message, statusCode);+            })();+        } else if (statusCode === 413) {+            return new AirtableError('REQUEST_TOO_LARGE', 'Request body is too large', statusCode);+        } else if (statusCode === 422) {+            return (() => {+                const type =+                    body && body.error && body.error.type+                        ? body.error.type+                        : 'UNPROCESSABLE_ENTITY';+                const message =+                    body && body.error && body.error.message+                        ? body.error.message+                        : 'The operation cannot be processed';+                return new AirtableError(type, message, statusCode);+            })();

Same question here, re: IIAFE

mzgoddard

comment created time in 6 days

Pull request review commentAirtable/airtable.js

Translate source code to typescript

+import forEach from 'lodash/forEach';+import get from 'lodash/get';+import assign from 'lodash/assign';+import isPlainObject from 'lodash/isPlainObject';+import fetch from './fetch';+import AbortController from './abort-controller';+import objectToQueryParamString from './object_to_query_param_string';+import AirtableError from './airtable_error';+import Table from './table';+import HttpHeaders from './http_headers';+import runAction from './run_action';+import packageVersion from './package_version';+import exponentialBackoffWithJitter from './exponential_backoff_with_jitter';+import type Airtable from './airtable';++const userAgent = `Airtable.js/${packageVersion}`;++type BaseRequestOptions = {+    method?: string;+    path?: string;+    qs?: any;+    headers?: any;+    body?: any;+    _numAttempts?: number;+};++class Base {+    readonly _airtable: Airtable;+    readonly _id: string;++    constructor(airtable: Airtable, baseId: string) {+        this._airtable = airtable;+        this._id = baseId;+    }++    table(tableName: string) {+        return new Table(this, null, tableName);+    }++    makeRequest(options: BaseRequestOptions) {+        const that = this;++        options = options || {};++        const method = get(options, 'method', 'GET').toUpperCase();++        const url = `${this._airtable._endpointUrl}/v${this._airtable._apiVersionMajor}/${+            this._id+        }${get(options, 'path', '/')}?${objectToQueryParamString(get(options, 'qs', {}))}`;++        const controller = new AbortController();++        const requestOptions: RequestInit = {+            method,+            headers: this._getRequestHeaders(get(options, 'headers', {})),+            signal: controller.signal,+        };++        if ('body' in options && _canRequestMethodIncludeBody(method)) {+            requestOptions.body = JSON.stringify(options.body);+        }++        const timeout = setTimeout(() => {+            controller.abort();+        }, this._airtable.requestTimeout);++        return new Promise((resolve, reject) => {+            fetch(url, requestOptions)+                .then((resp: Response & {statusCode: Response['status']}) => {+                    clearTimeout(timeout);+                    resp.statusCode = resp.status;+                    if (resp.status === 429 && !that._airtable._noRetryIfRateLimited) {+                        const numAttempts = get(options, '_numAttempts', 0);+                        const backoffDelayMs = exponentialBackoffWithJitter(numAttempts);+                        setTimeout(() => {+                            const newOptions = assign({}, options, {+                                _numAttempts: numAttempts + 1,+                            });+                            that.makeRequest(newOptions)+                                .then(resolve)+                                .catch(reject);+                        }, backoffDelayMs);+                    } else {+                        resp.json()+                            .then(body => {+                                const err =+                                    that._checkStatusForError(resp.status, body) ||+                                    _getErrorForNonObjectBody(resp.status, body);++                                if (err) {+                                    reject(err);+                                } else {+                                    resolve({+                                        statusCode: resp.status,+                                        headers: resp.headers,+                                        body,+                                    });+                                }+                            })+                            .catch(() => {+                                const err = _getErrorForNonObjectBody(resp.status);+                                reject(err);+                            });+                    }+                })+                .catch(err => {+                    clearTimeout(timeout);+                    err = new AirtableError('CONNECTION_ERROR', err.message, null);+                    reject(err);+                });+        });+    }++    /**+     * @deprecated This method is deprecated.+     */+    runAction(method, path, queryParams, bodyData, callback) {+        runAction(this, method, path, queryParams, bodyData, callback, 0);+    }++    _getRequestHeaders(headers) {+        const result = new HttpHeaders();++        result.set('Authorization', `Bearer ${this._airtable._apiKey}`);+        result.set('User-Agent', userAgent);+        result.set('Content-Type', 'application/json');+        forEach(headers, (headerValue, headerKey) => {+            result.set(headerKey, headerValue);+        });++        return result.toJSON();+    }++    _checkStatusForError(statusCode, body) {+        if (statusCode === 401) {+            return new AirtableError(+                'AUTHENTICATION_REQUIRED',+                'You should provide valid api key to perform this operation',+                statusCode+            );+        } else if (statusCode === 403) {+            return new AirtableError(+                'NOT_AUTHORIZED',+                'You are not authorized to perform this operation',+                statusCode+            );+        } else if (statusCode === 404) {+            return (() => {+                const message =+                    body && body.error && body.error.message+                        ? body.error.message+                        : 'Could not find what you are looking for';+                return new AirtableError('NOT_FOUND', message, statusCode);+            })();+        } else if (statusCode === 413) {+            return new AirtableError('REQUEST_TOO_LARGE', 'Request body is too large', statusCode);+        } else if (statusCode === 422) {+            return (() => {+                const type =+                    body && body.error && body.error.type+                        ? body.error.type+                        : 'UNPROCESSABLE_ENTITY';+                const message =+                    body && body.error && body.error.message+                        ? body.error.message+                        : 'The operation cannot be processed';+                return new AirtableError(type, message, statusCode);+            })();+        } else if (statusCode === 429) {+            return new AirtableError(+                'TOO_MANY_REQUESTS',+                'You have made too many requests in a short period of time. Please retry your request later',+                statusCode+            );+        } else if (statusCode === 500) {+            return new AirtableError(+                'SERVER_ERROR',+                'Try again. If the problem persists, contact support.',+                statusCode+            );+        } else if (statusCode === 503) {+            return new AirtableError(+                'SERVICE_UNAVAILABLE',+                'The service is temporarily unavailable. Please retry shortly.',+                statusCode+            );+        } else if (statusCode >= 400) {+            return (() => {+                const type =+                    body && body.error && body.error.type ? body.error.type : 'UNEXPECTED_ERROR';+                const message =+                    body && body.error && body.error.message+                        ? body.error.message+                        : 'An unexpected error occurred';+                return new AirtableError(type, message, statusCode);+            })();

And here

mzgoddard

comment created time in 6 days

Pull request review commentAirtable/airtable.js

Translate source code to typescript

+import forEach from 'lodash/forEach';+import get from 'lodash/get';+import assign from 'lodash/assign';+import isPlainObject from 'lodash/isPlainObject';+import fetch from './fetch';+import AbortController from './abort-controller';+import objectToQueryParamString from './object_to_query_param_string';+import AirtableError from './airtable_error';+import Table from './table';+import HttpHeaders from './http_headers';+import runAction from './run_action';+import packageVersion from './package_version';+import exponentialBackoffWithJitter from './exponential_backoff_with_jitter';+import type Airtable from './airtable';++const userAgent = `Airtable.js/${packageVersion}`;++type BaseRequestOptions = {+    method?: string;+    path?: string;+    qs?: any;+    headers?: any;+    body?: any;+    _numAttempts?: number;+};++class Base {+    readonly _airtable: Airtable;+    readonly _id: string;++    constructor(airtable: Airtable, baseId: string) {+        this._airtable = airtable;+        this._id = baseId;+    }++    table(tableName: string) {+        return new Table(this, null, tableName);+    }++    makeRequest(options: BaseRequestOptions) {+        const that = this;++        options = options || {};++        const method = get(options, 'method', 'GET').toUpperCase();++        const url = `${this._airtable._endpointUrl}/v${this._airtable._apiVersionMajor}/${+            this._id+        }${get(options, 'path', '/')}?${objectToQueryParamString(get(options, 'qs', {}))}`;++        const controller = new AbortController();++        const requestOptions: RequestInit = {+            method,+            headers: this._getRequestHeaders(get(options, 'headers', {})),+            signal: controller.signal,+        };++        if ('body' in options && _canRequestMethodIncludeBody(method)) {+            requestOptions.body = JSON.stringify(options.body);+        }++        const timeout = setTimeout(() => {+            controller.abort();+        }, this._airtable.requestTimeout);++        return new Promise((resolve, reject) => {+            fetch(url, requestOptions)+                .then((resp: Response & {statusCode: Response['status']}) => {+                    clearTimeout(timeout);+                    resp.statusCode = resp.status;+                    if (resp.status === 429 && !that._airtable._noRetryIfRateLimited) {+                        const numAttempts = get(options, '_numAttempts', 0);+                        const backoffDelayMs = exponentialBackoffWithJitter(numAttempts);+                        setTimeout(() => {+                            const newOptions = assign({}, options, {+                                _numAttempts: numAttempts + 1,+                            });+                            that.makeRequest(newOptions)+                                .then(resolve)+                                .catch(reject);+                        }, backoffDelayMs);+                    } else {+                        resp.json()+                            .then(body => {+                                const err =+                                    that._checkStatusForError(resp.status, body) ||+                                    _getErrorForNonObjectBody(resp.status, body);++                                if (err) {+                                    reject(err);+                                } else {+                                    resolve({+                                        statusCode: resp.status,+                                        headers: resp.headers,+                                        body,+                                    });+                                }+                            })+                            .catch(() => {+                                const err = _getErrorForNonObjectBody(resp.status);+                                reject(err);+                            });+                    }+                })+                .catch(err => {+                    clearTimeout(timeout);+                    err = new AirtableError('CONNECTION_ERROR', err.message, null);+                    reject(err);+                });+        });

Wondering what it would look like to make this all async/await and I just sketched this out: (obviously I have no idea if this actually works correctly)

try {
    const resp: Response & {statusCode: Response['status']} = await fetch(url, requestOptions);

    clearTimeout(timeout);

    resp.statusCode = resp.status;

    if (resp.status === 429 && !that._airtable._noRetryIfRateLimited) {
        const numAttempts = get(options, '_numAttempts', 0);
        const backoffDelayMs = exponentialBackoffWithJitter(numAttempts);

        await new Promise(resolve => {
          setTimeout(resolve, backoffDelayMs)
        });
        
        const newOptions = assign({}, options, {
            _numAttempts: numAttempts + 1,
        });
        return this.makeRequest(newOptions);
    } else {

        const body = await resp.json();
        const err = this._checkStatusForError(resp.status, body) ||
            _getErrorForNonObjectBody(resp.status, body);

        if (err) {
            throw err;
        } else {
            return {
                statusCode: resp.status,
                headers: resp.headers,
                body,
            };
        }

        // Not quite sure what to do with this part?
        // .catch(() => {
        //    const err = _getErrorForNonObjectBody(resp.status);
        //    reject(err);
        // });
    }
} catch (error) {
    clearTimeout(timeout);
    throw new AirtableError('CONNECTION_ERROR', error.message, null);
}
mzgoddard

comment created time in 6 days

Pull request review commentAirtable/airtable.js

Translate source code to typescript

+import forEach from 'lodash/forEach';+import get from 'lodash/get';+import assign from 'lodash/assign';+import isPlainObject from 'lodash/isPlainObject';+import fetch from './fetch';+import AbortController from './abort-controller';+import objectToQueryParamString from './object_to_query_param_string';+import AirtableError from './airtable_error';+import Table from './table';+import HttpHeaders from './http_headers';+import runAction from './run_action';+import packageVersion from './package_version';+import exponentialBackoffWithJitter from './exponential_backoff_with_jitter';+import type Airtable from './airtable';++const userAgent = `Airtable.js/${packageVersion}`;++type BaseRequestOptions = {+    method?: string;+    path?: string;+    qs?: any;+    headers?: any;+    body?: any;+    _numAttempts?: number;+};++class Base {+    readonly _airtable: Airtable;+    readonly _id: string;++    constructor(airtable: Airtable, baseId: string) {+        this._airtable = airtable;+        this._id = baseId;+    }++    table(tableName: string) {+        return new Table(this, null, tableName);+    }++    makeRequest(options: BaseRequestOptions) {+        const that = this;

Unnecessary, since all of your this sensitive code is in the same scope, or an arrow function body that has this same scope.

mzgoddard

comment created time in 6 days

Pull request review commentAirtable/airtable.js

Translate source code to typescript

+import forEach from 'lodash/forEach';+import get from 'lodash/get';+import assign from 'lodash/assign';+import isPlainObject from 'lodash/isPlainObject';+import fetch from './fetch';+import AbortController from './abort-controller';+import objectToQueryParamString from './object_to_query_param_string';+import AirtableError from './airtable_error';+import Table from './table';+import HttpHeaders from './http_headers';+import runAction from './run_action';+import packageVersion from './package_version';+import exponentialBackoffWithJitter from './exponential_backoff_with_jitter';+import type Airtable from './airtable';++const userAgent = `Airtable.js/${packageVersion}`;++type BaseRequestOptions = {+    method?: string;+    path?: string;+    qs?: any;+    headers?: any;+    body?: any;+    _numAttempts?: number;+};++class Base {+    readonly _airtable: Airtable;+    readonly _id: string;++    constructor(airtable: Airtable, baseId: string) {+        this._airtable = airtable;+        this._id = baseId;+    }++    table(tableName: string) {+        return new Table(this, null, tableName);+    }++    makeRequest(options: BaseRequestOptions) {+        const that = this;++        options = options || {};++        const method = get(options, 'method', 'GET').toUpperCase();++        const url = `${this._airtable._endpointUrl}/v${this._airtable._apiVersionMajor}/${+            this._id+        }${get(options, 'path', '/')}?${objectToQueryParamString(get(options, 'qs', {}))}`;++        const controller = new AbortController();++        const requestOptions: RequestInit = {+            method,+            headers: this._getRequestHeaders(get(options, 'headers', {})),+            signal: controller.signal,+        };++        if ('body' in options && _canRequestMethodIncludeBody(method)) {+            requestOptions.body = JSON.stringify(options.body);+        }++        const timeout = setTimeout(() => {+            controller.abort();+        }, this._airtable.requestTimeout);++        return new Promise((resolve, reject) => {+            fetch(url, requestOptions)+                .then((resp: Response & {statusCode: Response['status']}) => {

Why Response['status'] here? (Instead of Response.status)?

mzgoddard

comment created time in 6 days

Pull request review commentAirtable/airtable.js

Translate source code to typescript

+import forEach from 'lodash/forEach';+import get from 'lodash/get';+import assign from 'lodash/assign';+import isPlainObject from 'lodash/isPlainObject';+import fetch from './fetch';+import AbortController from './abort-controller';+import objectToQueryParamString from './object_to_query_param_string';+import AirtableError from './airtable_error';+import Table from './table';+import HttpHeaders from './http_headers';+import runAction from './run_action';+import packageVersion from './package_version';+import exponentialBackoffWithJitter from './exponential_backoff_with_jitter';+import type Airtable from './airtable';++const userAgent = `Airtable.js/${packageVersion}`;++type BaseRequestOptions = {+    method?: string;+    path?: string;+    qs?: any;+    headers?: any;+    body?: any;+    _numAttempts?: number;+};++class Base {+    readonly _airtable: Airtable;+    readonly _id: string;++    constructor(airtable: Airtable, baseId: string) {+        this._airtable = airtable;+        this._id = baseId;+    }++    table(tableName: string) {+        return new Table(this, null, tableName);+    }++    makeRequest(options: BaseRequestOptions) {+        const that = this;++        options = options || {};++        const method = get(options, 'method', 'GET').toUpperCase();++        const url = `${this._airtable._endpointUrl}/v${this._airtable._apiVersionMajor}/${+            this._id+        }${get(options, 'path', '/')}?${objectToQueryParamString(get(options, 'qs', {}))}`;++        const controller = new AbortController();++        const requestOptions: RequestInit = {+            method,+            headers: this._getRequestHeaders(get(options, 'headers', {})),+            signal: controller.signal,+        };++        if ('body' in options && _canRequestMethodIncludeBody(method)) {+            requestOptions.body = JSON.stringify(options.body);+        }++        const timeout = setTimeout(() => {+            controller.abort();+        }, this._airtable.requestTimeout);++        return new Promise((resolve, reject) => {

From line 67 to 111 (the whole return value): could this be written as a set of await ... expressions?

mzgoddard

comment created time in 6 days

Pull request review commentAirtable/airtable.js

Translate source code to typescript

+import forEach from 'lodash/forEach';+import get from 'lodash/get';+import assign from 'lodash/assign';+import isPlainObject from 'lodash/isPlainObject';+import fetch from './fetch';+import AbortController from './abort-controller';+import objectToQueryParamString from './object_to_query_param_string';+import AirtableError from './airtable_error';+import Table from './table';+import HttpHeaders from './http_headers';+import runAction from './run_action';+import packageVersion from './package_version';+import exponentialBackoffWithJitter from './exponential_backoff_with_jitter';+import type Airtable from './airtable';++const userAgent = `Airtable.js/${packageVersion}`;++type BaseRequestOptions = {+    method?: string;+    path?: string;+    qs?: any;+    headers?: any;+    body?: any;+    _numAttempts?: number;+};++class Base {+    readonly _airtable: Airtable;+    readonly _id: string;++    constructor(airtable: Airtable, baseId: string) {+        this._airtable = airtable;+        this._id = baseId;+    }++    table(tableName: string) {+        return new Table(this, null, tableName);+    }++    makeRequest(options: BaseRequestOptions) {

async makeRequest?

mzgoddard

comment created time in 6 days

Pull request review commentAirtable/airtable.js

Translate source code to typescript

+import forEach from 'lodash/forEach';+import get from 'lodash/get';+import assign from 'lodash/assign';+import isPlainObject from 'lodash/isPlainObject';+import fetch from './fetch';+import AbortController from './abort-controller';+import objectToQueryParamString from './object_to_query_param_string';+import AirtableError from './airtable_error';+import Table from './table';+import HttpHeaders from './http_headers';+import runAction from './run_action';+import packageVersion from './package_version';+import exponentialBackoffWithJitter from './exponential_backoff_with_jitter';+import type Airtable from './airtable';++const userAgent = `Airtable.js/${packageVersion}`;++type BaseRequestOptions = {+    method?: string;+    path?: string;+    qs?: any;+    headers?: any;+    body?: any;+    _numAttempts?: number;+};++class Base {+    readonly _airtable: Airtable;+    readonly _id: string;++    constructor(airtable: Airtable, baseId: string) {+        this._airtable = airtable;+        this._id = baseId;+    }++    table(tableName: string) {+        return new Table(this, null, tableName);+    }++    makeRequest(options: BaseRequestOptions) {+        const that = this;++        options = options || {};

Instead of this, can you do opts: BaseRequestOptions = {} in the parameter list?

mzgoddard

comment created time in 6 days

Pull request review commentAirtable/airtable.js

Translate source code to typescript

+class AirtableError {+    error;+    message;+    statusCode;++    constructor(error, message, statusCode) {+        this.error = error;+        this.message = message;+        this.statusCode = statusCode;+    }++    toString() {+        return [+            this.message,+            '(',+            this.error,+            ')',+            this.statusCode ? `[Http code ${this.statusCode}]` : '',+        ].join('');
`${this.message} (${this.error}) ${this.statusCode ? `[Http code ${this.statusCode}]` : ''}`;

?

mzgoddard

comment created time in 6 days

Pull request review commentAirtable/airtable.js

Translate source code to typescript

+import Base from './base';+import Record from './record';+import Table from './table';+import AirtableError from './airtable_error';++class Airtable {+    readonly _apiKey: string;+    readonly _endpointUrl: string;+    readonly _apiVersion: string;+    readonly _apiVersionMajor: string;+    readonly _noRetryIfRateLimited: boolean;++    requestTimeout: number;++    static Base = Base;+    static Record = Record;+    static Table = Table;+    static Error = AirtableError;++    static apiKey: string;+    static apiVersion: string;+    static endpointUrl: string;+    static noRetryIfRateLimited: boolean;++    constructor(+        opts: {+            apiKey?: string;+            apiVersion?: string;+            endpointUrl?: string;+            requestTimeout?: number;+            noRetryIfRateLimited?: boolean;+        } = {}+    ) {+        const defaultConfig = Airtable.default_config();++        const apiVersion = opts.apiVersion || Airtable.apiVersion || defaultConfig.apiVersion;++        Object.defineProperties(this, {+            _apiKey: {+                value: opts.apiKey || Airtable.apiKey || defaultConfig.apiKey,+            },+            _endpointUrl: {+                value: opts.endpointUrl || Airtable.endpointUrl || defaultConfig.endpointUrl,+            },+            _apiVersion: {+                value: apiVersion,+            },+            _apiVersionMajor: {+                value: apiVersion.split('.')[0],+            },+            _noRetryIfRateLimited: {+                value:+                    opts.noRetryIfRateLimited ||+                    Airtable.noRetryIfRateLimited ||+                    defaultConfig.noRetryIfRateLimited,+            },+        });++        this.requestTimeout = opts.requestTimeout || defaultConfig.requestTimeout;++        if (!this._apiKey) {+            throw new Error('An API key is required to connect to Airtable');+        }+    }++    base(baseId: string) {+        return Base.createFunctor(this, baseId);+    }++    static default_config() {+        return {+            endpointUrl: process.env.AIRTABLE_ENDPOINT_URL || 'https://api.airtable.com',+            apiVersion: '0.1.0',+            apiKey: process.env.AIRTABLE_API_KEY,+            noRetryIfRateLimited: false,+            requestTimeout: 300 * 1000, // 5 minutes

Does TS support numeric separators? Asking for a friend. 300_000 ;)

mzgoddard

comment created time in 6 days

Pull request review commentAirtable/airtable.js

Translate source code to typescript

+class AirtableError {+    error;+    message;+    statusCode;++    constructor(error, message, statusCode) {

Is there are legacy reason for this parameter list?

mzgoddard

comment created time in 6 days

Pull request review commentAirtable/airtable.js

Translate source code to typescript

+import Base from './base';+import Record from './record';+import Table from './table';+import AirtableError from './airtable_error';++class Airtable {+    readonly _apiKey: string;+    readonly _endpointUrl: string;+    readonly _apiVersion: string;+    readonly _apiVersionMajor: string;+    readonly _noRetryIfRateLimited: boolean;++    requestTimeout: number;++    static Base = Base;+    static Record = Record;+    static Table = Table;+    static Error = AirtableError;++    static apiKey: string;+    static apiVersion: string;+    static endpointUrl: string;+    static noRetryIfRateLimited: boolean;++    constructor(+        opts: {+            apiKey?: string;+            apiVersion?: string;+            endpointUrl?: string;+            requestTimeout?: number;+            noRetryIfRateLimited?: boolean;+        } = {}+    ) {+        const defaultConfig = Airtable.default_config();++        const apiVersion = opts.apiVersion || Airtable.apiVersion || defaultConfig.apiVersion;++        Object.defineProperties(this, {+            _apiKey: {+                value: opts.apiKey || Airtable.apiKey || defaultConfig.apiKey,+            },+            _endpointUrl: {+                value: opts.endpointUrl || Airtable.endpointUrl || defaultConfig.endpointUrl,+            },+            _apiVersion: {+                value: apiVersion,+            },+            _apiVersionMajor: {+                value: apiVersion.split('.')[0],+            },+            _noRetryIfRateLimited: {+                value:+                    opts.noRetryIfRateLimited ||+                    Airtable.noRetryIfRateLimited ||+                    defaultConfig.noRetryIfRateLimited,+            },+        });

Continuing from my last question, if you chose to use private fields, this would be simplified.

mzgoddard

comment created time in 6 days

Pull request review commentAirtable/airtable.js

Translate source code to typescript

+import Base from './base';+import Record from './record';+import Table from './table';+import AirtableError from './airtable_error';++class Airtable {+    readonly _apiKey: string;+    readonly _endpointUrl: string;+    readonly _apiVersion: string;+    readonly _apiVersionMajor: string;+    readonly _noRetryIfRateLimited: boolean;

Is there a legacy requirement to keep these named with _? This seems like a good use case for private fields, but I can understand not using them if some other code somewhere else is somehow relying on them.

mzgoddard

comment created time in 6 days

push eventbocoup/dcss

Rick Waldron

commit sha dc2cec56160cf79f611460fbd813880ff74b3289

Popup: inverted to stand out more

view details

push time in 7 days

push eventtc39/test262

Gus Caplan

commit sha 156186aee28566bd6ba5f44cea106353e8e247e5

New NativeFunction tests

view details

Gus Caplan

commit sha 5f99d05c6a6f5e12681258905cbb8b5c9d81ff3b

native function validator for your consideration

view details

push time in 7 days

PR merged tc39/test262

New NativeFunction tests

For https://github.com/tc39/ecma262/pull/1948

+416 -407

5 comments

5 changed files

devsnek

pr closed time in 7 days

push eventbocoup/dcss

Rick Waldron

commit sha 53f562d6b6e32f63b3b88a6d84d05ec533f4273d

Lint

view details

push time in 7 days

push eventbocoup/dcss

Rick Waldron

commit sha cc4c99a5fe25a3b8388c3f058c8c370152feffa4

MultiPathResponse, Editor: enter number of slide chosen for Choose Next Slide (instead of scrolling through all the slides)

view details

Rick Waldron

commit sha c4d962ac49bc113b526fbd74926b8516bec19f7c

Lint

view details

push time in 7 days

push eventbocoup/dcss

Rick Waldron

commit sha 5453a8a229057ab72a7cc5cd6ac026b771fe0bee

fixup! ContentSlide: remove "slide" from "Next ..." and "Previous ..." (per request)

view details

push time in 7 days

push eventbocoup/dcss

Rick Waldron

commit sha e7825f92fc99897f8ce2ae19d9823d2038a253c3

MultiPathResponse Prompt: Get rid of the check mark after selecting latest

view details

push time in 7 days

push eventbocoup/dcss

Rick Waldron

commit sha cd2dbdf9505f9760c1cef8c9f333377fcc88f471

ContentSlide: remove "slide" from "Next ..." and "Previous ..." (per request)

view details

push time in 7 days

push eventbocoup/dcss

Rick Waldron

commit sha a3c84dcce1ccfb108f56d65bad92731163cc258b

Cleanup

view details

push time in 7 days

push eventbocoup/dcss

Rick Waldron

commit sha 1775d122c8438a9c47ad70cf84ac2822d242dbb2

Cleanup

view details

push time in 7 days

pull request commenttc39/test262

New NativeFunction tests

@syg heads up for failures on V8

devsnek

comment created time in 8 days

Pull request review commenttc39/test262

New NativeFunction tests

 defines: [WellKnownIntrinsicObjects]  const WellKnownIntrinsicObjects = [   {-    intrinsicName: "%Array%",-    globalNameOrSource: "Array"

I don't see any value in renaming these. The names that they have are derived from the column header in the spec.

devsnek

comment created time in 8 days

pull request commenttc39/test262

Add tests for \9 in template/strict strings.

@syg heads up for failures in V8

rkirsling

comment created time in 8 days

push eventtc39/test262

Ross Kirsling

commit sha f24b9004b39bdb6fe6d56b7eb0556330e315b49f

Add tests for \9 in template/strict strings.

view details

Ross Kirsling

commit sha ede5b2400f5ce817623343d052ab25f382c6e66a

Test TV for tagged templates too.

view details

push time in 8 days

PR merged tc39/test262

Add tests for \9 in template/strict strings.

Supplement to #2654, per @syg's request.

I just included tests for \8 in that PR but we really should check \9 as well for completeness.

+46 -0

2 comments

3 changed files

rkirsling

pr closed time in 8 days

push eventbocoup/dcss

Rick Waldron

commit sha 1db3a9953bec850bad132ee845c57e7ae306fad9

Dependency: object-hash

view details

push time in 8 days

push eventbocoup/dcss

Rick Waldron

commit sha 89d10346d6dc3866d508a4a70c77656a6aacff10

Loading: fluid width and height

view details

Rick Waldron

commit sha e9a2db96e63ac8d82855594a63f65e5af331625d

Logging

view details

Rick Waldron

commit sha 9640e15995d9bd5f6d69279f671b82a05ebd102f

Lint

view details

push time in 8 days

issue commentJiHong88/SunEditor

Text styles not applied to list item element

My understanding of our user's feedback is that they expect it to act like google docs: https://youtu.be/bTUX4qwojUc (Which I agree with)

rwaldron

comment created time in 9 days

push eventbocoup/dcss

Rick Waldron

commit sha f6f5cf96e3f1b282509d59b863d8871bbadf2539

Minor nitpicking, cleanup

view details

Rick Waldron

commit sha c32340f0a0a8bf96085c3671d2a475e6a7991317

Scenario: run/preview layout fixes

view details

Rick Waldron

commit sha ebc453eeb2abe643d657792f01ceea13fe96d875

Lint

view details

Rick Waldron

commit sha b99decfbfad2460098178dbcfd6f5586994bfab1

Scenario: fix copying of scenarios that contain paths to finish slides

view details

Rick Waldron

commit sha d9f6749d5b7a5bd3e40db611ad0431fcb87d83e7

Migration: revise scenario_snapshot

view details

push time in 11 days

push eventbocoup/dcss

Rick Waldron

commit sha a064fcabcd45cbd72c5e98778f73ec221bb7f425

Loading/History/ScenarioList: layout fixes

view details

Rick Waldron

commit sha 983eb5a8a2b072791436d136739d7224f7b5a343

Scenario: always keep navigation buttons on screen

view details

Rick Waldron

commit sha 2296358c960f39f11a1dabfc0996a511e0a3204b

ContentSlide: don't let an empty video url prevent a slide from proceeding.

view details

Rick Waldron

commit sha a14204f1d643c2858f6284c4d03f9902bbed00be

Layout tweaks

view details

push time in 12 days

push eventbocoup/dcss

Rick Waldron

commit sha 4bf0919242d8c07868dc1375944d69dae607a86e

Remove unused services and components

view details

Rick Waldron

commit sha 816dd893820f8373183a481bac30adc4b376029e

Lint

view details

Rick Waldron

commit sha 1cc53076b2b39a897e6b2d4f3eea6e73a33a54fa

Renaming ariaLabelledBy, ariaDescribedBy => ariaLabelledby, ariaDescribedby

view details

Rick Waldron

commit sha 97c8a1ba136562b3b61348991c67cef66ca5f040

MultiButton/MultiChoice: better default behaviour

view details

Rick Waldron

commit sha 55689d0f7d1cfedda3c71c11ffe8b27c4bb4b225

FinishSlide: improved accessibility

view details

Rick Waldron

commit sha eff04873a356d4bce572365739f52b6101f8b3cd

ContentSlide: improved accessibility

view details

Rick Waldron

commit sha 3671a182ff1d482138723977e50725b32151de92

ContentSlide: handling required prompts that are missing buttons or paths

view details

Rick Waldron

commit sha 40aaf8c9937de4a1b08325b2889ed60daa2987f6

Lint

view details

push time in 12 days

PR opened tc39/test262

Reviewers
Redeclaration of arguments in direct eval in parameter expressions, ref gh-2478

@anba I used your report and spidermonkey tests (from here https://github.com/tc39/test262/issues/2478) as the basis for the test cases & templates. Can you take a look?

+6127 -5

0 comment

318 changed files

pr created time in 12 days

push eventtc39/test262

Rick Waldron

commit sha a65adba15be11afca99b692db3efaa2408c765a5

Redeclaration of argument in direct eval in parameter expressions

view details

Rick Waldron

commit sha 558bc8808285d1126d89a55cfda3216ef78abadb

Generate tests

view details

push time in 12 days

push eventtc39/test262

Rick Waldron

commit sha 9caa0b92c5666e99cf5c21a324eedfb3fdc430e0

Redeclaration of argument in direct eval in parameter expressions

view details

Rick Waldron

commit sha 1db78d2db70535fd5532a7f82f27c33fc1a6f816

Generate tests

view details

push time in 12 days

push eventbterlson/eshost

Rick Waldron

commit sha 2ea6cddc09fe5d0f49a2c61566a2a00064832b5b

Agent: fix jsshell error parser

view details

push time in 12 days

push eventtc39/test262

Rick Waldron

commit sha a293a82bfbd81aab6b579ce62b1bd00e09bbaab0

Add globbing support to test generator

view details

Rick Waldron

commit sha 17e550f5b32448f42a9bb69024cd6df58c9c587a

Redeclaration of "arguments" in direct eval in parameter expressions

view details

Rick Waldron

commit sha b256f002ee2d97f4a46db207cc2df200e454be58

Generate tests

view details

push time in 12 days

push eventbocoup/dcss

Rick Waldron

commit sha 41e421161a49461f2ff002b7a508a5326a5b32c1

Empty Choose Next Slide should be treated as non-existent in Scenario runner

view details

Rick Waldron

commit sha 94658894f105b60ad84446177b9f6b3a693f0894

Multi Button editor component should have blank button to start

view details

push time in 13 days

push eventjshint/jshint

Mike Pennisi

commit sha 3b2c8cfa2836d1e620334b4a7d3a01bf5e3c529e

[[FIX]] Disallow leading zeros in BigInts

view details

push time in 13 days

push eventjshint/jshint

Mike Pennisi

commit sha 91614b8e3e993bf3090dad2f19b3dd8a07b1b313

[[FIX]] Disallow leading zeros in BigInts

view details

Mike Pennisi

commit sha edb5711f1d3adcba0e35fad19568c11527fb5830

fixup! [[FIX]] Disallow leading zeros in BigInts

view details

push time in 13 days

PR merged jshint/jshint

[[FIX]] Disallow leading zeros in BigInts
+22 -9

2 comments

3 changed files

jugglinmike

pr closed time in 13 days

issue openedJiHong88/SunEditor

Text styles not applied to list item element

This issue was reported to me today, so I reproduced it and also did a mock up of what I would expect the correct behavior to look like.

Here's the reported issue:

image

And this is the expected formatting: image

It looks like the style just needs to be put on the <li> instead of a <span>?

created time in 13 days

push eventbocoup/dcss

Rick Waldron

commit sha f8ab9e1e08e65a63d53dd45be0b6e60c83a6aab7

ScenarioCard/DeletedCard: fix restore bug

view details

Rick Waldron

commit sha a9e8e924c3a3eeea367512e16d7c6e3ef3eea661

Title: implement page title handling

view details

Rick Waldron

commit sha de2aaf48d67cfc9506f056cf403a9afaa8eb6331

Lint

view details

Rick Waldron

commit sha 5e321c4d8a260e01a23214325468a3e480f25e19

Lint

view details

push time in 13 days

push eventbocoup/dcss

Rick Waldron

commit sha b3579d4c4c784fcdbbfde2963e7bfba5eb436f83

Navigation: remove duplicated menu item; delegate onKeyUp override to Events.onKeyUp

view details

push time in 13 days

pull request commentjshint/jshint

improved language to be more inclusive of less privileged groups (blm)

ping @rgeerts

rgeerts

comment created time in 13 days

pull request commentjshint/jshint

[[FEAT]] Add support for `import.meta`

@jugglinmike I've rebased this for you and merged it into v2.12.0

jugglinmike

comment created time in 13 days

PR closed jshint/jshint

Reviewers
[[FEAT]] Add support for `import.meta`

Tolerate parsing errors regarding escape sequences in IdentifierNames as these reflect a pre-existing deficiency which will require a dedicated patch to correct.

+91 -16

2 comments

5 changed files

jugglinmike

pr closed time in 13 days

push eventjshint/jshint

Mike Pennisi

commit sha 836df698d35da4edd575d30b0cad315738140f2c

[[FEAT]] Add support for `import.meta` Tolerate parsing errors regarding escape sequences in IdentifierNames as these reflect a pre-existing deficiency which will require a dedicated patch to correct.

view details

push time in 13 days

push eventjshint/jshint

Mike Pennisi

commit sha a509eb2dfa73ad87e5dbbe98dddb90fb0844c88e

[[FEAT]] Add support for `globalThis`

view details

push time in 13 days

push eventjshint/jshint

Mike Pennisi

commit sha b02a025f95f2c7cace8a4db482e2a926d074378f

[[FIX]] Disallow invalid numeric literals

view details

push time in 13 days

PR merged jshint/jshint

[[FIX]] Disallow invalid numeric literals

@rwaldron This is pretty much the opposite of "a simple fix to achieve more coverage". They can't all be winners.

+50 -15

2 comments

4 changed files

jugglinmike

pr closed time in 13 days

push eventbocoup/dcss

Rick Waldron

commit sha 071e38a9f59b6e773c830b6901a5f5355e7ccbea

Layout: disable boundary detection

view details

Rick Waldron

commit sha 848033c45740c43b4d78fdbea33b0727da82a483

Identity: key is an alias for hash

view details

Rick Waldron

commit sha 4f3889bfb667ae8b24739f4371b33c39d3900f92

Menu.Item => Menu.Item.Tabbable

view details

push time in 14 days

push eventbocoup/dcss

Rick Waldron

commit sha 760c3902226b967c55388998b4c1329ba1f347aa

ScenariosList: fix action button background and hover

view details

push time in 14 days

push eventbocoup/dcss

Rick Waldron

commit sha 3a7c9e8ad6156f88a77517f063eeafff9c6197eb

ScenariosList: fix action button background and hover

view details

push time in 14 days

push eventbocoup/dcss

Rick Waldron

commit sha a9aa3ada04566ffdd02b2f782412872e62483a83

ScenariosList: fix action button background and hover

view details

push time in 14 days

push eventbocoup/dcss

Rick Waldron

commit sha 475d441e56d6ccc16b2a76db1200124b98f554c8

Card.Group.Stackable: consolidate card group & fallback into single ui component

view details

Rick Waldron

commit sha 9f63342fd1da87a1152e6a8f2fae7204a76a52b4

More animal names

view details

Rick Waldron

commit sha f590d70802ece5d12d61584e11eb7e2755e03dec

Lint

view details

Rick Waldron

commit sha 7440f79d19e65de5c8f2c7a5308f1c34fb2f0979

Implement and integrate MenuItemTabbable

view details

Rick Waldron

commit sha e46a8a7aa8db65173b03011e905762a0c5733cbc

Deps: react-aria-live

view details

Rick Waldron

commit sha 27e121167901feabc610ca210ec83bbdae17f803

Events: provide onKeyUp fallback

view details

Rick Waldron

commit sha a87280d83cfc91418db2aff7802903d19b11b859

ScenarioCardActions: don't use basic buttons, theres not enough contrast

view details

Rick Waldron

commit sha 41d85f572788973eb990861209e8b763c4535772

Move makeDefaultDescription to own module

view details

Rick Waldron

commit sha 141e95220f6dde748cf1afe3ae14772ee4eb74bb

Accessibility fixes

view details

push time in 14 days

push eventbocoup/dcss

Rick Waldron

commit sha 244d520bf63f54b88256935d9aa4f9e5d0f793b6

OnKeyUp: capture enter and space press on options

view details

Rick Waldron

commit sha 2c8d858a68dc8a430957960d9c90b1b5d65f5e7a

Implement Modal.Accessible

view details

Rick Waldron

commit sha 279df5bde74e072e2f5e4e4700131117469a86c0

Lint

view details

push time in 15 days

push eventbocoup/dcss

Rick Waldron

commit sha 03d64c752ae98838eff9d0f4e5e01b5e801f4a4c

Moment: fix incorrect casing in relative time string formatting

view details

Rick Waldron

commit sha 04dc9788478da8089867607753c40fe0bc96ad46

Layout: implement isForMobile, isNotForMobile; update computeItemsRowsPerPage

view details

Rick Waldron

commit sha 40449b218a22434badbe932fc9f9d2ed9fa45a68

Static: make input font size 16px on mobile (for both form input components and menu item input components)

view details

Rick Waldron

commit sha e8cf6edff5b381e14e8f1be985275ae4303a64f9

Navigation: migrate to Layout.isForMobile()

view details

Rick Waldron

commit sha f3abcb6084df76e9cf305827e54e7b38c81e2756

UserMenu: migrate to Layout.isForMobile()

view details

Rick Waldron

commit sha 05e1ea52ecf48effc1de171c463f5038e3bc0ea4

fixup! Static: make input font size 16px on mobile (for both form input components and menu item input components)

view details

Rick Waldron

commit sha 569d2cb49b9cb354fb96c8ff18ee857762448839

ResponseRecall: use slide id to determine if this slide should be ignored.

view details

Rick Waldron

commit sha 29a9cb73ec4cd9fadf5177b4078842994b6adaac

PromptRequiredLabel: add aria-label

view details

Rick Waldron

commit sha fbc9b5724757b005dc30ef7a52003ee1cdf3250b

ConversationPromptEditor: remove blur handler

view details

Rick Waldron

commit sha 3cb996dccc71c63b96b1b7f0393cf0825257a800

CohortCard: fix "Created..." text

view details

Rick Waldron

commit sha 22e98df5a42b787dd980b41992721550a8ba7136

Prompts: fix placeholders

view details

Rick Waldron

commit sha d7aba60860679b3bb25b97ff1654468e5e91fadd

Lint

view details

Rick Waldron

commit sha 96aaf1db35d3d61ffa7957beb7d9756c7ca3b923

ScenariosList: migrate to Layout.isForMobile()

view details

Rick Waldron

commit sha 8f122702e2a1a5d259e2d8e0273e42f70f758838

CohortParticipants: migrate to Layout.isNotForMobile()

view details

Rick Waldron

commit sha 8676e2f50a4166feabb7b323c170752314e421a3

CohortScenarios: migrate to Layout.isForMobile()

view details

Rick Waldron

commit sha 9156b2725a1e058283af8de6b07e20430c7a5e42

DataTable: fix layouts

view details

Rick Waldron

commit sha 032bc0edcd982ab00d0a88c19ac0e09d67d8fa1f

Downloads: migrate to Layout.isForMobile()

view details

Rick Waldron

commit sha 5578299e50df1b289a8f9eeed493f77d225681cb

History: migrate to Layout.isForMobile()

view details

Rick Waldron

commit sha 9e7fa476d70ac4627e8fd0481aca07fd296f3dc4

ContentSlide: use as much space as possible for slide content

view details

Rick Waldron

commit sha cc041bbc012de1b7e88e5bdf7395f2b81d53264e

Loading: migrate to Layout.isForMobile()

view details

push time in 15 days

push eventbocoup/dcss

Rick Waldron

commit sha ceca2bbe6cee54131a4ef9e44c3aec15e592c6ff

Slides: fix order of components (MultiPathResponse)

view details

Rick Waldron

commit sha 41da52b09244eb12bb40396cd200766338c67394

MultiPathResponse: improved meta description

view details

Rick Waldron

commit sha 53d513e8423b73bbf0814b190261394c3a705f4a

Navigation: fix "Go to..." padding

view details

Rick Waldron

commit sha ba2504358e0c5c9d9c166cb1362a3fcc2afc5344

Sortable: support disabling sort

view details

Rick Waldron

commit sha 2833aa415aac6c176934fac097671539f7863020

Admin: "users_icon" => "roles_icon"

view details

Rick Waldron

commit sha 30360bc654a1422e367d6f07a1fadea467655624

Lint

view details

Rick Waldron

commit sha a73d914cb30907716b17a312511e061ad0b40f91

CohortScenarios: migrate from infinite scroll table to paginated table

view details

Rick Waldron

commit sha 8c52f3690dee7ee74b715e4d3cbb43689d401889

CohortParticipants: key audit

view details

Rick Waldron

commit sha 4772758a1451475b1e52c573dc39db58da7538d1

Cohort: simplify on mobile

view details

Rick Waldron

commit sha 0ad79f93cd1fbd8fa5adc485a8de2d63421e8283

UsersTable: simplify on mobile

view details

Rick Waldron

commit sha b558b5e186365c7717e1818c3c7a7f6bd3e60e60

UI: eliminate padding for large tab segments on mobile

view details

Rick Waldron

commit sha 1005ac6127231927a3af97daee5caca291358ff1

History: better messages, don't let users see empty data

view details

push time in 18 days

push eventbocoup/dcss

Rick Waldron

commit sha f95f060e3c0edf728305c52f06cd07f9dc870623

MultiPathResponseEditor: fix unstable key

view details

Rick Waldron

commit sha e5cef9c7a904a77a006a9e1522922bf7b6a999a4

Lint

view details

push time in 18 days

push eventbocoup/dcss

Rick Waldron

commit sha 9631a9874622ed4950e4f48048dba82bb1a5af76

Cohorts: simplify cohort admin layout on mobile

view details

Rick Waldron

commit sha 3d2d507038d482dc4d062b3b81672e82a21b7c04

Navigation: fix dropdown bug

view details

push time in 19 days

push eventbocoup/dcss

Rick Waldron

commit sha c3bef4502debb5782088b525101860b9be1bfde9

Layout fixes

view details

Rick Waldron

commit sha 86e60483a4728e0f61e1ac101898d7ab4687e7be

Lint fixes

view details

push time in 19 days

push eventbocoup/dcss

Rick Waldron

commit sha afcfae4c1f6fcbc3bd87e389fee820fbb69e626f

Deps: upgrade semantic-ui-react

view details

Rick Waldron

commit sha 24a80c363ae0dc22be032da57c59c0e9a0de9779

AudioPrompt: cleanup

view details

Rick Waldron

commit sha 5d99545fa371feba4a5caafad9f161d6b7bcc84f

Lint

view details

Rick Waldron

commit sha f3b7acfd802a46e49dc1c2980a8c36ef08277d9a

Editor/Slides: cleanup loading views

view details

push time in 19 days

push eventbocoup/dcss

Rick Waldron

commit sha 0777775a3d2695942111eaf5c175a00bffd2fdb9

EditorMenu: fix width

view details

Rick Waldron

commit sha cc81b465473ddaf9fd457d64417e295c237dcb7e

Navigation, Loading: layout and accessibility fixes

view details

Rick Waldron

commit sha e240fd441d47a858c25ec398cf7a56a9960e02b5

Scenario, ScenarioEditor, Cohorts: layout fixes

view details

Rick Waldron

commit sha 805e4a9189f6f391a53555bf2ce0513224f27bc0

UserMenu, UserSettings, ConfirmableLogout: fix cancel bug

view details

Rick Waldron

commit sha 5fd1466b0fd236a1017176345237fc478c279bb7

AudioPrompt: migrate Display to new AudioRecorder

view details

Rick Waldron

commit sha 48e14467ce09aefb459028e24d52c5b63dd979bd

Scenarios reducer: fix unlock bug

view details

Rick Waldron

commit sha a5c0bd60d5b112464d597c37c25413ce7bb90daa

LoginRoutePromptModal: re-order options to make more sense

view details

Rick Waldron

commit sha fceeb3e44151aec6bb9c685c663979b53986de5a

Lint fixes

view details

push time in 19 days

push eventbocoup/dcss

Rick Waldron

commit sha e44ddc3b8319040e310dcd37f587d22c7ec97414

Media: move fileToMediaURL into Media

view details

Rick Waldron

commit sha 48529472d93014a65ac08a25819e42a1458d11c8

ConversationPromptDisplay: remove instructions

view details

Rick Waldron

commit sha 4605efe13e15a3f0cdeb44f727889daa82490ab2

AudioRecorder: show audio element controls time interface will recording

view details

Rick Waldron

commit sha 3d6411136f115976a897a434d397085b460688f8

Lint fixes

view details

push time in 20 days

push eventbocoup/dcss

Rick Waldron

commit sha 06eddb0b1df95c4a94433a6caab907589809c6ba

Movers: make space on the right for last child

view details

push time in 20 days

push eventbocoup/dcss

Rick Waldron

commit sha 7e6a6cf27e9cd4dfbd2a13dac710a976ee7d8dde

ResponseRecall: don't assume there is a response value

view details

push time in 20 days

push eventbocoup/dcss

Rick Waldron

commit sha 57748ebba0861bd5ca398c6d3cfca36fd1d55f0e

ConversationPromptEditor: use Media.*

view details

push time in 20 days

push eventbocoup/dcss

Rick Waldron

commit sha e3a0d11d0afb4c6bace57faff1863f200e13441c

MultiPathNetworkGraphModal: use &quot;

view details

Rick Waldron

commit sha 7a86152599102748f6080c3d036625e31ded8bba

MultiPathResponseEditor: pass options to display input

view details

Rick Waldron

commit sha 75ebcddebf2481647160e42429d129b8864e3fb4

ConversationPromptEditor: capture user changes to start and end time

view details

Rick Waldron

commit sha 4258318f4dde7204ffc1068a54ae0fd0701fda09

ScenarioEditor: set autocomplete to off

view details

push time in 20 days

issue openedJiHong88/SunEditor

How to: onDrop, onPaste, if image, then upload to imageUploadUrl?

This is isn't an "issue", I'm just hoping to get some help. How would I connect onDrop and onPaste to the imageUpload mechanism? It seems like this should be possible, but I'm struggling to put the pieces together.

created time in 20 days

more