mirror of
https://github.com/n8n-io/n8n.git
synced 2025-01-11 12:57:29 -08:00
fix(core): Fix data transformation function that are reported not to work properly (#5338)
* 🔥 Remove test extensions
* 🚧 Add test description
* 📘 Expand types
* ⚡ Export extensions
* ⚡ Export collection
* ⚡ Mark all proxies
* ✏️ Rename for clarity
* ⚡ Export from barrel
* ✨ Create datatype completions
* ⚡ Mount datatype completions
* 🧪 Adjust tests
* ⚡ Add `path` prop
* 🔥 Remove `()` from completion labels
* ⚡ Filter out completions for pseudo-proxies
* 🐛 Fix method error
* ⚡ Add metrics
* ✏️ Improve naming
* ✨ Start completion on empty resolvable
* ✨ Implement completion previews
* ⚡ Break out completion manager
* ⚡ Implement in expression editor modal
* ✏️ Improve naming
* ⚡ Filter out irrelevant completions
* ✨ Add preview hint
* ✏️ Improve comments
* 🎨 Style preview hint
* ⚡ Expand `hasNoParams`
* ⚡ Add spacing for readability
* ⚡ Add error codes
* ✏️ Add comment
* 🐛 Fix Esc behavior
* ⚡ Parse Unicode
* ⚡ Throw on invalid `DateTime`
* ⚡ Fix second root completion detection
* ⚡ Switch message at completable prefix position
* 🐛 Fix function names for non-dev build
* 🐛 Fix `json` handling
* 🔥 Comment out previews
* ♻️ Apply feedback
* 🔥 Remove extensions
* 🚚 Rename extensions
* ⚡ Adjust some implementations
* 🔥 Remove dummy extensions
* 🐛 Fix object regex
* ♻️ Apply feedback
* ✏️ Fix typos
* ✏️ Add `fn is not a function` message
* 🔥 Remove check
* ✨ Add `isNotEmpty` for objects
* 🚚 Rename `global` to `alpha`
* 🔥 Remove `encrypt`
* ⚡ Restore `is not a function` error
* ⚡ Support `week` on `extract()`
* 🧪 Fix tests
* ⚡ Add validation to some string extensions
* ⚡ Validate number arrays in some extensions
* 🧪 Fix tests
* ✏️ Improve error message
* ⏪ Revert extensions framework changes
* 🧹 Previews cleanup
* ⚡ Condense blank completions
* ⚡ Refactor dollar completions
* ⚡ Refactor non-dollar completions
* ⚡ Refactor Luxon completions
* ⚡ Refactor datatype completions
* ⚡ Use `DATETIMEUNIT_MAP`
* ✏️ Update test description
* ⏪ Revert "Use `DATETIMEUNIT_MAP`"
This reverts commit 472a77df5c
.
* 🧪 Add tests
* ♻️ Restore generic extensions
* 🔥 Remove logs
* 🧪 Expand tests
* ✨ Add `Math` completions
* ✏️ List breaking change
* ⚡ Add doc tooltips
* 🐛 Fix node selector regex
* 🐛 Fix `context` resolution
* 🐛 Allow dollar completions in args
* ⚡ Make numeric array methods context-dependent
* 📝 Adjust docs
* 🐛 Fix selector ref
* ⚡ Surface error for valid URL
* 🐛 Disallow whitespace in `isEmail` check
* 🧪 Fix test for `isUrl`
* ⚡ Add comma validator in `toFloat`
* ⚡ Add validation to `$jmespath()`
* ⏪ Revert valid URL error
* ⚡ Adjust `$jmespath()` validation
* 🧪 Adjust `isUrl` test
* ⚡ Remove `{}` and `[]` from compact
* ✏️ Update docs
* 🚚 Rename `stripTags` to `removeTags`
* ⚡ Do not inject whitespace inside resolvable
* ⚡ Make completions aware of `()`
* ✏️ Add note
* ⚡ Update sorting
* ⚡ Hide active node name from node selector
* 🔥 Remove `length()` and its aliases
* ⚡ Validate non-zero for `chunk`
* ✏️ Reword all error messages
* 🐛 Fix `$now` and `$today`
* ⚡ Simplify with `stripExcessParens`
* ⚡ Fold luxon into datatype
* 🧪 Clean up tests
* 🔥 Remove tests for removed methods
* 👕 Fix type
* ⬆️ Upgrade lang pack
* ⏪ Undo change to `vitest` command
* 🔥 Remove unused method
* ⚡ Separate `return` line
* ✏️ Improve description
* 🧪 Expand tests for initial-only completions
* 🧪 Add bracket-aware completions
* ⚡ Make check for `all()` stricter
* ✏️ Adjust explanatory comments
* 🔥 Remove unneded copy
* 🔥 Remove outdated comment
* ⚡ Make naming consistent
* ✏️ Update comments
* ⚡ Improve URL scheme check
* ✏️ Add comment
* 🚚 Move extension
* ✏️ Update `BREAKING-CHANGES.md`
* ✏️ Update upcoming version
* ✏️ Fix grammar
* ✏️ Shorten message
* 🐛 Fix `Esc` behavior
* 🐛 Fix `isNumeric`
* ✨ Using UTC to handle-dates on back-end
* ✅ Added more unit tests for date extensions
* ⚡ Not using `JSON.stringify` to render dates
* ⚡ Using `deep-equal` library instead of our `deepCompare` function
* ✅ Adding more tests to array extensions
* ⚡ Fixing `inBetween` extension function
* ✅ Added tests for `.inBetween()`
* ⚡ Updating `isEven` and `isOdd` to throw for floats
* ⚡ Updating `Array.merge()` so it works without arguments
* 🔀 Fixing leftover merge confilct
* ⚡ Updating `removeFieldsContaining` and `keepFieldsContaining` to throw on empty strings
* ⚡ Fixing `pluck()` so it returns only plucked values
* ⬆️ Updating pnpm lockfile
* 👕 Fixing lint errors
* ⚡ Using workflow timezone to display dates
* ✔️ Updating tests with workflow timezone
* ⚡ Not using system timezone when creating Luxon dates
* ⚡ Updating `merge()` and `pluck()` array functions
* 🔀 Sync with `master`: Removing code that was preserved during merge
* ⚡ Updating `.pluck()` to return full array if no arguments are passed
* ⚡ Updating `keepFieldsContaining` and `merge` object functions
* ⚡ Using week as default for `date.extract()`
* ✅ Adding more test cases for DT functions
* ⚡ Removing `Object.merge` extension function. Adding missing `deep-equal` dependency
* ⚡ Handling `toDate` case when time component is not specified
* ⚡ Using workflow's timezone to render dates in output panel, updated unit tests after removing `Object.merge` function
* ⚡ Not parsing numbers as dates
* 👕 Fixing lint errors
* ⚡ Fixing a typo
* ⚡ Making date detection more strict so only stringified dates are getting converted
* 👌 Addressing PR feedback
* 🔥 Removing leftover comment
---------
Co-authored-by: Iván Ovejero <ivov.src@gmail.com>
This commit is contained in:
parent
83505cb0d4
commit
0cf45bc4c8
|
@ -73,13 +73,14 @@ import VueJsonPretty from 'vue-json-pretty';
|
||||||
import { LOCAL_STORAGE_MAPPING_FLAG } from '@/constants';
|
import { LOCAL_STORAGE_MAPPING_FLAG } from '@/constants';
|
||||||
import { IDataObject, INodeExecutionData } from 'n8n-workflow';
|
import { IDataObject, INodeExecutionData } from 'n8n-workflow';
|
||||||
import Draggable from '@/components/Draggable.vue';
|
import Draggable from '@/components/Draggable.vue';
|
||||||
import { convertPath, executionDataToJson, isString, shorten } from '@/utils';
|
import { parseDate, executionDataToJson, isString, shorten } from '@/utils';
|
||||||
import { INodeUi } from '@/Interface';
|
import { INodeUi } from '@/Interface';
|
||||||
import { externalHooks } from '@/mixins/externalHooks';
|
import { externalHooks } from '@/mixins/externalHooks';
|
||||||
import { mapStores } from 'pinia';
|
import { mapStores } from 'pinia';
|
||||||
import { useNDVStore } from '@/stores/ndv';
|
import { useNDVStore } from '@/stores/ndv';
|
||||||
import MappingPill from './MappingPill.vue';
|
import MappingPill from './MappingPill.vue';
|
||||||
import { getMappedExpression } from '@/utils/mappingUtils';
|
import { getMappedExpression } from '@/utils/mappingUtils';
|
||||||
|
import { useWorkflowsStore } from '@/stores/workflows';
|
||||||
|
|
||||||
const runDataJsonActions = () => import('@/components/RunDataJsonActions.vue');
|
const runDataJsonActions = () => import('@/components/RunDataJsonActions.vue');
|
||||||
|
|
||||||
|
@ -149,7 +150,7 @@ export default mixins(externalHooks).extend({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapStores(useNDVStore),
|
...mapStores(useNDVStore, useWorkflowsStore),
|
||||||
jsonData(): IDataObject[] {
|
jsonData(): IDataObject[] {
|
||||||
return executionDataToJson(this.inputData);
|
return executionDataToJson(this.inputData);
|
||||||
},
|
},
|
||||||
|
@ -209,7 +210,11 @@ export default mixins(externalHooks).extend({
|
||||||
}, 1000); // ensure dest data gets set if drop
|
}, 1000); // ensure dest data gets set if drop
|
||||||
},
|
},
|
||||||
getContent(value: unknown): string {
|
getContent(value: unknown): string {
|
||||||
return isString(value) ? `"${value}"` : JSON.stringify(value);
|
if (isString(value)) {
|
||||||
|
const parsedDate = parseDate(value, this.workflowsStore.workflow.settings?.timezone);
|
||||||
|
return parsedDate ? parsedDate.toString() : `"${value}"`;
|
||||||
|
}
|
||||||
|
return JSON.stringify(value);
|
||||||
},
|
},
|
||||||
getListItemName(path: string): string {
|
getListItemName(path: string): string {
|
||||||
return path.replace(/^(\["?\d"?]\.?)/g, '');
|
return path.replace(/^(\["?\d"?]\.?)/g, '');
|
||||||
|
|
|
@ -159,7 +159,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
/* eslint-disable prefer-spread */
|
/* eslint-disable prefer-spread */
|
||||||
import { INodeUi, ITableData, NDVState } from '@/Interface';
|
import { INodeUi, ITableData, NDVState } from '@/Interface';
|
||||||
import { getPairedItemId } from '@/utils';
|
import { getPairedItemId, parseDate } from '@/utils';
|
||||||
import Vue, { PropType } from 'vue';
|
import Vue, { PropType } from 'vue';
|
||||||
import mixins from 'vue-typed-mixins';
|
import mixins from 'vue-typed-mixins';
|
||||||
import { GenericValue, IDataObject, INodeExecutionData } from 'n8n-workflow';
|
import { GenericValue, IDataObject, INodeExecutionData } from 'n8n-workflow';
|
||||||
|
@ -171,6 +171,7 @@ import { useWorkflowsStore } from '@/stores/workflows';
|
||||||
import { useNDVStore } from '@/stores/ndv';
|
import { useNDVStore } from '@/stores/ndv';
|
||||||
import MappingPill from './MappingPill.vue';
|
import MappingPill from './MappingPill.vue';
|
||||||
import { getMappedExpression } from '@/utils/mappingUtils';
|
import { getMappedExpression } from '@/utils/mappingUtils';
|
||||||
|
import { DateTime } from 'luxon';
|
||||||
|
|
||||||
const MAX_COLUMNS_LIMIT = 40;
|
const MAX_COLUMNS_LIMIT = 40;
|
||||||
|
|
||||||
|
@ -365,6 +366,10 @@ export default mixins(externalHooks).extend({
|
||||||
return this.$locale.baseText('runData.emptyString');
|
return this.$locale.baseText('runData.emptyString');
|
||||||
}
|
}
|
||||||
if (typeof value === 'string') {
|
if (typeof value === 'string') {
|
||||||
|
const parsedDate = parseDate(value, this.workflowsStore.workflow.settings?.timezone);
|
||||||
|
if (parsedDate) {
|
||||||
|
return parsedDate.toString();
|
||||||
|
}
|
||||||
return value.replaceAll('\n', '\\n');
|
return value.replaceAll('\n', '\\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,8 @@ import { IDataObject, jsonParse } from 'n8n-workflow';
|
||||||
import { Schema, Optional, Primitives } from '@/Interface';
|
import { Schema, Optional, Primitives } from '@/Interface';
|
||||||
import { isObj } from '@/utils/typeGuards';
|
import { isObj } from '@/utils/typeGuards';
|
||||||
import { generatePath } from '@/utils/mappingUtils';
|
import { generatePath } from '@/utils/mappingUtils';
|
||||||
|
import { DateTime } from 'luxon';
|
||||||
|
import { useWorkflowsStore } from '@/stores/workflows';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Constants and utility functions than can be used to manipulate different data types and objects
|
Constants and utility functions than can be used to manipulate different data types and objects
|
||||||
|
@ -247,3 +249,17 @@ export const getSchema = (input: Optional<Primitives | object>, path = ''): Sche
|
||||||
|
|
||||||
return schema;
|
return schema;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Convert UTC dates that come from back-end to workflow timezone
|
||||||
|
export const parseDate = (input: string, timezone: string | undefined): DateTime | null => {
|
||||||
|
const isUTCDate = /\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)/.test(
|
||||||
|
input,
|
||||||
|
);
|
||||||
|
if (isUTCDate) {
|
||||||
|
const date = new Date(Date.parse(input));
|
||||||
|
if (date.toString() !== 'Invalid Date') {
|
||||||
|
return DateTime.fromJSDate(date, { zone: timezone });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@n8n_io/eslint-config": "",
|
"@n8n_io/eslint-config": "",
|
||||||
"@types/crypto-js": "^4.1.1",
|
"@types/crypto-js": "^4.1.1",
|
||||||
|
"@types/deep-equal": "^1.0.1",
|
||||||
"@types/express": "^4.17.6",
|
"@types/express": "^4.17.6",
|
||||||
"@types/jmespath": "^0.15.0",
|
"@types/jmespath": "^0.15.0",
|
||||||
"@types/lodash.get": "^4.4.6",
|
"@types/lodash.get": "^4.4.6",
|
||||||
|
@ -54,6 +55,7 @@
|
||||||
"@n8n_io/riot-tmpl": "^2.0.0",
|
"@n8n_io/riot-tmpl": "^2.0.0",
|
||||||
"ast-types": "0.15.2",
|
"ast-types": "0.15.2",
|
||||||
"crypto-js": "^4.1.1",
|
"crypto-js": "^4.1.1",
|
||||||
|
"deep-equal": "^2.2.0",
|
||||||
"esprima-next": "5.8.4",
|
"esprima-next": "5.8.4",
|
||||||
"jmespath": "^0.16.0",
|
"jmespath": "^0.16.0",
|
||||||
"js-base64": "^3.7.2",
|
"js-base64": "^3.7.2",
|
||||||
|
|
|
@ -60,11 +60,21 @@ export class Expression {
|
||||||
convertObjectValueToString(value: object): string {
|
convertObjectValueToString(value: object): string {
|
||||||
const typeName = Array.isArray(value) ? 'Array' : 'Object';
|
const typeName = Array.isArray(value) ? 'Array' : 'Object';
|
||||||
|
|
||||||
if (DateTime.isDateTime(value) && value.invalidReason !== null) {
|
if (value instanceof DateTime && value.invalidReason !== null) {
|
||||||
throw new Error('invalid DateTime');
|
throw new Error('invalid DateTime');
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = JSON.stringify(value)
|
let result = '';
|
||||||
|
if (value instanceof Date) {
|
||||||
|
// We don't want to use JSON.stringify for dates since it disregards workflow timezone
|
||||||
|
result = DateTime.fromJSDate(value, {
|
||||||
|
zone: this.workflow.settings.timezone?.toString() ?? 'default',
|
||||||
|
}).toISO();
|
||||||
|
} else {
|
||||||
|
result = JSON.stringify(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
result = result
|
||||||
.replace(/,"/g, ', "') // spacing for
|
.replace(/,"/g, ', "') // spacing for
|
||||||
.replace(/":/g, '": '); // readability
|
.replace(/":/g, '": '); // readability
|
||||||
|
|
||||||
|
|
|
@ -1,87 +1,7 @@
|
||||||
import { ExpressionError, ExpressionExtensionError } from '../ExpressionError';
|
import { ExpressionError, ExpressionExtensionError } from '../ExpressionError';
|
||||||
import type { ExtensionMap } from './Extensions';
|
import type { ExtensionMap } from './Extensions';
|
||||||
import { compact as oCompact, merge as oMerge } from './ObjectExtensions';
|
import { compact as oCompact } from './ObjectExtensions';
|
||||||
|
import deepEqual from 'deep-equal';
|
||||||
function deepCompare(left: unknown, right: unknown): boolean {
|
|
||||||
if (left === right) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check to see if they're the basic type
|
|
||||||
if (typeof left !== typeof right) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof left === 'number' && isNaN(left) && isNaN(right as number)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Explicitly return false if certain primitives don't equal each other
|
|
||||||
if (['number', 'string', 'bigint', 'boolean', 'symbol'].includes(typeof left) && left !== right) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Quickly check how many properties each has to avoid checking obviously mismatching
|
|
||||||
// objects
|
|
||||||
if (Object.keys(left as object).length !== Object.keys(right as object).length) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Quickly check if they're arrays
|
|
||||||
if (Array.isArray(left) !== Array.isArray(right)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if arrays are equal, ordering is important
|
|
||||||
if (Array.isArray(left)) {
|
|
||||||
if (left.length !== (right as unknown[]).length) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return left.every((v, i) => deepCompare(v, (right as object[])[i]));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check right first quickly. This is to see if we have mismatched properties.
|
|
||||||
// We'll check the left more indepth later to cover all our bases.
|
|
||||||
for (const key in right as object) {
|
|
||||||
if ((left as object).hasOwnProperty(key) !== (right as object).hasOwnProperty(key)) {
|
|
||||||
return false;
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
|
|
||||||
} else if (typeof (left as any)[key] !== typeof (right as any)[key]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check left more in depth
|
|
||||||
for (const key in left as object) {
|
|
||||||
if ((left as object).hasOwnProperty(key) !== (right as object).hasOwnProperty(key)) {
|
|
||||||
return false;
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
|
|
||||||
} else if (typeof (left as any)[key] !== typeof (right as any)[key]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
|
|
||||||
typeof (left as any)[key] === 'object'
|
|
||||||
) {
|
|
||||||
if (
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
|
|
||||||
(left as any)[key] !== (right as any)[key] &&
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
|
|
||||||
!deepCompare((left as any)[key], (right as any)[key])
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
|
|
||||||
if ((left as any)[key] !== (right as any)[key]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function first(value: unknown[]): unknown {
|
function first(value: unknown[]): unknown {
|
||||||
return value[0];
|
return value[0];
|
||||||
|
@ -103,18 +23,26 @@ function pluck(value: unknown[], extraArgs: unknown[]): unknown[] {
|
||||||
if (!Array.isArray(extraArgs)) {
|
if (!Array.isArray(extraArgs)) {
|
||||||
throw new ExpressionError('arguments must be passed to pluck');
|
throw new ExpressionError('arguments must be passed to pluck');
|
||||||
}
|
}
|
||||||
const fieldsToPluck = extraArgs;
|
if (!extraArgs || extraArgs.length === 0) {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
return value;
|
||||||
return (value as any[]).map((element: object) => {
|
|
||||||
const entries = Object.entries(element);
|
|
||||||
return entries.reduce((p, c) => {
|
|
||||||
const [key, val] = c as [string, Date | string | number];
|
|
||||||
if (fieldsToPluck.includes(key)) {
|
|
||||||
Object.assign(p, { [key]: val });
|
|
||||||
}
|
}
|
||||||
return p;
|
const plucked = value.reduce<unknown[]>((pluckedFromObject, current) => {
|
||||||
}, {});
|
if (current && typeof current === 'object') {
|
||||||
}) as unknown[];
|
const p: unknown[] = [];
|
||||||
|
Object.keys(current).forEach((k) => {
|
||||||
|
extraArgs.forEach((field: string) => {
|
||||||
|
if (current && field === k) {
|
||||||
|
p.push((current as { [key: string]: unknown })[k]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
if (p.length > 0) {
|
||||||
|
pluckedFromObject.push(p.length === 1 ? p[0] : p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pluckedFromObject;
|
||||||
|
}, new Array<unknown>());
|
||||||
|
return plucked;
|
||||||
}
|
}
|
||||||
|
|
||||||
function randomItem(value: unknown[]): unknown {
|
function randomItem(value: unknown[]): unknown {
|
||||||
|
@ -127,8 +55,13 @@ function unique(value: unknown[], extraArgs: string[]): unknown[] {
|
||||||
return value.reduce<unknown[]>((l, v) => {
|
return value.reduce<unknown[]>((l, v) => {
|
||||||
if (typeof v === 'object' && v !== null && extraArgs.every((i) => i in v)) {
|
if (typeof v === 'object' && v !== null && extraArgs.every((i) => i in v)) {
|
||||||
const alreadySeen = l.find((i) =>
|
const alreadySeen = l.find((i) =>
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
|
extraArgs.every((j) =>
|
||||||
extraArgs.every((j) => deepCompare((i as any)[j], (v as any)[j])),
|
deepEqual(
|
||||||
|
(i as Record<string, unknown>)[j],
|
||||||
|
(v as Record<string, unknown>, { strict: true })[j],
|
||||||
|
{ strict: true },
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
if (!alreadySeen) {
|
if (!alreadySeen) {
|
||||||
l.push(v);
|
l.push(v);
|
||||||
|
@ -138,7 +71,7 @@ function unique(value: unknown[], extraArgs: string[]): unknown[] {
|
||||||
}, []);
|
}, []);
|
||||||
}
|
}
|
||||||
return value.reduce<unknown[]>((l, v) => {
|
return value.reduce<unknown[]>((l, v) => {
|
||||||
if (l.findIndex((i) => deepCompare(i, v)) === -1) {
|
if (l.findIndex((i) => deepEqual(i, v, { strict: true })) === -1) {
|
||||||
l.push(v);
|
l.push(v);
|
||||||
}
|
}
|
||||||
return l;
|
return l;
|
||||||
|
@ -281,28 +214,58 @@ function renameKeys(value: unknown[], extraArgs: string[]): unknown[] {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function merge(value: unknown[], extraArgs: unknown[][]): unknown[] {
|
function mergeObjects(value: Record<string, unknown>, extraArgs: unknown[]): unknown {
|
||||||
|
const [other] = extraArgs;
|
||||||
|
|
||||||
|
if (!other) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof other !== 'object') {
|
||||||
|
throw new ExpressionExtensionError('merge(): expected object arg');
|
||||||
|
}
|
||||||
|
|
||||||
|
const newObject = { ...value };
|
||||||
|
for (const [key, val] of Object.entries(other)) {
|
||||||
|
if (!(key in newObject)) {
|
||||||
|
newObject[key] = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
function merge(value: unknown[], extraArgs: unknown[][]): unknown {
|
||||||
const [others] = extraArgs;
|
const [others] = extraArgs;
|
||||||
|
|
||||||
|
if (others === undefined) {
|
||||||
|
// If there are no arguments passed, merge all objects within the array
|
||||||
|
const merged = value.reduce((combined, current) => {
|
||||||
|
if (current !== null && typeof current === 'object' && !Array.isArray(current)) {
|
||||||
|
combined = mergeObjects(combined as Record<string, unknown>, [current]);
|
||||||
|
}
|
||||||
|
return combined;
|
||||||
|
}, {});
|
||||||
|
return merged;
|
||||||
|
}
|
||||||
|
|
||||||
if (!Array.isArray(others)) {
|
if (!Array.isArray(others)) {
|
||||||
throw new ExpressionExtensionError(
|
throw new ExpressionExtensionError(
|
||||||
'merge(): expected array arg, e.g. .merge([{ id: 1, otherValue: 3 }])',
|
'merge(): expected array arg, e.g. .merge([{ id: 1, otherValue: 3 }])',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const listLength = value.length > others.length ? value.length : others.length;
|
const listLength = value.length > others.length ? value.length : others.length;
|
||||||
const newList = new Array(listLength);
|
let merged = {};
|
||||||
for (let i = 0; i < listLength; i++) {
|
for (let i = 0; i < listLength; i++) {
|
||||||
if (value[i] !== undefined) {
|
if (value[i] !== undefined) {
|
||||||
if (typeof value[i] === 'object' && typeof others[i] === 'object') {
|
if (typeof value[i] === 'object' && typeof others[i] === 'object') {
|
||||||
newList[i] = oMerge(value[i] as object, [others[i]]);
|
merged = Object.assign(
|
||||||
} else {
|
merged,
|
||||||
newList[i] = value[i];
|
mergeObjects(value[i] as Record<string, unknown>, [others[i]]),
|
||||||
}
|
);
|
||||||
} else {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
||||||
newList[i] = others[i];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return newList;
|
}
|
||||||
|
return merged;
|
||||||
}
|
}
|
||||||
|
|
||||||
function union(value: unknown[], extraArgs: unknown[][]): unknown[] {
|
function union(value: unknown[], extraArgs: unknown[][]): unknown[] {
|
||||||
|
@ -312,7 +275,8 @@ function union(value: unknown[], extraArgs: unknown[][]): unknown[] {
|
||||||
}
|
}
|
||||||
const newArr: unknown[] = Array.from(value);
|
const newArr: unknown[] = Array.from(value);
|
||||||
for (const v of others) {
|
for (const v of others) {
|
||||||
if (newArr.findIndex((w) => deepCompare(w, v)) === -1) {
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
||||||
|
if (newArr.findIndex((w) => deepEqual(w, v, { strict: true })) === -1) {
|
||||||
newArr.push(v);
|
newArr.push(v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -328,7 +292,7 @@ function difference(value: unknown[], extraArgs: unknown[][]): unknown[] {
|
||||||
}
|
}
|
||||||
const newArr: unknown[] = [];
|
const newArr: unknown[] = [];
|
||||||
for (const v of value) {
|
for (const v of value) {
|
||||||
if (others.findIndex((w) => deepCompare(w, v)) === -1) {
|
if (others.findIndex((w) => deepEqual(w, v, { strict: true })) === -1) {
|
||||||
newArr.push(v);
|
newArr.push(v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -344,12 +308,13 @@ function intersection(value: unknown[], extraArgs: unknown[][]): unknown[] {
|
||||||
}
|
}
|
||||||
const newArr: unknown[] = [];
|
const newArr: unknown[] = [];
|
||||||
for (const v of value) {
|
for (const v of value) {
|
||||||
if (others.findIndex((w) => deepCompare(w, v)) !== -1) {
|
if (others.findIndex((w) => deepEqual(w, v, { strict: true })) !== -1) {
|
||||||
newArr.push(v);
|
newArr.push(v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const v of others) {
|
for (const v of others) {
|
||||||
if (value.findIndex((w) => deepCompare(w, v)) !== -1) {
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
||||||
|
if (value.findIndex((w) => deepEqual(w, v, { strict: true })) !== -1) {
|
||||||
newArr.push(v);
|
newArr.push(v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
|
import { ExpressionExtensionError } from './../ExpressionError';
|
||||||
/* eslint-disable @typescript-eslint/unbound-method */
|
/* eslint-disable @typescript-eslint/unbound-method */
|
||||||
/* eslint-disable @typescript-eslint/explicit-member-accessibility */
|
/* eslint-disable @typescript-eslint/explicit-member-accessibility */
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import type { DateTimeUnit, DurationLike, DurationObjectUnits, LocaleOptions } from 'luxon';
|
import type { DateTimeUnit, DurationLike, DurationObjectUnits, LocaleOptions } from 'luxon';
|
||||||
import type { ExtensionMap } from './Extensions';
|
import type { ExtensionMap } from './Extensions';
|
||||||
|
import { convertToDateTime } from './utils';
|
||||||
|
|
||||||
type DurationUnit =
|
type DurationUnit =
|
||||||
| 'milliseconds'
|
| 'milliseconds'
|
||||||
|
@ -82,11 +84,8 @@ function beginningOf(date: Date | DateTime, extraArgs: DurationUnit[]): Date {
|
||||||
if (isDateTime(date)) {
|
if (isDateTime(date)) {
|
||||||
return date.startOf(DATETIMEUNIT_MAP[unit] || unit).toJSDate();
|
return date.startOf(DATETIMEUNIT_MAP[unit] || unit).toJSDate();
|
||||||
}
|
}
|
||||||
let datetime = DateTime.fromJSDate(date);
|
const dateTime = DateTime.fromJSDate(date);
|
||||||
if (date.getTimezoneOffset() === 0) {
|
return dateTime.startOf(DATETIMEUNIT_MAP[unit] || unit).toJSDate();
|
||||||
datetime = datetime.setZone('UTC');
|
|
||||||
}
|
|
||||||
return datetime.startOf(DATETIMEUNIT_MAP[unit] || unit).toJSDate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function endOfMonth(date: Date | DateTime): Date {
|
function endOfMonth(date: Date | DateTime): Date {
|
||||||
|
@ -97,7 +96,7 @@ function endOfMonth(date: Date | DateTime): Date {
|
||||||
}
|
}
|
||||||
|
|
||||||
function extract(inputDate: Date | DateTime, extraArgs: DatePart[]): number | Date {
|
function extract(inputDate: Date | DateTime, extraArgs: DatePart[]): number | Date {
|
||||||
let [part] = extraArgs;
|
let [part = 'week'] = extraArgs;
|
||||||
let date = inputDate;
|
let date = inputDate;
|
||||||
if (isDateTime(date)) {
|
if (isDateTime(date)) {
|
||||||
date = date.toJSDate();
|
date = date.toJSDate();
|
||||||
|
@ -126,10 +125,22 @@ function format(date: Date | DateTime, extraArgs: unknown[]): string {
|
||||||
return DateTime.fromJSDate(date).toFormat(dateFormat, { ...localeOpts });
|
return DateTime.fromJSDate(date).toFormat(dateFormat, { ...localeOpts });
|
||||||
}
|
}
|
||||||
|
|
||||||
function isBetween(date: Date | DateTime, extraArgs: unknown[]): boolean {
|
function isBetween(
|
||||||
const [first, second] = extraArgs as string[];
|
date: Date | DateTime,
|
||||||
const firstDate = new Date(first);
|
extraArgs: Array<string | Date | DateTime>,
|
||||||
const secondDate = new Date(second);
|
): boolean | undefined {
|
||||||
|
if (extraArgs.length !== 2) {
|
||||||
|
throw new ExpressionExtensionError('isBetween(): expected exactly two args');
|
||||||
|
}
|
||||||
|
|
||||||
|
const [first, second] = extraArgs;
|
||||||
|
|
||||||
|
const firstDate = convertToDateTime(first);
|
||||||
|
const secondDate = convertToDateTime(second);
|
||||||
|
|
||||||
|
if (!firstDate || !secondDate) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (firstDate > secondDate) {
|
if (firstDate > secondDate) {
|
||||||
return secondDate < date && date < firstDate;
|
return secondDate < date && date < firstDate;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/**
|
/**
|
||||||
* @jest-environment jsdom
|
* @jest-environment jsdom
|
||||||
*/
|
*/
|
||||||
|
import { ExpressionExtensionError } from './../ExpressionError';
|
||||||
import type { ExtensionMap } from './Extensions';
|
import type { ExtensionMap } from './Extensions';
|
||||||
|
|
||||||
function format(value: number, extraArgs: unknown[]): string {
|
function format(value: number, extraArgs: unknown[]): string {
|
||||||
|
@ -14,10 +14,16 @@ function format(value: number, extraArgs: unknown[]): string {
|
||||||
}
|
}
|
||||||
|
|
||||||
function isEven(value: number) {
|
function isEven(value: number) {
|
||||||
|
if (!Number.isInteger(value)) {
|
||||||
|
throw new ExpressionExtensionError('isEven() is only callable on integers');
|
||||||
|
}
|
||||||
return value % 2 === 0;
|
return value % 2 === 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isOdd(value: number) {
|
function isOdd(value: number) {
|
||||||
|
if (!Number.isInteger(value)) {
|
||||||
|
throw new ExpressionExtensionError('isOdd() is only callable on integers');
|
||||||
|
}
|
||||||
return Math.abs(value) % 2 === 1;
|
return Math.abs(value) % 2 === 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,22 +1,6 @@
|
||||||
import { ExpressionExtensionError } from '../ExpressionError';
|
import { ExpressionExtensionError } from '../ExpressionError';
|
||||||
import type { ExtensionMap } from './Extensions';
|
import type { ExtensionMap } from './Extensions';
|
||||||
|
|
||||||
export function merge(value: object, extraArgs: unknown[]): unknown {
|
|
||||||
const [other] = extraArgs;
|
|
||||||
if (typeof other !== 'object' || !other) {
|
|
||||||
throw new ExpressionExtensionError('merge(): expected object arg');
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
const newObject: any = { ...value };
|
|
||||||
for (const [key, val] of Object.entries(other)) {
|
|
||||||
if (!(key in newObject)) {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
|
|
||||||
newObject[key] = val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return newObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isEmpty(value: object): boolean {
|
function isEmpty(value: object): boolean {
|
||||||
return Object.keys(value).length === 0;
|
return Object.keys(value).length === 0;
|
||||||
}
|
}
|
||||||
|
@ -43,8 +27,8 @@ function removeField(value: object, extraArgs: string[]): object {
|
||||||
|
|
||||||
function removeFieldsContaining(value: object, extraArgs: string[]): object {
|
function removeFieldsContaining(value: object, extraArgs: string[]): object {
|
||||||
const [match] = extraArgs;
|
const [match] = extraArgs;
|
||||||
if (typeof match !== 'string') {
|
if (typeof match !== 'string' || match === '') {
|
||||||
throw new ExpressionExtensionError('removeFieldsContaining(): expected string arg');
|
throw new ExpressionExtensionError('removeFieldsContaining(): expected non-empty string arg');
|
||||||
}
|
}
|
||||||
const newObject = { ...value };
|
const newObject = { ...value };
|
||||||
for (const [key, val] of Object.entries(value)) {
|
for (const [key, val] of Object.entries(value)) {
|
||||||
|
@ -58,12 +42,14 @@ function removeFieldsContaining(value: object, extraArgs: string[]): object {
|
||||||
|
|
||||||
function keepFieldsContaining(value: object, extraArgs: string[]): object {
|
function keepFieldsContaining(value: object, extraArgs: string[]): object {
|
||||||
const [match] = extraArgs;
|
const [match] = extraArgs;
|
||||||
if (typeof match !== 'string') {
|
if (typeof match !== 'string' || match === '') {
|
||||||
throw new ExpressionExtensionError('argument of keepFieldsContaining must be a string');
|
throw new ExpressionExtensionError(
|
||||||
|
'argument of keepFieldsContaining must be a non-empty string',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
const newObject = { ...value };
|
const newObject = { ...value };
|
||||||
for (const [key, val] of Object.entries(value)) {
|
for (const [key, val] of Object.entries(value)) {
|
||||||
if (typeof val === 'string' && !val.includes(match)) {
|
if (typeof val !== 'string' || (typeof val === 'string' && !val.includes(match))) {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
|
||||||
delete (newObject as any)[key];
|
delete (newObject as any)[key];
|
||||||
}
|
}
|
||||||
|
@ -120,11 +106,6 @@ urlEncode.doc = {
|
||||||
|
|
||||||
// @TODO_NEXT_PHASE: Surface extensions below which take args
|
// @TODO_NEXT_PHASE: Surface extensions below which take args
|
||||||
|
|
||||||
merge.doc = {
|
|
||||||
name: 'merge',
|
|
||||||
returnType: 'object',
|
|
||||||
};
|
|
||||||
|
|
||||||
hasField.doc = {
|
hasField.doc = {
|
||||||
name: 'hasField',
|
name: 'hasField',
|
||||||
returnType: 'boolean',
|
returnType: 'boolean',
|
||||||
|
@ -150,7 +131,6 @@ export const objectExtensions: ExtensionMap = {
|
||||||
functions: {
|
functions: {
|
||||||
isEmpty,
|
isEmpty,
|
||||||
isNotEmpty,
|
isNotEmpty,
|
||||||
merge,
|
|
||||||
hasField,
|
hasField,
|
||||||
removeField,
|
removeField,
|
||||||
removeFieldsContaining,
|
removeFieldsContaining,
|
||||||
|
|
|
@ -113,12 +113,15 @@ function removeTags(value: string): string {
|
||||||
}
|
}
|
||||||
|
|
||||||
function toDate(value: string): Date {
|
function toDate(value: string): Date {
|
||||||
const date = new Date(value.toString());
|
const date = new Date(Date.parse(value));
|
||||||
|
|
||||||
if (date.toString() === 'Invalid Date') {
|
if (date.toString() === 'Invalid Date') {
|
||||||
throw new ExpressionError.ExpressionExtensionError('cannot convert to date');
|
throw new ExpressionError.ExpressionExtensionError('cannot convert to date');
|
||||||
}
|
}
|
||||||
|
// If time component is not specified, force 00:00h
|
||||||
|
if (!/:/.test(value)) {
|
||||||
|
date.setHours(0, 0, 0);
|
||||||
|
}
|
||||||
return date;
|
return date;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
19
packages/workflow/src/Extensions/utils.ts
Normal file
19
packages/workflow/src/Extensions/utils.ts
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import { DateTime } from 'luxon';
|
||||||
|
|
||||||
|
// Utility functions and type guards for expression extensions
|
||||||
|
|
||||||
|
export const convertToDateTime = (value: string | Date | DateTime): DateTime | undefined => {
|
||||||
|
let converted: DateTime | undefined;
|
||||||
|
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
converted = DateTime.fromJSDate(new Date(value));
|
||||||
|
if (converted.invalidReason !== null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if (value instanceof Date) {
|
||||||
|
converted = DateTime.fromJSDate(value);
|
||||||
|
} else if (DateTime.isDateTime(value)) {
|
||||||
|
converted = value;
|
||||||
|
}
|
||||||
|
return converted;
|
||||||
|
};
|
|
@ -22,16 +22,58 @@ describe('Data Transformation Functions', () => {
|
||||||
{ value: 3, string: '3' },
|
{ value: 3, string: '3' },
|
||||||
{ value: 4, string: '4' },
|
{ value: 4, string: '4' },
|
||||||
{ value: 5, string: '5' },
|
{ value: 5, string: '5' },
|
||||||
{ value: 6, string: '6' }
|
{ value: 6, string: '6' },
|
||||||
|
{ value: { something: 'else' } }
|
||||||
].pluck("value") }}`),
|
].pluck("value") }}`),
|
||||||
|
).toEqual(
|
||||||
|
expect.arrayContaining([1, 2, 3, 4, 5, 6, { something: 'else' }]),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('.pluck() should work correctly for multiple values', () => {
|
||||||
|
expect(
|
||||||
|
evaluate(`={{ [
|
||||||
|
{
|
||||||
|
firstName: 'John',
|
||||||
|
lastName: 'Doe',
|
||||||
|
phone: {
|
||||||
|
home: '111-222',
|
||||||
|
office: '333-444'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
firstName: 'Jane',
|
||||||
|
lastName: 'Doe',
|
||||||
|
phone: {
|
||||||
|
office: '555-666'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
].pluck("firstName", "lastName") }}`),
|
||||||
|
).toEqual(
|
||||||
|
expect.arrayContaining([["John", "Doe"],["Jane", "Doe"]]),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('.pluck() should work return everything with no args', () => {
|
||||||
|
expect(
|
||||||
|
evaluate(`={{ [
|
||||||
|
{ value: 1, string: '1' },
|
||||||
|
{ value: 2, string: '2' },
|
||||||
|
{ value: 3, string: '3' },
|
||||||
|
{ value: 4, string: '4' },
|
||||||
|
{ value: 5, string: '5' },
|
||||||
|
{ value: 6, string: '6' },
|
||||||
|
{ value: { something: 'else' } }
|
||||||
|
].pluck() }}`),
|
||||||
).toEqual(
|
).toEqual(
|
||||||
expect.arrayContaining([
|
expect.arrayContaining([
|
||||||
{ value: 1 },
|
{ value: 1, string: '1' },
|
||||||
{ value: 2 },
|
{ value: 2, string: '2' },
|
||||||
{ value: 3 },
|
{ value: 3, string: '3' },
|
||||||
{ value: 4 },
|
{ value: 4, string: '4' },
|
||||||
{ value: 5 },
|
{ value: 5, string: '5' },
|
||||||
{ value: 6 },
|
{ value: 6, string: '6' },
|
||||||
|
{ value: { something: 'else' } }
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -42,6 +84,14 @@ describe('Data Transformation Functions', () => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('.unique() should work on an arrays containing nulls, objects and arrays', () => {
|
||||||
|
expect(
|
||||||
|
evaluate(
|
||||||
|
'={{ [1, 2, 3, "as", {}, {}, 1, 2, [1,2], "[sad]", "[sad]", null].unique() }}',
|
||||||
|
),
|
||||||
|
).toEqual([1, 2, 3, "as", {}, [1,2], "[sad]", null]);
|
||||||
|
});
|
||||||
|
|
||||||
test('.isEmpty() should work correctly on an array', () => {
|
test('.isEmpty() should work correctly on an array', () => {
|
||||||
expect(evaluate('={{ [].isEmpty() }}')).toEqual(true);
|
expect(evaluate('={{ [].isEmpty() }}')).toEqual(true);
|
||||||
});
|
});
|
||||||
|
@ -63,10 +113,15 @@ describe('Data Transformation Functions', () => {
|
||||||
evaluate(
|
evaluate(
|
||||||
'={{ [{ test1: 1, test2: 2 }, { test1: 1, test3: 3 }].merge([{ test1: 2, test3: 3 }, { test4: 4 }]) }}',
|
'={{ [{ test1: 1, test2: 2 }, { test1: 1, test3: 3 }].merge([{ test1: 2, test3: 3 }, { test4: 4 }]) }}',
|
||||||
),
|
),
|
||||||
).toEqual([
|
).toEqual({"test1": 1, "test2": 2, "test3": 3, "test4": 4});
|
||||||
{ test1: 1, test2: 2, test3: 3 },
|
});
|
||||||
{ test1: 1, test3: 3, test4: 4 },
|
|
||||||
]);
|
test('.merge() should work correctly without arguments', () => {
|
||||||
|
expect(
|
||||||
|
evaluate(
|
||||||
|
'={{ [{ a: 1, some: null }, { a: 2, c: "something" }, 2, "asds", { b: 23 }, null, [1, 2]].merge() }}',
|
||||||
|
),
|
||||||
|
).toEqual({"a": 1, "some": null, "c": "something", "b": 23});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('.smartJoin() should work correctly on an array of objects', () => {
|
test('.smartJoin() should work correctly on an array of objects', () => {
|
||||||
|
@ -119,6 +174,14 @@ describe('Data Transformation Functions', () => {
|
||||||
).toEqual([{ test1: 1 }, { test2: 2 }, { test1: 1, test3: 3 }, { test4: 4 }]);
|
).toEqual([{ test1: 1 }, { test2: 2 }, { test1: 1, test3: 3 }, { test4: 4 }]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('.union() should work on an arrays containing nulls, objects and arrays', () => {
|
||||||
|
expect(
|
||||||
|
evaluate(
|
||||||
|
'={{ [1, 2, "dd", {}, null].union([1, {}, null, 3]) }}',
|
||||||
|
),
|
||||||
|
).toEqual([1, 2, "dd", {}, null, 3]);
|
||||||
|
});
|
||||||
|
|
||||||
test('.intersection() should work on an array of objects', () => {
|
test('.intersection() should work on an array of objects', () => {
|
||||||
expect(
|
expect(
|
||||||
evaluate(
|
evaluate(
|
||||||
|
@ -127,6 +190,14 @@ describe('Data Transformation Functions', () => {
|
||||||
).toEqual([{ test2: 2 }]);
|
).toEqual([{ test2: 2 }]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('.intersection() should work on an arrays containing nulls, objects and arrays', () => {
|
||||||
|
expect(
|
||||||
|
evaluate(
|
||||||
|
'={{ [1, 2, "dd", {}, null].intersection([1, {}, null]) }}',
|
||||||
|
),
|
||||||
|
).toEqual([1, {}, null]);
|
||||||
|
});
|
||||||
|
|
||||||
test('.difference() should work on an array of objects', () => {
|
test('.difference() should work on an array of objects', () => {
|
||||||
expect(
|
expect(
|
||||||
evaluate(
|
evaluate(
|
||||||
|
@ -139,6 +210,14 @@ describe('Data Transformation Functions', () => {
|
||||||
).toEqual([]);
|
).toEqual([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('.difference() should work on an arrays containing nulls, objects and arrays', () => {
|
||||||
|
expect(
|
||||||
|
evaluate(
|
||||||
|
'={{ [1, 2, "dd", {}, null, ["a", 1]].difference([1, {}, null, ["a", 1]]) }}',
|
||||||
|
),
|
||||||
|
).toEqual([2, "dd"]);
|
||||||
|
});
|
||||||
|
|
||||||
test('.compact() should work on an array', () => {
|
test('.compact() should work on an array', () => {
|
||||||
expect(
|
expect(
|
||||||
evaluate(
|
evaluate(
|
||||||
|
|
|
@ -20,6 +20,33 @@ describe('Data Transformation Functions', () => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('.beginningOf("week") should work correctly on a string', () => {
|
||||||
|
const evaluatedDate = evaluate('={{ "2023-01-30".toDate().beginningOf("week") }}');
|
||||||
|
const expectedDate = DateTime.local(2023, 1, 23, { zone: TEST_TIMEZONE }).toJSDate();
|
||||||
|
|
||||||
|
if (evaluatedDate && evaluatedDate instanceof Date) {
|
||||||
|
expect(evaluatedDate.toDateString()).toEqual(expectedDate.toDateString());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('.beginningOf("month") should work correctly on a string', () => {
|
||||||
|
const evaluatedDate = evaluate('={{ "2023-06-16".toDate().beginningOf("month") }}');
|
||||||
|
const expectedDate = DateTime.local(2023, 6, 1, { zone: TEST_TIMEZONE }).toJSDate();
|
||||||
|
|
||||||
|
if (evaluatedDate && evaluatedDate instanceof Date) {
|
||||||
|
expect(evaluatedDate.toDateString()).toEqual(expectedDate.toDateString());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('.beginningOf("year") should work correctly on a string', () => {
|
||||||
|
const evaluatedDate = evaluate('={{ "2023-01-30".toDate().beginningOf("year") }}');
|
||||||
|
const expectedDate = DateTime.local(2023, 1, 1, { zone: TEST_TIMEZONE }).toJSDate();
|
||||||
|
|
||||||
|
if (evaluatedDate && evaluatedDate instanceof Date) {
|
||||||
|
expect(evaluatedDate.toDateString()).toEqual(expectedDate.toDateString());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
test('.endOfMonth() should work correctly on a date', () => {
|
test('.endOfMonth() should work correctly on a date', () => {
|
||||||
expect(evaluate('={{ DateTime.local(2023, 1, 16).endOfMonth() }}')).toEqual(
|
expect(evaluate('={{ DateTime.local(2023, 1, 16).endOfMonth() }}')).toEqual(
|
||||||
DateTime.local(2023, 1, 31, 23, 59, 59, 999, { zone: TEST_TIMEZONE }).toJSDate(),
|
DateTime.local(2023, 1, 31, 23, 59, 59, 999, { zone: TEST_TIMEZONE }).toJSDate(),
|
||||||
|
@ -30,6 +57,11 @@ describe('Data Transformation Functions', () => {
|
||||||
expect(evaluate('={{ DateTime.local(2023, 1, 20).extract("day") }}')).toEqual(20);
|
expect(evaluate('={{ DateTime.local(2023, 1, 20).extract("day") }}')).toEqual(20);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('.extract() should extract week for no args', () => {
|
||||||
|
expect(evaluate('={{ DateTime.local(2023, 1, 20).extract() }}')).toEqual(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
test('.format("yyyy LLL dd") should work correctly on a date', () => {
|
test('.format("yyyy LLL dd") should work correctly on a date', () => {
|
||||||
expect(evaluate('={{ DateTime.local(2023, 1, 16).format("yyyy LLL dd") }}')).toEqual(
|
expect(evaluate('={{ DateTime.local(2023, 1, 16).format("yyyy LLL dd") }}')).toEqual(
|
||||||
'2023 Jan 16',
|
'2023 Jan 16',
|
||||||
|
@ -40,5 +72,29 @@ describe('Data Transformation Functions', () => {
|
||||||
const date = new Date(2022, 0, 3);
|
const date = new Date(2022, 0, 3);
|
||||||
expect(evaluate(`={{ "${getLocalISOString(date)}".toDate() }}`)).toEqual(date);
|
expect(evaluate(`={{ "${getLocalISOString(date)}".toDate() }}`)).toEqual(date);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('.inBetween() should work on string and Date', () => {
|
||||||
|
expect(evaluate(`={{ $now.isBetween('2023-06-23'.toDate(), '2023-06-23') }}`)).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('.inBetween() should work on string and DateTime', () => {
|
||||||
|
expect(evaluate(`={{ $now.isBetween($now, '2023-06-23') }}`)).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('.inBetween() should not work for invalid strings', () => {
|
||||||
|
expect(evaluate(`={{ $now.isBetween($now, 'invalid') }}`)).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('.inBetween() should not work for numbers', () => {
|
||||||
|
expect(evaluate(`={{ $now.isBetween($now, 1) }}`)).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('.inBetween() should not work for a single argument', () => {
|
||||||
|
expect(() => evaluate(`={{ $now.isBetween($now) }}`)).toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('.inBetween() should not work for a more than two arguments', () => {
|
||||||
|
expect(() => evaluate(`={{ $now.isBetween($now, '2023-06-23', '2023-09-21'.toDate()) }}`)).toThrow();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -18,6 +18,9 @@ export const workflow = new Workflow({
|
||||||
connections: {},
|
connections: {},
|
||||||
active: false,
|
active: false,
|
||||||
nodeTypes,
|
nodeTypes,
|
||||||
|
settings: {
|
||||||
|
timezone: TEST_TIMEZONE,
|
||||||
|
}
|
||||||
});
|
});
|
||||||
export const expression = new Expression(workflow);
|
export const expression = new Expression(workflow);
|
||||||
|
|
||||||
|
|
|
@ -38,14 +38,22 @@ describe('Data Transformation Functions', () => {
|
||||||
expect(evaluate('={{ (9).isOdd() }}')).toEqual(true);
|
expect(evaluate('={{ (9).isOdd() }}')).toEqual(true);
|
||||||
expect(evaluate('={{ (8).isOdd() }}')).toEqual(false);
|
expect(evaluate('={{ (8).isOdd() }}')).toEqual(false);
|
||||||
expect(evaluate('={{ (0).isOdd() }}')).toEqual(false);
|
expect(evaluate('={{ (0).isOdd() }}')).toEqual(false);
|
||||||
expect(evaluate('={{ (NaN).isOdd() }}')).toEqual(false);
|
});
|
||||||
|
|
||||||
|
test('.isOdd() should not work on a float or NaN', () => {
|
||||||
|
expect(() => evaluate('={{ (NaN).isOdd() }}')).toThrow();
|
||||||
|
expect(() => evaluate('={{ (9.2).isOdd() }}')).toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('.isEven() should work on a number', () => {
|
test('.isEven() should work on a number', () => {
|
||||||
expect(evaluate('={{ (9).isEven() }}')).toEqual(false);
|
expect(evaluate('={{ (9).isEven() }}')).toEqual(false);
|
||||||
expect(evaluate('={{ (8).isEven() }}')).toEqual(true);
|
expect(evaluate('={{ (8).isEven() }}')).toEqual(true);
|
||||||
expect(evaluate('={{ (0).isEven() }}')).toEqual(true);
|
expect(evaluate('={{ (0).isEven() }}')).toEqual(true);
|
||||||
expect(evaluate('={{ (NaN).isEven() }}')).toEqual(false);
|
});
|
||||||
|
|
||||||
|
test('.isEven() should not work on a float or NaN', () => {
|
||||||
|
expect(() => evaluate('={{ (NaN).isEven() }}')).toThrow();
|
||||||
|
expect(() => evaluate('={{ (9.2).isEven() }}')).toThrow();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -7,14 +7,6 @@ describe('Data Transformation Functions', () => {
|
||||||
expect(evaluate('={{({ test1: 1 }).isEmpty()}}')).toEqual(false);
|
expect(evaluate('={{({ test1: 1 }).isEmpty()}}')).toEqual(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('.merge should work on an object', () => {
|
|
||||||
expect(evaluate('={{ ({ test1: 1, test2: 2 }).merge({ test2: 3, test3: 3 }) }}')).toEqual({
|
|
||||||
test1: 1,
|
|
||||||
test2: 2,
|
|
||||||
test3: 3,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test('.hasField should work on an object', () => {
|
test('.hasField should work on an object', () => {
|
||||||
expect(evaluate('={{ ({ test1: 1 }).hasField("test1") }}')).toEqual(true);
|
expect(evaluate('={{ ({ test1: 1 }).hasField("test1") }}')).toEqual(true);
|
||||||
expect(evaluate('={{ ({ test1: 1 }).hasField("test2") }}')).toEqual(false);
|
expect(evaluate('={{ ({ test1: 1 }).hasField("test2") }}')).toEqual(false);
|
||||||
|
@ -44,6 +36,14 @@ describe('Data Transformation Functions', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('.removeFieldsContaining should not work for empty string', () => {
|
||||||
|
expect(
|
||||||
|
() => evaluate(
|
||||||
|
'={{ ({ test1: "i exist", test2: "i should be removed", test3: "i should also be removed" }).removeFieldsContaining("") }}',
|
||||||
|
),
|
||||||
|
).toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
test('.keepFieldsContaining should work on an object', () => {
|
test('.keepFieldsContaining should work on an object', () => {
|
||||||
expect(
|
expect(
|
||||||
evaluate(
|
evaluate(
|
||||||
|
@ -54,6 +54,24 @@ describe('Data Transformation Functions', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('.keepFieldsContaining should work on a nested object', () => {
|
||||||
|
expect(
|
||||||
|
evaluate(
|
||||||
|
'={{ ({ test1: "i exist", test2: "i should be removed", test3: { test4: "me too" } }).keepFieldsContaining("exist") }}',
|
||||||
|
),
|
||||||
|
).toEqual({
|
||||||
|
test1: 'i exist',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('.keepFieldsContaining should not work for empty string', () => {
|
||||||
|
expect(
|
||||||
|
() => evaluate(
|
||||||
|
'={{ ({ test1: "i exist", test2: "i should be removed", test3: "i should also be removed" }).keepFieldsContaining("") }}',
|
||||||
|
),
|
||||||
|
).toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
test('.compact should work on an object', () => {
|
test('.compact should work on an object', () => {
|
||||||
expect(
|
expect(
|
||||||
evaluate('={{ ({ test1: 1, test2: "2", test3: undefined, test4: null }).compact() }}'),
|
evaluate('={{ ({ test1: 1, test2: "2", test3: undefined, test4: null }).compact() }}'),
|
||||||
|
|
|
@ -934,6 +934,7 @@ importers:
|
||||||
'@n8n_io/eslint-config': ''
|
'@n8n_io/eslint-config': ''
|
||||||
'@n8n_io/riot-tmpl': ^2.0.0
|
'@n8n_io/riot-tmpl': ^2.0.0
|
||||||
'@types/crypto-js': ^4.1.1
|
'@types/crypto-js': ^4.1.1
|
||||||
|
'@types/deep-equal': ^1.0.1
|
||||||
'@types/express': ^4.17.6
|
'@types/express': ^4.17.6
|
||||||
'@types/jmespath': ^0.15.0
|
'@types/jmespath': ^0.15.0
|
||||||
'@types/lodash.get': ^4.4.6
|
'@types/lodash.get': ^4.4.6
|
||||||
|
@ -944,6 +945,7 @@ importers:
|
||||||
'@types/xml2js': ^0.4.3
|
'@types/xml2js': ^0.4.3
|
||||||
ast-types: 0.15.2
|
ast-types: 0.15.2
|
||||||
crypto-js: ^4.1.1
|
crypto-js: ^4.1.1
|
||||||
|
deep-equal: ^2.2.0
|
||||||
esprima-next: 5.8.4
|
esprima-next: 5.8.4
|
||||||
jmespath: ^0.16.0
|
jmespath: ^0.16.0
|
||||||
js-base64: ^3.7.2
|
js-base64: ^3.7.2
|
||||||
|
@ -960,6 +962,7 @@ importers:
|
||||||
'@n8n_io/riot-tmpl': 2.0.0
|
'@n8n_io/riot-tmpl': 2.0.0
|
||||||
ast-types: 0.15.2
|
ast-types: 0.15.2
|
||||||
crypto-js: 4.1.1
|
crypto-js: 4.1.1
|
||||||
|
deep-equal: 2.2.0
|
||||||
esprima-next: 5.8.4
|
esprima-next: 5.8.4
|
||||||
jmespath: 0.16.0
|
jmespath: 0.16.0
|
||||||
js-base64: 3.7.2
|
js-base64: 3.7.2
|
||||||
|
@ -975,6 +978,7 @@ importers:
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@n8n_io/eslint-config': link:../@n8n_io/eslint-config
|
'@n8n_io/eslint-config': link:../@n8n_io/eslint-config
|
||||||
'@types/crypto-js': 4.1.1
|
'@types/crypto-js': 4.1.1
|
||||||
|
'@types/deep-equal': 1.0.1
|
||||||
'@types/express': 4.17.14
|
'@types/express': 4.17.14
|
||||||
'@types/jmespath': 0.15.0
|
'@types/jmespath': 0.15.0
|
||||||
'@types/lodash.get': 4.4.7
|
'@types/lodash.get': 4.4.7
|
||||||
|
@ -5300,6 +5304,8 @@ packages:
|
||||||
resolution: {integrity: sha512-KlPPdikagvL6ELjWsljbyDIPzNCeliYkqRpI+zea99vBBbCIA5JNshZAwQKTON139c87y9qvTFVgkFd14rtS4g==}
|
resolution: {integrity: sha512-KlPPdikagvL6ELjWsljbyDIPzNCeliYkqRpI+zea99vBBbCIA5JNshZAwQKTON139c87y9qvTFVgkFd14rtS4g==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@types/deep-equal/1.0.1:
|
||||||
|
resolution: {integrity: sha512-mMUu4nWHLBlHtxXY17Fg6+ucS/MnndyOWyOe7MmwkoMYxvfQU2ajtRaEvqSUv+aVkMqH/C0NCI8UoVfRNQ10yg==}
|
||||||
/@types/detect-port/1.3.2:
|
/@types/detect-port/1.3.2:
|
||||||
resolution: {integrity: sha512-xxgAGA2SAU4111QefXPSp5eGbDm/hW6zhvYl9IeEPZEry9F4d66QAHm5qpUXjb6IsevZV/7emAEx5MhP6O192g==}
|
resolution: {integrity: sha512-xxgAGA2SAU4111QefXPSp5eGbDm/hW6zhvYl9IeEPZEry9F4d66QAHm5qpUXjb6IsevZV/7emAEx5MhP6O192g==}
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -9313,7 +9319,6 @@ packages:
|
||||||
which-boxed-primitive: 1.0.2
|
which-boxed-primitive: 1.0.2
|
||||||
which-collection: 1.0.1
|
which-collection: 1.0.1
|
||||||
which-typed-array: 1.1.9
|
which-typed-array: 1.1.9
|
||||||
dev: true
|
|
||||||
|
|
||||||
/deep-is/0.1.4:
|
/deep-is/0.1.4:
|
||||||
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
|
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
|
||||||
|
@ -9866,14 +9871,14 @@ packages:
|
||||||
has: 1.0.3
|
has: 1.0.3
|
||||||
has-property-descriptors: 1.0.0
|
has-property-descriptors: 1.0.0
|
||||||
has-symbols: 1.0.3
|
has-symbols: 1.0.3
|
||||||
internal-slot: 1.0.3
|
internal-slot: 1.0.4
|
||||||
is-callable: 1.2.7
|
is-callable: 1.2.7
|
||||||
is-negative-zero: 2.0.2
|
is-negative-zero: 2.0.2
|
||||||
is-regex: 1.1.4
|
is-regex: 1.1.4
|
||||||
is-shared-array-buffer: 1.0.2
|
is-shared-array-buffer: 1.0.2
|
||||||
is-string: 1.0.7
|
is-string: 1.0.7
|
||||||
is-weakref: 1.0.2
|
is-weakref: 1.0.2
|
||||||
object-inspect: 1.12.2
|
object-inspect: 1.12.3
|
||||||
object-keys: 1.1.1
|
object-keys: 1.1.1
|
||||||
object.assign: 4.1.4
|
object.assign: 4.1.4
|
||||||
regexp.prototype.flags: 1.4.3
|
regexp.prototype.flags: 1.4.3
|
||||||
|
@ -9949,7 +9954,6 @@ packages:
|
||||||
is-string: 1.0.7
|
is-string: 1.0.7
|
||||||
isarray: 2.0.5
|
isarray: 2.0.5
|
||||||
stop-iteration-iterator: 1.0.0
|
stop-iteration-iterator: 1.0.0
|
||||||
dev: true
|
|
||||||
|
|
||||||
/es-module-lexer/0.9.3:
|
/es-module-lexer/0.9.3:
|
||||||
resolution: {integrity: sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==}
|
resolution: {integrity: sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==}
|
||||||
|
@ -12212,14 +12216,6 @@ packages:
|
||||||
through: 2.3.8
|
through: 2.3.8
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/internal-slot/1.0.3:
|
|
||||||
resolution: {integrity: sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==}
|
|
||||||
engines: {node: '>= 0.4'}
|
|
||||||
dependencies:
|
|
||||||
get-intrinsic: 1.1.3
|
|
||||||
has: 1.0.3
|
|
||||||
side-channel: 1.0.4
|
|
||||||
|
|
||||||
/internal-slot/1.0.4:
|
/internal-slot/1.0.4:
|
||||||
resolution: {integrity: sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ==}
|
resolution: {integrity: sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
@ -12502,7 +12498,6 @@ packages:
|
||||||
|
|
||||||
/is-map/2.0.2:
|
/is-map/2.0.2:
|
||||||
resolution: {integrity: sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==}
|
resolution: {integrity: sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==}
|
||||||
dev: true
|
|
||||||
|
|
||||||
/is-nan/1.3.2:
|
/is-nan/1.3.2:
|
||||||
resolution: {integrity: sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==}
|
resolution: {integrity: sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==}
|
||||||
|
@ -12608,7 +12603,6 @@ packages:
|
||||||
|
|
||||||
/is-set/2.0.2:
|
/is-set/2.0.2:
|
||||||
resolution: {integrity: sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==}
|
resolution: {integrity: sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==}
|
||||||
dev: true
|
|
||||||
|
|
||||||
/is-shared-array-buffer/1.0.2:
|
/is-shared-array-buffer/1.0.2:
|
||||||
resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==}
|
resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==}
|
||||||
|
@ -12646,17 +12640,6 @@ packages:
|
||||||
gopd: 1.0.1
|
gopd: 1.0.1
|
||||||
has-tostringtag: 1.0.0
|
has-tostringtag: 1.0.0
|
||||||
|
|
||||||
/is-typed-array/1.1.9:
|
|
||||||
resolution: {integrity: sha512-kfrlnTTn8pZkfpJMUgYD7YZ3qzeJgWUn8XfVYBARc4wnmNOmLbmuuaAs3q5fvB0UJOn6yHAKaGTPM7d6ezoD/A==}
|
|
||||||
engines: {node: '>= 0.4'}
|
|
||||||
dependencies:
|
|
||||||
available-typed-arrays: 1.0.5
|
|
||||||
call-bind: 1.0.2
|
|
||||||
es-abstract: 1.20.4
|
|
||||||
for-each: 0.3.3
|
|
||||||
has-tostringtag: 1.0.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/is-typedarray/1.0.0:
|
/is-typedarray/1.0.0:
|
||||||
resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==}
|
resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==}
|
||||||
|
|
||||||
|
@ -12682,7 +12665,6 @@ packages:
|
||||||
|
|
||||||
/is-weakmap/2.0.1:
|
/is-weakmap/2.0.1:
|
||||||
resolution: {integrity: sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==}
|
resolution: {integrity: sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==}
|
||||||
dev: true
|
|
||||||
|
|
||||||
/is-weakref/1.0.2:
|
/is-weakref/1.0.2:
|
||||||
resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==}
|
resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==}
|
||||||
|
@ -12694,7 +12676,6 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
call-bind: 1.0.2
|
call-bind: 1.0.2
|
||||||
get-intrinsic: 1.1.3
|
get-intrinsic: 1.1.3
|
||||||
dev: true
|
|
||||||
|
|
||||||
/is-whitespace/0.3.0:
|
/is-whitespace/0.3.0:
|
||||||
resolution: {integrity: sha512-RydPhl4S6JwAyj0JJjshWJEFG6hNye3pZFBRZaTUfZFwGHxzppNaNOVgQuS/E/SlhrApuMXrpnK1EEIXfdo3Dg==}
|
resolution: {integrity: sha512-RydPhl4S6JwAyj0JJjshWJEFG6hNye3pZFBRZaTUfZFwGHxzppNaNOVgQuS/E/SlhrApuMXrpnK1EEIXfdo3Dg==}
|
||||||
|
@ -12719,7 +12700,6 @@ packages:
|
||||||
|
|
||||||
/isarray/2.0.5:
|
/isarray/2.0.5:
|
||||||
resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==}
|
resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==}
|
||||||
dev: true
|
|
||||||
|
|
||||||
/isbot/3.6.1:
|
/isbot/3.6.1:
|
||||||
resolution: {integrity: sha512-e1RmjWns87x60QyiHberWWMJGutL3+Ad0nZ8cz735iDEDDS6ApPfKSFo4EMj0PmMZ0m0ntpWIM0ADdqDFvUJPQ==}
|
resolution: {integrity: sha512-e1RmjWns87x60QyiHberWWMJGutL3+Ad0nZ8cz735iDEDDS6ApPfKSFo4EMj0PmMZ0m0ntpWIM0ADdqDFvUJPQ==}
|
||||||
|
@ -15385,9 +15365,6 @@ packages:
|
||||||
kind-of: 3.2.2
|
kind-of: 3.2.2
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/object-inspect/1.12.2:
|
|
||||||
resolution: {integrity: sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==}
|
|
||||||
|
|
||||||
/object-inspect/1.12.3:
|
/object-inspect/1.12.3:
|
||||||
resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==}
|
resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==}
|
||||||
|
|
||||||
|
@ -15397,7 +15374,6 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
call-bind: 1.0.2
|
call-bind: 1.0.2
|
||||||
define-properties: 1.1.4
|
define-properties: 1.1.4
|
||||||
dev: true
|
|
||||||
|
|
||||||
/object-keys/1.1.1:
|
/object-keys/1.1.1:
|
||||||
resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
|
resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
|
||||||
|
@ -18359,7 +18335,6 @@ packages:
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
dependencies:
|
dependencies:
|
||||||
internal-slot: 1.0.4
|
internal-slot: 1.0.4
|
||||||
dev: true
|
|
||||||
|
|
||||||
/stoppable/1.1.0:
|
/stoppable/1.1.0:
|
||||||
resolution: {integrity: sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==}
|
resolution: {integrity: sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==}
|
||||||
|
@ -19912,9 +19887,9 @@ packages:
|
||||||
inherits: 2.0.4
|
inherits: 2.0.4
|
||||||
is-arguments: 1.1.1
|
is-arguments: 1.1.1
|
||||||
is-generator-function: 1.0.10
|
is-generator-function: 1.0.10
|
||||||
is-typed-array: 1.1.9
|
is-typed-array: 1.1.10
|
||||||
safe-buffer: 5.2.1
|
safe-buffer: 5.2.1
|
||||||
which-typed-array: 1.1.8
|
which-typed-array: 1.1.9
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/util/0.12.5:
|
/util/0.12.5:
|
||||||
|
@ -20903,7 +20878,6 @@ packages:
|
||||||
is-set: 2.0.2
|
is-set: 2.0.2
|
||||||
is-weakmap: 2.0.1
|
is-weakmap: 2.0.1
|
||||||
is-weakset: 2.0.2
|
is-weakset: 2.0.2
|
||||||
dev: true
|
|
||||||
|
|
||||||
/which-module/1.0.0:
|
/which-module/1.0.0:
|
||||||
resolution: {integrity: sha512-F6+WgncZi/mJDrammbTuHe1q0R5hOXv/mBaiNA2TCNT/LTHusX0V+CJnj9XT8ki5ln2UZyyddDgHfCzyrOH7MQ==}
|
resolution: {integrity: sha512-F6+WgncZi/mJDrammbTuHe1q0R5hOXv/mBaiNA2TCNT/LTHusX0V+CJnj9XT8ki5ln2UZyyddDgHfCzyrOH7MQ==}
|
||||||
|
@ -20913,18 +20887,6 @@ packages:
|
||||||
resolution: {integrity: sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==}
|
resolution: {integrity: sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/which-typed-array/1.1.8:
|
|
||||||
resolution: {integrity: sha512-Jn4e5PItbcAHyLoRDwvPj1ypu27DJbtdYXUa5zsinrUx77Uvfb0cXwwnGMTn7cjUfhhqgVQnVJCwF+7cgU7tpw==}
|
|
||||||
engines: {node: '>= 0.4'}
|
|
||||||
dependencies:
|
|
||||||
available-typed-arrays: 1.0.5
|
|
||||||
call-bind: 1.0.2
|
|
||||||
es-abstract: 1.20.4
|
|
||||||
for-each: 0.3.3
|
|
||||||
has-tostringtag: 1.0.0
|
|
||||||
is-typed-array: 1.1.9
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/which-typed-array/1.1.9:
|
/which-typed-array/1.1.9:
|
||||||
resolution: {integrity: sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==}
|
resolution: {integrity: sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
Loading…
Reference in a new issue