New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@sanity/diff

Package Overview
Dependencies
Maintainers
35
Versions
1029
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@sanity/diff - npm Package Compare versions

Comparing version 2.25.5-next.6 to 2.26.1-purple-unicorn.560

lib/dts/src/calculate/diffArray.d.ts

862

lib/index.js

@@ -1,39 +0,835 @@

"use strict";
import { diff_match_patch, DIFF_INSERT, DIFF_DELETE, DIFF_EQUAL } from 'diff-match-patch';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _exportNames = {
diffInput: true,
wrap: true
};
Object.defineProperty(exports, "diffInput", {
enumerable: true,
get: function get() {
return _diffInput.diffInput;
}
});
Object.defineProperty(exports, "wrap", {
enumerable: true,
get: function get() {
return _inputWrappers.wrap;
}
});
function replaceProperty(parent, prop, value) {
delete parent[prop];
parent[prop] = value;
return value;
}
var _types = require("./types");
/*
* Longest common subsequence implementation, for diffing arrays
* Reference: http://en.wikipedia.org/wiki/Longest_common_subsequence_problem
*/
function getLongestCommonSubsequence(previous, next) {
const matrix = getLengthMatrix(previous, next);
const result = backtrack(matrix, previous, next);
return result;
}
function getLengthMatrix(previous, next) {
const len1 = previous.length;
const len2 = next.length;
let x = 0;
let y = 0;
// initialize empty matrix of len1+1 x len2+1
const matrix = new Array(len1 + 1);
for (x = 0; x < len1 + 1; x++) {
matrix[x] = [len2 + 1];
for (y = 0; y < len2 + 1; y++) {
matrix[x][y] = 0;
}
}
// save sequence lengths for each coordinate
for (x = 1; x < len1 + 1; x++) {
for (y = 1; y < len2 + 1; y++) {
if (previous[x - 1] === next[y - 1]) {
matrix[x][y] = matrix[x - 1][y - 1] + 1;
}
else {
matrix[x][y] = Math.max(matrix[x - 1][y], matrix[x][y - 1]);
}
}
}
return matrix;
}
function backtrack(matrix, previous, next) {
let prevIndex = previous.length;
let nextIndex = next.length;
const subsequence = {
sequence: [],
prevIndices: [],
nextIndices: [],
};
while (prevIndex !== 0 && nextIndex !== 0) {
const areEqual = previous[prevIndex - 1] === next[nextIndex - 1];
if (areEqual) {
subsequence.sequence.unshift(previous[prevIndex - 1]);
subsequence.prevIndices.unshift(prevIndex - 1);
subsequence.nextIndices.unshift(nextIndex - 1);
--prevIndex;
--nextIndex;
}
else {
const valueAtMatrixAbove = matrix[prevIndex][nextIndex - 1];
const valueAtMatrixLeft = matrix[prevIndex - 1][nextIndex];
if (valueAtMatrixAbove > valueAtMatrixLeft) {
--nextIndex;
}
else {
--prevIndex;
}
}
}
return subsequence;
}
Object.keys(_types).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
if (key in exports && exports[key] === _types[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function get() {
return _types[key];
function diffArray(fromInput, toInput, options) {
if (fromInput === toInput) {
const fromValue = fromInput.value;
const toValue = toInput.value;
return {
type: 'array',
action: 'unchanged',
isChanged: false,
fromValue,
toValue,
get items() {
const items = diffExactByPosition(fromInput, toInput, options);
if (!items)
throw new Error('invariant broken: equivalent input, but diff detected');
return replaceProperty(this, 'items', items);
},
};
}
});
});
// The key-ed approach should handle most cases (_key'ed objects, primitives):
const keyedA = indexByKey(fromInput);
const keyedB = indexByKey(toInput);
if (keyedA && keyedB) {
return diffArrayByKey(fromInput, keyedA, toInput, keyedB);
}
// Check if they are 100% equivalent:
const items = diffExactByPosition(fromInput, toInput, options);
if (items)
return buildArrayDiff(fromInput, toInput, items, false);
// Otherwise we create a diff where we model it as removing the from-items and adding the to-items.
return diffArrayByReinsert(fromInput, toInput);
}
function buildArrayDiff(fromInput, toInput, items, isChanged) {
const fromValue = fromInput.value;
const toValue = toInput.value;
return isChanged
? {
type: 'array',
action: 'changed',
isChanged: true,
fromValue,
toValue,
items,
annotation: toInput.annotation,
}
: {
type: 'array',
action: 'unchanged',
isChanged: false,
fromValue,
toValue,
items,
};
}
/**
* Diffes the two arrays by position. Returns an `items` array if they are unchanged, or undefined
* if there are any changes anywhere.
*/
function diffExactByPosition(fromInput, toInput, options) {
if (fromInput.length !== toInput.length) {
return undefined;
}
const items = [];
for (let idx = 0; idx < fromInput.length; idx++) {
const diff = diffInput(fromInput.at(idx), toInput.at(idx), options);
if (diff.isChanged) {
return undefined;
}
items.push({
fromIndex: idx,
toIndex: idx,
hasMoved: false,
diff,
annotation: toInput.annotationAt(idx),
});
}
return items;
}
function diffArrayByReinsert(fromInput, toInput, options) {
const items = [];
for (let idx = 0; idx < toInput.length; idx++) {
const input = toInput.at(idx);
items.push({
fromIndex: undefined,
toIndex: idx,
hasMoved: false,
diff: addedInput(input, undefined),
annotation: input.annotation,
});
}
for (let idx = 0; idx < fromInput.length; idx++) {
const input = fromInput.at(idx);
items.push({
fromIndex: idx,
toIndex: undefined,
hasMoved: false,
diff: removedInput(input, undefined),
annotation: input.annotation,
});
}
return buildArrayDiff(fromInput, toInput, items, true);
}
/**
* Diff an array when all the elements have _key in the same position.
*/
function diffArrayByKey(fromArray, fromKeyIndex, toArray, toKeyIndex, options) {
const items = [];
let isChanged = false;
function diffCommon(key, fromIndex, toIndex, hasMoved) {
deletePositionInIndex(fromKeyIndex.index, key, fromIndex);
deletePositionInIndex(toKeyIndex.index, key, toIndex);
const fromInput = fromArray.at(fromIndex);
const toInput = toArray.at(toIndex);
const diff = diffInput(fromInput, toInput);
items.push({
fromIndex,
toIndex,
hasMoved,
diff,
annotation: toArray.annotationAt(toIndex),
});
if (diff.isChanged || fromIndex !== toIndex) {
isChanged = true;
}
}
const lcs = getLongestCommonSubsequence(fromKeyIndex.keys, toKeyIndex.keys);
for (let fromIndex = 0; fromIndex < fromKeyIndex.keys.length; fromIndex++) {
const key = fromKeyIndex.keys[fromIndex];
const subsequenceIdx = lcs.prevIndices.indexOf(fromIndex);
if (subsequenceIdx !== -1) {
// Part of the common subsequence => hasMoved:false
diffCommon(key, fromIndex, lcs.nextIndices[subsequenceIdx], false);
continue;
}
// Not a part of the subsequence. Try to find another item which has the same key
// and also is not part of the common subsequence.
const toIndexes = toKeyIndex.index.get(key);
const toIndex = toIndexes && toIndexes.find((idx) => !lcs.nextIndices.includes(idx));
if (toIndex !== undefined) {
diffCommon(key, fromIndex, toIndex, true);
continue;
}
const input = fromArray.at(fromIndex);
items.push({
fromIndex,
toIndex: undefined,
hasMoved: false,
diff: removedInput(input, undefined),
annotation: fromArray.annotationAt(fromIndex),
});
isChanged = true;
}
// The remaining data in toKeyIndex are the new elements which has been added
for (const positions of toKeyIndex.index.values()) {
for (const toIndex of positions) {
const input = toArray.at(toIndex);
items.push({
fromIndex: undefined,
toIndex,
hasMoved: false,
diff: addedInput(input, undefined),
annotation: toArray.annotationAt(toIndex),
});
}
isChanged = true;
}
items.sort(compareItemDiff);
return buildArrayDiff(fromArray, toArray, items, isChanged);
}
function compareItemDiff(a, b) {
if (a.toIndex !== undefined && b.toIndex !== undefined) {
return a.toIndex - b.toIndex;
}
if (a.fromIndex !== undefined && b.fromIndex !== undefined) {
return a.fromIndex - b.fromIndex;
}
if (a.fromIndex !== undefined && b.toIndex !== undefined) {
// A was removed and B was added. Prefer to sort removals last.
return -1;
}
if (a.toIndex !== undefined && b.fromIndex !== undefined) {
// A was added and B was removed. Prefer to sort removals last.
return 1;
}
throw new Error('invalid item diff comparison');
}
function deletePositionInIndex(index, key, pos) {
const positions = index.get(key);
deleteArrayValue(positions, pos);
if (positions.length === 0) {
index.delete(key);
}
}
function deleteArrayValue(arr, value) {
const idx = arr.indexOf(value);
if (idx === -1)
throw new Error('value not found');
arr.splice(idx, 1);
}
/**
* Indexes the array by a key. This handles cases where the items are:
*
* - Objects with _key
* - Strings
* - Numbers
*/
function indexByKey(arr) {
const index = new Map();
const keys = [];
const length = arr.length;
for (let i = 0; i < length; i++) {
const item = arr.at(i);
let key = null;
switch (item.type) {
case 'string':
key = `s${item.value}`;
break;
case 'number':
key = item.value;
break;
case 'boolean':
key = item.value;
break;
case 'null':
key = 'n';
break;
case 'object':
{
const keyField = item.get('_key');
if (keyField && keyField.type === 'string') {
key = `k${keyField.value}`;
// We do not handle duplicate _key
if (index.has(key))
return undefined;
}
}
break;
}
// No key => abort
if (key === null)
return undefined;
keys.push(key);
let positions = index.get(key);
if (!positions) {
positions = [];
index.set(key, positions);
}
positions.push(i);
}
// All is good.
return { keys, index };
}
function removedArray(input, toValue, options) {
return {
type: 'array',
action: 'removed',
isChanged: true,
fromValue: input.value,
toValue,
annotation: input.annotation,
get items() {
const items = [];
for (let i = 0; i < input.length; i++) {
const item = input.at(i);
items.push({
fromIndex: i,
toIndex: undefined,
hasMoved: false,
diff: removedInput(item, undefined),
annotation: input.annotationAt(i),
});
}
return replaceProperty(this, 'items', items);
},
};
}
function addedArray(input, fromValue, options) {
return {
type: 'array',
action: 'added',
isChanged: true,
fromValue,
toValue: input.value,
annotation: input.annotation,
get items() {
const items = [];
for (let i = 0; i < input.length; i++) {
const item = input.at(i);
items.push({
fromIndex: undefined,
toIndex: i,
hasMoved: false,
diff: addedInput(item, undefined),
annotation: input.annotationAt(i),
});
}
return replaceProperty(this, 'items', items);
},
};
}
var _diffInput = require("./calculate/diffInput");
const dmp = new diff_match_patch();
function diffString(fromInput, toInput, options) {
const fromValue = fromInput.value;
const toValue = toInput.value;
if (fromValue === toValue) {
return {
type: 'string',
action: 'unchanged',
isChanged: false,
fromValue,
toValue,
segments: [{ type: 'stringSegment', action: 'unchanged', text: fromValue }],
};
}
return {
type: 'string',
action: 'changed',
isChanged: true,
fromValue,
toValue,
annotation: toInput.annotation,
// Compute and memoize string segments only when accessed
get segments() {
const segments = buildSegments(fromInput, toInput);
return replaceProperty(this, 'segments', segments);
},
};
}
function buildSegments(fromInput, toInput) {
const segments = [];
const dmpDiffs = dmp.diff_main(fromInput.value, toInput.value);
dmp.diff_cleanupSemantic(dmpDiffs);
let fromIdx = 0;
let toIdx = 0;
for (const [op, text] of dmpDiffs) {
switch (op) {
case DIFF_EQUAL:
segments.push({ type: 'stringSegment', action: 'unchanged', text });
fromIdx += text.length;
toIdx += text.length;
break;
case DIFF_DELETE:
for (const segment of fromInput.sliceAnnotation(fromIdx, fromIdx + text.length)) {
segments.push({
type: 'stringSegment',
action: 'removed',
text: segment.text,
annotation: segment.annotation,
});
}
fromIdx += text.length;
break;
case DIFF_INSERT:
for (const segment of toInput.sliceAnnotation(toIdx, toIdx + text.length)) {
segments.push({
type: 'stringSegment',
action: 'added',
text: segment.text,
annotation: segment.annotation,
});
}
toIdx += text.length;
break;
default:
throw new Error(`Unhandled diff-match-patch operation "${op}"`);
}
}
return segments;
}
function removedString(input, toValue, options) {
return {
type: 'string',
action: 'removed',
isChanged: true,
fromValue: input.value,
toValue,
annotation: input.annotation,
get segments() {
const segments = input
.sliceAnnotation(0, input.value.length)
.map((segment) => ({ type: 'stringSegment', action: 'removed', ...segment }));
return replaceProperty(this, 'segments', segments);
},
};
}
function addedString(input, fromValue, options) {
return {
type: 'string',
action: 'added',
isChanged: true,
fromValue,
toValue: input.value,
annotation: input.annotation,
get segments() {
const segments = input
.sliceAnnotation(0, input.value.length)
.map((segment) => ({ type: 'stringSegment', action: 'added', ...segment }));
return replaceProperty(this, 'segments', segments);
},
};
}
var _inputWrappers = require("./inputWrappers");
function diffTypeChange(fromInput, toInput, options) {
return {
type: 'typeChange',
action: 'changed',
isChanged: true,
fromType: fromInput.type,
fromValue: fromInput.value,
fromDiff: removedInput(fromInput, undefined),
toType: toInput.type,
toValue: toInput.value,
toDiff: addedInput(toInput, undefined),
annotation: toInput.annotation,
};
}
const ignoredFields = new Set(['_id', '_type', '_createdAt', '_updatedAt', '_rev', '_weak']);
function diffObject(fromInput, toInput, options) {
const fields = {};
let isChanged = false;
for (const key of fromInput.keys) {
if (ignoredFields.has(key))
continue;
const fromField = fromInput.get(key);
const toField = toInput.get(key);
if (toField) {
const fieldDiff = diffInput(fromField, toField, options);
fields[key] = fieldDiff;
if (fieldDiff.isChanged)
isChanged = true;
}
else {
fields[key] = removedInput(fromField, undefined);
isChanged = true;
}
}
for (const key of toInput.keys) {
if (ignoredFields.has(key))
continue;
// Already handled above
if (fromInput.get(key))
continue;
const toField = toInput.get(key);
fields[key] = addedInput(toField, undefined);
isChanged = true;
}
const fromValue = fromInput.value;
const toValue = toInput.value;
if (!isChanged) {
return {
type: 'object',
action: 'unchanged',
isChanged: false,
fromValue,
toValue,
fields,
};
}
return {
type: 'object',
action: 'changed',
isChanged: true,
fromValue,
toValue,
fields,
annotation: toInput.annotation,
};
}
function removedObject(input, toValue, options) {
return {
type: 'object',
action: 'removed',
isChanged: true,
fromValue: input.value,
toValue,
annotation: input.annotation,
get fields() {
const fields = {};
for (const key of input.keys) {
const value = input.get(key);
fields[key] = removedInput(value, undefined);
}
return replaceProperty(this, 'fields', fields);
},
};
}
function addedObject(input, fromValue, options) {
return {
type: 'object',
action: 'added',
isChanged: true,
fromValue,
toValue: input.value,
annotation: input.annotation,
get fields() {
const fields = {};
for (const key of input.keys) {
const value = input.get(key);
fields[key] = addedInput(value, undefined);
}
return replaceProperty(this, 'fields', fields);
},
};
}
function diffNumber(fromInput, toInput, options) {
const fromValue = fromInput.value;
const toValue = toInput.value;
const type = fromInput.type;
if (fromValue === toValue)
return {
type,
action: 'unchanged',
fromValue,
toValue,
isChanged: false,
};
return {
type: fromInput.type,
action: 'changed',
isChanged: true,
fromValue: fromValue,
toValue: toValue,
annotation: toInput.annotation,
};
}
function diffBoolean(fromInput, toInput, options) {
const fromValue = fromInput.value;
const toValue = toInput.value;
const type = fromInput.type;
if (fromValue === toValue)
return {
type,
action: 'unchanged',
fromValue,
toValue,
isChanged: false,
};
return {
type: fromInput.type,
action: 'changed',
isChanged: true,
fromValue: fromValue,
toValue: toValue,
annotation: toInput.annotation,
};
}
function diffInput(fromInput, toInput, options = {}) {
if (fromInput.type !== toInput.type) {
if (fromInput.type === 'null') {
return addedInput(toInput, null);
}
if (toInput.type === 'null') {
return removedInput(fromInput, null);
}
return diffTypeChange(fromInput, toInput);
}
return diffWithType(fromInput.type, fromInput, toInput, options);
}
function diffWithType(type, fromInput, toInput, options) {
switch (type) {
case 'null':
return {
type: 'null',
action: 'unchanged',
isChanged: false,
toValue: null,
fromValue: null,
};
case 'boolean':
return diffBoolean(fromInput, toInput);
case 'number':
return diffNumber(fromInput, toInput);
case 'string':
return diffString(fromInput, toInput);
case 'array':
return diffArray(fromInput, toInput, options);
case 'object':
return diffObject(fromInput, toInput, options);
default:
throw new Error(`Unhandled diff type "${type}"`);
}
}
function removedInput(input, toValue, options) {
switch (input.type) {
case 'null':
return {
type: 'null',
action: 'removed',
isChanged: true,
fromValue: null,
toValue,
annotation: input.annotation,
};
case 'boolean':
return {
type: 'boolean',
action: 'removed',
isChanged: true,
fromValue: input.value,
toValue,
annotation: input.annotation,
};
case 'number':
return {
type: 'number',
action: 'removed',
isChanged: true,
fromValue: input.value,
toValue,
annotation: input.annotation,
};
case 'string':
return removedString(input, toValue);
case 'array':
return removedArray(input, toValue);
case 'object':
return removedObject(input, toValue);
default:
throw new Error('Unhandled diff type');
}
}
function addedInput(input, fromValue, options) {
switch (input.type) {
case 'null':
return {
type: 'null',
action: 'added',
isChanged: true,
fromValue,
toValue: null,
annotation: input.annotation,
};
case 'boolean':
return {
type: 'boolean',
action: 'added',
isChanged: true,
fromValue,
toValue: input.value,
annotation: input.annotation,
};
case 'number':
return {
type: 'number',
action: 'added',
isChanged: true,
fromValue,
toValue: input.value,
annotation: input.annotation,
};
case 'string':
return addedString(input, fromValue);
case 'array':
return addedArray(input, fromValue);
case 'object':
return addedObject(input, fromValue);
default:
throw new Error('Unhandled diff type');
}
}
class ArrayWrapper {
type = 'array';
length;
value;
annotation;
elements = [];
constructor(value, annotation) {
this.annotation = annotation;
this.value = value;
this.length = value.length;
}
at(idx) {
if (idx >= this.length)
throw new Error('out of bounds');
const input = this.elements[idx];
if (input) {
return input;
}
return (this.elements[idx] = wrap(this.value[idx], this.annotation));
}
annotationAt() {
return this.annotation;
}
}
class ObjectWrapper {
type = 'object';
value;
keys;
annotation;
fields = {};
constructor(value, annotation) {
this.value = value;
this.annotation = annotation;
this.keys = Object.keys(value);
}
get(key) {
const input = this.fields[key];
if (input) {
return input;
}
if (!this.value.hasOwnProperty(key)) {
return undefined;
}
const raw = this.value[key];
return (this.fields[key] = wrap(raw, this.annotation));
}
}
class StringWrapper {
type = 'string';
value;
annotation;
constructor(value, annotation) {
this.value = value;
this.annotation = annotation;
}
sliceAnnotation(start, end) {
return [{ text: this.value.slice(start, end), annotation: this.annotation }];
}
}
class BasicWrapper {
type;
value;
annotation;
constructor(type, value, annotation) {
this.type = type;
this.value = value;
this.annotation = annotation;
}
}
function wrap(input, annotation) {
if (Array.isArray(input)) {
return new ArrayWrapper(input, annotation);
}
else if (input === null) {
return new BasicWrapper('null', input, annotation);
}
const type = typeof input;
switch (type) {
case 'number':
return new BasicWrapper(type, input, annotation);
case 'boolean':
return new BasicWrapper(type, input, annotation);
case 'object':
return new ObjectWrapper(input, annotation);
case 'string':
return new StringWrapper(input, annotation);
default:
throw new Error(`cannot wrap value of type: ${type}`);
}
}
export { diffInput, wrap };
//# sourceMappingURL=index.js.map

