@codifycli/plugin-core 1.2.3-beta.1 → 1.2.3-beta.2

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.
@@ -4,7 +4,7 @@
4
4
  declare class CodifyCliSenderImpl {
5
5
  private readonly validateIpcMessageV2;
6
6
  requestPressKeyToContinuePrompt(message?: string): Promise<void>;
7
- sendApplyNote(message: string, resourceType?: string): Promise<void>;
7
+ sendApplyNote(message: string, resourceType?: string): void;
8
8
  getCodifyCliCredentials(): Promise<string>;
9
9
  private sendAndWaitForResponse;
10
10
  }
@@ -17,16 +17,17 @@ class CodifyCliSenderImpl {
17
17
  }
18
18
  });
19
19
  }
20
- async sendApplyNote(message, resourceType) {
20
+ sendApplyNote(message, resourceType) {
21
21
  if (!process.send || !process.connected) {
22
22
  return;
23
23
  }
24
- await this.sendAndWaitForResponse({
24
+ process.send({
25
25
  cmd: MessageCmd.APPLY_NOTE_REQUEST,
26
26
  data: {
27
27
  message,
28
28
  ...(resourceType && { resourceType }),
29
- }
29
+ },
30
+ requestId: nanoid(8),
30
31
  });
31
32
  }
32
33
  async getCodifyCliCredentials() {
@@ -49,5 +49,6 @@ export declare class ChangeSet<T extends StringIndexedObject> {
49
49
  */
50
50
  private static calculateParameterChanges;
51
51
  private static combineResourceOperations;
52
+ private static isAbsent;
52
53
  private static isSame;
53
54
  }
@@ -92,9 +92,9 @@ export class ChangeSet {
92
92
  */
93
93
  static calculateParameterChanges(desiredParameters, currentParameters, parameterOptions) {
94
94
  const parameterChangeSet = new Array();
95
- // Filter out null and undefined values or else the diff below will not work
96
- const desired = Object.fromEntries(Object.entries(desiredParameters).filter(([, v]) => v !== null && v !== undefined));
97
- const current = Object.fromEntries(Object.entries(currentParameters).filter(([, v]) => v !== null && v !== undefined));
95
+ // Filter out null, undefined, [], and {} all treated as "no value"
96
+ const desired = Object.fromEntries(Object.entries(desiredParameters).filter(([, v]) => !ChangeSet.isAbsent(v)));
97
+ const current = Object.fromEntries(Object.entries(currentParameters).filter(([, v]) => !ChangeSet.isAbsent(v)));
98
98
  for (const k of new Set([...Object.keys(current), ...Object.keys(desired)])) {
99
99
  if (ChangeSet.isSame(desired[k], current[k], parameterOptions?.[k])) {
100
100
  parameterChangeSet.push({
@@ -148,6 +148,15 @@ export class ChangeSet {
148
148
  const indexNext = orderOfOperations.indexOf(next);
149
149
  return orderOfOperations[Math.max(indexPrev, indexNext)];
150
150
  }
151
+ static isAbsent(v) {
152
+ if (v === null || v === undefined)
153
+ return true;
154
+ if (Array.isArray(v))
155
+ return v.length === 0;
156
+ if (typeof v === 'object')
157
+ return Object.keys(v).length === 0;
158
+ return false;
159
+ }
151
160
  static isSame(desired, current, setting) {
152
161
  return (setting?.isEqual ?? ((a, b) => a === b))(desired, current);
153
162
  }
@@ -24,7 +24,7 @@ export class FileUtils {
24
24
  await FileUtils.createShellRcIfNotExists();
25
25
  const lineToInsert = addLeadingSpacer(addTrailingSpacer(line));
26
26
  await fs.appendFile(Utils.getPrimaryShellRc(), lineToInsert);
27
- await CodifyCliSender.sendApplyNote(ApplyNotes.sourceShellRc());
27
+ CodifyCliSender.sendApplyNote(ApplyNotes.sourceShellRc());
28
28
  function addLeadingSpacer(line) {
29
29
  return line.startsWith('\n')
30
30
  ? line
@@ -43,7 +43,7 @@ export class FileUtils {
43
43
  console.log(`Adding to ${path.basename(shellRc)}:
44
44
  ${lines.join('\n')}`);
45
45
  await fs.appendFile(shellRc, formattedLines);
46
- await CodifyCliSender.sendApplyNote(ApplyNotes.sourceShellRc());
46
+ CodifyCliSender.sendApplyNote(ApplyNotes.sourceShellRc());
47
47
  }
48
48
  /**
49
49
  * This method adds a directory path to the shell rc file if it doesn't already exist.
@@ -64,7 +64,7 @@ ${lines.join('\n')}`);
64
64
  else {
65
65
  await fs.appendFile(shellRc, `\nexport PATH=${value}:$PATH;`, { encoding: 'utf8' });
66
66
  }
67
- await CodifyCliSender.sendApplyNote(ApplyNotes.sourceShellRc());
67
+ CodifyCliSender.sendApplyNote(ApplyNotes.sourceShellRc());
68
68
  }
69
69
  static async removeFromFile(filePath, search) {
70
70
  const contents = await fs.readFile(filePath, 'utf8');
@@ -31,7 +31,7 @@ export function resolvePathWithVariables(pathWithVariables) {
31
31
  export function addVariablesToPath(pathWithoutVariables) {
32
32
  let result = pathWithoutVariables;
33
33
  for (const [key, value] of Object.entries(process.env)) {
34
- if (!value || !path.isAbsolute(value) || value === '/' || key === 'HOME' || key === 'PATH' || key === 'SHELL' || key === 'PWD') {
34
+ if (!value || !path.isAbsolute(value) || value === '/' || value === homeDirectory || key === 'HOME' || key === 'PATH' || key === 'SHELL' || key === 'PWD') {
35
35
  continue;
36
36
  }
37
37
  result = result.replaceAll(value, `$${key}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codifycli/plugin-core",
3
- "version": "1.2.3-beta.1",
3
+ "version": "1.2.3-beta.2",
4
4
  "description": "TypeScript library for building Codify plugins to manage system resources (applications, CLI tools, settings) through infrastructure-as-code",
5
5
  "main": "dist/index.js",
6
6
  "typings": "dist/index.d.ts",
@@ -27,17 +27,18 @@ class CodifyCliSenderImpl {
27
27
  })
28
28
  }
29
29
 
30
- async sendApplyNote(message: string, resourceType?: string): Promise<void> {
30
+ sendApplyNote(message: string, resourceType?: string): void {
31
31
  if (!process.send || !process.connected) {
32
32
  return;
33
33
  }
34
34
 
35
- await this.sendAndWaitForResponse(<IpcMessageV2>{
35
+ process.send(<IpcMessageV2>{
36
36
  cmd: MessageCmd.APPLY_NOTE_REQUEST,
37
37
  data: <ApplyNoteRequestData>{
38
38
  message,
39
39
  ...(resourceType && { resourceType }),
40
- }
40
+ },
41
+ requestId: nanoid(8),
41
42
  });
42
43
  }
43
44
 
@@ -304,6 +304,33 @@ describe('Change set tests', () => {
304
304
  expect(cs.operation).to.eq(ResourceOperation.DESTROY);
305
305
  })
306
306
 
307
+ it('treats empty objects {} as no-op', () => {
308
+ const desired = {
309
+ keyboard: {},
310
+ dock: {},
311
+ }
312
+
313
+ const current = {
314
+ keyboard: {},
315
+ dock: {},
316
+ }
317
+
318
+ const cs = ChangeSet.calculateModification(desired, current);
319
+ expect(cs.parameterChanges.length).to.eq(0);
320
+ expect(cs.operation).to.eq(ResourceOperation.NOOP);
321
+ })
322
+
323
+ it('treats empty object {} as absent when current has no value', () => {
324
+ const desired = {
325
+ keyboard: {},
326
+ dock: {},
327
+ }
328
+
329
+ const cs = ChangeSet.calculateModification(desired, {});
330
+ expect(cs.parameterChanges.length).to.eq(0);
331
+ expect(cs.operation).to.eq(ResourceOperation.NOOP);
332
+ })
333
+
307
334
  it('correctly determines array equality 5', () => {
308
335
  const arrA = [{ key1: 'b' }, { key1: 'a' }, { key1: 'a' }];
309
336
  const arrB = [{ key1: 'a' }, { key1: 'a' }, { key1: 'b' }];
@@ -154,13 +154,13 @@ export class ChangeSet<T extends StringIndexedObject> {
154
154
  ): ParameterChange<T>[] {
155
155
  const parameterChangeSet = new Array<ParameterChange<T>>();
156
156
 
157
- // Filter out null and undefined values or else the diff below will not work
157
+ // Filter out null, undefined, [], and {} all treated as "no value"
158
158
  const desired = Object.fromEntries(
159
- Object.entries(desiredParameters).filter(([, v]) => v !== null && v !== undefined)
159
+ Object.entries(desiredParameters).filter(([, v]) => !ChangeSet.isAbsent(v))
160
160
  ) as Partial<T>
161
161
 
162
162
  const current = Object.fromEntries(
163
- Object.entries(currentParameters).filter(([, v]) => v !== null && v !== undefined)
163
+ Object.entries(currentParameters).filter(([, v]) => !ChangeSet.isAbsent(v))
164
164
  ) as Partial<T>
165
165
 
166
166
  for (const k of new Set([...Object.keys(current), ...Object.keys(desired)])) {
@@ -227,6 +227,13 @@ export class ChangeSet<T extends StringIndexedObject> {
227
227
  return orderOfOperations[Math.max(indexPrev, indexNext)];
228
228
  }
229
229
 
230
+ private static isAbsent(v: unknown): boolean {
231
+ if (v === null || v === undefined) return true;
232
+ if (Array.isArray(v)) return v.length === 0;
233
+ if (typeof v === 'object') return Object.keys(v as object).length === 0;
234
+ return false;
235
+ }
236
+
230
237
  private static isSame(
231
238
  desired: unknown,
232
239
  current: unknown,
@@ -35,7 +35,7 @@ export class FileUtils {
35
35
  );
36
36
 
37
37
  await fs.appendFile(Utils.getPrimaryShellRc(), lineToInsert)
38
- await CodifyCliSender.sendApplyNote(ApplyNotes.sourceShellRc());
38
+ CodifyCliSender.sendApplyNote(ApplyNotes.sourceShellRc());
39
39
 
40
40
  function addLeadingSpacer(line: string): string {
41
41
  return line.startsWith('\n')
@@ -60,7 +60,7 @@ export class FileUtils {
60
60
  ${lines.join('\n')}`)
61
61
 
62
62
  await fs.appendFile(shellRc, formattedLines)
63
- await CodifyCliSender.sendApplyNote(ApplyNotes.sourceShellRc());
63
+ CodifyCliSender.sendApplyNote(ApplyNotes.sourceShellRc());
64
64
  }
65
65
 
66
66
  /**
@@ -85,7 +85,7 @@ ${lines.join('\n')}`)
85
85
  await fs.appendFile(shellRc, `\nexport PATH=${value}:$PATH;`, { encoding: 'utf8' });
86
86
  }
87
87
 
88
- await CodifyCliSender.sendApplyNote(ApplyNotes.sourceShellRc());
88
+ CodifyCliSender.sendApplyNote(ApplyNotes.sourceShellRc());
89
89
  }
90
90
 
91
91
  static async removeFromFile(filePath: string, search: string): Promise<void> {
@@ -43,7 +43,7 @@ export function resolvePathWithVariables(pathWithVariables: string) {
43
43
  export function addVariablesToPath(pathWithoutVariables: string) {
44
44
  let result = pathWithoutVariables;
45
45
  for (const [key, value] of Object.entries(process.env)) {
46
- if (!value || !path.isAbsolute(value) || value === '/' || key === 'HOME' || key === 'PATH' || key === 'SHELL' || key === 'PWD') {
46
+ if (!value || !path.isAbsolute(value) || value === '/' || value === homeDirectory || key === 'HOME' || key === 'PATH' || key === 'SHELL' || key === 'PWD') {
47
47
  continue;
48
48
  }
49
49