Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

unleash-client

Package Overview
Dependencies
Maintainers
4
Versions
123
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

unleash-client - npm Package Compare versions

Comparing version 3.2.9 to 3.3.0

lib/helpers.d.ts

6

CHANGELOG.md
# Changelog
## 3.3.0
- feat: Add support for fallback function (#150)
- feat: customHeaderFunction to dynamic setting headers (#152)
- fix: upgrade @unleash/client-specification to version 3.3.0
## 3.2.9

@@ -4,0 +10,0 @@

5

examples/with_context.js

@@ -6,2 +6,5 @@ const { initialize, isEnabled } = require('../lib');

url: 'http://unleash.herokuapp.com/api/',
refreshInterval: 10000,
metricsInterval: 10000,
environment: 'production',
});

@@ -16,3 +19,3 @@

const context = {
userId: '123',
userId: `${Math.random() * 100}`,
sessionId: Math.round(Math.random() * 1000),

@@ -19,0 +22,0 @@ remoteAddress: '127.0.0.1',

4

lib/client.d.ts

@@ -14,6 +14,6 @@ import { EventEmitter } from 'events';

warnOnce(missingStrategy: string, name: string, strategies: StrategyTransportInterface[]): void;
isEnabled(name: string, context: Context, fallbackValue?: boolean): boolean;
isFeatureEnabled(feature: FeatureInterface, context: Context, fallbackValue?: boolean): boolean;
isEnabled(name: string, context: Context, fallback: Function): boolean;
isFeatureEnabled(feature: FeatureInterface, context: Context, fallback: Function): boolean;
getVariant(name: string, context: Context, fallbackVariant?: Variant): Variant;
}
//# sourceMappingURL=client.d.ts.map