23

package.json
{
"name": "@sanity/diff",
"version": "2.25.5-next.6+9e5a11cb0",
"version": "2.26.1-purple-unicorn.560+8672b3b5e3",
"description": "Generates diffs between documents and primitive types",
"main": "./lib/index.js",
"types": "./dist/dts",
"main": "./lib/index.cjs",
"module": "./lib/index.js",
"exports": {
".": {
"source": "./src/index.ts",
"require": "./lib/index.cjs",
"default": "./lib/index.js"
}
},
"types": "./lib/dts/src/index.d.ts",
"author": "Sanity.io <hello@sanity.io>",

@@ -13,3 +21,6 @@ "engines": {

"scripts": {
"clean": "rimraf lib coverage"
"build": "../../../bin/pkg-utils bundle",
"clean": "rimraf lib coverage",
"prebuild": "yarn clean",
"watch": "../../../bin/pkg-utils bundle --watch"
},

@@ -31,3 +42,3 @@ "keywords": [

"devDependencies": {
"rimraf": "^2.7.1"
"rimraf": "^3.0.2"
},

@@ -43,3 +54,3 @@ "repository": {

"homepage": "https://www.sanity.io/",
"gitHead": "9e5a11cb084b22b074ed0f204f9cc640b489529d"
"gitHead": "8672b3b5e39b2aeea500d16ac197b068ba7c943c"
}

@@ -85,13 +85,7 @@ export type DiffOptions = Record<string, never>

type FullDiff<A, V, P> = (
| AddedDiff<A, V>
| RemovedDiff<A, V>
| ChangedDiff<A, V>
| UnchangedDiff<A, V>
) &
P
type FullDiff<A, V> = AddedDiff<A, V> | RemovedDiff<A, V> | ChangedDiff<A, V> | UnchangedDiff<A, V>
export type StringDiff<A> = FullDiff<A, string, {type: 'string'; segments: StringDiffSegment<A>[]}>
export type NumberDiff<A> = FullDiff<A, number, {type: 'number'}>
export type BooleanDiff<A> = FullDiff<A, boolean, {type: 'boolean'}>
export type StringDiff<A> = FullDiff<A, string> & {type: 'string'; segments: StringDiffSegment<A>[]}
export type NumberDiff<A> = FullDiff<A, number> & {type: 'number'}
export type BooleanDiff<A> = FullDiff<A, boolean> & {type: 'boolean'}
export type TypeChangeDiff<A> = {

@@ -113,13 +107,11 @@ type: 'typeChange'

export type ObjectDiff<A, T extends object = Record<string, any>> = FullDiff<
A,
T,
{
type: 'object'
fields: Record<keyof T, Diff<A>>
}
>
export type ArrayDiff<A, V = unknown> = FullDiff<A, V[], {type: 'array'; items: ItemDiff<A>[]}>
export type NullDiff<A> = FullDiff<A, null, {type: 'null'}>
export type ObjectDiff<A, T extends object = Record<string, any>> = FullDiff<A, T> & {
type: 'object'
fields: Record<keyof T, Diff<A>>
}
export type ArrayDiff<A, V = unknown> = FullDiff<A, V[]> & {type: 'array'; items: ItemDiff<A>[]}
export type NullDiff<A> = FullDiff<A, null> & {type: 'null'}
export type Diff<A> =

@@ -126,0 +118,0 @@ | NullDiff<A>

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