@cratis/arc 22.161.0 → 22.163.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.
- package/commands/Command.ts +14 -4
- package/commands/for_Command/GeneratedCommand.ts +32 -0
- package/commands/for_Command/when_executing/with_a_failed_submission.ts +40 -0
- package/commands/for_Command/when_executing/with_an_edit_during_submission.ts +33 -0
- package/commands/for_Command/when_executing/with_generated_property_accessors.ts +54 -0
- package/commands/for_Command/when_executing/with_submitted_falsy_values.ts +30 -0
- package/commands/for_Command/when_setting_initial_values_from_current_values.ts +28 -0
- package/dist/cjs/commands/Command.d.ts +1 -0
- package/dist/cjs/commands/Command.d.ts.map +1 -1
- package/dist/cjs/commands/Command.js +13 -4
- package/dist/cjs/commands/Command.js.map +1 -1
- package/dist/cjs/queries/QueryInstanceCache.d.ts +1 -0
- package/dist/cjs/queries/QueryInstanceCache.d.ts.map +1 -1
- package/dist/cjs/queries/QueryInstanceCache.js +6 -2
- package/dist/cjs/queries/QueryInstanceCache.js.map +1 -1
- package/dist/esm/commands/Command.d.ts +1 -0
- package/dist/esm/commands/Command.d.ts.map +1 -1
- package/dist/esm/commands/Command.js +13 -4
- package/dist/esm/commands/Command.js.map +1 -1
- package/dist/esm/commands/for_Command/GeneratedCommand.d.ts +15 -0
- package/dist/esm/commands/for_Command/GeneratedCommand.d.ts.map +1 -0
- package/dist/esm/commands/for_Command/GeneratedCommand.js +24 -0
- package/dist/esm/commands/for_Command/GeneratedCommand.js.map +1 -0
- package/dist/esm/commands/for_Command/when_executing/with_a_failed_submission.d.ts +2 -0
- package/dist/esm/commands/for_Command/when_executing/with_a_failed_submission.d.ts.map +1 -0
- package/dist/esm/commands/for_Command/when_executing/with_a_failed_submission.js +34 -0
- package/dist/esm/commands/for_Command/when_executing/with_a_failed_submission.js.map +1 -0
- package/dist/esm/commands/for_Command/when_executing/with_an_edit_during_submission.d.ts +2 -0
- package/dist/esm/commands/for_Command/when_executing/with_an_edit_during_submission.d.ts.map +1 -0
- package/dist/esm/commands/for_Command/when_executing/with_an_edit_during_submission.js +26 -0
- package/dist/esm/commands/for_Command/when_executing/with_an_edit_during_submission.js.map +1 -0
- package/dist/esm/commands/for_Command/when_executing/with_generated_property_accessors.d.ts +2 -0
- package/dist/esm/commands/for_Command/when_executing/with_generated_property_accessors.d.ts.map +1 -0
- package/dist/esm/commands/for_Command/when_executing/with_generated_property_accessors.js +44 -0
- package/dist/esm/commands/for_Command/when_executing/with_generated_property_accessors.js.map +1 -0
- package/dist/esm/commands/for_Command/when_executing/with_submitted_falsy_values.d.ts +2 -0
- package/dist/esm/commands/for_Command/when_executing/with_submitted_falsy_values.d.ts.map +1 -0
- package/dist/esm/commands/for_Command/when_executing/with_submitted_falsy_values.js +24 -0
- package/dist/esm/commands/for_Command/when_executing/with_submitted_falsy_values.js.map +1 -0
- package/dist/esm/commands/for_Command/when_setting_initial_values_from_current_values.d.ts +2 -0
- package/dist/esm/commands/for_Command/when_setting_initial_values_from_current_values.d.ts.map +1 -0
- package/dist/esm/commands/for_Command/when_setting_initial_values_from_current_values.js +24 -0
- package/dist/esm/commands/for_Command/when_setting_initial_values_from_current_values.js.map +1 -0
- package/dist/esm/queries/QueryInstanceCache.d.ts +1 -0
- package/dist/esm/queries/QueryInstanceCache.d.ts.map +1 -1
- package/dist/esm/queries/QueryInstanceCache.js +6 -2
- package/dist/esm/queries/QueryInstanceCache.js.map +1 -1
- package/dist/esm/queries/for_QueryInstanceCache/when_setting_last_result/with_changed_metadata.d.ts +2 -0
- package/dist/esm/queries/for_QueryInstanceCache/when_setting_last_result/with_changed_metadata.d.ts.map +1 -0
- package/dist/esm/queries/for_QueryInstanceCache/when_setting_last_result/with_changed_metadata.js +51 -0
- package/dist/esm/queries/for_QueryInstanceCache/when_setting_last_result/with_changed_metadata.js.map +1 -0
- package/dist/esm/queries/for_QueryInstanceCache/when_setting_last_result/with_equivalent_metadata.d.ts +2 -0
- package/dist/esm/queries/for_QueryInstanceCache/when_setting_last_result/with_equivalent_metadata.d.ts.map +1 -0
- package/dist/esm/queries/for_QueryInstanceCache/when_setting_last_result/with_equivalent_metadata.js +27 -0
- package/dist/esm/queries/for_QueryInstanceCache/when_setting_last_result/with_equivalent_metadata.js.map +1 -0
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/queries/QueryInstanceCache.ts +17 -4
- package/queries/for_QueryInstanceCache/when_setting_last_result/with_changed_metadata.ts +58 -0
- package/queries/for_QueryInstanceCache/when_setting_last_result/with_equivalent_metadata.ts +39 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@cratis/arc",
|
|
4
|
-
"version": "22.
|
|
4
|
+
"version": "22.163.0",
|
|
5
5
|
"description": "TypeScript command, query, validation, identity, and messaging runtime for Cratis Arc — the CQRS application framework for ASP.NET Core — used by generated proxies.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"cratis",
|
|
@@ -7,6 +7,7 @@ import type {
|
|
|
7
7
|
CacheEntryDiagnostics,
|
|
8
8
|
} from './ObservableQueryDiagnosticsSnapshot.js';
|
|
9
9
|
import { reconcileQueryData } from './reconcileQueryData.js';
|
|
10
|
+
import { deepEqual } from '../deepEqual.js';
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Represents a key that uniquely identifies a query instance in the cache, based on the query type name and its serialized arguments.
|
|
@@ -203,13 +204,12 @@ export class QueryInstanceCache {
|
|
|
203
204
|
entry.lastResult = reconciled as QueryResultWithState<unknown>;
|
|
204
205
|
|
|
205
206
|
// Reconciliation returns the previous data reference when nothing changed, so identity is
|
|
206
|
-
// enough to detect it. Data
|
|
207
|
+
// enough to detect it. Data and result metadata affect what the user sees; changeSet is
|
|
207
208
|
// ephemeral and does not independently require a notification.
|
|
208
209
|
if (
|
|
209
210
|
previousResult !== undefined &&
|
|
210
|
-
previousResult.
|
|
211
|
-
previousResult
|
|
212
|
-
previousResult.data === reconciled.data
|
|
211
|
+
previousResult.data === reconciled.data &&
|
|
212
|
+
this.hasEquivalentMetadata(previousResult, reconciled)
|
|
213
213
|
) {
|
|
214
214
|
return;
|
|
215
215
|
}
|
|
@@ -253,6 +253,19 @@ export class QueryInstanceCache {
|
|
|
253
253
|
);
|
|
254
254
|
}
|
|
255
255
|
|
|
256
|
+
private hasEquivalentMetadata<TDataType>(result: QueryResultWithState<TDataType>, previous: QueryResultWithState<TDataType>): boolean {
|
|
257
|
+
return result.isPerforming === previous.isPerforming
|
|
258
|
+
&& result.isSuccess === previous.isSuccess
|
|
259
|
+
&& result.isReady === previous.isReady
|
|
260
|
+
&& result.isAuthorized === previous.isAuthorized
|
|
261
|
+
&& result.isValid === previous.isValid
|
|
262
|
+
&& result.hasExceptions === previous.hasExceptions
|
|
263
|
+
&& result.exceptionStackTrace === previous.exceptionStackTrace
|
|
264
|
+
&& deepEqual(result.paging, previous.paging)
|
|
265
|
+
&& deepEqual(result.exceptionMessages, previous.exceptionMessages)
|
|
266
|
+
&& deepEqual(result.validationResults, previous.validationResults);
|
|
267
|
+
}
|
|
268
|
+
|
|
256
269
|
/**
|
|
257
270
|
* Registers a listener that is invoked whenever the cached result for the given key changes.
|
|
258
271
|
* @template TDataType The type of data returned by the query.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import sinon from 'sinon';
|
|
5
|
+
import { QueryInstanceCache } from '../../QueryInstanceCache.js';
|
|
6
|
+
import { QueryResultWithState } from '../../QueryResultWithState.js';
|
|
7
|
+
import { PagingInfo } from '../../PagingInfo.js';
|
|
8
|
+
import { ValidationResult } from '../../../validation/ValidationResult.js';
|
|
9
|
+
import { ValidationResultSeverity } from '../../../validation/ValidationResultSeverity.js';
|
|
10
|
+
import { ValidationResultReason } from '../../../validation/ValidationResultReason.js';
|
|
11
|
+
|
|
12
|
+
type Item = { id: string };
|
|
13
|
+
const validation = new ValidationResult(ValidationResultSeverity.Error, 'first', ['name'], { limit: 1 });
|
|
14
|
+
const changes: [string, Partial<QueryResultWithState<Item[]>>][] = [
|
|
15
|
+
['paging total items', { paging: { ...new PagingInfo(), totalItems: 2 } }],
|
|
16
|
+
['paging total pages', { paging: { ...new PagingInfo(), totalPages: 2 } }],
|
|
17
|
+
['paging page', { paging: { ...new PagingInfo(), page: 1 } }],
|
|
18
|
+
['paging size', { paging: { ...new PagingInfo(), size: 10 } }],
|
|
19
|
+
['authorization', { isAuthorized: false }],
|
|
20
|
+
['validity', { isValid: false }],
|
|
21
|
+
['exception flag', { hasExceptions: true }],
|
|
22
|
+
['exception messages', { exceptionMessages: ['second'] }],
|
|
23
|
+
['exception stack trace', { exceptionStackTrace: 'second stack' }],
|
|
24
|
+
['performing state', { isPerforming: true }],
|
|
25
|
+
['validation message', { validationResults: [{ ...validation, message: 'second' }] }],
|
|
26
|
+
['validation severity', { validationResults: [{ ...validation, severity: ValidationResultSeverity.Warning }] }],
|
|
27
|
+
['validation members', { validationResults: [{ ...validation, members: ['other'] }] }],
|
|
28
|
+
['validation state', { validationResults: [{ ...validation, state: { limit: 2 } }] }],
|
|
29
|
+
['validation reason', { validationResults: [{ ...validation, reason: ValidationResultReason.ConstraintViolation }] }],
|
|
30
|
+
['validation reason detail', { validationResults: [{ ...validation, reasonDetail: 'required' }] }]
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
for (const [field, change] of changes) {
|
|
34
|
+
describe(`when setting the last result with changed ${field}`, () => {
|
|
35
|
+
const key = 'MyQuery::';
|
|
36
|
+
let cache: QueryInstanceCache;
|
|
37
|
+
let listener: sinon.SinonStub;
|
|
38
|
+
let first: QueryResultWithState<Item[]>;
|
|
39
|
+
|
|
40
|
+
beforeEach(() => {
|
|
41
|
+
cache = new QueryInstanceCache();
|
|
42
|
+
cache.getOrCreate(key, () => ({}));
|
|
43
|
+
first = Object.assign(QueryResultWithState.empty([{ id: 'a' }]), {
|
|
44
|
+
isSuccess: false,
|
|
45
|
+
validationResults: [validation],
|
|
46
|
+
exceptionMessages: ['first']
|
|
47
|
+
});
|
|
48
|
+
cache.setLastResult(key, first);
|
|
49
|
+
listener = sinon.stub();
|
|
50
|
+
cache.addListener(key, listener);
|
|
51
|
+
const next = Object.assign(QueryResultWithState.empty([{ id: 'a' }]), first, { data: [{ id: 'a' }] }, change);
|
|
52
|
+
cache.setLastResult(key, next);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('should notify the listener', () => listener.calledOnce.should.be.true);
|
|
56
|
+
it('should preserve unchanged row references', () => cache.getLastResult<Item[]>(key)!.data[0].should.equal(first.data[0]));
|
|
57
|
+
});
|
|
58
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import sinon from 'sinon';
|
|
5
|
+
import { QueryInstanceCache } from '../../QueryInstanceCache.js';
|
|
6
|
+
import { QueryResultWithState } from '../../QueryResultWithState.js';
|
|
7
|
+
import { PagingInfo } from '../../PagingInfo.js';
|
|
8
|
+
import { ValidationResult } from '../../../validation/ValidationResult.js';
|
|
9
|
+
import { ValidationResultSeverity } from '../../../validation/ValidationResultSeverity.js';
|
|
10
|
+
|
|
11
|
+
for (const changeSetOnly of [false, true]) {
|
|
12
|
+
describe(`when setting the last result with ${changeSetOnly ? 'only a changed change set' : 'equivalent metadata'}`, () => {
|
|
13
|
+
let listener: sinon.SinonStub;
|
|
14
|
+
|
|
15
|
+
const result = () => new QueryResultWithState(
|
|
16
|
+
[{ id: 'a' }],
|
|
17
|
+
{ ...new PagingInfo(), totalItems: 1, totalPages: 1 },
|
|
18
|
+
false, true, false,
|
|
19
|
+
[new ValidationResult(ValidationResultSeverity.Error, 'invalid', ['name'], { limit: 1 })],
|
|
20
|
+
true, ['failed'], 'stack', false
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
const cache = new QueryInstanceCache();
|
|
25
|
+
const key = 'MyQuery::';
|
|
26
|
+
cache.getOrCreate(key, () => ({}));
|
|
27
|
+
cache.setLastResult(key, result());
|
|
28
|
+
listener = sinon.stub();
|
|
29
|
+
cache.addListener(key, listener);
|
|
30
|
+
const next = result();
|
|
31
|
+
if (changeSetOnly) {
|
|
32
|
+
Object.assign(next, { changeSet: { added: [{ id: 'a' }], replaced: [], removed: [] } });
|
|
33
|
+
}
|
|
34
|
+
cache.setLastResult(key, next);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('should suppress the notification', () => listener.notCalled.should.be.true);
|
|
38
|
+
});
|
|
39
|
+
}
|