@markwharton/liquidplanner 2.7.1 → 3.0.0

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/dist/client.js CHANGED
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * @see https://api-docs.liquidplanner.com/
8
8
  */
9
- import { buildAuthHeader, hoursToMinutes, normalizeItemType, filterIs, filterIsNot, filterIn, filterAfter, filterBefore, joinFilters, paginatedFetch, } from './utils.js';
9
+ import { buildAuthHeader, hoursToMinutes, filterIs, filterIsNot, filterIn, filterAfter, filterBefore, joinFilters, paginatedFetch, } from './utils.js';
10
10
  import { buildTree, getTreeAncestors } from './tree.js';
11
11
  import { parseLPErrorResponse } from './errors.js';
12
12
  import { LP_API_BASE } from './constants.js';
@@ -16,7 +16,7 @@ function transformItem(raw) {
16
16
  const item = {
17
17
  id: raw.id,
18
18
  name: raw.name || null,
19
- itemType: normalizeItemType(raw.itemType),
19
+ itemType: raw.itemType,
20
20
  parentId: raw.parentId,
21
21
  costCodeId: raw.costCodeId,
22
22
  userId: raw.userId,
@@ -346,7 +346,7 @@ export class LPClient {
346
346
  return rawData.map((a) => ({
347
347
  id: a.id,
348
348
  name: a.name || null,
349
- itemType: normalizeItemType(a.itemType),
349
+ itemType: a.itemType,
350
350
  })).reverse(); // LP API returns child→root, normalize to root→child
351
351
  }, { description: `Get ancestors for item ${itemId}` });
352
352
  });
