@cyberismo/data-handler 0.0.18 → 0.0.19

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.
Files changed (112) hide show
  1. package/dist/command-handler.d.ts +2 -0
  2. package/dist/command-handler.js +26 -2
  3. package/dist/command-handler.js.map +1 -1
  4. package/dist/command-manager.d.ts +2 -0
  5. package/dist/command-manager.js +3 -0
  6. package/dist/command-manager.js.map +1 -1
  7. package/dist/commands/create.d.ts +3 -1
  8. package/dist/commands/create.js +10 -1
  9. package/dist/commands/create.js.map +1 -1
  10. package/dist/commands/migrate.d.ts +33 -0
  11. package/dist/commands/migrate.js +66 -0
  12. package/dist/commands/migrate.js.map +1 -0
  13. package/dist/containers/project/card-cache.js +13 -1
  14. package/dist/containers/project/card-cache.js.map +1 -1
  15. package/dist/containers/project/project-paths.d.ts +1 -0
  16. package/dist/containers/project/project-paths.js +5 -2
  17. package/dist/containers/project/project-paths.js.map +1 -1
  18. package/dist/containers/project.d.ts +10 -0
  19. package/dist/containers/project.js +39 -2
  20. package/dist/containers/project.js.map +1 -1
  21. package/dist/containers/template.js +2 -0
  22. package/dist/containers/template.js.map +1 -1
  23. package/dist/interfaces/command-options.d.ts +6 -1
  24. package/dist/interfaces/project-interfaces.d.ts +4 -0
  25. package/dist/interfaces/project-interfaces.js.map +1 -1
  26. package/dist/migrations/index.d.ts +14 -0
  27. package/dist/migrations/index.js +14 -0
  28. package/dist/migrations/index.js.map +1 -0
  29. package/dist/migrations/migration-executor.d.ts +79 -0
  30. package/dist/migrations/migration-executor.js +312 -0
  31. package/dist/migrations/migration-executor.js.map +1 -0
  32. package/dist/migrations/migration-worker.d.ts +13 -0
  33. package/dist/migrations/migration-worker.js +156 -0
  34. package/dist/migrations/migration-worker.js.map +1 -0
  35. package/dist/migrations/worker-executor.d.ts +24 -0
  36. package/dist/migrations/worker-executor.js +157 -0
  37. package/dist/migrations/worker-executor.js.map +1 -0
  38. package/dist/project-settings.d.ts +2 -0
  39. package/dist/project-settings.js +7 -0
  40. package/dist/project-settings.js.map +1 -1
  41. package/dist/resources/calculation-resource.d.ts +9 -0
  42. package/dist/resources/calculation-resource.js +13 -2
  43. package/dist/resources/calculation-resource.js.map +1 -1
  44. package/dist/resources/card-type-resource.d.ts +7 -2
  45. package/dist/resources/card-type-resource.js +13 -13
  46. package/dist/resources/card-type-resource.js.map +1 -1
  47. package/dist/resources/field-type-resource.d.ts +5 -0
  48. package/dist/resources/field-type-resource.js +13 -7
  49. package/dist/resources/field-type-resource.js.map +1 -1
  50. package/dist/resources/file-resource.d.ts +13 -1
  51. package/dist/resources/file-resource.js +17 -8
  52. package/dist/resources/file-resource.js.map +1 -1
  53. package/dist/resources/graph-model-resource.d.ts +5 -0
  54. package/dist/resources/graph-model-resource.js +6 -0
  55. package/dist/resources/graph-model-resource.js.map +1 -1
  56. package/dist/resources/graph-view-resource.d.ts +5 -0
  57. package/dist/resources/graph-view-resource.js +6 -0
  58. package/dist/resources/graph-view-resource.js.map +1 -1
  59. package/dist/resources/link-type-resource.d.ts +6 -0
  60. package/dist/resources/link-type-resource.js +26 -0
  61. package/dist/resources/link-type-resource.js.map +1 -1
  62. package/dist/resources/report-resource.d.ts +6 -1
  63. package/dist/resources/report-resource.js +7 -1
  64. package/dist/resources/report-resource.js.map +1 -1
  65. package/dist/resources/resource-object.d.ts +22 -7
  66. package/dist/resources/resource-object.js +44 -15
  67. package/dist/resources/resource-object.js.map +1 -1
  68. package/dist/resources/template-resource.d.ts +5 -1
  69. package/dist/resources/template-resource.js +6 -1
  70. package/dist/resources/template-resource.js.map +1 -1
  71. package/dist/resources/workflow-resource.d.ts +6 -2
  72. package/dist/resources/workflow-resource.js +11 -6
  73. package/dist/resources/workflow-resource.js.map +1 -1
  74. package/dist/utils/card-utils.d.ts +1 -1
  75. package/dist/utils/common-utils.d.ts +8 -0
  76. package/dist/utils/common-utils.js +14 -0
  77. package/dist/utils/common-utils.js.map +1 -1
  78. package/dist/utils/file-utils.d.ts +15 -3
  79. package/dist/utils/file-utils.js +48 -9
  80. package/dist/utils/file-utils.js.map +1 -1
  81. package/dist/utils/json.js +2 -2
  82. package/dist/utils/json.js.map +1 -1
  83. package/package.json +5 -3
  84. package/src/command-handler.ts +38 -1
  85. package/src/command-manager.ts +3 -0
  86. package/src/commands/create.ts +11 -0
  87. package/src/commands/migrate.ts +88 -0
  88. package/src/containers/project/card-cache.ts +18 -1
  89. package/src/containers/project/project-paths.ts +6 -2
  90. package/src/containers/project.ts +66 -1
  91. package/src/containers/template.ts +5 -0
  92. package/src/interfaces/command-options.ts +8 -0
  93. package/src/interfaces/project-interfaces.ts +4 -0
  94. package/src/migrations/index.ts +20 -0
  95. package/src/migrations/migration-executor.ts +478 -0
  96. package/src/migrations/migration-worker.ts +190 -0
  97. package/src/migrations/worker-executor.ts +185 -0
  98. package/src/project-settings.ts +7 -0
  99. package/src/resources/calculation-resource.ts +13 -2
  100. package/src/resources/card-type-resource.ts +19 -14
  101. package/src/resources/field-type-resource.ts +18 -7
  102. package/src/resources/file-resource.ts +25 -8
  103. package/src/resources/graph-model-resource.ts +6 -0
  104. package/src/resources/graph-view-resource.ts +6 -0
  105. package/src/resources/link-type-resource.ts +34 -0
  106. package/src/resources/report-resource.ts +7 -1
  107. package/src/resources/resource-object.ts +57 -18
  108. package/src/resources/template-resource.ts +6 -1
  109. package/src/resources/workflow-resource.ts +17 -7
  110. package/src/utils/common-utils.ts +15 -0
  111. package/src/utils/file-utils.ts +56 -12
  112. package/src/utils/json.ts +2 -6
