@dotinc/ogre 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. package/.tap/processinfo/{86009ac9-5819-45bf-b53f-0448d4b7ef56.json → 21e0110c-397c-4c38-a84a-89c55de41e7e.json} +6 -6
  2. package/.tap/processinfo/{9e67801c-9248-4793-994c-a9b4f59ebc4f.json → 36da4059-ca5e-4b70-a993-7f99f94f7588.json} +6 -6
  3. package/.tap/processinfo/{31ca0767-30d5-4c1b-aab3-1a456fd51558.json → 4aa37d99-4038-4033-9d38-beea00272892.json} +6 -6
  4. package/.tap/processinfo/{65d66b6b-127f-4a35-b64a-5b0ac1df524c.json → 51b87298-fd44-42a9-b0f8-d4533e58fd1c.json} +30 -30
  5. package/.tap/processinfo/{f9080426-4139-4a9a-a59f-ccbfa4cadf7d.json → 75459f57-2341-476b-8576-587b93699d8b.json} +6 -6
  6. package/.tap/processinfo/{77376985-2ddf-4b47-9631-c637b0ecbcdd.json → 9656a0a0-72ab-4ea7-abb7-7415cd3842b4.json} +22 -22
  7. package/.tap/processinfo/{52683acc-91e1-4d54-9715-1cbaf91964a7.json → d1b618cb-a921-4f1c-ae4a-6bca359b5909.json} +6 -6
  8. package/.tap/test-results/src/branch.test.ts.tap +6 -6
  9. package/.tap/test-results/src/checkout.test.ts.tap +9 -9
  10. package/.tap/test-results/src/commit.test.ts.tap +24 -18
  11. package/.tap/test-results/src/merge.test.ts.tap +2 -2
  12. package/.tap/test-results/src/repository.test.ts.tap +31 -31
  13. package/.tap/test-results/src/tag.test.ts.tap +3 -3
  14. package/.tap/test-results/src/utils.test.ts.tap +7 -7
  15. package/CHANGELOG.md +20 -0
  16. package/lib/git2json.d.ts +1 -1
  17. package/lib/interfaces.d.ts +1 -4
  18. package/lib/repository.d.ts +13 -7
  19. package/lib/repository.js +35 -6
  20. package/package.json +2 -2
  21. package/src/commit.test.ts +17 -1
  22. package/src/commit.ts +7 -7
  23. package/src/git2json.ts +1 -1
  24. package/src/interfaces.ts +1 -2
  25. package/src/repository.test.ts +39 -43
  26. package/src/repository.ts +48 -11
package/src/interfaces.ts CHANGED
@@ -7,8 +7,7 @@ export interface Reference {
7
7
  value: string;
8
8
  }
9
9
 
10
- export interface History<T extends { [k: string]: any }> {
11
- original: T;
10
+ export interface History {
12
11
  refs: Map<string, Reference>;
13
12
  commits: Commit[];
14
13
  }
@@ -163,7 +163,7 @@ test("history", async (t) => {
163
163
  original: co,
164
164
  refs: new Map<string, Reference>(),
165
165
  commits: [],
166
- } as History<ComplexObject>,
166
+ } as History,
167
167
  }),