@@ -52,10 +52,10 @@ "use strict";

};
UnleashClient.prototype.isEnabled = function (name, context, fallbackValue) {
UnleashClient.prototype.isEnabled = function (name, context, fallback) {
var feature = this.repository.getToggle(name);
return this.isFeatureEnabled(feature, context, fallbackValue);
return this.isFeatureEnabled(feature, context, fallback);
};
UnleashClient.prototype.isFeatureEnabled = function (feature, context, fallbackValue) {
UnleashClient.prototype.isFeatureEnabled = function (feature, context, fallback) {
var _this = this;
if (!feature && typeof fallbackValue === 'boolean') {
return fallbackValue;
if (!feature) {
return fallback();
}

@@ -93,3 +93,5 @@ if (!feature || !feature.enabled) {

}
var enabled = this.isFeatureEnabled(feature, context, fallbackVariant.enabled);
var enabled = this.isFeatureEnabled(feature, context, function () {
return fallbackVariant ? fallbackVariant.enabled : false;
});
if (!enabled) {

@@ -96,0 +98,0 @@ return fallbackVariant;

@@ -1,1 +0,1 @@

{ "name": "unleash-client-node", "version": "3.2.9", "sdkVersion": "unleash-client-node:3.2.9" }
{ "name": "unleash-client-node", "version": "3.3.0", "sdkVersion": "unleash-client-node:3.3.0" }
import { EventEmitter } from 'events';
import { Data } from './request';
import { CustomHeaders } from './unleash';
import { CustomHeaders, CustomHeadersFunction } from './unleash';
export interface MetricsOptions {

@@ -13,2 +13,3 @@ appName: string;

headers?: CustomHeaders;
customHeadersFunction?: CustomHeadersFunction;
timeout?: number;

@@ -29,8 +30,9 @@ }

private headers?;
private customHeadersFunction?;
private timeout?;
constructor({ appName, instanceId, strategies, metricsInterval, disableMetrics, url, headers, timeout, }: MetricsOptions);
constructor({ appName, instanceId, strategies, metricsInterval, disableMetrics, url, headers, customHeadersFunction, timeout, }: MetricsOptions);
private startTimer;
stop(): void;
registerInstance(): boolean;
sendMetrics(): boolean;
registerInstance(): Promise<boolean>;
sendMetrics(): Promise<boolean>;
assertBucket(name: string): false | undefined;

@@ -37,0 +39,0 @@ count(name: string, enabled: boolean): boolean;

@@ -15,2 +15,37 @@ "use strict";

})();
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
Object.defineProperty(exports, "__esModule", { value: true });

@@ -24,3 +59,3 @@ var events_1 = require("events");

function Metrics(_a) {
var appName = _a.appName, instanceId = _a.instanceId, strategies = _a.strategies, _b = _a.metricsInterval, metricsInterval = _b === void 0 ? 0 : _b, _c = _a.disableMetrics, disableMetrics = _c === void 0 ? false : _c, url = _a.url, headers = _a.headers, timeout = _a.timeout;
var appName = _a.appName, instanceId = _a.instanceId, strategies = _a.strategies, _b = _a.metricsInterval, metricsInterval = _b === void 0 ? 0 : _b, _c = _a.disableMetrics, disableMetrics = _c === void 0 ? false : _c, url = _a.url, headers = _a.headers, customHeadersFunction = _a.customHeadersFunction, timeout = _a.timeout;
var _this = _super.call(this) || this;

@@ -35,2 +70,3 @@ _this.disabled = disableMetrics;

_this.headers = headers;
_this.customHeadersFunction = customHeadersFunction;
_this.started = new Date();

@@ -66,70 +102,106 @@ _this.timeout = timeout;

Metrics.prototype.registerInstance = function () {
var _this = this;
if (this.disabled) {
return false;
}
var url = url_1.resolve(this.url, './client/register');
var payload = this.getClientData();
request_1.post({
url: url,
json: payload,
appName: this.appName,
instanceId: this.instanceId,
headers: this.headers,
timeout: this.timeout,
}, function (err, res, body) {
if (err) {
_this.emit('error', err);
return;
}
/* istanbul ignore next if */
if (!(res.statusCode && res.statusCode >= 200 && res.statusCode < 300)) {
_this.emit('warn', url + " returning " + res.statusCode, body);
return;
}
/* istanbul ignore next line */
_this.emit('registered', payload);
return __awaiter(this, void 0, void 0, function () {
var url, payload, headers, _a;
var _this = this;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
if (this.disabled) {
return [2 /*return*/, false];
}
url = url_1.resolve(this.url, './client/register');
payload = this.getClientData();
if (!this.customHeadersFunction) return [3 /*break*/, 2];
return [4 /*yield*/, this.customHeadersFunction()];
case 1:
_a = _b.sent();
return [3 /*break*/, 3];
case 2:
_a = this.headers;
_b.label = 3;
case 3:
headers = _a;
request_1.post({
url: url,
json: payload,
appName: this.appName,
instanceId: this.instanceId,
headers: headers,
timeout: this.timeout,
}, function (err, res, body) {
if (err) {
_this.emit('error', err);
return;
}
/* istanbul ignore next if */
if (!(res.statusCode && res.statusCode >= 200 && res.statusCode < 300)) {
_this.emit('warn', url + " returning " + res.statusCode, body);
return;
}
/* istanbul ignore next line */
_this.emit('registered', payload);
});
return [2 /*return*/, true];
}
});
});
return true;
};
Metrics.prototype.sendMetrics = function () {
var _this = this;
/* istanbul ignore next if */
if (this.disabled) {
return false;
}
if (this.bucketIsEmpty()) {
this.resetBucket();
this.startTimer();
return true;
}
var url = url_1.resolve(this.url, './client/metrics');
var payload = this.getPayload();
request_1.post({
url: url,
json: payload,
appName: this.appName,
instanceId: this.instanceId,
headers: this.headers,
timeout: this.timeout,
}, function (err, res, body) {
_this.startTimer();
if (err) {
_this.emit('error', err);
return;
}
/* istanbul ignore next if */
if (res.statusCode === 404) {
_this.emit('warn', url + " returning 404, stopping metrics");
_this.stop();
return;
}
/* istanbul ignore next if */
if (!(res.statusCode && res.statusCode >= 200 && res.statusCode < 300)) {
_this.emit('warn', url + " returning " + res.statusCode, body);
return;
}
_this.emit('sent', payload);
return __awaiter(this, void 0, void 0, function () {
var url, payload, headers, _a;
var _this = this;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
/* istanbul ignore next if */
if (this.disabled) {
return [2 /*return*/, false];
}
if (this.bucketIsEmpty()) {
this.resetBucket();
this.startTimer();
return [2 /*return*/, true];
}
url = url_1.resolve(this.url, './client/metrics');
payload = this.getPayload();
if (!this.customHeadersFunction) return [3 /*break*/, 2];
return [4 /*yield*/, this.customHeadersFunction()];
case 1:
_a = _b.sent();
return [3 /*break*/, 3];
case 2:
_a = this.headers;
_b.label = 3;
case 3:
headers = _a;
request_1.post({
url: url,
json: payload,
appName: this.appName,
instanceId: this.instanceId,
headers: headers,
timeout: this.timeout,
}, function (err, res, body) {
_this.startTimer();
if (err) {
_this.emit('error', err);
return;
}
/* istanbul ignore next if */
if (res.statusCode === 404) {
_this.emit('warn', url + " returning 404, stopping metrics");
_this.stop();
return;
}
/* istanbul ignore next if */
if (!(res.statusCode && res.statusCode >= 200 && res.statusCode < 300)) {
_this.emit('warn', url + " returning " + res.statusCode, body);
return;
}
_this.emit('sent', payload);
});
return [2 /*return*/, true];
}
});
});
return true;
};

@@ -136,0 +208,0 @@ Metrics.prototype.assertBucket = function (name) {

import { EventEmitter } from 'events';
import { Storage } from './storage';
import { FeatureInterface } from './feature';
import { CustomHeaders } from './unleash';
import { CustomHeaders, CustomHeadersFunction } from './unleash';
export declare type StorageImpl = typeof Storage;

@@ -20,2 +20,3 @@ export interface RepositoryInterface extends EventEmitter {

headers?: CustomHeaders;
customHeadersFunction?: CustomHeadersFunction;
}

@@ -31,7 +32,8 @@ export default class Repository extends EventEmitter implements EventEmitter {

private headers?;
private customHeadersFunction?;
private timeout?;
constructor({ backupPath, url, appName, instanceId, refreshInterval, StorageImpl, timeout, headers, }: RepositoryOptions);
constructor({ backupPath, url, appName, instanceId, refreshInterval, StorageImpl, timeout, headers, customHeadersFunction, }: RepositoryOptions);
timedFetch(): void;
validateFeature(feature: FeatureInterface): void;
fetch(): void;
fetch(): Promise<void>;
stop(): void;

@@ -38,0 +40,0 @@ getToggle(name: string): FeatureInterface;

@@ -15,2 +15,37 @@ "use strict";

})();
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
Object.defineProperty(exports, "__esModule", { value: true });

@@ -24,3 +59,3 @@ var events_1 = require("events");

function Repository(_a) {
var backupPath = _a.backupPath, url = _a.url, appName = _a.appName, instanceId = _a.instanceId, refreshInterval = _a.refreshInterval, _b = _a.StorageImpl, StorageImpl = _b === void 0 ? storage_1.Storage : _b, timeout = _a.timeout, headers = _a.headers;
var backupPath = _a.backupPath, url = _a.url, appName = _a.appName, instanceId = _a.instanceId, refreshInterval = _a.refreshInterval, _b = _a.StorageImpl, StorageImpl = _b === void 0 ? storage_1.Storage : _b, timeout = _a.timeout, headers = _a.headers, customHeadersFunction = _a.customHeadersFunction;
var _this = _super.call(this) || this;

@@ -33,2 +68,3 @@ _this.url = url;

_this.timeout = timeout;
_this.customHeadersFunction = customHeadersFunction;
_this.storage = new StorageImpl({ backupPath: backupPath, appName: appName });

@@ -66,40 +102,59 @@ _this.storage.on('error', function (err) { return _this.emit('error', err); });

Repository.prototype.fetch = function () {
var _this = this;
var url = url_1.resolve(this.url, './client/features');
request_1.get({
url: url,
etag: this.etag,
appName: this.appName,
timeout: this.timeout,
instanceId: this.instanceId,
headers: this.headers,
}, function (error, res, body) {
// start timer for next fetch
_this.timedFetch();
if (error) {
return _this.emit('error', error);
}
if (res.statusCode === 304) {
// No new data
return;
}
if (!(res.statusCode >= 200 && res.statusCode < 300)) {
return _this.emit('error', new Error("Response was not statusCode 2XX, but was " + res.statusCode));
}
try {
var data = JSON.parse(body);
var obj = data.features.reduce(function (o, feature) {
_this.validateFeature(feature);
o[feature.name] = feature;
return o;
}, {});
_this.storage.reset(obj);
_this.etag = Array.isArray(res.headers.etag)
? res.headers.etag.join(' ')
: res.headers.etag;
_this.emit('data');
}
catch (err) {
_this.emit('error', err);
}
return __awaiter(this, void 0, void 0, function () {
var url, headers, _a;
var _this = this;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
url = url_1.resolve(this.url, './client/features');
if (!this.customHeadersFunction) return [3 /*break*/, 2];
return [4 /*yield*/, this.customHeadersFunction()];
case 1:
_a = _b.sent();
return [3 /*break*/, 3];
case 2:
_a = this.headers;
_b.label = 3;
case 3:
headers = _a;
request_1.get({
url: url,
etag: this.etag,
appName: this.appName,
timeout: this.timeout,
instanceId: this.instanceId,
headers: headers,
}, function (error, res, body) {
// start timer for next fetch
_this.timedFetch();
if (error) {
return _this.emit('error', error);
}
if (res.statusCode === 304) {
// No new data
return;
}
if (!(res.statusCode >= 200 && res.statusCode < 300)) {
return _this.emit('error', new Error("Response was not statusCode 2XX, but was " + res.statusCode));
}
try {
var data = JSON.parse(body);
var obj = data.features.reduce(function (o, feature) {
_this.validateFeature(feature);
o[feature.name] = feature;
return o;
}, {});
_this.storage.reset(obj);
_this.etag = Array.isArray(res.headers.etag)
? res.headers.etag.join(' ')
: res.headers.etag;
_this.emit('data');
}
catch (err) {
_this.emit('error', err);
}
});
return [2 /*return*/];
}
});
});

@@ -106,0 +161,0 @@ };

@@ -8,5 +8,7 @@ import { RepositoryInterface } from './repository';

import { Variant } from './variant';
import { FallbackFunction } from './helpers';
export interface CustomHeaders {
[key: string]: string;
}
export declare type CustomHeadersFunction = () => Promise<CustomHeaders>;
export interface UnleashConfig {

@@ -23,2 +25,3 @@ appName: string;

customHeaders?: CustomHeaders;
customHeadersFunction?: CustomHeadersFunction;
timeout?: number;

@@ -36,4 +39,5 @@ repository?: RepositoryInterface;

private staticContext;
constructor({ appName, environment, instanceId, url, refreshInterval, metricsInterval, disableMetrics, backupPath, strategies, repository, customHeaders, timeout, }: UnleashConfig);
constructor({ appName, environment, instanceId, url, refreshInterval, metricsInterval, disableMetrics, backupPath, strategies, repository, customHeaders, customHeadersFunction, timeout, }: UnleashConfig);
destroy(): void;
isEnabled(name: string, context: Context, fallbackFunction?: FallbackFunction): boolean;
isEnabled(name: string, context: Context, fallbackValue?: boolean): boolean;

@@ -40,0 +44,0 @@ getVariant(name: string, context: any, fallbackVariant?: Variant): Variant;

@@ -25,2 +25,3 @@ "use strict";

var variant_1 = require("./variant");
var helpers_1 = require("./helpers");
var BACKUP_PATH = os_1.tmpdir();

@@ -30,3 +31,3 @@ var Unleash = /** @class */ (function (_super) {

function Unleash(_a) {
var appName = _a.appName, _b = _a.environment, environment = _b === void 0 ? 'default' : _b, instanceId = _a.instanceId, url = _a.url, _c = _a.refreshInterval, refreshInterval = _c === void 0 ? 15 * 1000 : _c, _d = _a.metricsInterval, metricsInterval = _d === void 0 ? 60 * 1000 : _d, _e = _a.disableMetrics, disableMetrics = _e === void 0 ? false : _e, _f = _a.backupPath, backupPath = _f === void 0 ? BACKUP_PATH : _f, _g = _a.strategies, strategies = _g === void 0 ? [] : _g, repository = _a.repository, customHeaders = _a.customHeaders, timeout = _a.timeout;
var appName = _a.appName, _b = _a.environment, environment = _b === void 0 ? 'default' : _b, instanceId = _a.instanceId, url = _a.url, _c = _a.refreshInterval, refreshInterval = _c === void 0 ? 15 * 1000 : _c, _d = _a.metricsInterval, metricsInterval = _d === void 0 ? 60 * 1000 : _d, _e = _a.disableMetrics, disableMetrics = _e === void 0 ? false : _e, _f = _a.backupPath, backupPath = _f === void 0 ? BACKUP_PATH : _f, _g = _a.strategies, strategies = _g === void 0 ? [] : _g, repository = _a.repository, customHeaders = _a.customHeaders, customHeadersFunction = _a.customHeadersFunction, timeout = _a.timeout;
var _this = _super.call(this) || this;

@@ -72,2 +73,3 @@ if (!url) {

headers: customHeaders,
customHeadersFunction: customHeadersFunction,
timeout: timeout,

@@ -97,2 +99,3 @@ });

headers: customHeaders,
customHeadersFunction: customHeadersFunction,
timeout: timeout,

@@ -123,10 +126,11 @@ });

};
Unleash.prototype.isEnabled = function (name, context, fallbackValue) {
Unleash.prototype.isEnabled = function (name, context, fallback) {
var enhancedContext = Object.assign({}, this.staticContext, context);
var fallbackFunc = helpers_1.createFallbackFunction(name, enhancedContext, fallback);
var result;
if (this.client !== undefined) {
result = this.client.isEnabled(name, enhancedContext, fallbackValue);
result = this.client.isEnabled(name, enhancedContext, fallbackFunc);
}
else {
result = typeof fallbackValue === 'boolean' ? fallbackValue : false;
result = fallbackFunc();
this.emit('warn', "Unleash has not been initialized yet. isEnabled(" + name + ") defaulted to " + result);

@@ -133,0 +137,0 @@ }

{
"name": "unleash-client",
"version": "3.2.9",
"version": "3.3.0",
"description": "Unleash Client for Node",

@@ -50,3 +50,3 @@ "license": "Apache-2.0",

"@types/request": "^2.0.8",
"@unleash/client-specification": "^3.2.0",
"@unleash/client-specification": "^3.3.0",
"ava": "^2.2.0",

@@ -53,0 +53,0 @@ "cross-env": "^6.0.0",

@@ -120,2 +120,5 @@ # unleash-client-node

- **customHeaders** - Provide a map(object) of custom headers to be sent to the unleash-server
- **customHeadersFunction** - Provide a function that return a Promise resolving as custom headers
to be sent to unleash-server. When options are set, this will take precedence over
`customHeaders` option.
- **timeout** - specify a timeout in milliseconds for outgoing HTTP requests. Defaults to 10000ms.

@@ -122,0 +125,0 @@ - **repository** - Provide a custom repository implementation to manage the underlying data

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc