@contrail/cli 3.4.0-alpha → 3.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -151
- package/dist/__mocks__/@contrail/sdk.d.ts +123 -0
- package/dist/__mocks__/@contrail/sdk.js +716 -0
- package/dist/api/types.d.ts +0 -35
- package/dist/api/types.js +1 -47
- package/dist/auth/login.js +8 -0
- package/dist/basecommand.d.ts +1 -3
- package/dist/basecommand.js +1 -1
- package/dist/commands/app/create.js +1 -14
- package/dist/commands/app/extension/deploy.js +4 -2
- package/dist/commands/app/init.js +0 -14
- package/dist/commands/types/uploadTypes.d.ts +2 -1
- package/dist/commands/types/uploadTypes.js +9 -3
- package/dist/commands/types/uploadUsageInfo.js +16 -2
- package/dist/common.d.ts +1 -1
- package/dist/helpers/app/app-deployer.js +8 -4
- package/dist/helpers/app/extension-package-deployer.js +1 -1
- package/dist/helpers/app/extensions-deployer.js +2 -0
- package/dist/helpers/app/setup-template.d.ts +0 -2
- package/dist/helpers/app/setup-template.js +1 -2
- package/dist/helpers/config/config.js +3 -3
- package/dist/helpers/file-helpers.d.ts +0 -1
- package/dist/helpers/file-helpers.js +1 -19
- package/dist/helpers/types/type-uploader.d.ts +3 -1
- package/dist/helpers/types/type-uploader.js +55 -37
- package/dist/helpers/types/type-uploader.spec.js +506 -0
- package/dist/interfaces.d.ts +3 -1
- package/dist/templates/typescript/README.md +3 -1
- package/dist/templates/typescript/app.yml +1 -1
- package/dist/templates/typescript/assets/sample.json +1 -1
- package/dist/templates/typescript/eslintrc.json +1 -4
- package/dist/templates/typescript/tsconfig.json +5 -21
- package/oclif.manifest.json +9 -361
- package/package.json +5 -6
- package/dist/commands/entities/create.d.ts +0 -37
- package/dist/commands/entities/create.js +0 -555
- package/dist/commands/entities/create.spec.d.ts +0 -1
- package/dist/commands/entities/create.spec.js +0 -514
- package/dist/commands/entities/delete.d.ts +0 -16
- package/dist/commands/entities/delete.js +0 -118
- package/dist/commands/entities/get.d.ts +0 -20
- package/dist/commands/entities/get.js +0 -204
- package/dist/commands/entities/update.d.ts +0 -18
- package/dist/commands/entities/update.js +0 -116
- package/dist/helpers/entities/assortment-items.d.ts +0 -10
- package/dist/helpers/entities/assortment-items.js +0 -84
- package/dist/helpers/entities/batchFetch.d.ts +0 -8
- package/dist/helpers/entities/batchFetch.js +0 -41
- package/dist/helpers/entities/index.d.ts +0 -3
- package/dist/helpers/entities/index.js +0 -6
- package/dist/helpers/entities/items.d.ts +0 -10
- package/dist/helpers/entities/items.js +0 -102
- package/dist/helpers/entities/project-items.d.ts +0 -3
- package/dist/helpers/entities/project-items.js +0 -65
- package/dist/helpers/propertySpecifications/dateRanges.d.ts +0 -38
- package/dist/helpers/propertySpecifications/dateRanges.js +0 -89
- package/dist/helpers/propertySpecifications/dateRanges.spec.d.ts +0 -1
- package/dist/helpers/propertySpecifications/dateRanges.spec.js +0 -95
- package/dist/helpers/propertySpecifications/propertySpecifications.d.ts +0 -27
- package/dist/helpers/propertySpecifications/propertySpecifications.js +0 -70
- package/dist/helpers/propertySpecifications/propertySpecifications.spec.d.ts +0 -1
- package/dist/helpers/propertySpecifications/propertySpecifications.spec.js +0 -266
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.findItemsNotInProject = exports.throwIfInvalidProjectId = void 0;
|
|
4
|
-
const sdk_1 = require("@contrail/sdk");
|
|
5
|
-
const batchFetch_1 = require("./batchFetch");
|
|
6
|
-
const items_1 = require("./items");
|
|
7
|
-
const PROJECT_ITEMS_CACHE_BY_PROJECT_ID = new Map();
|
|
8
|
-
const PROJECT_ITEMS_CACHE_BY_ITEM_ID = new Map();
|
|
9
|
-
async function throwIfInvalidProjectId(projectId) {
|
|
10
|
-
if (!(await doesProjectExistById(projectId))) {
|
|
11
|
-
throw new Error(`Project with ID ${projectId} does not exist`);
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
exports.throwIfInvalidProjectId = throwIfInvalidProjectId;
|
|
15
|
-
async function doesProjectExistById(id) {
|
|
16
|
-
const client = new sdk_1.Entities();
|
|
17
|
-
const project = await client
|
|
18
|
-
.get({
|
|
19
|
-
entityName: 'project',
|
|
20
|
-
id,
|
|
21
|
-
customParams: { propertySlugs: 'id' },
|
|
22
|
-
})
|
|
23
|
-
.catch((e) => {
|
|
24
|
-
console.error(e);
|
|
25
|
-
return null; // Return null if project does not exist
|
|
26
|
-
});
|
|
27
|
-
return !!project;
|
|
28
|
-
}
|
|
29
|
-
async function cacheProjectItemsByProjectId(projectId) {
|
|
30
|
-
if (PROJECT_ITEMS_CACHE_BY_PROJECT_ID.has(projectId)) {
|
|
31
|
-
return; // Already cached
|
|
32
|
-
}
|
|
33
|
-
const projectItems = await (0, batchFetch_1.batchFetch)('project-item', {
|
|
34
|
-
criteria: { projectId },
|
|
35
|
-
customParams: { propertySlugs: 'id,projectId,assortmentId' },
|
|
36
|
-
});
|
|
37
|
-
PROJECT_ITEMS_CACHE_BY_PROJECT_ID.set(projectId, projectItems);
|
|
38
|
-
projectItems.forEach((item) => {
|
|
39
|
-
if (!PROJECT_ITEMS_CACHE_BY_ITEM_ID.has(item.itemId)) {
|
|
40
|
-
PROJECT_ITEMS_CACHE_BY_ITEM_ID.set(item.itemId, []);
|
|
41
|
-
}
|
|
42
|
-
PROJECT_ITEMS_CACHE_BY_ITEM_ID.get(item.itemId).push(item);
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
async function getProjectItemsByProjectId(projectId) {
|
|
46
|
-
await cacheProjectItemsByProjectId(projectId);
|
|
47
|
-
const projectItems = PROJECT_ITEMS_CACHE_BY_PROJECT_ID.get(projectId);
|
|
48
|
-
if (!projectItems) {
|
|
49
|
-
throw new Error(`No project items found for project ID ${projectId}`);
|
|
50
|
-
}
|
|
51
|
-
return projectItems;
|
|
52
|
-
}
|
|
53
|
-
async function findItemsNotInProject(projectId) {
|
|
54
|
-
const [projectItemsInProject, allItems] = await Promise.all([
|
|
55
|
-
getProjectItemsByProjectId(projectId),
|
|
56
|
-
(0, items_1.getManyItems)(),
|
|
57
|
-
]);
|
|
58
|
-
const projectItemIds = new Set(projectItemsInProject.map((item) => item.itemId));
|
|
59
|
-
const itemsNotInProject = allItems.filter((item) => !projectItemIds.has(item.id));
|
|
60
|
-
if (itemsNotInProject.length === 0) {
|
|
61
|
-
throw new Error(`All items are already in project ${projectId}`);
|
|
62
|
-
}
|
|
63
|
-
return itemsNotInProject.sort((a, b) => a.id.localeCompare(b.id)); // sorting ensures consistent order of response
|
|
64
|
-
}
|
|
65
|
-
exports.findItemsNotInProject = findItemsNotInProject;
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Date Range Parsing Utilities
|
|
3
|
-
*
|
|
4
|
-
* Handles parsing date range syntax like:
|
|
5
|
-
* - createdOn=2025-09-15..2025-09-16 (date range)
|
|
6
|
-
* - createdOn=2025-09-15T04:00:00Z..2025-09-16T03:59:59Z (timestamp range)
|
|
7
|
-
* - createdOn=2025-09-15.. (from date onwards)
|
|
8
|
-
* - createdOn=..2025-09-16 (until date)
|
|
9
|
-
*/
|
|
10
|
-
export type DateRange = {
|
|
11
|
-
start?: Date;
|
|
12
|
-
end?: Date;
|
|
13
|
-
};
|
|
14
|
-
export type DateRangeSpecification = {
|
|
15
|
-
slug: string;
|
|
16
|
-
range: DateRange;
|
|
17
|
-
};
|
|
18
|
-
/**
|
|
19
|
-
* Checks if a property specification contains date range syntax (..)
|
|
20
|
-
*/
|
|
21
|
-
export declare function isDateRangeSpecification(spec: string): boolean;
|
|
22
|
-
/**
|
|
23
|
-
* Parses a date range specification into its components
|
|
24
|
-
* @param spec - The specification string (e.g., "createdOn=2025-09-15..2025-09-16")
|
|
25
|
-
*/
|
|
26
|
-
export declare function parseDateRangeSpecification(spec: string): DateRangeSpecification;
|
|
27
|
-
/**
|
|
28
|
-
* Parses a date string, handling both date-only and full timestamp formats
|
|
29
|
-
* @param dateStr - Date string (e.g., "2025-09-15" or "2025-09-15T04:00:00Z")
|
|
30
|
-
* @param boundType - Whether this is the 'start' or 'end' of a range (affects date-only handling)
|
|
31
|
-
*/
|
|
32
|
-
export declare function parseDate(dateStr: string, boundType?: 'start' | 'end'): Date;
|
|
33
|
-
/**
|
|
34
|
-
* Converts a date range to JSURL BETWEEN format
|
|
35
|
-
* @param range - The date range object
|
|
36
|
-
* @param isEndOfRange - Whether this date represents the end of a range (affects date-only handling)
|
|
37
|
-
*/
|
|
38
|
-
export declare function convertDateRangeToJSURL(range: DateRange): string;
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Date Range Parsing Utilities
|
|
4
|
-
*
|
|
5
|
-
* Handles parsing date range syntax like:
|
|
6
|
-
* - createdOn=2025-09-15..2025-09-16 (date range)
|
|
7
|
-
* - createdOn=2025-09-15T04:00:00Z..2025-09-16T03:59:59Z (timestamp range)
|
|
8
|
-
* - createdOn=2025-09-15.. (from date onwards)
|
|
9
|
-
* - createdOn=..2025-09-16 (until date)
|
|
10
|
-
*/
|
|
11
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.convertDateRangeToJSURL = exports.parseDate = exports.parseDateRangeSpecification = exports.isDateRangeSpecification = void 0;
|
|
13
|
-
/**
|
|
14
|
-
* Checks if a property specification contains date range syntax (..)
|
|
15
|
-
*/
|
|
16
|
-
function isDateRangeSpecification(spec) {
|
|
17
|
-
return spec.includes('=') && spec.includes('..');
|
|
18
|
-
}
|
|
19
|
-
exports.isDateRangeSpecification = isDateRangeSpecification;
|
|
20
|
-
/**
|
|
21
|
-
* Parses a date range specification into its components
|
|
22
|
-
* @param spec - The specification string (e.g., "createdOn=2025-09-15..2025-09-16")
|
|
23
|
-
*/
|
|
24
|
-
function parseDateRangeSpecification(spec) {
|
|
25
|
-
const equalsIndex = spec.indexOf('=');
|
|
26
|
-
if (equalsIndex === -1) {
|
|
27
|
-
throw new Error(`Invalid date range specification: ${spec}`);
|
|
28
|
-
}
|
|
29
|
-
const slug = spec.substring(0, equalsIndex);
|
|
30
|
-
const rangeValue = spec.substring(equalsIndex + 1);
|
|
31
|
-
if (!slug || !rangeValue || !rangeValue.includes('..')) {
|
|
32
|
-
throw new Error(`Invalid date range specification: ${spec}`);
|
|
33
|
-
}
|
|
34
|
-
const [startStr, endStr] = rangeValue.split('..');
|
|
35
|
-
const range = {
|
|
36
|
-
start: startStr.trim() ? parseDate(startStr.trim(), 'start') : undefined,
|
|
37
|
-
end: endStr.trim() ? parseDate(endStr.trim(), 'end') : undefined,
|
|
38
|
-
};
|
|
39
|
-
// At least one bound must be specified
|
|
40
|
-
if (!range.start && !range.end) {
|
|
41
|
-
throw new Error(`Date range must have at least one bound: ${spec}`);
|
|
42
|
-
}
|
|
43
|
-
return { slug, range };
|
|
44
|
-
}
|
|
45
|
-
exports.parseDateRangeSpecification = parseDateRangeSpecification;
|
|
46
|
-
/**
|
|
47
|
-
* Parses a date string, handling both date-only and full timestamp formats
|
|
48
|
-
* @param dateStr - Date string (e.g., "2025-09-15" or "2025-09-15T04:00:00Z")
|
|
49
|
-
* @param boundType - Whether this is the 'start' or 'end' of a range (affects date-only handling)
|
|
50
|
-
*/
|
|
51
|
-
function parseDate(dateStr, boundType = 'start') {
|
|
52
|
-
if (!dateStr.includes('T')) {
|
|
53
|
-
if (boundType === 'start') {
|
|
54
|
-
dateStr = `${dateStr}T00:00:00.000Z`;
|
|
55
|
-
}
|
|
56
|
-
else {
|
|
57
|
-
dateStr = `${dateStr}T23:59:59.999Z`;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
const date = new Date(dateStr);
|
|
61
|
-
if (isNaN(date.getTime())) {
|
|
62
|
-
throw new Error(`Invalid date format: ${dateStr}`);
|
|
63
|
-
}
|
|
64
|
-
return date;
|
|
65
|
-
}
|
|
66
|
-
exports.parseDate = parseDate;
|
|
67
|
-
/**
|
|
68
|
-
* Converts a date range to JSURL BETWEEN format
|
|
69
|
-
* @param range - The date range object
|
|
70
|
-
* @param isEndOfRange - Whether this date represents the end of a range (affects date-only handling)
|
|
71
|
-
*/
|
|
72
|
-
function convertDateRangeToJSURL(range) {
|
|
73
|
-
if (!range.start && !range.end) {
|
|
74
|
-
throw new Error('Date range must have at least one bound');
|
|
75
|
-
}
|
|
76
|
-
if (range.start && range.end) {
|
|
77
|
-
const startISO = range.start.toISOString();
|
|
78
|
-
const endISO = range.end.toISOString();
|
|
79
|
-
return `BETWEEN ${startISO} and ${endISO}`;
|
|
80
|
-
}
|
|
81
|
-
else if (range.start) {
|
|
82
|
-
return `>= ${range.start.toISOString()}`;
|
|
83
|
-
}
|
|
84
|
-
else {
|
|
85
|
-
const endISO = range.end.toISOString();
|
|
86
|
-
return `<= ${endISO}`;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
exports.convertDateRangeToJSURL = convertDateRangeToJSURL;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const dateRanges_1 = require("./dateRanges");
|
|
4
|
-
describe('Date Range Specifications', () => {
|
|
5
|
-
describe('isDateRangeSpecification', () => {
|
|
6
|
-
it('should identify valid date range specifications', () => {
|
|
7
|
-
expect((0, dateRanges_1.isDateRangeSpecification)('createdOn=2025-09-15..2025-09-16')).toBe(true);
|
|
8
|
-
expect((0, dateRanges_1.isDateRangeSpecification)('updatedAt=2025-09-15T04:00:00Z..2025-09-16T03:59:59Z')).toBe(true);
|
|
9
|
-
expect((0, dateRanges_1.isDateRangeSpecification)('date=2025-09-15..')).toBe(true);
|
|
10
|
-
expect((0, dateRanges_1.isDateRangeSpecification)('date=..2025-09-16')).toBe(true);
|
|
11
|
-
});
|
|
12
|
-
it('should reject non-date-range specifications', () => {
|
|
13
|
-
expect((0, dateRanges_1.isDateRangeSpecification)('id=123')).toBe(false);
|
|
14
|
-
expect((0, dateRanges_1.isDateRangeSpecification)('name=test')).toBe(false);
|
|
15
|
-
expect((0, dateRanges_1.isDateRangeSpecification)('invalid')).toBe(false);
|
|
16
|
-
expect((0, dateRanges_1.isDateRangeSpecification)('field=value')).toBe(false);
|
|
17
|
-
});
|
|
18
|
-
});
|
|
19
|
-
describe('parseDate', () => {
|
|
20
|
-
it('should parse ISO timestamps correctly', () => {
|
|
21
|
-
const date = (0, dateRanges_1.parseDate)('2025-09-15T04:00:00Z');
|
|
22
|
-
expect(date.toISOString()).toBe('2025-09-15T04:00:00.000Z');
|
|
23
|
-
});
|
|
24
|
-
it('should parse date-only strings as start of day', () => {
|
|
25
|
-
const date = (0, dateRanges_1.parseDate)('2025-09-15', 'start');
|
|
26
|
-
expect(date.toISOString()).toBe('2025-09-15T00:00:00.000Z');
|
|
27
|
-
});
|
|
28
|
-
it('should parse date-only strings as end of day for end bound', () => {
|
|
29
|
-
const date = (0, dateRanges_1.parseDate)('2025-09-15', 'end');
|
|
30
|
-
expect(date.toISOString()).toBe('2025-09-15T23:59:59.999Z');
|
|
31
|
-
});
|
|
32
|
-
it('should throw for invalid date strings', () => {
|
|
33
|
-
expect(() => (0, dateRanges_1.parseDate)('invalid-date')).toThrow('Invalid date format');
|
|
34
|
-
expect(() => (0, dateRanges_1.parseDate)('2025-13-50')).toThrow('Invalid date format');
|
|
35
|
-
});
|
|
36
|
-
});
|
|
37
|
-
describe('parseDateRangeSpecification', () => {
|
|
38
|
-
it('should parse full date ranges', () => {
|
|
39
|
-
const result = (0, dateRanges_1.parseDateRangeSpecification)('createdOn=2025-09-15..2025-09-16');
|
|
40
|
-
expect(result.slug).toBe('createdOn');
|
|
41
|
-
expect(result.range.start).toEqual(new Date('2025-09-15T00:00:00.000Z'));
|
|
42
|
-
expect(result.range.end).toEqual(new Date('2025-09-16T23:59:59.999Z'));
|
|
43
|
-
});
|
|
44
|
-
it('should parse open-ended start ranges', () => {
|
|
45
|
-
const result = (0, dateRanges_1.parseDateRangeSpecification)('createdOn=2025-09-15..');
|
|
46
|
-
expect(result.slug).toBe('createdOn');
|
|
47
|
-
expect(result.range.start).toEqual(new Date('2025-09-15T00:00:00.000Z'));
|
|
48
|
-
expect(result.range.end).toBeUndefined();
|
|
49
|
-
});
|
|
50
|
-
it('should parse open-ended end ranges', () => {
|
|
51
|
-
const result = (0, dateRanges_1.parseDateRangeSpecification)('createdOn=..2025-09-16');
|
|
52
|
-
expect(result.slug).toBe('createdOn');
|
|
53
|
-
expect(result.range.start).toBeUndefined();
|
|
54
|
-
expect(result.range.end).toEqual(new Date('2025-09-16T23:59:59.999Z'));
|
|
55
|
-
});
|
|
56
|
-
it('should parse timestamp ranges', () => {
|
|
57
|
-
const result = (0, dateRanges_1.parseDateRangeSpecification)('createdOn=2025-09-15T04:00:00Z..2025-09-16T03:59:59Z');
|
|
58
|
-
expect(result.slug).toBe('createdOn');
|
|
59
|
-
expect(result.range.start).toEqual(new Date('2025-09-15T04:00:00.000Z'));
|
|
60
|
-
expect(result.range.end).toEqual(new Date('2025-09-16T03:59:59.000Z'));
|
|
61
|
-
});
|
|
62
|
-
it('should throw for invalid specifications', () => {
|
|
63
|
-
expect(() => (0, dateRanges_1.parseDateRangeSpecification)('invalid')).toThrow('Invalid date range specification');
|
|
64
|
-
expect(() => (0, dateRanges_1.parseDateRangeSpecification)('field=..')).toThrow('Date range must have at least one bound');
|
|
65
|
-
expect(() => (0, dateRanges_1.parseDateRangeSpecification)('field=invalid..date')).toThrow('Invalid date format');
|
|
66
|
-
});
|
|
67
|
-
});
|
|
68
|
-
describe('convertDateRangeToJSURL', () => {
|
|
69
|
-
it('should convert full ranges to BETWEEN format', () => {
|
|
70
|
-
const range = {
|
|
71
|
-
start: new Date('2025-09-15T00:00:00.000Z'),
|
|
72
|
-
end: new Date('2025-09-16T23:59:59.999Z'),
|
|
73
|
-
};
|
|
74
|
-
const result = (0, dateRanges_1.convertDateRangeToJSURL)(range);
|
|
75
|
-
expect(result).toBe('BETWEEN 2025-09-15T00:00:00.000Z and 2025-09-16T23:59:59.999Z');
|
|
76
|
-
});
|
|
77
|
-
it('should convert start-only ranges to >= format', () => {
|
|
78
|
-
const range = {
|
|
79
|
-
start: new Date('2025-09-15T00:00:00.000Z'),
|
|
80
|
-
};
|
|
81
|
-
const result = (0, dateRanges_1.convertDateRangeToJSURL)(range);
|
|
82
|
-
expect(result).toBe('>= 2025-09-15T00:00:00.000Z');
|
|
83
|
-
});
|
|
84
|
-
it('should convert end-only ranges to <= format', () => {
|
|
85
|
-
const range = {
|
|
86
|
-
end: new Date('2025-09-16T23:59:59.999Z'),
|
|
87
|
-
};
|
|
88
|
-
const result = (0, dateRanges_1.convertDateRangeToJSURL)(range);
|
|
89
|
-
expect(result).toBe('<= 2025-09-16T23:59:59.999Z');
|
|
90
|
-
});
|
|
91
|
-
it('should throw for empty ranges', () => {
|
|
92
|
-
expect(() => (0, dateRanges_1.convertDateRangeToJSURL)({})).toThrow('Date range must have at least one bound');
|
|
93
|
-
});
|
|
94
|
-
});
|
|
95
|
-
});
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Property Specification
|
|
3
|
-
*
|
|
4
|
-
* Property Specifications is an argument pattern used to express that
|
|
5
|
-
* a specific property has a specific value. In GET requests, this is used
|
|
6
|
-
* to specify the query parameters. In PUT requests, this is used to
|
|
7
|
-
* specify the properties to update.
|
|
8
|
-
*
|
|
9
|
-
* Each follows the format: slug=value
|
|
10
|
-
*
|
|
11
|
-
* It is typically used as the argv in a command.
|
|
12
|
-
*
|
|
13
|
-
* Examples:
|
|
14
|
-
*
|
|
15
|
-
* contrail entities get color -- id=123
|
|
16
|
-
* contrail entities update color 123 -- name=obsidian hex=000000
|
|
17
|
-
* contrail entities get loader-processes -- createdOn=2025-09-15..2025-09-16
|
|
18
|
-
*/
|
|
19
|
-
export type PropertySpecification = {
|
|
20
|
-
slug: string;
|
|
21
|
-
value: string;
|
|
22
|
-
};
|
|
23
|
-
export type PropertySpecificationBySlug = Map<string, string | number | string[] | number[]>;
|
|
24
|
-
export type PropertySpecificationString = `${string}=${string}`;
|
|
25
|
-
export declare function parsePropertySpecification(spec: PropertySpecificationString): PropertySpecification;
|
|
26
|
-
export declare function parsePropertySpecificationsIntoMap(propertySpecifications: PropertySpecificationString[]): PropertySpecificationBySlug;
|
|
27
|
-
export declare function isValidPropertySpecification(maybeSpec: string): maybeSpec is PropertySpecificationString;
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Property Specification
|
|
4
|
-
*
|
|
5
|
-
* Property Specifications is an argument pattern used to express that
|
|
6
|
-
* a specific property has a specific value. In GET requests, this is used
|
|
7
|
-
* to specify the query parameters. In PUT requests, this is used to
|
|
8
|
-
* specify the properties to update.
|
|
9
|
-
*
|
|
10
|
-
* Each follows the format: slug=value
|
|
11
|
-
*
|
|
12
|
-
* It is typically used as the argv in a command.
|
|
13
|
-
*
|
|
14
|
-
* Examples:
|
|
15
|
-
*
|
|
16
|
-
* contrail entities get color -- id=123
|
|
17
|
-
* contrail entities update color 123 -- name=obsidian hex=000000
|
|
18
|
-
* contrail entities get loader-processes -- createdOn=2025-09-15..2025-09-16
|
|
19
|
-
*/
|
|
20
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
-
exports.isValidPropertySpecification = exports.parsePropertySpecificationsIntoMap = exports.parsePropertySpecification = void 0;
|
|
22
|
-
const dateRanges_1 = require("./dateRanges");
|
|
23
|
-
function parsePropertySpecification(spec) {
|
|
24
|
-
const equalsIndex = spec.indexOf('=');
|
|
25
|
-
if (equalsIndex === -1) {
|
|
26
|
-
throw new Error(`Invalid property specification: ${spec}`);
|
|
27
|
-
}
|
|
28
|
-
const slug = spec.substring(0, equalsIndex);
|
|
29
|
-
const value = spec.substring(equalsIndex + 1);
|
|
30
|
-
if (!slug || !value) {
|
|
31
|
-
throw new Error(`Invalid property specification: ${spec}`);
|
|
32
|
-
}
|
|
33
|
-
return { slug, value };
|
|
34
|
-
}
|
|
35
|
-
exports.parsePropertySpecification = parsePropertySpecification;
|
|
36
|
-
function parsePropertySpecificationsIntoMap(propertySpecifications) {
|
|
37
|
-
const propertySpecBySlug = new Map();
|
|
38
|
-
for (const spec of propertySpecifications) {
|
|
39
|
-
if ((0, dateRanges_1.isDateRangeSpecification)(spec)) {
|
|
40
|
-
const { slug, range } = (0, dateRanges_1.parseDateRangeSpecification)(spec);
|
|
41
|
-
const jsUrlValue = (0, dateRanges_1.convertDateRangeToJSURL)(range);
|
|
42
|
-
propertySpecBySlug.set(slug, jsUrlValue);
|
|
43
|
-
continue;
|
|
44
|
-
}
|
|
45
|
-
const { slug, value } = parsePropertySpecification(spec);
|
|
46
|
-
if (slug === 'roles') {
|
|
47
|
-
// Special handling for roles, which can be an array
|
|
48
|
-
const rolesArray = value.split(',').map((role) => role.trim());
|
|
49
|
-
propertySpecBySlug.set(slug, rolesArray);
|
|
50
|
-
continue;
|
|
51
|
-
}
|
|
52
|
-
propertySpecBySlug.set(slug, value);
|
|
53
|
-
}
|
|
54
|
-
return propertySpecBySlug;
|
|
55
|
-
}
|
|
56
|
-
exports.parsePropertySpecificationsIntoMap = parsePropertySpecificationsIntoMap;
|
|
57
|
-
function isValidPropertySpecification(maybeSpec) {
|
|
58
|
-
try {
|
|
59
|
-
if ((0, dateRanges_1.isDateRangeSpecification)(maybeSpec)) {
|
|
60
|
-
(0, dateRanges_1.parseDateRangeSpecification)(maybeSpec);
|
|
61
|
-
return true;
|
|
62
|
-
}
|
|
63
|
-
const { slug, value } = parsePropertySpecification(maybeSpec);
|
|
64
|
-
return !!slug && !!value;
|
|
65
|
-
}
|
|
66
|
-
catch (e) {
|
|
67
|
-
return false;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
exports.isValidPropertySpecification = isValidPropertySpecification;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,266 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const propertySpecifications_1 = require("./propertySpecifications");
|
|
4
|
-
describe('parsePropertySpecification', () => {
|
|
5
|
-
it('should parse a valid property specification', () => {
|
|
6
|
-
const spec = 'name=john';
|
|
7
|
-
const result = (0, propertySpecifications_1.parsePropertySpecification)(spec);
|
|
8
|
-
expect(result).toEqual({
|
|
9
|
-
slug: 'name',
|
|
10
|
-
value: 'john',
|
|
11
|
-
});
|
|
12
|
-
});
|
|
13
|
-
it('should parse property specification with numeric value', () => {
|
|
14
|
-
const spec = 'id=123';
|
|
15
|
-
const result = (0, propertySpecifications_1.parsePropertySpecification)(spec);
|
|
16
|
-
expect(result).toEqual({
|
|
17
|
-
slug: 'id',
|
|
18
|
-
value: '123',
|
|
19
|
-
});
|
|
20
|
-
});
|
|
21
|
-
it('should parse property specification with hex color value', () => {
|
|
22
|
-
const spec = 'hex=000000';
|
|
23
|
-
const result = (0, propertySpecifications_1.parsePropertySpecification)(spec);
|
|
24
|
-
expect(result).toEqual({
|
|
25
|
-
slug: 'hex',
|
|
26
|
-
value: '000000',
|
|
27
|
-
});
|
|
28
|
-
});
|
|
29
|
-
it('should parse property specification with spaces in value', () => {
|
|
30
|
-
const spec = 'name=john doe';
|
|
31
|
-
const result = (0, propertySpecifications_1.parsePropertySpecification)(spec);
|
|
32
|
-
expect(result).toEqual({
|
|
33
|
-
slug: 'name',
|
|
34
|
-
value: 'john doe',
|
|
35
|
-
});
|
|
36
|
-
});
|
|
37
|
-
it('should parse property specification with equals sign in value', () => {
|
|
38
|
-
const spec = 'formula=x=y+z';
|
|
39
|
-
const result = (0, propertySpecifications_1.parsePropertySpecification)(spec);
|
|
40
|
-
expect(result).toEqual({
|
|
41
|
-
slug: 'formula',
|
|
42
|
-
value: 'x=y+z', // Now correctly keeps everything after first equals
|
|
43
|
-
});
|
|
44
|
-
});
|
|
45
|
-
it('should parse property specification with empty value', () => {
|
|
46
|
-
const spec = 'description=';
|
|
47
|
-
// Current implementation throws error for empty values
|
|
48
|
-
expect(() => (0, propertySpecifications_1.parsePropertySpecification)(spec)).toThrow('Invalid property specification: description=');
|
|
49
|
-
});
|
|
50
|
-
it('should throw error for spec without equals sign', () => {
|
|
51
|
-
const spec = 'invalidspec';
|
|
52
|
-
expect(() => (0, propertySpecifications_1.parsePropertySpecification)(spec)).toThrow('Invalid property specification: invalidspec');
|
|
53
|
-
});
|
|
54
|
-
it('should throw error for spec with only equals sign', () => {
|
|
55
|
-
const spec = '=';
|
|
56
|
-
expect(() => (0, propertySpecifications_1.parsePropertySpecification)(spec)).toThrow('Invalid property specification: =');
|
|
57
|
-
});
|
|
58
|
-
it('should throw error for spec with empty slug', () => {
|
|
59
|
-
const spec = '=value';
|
|
60
|
-
expect(() => (0, propertySpecifications_1.parsePropertySpecification)(spec)).toThrow('Invalid property specification: =value');
|
|
61
|
-
});
|
|
62
|
-
it('should parse property specification with special characters in slug', () => {
|
|
63
|
-
const spec = 'property_name=value';
|
|
64
|
-
const result = (0, propertySpecifications_1.parsePropertySpecification)(spec);
|
|
65
|
-
expect(result).toEqual({
|
|
66
|
-
slug: 'property_name',
|
|
67
|
-
value: 'value',
|
|
68
|
-
});
|
|
69
|
-
});
|
|
70
|
-
it('should parse property specification with hyphenated slug', () => {
|
|
71
|
-
const spec = 'property-name=value';
|
|
72
|
-
const result = (0, propertySpecifications_1.parsePropertySpecification)(spec);
|
|
73
|
-
expect(result).toEqual({
|
|
74
|
-
slug: 'property-name',
|
|
75
|
-
value: 'value',
|
|
76
|
-
});
|
|
77
|
-
});
|
|
78
|
-
});
|
|
79
|
-
describe('parsePropertySpecificationsIntoMap', () => {
|
|
80
|
-
it('should parse multiple property specifications into a map', () => {
|
|
81
|
-
const specs = ['name=obsidian', 'hex=000000', 'id=123'];
|
|
82
|
-
const result = (0, propertySpecifications_1.parsePropertySpecificationsIntoMap)(specs);
|
|
83
|
-
expect(result).toBeInstanceOf(Map);
|
|
84
|
-
expect(result.get('name')).toBe('obsidian');
|
|
85
|
-
expect(result.get('hex')).toBe('000000');
|
|
86
|
-
expect(result.get('id')).toBe('123');
|
|
87
|
-
expect(result.size).toBe(3);
|
|
88
|
-
});
|
|
89
|
-
it('should handle empty array', () => {
|
|
90
|
-
const specs = [];
|
|
91
|
-
const result = (0, propertySpecifications_1.parsePropertySpecificationsIntoMap)(specs);
|
|
92
|
-
expect(result).toBeInstanceOf(Map);
|
|
93
|
-
expect(result.size).toBe(0);
|
|
94
|
-
});
|
|
95
|
-
it('should handle single property specification', () => {
|
|
96
|
-
const specs = ['name=john'];
|
|
97
|
-
const result = (0, propertySpecifications_1.parsePropertySpecificationsIntoMap)(specs);
|
|
98
|
-
expect(result).toBeInstanceOf(Map);
|
|
99
|
-
expect(result.get('name')).toBe('john');
|
|
100
|
-
expect(result.size).toBe(1);
|
|
101
|
-
});
|
|
102
|
-
it('should handle property specifications with complex values', () => {
|
|
103
|
-
const specs = [
|
|
104
|
-
'description=This is a long description with spaces',
|
|
105
|
-
'formula=x=y+z',
|
|
106
|
-
'url=https://example.com',
|
|
107
|
-
];
|
|
108
|
-
const result = (0, propertySpecifications_1.parsePropertySpecificationsIntoMap)(specs);
|
|
109
|
-
expect(result.get('description')).toBe('This is a long description with spaces');
|
|
110
|
-
expect(result.get('formula')).toBe('x=y+z'); // Now correctly handles multiple equals
|
|
111
|
-
expect(result.get('url')).toBe('https://example.com');
|
|
112
|
-
expect(result.size).toBe(3);
|
|
113
|
-
});
|
|
114
|
-
it('should handle duplicate slugs by overwriting with last value', () => {
|
|
115
|
-
const specs = ['name=first', 'name=second', 'name=third'];
|
|
116
|
-
const result = (0, propertySpecifications_1.parsePropertySpecificationsIntoMap)(specs);
|
|
117
|
-
expect(result.get('name')).toBe('third');
|
|
118
|
-
expect(result.size).toBe(1);
|
|
119
|
-
});
|
|
120
|
-
it('should handle property specifications with empty values', () => {
|
|
121
|
-
const specs = ['name=john', 'description=', 'notes='];
|
|
122
|
-
// Current implementation throws error for empty values
|
|
123
|
-
expect(() => (0, propertySpecifications_1.parsePropertySpecificationsIntoMap)(specs)).toThrow('Invalid property specification: description=');
|
|
124
|
-
});
|
|
125
|
-
it('should throw error for invalid property specifications', () => {
|
|
126
|
-
const specs = [
|
|
127
|
-
'name=john',
|
|
128
|
-
'invalid',
|
|
129
|
-
];
|
|
130
|
-
expect(() => (0, propertySpecifications_1.parsePropertySpecificationsIntoMap)(specs)).toThrow('Invalid property specification: invalid');
|
|
131
|
-
});
|
|
132
|
-
});
|
|
133
|
-
describe('isValidPropertySpecification', () => {
|
|
134
|
-
it('should return true for valid property specifications', () => {
|
|
135
|
-
expect((0, propertySpecifications_1.isValidPropertySpecification)('name=john')).toBe(true);
|
|
136
|
-
expect((0, propertySpecifications_1.isValidPropertySpecification)('id=123')).toBe(true);
|
|
137
|
-
expect((0, propertySpecifications_1.isValidPropertySpecification)('hex=000000')).toBe(true);
|
|
138
|
-
expect((0, propertySpecifications_1.isValidPropertySpecification)('description=This is a description')).toBe(true);
|
|
139
|
-
expect((0, propertySpecifications_1.isValidPropertySpecification)('formula=x=y+z')).toBe(true);
|
|
140
|
-
expect((0, propertySpecifications_1.isValidPropertySpecification)('property_name=value')).toBe(true);
|
|
141
|
-
expect((0, propertySpecifications_1.isValidPropertySpecification)('property-name=value')).toBe(true);
|
|
142
|
-
});
|
|
143
|
-
it('should return true for property specifications with empty values', () => {
|
|
144
|
-
// Current implementation rejects empty values
|
|
145
|
-
expect((0, propertySpecifications_1.isValidPropertySpecification)('description=')).toBe(false);
|
|
146
|
-
expect((0, propertySpecifications_1.isValidPropertySpecification)('notes=')).toBe(false);
|
|
147
|
-
});
|
|
148
|
-
it('should return false for invalid property specifications', () => {
|
|
149
|
-
expect((0, propertySpecifications_1.isValidPropertySpecification)('invalid')).toBe(false);
|
|
150
|
-
expect((0, propertySpecifications_1.isValidPropertySpecification)('')).toBe(false);
|
|
151
|
-
expect((0, propertySpecifications_1.isValidPropertySpecification)('=')).toBe(false);
|
|
152
|
-
expect((0, propertySpecifications_1.isValidPropertySpecification)('=value')).toBe(false);
|
|
153
|
-
});
|
|
154
|
-
it('should return false for null or undefined', () => {
|
|
155
|
-
expect((0, propertySpecifications_1.isValidPropertySpecification)(null)).toBe(false);
|
|
156
|
-
expect((0, propertySpecifications_1.isValidPropertySpecification)(undefined)).toBe(false);
|
|
157
|
-
});
|
|
158
|
-
it('should return false for non-string values', () => {
|
|
159
|
-
expect((0, propertySpecifications_1.isValidPropertySpecification)(123)).toBe(false);
|
|
160
|
-
expect((0, propertySpecifications_1.isValidPropertySpecification)({})).toBe(false);
|
|
161
|
-
expect((0, propertySpecifications_1.isValidPropertySpecification)([])).toBe(false);
|
|
162
|
-
});
|
|
163
|
-
it('should act as type guard', () => {
|
|
164
|
-
const maybeSpec = 'name=john';
|
|
165
|
-
if ((0, propertySpecifications_1.isValidPropertySpecification)(maybeSpec)) {
|
|
166
|
-
// TypeScript should recognize maybeSpec as PropertySpecificationString
|
|
167
|
-
const parsed = (0, propertySpecifications_1.parsePropertySpecification)(maybeSpec);
|
|
168
|
-
expect(parsed.slug).toBe('name');
|
|
169
|
-
expect(parsed.value).toBe('john');
|
|
170
|
-
}
|
|
171
|
-
});
|
|
172
|
-
});
|
|
173
|
-
describe('integration tests', () => {
|
|
174
|
-
it('should work with the example from the comments', () => {
|
|
175
|
-
// Example: contrail entities get color -- id=123
|
|
176
|
-
const getSpecs = ['id=123'];
|
|
177
|
-
const getMap = (0, propertySpecifications_1.parsePropertySpecificationsIntoMap)(getSpecs);
|
|
178
|
-
expect(getMap.get('id')).toBe('123');
|
|
179
|
-
expect(getMap.size).toBe(1);
|
|
180
|
-
});
|
|
181
|
-
it('should work with the update example from the comments', () => {
|
|
182
|
-
// Example: contrail entities update color 123 -- name=obsidian hex=000000
|
|
183
|
-
const updateSpecs = ['name=obsidian', 'hex=000000'];
|
|
184
|
-
const updateMap = (0, propertySpecifications_1.parsePropertySpecificationsIntoMap)(updateSpecs);
|
|
185
|
-
expect(updateMap.get('name')).toBe('obsidian');
|
|
186
|
-
expect(updateMap.get('hex')).toBe('000000');
|
|
187
|
-
expect(updateMap.size).toBe(2);
|
|
188
|
-
});
|
|
189
|
-
it('should handle validation before parsing', () => {
|
|
190
|
-
const userInputs = ['name=john', 'invalid', 'id=123', '=empty'];
|
|
191
|
-
const validSpecs = userInputs.filter(propertySpecifications_1.isValidPropertySpecification);
|
|
192
|
-
expect(validSpecs).toEqual(['name=john', 'id=123']);
|
|
193
|
-
const map = (0, propertySpecifications_1.parsePropertySpecificationsIntoMap)(validSpecs);
|
|
194
|
-
expect(map.get('name')).toBe('john');
|
|
195
|
-
expect(map.get('id')).toBe('123');
|
|
196
|
-
expect(map.size).toBe(2);
|
|
197
|
-
});
|
|
198
|
-
});
|
|
199
|
-
describe('edge cases and limitations', () => {
|
|
200
|
-
it('should handle multiple equals signs correctly', () => {
|
|
201
|
-
// This test shows that multiple equals signs are now handled correctly
|
|
202
|
-
const spec = 'calculation=2+2=4';
|
|
203
|
-
const result = (0, propertySpecifications_1.parsePropertySpecification)(spec);
|
|
204
|
-
expect(result).toEqual({
|
|
205
|
-
slug: 'calculation',
|
|
206
|
-
value: '2+2=4', // now preserves everything after first equals
|
|
207
|
-
});
|
|
208
|
-
});
|
|
209
|
-
it('should handle the specific example equation=x+z=1', () => {
|
|
210
|
-
const spec = 'equation=x+z=1';
|
|
211
|
-
const result = (0, propertySpecifications_1.parsePropertySpecification)(spec);
|
|
212
|
-
expect(result).toEqual({
|
|
213
|
-
slug: 'equation',
|
|
214
|
-
value: 'x+z=1',
|
|
215
|
-
});
|
|
216
|
-
});
|
|
217
|
-
it('should handle mathematical expressions with one equals', () => {
|
|
218
|
-
const spec = 'formula=x+y';
|
|
219
|
-
const result = (0, propertySpecifications_1.parsePropertySpecification)(spec);
|
|
220
|
-
expect(result).toEqual({
|
|
221
|
-
slug: 'formula',
|
|
222
|
-
value: 'x+y',
|
|
223
|
-
});
|
|
224
|
-
});
|
|
225
|
-
it('should handle URLs without equals signs', () => {
|
|
226
|
-
const spec = 'url=https://example.com/path';
|
|
227
|
-
const result = (0, propertySpecifications_1.parsePropertySpecification)(spec);
|
|
228
|
-
expect(result).toEqual({
|
|
229
|
-
slug: 'url',
|
|
230
|
-
value: 'https://example.com/path',
|
|
231
|
-
});
|
|
232
|
-
});
|
|
233
|
-
it('should handle JSON-like values', () => {
|
|
234
|
-
const spec = 'config={"key":"value"}';
|
|
235
|
-
const result = (0, propertySpecifications_1.parsePropertySpecification)(spec);
|
|
236
|
-
expect(result).toEqual({
|
|
237
|
-
slug: 'config',
|
|
238
|
-
value: '{"key":"value"}',
|
|
239
|
-
});
|
|
240
|
-
});
|
|
241
|
-
it('should handle comma-separated values', () => {
|
|
242
|
-
const spec = 'tags=red,blue,green';
|
|
243
|
-
const result = (0, propertySpecifications_1.parsePropertySpecification)(spec);
|
|
244
|
-
expect(result).toEqual({
|
|
245
|
-
slug: 'tags',
|
|
246
|
-
value: 'red,blue,green',
|
|
247
|
-
});
|
|
248
|
-
});
|
|
249
|
-
it('should handle escaped characters', () => {
|
|
250
|
-
const spec = 'message=Hello\\nWorld';
|
|
251
|
-
const result = (0, propertySpecifications_1.parsePropertySpecification)(spec);
|
|
252
|
-
expect(result).toEqual({
|
|
253
|
-
slug: 'message',
|
|
254
|
-
value: 'Hello\\nWorld',
|
|
255
|
-
});
|
|
256
|
-
});
|
|
257
|
-
it('should handle whitespace in slugs', () => {
|
|
258
|
-
// This documents current behavior - spaces in slugs are allowed
|
|
259
|
-
const spec = 'property name=value';
|
|
260
|
-
const result = (0, propertySpecifications_1.parsePropertySpecification)(spec);
|
|
261
|
-
expect(result).toEqual({
|
|
262
|
-
slug: 'property name',
|
|
263
|
-
value: 'value',
|
|
264
|
-
});
|
|
265
|
-
});
|
|
266
|
-
});
|