@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
@@ -0,0 +1,190 @@
1
+ /**
2
+ Cyberismo
3
+ Copyright © Cyberismo Ltd and contributors 2025
4
+ This program is free software: you can redistribute it and/or modify it under
5
+ the terms of the GNU Affero General Public License version 3 as published by
6
+ the Free Software Foundation.
7
+ This program is distributed in the hope that it will be useful, but WITHOUT
8
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
9
+ FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
10
+ details. You should have received a copy of the GNU Affero General Public
11
+ License along with this program. If not, see <https://www.gnu.org/licenses/>.
12
+ */
13
+
14
+ import { parentPort } from 'node:worker_threads';
15
+ import { pathToFileURL } from 'node:url';
16
+
17
+ import type {
18
+ Migration,
19
+ MigrationContext,
20
+ MigrationStepResult,
21
+ } from '@cyberismo/migrations';
22
+ import type { WorkerMessage, WorkerResponse } from './migration-executor.js';
23
+
24
+ let currentMigration: Migration | undefined;
25
+ let abortController: AbortController | undefined;
26
+
27
+ /**
28
+ * Execute a specific step of a migration.
29
+ *
30
+ * Runs one of the migration steps (before, backup, migrate, after).
31
+ * Checks for cancellation before and after execution.
32
+ *
33
+ * @param migration The loaded migration object
34
+ * @param stepName Which step to execute ('before', 'backup', 'migrate', or 'after')
35
+ * @param context Migration context with paths and version information
36
+ * @returns Migration step result
37
+ */
38
+ async function executeStep(
39
+ migration: Migration,
40
+ stepName: string,
41
+ context: MigrationContext,
42
+ ): Promise<MigrationStepResult> {
43
+ if (abortController?.signal.aborted) {
44
+ return {
45
+ success: false,
46
+ error: new Error('Migration cancelled'),
47
+ };
48
+ }
49
+
50
+ try {
51
+ let result: MigrationStepResult;
52
+
53
+ switch (stepName) {
54
+ case 'before':
55
+ if (!migration.before) {
56
+ return { success: true, message: `No 'before' step` };
57
+ }
58
+ result = await migration.before(context);
59
+ break;
60
+
61
+ case 'backup':
62
+ if (!migration.backup) {
63
+ return { success: true, message: `No 'backup' step` };
64
+ }
65
+ result = await migration.backup(context);
66
+ break;
67
+
68
+ case 'migrate':
69
+ result = await migration.migrate(context);
70
+ break;
71
+
72
+ case 'after':
73
+ if (!migration.after) {
74
+ return { success: true, message: `No 'after' step` };
75
+ }
76
+ result = await migration.after(context);
77
+ break;
78
+
79
+ default:
80
+ return {
81
+ success: false,
82
+ error: new Error(`Unknown migration step: ${stepName}`),
83
+ };
84
+ }
85
+
86
+ if (abortController?.signal.aborted) {
87
+ return {
88
+ success: false,
89
+ error: new Error('Migration cancelled'),
90
+ };
91
+ }
92
+
93
+ return result;
94
+ } catch (error) {
95
+ return {
96
+ success: false,
97
+ error: error instanceof Error ? error : new Error(String(error)),
98
+ };
99
+ }
100
+ }
101
+
102
+ /**
103
+ * Handle a cancellation request from the main thread.
104
+ */
105
+ async function handleCancel(): Promise<void> {
106
+ abortController?.abort();
107
+
108
+ if (currentMigration?.cancel) {
109
+ try {
110
+ await currentMigration.cancel();
111
+ } catch {
112
+ // Ignore errors during cancellation - we're cancelling anyway
113
+ }
114
+ }
115
+ }
116
+
117
+ /**
118
+ * Worker thread message handler.
119
+ *
120
+ * Listens for messages from the main thread:
121
+ * - 'execute': Run a migration step
122
+ * - 'cancel': Cancel the currently running migration
123
+ *
124
+ * Responds with:
125
+ * - 'result': The migration step result
126
+ * - 'error': An error message when execution failed
127
+ */
128
+ if (parentPort) {
129
+ parentPort.on('message', (message: WorkerMessage) => {
130
+ if (!parentPort) return;
131
+
132
+ void (async () => {
133
+ try {
134
+ if (message.type === 'cancel') {
135
+ await handleCancel();
136
+ const response: WorkerResponse = {
137
+ type: 'result',
138
+ result: {
139
+ success: false,
140
+ error: new Error('Migration cancelled'),
141
+ },
142
+ };
143
+ parentPort.postMessage(response);
144
+ return;
145
+ }
146
+
147
+ if (message.type === 'execute') {
148
+ if (!message.migrationPath || !message.stepName || !message.context) {
149
+ throw new Error('Missing required parameters for "execute"');
150
+ }
151
+
152
+ abortController = new AbortController();
153
+
154
+ const migrationUrl = pathToFileURL(message.migrationPath).href;
155
+ const migrationModule = await import(migrationUrl);
156
+ currentMigration = migrationModule.default || migrationModule;
157
+
158
+ if (typeof currentMigration?.migrate !== 'function') {
159
+ throw new Error(
160
+ `Migration does not implement migrate() function: ${message.migrationPath}`,
161
+ );
162
+ }
163
+
164
+ const contextWithSignal: MigrationContext = {
165
+ ...message.context,
166
+ signal: abortController.signal,
167
+ };
168
+
169
+ const result = await executeStep(
170
+ currentMigration,
171
+ message.stepName,
172
+ contextWithSignal,
173
+ );
174
+
175
+ const response: WorkerResponse = {
176
+ type: 'result',
177
+ result,
178
+ };
179
+ parentPort.postMessage(response);
180
+ }
181
+ } catch (error) {
182
+ const response: WorkerResponse = {
183
+ type: 'error',
184
+ error: error instanceof Error ? error.message : String(error),
185
+ };
186
+ parentPort.postMessage(response);
187
+ }
188
+ })();
189
+ });
190
+ }
@@ -0,0 +1,185 @@
1
+ /**
2
+ Cyberismo
3
+ Copyright © Cyberismo Ltd and contributors 2025
4
+ This program is free software: you can redistribute it and/or modify it under
5
+ the terms of the GNU Affero General Public License version 3 as published by
6
+ the Free Software Foundation.
7
+ This program is distributed in the hope that it will be useful, but WITHOUT
8
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
9
+ FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
10
+ details. You should have received a copy of the GNU Affero General Public
11
+ License along with this program. If not, see <https://www.gnu.org/licenses/>.
12
+ */
13
+
14
+ import { dirname, join } from 'node:path';
15
+ import { fileURLToPath } from 'node:url';
16
+ import { Worker } from 'node:worker_threads';
17
+
18
+ import { getChildLogger } from '../utils/log-utils.js';
19
+ import type {
20
+ MigrationContext,
21
+ MigrationStepResult,
22
+ } from '@cyberismo/migrations';
23
+ import type { WorkerMessage, WorkerResponse } from './migration-executor.js';
24
+
25
+ const CANCEL_PERIOD_MS = 100;
26
+ const logger = getChildLogger({ module: 'WorkerExecutor' });
27
+
28
+ /**
29
+ * Execute a migration step in a separate worker thread.
30
+ * The worker loads the migration program dynamically and executes the specified step.
31
+ *
32
+ * @param migrationPath Absolute path to the migration's 'index.js' file
33
+ * @param stepName The migration step to execute
34
+ * @param context Migration context
35
+ * @param timeoutMilliSeconds Timeout in milliseconds to wait for the step to complete
36
+ * @returns Migration step result
37
+ */
38
+ export async function executeStep(
39
+ migrationPath: string,
40
+ stepName: string,
41
+ context: MigrationContext,
42
+ timeoutMilliSeconds: number,
43
+ ): Promise<MigrationStepResult> {
44
+ // Always uses the compiled .js version from the dist directory.
45
+ function _workerPath() {
46
+ const currentFilePath = fileURLToPath(import.meta.url);
47
+ const currentDir = dirname(currentFilePath);
48
+ const srcMigrationsSegment = join('src', 'migrations');
49
+ const distMigrationsSegment = join('dist', 'migrations');
50
+ const distDir = currentDir.replace(
51
+ srcMigrationsSegment,
52
+ distMigrationsSegment,
53
+ );
54
+ return join(distDir, 'migration-worker.js');
55
+ }
56
+
57
+ return new Promise((resolve, reject) => {
58
+ const worker = new Worker(_workerPath(), {
59
+ execArgv: process.execArgv,
60
+ });
61
+ let timeoutId: NodeJS.Timeout | undefined;
62
+ let isResolved = false;
63
+
64
+ const cleanup = () => {
65
+ if (timeoutId) {
66
+ clearTimeout(timeoutId);
67
+ timeoutId = undefined;
68
+ }
69
+ };
70
+
71
+ const terminate = async (sendCancel: boolean = false): Promise<void> => {
72
+ if (sendCancel) {
73
+ try {
74
+ const cancelMessage: WorkerMessage = { type: 'cancel' };
75
+ worker.postMessage(cancelMessage);
76
+ await new Promise((_resolve) =>
77
+ setTimeout(_resolve, CANCEL_PERIOD_MS),
78
+ );
79
+ } catch {
80
+ // Ignore errors when sending cancel message
81
+ }
82
+ }
83
+
84
+ try {
85
+ await worker.terminate();
86
+ } catch (error) {
87
+ logger.debug({ error }, 'Error terminating worker');
88
+ }
89
+ };
90
+
91
+ timeoutId = setTimeout(() => {
92
+ if (!isResolved) {
93
+ isResolved = true;
94
+ cleanup();
95
+
96
+ void (async () => {
97
+ await terminate(true);
98
+ resolve({
99
+ success: false,
100
+ error: new Error(`Migration step '${stepName}' timeout`),
101
+ });
102
+ })();
103
+ }
104
+ }, timeoutMilliSeconds);
105
+
106
+ worker.on('message', (response: WorkerResponse) => {
107
+ if (isResolved) return;
108
+
109
+ isResolved = true;
110
+ cleanup();
111
+
112
+ void (async () => {
113
+ if (response.type === 'error') {
114
+ await terminate(false);
115
+ resolve({
116
+ success: false,
117
+ error: new Error(response.error || 'Unknown worker error'),
118
+ });
119
+ } else if (response.type === 'result' && response.result) {
120
+ await terminate(false);
121
+ resolve(response.result);
122
+ } else {
123
+ await terminate(false);
124
+ resolve({
125
+ success: false,
126
+ error: new Error('Invalid worker response'),
127
+ });
128
+ }
129
+ })();
130
+ });
131
+
132
+ worker.on('error', (error) => {
133
+ if (isResolved) return;
134
+
135
+ isResolved = true;
136
+ cleanup();
137
+
138
+ void (async () => {
139
+ await terminate(false);
140
+ resolve({
141
+ success: false,
142
+ error,
143
+ });
144
+ })();
145
+ });
146
+
147
+ worker.on('exit', (code) => {
148
+ if (isResolved) return;
149
+
150
+ isResolved = true;
151
+ cleanup();
152
+
153
+ if (code !== 0) {
154
+ resolve({
155
+ success: false,
156
+ error: new Error(`Worker exited with code ${code}`),
157
+ });
158
+ }
159
+ });
160
+
161
+ const migrationContext: MigrationContext = {
162
+ cardRootPath: context.cardRootPath,
163
+ cardsConfigPath: context.cardsConfigPath,
164
+ fromVersion: context.fromVersion,
165
+ toVersion: context.toVersion,
166
+ backupDir: context.backupDir,
167
+ };
168
+
169
+ const executeMessage: WorkerMessage = {
170
+ type: 'execute',
171
+ migrationPath,
172
+ stepName,
173
+ context: migrationContext,
174
+ };
175
+
176
+ try {
177
+ worker.postMessage(executeMessage);
178
+ } catch (error) {
179
+ isResolved = true;
180
+ cleanup();
181
+ void terminate(false);
182
+ reject(error);
183
+ }
184
+ });
185
+ }
@@ -35,6 +35,8 @@ export class ProjectConfiguration implements ProjectSettings {
35
35
  schemaVersion?: number;
36
36
  name: string;
37
37
  cardKeyPrefix: string;
38
+ category?: string;
39
+ description: string;
38
40
  modules: ModuleSetting[];
39
41
  hubs: HubSetting[];
40
42
  private logger = getChildLogger({ module: 'Project' });
@@ -45,6 +47,7 @@ export class ProjectConfiguration implements ProjectSettings {
45
47
  this.name = '';
46
48
  this.settingPath = path;
47
49
  this.cardKeyPrefix = '';
50
+ this.description = '';
48
51
  this.modules = [];
49
52
  this.hubs = [];
50
53
  this.autoSave = autoSave;
@@ -79,6 +82,8 @@ export class ProjectConfiguration implements ProjectSettings {
79
82
  this.schemaVersion = settings.schemaVersion;
80
83
  this.cardKeyPrefix = settings.cardKeyPrefix;
81
84
  this.name = settings.name;
85
+ this.category = settings.category;
86
+ this.description = settings.description || '';
82
87
  this.modules = settings.modules || [];
83
88
  this.hubs = settings.hubs || [];
84
89
  } else {
@@ -106,6 +111,8 @@ export class ProjectConfiguration implements ProjectSettings {
106
111
  schemaVersion: this.schemaVersion,
107
112
  cardKeyPrefix: this.cardKeyPrefix,
108
113
  name: this.name,
114
+ category: this.category,
115
+ description: this.description,
109
116
  modules: this.modules,
110
117
  hubs: this.hubs,
111
118
  };
@@ -34,6 +34,11 @@ export class CalculationResource extends FolderResource<
34
34
  CalculationMetadata,
35
35
  CalculationContent
36
36
  > {
37
+ /**
38
+ * Creates instance of CalculationResource
39
+ * @param project Project to use
40
+ * @param name Resource name
41
+ */
37
42
  constructor(project: Project, name: ResourceName) {
38
43
  super(project, name, 'calculations');
39
44
 
@@ -41,9 +46,15 @@ export class CalculationResource extends FolderResource<
41
46
  this.contentSchema = super.contentSchemaContent(this.contentSchemaId);
42
47
  }
43
48
 
44
- // When resource name changes
49
+ /**
50
+ * When resource name changes
51
+ * @param existingName Current resource name
52
+ */
45
53
  protected async onNameChange(existingName: string) {
46
- await super.updateCalculations(existingName, this.content.name);
54
+ await Promise.all([
55
+ super.updateCalculations(existingName, this.content.name),
56
+ super.updateCardContentReferences(existingName, this.content.name),
57
+ ]);
47
58
  await this.write();
48
59
  }
49
60
 
@@ -17,6 +17,7 @@ import { FileResource } from './file-resource.js';
17
17
  import { resourceName, resourceNameToString } from '../utils/resource-utils.js';
18
18
  import { ResourcesFrom } from '../containers/project.js';
19
19
  import { sortCards } from '../utils/card-utils.js';
20
+ import { removeValue } from '../utils/common-utils.js';
20
21
  import { Validate } from '../commands/validate.js';
21
22
 
22
23
  import type {
@@ -39,6 +40,11 @@ import type { ResourceName } from '../utils/resource-utils.js';
39
40
  * Card type resource class.
40
41
  */
41
42
  export class CardTypeResource extends FileResource<CardType> {
43
+ /**
44
+ * Creates instance of CardTypeResource
45
+ * @param project Project to use
46
+ * @param name Resource name
47
+ */
42
48
  constructor(project: Project, name: ResourceName) {
43
49
  super(project, name, 'cardTypes');
44
50
 
@@ -69,7 +75,10 @@ export class CardTypeResource extends FileResource<CardType> {
69
75
  if (op && op.name === 'rank') return;
70
76
 
71
77
  // Collect both project cards and template cards.
72
- const cards = await this.collectCards(this.content.name);
78
+ const cards = await this.collectCards(
79
+ this.content.name,
80
+ (card, cardTypeName) => card.metadata?.cardType === cardTypeName,
81
+ );
73
82
 
74
83
  if (op && op.name === 'change') {
75
84
  const from = (op as ChangeOperation<string>).target;
@@ -119,7 +128,10 @@ export class CardTypeResource extends FileResource<CardType> {
119
128
  op: ChangeOperation<Type>,
120
129
  ) {
121
130
  await this.verifyStateMapping(stateMapping, op);
122
- const cards = await this.collectCards(this.content.name);
131
+ const cards = await this.collectCards(
132
+ this.content.name,
133
+ (card, cardTypeName) => card.metadata?.cardType === cardTypeName,
134
+ );
123
135
 
124
136
  const unmappedStates: string[] = [];
125
137
 
@@ -179,15 +191,6 @@ export class CardTypeResource extends FileResource<CardType> {
179
191
  return references;
180
192
  }
181
193
 
182
- // Remove value from array.
183
- // todo: make it as generic and move to utils
184
- private removeValue(array: string[], value: string) {
185
- const index = array.findIndex((element) => element === value);
186
- if (index !== -1) {
187
- array.splice(index, 1);
188
- }
189
- }
190
-
191
194
  // If value from 'customFields' is removed, remove it also from 'optionallyVisible' and 'alwaysVisible' arrays.
192
195
  private removeValueFromOtherArrays<Type>(
193
196
  op: Operation<Type>,
@@ -201,8 +204,8 @@ export class CardTypeResource extends FileResource<CardType> {
201
204
  field = { name: target['name' as keyof Type] };
202
205
  }
203
206
  const fieldName = (field ? field.name : target) as string;
204
- this.removeValue(content.alwaysVisibleFields, fieldName);
205
- this.removeValue(content.optionallyVisibleFields, fieldName);
207
+ removeValue(content.alwaysVisibleFields, fieldName);
208
+ removeValue(content.optionallyVisibleFields, fieldName);
206
209
  }
207
210
 
208
211
  // Sets content container values to be either '[]' or with proper values.
@@ -385,6 +388,7 @@ export class CardTypeResource extends FileResource<CardType> {
385
388
  await Promise.all([
386
389
  super.updateHandleBars(existingName, this.content.name),
387
390
  super.updateCalculations(existingName, this.content.name),
391
+ super.updateCardContentReferences(existingName, this.content.name),
388
392
  this.updateLinkTypes(existingName),
389
393
  ]);
390
394
 
@@ -394,7 +398,8 @@ export class CardTypeResource extends FileResource<CardType> {
394
398
  /**
395
399
  * Creates a new card type object. Base class writes the object to disk automatically.
396
400
  * @param workflowName Workflow name that this card type uses.
397
- * @throws when workflow is empty, or does not exist in the project.
401
+ * @throws when workflow is empty, or
402
+ * when workflow does not exist in the project.
398
403
  */
399
404
  public async createCardType(workflowName: string) {
400
405
  if (!workflowName) {
@@ -50,6 +50,11 @@ export class FieldTypeResource extends FileResource<FieldType> {
50
50
  private fromType: DataType = 'integer';
51
51
  private toType: DataType = 'integer';
52
52
 
53
+ /**
54
+ * Creates an instance of FieldTypeResource
55
+ * @param project Project to use
56
+ * @param name Resource name
57
+ */
53
58
  constructor(project: Project, name: ResourceName) {
54
59
  super(project, name, 'fieldTypes');
55
60
 
@@ -206,7 +211,12 @@ export class FieldTypeResource extends FileResource<FieldType> {
206
211
  const removedValue = (op.target as EnumDefinition).enumValue;
207
212
  const cardTypes = this.relevantCardTypes();
208
213
  const allCards = await Promise.all(
209
- cardTypes.map((cardType) => this.collectCards(cardType)),
214
+ cardTypes.map((cardType) =>
215
+ this.collectCards(
216
+ cardType,
217
+ (card, cardTypeName) => card.metadata?.cardType === cardTypeName,
218
+ ),
219
+ ),
210
220
  );
211
221
  const cardsToUpdate = allCards
212
222
  .flat()
@@ -283,6 +293,7 @@ export class FieldTypeResource extends FileResource<FieldType> {
283
293
  await Promise.all([
284
294
  super.updateHandleBars(existingName, this.content.name),
285
295
  super.updateCalculations(existingName, this.content.name),
296
+ super.updateCardContentReferences(existingName, this.content.name),
286
297
  this.updateCardTypes(existingName),
287
298
  ]);
288
299
  await this.write();
@@ -314,16 +325,16 @@ export class FieldTypeResource extends FileResource<FieldType> {
314
325
  */
315
326
  public static fieldDataTypes(): DataType[] {
316
327
  return [
317
- 'shortText',
318
- 'longText',
319
- 'number',
320
- 'integer',
321
328
  'boolean',
322
- 'enum',
323
- 'list',
324
329
  'date',
325
330
  'dateTime',
331
+ 'enum',
332
+ 'integer',
333
+ 'list',
334
+ 'longText',
335
+ 'number',
326
336
  'person',
337
+ 'shortText',
327
338
  ];
328
339
  }
329
340
 
@@ -35,23 +35,35 @@ export abstract class FileResource<
35
35
  super(project, name, type);
36
36
  this.initialize();
37
37
  }
38
- // Collects cards that are using the 'cardTypeName'.
39
- protected async collectCards(cardTypeName: string) {
40
- function filteredCards(cardSource: Card[], cardTypeName: string): Card[] {
41
- const cards = cardSource;
42
- return cards.filter((card) => card.metadata?.cardType === cardTypeName);
38
+ /**
39
+ * Collects cards that match the given filter function.
40
+ * @param resourceName The resource name to filter by
41
+ * @param filterFn Function that returns true for cards to include
42
+ * @returns Array of cards that match the filter
43
+ */
44
+ protected async collectCards(
45
+ resourceName: string,
46
+ filterFn: (card: Card, resourceName: string) => boolean,
47
+ ): Promise<Card[]> {
48
+ function filteredCards(
49
+ cardSource: Card[],
50
+ resourceName: string,
51
+ filterFn: (card: Card, resourceName: string) => boolean,
52
+ ): Card[] {
53
+ return cardSource.filter((card) => filterFn(card, resourceName));
43
54
  }
44
55
 
45
56
  // Collect both project cards ...
46
57
  const projectCards = filteredCards(
47
58
  this.project.cards(this.project.paths.cardRootFolder),
48
- cardTypeName,
59
+ resourceName,
60
+ filterFn,
49
61
  );
50
62
  // ... and cards from each template that would be affected.
51
63
  const templates = this.project.resources.templates(ResourcesFrom.localOnly);
52
64
  const templateCards = templates.map((template) => {
53
65
  const templateObject = template.templateObject();
54
- return filteredCards(templateObject.cards(), cardTypeName);
66
+ return filteredCards(templateObject.cards(), resourceName, filterFn);
55
67
  });
56
68
  // Return all affected cards
57
69
  const cards = [projectCards, ...templateCards].reduce(
@@ -67,7 +79,12 @@ export abstract class FileResource<
67
79
  */
68
80
  protected abstract onNameChange?(previousName: string): Promise<void>;
69
81
 
70
- // Updates resource key to a new prefix
82
+ /**
83
+ * Updates resource key to a new prefix
84
+ * @param name Resource name
85
+ * @param prefixes list of prefixes in the project
86
+ * @returns updated resource name
87
+ */
71
88
  protected updatePrefixInResourceName(name: string, prefixes: string[]) {
72
89
  const { identifier, prefix, type } = resourceName(name);
73
90
  if (this.moduleResource) {
@@ -38,6 +38,11 @@ export class GraphModelResource extends FolderResource<
38
38
  GraphModelMetadata,
39
39
  GraphModelContent
40
40
  > {
41
+ /**
42
+ * Creates an instance of GraphModelResource
43
+ * @param project Project to use
44
+ * @param name Resource name
45
+ */
41
46
  constructor(project: Project, name: ResourceName) {
42
47
  super(project, name, 'graphModels');
43
48
 
@@ -55,6 +60,7 @@ export class GraphModelResource extends FolderResource<
55
60
  join(this.internalFolder, CONTENT_FILES.model),
56
61
  ]),
57
62
  super.updateCalculations(existingName, this.content.name),
63
+ super.updateCardContentReferences(existingName, this.content.name),
58
64
  ]);
59
65
  // Finally, write updated content.
60
66
  await this.write();
@@ -39,6 +39,11 @@ export class GraphViewResource extends FolderResource<
39
39
  GraphViewMetadata,
40
40
  GraphViewContent
41
41
  > {
42
+ /**
43
+ * Creates instance of GraphViewResource
44
+ * @param project Project to use
45
+ * @param name Resource name
46
+ */
42
47
  constructor(project: Project, name: ResourceName) {
43
48
  super(project, name, 'graphViews');
44
49
 
@@ -56,6 +61,7 @@ export class GraphViewResource extends FolderResource<
56
61
  await this.handleBarFile(),
57
62
  ]),
58
63
  super.updateCalculations(existingName, this.content.name),
64
+ super.updateCardContentReferences(existingName, this.content.name),
59
65
  ]);
60
66
  await this.write();
61
67
  }