@cyberismo/data-handler 0.0.21 → 0.0.22
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/command-handler.js +13 -24
- package/dist/command-handler.js.map +1 -1
- package/dist/command-manager.d.ts +21 -6
- package/dist/command-manager.js +34 -32
- package/dist/command-manager.js.map +1 -1
- package/dist/commands/calculate.js +101 -46
- package/dist/commands/calculate.js.map +1 -1
- package/dist/commands/create.js +417 -328
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/edit.js +117 -68
- package/dist/commands/edit.js.map +1 -1
- package/dist/commands/export.js +301 -252
- package/dist/commands/export.js.map +1 -1
- package/dist/commands/fetch.js +205 -156
- package/dist/commands/fetch.js.map +1 -1
- package/dist/commands/import.js +189 -134
- package/dist/commands/import.js.map +1 -1
- package/dist/commands/migrate.js +91 -45
- package/dist/commands/migrate.js.map +1 -1
- package/dist/commands/move.js +347 -267
- package/dist/commands/move.js.map +1 -1
- package/dist/commands/remove.d.ts +1 -0
- package/dist/commands/remove.js +202 -135
- package/dist/commands/remove.js.map +1 -1
- package/dist/commands/rename.js +233 -187
- package/dist/commands/rename.js.map +1 -1
- package/dist/commands/show.d.ts +8 -8
- package/dist/commands/show.js +477 -372
- package/dist/commands/show.js.map +1 -1
- package/dist/commands/transition.js +119 -73
- package/dist/commands/transition.js.map +1 -1
- package/dist/commands/update.js +8 -3
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/validate.js +1 -1
- package/dist/commands/validate.js.map +1 -1
- package/dist/containers/project/calculation-engine.js +0 -1
- package/dist/containers/project/calculation-engine.js.map +1 -1
- package/dist/containers/project/card-cache.js +1 -1
- package/dist/containers/project/card-cache.js.map +1 -1
- package/dist/containers/project.d.ts +16 -0
- package/dist/containers/project.js +59 -1
- package/dist/containers/project.js.map +1 -1
- package/dist/containers/template.js +1 -1
- package/dist/containers/template.js.map +1 -1
- package/dist/interfaces/command-options.d.ts +1 -0
- package/dist/interfaces/resource-interfaces.d.ts +5 -12
- package/dist/interfaces/resource-interfaces.js.map +1 -1
- package/dist/macros/base-macro.js +1 -1
- package/dist/macros/base-macro.js.map +1 -1
- package/dist/macros/graph/index.js +3 -1
- package/dist/macros/graph/index.js.map +1 -1
- package/dist/macros/index.js +3 -1
- package/dist/macros/index.js.map +1 -1
- package/dist/macros/report/index.js +1 -1
- package/dist/macros/report/index.js.map +1 -1
- package/dist/module-manager.js +5 -3
- package/dist/module-manager.js.map +1 -1
- package/dist/project-settings.js +2 -2
- package/dist/project-settings.js.map +1 -1
- package/dist/resources/card-type-resource.js +1 -1
- package/dist/resources/card-type-resource.js.map +1 -1
- package/dist/resources/create-defaults.js +0 -1
- package/dist/resources/create-defaults.js.map +1 -1
- package/dist/resources/field-type-resource.js +2 -5
- package/dist/resources/field-type-resource.js.map +1 -1
- package/dist/resources/file-resource.js +4 -1
- package/dist/resources/file-resource.js.map +1 -1
- package/dist/resources/folder-resource.d.ts +1 -1
- package/dist/resources/folder-resource.js +4 -1
- package/dist/resources/folder-resource.js.map +1 -1
- package/dist/resources/graph-model-resource.d.ts +1 -8
- package/dist/resources/graph-model-resource.js +0 -14
- package/dist/resources/graph-model-resource.js.map +1 -1
- package/dist/resources/graph-view-resource.d.ts +1 -8
- package/dist/resources/graph-view-resource.js +0 -14
- package/dist/resources/graph-view-resource.js.map +1 -1
- package/dist/resources/link-type-resource.js +1 -1
- package/dist/resources/link-type-resource.js.map +1 -1
- package/dist/resources/report-resource.d.ts +1 -8
- package/dist/resources/report-resource.js +0 -14
- package/dist/resources/report-resource.js.map +1 -1
- package/dist/resources/resource-object.d.ts +11 -1
- package/dist/resources/resource-object.js +19 -2
- package/dist/resources/resource-object.js.map +1 -1
- package/dist/resources/template-resource.d.ts +1 -9
- package/dist/resources/template-resource.js +0 -15
- package/dist/resources/template-resource.js.map +1 -1
- package/dist/resources/workflow-resource.js +1 -1
- package/dist/resources/workflow-resource.js.map +1 -1
- package/dist/utils/card-utils.js +1 -1
- package/dist/utils/card-utils.js.map +1 -1
- package/dist/utils/commit-context.d.ts +23 -0
- package/dist/utils/commit-context.js +30 -0
- package/dist/utils/commit-context.js.map +1 -0
- package/dist/utils/file-utils.js +3 -1
- package/dist/utils/file-utils.js.map +1 -1
- package/dist/utils/git-manager.d.ts +29 -0
- package/dist/utils/git-manager.js +76 -0
- package/dist/utils/git-manager.js.map +1 -0
- package/dist/utils/handlebars-helpers.d.ts +22 -0
- package/dist/utils/handlebars-helpers.js +78 -0
- package/dist/utils/handlebars-helpers.js.map +1 -0
- package/dist/utils/json.js +6 -2
- package/dist/utils/json.js.map +1 -1
- package/dist/utils/log-utils.d.ts +7 -2
- package/dist/utils/log-utils.js +28 -3
- package/dist/utils/log-utils.js.map +1 -1
- package/dist/utils/report.d.ts +0 -19
- package/dist/utils/report.js +4 -67
- package/dist/utils/report.js.map +1 -1
- package/dist/utils/rw-lock.d.ts +71 -0
- package/dist/utils/rw-lock.js +220 -0
- package/dist/utils/rw-lock.js.map +1 -0
- package/dist/utils/user-preferences.js +3 -3
- package/dist/utils/user-preferences.js.map +1 -1
- package/package.json +5 -5
- package/src/command-handler.ts +14 -22
- package/src/command-manager.ts +43 -37
- package/src/commands/calculate.ts +8 -1
- package/src/commands/create.ts +24 -1
- package/src/commands/edit.ts +3 -0
- package/src/commands/export.ts +8 -2
- package/src/commands/fetch.ts +3 -0
- package/src/commands/import.ts +5 -0
- package/src/commands/migrate.ts +2 -0
- package/src/commands/move.ts +34 -0
- package/src/commands/remove.ts +24 -2
- package/src/commands/rename.ts +2 -0
- package/src/commands/show.ts +63 -34
- package/src/commands/transition.ts +2 -0
- package/src/commands/update.ts +9 -3
- package/src/commands/validate.ts +1 -1
- package/src/containers/project/calculation-engine.ts +0 -1
- package/src/containers/project/card-cache.ts +1 -0
- package/src/containers/project.ts +75 -1
- package/src/containers/template.ts +1 -1
- package/src/interfaces/command-options.ts +1 -0
- package/src/interfaces/resource-interfaces.ts +5 -12
- package/src/macros/base-macro.ts +1 -1
- package/src/macros/graph/index.ts +3 -0
- package/src/macros/index.ts +3 -1
- package/src/macros/report/index.ts +1 -0
- package/src/module-manager.ts +5 -2
- package/src/project-settings.ts +2 -1
- package/src/resources/card-type-resource.ts +1 -1
- package/src/resources/create-defaults.ts +0 -1
- package/src/resources/field-type-resource.ts +2 -4
- package/src/resources/file-resource.ts +3 -1
- package/src/resources/folder-resource.ts +7 -2
- package/src/resources/graph-model-resource.ts +1 -25
- package/src/resources/graph-view-resource.ts +1 -25
- package/src/resources/link-type-resource.ts +1 -1
- package/src/resources/report-resource.ts +1 -25
- package/src/resources/resource-object.ts +22 -1
- package/src/resources/template-resource.ts +0 -23
- package/src/resources/workflow-resource.ts +1 -1
- package/src/utils/card-utils.ts +1 -1
- package/src/utils/commit-context.ts +45 -0
- package/src/utils/file-utils.ts +3 -1
- package/src/utils/git-manager.ts +87 -0
- package/src/utils/handlebars-helpers.ts +95 -0
- package/src/utils/json.ts +6 -2
- package/src/utils/log-utils.ts +33 -4
- package/src/utils/report.ts +8 -78
- package/src/utils/rw-lock.ts +279 -0
- package/src/utils/user-preferences.ts +3 -0
|
@@ -83,18 +83,14 @@ export interface FieldType extends ResourceBaseMetadata {
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
// Graph model content.
|
|
86
|
-
export
|
|
87
|
-
category?: string;
|
|
88
|
-
}
|
|
86
|
+
export type GraphModelMetadata = ResourceBaseMetadata;
|
|
89
87
|
export interface GraphModel extends GraphModelMetadata {
|
|
90
88
|
content: GraphModelContent;
|
|
91
89
|
}
|
|
92
90
|
export type GraphModelContentPropertyName = 'model';
|
|
93
91
|
|
|
94
92
|
// Graph view content.
|
|
95
|
-
export
|
|
96
|
-
category?: string;
|
|
97
|
-
}
|
|
93
|
+
export type GraphViewMetadata = ResourceBaseMetadata;
|
|
98
94
|
export type GraphViewContentPropertyName = 'viewTemplate';
|
|
99
95
|
export interface GraphView extends GraphViewMetadata {
|
|
100
96
|
content: GraphViewContent;
|
|
@@ -128,13 +124,12 @@ export type ReportContentPropertyName =
|
|
|
128
124
|
| 'schema';
|
|
129
125
|
|
|
130
126
|
// Metadata for report
|
|
131
|
-
export
|
|
132
|
-
category: string;
|
|
133
|
-
}
|
|
127
|
+
export type ReportMetadata = ResourceBaseMetadata;
|
|
134
128
|
|
|
135
129
|
// Base interface for all resources.
|
|
136
130
|
export interface ResourceBaseMetadata {
|
|
137
131
|
name: string;
|
|
132
|
+
category?: string;
|
|
138
133
|
description?: string;
|
|
139
134
|
displayName: string;
|
|
140
135
|
usedIn?: string[];
|
|
@@ -173,9 +168,7 @@ export interface TemplateConfiguration extends TemplateMetadata {
|
|
|
173
168
|
}
|
|
174
169
|
|
|
175
170
|
// Template configuration content details.
|
|
176
|
-
export
|
|
177
|
-
category?: string;
|
|
178
|
-
}
|
|
171
|
+
export type TemplateMetadata = ResourceBaseMetadata;
|
|
179
172
|
type ContentUpdateKey = { key: 'content'; subKey: string };
|
|
180
173
|
type PropertyUpdateKey<K extends string = string> = {
|
|
181
174
|
key: Exclude<K, 'content'>;
|
package/src/macros/base-macro.ts
CHANGED
|
@@ -134,7 +134,7 @@ abstract class BaseMacro {
|
|
|
134
134
|
} catch (error) {
|
|
135
135
|
if (error instanceof Error) {
|
|
136
136
|
const errorMessage = `From card '${context.cardKey}' a macro validation error:\n\n${error.message}.\n\nCard content:\n ${input}`;
|
|
137
|
-
throw new Error(errorMessage);
|
|
137
|
+
throw new Error(errorMessage, { cause: error });
|
|
138
138
|
}
|
|
139
139
|
throw error;
|
|
140
140
|
}
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
import BaseMacro from '../base-macro.js';
|
|
15
15
|
import { createImage, validateMacroContent } from '../index.js';
|
|
16
|
+
import { registerComparisonHelpers } from '../../utils/handlebars-helpers.js';
|
|
16
17
|
import Handlebars from 'handlebars';
|
|
17
18
|
import macroMetadata from './metadata.js';
|
|
18
19
|
import { ClingoError } from '@cyberismo/node-clingo';
|
|
@@ -54,6 +55,7 @@ class GraphMacro extends BaseMacro {
|
|
|
54
55
|
};
|
|
55
56
|
|
|
56
57
|
const handlebars = Handlebars.create();
|
|
58
|
+
registerComparisonHelpers(handlebars);
|
|
57
59
|
const view = handlebars.compile(viewContent.viewTemplate)(
|
|
58
60
|
handlebarsContext,
|
|
59
61
|
);
|
|
@@ -69,6 +71,7 @@ class GraphMacro extends BaseMacro {
|
|
|
69
71
|
if (error instanceof ClingoError) {
|
|
70
72
|
throw new Error(
|
|
71
73
|
`Error running graph in view '${options.view}' in model '${options.model}': ${error.details.errors.join('\n')}`,
|
|
74
|
+
{ cause: error },
|
|
72
75
|
);
|
|
73
76
|
}
|
|
74
77
|
throw error;
|
package/src/macros/index.ts
CHANGED
|
@@ -175,7 +175,9 @@ export function validateMacroContent<T>(
|
|
|
175
175
|
if (error instanceof DHValidationError) {
|
|
176
176
|
message = `${error.errors?.map((e) => e.message).join(', ')}`;
|
|
177
177
|
}
|
|
178
|
-
throw new Error(`${macro.name} macro JSON validation error: ${message}
|
|
178
|
+
throw new Error(`${macro.name} macro JSON validation error: ${message}`, {
|
|
179
|
+
cause: error,
|
|
180
|
+
});
|
|
179
181
|
}
|
|
180
182
|
}
|
|
181
183
|
|
package/src/module-manager.ts
CHANGED
|
@@ -157,6 +157,7 @@ export class ModuleManager {
|
|
|
157
157
|
if (error instanceof Error)
|
|
158
158
|
throw new Error(
|
|
159
159
|
`Failed to clone module '${module.name}': ${error.message}`,
|
|
160
|
+
{ cause: error },
|
|
160
161
|
);
|
|
161
162
|
}
|
|
162
163
|
|
|
@@ -245,7 +246,7 @@ export class ModuleManager {
|
|
|
245
246
|
module: ModuleSetting,
|
|
246
247
|
credentials?: Credentials,
|
|
247
248
|
) {
|
|
248
|
-
let moduleRoot
|
|
249
|
+
let moduleRoot: string;
|
|
249
250
|
if (this.isFileModule(module)) {
|
|
250
251
|
const urlStart = FILE_PROTOCOL.length;
|
|
251
252
|
// Remove 'file:' from location
|
|
@@ -350,7 +351,9 @@ export class ModuleManager {
|
|
|
350
351
|
});
|
|
351
352
|
}
|
|
352
353
|
} catch (error) {
|
|
353
|
-
throw new Error(`Failed to prepare temporary directory: ${error}
|
|
354
|
+
throw new Error(`Failed to prepare temporary directory: ${error}`, {
|
|
355
|
+
cause: error,
|
|
356
|
+
});
|
|
354
357
|
}
|
|
355
358
|
}
|
|
356
359
|
|
package/src/project-settings.ts
CHANGED
|
@@ -150,6 +150,7 @@ export class ProjectConfiguration implements ProjectSettings {
|
|
|
150
150
|
if (error instanceof TypeError) {
|
|
151
151
|
throw new Error(
|
|
152
152
|
`Invalid hub URL '${trimmedHub}'. Please provide a valid HTTP or HTTPS URL.`,
|
|
153
|
+
{ cause: error },
|
|
153
154
|
);
|
|
154
155
|
}
|
|
155
156
|
throw error;
|
|
@@ -195,7 +196,7 @@ export class ProjectConfiguration implements ProjectSettings {
|
|
|
195
196
|
if (this.schemaVersion < SCHEMA_VERSION) {
|
|
196
197
|
return {
|
|
197
198
|
isCompatible: false,
|
|
198
|
-
message: `Schema version mismatch: Project schema version (${this.schemaVersion}) is older than the application schema version (${SCHEMA_VERSION}). A migration is needed.`,
|
|
199
|
+
message: `Schema version mismatch: Project schema version (${this.schemaVersion}) is older than the application schema version (${SCHEMA_VERSION}). A migration is needed. Run 'cyberismo migrate' to update the project schema.`,
|
|
199
200
|
};
|
|
200
201
|
}
|
|
201
202
|
|
|
@@ -448,7 +448,7 @@ export class CardTypeResource extends FileResource<CardType> {
|
|
|
448
448
|
) {
|
|
449
449
|
const { key } = updateKey;
|
|
450
450
|
|
|
451
|
-
if (key
|
|
451
|
+
if (this.isBaseProperty(key)) {
|
|
452
452
|
await super.update(updateKey, op);
|
|
453
453
|
} else {
|
|
454
454
|
const content = structuredClone(this.content);
|
|
@@ -408,16 +408,14 @@ export class FieldTypeResource extends FileResource<FieldType> {
|
|
|
408
408
|
) {
|
|
409
409
|
const { key } = updateKey;
|
|
410
410
|
|
|
411
|
-
if (key
|
|
411
|
+
if (this.isBaseProperty(key)) {
|
|
412
412
|
await super.update(updateKey, op);
|
|
413
413
|
} else {
|
|
414
414
|
const content = structuredClone(this.content);
|
|
415
415
|
const typeChange = key === 'dataType';
|
|
416
416
|
const enumChange = key === 'enumValues';
|
|
417
417
|
const existingType = this.content.dataType;
|
|
418
|
-
if (key === '
|
|
419
|
-
content.name = super.handleScalar(op) as string;
|
|
420
|
-
} else if (key === 'dataType') {
|
|
418
|
+
if (key === 'dataType') {
|
|
421
419
|
const toType = op as ChangeOperation<DataType>;
|
|
422
420
|
if (!FieldTypeResource.fieldDataTypes().includes(toType.to)) {
|
|
423
421
|
throw new Error(
|
|
@@ -118,8 +118,10 @@ export abstract class FileResource<
|
|
|
118
118
|
content.displayName = super.handleScalar(op) as string;
|
|
119
119
|
} else if (key === 'description') {
|
|
120
120
|
content.description = super.handleScalar(op) as string;
|
|
121
|
+
} else if (key === 'category') {
|
|
122
|
+
content.category = super.handleScalar(op) as string;
|
|
121
123
|
} else {
|
|
122
|
-
throw new Error(`Unknown property '${key}' for
|
|
124
|
+
throw new Error(`Unknown property '${key}' for file resource`);
|
|
123
125
|
}
|
|
124
126
|
|
|
125
127
|
await super.postUpdate(content, updateKey, op);
|
|
@@ -62,7 +62,7 @@ export abstract class FolderResource<
|
|
|
62
62
|
* Creates a new folder type object. Base class writes the object to disk automatically.
|
|
63
63
|
* @param newContent Content for the type.
|
|
64
64
|
*/
|
|
65
|
-
|
|
65
|
+
public async create(newContent?: T) {
|
|
66
66
|
// Validate resource identifier before creating on disk
|
|
67
67
|
this.validateResourceIdentifier();
|
|
68
68
|
await super.create(newContent);
|
|
@@ -154,7 +154,10 @@ export abstract class FolderResource<
|
|
|
154
154
|
} catch (error) {
|
|
155
155
|
const message =
|
|
156
156
|
error instanceof Error ? error.message : 'Unknown error';
|
|
157
|
-
throw new Error(
|
|
157
|
+
throw new Error(
|
|
158
|
+
`Invalid JSON content for '${key}' update: ${message}`,
|
|
159
|
+
{ cause: error },
|
|
160
|
+
);
|
|
158
161
|
}
|
|
159
162
|
}
|
|
160
163
|
const contentToWrite = isJson
|
|
@@ -252,6 +255,8 @@ export abstract class FolderResource<
|
|
|
252
255
|
content.displayName = super.handleScalar(op) as string;
|
|
253
256
|
} else if (key === 'description') {
|
|
254
257
|
content.description = super.handleScalar(op) as string;
|
|
258
|
+
} else if (key === 'category') {
|
|
259
|
+
content.category = super.handleScalar(op) as string;
|
|
255
260
|
} else {
|
|
256
261
|
throw new Error(`Unknown property '${key}' for folder resource`);
|
|
257
262
|
}
|
|
@@ -22,12 +22,8 @@ import { writeFileSafe } from '../utils/file-utils.js';
|
|
|
22
22
|
import { CONTENT_FILES } from '../interfaces/folder-content-interfaces.js';
|
|
23
23
|
|
|
24
24
|
import type { Card } from '../interfaces/project-interfaces.js';
|
|
25
|
-
import type {
|
|
26
|
-
GraphModelMetadata,
|
|
27
|
-
UpdateKey,
|
|
28
|
-
} from '../interfaces/resource-interfaces.js';
|
|
25
|
+
import type { GraphModelMetadata } from '../interfaces/resource-interfaces.js';
|
|
29
26
|
import type { GraphModelContent } from '../interfaces/folder-content-interfaces.js';
|
|
30
|
-
import type { Operation } from './resource-object.js';
|
|
31
27
|
import type { Project } from '../containers/project.js';
|
|
32
28
|
import type { ResourceName } from '../utils/resource-utils.js';
|
|
33
29
|
|
|
@@ -103,26 +99,6 @@ export class GraphModelResource extends FolderResource<
|
|
|
103
99
|
return this.onNameChange(existingName);
|
|
104
100
|
}
|
|
105
101
|
|
|
106
|
-
/**
|
|
107
|
-
* Updates graph model resource.
|
|
108
|
-
* @param updateKey Key to modify
|
|
109
|
-
* @param op Operation to perform on 'key'
|
|
110
|
-
*/
|
|
111
|
-
public async update<Type, K extends string>(
|
|
112
|
-
updateKey: UpdateKey<K>,
|
|
113
|
-
op: Operation<Type>,
|
|
114
|
-
) {
|
|
115
|
-
if (updateKey.key === 'category') {
|
|
116
|
-
const content = structuredClone(this.content);
|
|
117
|
-
content.category = super.handleScalar(op) as string;
|
|
118
|
-
|
|
119
|
-
await super.postUpdate(content, updateKey, op);
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
await super.update(updateKey, op);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
102
|
/**
|
|
127
103
|
* List where this resource is used.
|
|
128
104
|
* Always returns card key references first, then calculation references.
|
|
@@ -24,11 +24,7 @@ import { sortCards } from '../utils/card-utils.js';
|
|
|
24
24
|
|
|
25
25
|
import type { Card } from '../interfaces/project-interfaces.js';
|
|
26
26
|
import type { GraphViewContent } from '../interfaces/folder-content-interfaces.js';
|
|
27
|
-
import type {
|
|
28
|
-
GraphViewMetadata,
|
|
29
|
-
UpdateKey,
|
|
30
|
-
} from '../interfaces/resource-interfaces.js';
|
|
31
|
-
import type { Operation } from './resource-object.js';
|
|
27
|
+
import type { GraphViewMetadata } from '../interfaces/resource-interfaces.js';
|
|
32
28
|
import type { Project } from '../containers/project.js';
|
|
33
29
|
import type { ResourceName } from '../utils/resource-utils.js';
|
|
34
30
|
|
|
@@ -115,26 +111,6 @@ export class GraphViewResource extends FolderResource<
|
|
|
115
111
|
return this.onNameChange(existingName);
|
|
116
112
|
}
|
|
117
113
|
|
|
118
|
-
/**
|
|
119
|
-
* Updates graph view resource.
|
|
120
|
-
* @param updateKey Key to modify
|
|
121
|
-
* @param op Operation to perform on 'key'
|
|
122
|
-
*/
|
|
123
|
-
public async update<Type, K extends string>(
|
|
124
|
-
updateKey: UpdateKey<K>,
|
|
125
|
-
op: Operation<Type>,
|
|
126
|
-
) {
|
|
127
|
-
if (updateKey.key === 'category') {
|
|
128
|
-
const content = structuredClone(this.content) as GraphViewMetadata;
|
|
129
|
-
content.category = super.handleScalar(op) as string;
|
|
130
|
-
|
|
131
|
-
await super.postUpdate(content, updateKey, op);
|
|
132
|
-
return;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
await super.update(updateKey, op);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
114
|
/**
|
|
139
115
|
* List where this resource is used.
|
|
140
116
|
* Always returns card key references first, then calculation references.
|
|
@@ -127,7 +127,7 @@ export class LinkTypeResource extends FileResource<LinkType> {
|
|
|
127
127
|
) {
|
|
128
128
|
const { key } = updateKey;
|
|
129
129
|
|
|
130
|
-
if (key
|
|
130
|
+
if (this.isBaseProperty(key)) {
|
|
131
131
|
await super.update(updateKey, op);
|
|
132
132
|
} else {
|
|
133
133
|
const content = structuredClone(this.content);
|
|
@@ -24,13 +24,9 @@ import { sortCards } from '../utils/card-utils.js';
|
|
|
24
24
|
import { Validate } from '../commands/validate.js';
|
|
25
25
|
|
|
26
26
|
import type { Card } from '../interfaces/project-interfaces.js';
|
|
27
|
-
import type { Operation } from './resource-object.js';
|
|
28
27
|
import type { Project } from '../containers/project.js';
|
|
29
28
|
import type { ReportContent } from '../interfaces/folder-content-interfaces.js';
|
|
30
|
-
import type {
|
|
31
|
-
ReportMetadata,
|
|
32
|
-
UpdateKey,
|
|
33
|
-
} from '../interfaces/resource-interfaces.js';
|
|
29
|
+
import type { ReportMetadata } from '../interfaces/resource-interfaces.js';
|
|
34
30
|
import type { ResourceName } from '../utils/resource-utils.js';
|
|
35
31
|
|
|
36
32
|
const PARAMETER_SCHEMA_ID = 'jsonSchema';
|
|
@@ -120,26 +116,6 @@ export class ReportResource extends FolderResource<
|
|
|
120
116
|
return this.onNameChange(existingName);
|
|
121
117
|
}
|
|
122
118
|
|
|
123
|
-
/**
|
|
124
|
-
* Updates report resource.
|
|
125
|
-
* @param updateKey Key to modify
|
|
126
|
-
* @param op Operation to perform on 'key'
|
|
127
|
-
*/
|
|
128
|
-
public async update<Type, K extends string>(
|
|
129
|
-
updateKey: UpdateKey<K>,
|
|
130
|
-
op: Operation<Type>,
|
|
131
|
-
) {
|
|
132
|
-
if (updateKey.key === 'category') {
|
|
133
|
-
const content = structuredClone(this.content);
|
|
134
|
-
content.category = super.handleScalar(op) as string;
|
|
135
|
-
|
|
136
|
-
await super.postUpdate(content, updateKey, op);
|
|
137
|
-
return;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
await super.update(updateKey, op);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
119
|
/**
|
|
144
120
|
* List where this resource is used.
|
|
145
121
|
* Always returns card key references first, then calculation references.
|
|
@@ -270,7 +270,7 @@ export abstract class ResourceObject<
|
|
|
270
270
|
* @param newContent Content for resource.
|
|
271
271
|
* @throws when resource already exists in the project.
|
|
272
272
|
*/
|
|
273
|
-
|
|
273
|
+
public async create(newContent?: T) {
|
|
274
274
|
this.validateResourceIdentifier();
|
|
275
275
|
|
|
276
276
|
if (this.exists()) {
|
|
@@ -357,6 +357,7 @@ export abstract class ResourceObject<
|
|
|
357
357
|
if (error instanceof Error) {
|
|
358
358
|
throw new Error(
|
|
359
359
|
`Cannot perform operation on '${arrayName}'. ${error.message}`,
|
|
360
|
+
{ cause: error },
|
|
360
361
|
);
|
|
361
362
|
}
|
|
362
363
|
}
|
|
@@ -507,6 +508,7 @@ export abstract class ResourceObject<
|
|
|
507
508
|
const errorValue = typeof op === 'object' ? toValue(op) : op;
|
|
508
509
|
throw new Error(
|
|
509
510
|
`Cannot ${op.name} '${updateKey.key}' --> '${errorValue}: ${error.message}'`,
|
|
511
|
+
{ cause: error },
|
|
510
512
|
);
|
|
511
513
|
}
|
|
512
514
|
}
|
|
@@ -576,6 +578,25 @@ export abstract class ResourceObject<
|
|
|
576
578
|
} as ChangeOperation<string>);
|
|
577
579
|
}
|
|
578
580
|
|
|
581
|
+
/**
|
|
582
|
+
* Base properties shared by all resources.
|
|
583
|
+
*/
|
|
584
|
+
private static readonly BASE_PROPERTIES = [
|
|
585
|
+
'name',
|
|
586
|
+
'displayName',
|
|
587
|
+
'description',
|
|
588
|
+
'category',
|
|
589
|
+
] as const;
|
|
590
|
+
|
|
591
|
+
/**
|
|
592
|
+
* Checks if the given key is a base property shared by all resources.
|
|
593
|
+
* @param key The property key to check
|
|
594
|
+
* @returns true if the key is a base property
|
|
595
|
+
*/
|
|
596
|
+
protected isBaseProperty(key: string): boolean {
|
|
597
|
+
return (ResourceObject.BASE_PROPERTIES as readonly string[]).includes(key);
|
|
598
|
+
}
|
|
599
|
+
|
|
579
600
|
/**
|
|
580
601
|
* Update resource; the base class makes some checks only.
|
|
581
602
|
* @template type Resource type
|
|
@@ -22,13 +22,11 @@ import { Template } from '../containers/template.js';
|
|
|
22
22
|
import { writeJsonFile } from '../utils/json.js';
|
|
23
23
|
|
|
24
24
|
import type { Card } from '../interfaces/project-interfaces.js';
|
|
25
|
-
import type { Operation } from './resource-object.js';
|
|
26
25
|
import type { Project } from '../containers/project.js';
|
|
27
26
|
import type { ResourceName } from '../utils/resource-utils.js';
|
|
28
27
|
import type {
|
|
29
28
|
TemplateConfiguration,
|
|
30
29
|
TemplateMetadata,
|
|
31
|
-
UpdateKey,
|
|
32
30
|
} from '../interfaces/resource-interfaces.js';
|
|
33
31
|
|
|
34
32
|
/**
|
|
@@ -135,27 +133,6 @@ export class TemplateResource extends FolderResource<TemplateMetadata, never> {
|
|
|
135
133
|
return this.cardContainer;
|
|
136
134
|
}
|
|
137
135
|
|
|
138
|
-
/**
|
|
139
|
-
* Updates template resource.
|
|
140
|
-
* @param updateKey Key to modify
|
|
141
|
-
* @param op Operation to perform on 'key'
|
|
142
|
-
* @throws if key is unknown.
|
|
143
|
-
*/
|
|
144
|
-
public async update<Type, K extends string>(
|
|
145
|
-
updateKey: UpdateKey<K>,
|
|
146
|
-
op: Operation<Type>,
|
|
147
|
-
) {
|
|
148
|
-
if (updateKey.key === 'category') {
|
|
149
|
-
const content = structuredClone(this.content);
|
|
150
|
-
content.category = super.handleScalar(op) as string;
|
|
151
|
-
|
|
152
|
-
await super.postUpdate(content, updateKey, op);
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
await super.update(updateKey, op);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
136
|
/**
|
|
160
137
|
* List where template is used.
|
|
161
138
|
* Always returns card key references first, then calculation references.
|
|
@@ -281,7 +281,7 @@ export class WorkflowResource extends FileResource<Workflow> {
|
|
|
281
281
|
) {
|
|
282
282
|
const { key } = updateKey;
|
|
283
283
|
|
|
284
|
-
if (key
|
|
284
|
+
if (this.isBaseProperty(key)) {
|
|
285
285
|
await super.update(updateKey, op);
|
|
286
286
|
} else {
|
|
287
287
|
const content = structuredClone(this.content) as Workflow;
|
package/src/utils/card-utils.ts
CHANGED
|
@@ -95,7 +95,7 @@ export const cardPathParts = (
|
|
|
95
95
|
const cardKey = pathParts.at(pathParts.length - 1);
|
|
96
96
|
const parents = [];
|
|
97
97
|
let template = '';
|
|
98
|
-
let startIndex
|
|
98
|
+
let startIndex: number;
|
|
99
99
|
let templatesNameIndex = -1;
|
|
100
100
|
|
|
101
101
|
const cardRootIndex = pathParts.indexOf('cardRoot');
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
Cyberismo
|
|
3
|
+
Copyright © Cyberismo Ltd and contributors 2026
|
|
4
|
+
|
|
5
|
+
This program is free software: you can redistribute it and/or modify it under
|
|
6
|
+
the terms of the GNU Affero General Public License version 3 as published by
|
|
7
|
+
the Free Software Foundation. This program is distributed in the hope that it
|
|
8
|
+
will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
|
9
|
+
of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
10
|
+
See the GNU Affero General Public License for more details.
|
|
11
|
+
You should have received a copy of the GNU Affero General Public
|
|
12
|
+
License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
16
|
+
|
|
17
|
+
export interface CommitContext {
|
|
18
|
+
message?: string;
|
|
19
|
+
author?: { name: string; email: string };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const context = new AsyncLocalStorage<CommitContext>();
|
|
23
|
+
|
|
24
|
+
export function runWithCommitContext<T>(
|
|
25
|
+
ctx: CommitContext,
|
|
26
|
+
fn: () => Promise<T>,
|
|
27
|
+
): Promise<T> {
|
|
28
|
+
const current = context.getStore();
|
|
29
|
+
// Merge with any existing context (e.g. author set at middleware level, message set at decorator level)
|
|
30
|
+
const merged = { ...current, ...ctx };
|
|
31
|
+
return context.run(merged, fn);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function getCommitContext(): CommitContext {
|
|
35
|
+
return context.getStore() ?? {};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function runWithDefaultCommitMessage<T>(
|
|
39
|
+
message: string,
|
|
40
|
+
fn: () => Promise<T>,
|
|
41
|
+
): Promise<T> {
|
|
42
|
+
return getCommitContext().message !== undefined
|
|
43
|
+
? fn()
|
|
44
|
+
: runWithCommitContext({ message }, fn);
|
|
45
|
+
}
|
package/src/utils/file-utils.ts
CHANGED
|
@@ -36,7 +36,9 @@ export async function availableSpace(path: string): Promise<number> {
|
|
|
36
36
|
const stats = await statfs(path);
|
|
37
37
|
return stats.bavail * stats.bsize;
|
|
38
38
|
} catch (error) {
|
|
39
|
-
throw new Error(`Failed to check available disk space: ${error}
|
|
39
|
+
throw new Error(`Failed to check available disk space: ${error}`, {
|
|
40
|
+
cause: error,
|
|
41
|
+
});
|
|
40
42
|
}
|
|
41
43
|
}
|
|
42
44
|
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
Cyberismo
|
|
3
|
+
Copyright © Cyberismo Ltd and contributors 2026
|
|
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. This program is distributed in the hope that it
|
|
7
|
+
will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
|
8
|
+
of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
9
|
+
See the GNU Affero General Public License for more details.
|
|
10
|
+
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 { simpleGit, type SimpleGit } from 'simple-git';
|
|
15
|
+
import { getChildLogger } from './log-utils.js';
|
|
16
|
+
|
|
17
|
+
export class GitManager {
|
|
18
|
+
private git: SimpleGit;
|
|
19
|
+
private logger = getChildLogger({ module: 'GitManager' });
|
|
20
|
+
|
|
21
|
+
constructor(projectPath: string) {
|
|
22
|
+
this.git = simpleGit(projectPath, {
|
|
23
|
+
config: ['user.name=Cyberismo Bot', 'user.email=bot@cyberismo.com'],
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Ensure the project is a git repo. Idempotent. */
|
|
28
|
+
async initialize(author?: { name: string; email: string }): Promise<void> {
|
|
29
|
+
const isRepo = await this.git.checkIsRepo();
|
|
30
|
+
if (!isRepo) {
|
|
31
|
+
await this.git.init();
|
|
32
|
+
// Initial commit so rollback has a baseline
|
|
33
|
+
await this.git.add('.');
|
|
34
|
+
const commitOpts: Record<string, string | null> = {
|
|
35
|
+
'--allow-empty': null,
|
|
36
|
+
};
|
|
37
|
+
if (author) {
|
|
38
|
+
commitOpts['--author'] = `${author.name} <${author.email}>`;
|
|
39
|
+
}
|
|
40
|
+
await this.git.commit('Initial commit', undefined, commitOpts);
|
|
41
|
+
this.logger.info('New repo created with baseline commit');
|
|
42
|
+
} else {
|
|
43
|
+
this.logger.debug('Repo already exists');
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Commit current changes (cardRoot + .cards). */
|
|
48
|
+
async commit(
|
|
49
|
+
message: string = 'Autocommit',
|
|
50
|
+
author?: { name: string; email: string },
|
|
51
|
+
): Promise<void> {
|
|
52
|
+
// Stage only the directories we care about
|
|
53
|
+
this.logger.debug('Staging changes');
|
|
54
|
+
await this.git.add(['cardRoot', '.cards']);
|
|
55
|
+
|
|
56
|
+
// Check if there's anything to commit
|
|
57
|
+
const status = await this.git.status();
|
|
58
|
+
if (status.staged.length === 0) {
|
|
59
|
+
this.logger.debug('Nothing to commit, skipping');
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
this.logger.info(
|
|
64
|
+
{ message, stagedFiles: status.staged.length },
|
|
65
|
+
'Committing changes',
|
|
66
|
+
);
|
|
67
|
+
const commitOpts: Record<string, string> = {};
|
|
68
|
+
if (author) {
|
|
69
|
+
commitOpts['--author'] = `${author.name} <${author.email}>`;
|
|
70
|
+
}
|
|
71
|
+
await this.git.commit(message, undefined, commitOpts);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Rollback: restore cardRoot and .cards to last committed state. */
|
|
75
|
+
async rollback(): Promise<void> {
|
|
76
|
+
this.logger.info('Rollback starting');
|
|
77
|
+
// Restore modified tracked files (ignore errors if paths have no tracked content)
|
|
78
|
+
try {
|
|
79
|
+
await this.git.checkout(['--', 'cardRoot', '.cards']);
|
|
80
|
+
} catch {
|
|
81
|
+
this.logger.debug('No tracked files to restore');
|
|
82
|
+
}
|
|
83
|
+
// Remove new untracked files created during the failed write
|
|
84
|
+
await this.git.clean('f', ['-d', 'cardRoot', '.cards']);
|
|
85
|
+
this.logger.info('Rollback completed');
|
|
86
|
+
}
|
|
87
|
+
}
|