@@ -25,6 +25,11 @@ import type { ResourceName } from '../utils/resource-utils.js';
25
25
  * Link Type resource class.
26
26
  */
27
27
  export class LinkTypeResource extends FileResource<LinkType> {
28
+ /**
29
+ * Creates instance of LinkTypeResource
30
+ * @param project Project to use
31
+ * @param name Resource name
32
+ */
28
33
  constructor(project: Project, name: ResourceName) {
29
34
  super(project, name, 'linkTypes');
30
35
 
@@ -32,6 +37,33 @@ export class LinkTypeResource extends FileResource<LinkType> {
32
37
  this.contentSchema = super.contentSchemaContent(this.contentSchemaId);
33
38
  }
34
39
 
40
+ // Update card metadata links when link type is renamed
41
+ private async updateCardLinks(from: string, to: string) {
42
+ const cards = await this.collectCards(
43
+ from,
44
+ (card, linkTypeName) =>
45
+ card.metadata?.links?.some((link) => link.linkType === linkTypeName) ??
46
+ false,
47
+ );
48
+ if (cards.length === 0) {
49
+ return;
50
+ }
51
+
52
+ await Promise.all(
53
+ cards.map(async (card) => {
54
+ if (card.metadata?.links) {
55
+ card.metadata.links = card.metadata.links.map((link) => {
56
+ if (link.linkType === from) {
57
+ return { ...link, linkType: to };
58
+ }
59
+ return link;
60
+ });
61
+ await this.project.updateCardMetadata(card, card.metadata);
62
+ }
63
+ }),
64
+ );
65
+ }
66
+
35
67
  /**
36
68
  * When resource name changes.
37
69
  * @param existingName Current resource name.
@@ -52,6 +84,8 @@ export class LinkTypeResource extends FileResource<LinkType> {
52
84
  await Promise.all([
53
85
  super.updateHandleBars(existingName, this.content.name),
54
86
  super.updateCalculations(existingName, this.content.name),
87
+ super.updateCardContentReferences(existingName, this.content.name),
88
+ this.updateCardLinks(existingName, this.content.name),
55
89
  ]);
56
90
  // Finally, write updated content.
57
91
  await this.write();
@@ -42,6 +42,11 @@ export class ReportResource extends FolderResource<
42
42
  ReportMetadata,
43
43
  ReportContent
44
44
  > {
45
+ /**
46
+ * Creates instance of ReportResource
47
+ * @param project Project to use
48
+ * @param name Resource name
49
+ */
45
50
  constructor(project: Project, name: ResourceName) {
46
51
  super(project, name, 'reports');
47
52
 
@@ -68,6 +73,7 @@ export class ReportResource extends FolderResource<
68
73
  await this.handleBarFiles(),
69
74
  ),
70
75
  super.updateCalculations(existingName, this.content.name),
76
+ super.updateCardContentReferences(existingName, this.content.name),
71
77
  ]);
