@followupus/common 0.0.2 → 0.2.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/README.md CHANGED
@@ -1 +1,15 @@
1
- # common
1
+ # common
2
+
3
+ ## add a changelog after some modifications
4
+ ```sh
5
+ pnpm changeset
6
+ ```
7
+
8
+ ## publish package
9
+
10
+ update the `version` property in package.json
11
+
12
+ ```sh
13
+ pnpm version
14
+ pnpm release
15
+ ```
package/dist/index.d.ts CHANGED
@@ -1 +1,3 @@
1
1
  export declare const myPackage: (taco?: string) => string;
2
+ export * from "./shared/index";
3
+ export * from "./utils";
package/dist/index.js CHANGED
@@ -1 +1,3 @@
1
- export const myPackage = (taco = '') => `${taco} from my package`;
1
+ export const myPackage = (taco = '') => `${taco} from my package`;
2
+ export * from "./shared/index";
3
+ export * from "./utils";
@@ -0,0 +1,2 @@
1
+ export * from "./shared";
2
+ export * from "./types";
@@ -0,0 +1,2 @@
1
+ export * from "./shared";
2
+ export * from "./types";
@@ -0,0 +1,51 @@
1
+ import { IBaseCustomColumn, IBaseGroup, IFilter, IFilterCondition, IPureBoard } from "./types";
2
+ export declare const COLUMN_TYPES: {
3
+ NUMBER: string;
4
+ TEXT: string;
5
+ DROPDOWN: string;
6
+ DATE: string;
7
+ STATUS: string;
8
+ PRIORITY: string;
9
+ LINK: string;
10
+ TAGS: string;
11
+ EMAIL: string;
12
+ PEOPLE: string;
13
+ LINKED_COLUMN: string;
14
+ REF_LINED_COLUMN: string;
15
+ FILES: string;
16
+ HOURS: string;
17
+ TIMELINE: string;
18
+ PHONE: string;
19
+ CURRENCY: string;
20
+ };
21
+ export declare const OP_TYPES: {
22
+ CONTAINS: string;
23
+ NOT_CONTAINS: string;
24
+ IS: string;
25
+ IS_NOT: string;
26
+ EMPTY: string;
27
+ NOT_EMPTY: string;
28
+ EQUAL: string;
29
+ GREATER: string;
30
+ GREATER_EQUAL: string;
31
+ NOT_EQUAL: string;
32
+ LESS_EQUAL: string;
33
+ LESS: string;
34
+ };
35
+ export declare const COLUMN_OP_TYPES: {
36
+ [x: string]: string[];
37
+ };
38
+ export declare const DATE_GROUPS: {
39
+ OVERDUE: string;
40
+ TODAY: string;
41
+ TOMORROW: string;
42
+ THIS_WEEK: string;
43
+ NEXT_WEEK: string;
44
+ THIS_MONTH: string;
45
+ FUTURE: string;
46
+ };
47
+ export declare const getColumnValueType: (columnType: string) => "string" | "number" | "object" | "stringArray" | "objectArray";
48
+ export declare const getDateGroupKey: (dateStr: string) => string | null;
49
+ export declare const getValidConditions: (filter: IFilter, headers?: IBaseCustomColumn[]) => IFilterCondition[];
50
+ export declare const filterItemsByConditions: (groups: IBaseGroup[], filter: IFilter, headers?: IBaseCustomColumn[]) => IBaseGroup[];
51
+ export declare const filterBoardTree: (board: IPureBoard, filter: IFilter) => IPureBoard;
@@ -0,0 +1,392 @@
1
+ import { isEqual } from "lodash";
2
+ import dayjs from "dayjs";
3
+ export const COLUMN_TYPES = {
4
+ NUMBER: "number",
5
+ TEXT: "string",
6
+ DROPDOWN: "dropdown",
7
+ DATE: "date",
8
+ STATUS: "status",
9
+ PRIORITY: "priority",
10
+ LINK: "link",
11
+ TAGS: "tags",
12
+ EMAIL: "email",
13
+ PEOPLE: "people",
14
+ LINKED_COLUMN: "linked_column",
15
+ REF_LINED_COLUMN: "ref_linked_column",
16
+ FILES: "files",
17
+ HOURS: "hours",
18
+ TIMELINE: "timeline",
19
+ PHONE: "phone",
20
+ CURRENCY: "currency",
21
+ };
22
+ export const OP_TYPES = {
23
+ CONTAINS: "like",
24
+ NOT_CONTAINS: "not_like",
25
+ IS: "in",
26
+ IS_NOT: "not_in",
27
+ EMPTY: "empty",
28
+ NOT_EMPTY: "not_empty",
29
+ EQUAL: "eq",
30
+ GREATER: "gt",
31
+ GREATER_EQUAL: "gte",
32
+ NOT_EQUAL: "neq",
33
+ LESS_EQUAL: "lte",
34
+ LESS: "lt",
35
+ };
36
+ export const COLUMN_OP_TYPES = {
37
+ [COLUMN_TYPES.TEXT]: [
38
+ OP_TYPES.CONTAINS,
39
+ OP_TYPES.NOT_CONTAINS,
40
+ OP_TYPES.IS,
41
+ OP_TYPES.IS_NOT,
42
+ ],
43
+ [COLUMN_TYPES.DATE]: [
44
+ OP_TYPES.IS,
45
+ OP_TYPES.IS_NOT,
46
+ OP_TYPES.EMPTY,
47
+ OP_TYPES.NOT_EMPTY,
48
+ ],
49
+ [COLUMN_TYPES.NUMBER]: [
50
+ OP_TYPES.EQUAL,
51
+ OP_TYPES.NOT_EQUAL,
52
+ OP_TYPES.GREATER,
53
+ OP_TYPES.GREATER_EQUAL,
54
+ OP_TYPES.LESS,
55
+ OP_TYPES.LESS_EQUAL,
56
+ OP_TYPES.EMPTY,
57
+ OP_TYPES.NOT_EMPTY,
58
+ ],
59
+ [COLUMN_TYPES.DROPDOWN]: [
60
+ OP_TYPES.IS,
61
+ OP_TYPES.IS_NOT,
62
+ OP_TYPES.EMPTY,
63
+ OP_TYPES.NOT_EMPTY,
64
+ ],
65
+ [COLUMN_TYPES.STATUS]: [
66
+ OP_TYPES.IS,
67
+ OP_TYPES.IS_NOT,
68
+ OP_TYPES.EMPTY,
69
+ OP_TYPES.NOT_EMPTY,
70
+ ],
71
+ [COLUMN_TYPES.PRIORITY]: [
72
+ OP_TYPES.IS,
73
+ OP_TYPES.IS_NOT,
74
+ OP_TYPES.EMPTY,
75
+ OP_TYPES.NOT_EMPTY,
76
+ ],
77
+ [COLUMN_TYPES.PEOPLE]: [
78
+ OP_TYPES.IS,
79
+ OP_TYPES.IS_NOT,
80
+ OP_TYPES.EMPTY,
81
+ OP_TYPES.NOT_EMPTY,
82
+ ],
83
+ };
84
+ export const DATE_GROUPS = {
85
+ OVERDUE: "overdue",
86
+ TODAY: "today",
87
+ TOMORROW: "tomorrow",
88
+ THIS_WEEK: "thisWeek",
89
+ NEXT_WEEK: "nextWeek",
90
+ THIS_MONTH: "thisMonth",
91
+ FUTURE: "future",
92
+ };
93
+ export const getColumnValueType = (columnType) => {
94
+ switch (columnType) {
95
+ case COLUMN_TYPES.PEOPLE:
96
+ case COLUMN_TYPES.FILES:
97
+ case COLUMN_TYPES.TAGS:
98
+ return "stringArray";
99
+ case COLUMN_TYPES.TIMELINE:
100
+ return "object";
101
+ case COLUMN_TYPES.NUMBER:
102
+ case COLUMN_TYPES.HOURS:
103
+ case COLUMN_TYPES.CURRENCY:
104
+ return "number";
105
+ case COLUMN_TYPES.LINKED_COLUMN:
106
+ case COLUMN_TYPES.REF_LINED_COLUMN:
107
+ return "objectArray";
108
+ default:
109
+ return "string";
110
+ }
111
+ };
112
+ export const getDateGroupKey = (dateStr) => {
113
+ if (!dateStr)
114
+ return null;
115
+ const current = dayjs();
116
+ const day = dayjs(dateStr);
117
+ if (day.isBefore(current, "date")) {
118
+ return DATE_GROUPS.OVERDUE;
119
+ }
120
+ if (current.isSame(day, "date")) {
121
+ return DATE_GROUPS.TODAY;
122
+ }
123
+ const tomorrow = current.add(1, "day");
124
+ if (tomorrow.isSame(day, "date")) {
125
+ return DATE_GROUPS.TOMORROW;
126
+ }
127
+ const endDayOfThisWeek = current.set("day", 6);
128
+ if (day.isBefore(endDayOfThisWeek, "date") ||
129
+ day.isSame(endDayOfThisWeek, "date")) {
130
+ return DATE_GROUPS.THIS_WEEK;
131
+ }
132
+ const endDayOfNextWeek = endDayOfThisWeek.add(7, "day");
133
+ if (day.isAfter(endDayOfThisWeek, "date") &&
134
+ (day.isBefore(endDayOfNextWeek, "date") ||
135
+ day.isSame(endDayOfNextWeek, "date"))) {
136
+ return DATE_GROUPS.THIS_WEEK;
137
+ }
138
+ const endDayOfThisMonth = current.set("date", current.daysInMonth());
139
+ if (day.isBefore(endDayOfThisMonth, "date") ||
140
+ day.isSame(endDayOfThisMonth, "date")) {
141
+ return DATE_GROUPS.THIS_MONTH;
142
+ }
143
+ return DATE_GROUPS.FUTURE;
144
+ };
145
+ export const getValidConditions = (filter, headers) => {
146
+ if (!filter.criteria?.length || !headers?.length) {
147
+ return [];
148
+ }
149
+ // for dropdown, if set the option is in filter condition, and user delete the option. should ignore it or make the option can't be deleted
150
+ return filter.criteria.filter((c) => {
151
+ const validHeader = headers.find((h) => h.columnId === c.columnId);
152
+ if (!validHeader)
153
+ return false;
154
+ const validOperate = COLUMN_OP_TYPES[validHeader.type].includes(c.op);
155
+ if (!validOperate)
156
+ return false;
157
+ const isDropdown = [
158
+ COLUMN_TYPES.DROPDOWN,
159
+ COLUMN_TYPES.STATUS,
160
+ COLUMN_TYPES.PRIORITY,
161
+ ].includes(validHeader.type);
162
+ switch (c.op) {
163
+ case OP_TYPES.IS:
164
+ case OP_TYPES.IS_NOT:
165
+ return (c.blank ||
166
+ (!!c.value?.filter((v) => !!v || v === 0)?.length &&
167
+ (!isDropdown ||
168
+ c.value.some((v) => validHeader.dropdowns?.find((d) => d.key === v)))));
169
+ case OP_TYPES.EMPTY:
170
+ case OP_TYPES.NOT_EMPTY:
171
+ return true;
172
+ case OP_TYPES.CONTAINS:
173
+ case OP_TYPES.NOT_CONTAINS:
174
+ return !!c.value?.length;
175
+ case OP_TYPES.EQUAL:
176
+ case OP_TYPES.NOT_EQUAL:
177
+ case OP_TYPES.GREATER:
178
+ case OP_TYPES.GREATER_EQUAL:
179
+ case OP_TYPES.LESS:
180
+ case OP_TYPES.LESS_EQUAL:
181
+ return !!c.value?.length;
182
+ default:
183
+ return false;
184
+ }
185
+ });
186
+ };
187
+ const isInConditionValues = (itemVal, valueType, condition) => {
188
+ switch (valueType) {
189
+ case "number":
190
+ case "string":
191
+ return ((condition.blank && !itemVal) ||
192
+ !!condition.value.find((val) => val === itemVal));
193
+ case "stringArray":
194
+ return ((condition.blank && !itemVal?.length) ||
195
+ condition.value.some((val) => Array.isArray(itemVal) && itemVal?.includes(val)));
196
+ case "object":
197
+ return ((condition.blank && !itemVal) ||
198
+ !!condition.value.find((val) => isEqual(val, itemVal)));
199
+ case "objectArray":
200
+ return ((condition.blank && !itemVal?.length) ||
201
+ condition.value.some((val) => Array.isArray(itemVal) &&
202
+ itemVal.find((existVal) => isEqual(val, existVal))));
203
+ default:
204
+ break;
205
+ }
206
+ return false;
207
+ };
208
+ const isDateInConditionValues = (itemVal, condition) => {
209
+ if (condition.blank && !itemVal)
210
+ return true;
211
+ const groupKey = getDateGroupKey(itemVal);
212
+ if (!groupKey)
213
+ return !!condition.blank;
214
+ return !!condition.value?.find((dateGroup) => dateGroup === groupKey);
215
+ };
216
+ const isEmpty = (itemVal, valueType) => {
217
+ switch (valueType) {
218
+ case "number":
219
+ case "string":
220
+ return !itemVal;
221
+ case "stringArray":
222
+ return !itemVal?.length;
223
+ case "object":
224
+ return !itemVal || !Object.keys(itemVal).length;
225
+ case "objectArray":
226
+ return !itemVal?.length;
227
+ default:
228
+ break;
229
+ }
230
+ return true;
231
+ };
232
+ const isContains = (itemVal, valueType, condition) => {
233
+ const checkVal = condition.value?.length
234
+ ? condition.value[0]
235
+ : "";
236
+ switch (valueType) {
237
+ case "number":
238
+ return itemVal || itemVal === 0
239
+ ? itemVal.toString().includes(checkVal)
240
+ : false;
241
+ case "string":
242
+ return !!itemVal && itemVal.includes(checkVal);
243
+ case "stringArray":
244
+ return itemVal?.length
245
+ ? itemVal.some((val) => val.includes(checkVal))
246
+ : false;
247
+ default:
248
+ break;
249
+ }
250
+ return false;
251
+ };
252
+ const isEquals = (itemVal, valueType, condition) => {
253
+ const checkVal = condition.value?.length ? condition.value[0] : "";
254
+ switch (valueType) {
255
+ case "number":
256
+ return typeof checkVal === "number"
257
+ ? checkVal === itemVal
258
+ : parseInt(checkVal, 10) === itemVal;
259
+ case "string":
260
+ return !!itemVal && checkVal === itemVal;
261
+ case "object":
262
+ return isEqual(itemVal, checkVal);
263
+ default:
264
+ break;
265
+ }
266
+ return false;
267
+ };
268
+ const isGreater = (itemVal, condition) => {
269
+ const checkVal = condition.value?.length ? condition.value[0] : "";
270
+ if (typeof checkVal === "number") {
271
+ return itemVal > checkVal;
272
+ }
273
+ else {
274
+ const parseVal = parseInt(checkVal, 10);
275
+ return parseVal ? itemVal > parseVal : false;
276
+ }
277
+ };
278
+ const isGreaterEqual = (itemVal, condition) => {
279
+ const checkVal = condition.value?.length ? condition.value[0] : "";
280
+ if (typeof checkVal === "number") {
281
+ return itemVal >= checkVal;
282
+ }
283
+ else {
284
+ const parseVal = parseInt(checkVal, 10);
285
+ return parseVal ? itemVal >= parseVal : false;
286
+ }
287
+ };
288
+ const isLess = (itemVal, condition) => {
289
+ const checkVal = condition.value?.length ? condition.value[0] : "";
290
+ if (typeof checkVal === "number") {
291
+ return itemVal < checkVal;
292
+ }
293
+ else {
294
+ const parseVal = parseInt(checkVal, 10);
295
+ return parseVal ? itemVal < parseVal : false;
296
+ }
297
+ };
298
+ const isLessEqual = (itemVal, condition) => {
299
+ const checkVal = condition.value?.length ? condition.value[0] : "";
300
+ if (typeof checkVal === "number") {
301
+ return itemVal <= checkVal;
302
+ }
303
+ else {
304
+ const parseVal = parseInt(checkVal, 10);
305
+ return parseVal ? itemVal <= parseVal : false;
306
+ }
307
+ };
308
+ export const filterItemsByConditions = (groups, filter, headers) => {
309
+ if (!headers?.length)
310
+ return groups;
311
+ const validConditions = getValidConditions(filter, headers);
312
+ if (!validConditions?.length) {
313
+ return groups;
314
+ }
315
+ groups.forEach((g) => {
316
+ g.items = g.items?.filter((item) => {
317
+ let match = false;
318
+ for (const condition of validConditions) {
319
+ const header = headers?.find((h) => h.columnId === condition.columnId);
320
+ const valueType = getColumnValueType(header?.type ?? "");
321
+ const itemVal = item.data?.[condition.columnId]?.value;
322
+ const isDate = header?.type === COLUMN_TYPES.DATE;
323
+ switch (condition.op) {
324
+ case OP_TYPES.IS:
325
+ match = isDate
326
+ ? isDateInConditionValues(itemVal, condition)
327
+ : isInConditionValues(itemVal, valueType, condition);
328
+ break;
329
+ case OP_TYPES.IS_NOT:
330
+ match = isDate
331
+ ? !isDateInConditionValues(itemVal, condition)
332
+ : !isInConditionValues(itemVal, valueType, condition);
333
+ break;
334
+ case OP_TYPES.EMPTY:
335
+ match = isEmpty(itemVal, valueType);
336
+ break;
337
+ case OP_TYPES.NOT_EMPTY:
338
+ match = !isEmpty(itemVal, valueType);
339
+ break;
340
+ case OP_TYPES.CONTAINS:
341
+ match = isContains(itemVal, valueType, condition);
342
+ break;
343
+ case OP_TYPES.NOT_CONTAINS:
344
+ match = !isContains(itemVal, valueType, condition);
345
+ break;
346
+ case OP_TYPES.EQUAL:
347
+ match = isEquals(itemVal, valueType, condition);
348
+ break;
349
+ case OP_TYPES.NOT_EQUAL:
350
+ match = !isEquals(itemVal, valueType, condition);
351
+ break;
352
+ case OP_TYPES.GREATER:
353
+ match =
354
+ typeof itemVal === "number"
355
+ ? isGreater(itemVal, condition)
356
+ : false;
357
+ break;
358
+ case OP_TYPES.GREATER_EQUAL:
359
+ match =
360
+ typeof itemVal === "number"
361
+ ? isGreaterEqual(itemVal, condition)
362
+ : false;
363
+ break;
364
+ case OP_TYPES.LESS_EQUAL:
365
+ match =
366
+ typeof itemVal === "number"
367
+ ? isLessEqual(itemVal, condition)
368
+ : false;
369
+ break;
370
+ case OP_TYPES.LESS:
371
+ match =
372
+ typeof itemVal === "number" ? isLess(itemVal, condition) : false;
373
+ break;
374
+ default:
375
+ break;
376
+ }
377
+ if (!match)
378
+ return false;
379
+ }
380
+ return match;
381
+ });
382
+ });
383
+ return groups;
384
+ };
385
+ export const filterBoardTree = (board, filter) => {
386
+ if (!board.groups?.length || !board.headers?.length)
387
+ return board;
388
+ return {
389
+ ...board,
390
+ groups: filterItemsByConditions(board.groups, filter, board.headers),
391
+ };
392
+ };
@@ -0,0 +1,96 @@
1
+ export interface IBaseUser {
2
+ userId: string;
3
+ username: string;
4
+ email: string;
5
+ firstname: string;
6
+ lastname: string;
7
+ }
8
+ export interface ILinkedSource {
9
+ boardId: string;
10
+ boardName?: string;
11
+ columnId: string;
12
+ deleted?: number;
13
+ type: string;
14
+ name: string;
15
+ dropdowns?: {
16
+ key: string;
17
+ value: string;
18
+ }[];
19
+ setting?: IColumnSetting;
20
+ }
21
+ export interface IBaseItem {
22
+ itemId: string;
23
+ groupId?: string;
24
+ boardId?: string;
25
+ parentId?: string;
26
+ data?: Record<string, any>;
27
+ subs?: IBaseItem[];
28
+ index?: number;
29
+ deleted?: boolean;
30
+ }
31
+ export interface IColumnSetting {
32
+ currency?: string;
33
+ }
34
+ export interface IBaseCustomColumn {
35
+ columnId: string;
36
+ type: string;
37
+ name: string;
38
+ description?: string;
39
+ dropdowns?: {
40
+ key: string;
41
+ value: string;
42
+ }[];
43
+ linkedGroup?: string;
44
+ linked?: ILinkedSource;
45
+ index?: number;
46
+ deleted?: boolean;
47
+ }
48
+ export interface IBaseGroup {
49
+ groupId: string;
50
+ name: string;
51
+ description?: string;
52
+ items?: IBaseItem[];
53
+ deleted?: boolean;
54
+ }
55
+ export interface IBaseBoard {
56
+ name: string;
57
+ boardId: string;
58
+ type?: "board";
59
+ folderId?: string;
60
+ policyId?: string;
61
+ shareType?: number;
62
+ terminology?: string;
63
+ activeStatus?: number;
64
+ orgId?: string;
65
+ description?: string;
66
+ workspaceId?: string;
67
+ createdBy?: string;
68
+ updatedBy?: string;
69
+ createdAt?: string;
70
+ updatedAt?: string;
71
+ }
72
+ export interface IPureBoard extends IBaseBoard {
73
+ headers?: IBaseCustomColumn[];
74
+ groups?: IBaseGroup[];
75
+ people?: IBaseUser[];
76
+ }
77
+ export interface IFilterCondition {
78
+ conditionId?: string;
79
+ columnId: string;
80
+ op: string;
81
+ value: string[] | number[];
82
+ blank?: boolean;
83
+ }
84
+ export interface IFilter {
85
+ andOr: "and" | "or";
86
+ criteria?: IFilterCondition[];
87
+ }
88
+ export interface IDynamicView {
89
+ filter?: IFilter;
90
+ viewId?: string;
91
+ displayColumns?: string[];
92
+ editScope: "all" | "own" | "notAllow";
93
+ allowNewItem?: boolean;
94
+ allowComment?: boolean;
95
+ step: "itemsSetting" | "customizeView" | "itemDetail" | "customizeForm" | "shareSetting";
96
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export * from "./validator";
@@ -0,0 +1 @@
1
+ export * from "./validator";
@@ -0,0 +1,3 @@
1
+ export declare const isValidEmail: (email: string) => Promise<boolean>;
2
+ export declare const isValidUrl: (url: string) => Promise<boolean>;
3
+ export declare const isValidPhone: (phone: string) => Promise<boolean>;
@@ -0,0 +1,36 @@
1
+ import Schema from "async-validator";
2
+ const descriptor = {
3
+ email: {
4
+ type: "email",
5
+ // @ts-ignore
6
+ validator: Schema.validators.email,
7
+ },
8
+ url: {
9
+ type: "url",
10
+ // @ts-ignore
11
+ validator: Schema.validators.url,
12
+ },
13
+ phone: {
14
+ type: "string", //+1 (408) 888-8888
15
+ pattern: /^(\+?1)?\(?[2-9]\d{2}\)?\s?\d{3}-?\d{4}$/,
16
+ },
17
+ };
18
+ const validator = new Schema(descriptor);
19
+ export const isValidEmail = async (email) => {
20
+ await validator.validate({ email }).catch(err => {
21
+ throw err;
22
+ });
23
+ return true;
24
+ };
25
+ export const isValidUrl = async (url) => {
26
+ await validator.validate({ url }).catch(err => {
27
+ throw err;
28
+ });
29
+ return true;
30
+ };
31
+ export const isValidPhone = async (phone) => {
32
+ await validator.validate({ phone }).catch(err => {
33
+ throw err;
34
+ });
35
+ return true;
36
+ };
package/package.json CHANGED
@@ -1,26 +1,14 @@
1
1
  {
2
2
  "name": "@followupus/common",
3
- "version": "0.0.2",
3
+ "version": "0.2.0",
4
4
  "description": "followup common utils npm package with TypeScript and VSCode",
5
+ "main": "./dist/index.js",
5
6
  "module": "./dist/index.js",
6
7
  "types": "./dist/index.d.ts",
7
8
  "files": [
8
9
  "dist"
9
10
  ],
10
11
  "type": "module",
11
- "scripts": {
12
- "dev": "tsc --watch",
13
- "build": "tsc --project tsconfig.build.json",
14
- "clean": "rm -rf ./dist",
15
- "lint:staged": "lint-staged",
16
- "format": "prettier --write \"**/*.{ts,tsx}\"",
17
- "changeset": "changeset",
18
- "version": "changeset version",
19
- "release": "pnpm build && changeset publish --access public --registry=https://registry.npmjs.com/",
20
- "lint": "eslint ./src/ --fix",
21
- "test": "tsc && node --test build/test/**/*.spec.js",
22
- "typecheck": "tsc --noEmit"
23
- },
24
12
  "repository": {
25
13
  "type": "git",
26
14
  "url": "git+https://github.com/followup/common.git"
@@ -51,6 +39,7 @@
51
39
  "@changesets/cli": "^2.27.1",
52
40
  "@commitlint/cli": "^19.1.0",
53
41
  "@commitlint/config-conventional": "^19.1.0",
42
+ "@types/lodash": "^4.17.0",
54
43
  "@types/node": "^20.11.26",
55
44
  "@typescript-eslint/eslint-plugin": "^7.2.0",
56
45
  "@typescript-eslint/parser": "^7.2.0",
@@ -74,5 +63,23 @@
74
63
  "simple-git-hooks": {
75
64
  "pre-commit": "pnpm lint-staged",
76
65
  "commit-msg": "pnpm commitlint -e \"$@\""
66
+ },
67
+ "dependencies": {
68
+ "async-validator": "^4.2.5",
69
+ "dayjs": "^1.11.10",
70
+ "lodash": "^4.17.21"
71
+ },
72
+ "scripts": {
73
+ "dev": "tsc --watch",
74
+ "build": "tsc --project tsconfig.build.json",
75
+ "clean": "rm -rf ./dist",
76
+ "lint:staged": "lint-staged",
77
+ "format": "prettier --write \"**/*.{ts,tsx}\"",
78
+ "changeset": "changeset",
79
+ "version": "changeset version",
80
+ "release": "pnpm build && changeset publish --access public --registry=https://registry.npmjs.com/",
81
+ "lint": "eslint ./src/ --fix",
82
+ "test": "tsc && node --test build/test/**/*.spec.js",
83
+ "typecheck": "tsc --noEmit"
77
84
  }
78
- }
85
+ }