@@ -489,11 +489,11 @@ export class LPClient {
489
489
  const tree = treeResult.data;
490
490
  const assignments = [];
491
491
  for (const node of tree.byId.values()) {
492
- if (node.itemType !== 'Assignment' || node.userId !== memberId)
492
+ if (node.itemType !== 'assignments' || node.userId !== memberId)
493
493
  continue;
494
494
  const ancestors = getTreeAncestors(tree, node.id);
495
- const taskAncestor = ancestors.find(a => a.itemType === 'Task');
496
- const hierarchyAncestors = ancestors.filter(a => a.itemType === 'Project' || a.itemType === 'Folder');
495
+ const taskAncestor = ancestors.find(a => a.itemType === 'tasks');
496
+ const hierarchyAncestors = ancestors.filter(a => a.itemType === 'projects' || a.itemType === 'folders');
497
497
  const { children: _, ...itemFields } = node;
498
498
  const result = { ...itemFields };
499
499
  result.taskName = taskAncestor?.name ?? null;
package/dist/index.d.ts CHANGED
@@ -30,12 +30,12 @@
30
30
  */
31
31
  export { LPClient } from './client.js';
32
32
  export { resolveTaskToAssignment } from './workflows.js';
33
- export type { LPConfig, LPCacheConfig, LPRetryConfig, LPItemType, LPHierarchyItem, LPItem, LPAncestor, LPWorkspace, LPMember, LPCostCode, LPSyncResult, LPTimesheetEntry, LPTaskResolution, LPUpsertOptions, LPAssignment, LPFindItemsOptions, LPTreeNode, LPWorkspaceTree, } from './types.js';
33
+ export type { LPConfig, LPCacheConfig, LPRetryConfig, LPItemType, LPUserType, LPHierarchyItem, LPItem, LPAncestor, LPWorkspace, LPMember, LPCostCode, LPSyncResult, LPTimesheetEntry, LPTaskResolution, LPUpsertOptions, LPAssignment, LPFindItemsOptions, LPTreeNode, LPWorkspaceTree, } from './types.js';
34
34
  export type { AccessTier } from './types.js';
35
35
  export { METHOD_TIERS, ENTITIES } from './types.js';
36
- export { ok, err, getErrorMessage, TTLCache, MemoryCacheStore, LayeredCache } from '@markwharton/api-core';
36
+ export { ok, err, getErrorMessage, normalizeEnum, TTLCache, MemoryCacheStore, LayeredCache } from '@markwharton/api-core';
37
37
  export type { Result, RetryConfig, OnRequestCallback, ClientConfig, Cache, CacheStore, CacheGetOptions } from '@markwharton/api-core';
38
- export { hoursToMinutes, normalizeItemType, buildAuthHeader, filterIs, filterIsNot, filterIn, filterGt, filterLt, filterAfter, filterBefore, joinFilters, paginatedFetch, } from './utils.js';
38
+ export { hoursToMinutes, buildAuthHeader, filterIs, filterIsNot, filterIn, filterGt, filterLt, filterAfter, filterBefore, joinFilters, paginatedFetch, } from './utils.js';
39
39
  export type { PaginateOptions } from './utils.js';
40
40
  export { buildTree, getTreeAncestors, getTreeHierarchyPath, findInTree, } from './tree.js';
41
41
  export { LP_API_BASE } from './constants.js';
package/dist/index.js CHANGED
@@ -34,9 +34,9 @@ export { LPClient } from './client.js';
34
34
  export { resolveTaskToAssignment } from './workflows.js';
35
35
  export { METHOD_TIERS, ENTITIES } from './types.js';
36
36
  // Re-exported from @markwharton/api-core
37
- export { ok, err, getErrorMessage, TTLCache, MemoryCacheStore, LayeredCache } from '@markwharton/api-core';
37
+ export { ok, err, getErrorMessage, normalizeEnum, TTLCache, MemoryCacheStore, LayeredCache } from '@markwharton/api-core';
38
38
  // Utilities
39
- export { hoursToMinutes, normalizeItemType, buildAuthHeader, filterIs, filterIsNot, filterIn, filterGt, filterLt, filterAfter, filterBefore, joinFilters, paginatedFetch, } from './utils.js';
39
+ export { hoursToMinutes, buildAuthHeader, filterIs, filterIsNot, filterIn, filterGt, filterLt, filterAfter, filterBefore, joinFilters, paginatedFetch, } from './utils.js';
40
40
  // Tree utilities
41
41
  export { buildTree, getTreeAncestors, getTreeHierarchyPath, findInTree, } from './tree.js';
42
42
  // Constants
package/dist/tree.d.ts CHANGED
@@ -22,8 +22,8 @@ export declare function getTreeAncestors(tree: LPWorkspaceTree, itemId: number):
22
22
  /**
23
23
  * Build a formatted hierarchy path for an item
24
24
  *
25
- * Returns a string like "Project A › Subfolder B" from Project and Folder ancestors.
26
- * Excludes system containers (Package, WorkspaceRoot) and Tasks.
25
+ * Returns a string like "Project A › Subfolder B" from projects and folders ancestors.
26
+ * Excludes system containers (packages, workspaceRoots) and tasks.
27
27
  */
28
28
  export declare function getTreeHierarchyPath(tree: LPWorkspaceTree, itemId: number): string;
29
29
  /**
package/dist/tree.js CHANGED
@@ -62,12 +62,12 @@ export function getTreeAncestors(tree, itemId) {
62
62
  /**
63
63
  * Build a formatted hierarchy path for an item
64
64
  *
65
- * Returns a string like "Project A › Subfolder B" from Project and Folder ancestors.
66
- * Excludes system containers (Package, WorkspaceRoot) and Tasks.
65
+ * Returns a string like "Project A › Subfolder B" from projects and folders ancestors.
66
+ * Excludes system containers (packages, workspaceRoots) and tasks.
67
67
  */
68
68
  export function getTreeHierarchyPath(tree, itemId) {
69
69
  const ancestors = getTreeAncestors(tree, itemId);
70
- const hierarchyAncestors = ancestors.filter(a => a.itemType === 'Project' || a.itemType === 'Folder');
70
+ const hierarchyAncestors = ancestors.filter(a => a.itemType === 'projects' || a.itemType === 'folders');
71
71
  return hierarchyAncestors
72
72
  .map(a => a.name ?? `[${a.id}]`)
73
73
  .join(' › ');
package/dist/types.d.ts CHANGED
@@ -8,7 +8,7 @@ import type { Result, RetryConfig, ClientConfig, EntityDef } from '@markwharton/
8
8
  /**
9
9
  * LiquidPlanner item types in the hierarchy
10
10
  */
11
- export type LPItemType = 'Task' | 'Assignment' | 'Folder' | 'Project' | 'Package' | 'WorkspaceRoot' | 'Milestone' | 'Event';
11
+ export type LPItemType = 'workspaceRoots' | 'packages' | 'projects' | 'folders' | 'tasks' | 'assignments' | 'custom';
12
12
  /**
13
13
  * A generic hierarchy item for representing project/task paths.
14
14
  *
@@ -131,6 +131,8 @@ export interface LPWorkspace {
131
131
  /** Workspace name */
132
132
  name: string;
133
133
  }
134
+ /** LiquidPlanner user type */
135
+ export type LPUserType = 'member' | 'resource' | 'placeholder';
134
136
  /**
135
137
  * A workspace member from LiquidPlanner
136
138
  *
@@ -154,7 +156,7 @@ export interface LPMember {
154
156
  /** Last name */
155
157
  lastName: string;
156
158
  /** User type (member, resource, or placeholder) */
157
- userType: 'member' | 'resource' | 'placeholder';
159
+ userType: LPUserType;
158
160
  }
159
161
  /**
160
162
  * Result of resolving an LP Item ID to the correct Assignment ID for logging time
package/dist/utils.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  /**
2
2
  * LiquidPlanner Utility Functions
3
3
  */
4
- import type { LPItemType } from './types.js';
5
4
  import type { Result } from '@markwharton/api-core';
6
5
  /**
7
6
  * Build a URL-encoded filter for LP API: field[is]="value"
@@ -67,13 +66,6 @@ export declare function paginatedFetch<TRaw, TResult>(options: PaginateOptions<T
67
66
  * @throws Error if hours is not a valid non-negative number
68
67
  */
69
68
  export declare function hoursToMinutes(hours: number): number;
70
- /**
71
- * Normalize LP API itemType values to internal format
72
- *
73
- * LP API returns lowercase plural (e.g., "tasks", "assignments")
74
- * but our code uses PascalCase singular (e.g., "Task", "Assignment")
75
- */
76
- export declare function normalizeItemType(apiItemType: string): LPItemType;
77
69
  /**
78
70
  * Build the Authorization header for LP API requests
79
71
  */
package/dist/utils.js CHANGED
@@ -124,35 +124,6 @@ export function hoursToMinutes(hours) {
124
124
  }
125
125
  return Math.round(hours * 60);
126
126
  }