168
168
  {
169
169
  message: "unreachable: 'HEAD' is not present",
@@ -201,17 +201,29 @@ test("status", async (t) => {
201
201
  const [repo] = await getBaseline({ name: "base name" });
202
202
  repo.data.name = "changed name";
203
203
  const dirtyState = repo.status();
204
- t.equal(dirtyState.length, 1, "Status doesn't contain changes");
204
+ t.equal(
205
+ dirtyState.length,
206
+ 1,
207
+ `Status doesn't contain the expected # of changes: ${JSON.stringify(dirtyState)}`,
208
+ );
205
209
  });
206
- t.test("reading status doesn't clean observer", async (t) => {
210
+ t.test("reading status shouldn't clean observer", async (t) => {
207
211
  const [repo] = await getBaseline({ name: "base name" });
208
212
  repo.data.name = "changed name";
209
213
  const dirtyState = repo.status();
210
- t.equal(dirtyState.length, 1, "Status doesn't contain changes");
214
+ t.equal(
215
+ dirtyState.length,
216
+ 1,
217
+ `Status doesn't contain the expected # of changes: ${JSON.stringify(dirtyState)}`,
218
+ );
211
219
 
212
220
  const dirtyState2 = repo.status();
213
- t.equal(dirtyState2.length, 1, "Status doesn't contain changes");
214
- t.match(dirtyState2, dirtyState2, "different pending changes??");
221
+ t.equal(
222
+ dirtyState2.length,
223
+ 1,
224
+ `Status doesn't contain the expected # of changes: ${JSON.stringify(dirtyState)}`,
225
+ );
226
+ t.match(dirtyState2, dirtyState2, "why different pending changes??");
215
227
  });
216
228
  t.test("after commit no change", async (t) => {
217
229
  const [repo] = await getBaseline();
@@ -275,43 +287,27 @@ test("apply", async (t) => {
275
287
  t.match(dirtyState, patches, "It should have the right changes");
276
288
  });
277
289
 
278
- t.test(
279
- "patch for undefined props doesn't work as expected https://github.com/Starcounter-Jack/JSON-Patch/issues/280",
280
- async (t) => {
281
- const [repo] = await getBaseline();
282
- const cleanState = repo.status();
283
- t.match(cleanState, [], "Shouldn't have pending changes");
284
-
285
- const targetState: ComplexObject = {
286
- uuid: undefined,
287
- description: undefined,
288
- name: "a name",
289
- nested: [],
290
- };
291
- const patches = compare(repo.data, targetState);
292
- // this should record changes on the observer
293
- const err = repo.apply(patches);
294
- t.match(
295
- err,
296
- {
297
- name: "OPERATION_PATH_UNRESOLVABLE",
298
- operation: {
299
- op: "replace",
300
- path: "/name",
301
- value: "a name",
302
- },
303
- },
304
- "Failed to apply patch",
305
- );
306
- t.notMatch(
307
- repo.data,
308
- targetState,
309
- "The final state shoould not match up, because patch failed",
310
- );
311
- const dirtyState = repo.status();
312
- t.equal(dirtyState.length, 0, "Status doesn't contain changes");
313
- },
314
- );
290
+ t.test("patch for undefined props with workaround", async (t) => {
291
+ // solution for workaround from: https://github.com/Starcounter-Jack/JSON-Patch/issues/280
292
+ const [repo] = await getBaseline();
293
+ const cleanState = repo.status();
294
+ t.match(cleanState, [], "Shouldn't have pending changes");
295
+
296
+ const targetState: ComplexObject = {
297
+ uuid: undefined,
298
+ description: undefined,
299
+ name: "a name",
300
+ nested: [],
301
+ };
302
+ const patches = compare(repo.data, targetState);
303
+ // this should record changes on the observer
304
+ const err = repo.apply(patches);
305
+ t.equal(err, undefined, "Failed to apply patch");
306
+
307
+ t.match(repo.data, targetState, "The final state should match up");
308
+ const dirtyState = repo.status();
309
+ t.equal(dirtyState.length, 1, "Status should contain 1 change");
310
+ });
315
311
 
316
312
  t.test("multiple patches", async (t) => {
317
313
  const [repo] = await getBaseline({ name: "base name" });
package/src/repository.ts CHANGED
@@ -24,13 +24,13 @@ export const REFS_HEAD_KEY = "HEAD";
24
24
  export const REFS_MAIN_KEY = `${headsRefPathPrefix}main`;
25
25
 
26
26
  export interface RepositoryOptions<T extends { [k: string]: any }> {
27
- history?: History<T>;
27
+ history?: History;
28
28
  }
29
29
 
30
30
  export interface RepositoryObject<T extends { [k: string]: any }> {
31
31
  data: T;
32
32
 
33
- getHistory(): History<T>;
33
+ getHistory(): History;
34
34
 
35
35
  /**
36
36
  * Returns the diff between the current HEAD and provided shaish expression
@@ -65,7 +65,7 @@ export interface RepositoryObject<T extends { [k: string]: any }> {
65
65
 
66
66
  createBranch(name: string): string;
67
67
 
68
- merge(source: string | RepositoryObject<T> | History<T>): string;
68
+ merge(source: string | RepositoryObject<T> | History): string;
69
69
 
70
70
  /**
71
71
  * Branch returns the current branch name
@@ -80,6 +80,11 @@ export interface RepositoryObject<T extends { [k: string]: any }> {
80
80
  * @param shaish
81
81
  */
82
82
  reset(mode?: "soft" | "hard", shaish?: string): void;
83
+
84
+ /**
85
+ * Returns the remote references from the initialization of the repository
86
+ */
87
+ remote(): Map<string, Reference> | undefined;
83
88
  }
84
89
 
85
90
  /**
@@ -88,10 +93,13 @@ export interface RepositoryObject<T extends { [k: string]: any }> {
88
93
  export class Repository<T extends { [k: PropertyKey]: any }>
89
94
  implements RepositoryObject<T>
90
95
  {
91
- constructor(obj: T, options: RepositoryOptions<T>) {
96
+ constructor(obj: Partial<T>, options: RepositoryOptions<T>) {
97
+ // FIXME: move this to refs/remote as git would do?
98
+ this.remoteRefs = options.history?.refs;
92
99
  this.original = deepClone(obj);
93
- this.data = obj;
94
- this.observer = observe(obj);
100
+ // store js ref, so obj can still be modified without going through repo.data
101
+ this.data = obj as T;
102
+ this.observer = observe(obj as T);
95
103
  this.refs =
96
104
  options.history?.refs ??
97
105
  new Map<string, Reference>([
@@ -119,11 +127,18 @@ export class Repository<T extends { [k: PropertyKey]: any }>
119
127
 
120
128
  data: T;
121
129
 
130
+ // stores the remote state upon initialization
131
+ private readonly remoteRefs: Map<string, Reference> | undefined;
132
+
122
133
  private observer: Observer<T>;
123
134
 
124
135
  private readonly refs: Map<string, Reference>;
125
136
  private readonly commits: Commit[];
126
137
 
138
+ remote(): Map<string, Reference> | undefined {
139
+ return this.remoteRefs;
140
+ }
141
+
127
142
  private moveTo(commit: Commit) {
128
143
  const targetTree = treeToObject(commit.tree);
129
144
  const patchToTarget = compare(this.data, targetTree);
@@ -136,9 +151,32 @@ export class Repository<T extends { [k: PropertyKey]: any }>
136
151
  }
137
152
 
138
153
  apply(patch: Operation[]): JsonPatchError | undefined {
139
- const err = validate(patch, this.data);
154
+ const p = deepClone(patch) as Operation[];
155
+ const err = validate(p, this.data);
140
156
  if (err) {
141
- return err;
157
+ // credit goes to @NicBright
158
+ // https://github.com/Starcounter-Jack/JSON-Patch/issues/280#issuecomment-1980435509
159
+ if (err.name === "OPERATION_PATH_UNRESOLVABLE") {
160
+ if (err.operation.op === "replace") {
161
+ // can happen e.g. when states are like this:
162
+ // from.x = undefined
163
+ // to.x = 'something'
164
+ const op = p.find(
165
+ (o) => o.path === err.operation.path && o.op === err.operation.op,
166
+ );
167
+ if (!op) return err;
168
+ // try it once more with operation 'add' instead
169
+ op.op = "add";
170
+ return this.apply(p);
171
+ } else if (err.operation.op === "remove") {
172
+ // Can happen e.g. when states are like this:
173
+ // from.entity.reason = null;
174
+ // to.entity.reason = undefined;
175
+ // we don't do anything in this case because "to" is already in a good state!
176
+ }
177
+ } else {
178
+ return err;
179
+ }
142
180
  }
143
181
  applyPatch(this.data, patch);
144
182
  // const changed = patch.reduce(applyReducer, this.data);
@@ -347,9 +385,8 @@ export class Repository<T extends { [k: PropertyKey]: any }>
347
385
  return commitsList;
348
386
  }
349
387
 
350
- getHistory(): History<T> {
388
+ getHistory(): History {
351
389
  return {
352
- original: this.original,
353
390
  refs: new Map(this.refs),
354
391
  commits: this.collectCommits(),
355
392
  };
@@ -370,7 +407,7 @@ export class Repository<T extends { [k: PropertyKey]: any }>
370
407
 
371
408
  // endregion
372
409
 
373
- merge(source: string | RepositoryObject<T> | History<T>): string {
410
+ merge(source: string | RepositoryObject<T> | History): string {
374
411
  // inspiration
375
412
  // http://think-like-a-git.net
376
413
  // also check isomorphic-git