72
78
  // Finally, write updated content.
73
79
  await this.write();
@@ -152,9 +158,9 @@ export class ReportResource extends FolderResource<
152
158
 
153
159
  /**
154
160
  * Validates report.
155
- * @throws when there are validation errors.
156
161
  * @param content Content to be validated.
157
162
  * @note If content is not provided, base class validation will use resource's current content.
163
+ * @throws when there are validation errors.
158
164
  */
159
165
  public async validate(content?: object) {
160
166
  const resourceContent = this.contentData();
@@ -416,7 +416,13 @@ export abstract class ResourceObject<
416
416
  }
417
417
  }
418
418
 
419
- // Log details
419
+ /**
420
+ * Log to migration log resource change
421
+ * @param operationType Operation type
422
+ * @param op Details of operation
423
+ * @param key Which property has been changed
424
+ * @throws when operation type is unknown
425
+ */
420
426
  protected async logResourceOperation<Type>(
421
427
  operationType: 'create' | 'delete' | 'update' | 'rename',
422
428
  op?: Operation<Type>,
@@ -596,27 +602,11 @@ export abstract class ResourceObject<
596
602
  }
597
603
  }
598
604
 
599
- /**
600
- * Validates resource identifier to prevent filesystem operations with invalid names
601
- * todo: To Validate?
602
- */
603
- protected validateResourceIdentifier() {
604
- if (!this.moduleResource && this.resourceName.identifier) {
605
- const identifier = this.resourceName.identifier;
606
- if (!/^[a-zA-Z0-9._-]+$/.test(identifier)) {
607
- throw new Error(
608
- `Resource identifier must follow naming rules. Identifier '${identifier}' is invalid`,
609
- );
610
- }
611
- }
612
- }
613
-
614
605
  /**
615
606
  * Update calculation files.
616
607
  * @param from Resource name to update
617
608
  * @param to New name for resource
618
- * @throws if 'from' or 'to' is empty string, or
619
- * if there was error accessing calculation files.
609
+ * @throws if 'from' or 'to' is empty string
620
610
  */