127
- /**
128
- * Normalize LP API itemType values to internal format
129
- *
130
- * LP API returns lowercase plural (e.g., "tasks", "assignments")
131
- * but our code uses PascalCase singular (e.g., "Task", "Assignment")
132
- */
133
- export function normalizeItemType(apiItemType) {
134
- const mapping = {
135
- // LP API lowercase plural format
136
- 'tasks': 'Task',
137
- 'assignments': 'Assignment',
138
- 'folders': 'Folder',
139
- 'projects': 'Project',
140
- 'packages': 'Package',
141
- 'workspaceRoots': 'WorkspaceRoot',
142
- 'milestones': 'Milestone',
143
- 'events': 'Event',
144
- // Already-normalized values (for safety)
145
- 'Task': 'Task',
146
- 'Assignment': 'Assignment',
147
- 'Folder': 'Folder',
148
- 'Project': 'Project',
149
- 'Package': 'Package',
150
- 'WorkspaceRoot': 'WorkspaceRoot',
151
- 'Milestone': 'Milestone',
152
- 'Event': 'Event',
153
- };
154
- return mapping[apiItemType] || apiItemType;
155
- }
156
127
  /**
157
128
  * Build the Authorization header for LP API requests
158
129
  */
package/dist/workflows.js CHANGED
@@ -40,7 +40,7 @@ export async function resolveTaskToAssignment(client, itemId, lpMemberId) {
40
40
  const itemResult = await client.getItem(itemId);
41
41
  if (!itemResult.ok || !itemResult.data) {
42
42
  return {
43
- inputItem: { id: itemId, name: null, itemType: 'Task' },
43
+ inputItem: { id: itemId, name: null, itemType: 'tasks' },
44
44
  assignmentId: 0,
45
45
  error: itemResult.error || 'Item not found',
46
46
  };
@@ -48,7 +48,7 @@ export async function resolveTaskToAssignment(client, itemId, lpMemberId) {
48
48
  const item = itemResult.data;
49
49
  // Step 2: Check item type and resolve accordingly
50
50
  switch (item.itemType) {
51
- case 'Assignment':
51
+ case 'assignments':
52
52
  // Already an assignment, use it directly
53
53
  return {
54
54
  inputItem: item,
@@ -56,7 +56,7 @@ export async function resolveTaskToAssignment(client, itemId, lpMemberId) {
56
56
  assignmentName: item.name ?? undefined,
57
57
  assignmentUserId: item.userId,
58
58
  };
59
- case 'Task': {
59
+ case 'tasks': {
60
60
  // Find assignments under this task
61
61
  const assignResult = await client.findAssignments(item.id);
62
62
  if (!assignResult.ok) {
@@ -119,19 +119,11 @@ export async function resolveTaskToAssignment(client, itemId, lpMemberId) {
119
119
  multipleAssignments: assignments,
120
120
  };
121
121
  }
122
- case 'Folder':
123
- case 'Milestone':
124
- case 'Event':
125
- return {
126
- inputItem: item,
127
- assignmentId: 0,
128
- error: `Cannot log time to a ${item.itemType}. Enter a Task or Assignment ID.`,
129
- };
130
122
  default:
131
123
  return {
132
124
  inputItem: item,
133
125
  assignmentId: 0,
134
- error: `Unsupported item type: ${item.itemType}`,
126
+ error: `Cannot log time to item type '${item.itemType}'. Provide a task or assignment ID.`,
135
127
  };
136
128
  }
137
129
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@markwharton/liquidplanner",
3
- "version": "2.7.1",
3
+ "version": "3.0.0",
4
4
  "description": "LiquidPlanner API client for timesheet integration",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -16,7 +16,7 @@
16
16
  "clean": "rm -rf dist"
17
17
  },
18
18
  "dependencies": {
19
- "@markwharton/api-core": "^1.5.0"
19
+ "@markwharton/api-core": "^1.6.1"
20
20
  },
21
21
  "devDependencies": {
22
22
  "@types/node": "^20.10.0",