621
611
  protected async updateCalculations(from: string, to: string) {
622
612
  if (!from.trim() || !to.trim()) {
@@ -674,6 +664,39 @@ export abstract class ResourceObject<
674
664
  );
675
665
  }
676
666
 
667
+ /**
668
+ * Update references in card content.
669
+ * Searches through all card content in the cache and replaces references to the old resource name.
670
+ * @param from Resource name to update
671
+ * @param to New name for resource
672
+ * @throws if 'from' or 'to' is empty string
673
+ */
674
+ protected async updateCardContentReferences(from: string, to: string) {
675
+ if (!from.trim() || !to.trim()) {
676
+ throw new Error(
677
+ 'updateCardContentReferences: "from" and "to" parameters must not be empty',
678
+ );
679
+ }
680
+
681
+ const allCards = this.cards();
682
+ const cardsToUpdate = allCards.filter(
683
+ (card) => card.content && card.content.includes(from),
684
+ );
685
+
686
+ if (cardsToUpdate.length === 0) {
687
+ return;
688
+ }
689
+
690
+ await Promise.all(
691
+ cardsToUpdate.map(async (card) => {
692
+ if (card.content) {
693
+ const updatedContent = card.content.replaceAll(from, to);
694
+ await this.project.updateCardContent(card.key, updatedContent);
695
+ }
696
+ }),
697
+ );
698
+ }
699
+
677
700
  /**
678
701
  * Check if there are references to the resource in the card content.
679
702
  * @note that this needs to be async, since inherited classes need to async operations
@@ -695,6 +718,22 @@ export abstract class ResourceObject<
695
718
  .map((card) => card.key);
696
719
  }
697
720
 
721
+ /**
722
+ * Validates resource identifier to prevent filesystem operations with invalid names
723
+ * todo: move to Validate?
724
+ * @throws if identifier is incorrect
725
+ */
726
+ protected validateResourceIdentifier() {
727
+ if (!this.moduleResource && this.resourceName.identifier) {
728
+ const identifier = this.resourceName.identifier;
729
+ if (!/^[a-zA-Z0-9._-]+$/.test(identifier)) {
730
+ throw new Error(
731
+ `Resource identifier must follow naming rules. Identifier '${identifier}' is invalid`,
732
+ );
733
+ }
734
+ }
735
+ }
736
+
698
737
  /**
699
738
  * Checks if resource name is valid.
700
739
  * @param newName New name for resource.
@@ -39,6 +39,11 @@ export class TemplateResource extends FolderResource<TemplateMetadata, never> {
39
39
  private cardsFolder = '';
40
40
  private cardsSchema = super.contentSchemaContent('cardBaseSchema');
41
41
 
42
+ /**
43
+ * Creates an instance of TemplateResource
44
+ * @param project Project to use
45
+ * @param name Resource name
46
+ */
42
47
  constructor(project: Project, name: ResourceName) {
43
48
  super(project, name, 'templates');
44
49
 
@@ -63,6 +68,7 @@ export class TemplateResource extends FolderResource<TemplateMetadata, never> {
63
68
  await Promise.all([
64
69
  super.updateHandleBars(existingName, this.content.name),
65
70
  super.updateCalculations(existingName, this.content.name),
71
+ super.updateCardContentReferences(existingName, this.content.name),
66
72
  ]);
67
73
  await this.write();
68
74
  }
@@ -70,7 +76,6 @@ export class TemplateResource extends FolderResource<TemplateMetadata, never> {
70
76
  /**
71
77
  * Sets new metadata into the template object.
72
78
  * @param newContent metadata content for the template.
73
- * @throws if 'newContent' is not valid.
74
79
  */
75
80
  public async create(newContent?: TemplateMetadata) {
76
81
  if (!newContent) {
@@ -36,6 +36,11 @@ import type { ResourceName } from '../utils/resource-utils.js';
36
36
  * Workflow resource class.
37
37
  */
38
38
  export class WorkflowResource extends FileResource<Workflow> {
39
+ /**
40
+ * Creates an instance of WorkflowResource
41
+ * @param project Project to use
42
+ * @param name Resource name
43
+ */
39
44
  constructor(project: Project, name: ResourceName) {
40
45
  super(project, name, 'workflows');
41
46
 
@@ -49,7 +54,12 @@ export class WorkflowResource extends FileResource<Workflow> {
49
54
  const promises: Promise<Card[]>[] = [];
50
55
  for (const cardType of cardTypes) {
51
56
  if (cardType.data?.workflow === resourceNameToString(this.resourceName)) {
52
- promises.push(this.collectCards(cardType.data.name));
57
+ promises.push(
58
+ this.collectCards(
59
+ cardType.data.name,
60
+ (card, cardTypeName) => card.metadata?.cardType === cardTypeName,
61
+ ),
62
+ );
53
63
  }
54
64
  }
55
65
  return (await Promise.all(promises)).flat();
@@ -60,6 +70,7 @@ export class WorkflowResource extends FileResource<Workflow> {
60
70
  await Promise.all([
61
71
  super.updateHandleBars(existingName, this.content.name),
62
72
  super.updateCalculations(existingName, this.content.name),
73
+ super.updateCardContentReferences(existingName, this.content.name),
63
74
  this.updateCardTypes(existingName),
64
75
  ]);
65
76
  // Finally, write updated content.
@@ -132,6 +143,11 @@ export class WorkflowResource extends FileResource<Workflow> {
132
143
  }
133
144
  }
134
145
 
146
+ // Check if operation is a string operation.
147
+ private isStringOperation(op: Operation<unknown>): op is Operation<string> {
148
+ return typeof op.target === 'string';
149
+ }
150
+
135
151
  // Returns target name irregardless of the type
136
152
  private targetName(op: Operation<WorkflowState | WorkflowTransition>) {
137
153
  const name = op.target.name ? op.target.name : op.target;
@@ -168,11 +184,6 @@ export class WorkflowResource extends FileResource<Workflow> {
168
184
  return op.to;
169
185
  }
170
186
 
171
- // Check if operation is a string operation.
172
- private isStringOperation(op: Operation<unknown>): op is Operation<string> {
173
- return typeof op.target === 'string';
174
- }
175
-
176
187
  // Update card states when state is changed
177
188
  private async updateCardStates(oldState: string, newState: string) {
178
189
  const cards = await this.collectCardsUsingWorkflow();
@@ -209,7 +220,6 @@ export class WorkflowResource extends FileResource<Workflow> {
209
220
  /**
210
221
  * Sets new metadata into the workflow object.
211
222
  * @param newContent metadata content for the workflow.
212
- * @throws if 'newContent' is not valid.
213
223
  */
214
224
  public async create(newContent?: Workflow) {
215
225
  if (!newContent) {
@@ -52,3 +52,18 @@ export function isBigInt(value: string): boolean {
52
52
  return false;
53
53
  }
54
54
  }
55
+
56
+ /**
57
+ * Removes the first occurrence of a value from an array.
58
+ * Modifies the array in place.
59
+ * @param array The array to modify
60
+ * @param value The value to remove
61
+ * @returns The modified array (same reference as input)
62
+ */
63
+ export function removeValue<T>(array: T[], value: T): T[] {
64
+ const index = array.findIndex((element) => element === value);
65
+ if (index !== -1) {
66
+ array.splice(index, 1);
67
+ }
68
+ return array;
69
+ }
@@ -17,6 +17,8 @@ import {
17
17
  mkdir,
18
18
  readdir,
19
19
  rm,
20
+ stat,
21
+ statfs,
20
22
  unlink,
21
23
  writeFile,
22
24
  } from 'node:fs/promises';
@@ -25,19 +27,17 @@ import { dirname, join, sep } from 'node:path';
25
27
  import { homedir } from 'node:os';
26
28
 
27
29
  /**
28
- * Works like the writeFile method, but ensures that the directory exists
29
- * There is only one difference: This method only supports a string as the filePath
30
+ * Get available disk space for a given path.
31
+ * @param path Path to check
32
+ * @returns Available space in bytes
30
33
  */
31
- export async function writeFileSafe(
32
- filePath: string,
33
- data: Parameters<typeof writeFile>[1],
34
- options?: Parameters<typeof writeFile>[2],
35
- ) {
36
- const dir = dirname(filePath);
37
- await mkdir(dir, {
38
- recursive: true,
39
- });
40
- return writeFile(filePath, data, options);
34
+ export async function availableSpace(path: string): Promise<number> {
35
+ try {
36
+ const stats = await statfs(path);
37
+ return stats.bavail * stats.bsize;
38
+ } catch (error) {
39
+ throw new Error(`Failed to check available disk space: ${error}`);
40
+ }
41
41
  }
42
42
 
43
43
  /**
@@ -87,6 +87,34 @@ export async function deleteFile(path: string): Promise<boolean> {
87
87
  return true;
88
88
  }
89
89
 
90
+ /**
91
+ * Calculate the total size of a directory recursively.
92
+ * @param dirPath Path to directory
93
+ * @returns Size in bytes
94
+ */
95
+ export async function folderSize(dirPath: string): Promise<number> {
96
+ let size = 0;
97
+
98
+ try {
99
+ const entries = await readdir(dirPath, { withFileTypes: true });
100
+
101
+ for (const entry of entries) {
102
+ const fullPath = join(dirPath, entry.name);
103
+
104
+ if (entry.isDirectory()) {
105
+ size += await folderSize(fullPath);
106
+ } else if (entry.isFile()) {
107
+ const stats = await stat(fullPath);
108
+ size += stats.size;
109
+ }
110
+ }
111
+ } catch {
112
+ // Ignore permission errors or missing directories
113
+ }
114
+
115
+ return size;
116
+ }
117
+
90
118
  /**
91
119
  * Removes extension from filename.
92
120
  * @param filename Filename
@@ -180,3 +208,19 @@ export function resolveTilde(filePath: string): string {
180
208
  * Path separator RE.
181
209
  */
182
210
  export const sepRegex = /[/\\]/;
211
+
212
+ /**
213
+ * Works like the writeFile method, but ensures that the directory exists
214
+ * There is only one difference: This method only supports a string as the filePath
215
+ */
216
+ export async function writeFileSafe(
217
+ filePath: string,
218
+ data: Parameters<typeof writeFile>[1],
219
+ options?: Parameters<typeof writeFile>[2],
220
+ ) {
221
+ const dir = dirname(filePath);
222
+ await mkdir(dir, {
223
+ recursive: true,
224
+ });
225
+ return writeFile(filePath, data, options);
226
+ }
package/src/utils/json.ts CHANGED
@@ -28,9 +28,7 @@ export function readJsonFileSync(file: string) {
28
28
  return returnValue;
29
29
  } catch (error) {
30
30
  if (error instanceof Error) {
31
- throw new Error(
32
- `Error while handling JSON file '${file}' : ${error.message}`,
33
- );
31
+ throw new Error(`Invalid JSON in file '${file}': ${error.message}`);
34
32
  }
35
33
  }
36
34
  }
@@ -47,9 +45,7 @@ export async function readJsonFile(file: string) {
47
45
  return JSON.parse(raw);
48
46
  } catch (error) {
49
47
  if (error instanceof Error) {
50
- throw new Error(
51
- `Error while handling JSON file '${file}' : ${error.message}`,
52
- );
48
+ throw new Error(`Invalid JSON in file '${file}': ${error.message}`);
53
49
  }
54
50
  }
55
51
  }