@cratis/fundamentals 7.16.8 → 7.17.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/ConceptAs.ts +3 -0
- package/DateOnly.ts +112 -0
- package/Guid.ts +3 -0
- package/JsonSerializer.ts +99 -39
- package/TimeOnly.ts +108 -0
- package/TimeSpan.ts +4 -0
- package/ValueMap.ts +3 -0
- package/dist/ConceptAs.d.ts +2 -0
- package/dist/ConceptAs.d.ts.map +1 -1
- package/dist/ConceptAs.js +2 -0
- package/dist/ConceptAs.js.map +1 -1
- package/dist/DateOnly.d.ts +14 -0
- package/dist/DateOnly.d.ts.map +1 -0
- package/dist/DateOnly.js +51 -0
- package/dist/DateOnly.js.map +1 -0
- package/dist/Guid.d.ts +2 -0
- package/dist/Guid.d.ts.map +1 -1
- package/dist/Guid.js +2 -0
- package/dist/Guid.js.map +1 -1
- package/dist/JsonSerializer.d.ts +2 -0
- package/dist/JsonSerializer.d.ts.map +1 -1
- package/dist/JsonSerializer.js +42 -35
- package/dist/JsonSerializer.js.map +1 -1
- package/dist/TimeOnly.d.ts +13 -0
- package/dist/TimeOnly.d.ts.map +1 -0
- package/dist/TimeOnly.js +58 -0
- package/dist/TimeOnly.js.map +1 -0
- package/dist/TimeSpan.d.ts +2 -0
- package/dist/TimeSpan.d.ts.map +1 -1
- package/dist/TimeSpan.js +2 -0
- package/dist/TimeSpan.js.map +1 -1
- package/dist/ValueMap.d.ts +2 -0
- package/dist/ValueMap.d.ts.map +1 -1
- package/dist/ValueMap.js +2 -0
- package/dist/ValueMap.js.map +1 -1
- package/dist/cjs/ConceptAs.d.ts +2 -0
- package/dist/cjs/ConceptAs.d.ts.map +1 -1
- package/dist/cjs/ConceptAs.js +3 -0
- package/dist/cjs/ConceptAs.js.map +1 -1
- package/dist/cjs/DateOnly.d.ts +14 -0
- package/dist/cjs/DateOnly.d.ts.map +1 -0
- package/dist/cjs/DateOnly.js +56 -0
- package/dist/cjs/DateOnly.js.map +1 -0
- package/dist/cjs/Guid.d.ts +2 -0
- package/dist/cjs/Guid.d.ts.map +1 -1
- package/dist/cjs/Guid.js +3 -0
- package/dist/cjs/Guid.js.map +1 -1
- package/dist/cjs/JsonSerializer.d.ts +2 -0
- package/dist/cjs/JsonSerializer.d.ts.map +1 -1
- package/dist/cjs/JsonSerializer.js +43 -34
- package/dist/cjs/JsonSerializer.js.map +1 -1
- package/dist/cjs/TimeOnly.d.ts +13 -0
- package/dist/cjs/TimeOnly.d.ts.map +1 -0
- package/dist/cjs/TimeOnly.js +63 -0
- package/dist/cjs/TimeOnly.js.map +1 -0
- package/dist/cjs/TimeSpan.d.ts +2 -0
- package/dist/cjs/TimeSpan.d.ts.map +1 -1
- package/dist/cjs/TimeSpan.js +2 -0
- package/dist/cjs/TimeSpan.js.map +1 -1
- package/dist/cjs/ValueMap.d.ts +2 -0
- package/dist/cjs/ValueMap.d.ts.map +1 -1
- package/dist/cjs/ValueMap.js +3 -0
- package/dist/cjs/ValueMap.js.map +1 -1
- package/dist/cjs/duplicateInstanceGuard.d.ts +2 -0
- package/dist/cjs/duplicateInstanceGuard.d.ts.map +1 -0
- package/dist/cjs/duplicateInstanceGuard.js +53 -0
- package/dist/cjs/duplicateInstanceGuard.js.map +1 -0
- package/dist/cjs/geospatial/LineString.d.ts +2 -0
- package/dist/cjs/geospatial/LineString.d.ts.map +1 -1
- package/dist/cjs/geospatial/LineString.js +3 -0
- package/dist/cjs/geospatial/LineString.js.map +1 -1
- package/dist/cjs/geospatial/Point.d.ts +2 -0
- package/dist/cjs/geospatial/Point.d.ts.map +1 -1
- package/dist/cjs/geospatial/Point.js +2 -0
- package/dist/cjs/geospatial/Point.js.map +1 -1
- package/dist/cjs/geospatial/Polygon.d.ts +2 -0
- package/dist/cjs/geospatial/Polygon.d.ts.map +1 -1
- package/dist/cjs/geospatial/Polygon.js +3 -0
- package/dist/cjs/geospatial/Polygon.js.map +1 -1
- package/dist/cjs/geospatial/index.js +14 -0
- package/dist/cjs/geospatial/index.js.map +1 -0
- package/dist/cjs/index.d.ts +3 -0
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +14 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/json/DateOnlyJsonConverter.d.ts +9 -0
- package/dist/cjs/json/DateOnlyJsonConverter.d.ts.map +1 -0
- package/dist/cjs/json/DateOnlyJsonConverter.js +19 -0
- package/dist/cjs/json/DateOnlyJsonConverter.js.map +1 -0
- package/dist/cjs/json/JsonConverter.d.ts.map +1 -1
- package/dist/cjs/json/JsonConverter.js.map +1 -1
- package/dist/cjs/json/TimeOnlyJsonConverter.d.ts +9 -0
- package/dist/cjs/json/TimeOnlyJsonConverter.d.ts.map +1 -0
- package/dist/cjs/json/TimeOnlyJsonConverter.js +19 -0
- package/dist/cjs/json/TimeOnlyJsonConverter.js.map +1 -0
- package/dist/cjs/json/index.d.ts +2 -0
- package/dist/cjs/json/index.d.ts.map +1 -1
- package/dist/cjs/json/index.js +26 -0
- package/dist/cjs/json/index.js.map +1 -0
- package/dist/cjs/typeKey.d.ts +7 -0
- package/dist/cjs/typeKey.d.ts.map +1 -0
- package/dist/cjs/typeKey.js +14 -0
- package/dist/cjs/typeKey.js.map +1 -0
- package/dist/duplicateInstanceGuard.d.ts +2 -0
- package/dist/duplicateInstanceGuard.d.ts.map +1 -0
- package/dist/duplicateInstanceGuard.js +49 -0
- package/dist/duplicateInstanceGuard.js.map +1 -0
- package/dist/esm/ConceptAs.d.ts +2 -0
- package/dist/esm/ConceptAs.d.ts.map +1 -1
- package/dist/esm/ConceptAs.js +3 -0
- package/dist/esm/ConceptAs.js.map +1 -1
- package/dist/esm/DateOnly.d.ts +14 -0
- package/dist/esm/DateOnly.d.ts.map +1 -0
- package/dist/esm/DateOnly.js +54 -0
- package/dist/esm/DateOnly.js.map +1 -0
- package/dist/esm/Guid.d.ts +2 -0
- package/dist/esm/Guid.d.ts.map +1 -1
- package/dist/esm/Guid.js +3 -0
- package/dist/esm/Guid.js.map +1 -1
- package/dist/esm/JsonSerializer.d.ts +2 -0
- package/dist/esm/JsonSerializer.d.ts.map +1 -1
- package/dist/esm/JsonSerializer.js +43 -34
- package/dist/esm/JsonSerializer.js.map +1 -1
- package/dist/esm/TimeOnly.d.ts +13 -0
- package/dist/esm/TimeOnly.d.ts.map +1 -0
- package/dist/esm/TimeOnly.js +61 -0
- package/dist/esm/TimeOnly.js.map +1 -0
- package/dist/esm/TimeSpan.d.ts +2 -0
- package/dist/esm/TimeSpan.d.ts.map +1 -1
- package/dist/esm/TimeSpan.js +2 -0
- package/dist/esm/TimeSpan.js.map +1 -1
- package/dist/esm/ValueMap.d.ts +2 -0
- package/dist/esm/ValueMap.d.ts.map +1 -1
- package/dist/esm/ValueMap.js +3 -0
- package/dist/esm/ValueMap.js.map +1 -1
- package/dist/esm/duplicateInstanceGuard.d.ts +2 -0
- package/dist/esm/duplicateInstanceGuard.d.ts.map +1 -0
- package/dist/esm/duplicateInstanceGuard.js +51 -0
- package/dist/esm/duplicateInstanceGuard.js.map +1 -0
- package/dist/esm/geospatial/LineString.d.ts +2 -0
- package/dist/esm/geospatial/LineString.d.ts.map +1 -1
- package/dist/esm/geospatial/LineString.js +3 -0
- package/dist/esm/geospatial/LineString.js.map +1 -1
- package/dist/esm/geospatial/Point.d.ts +2 -0
- package/dist/esm/geospatial/Point.d.ts.map +1 -1
- package/dist/esm/geospatial/Point.js +2 -0
- package/dist/esm/geospatial/Point.js.map +1 -1
- package/dist/esm/geospatial/Polygon.d.ts +2 -0
- package/dist/esm/geospatial/Polygon.d.ts.map +1 -1
- package/dist/esm/geospatial/Polygon.js +3 -0
- package/dist/esm/geospatial/Polygon.js.map +1 -1
- package/dist/esm/geospatial/index.js +5 -0
- package/dist/esm/geospatial/index.js.map +1 -0
- package/dist/esm/index.d.ts +3 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +5 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/json/DateOnlyJsonConverter.d.ts +9 -0
- package/dist/esm/json/DateOnlyJsonConverter.d.ts.map +1 -0
- package/dist/esm/json/DateOnlyJsonConverter.js +17 -0
- package/dist/esm/json/DateOnlyJsonConverter.js.map +1 -0
- package/dist/esm/json/JsonConverter.d.ts.map +1 -1
- package/dist/esm/json/JsonConverter.js.map +1 -1
- package/dist/esm/json/TimeOnlyJsonConverter.d.ts +9 -0
- package/dist/esm/json/TimeOnlyJsonConverter.d.ts.map +1 -0
- package/dist/esm/json/TimeOnlyJsonConverter.js +17 -0
- package/dist/esm/json/TimeOnlyJsonConverter.js.map +1 -0
- package/dist/esm/json/index.d.ts +2 -0
- package/dist/esm/json/index.d.ts.map +1 -1
- package/dist/esm/json/index.js +11 -0
- package/dist/esm/json/index.js.map +1 -0
- package/dist/esm/typeKey.d.ts +7 -0
- package/dist/esm/typeKey.d.ts.map +1 -0
- package/dist/esm/typeKey.js +8 -0
- package/dist/esm/typeKey.js.map +1 -0
- package/dist/geospatial/LineString.d.ts +2 -0
- package/dist/geospatial/LineString.d.ts.map +1 -1
- package/dist/geospatial/LineString.js +2 -0
- package/dist/geospatial/LineString.js.map +1 -1
- package/dist/geospatial/Point.d.ts +2 -0
- package/dist/geospatial/Point.d.ts.map +1 -1
- package/dist/geospatial/Point.js +2 -0
- package/dist/geospatial/Point.js.map +1 -1
- package/dist/geospatial/Polygon.d.ts +2 -0
- package/dist/geospatial/Polygon.d.ts.map +1 -1
- package/dist/geospatial/Polygon.js +2 -0
- package/dist/geospatial/Polygon.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/json/DateOnlyJsonConverter.d.ts +9 -0
- package/dist/json/DateOnlyJsonConverter.d.ts.map +1 -0
- package/dist/json/DateOnlyJsonConverter.js +14 -0
- package/dist/json/DateOnlyJsonConverter.js.map +1 -0
- package/dist/json/JsonConverter.d.ts.map +1 -1
- package/dist/json/JsonConverter.js.map +1 -1
- package/dist/json/TimeOnlyJsonConverter.d.ts +9 -0
- package/dist/json/TimeOnlyJsonConverter.d.ts.map +1 -0
- package/dist/json/TimeOnlyJsonConverter.js +14 -0
- package/dist/json/TimeOnlyJsonConverter.js.map +1 -0
- package/dist/json/index.d.ts +2 -0
- package/dist/json/index.d.ts.map +1 -1
- package/dist/json/index.js +2 -0
- package/dist/json/index.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/typeKey.d.ts +7 -0
- package/dist/typeKey.d.ts.map +1 -0
- package/dist/typeKey.js +6 -0
- package/dist/typeKey.js.map +1 -0
- package/duplicateInstanceGuard.ts +114 -0
- package/for_DateOnly/when_parsing_a_calendar_date.ts +13 -0
- package/for_DateOnly/when_reading_it_west_of_utc.ts +24 -0
- package/for_JsonSerializer/RegisteredConverterTypes.ts +45 -0
- package/for_JsonSerializer/when_a_converter_is_registered_for_a_concept.ts +46 -0
- package/for_JsonSerializer/when_a_converter_is_registered_for_a_type_that_already_has_one.ts +25 -0
- package/for_JsonSerializer/when_a_converter_is_registered_for_a_type_the_package_already_converts.ts +51 -0
- package/for_JsonSerializer/when_a_converter_is_registered_for_a_value_map.ts +49 -0
- package/for_JsonSerializer/when_a_converter_is_registered_for_an_unknown_type.ts +30 -0
- package/for_JsonSerializer/when_a_type_comes_from_another_copy_of_the_package.ts +63 -0
- package/for_JsonSerializer/when_deserializing_temporal_values.ts +31 -0
- package/for_JsonSerializer/when_metadata_is_written_by_another_copy_of_the_package.ts +46 -0
- package/for_JsonSerializer/when_no_converter_is_registered_for_a_type.ts +32 -0
- package/for_JsonSerializer/when_round_tripping_a_value_map_of_primitive_values.ts +35 -0
- package/for_JsonSerializer/when_round_tripping_concepts_outside_a_declared_field.ts +39 -0
- package/for_TimeOnly/when_parsing_a_time_of_day.ts +17 -0
- package/for_TimeOnly/when_parsing_the_shapes_the_server_sends.ts +24 -0
- package/for_duplicateInstanceGuard/given/a_realm.ts +62 -0
- package/for_duplicateInstanceGuard/when_a_second_copy_is_loaded.ts +28 -0
- package/for_duplicateInstanceGuard/when_a_third_copy_is_loaded.ts +20 -0
- package/for_duplicateInstanceGuard/when_checking_the_package_manifest.ts +26 -0
- package/for_duplicateInstanceGuard/when_one_copy_is_loaded.ts +17 -0
- package/for_duplicateInstanceGuard/when_one_copy_registers_more_than_once.ts +20 -0
- package/for_duplicateInstanceGuard/when_something_else_holds_the_global_key.ts +25 -0
- package/for_duplicateInstanceGuard/when_the_runtime_has_no_console.ts +30 -0
- package/geospatial/LineString.ts +3 -0
- package/geospatial/Point.ts +3 -0
- package/geospatial/Polygon.ts +3 -0
- package/index.ts +3 -0
- package/json/DateOnlyJsonConverter.ts +28 -0
- package/json/JsonConverter.ts +5 -0
- package/json/TimeOnlyJsonConverter.ts +28 -0
- package/json/index.ts +2 -0
- package/package.json +1 -1
- package/typeKey.ts +54 -0
|
@@ -0,0 +1,28 @@
|
|
|
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 { a_realm } from './given/a_realm';
|
|
5
|
+
|
|
6
|
+
describe('when a second copy is loaded', () => {
|
|
7
|
+
let realm: a_realm;
|
|
8
|
+
|
|
9
|
+
beforeEach(async () => {
|
|
10
|
+
realm = new a_realm();
|
|
11
|
+
(await realm.loadCopy()).registerModuleInstance();
|
|
12
|
+
(await realm.loadCopy()).registerModuleInstance();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
afterEach(() => realm.release());
|
|
16
|
+
|
|
17
|
+
it('should warn exactly once', () => realm.warnings.length.should.equal(1));
|
|
18
|
+
it('should name the package', () => realm.warnings[0].should.contain('@cratis/fundamentals'));
|
|
19
|
+
it('should say how many times it was loaded', () => realm.warnings[0].should.contain('Loaded 2 times'));
|
|
20
|
+
it('should say a registered converter reaches only one copy', () => realm.warnings[0].should.contain('reaches only the copy it was registered on'));
|
|
21
|
+
it('should say instanceof breaks across the copies', () => realm.warnings[0].should.contain('instanceof against the other copy'));
|
|
22
|
+
it('should say a top level pin does not reach the nested copy', () => realm.warnings[0].should.contain('does not reach a copy nested under a dependency'));
|
|
23
|
+
it('should say how to collapse two installs', () => realm.warnings[0].should.contain('yarn dedupe @cratis/fundamentals'));
|
|
24
|
+
it('should say that dedupe is the wrong answer for one install loaded as both formats', () => realm.warnings[0].should.contain('no dedupe will fix'));
|
|
25
|
+
it('should name where the first copy was loaded from', () => realm.warnings[0].should.contain(' 1. '));
|
|
26
|
+
it('should name where the second copy was loaded from', () => realm.warnings[0].should.contain(' 2. '));
|
|
27
|
+
it('should locate the copies rather than give up on the stack', () => realm.warnings[0].should.not.contain('unknown location'));
|
|
28
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
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 { a_realm } from './given/a_realm';
|
|
5
|
+
|
|
6
|
+
describe('when a third copy is loaded', () => {
|
|
7
|
+
let realm: a_realm;
|
|
8
|
+
|
|
9
|
+
beforeEach(async () => {
|
|
10
|
+
realm = new a_realm();
|
|
11
|
+
(await realm.loadCopy()).registerModuleInstance();
|
|
12
|
+
(await realm.loadCopy()).registerModuleInstance();
|
|
13
|
+
(await realm.loadCopy()).registerModuleInstance();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
afterEach(() => realm.release());
|
|
17
|
+
|
|
18
|
+
it('should still warn exactly once', () => realm.warnings.length.should.equal(1));
|
|
19
|
+
it('should report the count it knew when it warned', () => realm.warnings[0].should.contain('Loaded 2 times'));
|
|
20
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
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 { readFileSync } from 'node:fs';
|
|
5
|
+
import { dirname, join } from 'node:path';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
|
|
8
|
+
// The guard registers each copy as a load-bearing side effect at module scope, so a bundler must never be told
|
|
9
|
+
// it is free to drop it. Declaring `"sideEffects": false` is the one manifest change that would disable
|
|
10
|
+
// duplicate detection without breaking anything: no build fails, no other spec notices, and a duplicated
|
|
11
|
+
// package quietly goes back to being invisible - which is the failure the guard exists to make visible.
|
|
12
|
+
//
|
|
13
|
+
// duplicateInstanceGuard.ts asks for this in a comment. A comment does not survive a dependency bump or a
|
|
14
|
+
// bundle-size pass, so this is what actually holds the line.
|
|
15
|
+
describe('when checking the package manifest', () => {
|
|
16
|
+
const manifest = JSON.parse(
|
|
17
|
+
readFileSync(join(dirname(fileURLToPath(import.meta.url)), '..', 'package.json'), 'utf-8')) as {
|
|
18
|
+
sideEffects?: unknown;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// Absent is correct: a bundler then treats the package as side-effectful. An explicit `true` is fine too.
|
|
22
|
+
// Only `false` is the problem, so only `false` is rejected.
|
|
23
|
+
const declaresNoSideEffects = manifest.sideEffects === false;
|
|
24
|
+
|
|
25
|
+
it('should not declare the package free of side effects', () => declaresNoSideEffects.should.be.false);
|
|
26
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
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 { a_realm } from './given/a_realm';
|
|
5
|
+
|
|
6
|
+
describe('when one copy is loaded', () => {
|
|
7
|
+
let realm: a_realm;
|
|
8
|
+
|
|
9
|
+
beforeEach(async () => {
|
|
10
|
+
realm = new a_realm();
|
|
11
|
+
(await realm.loadCopy()).registerModuleInstance();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
afterEach(() => realm.release());
|
|
15
|
+
|
|
16
|
+
it('should not warn', () => realm.warnings.should.be.empty);
|
|
17
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
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 { a_realm } from './given/a_realm';
|
|
5
|
+
|
|
6
|
+
describe('when one copy registers more than once', () => {
|
|
7
|
+
let realm: a_realm;
|
|
8
|
+
|
|
9
|
+
beforeEach(async () => {
|
|
10
|
+
realm = new a_realm();
|
|
11
|
+
const copy = await realm.loadCopy();
|
|
12
|
+
copy.registerModuleInstance();
|
|
13
|
+
copy.registerModuleInstance();
|
|
14
|
+
copy.registerModuleInstance();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
afterEach(() => realm.release());
|
|
18
|
+
|
|
19
|
+
it('should not warn', () => realm.warnings.should.be.empty);
|
|
20
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
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 { a_realm } from './given/a_realm';
|
|
5
|
+
|
|
6
|
+
describe('when something else holds the global key', () => {
|
|
7
|
+
let realm: a_realm;
|
|
8
|
+
|
|
9
|
+
beforeEach(async () => {
|
|
10
|
+
realm = new a_realm();
|
|
11
|
+
|
|
12
|
+
// The bookkeeping is a cross-version contract, so a copy of a different version of this package
|
|
13
|
+
// may have written the key first - in a shape this one does not recognize. Detection has to
|
|
14
|
+
// survive that rather than take the process down over its own diagnostic.
|
|
15
|
+
(globalThis as Record<symbol, unknown>)[Symbol.for('@cratis/fundamentals.loadedInstances')] = 'written by something else';
|
|
16
|
+
|
|
17
|
+
(await realm.loadCopy()).registerModuleInstance();
|
|
18
|
+
(await realm.loadCopy()).registerModuleInstance();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
afterEach(() => realm.release());
|
|
22
|
+
|
|
23
|
+
it('should still report the duplicate', () => realm.warnings.length.should.equal(1));
|
|
24
|
+
it('should count only the copies it registered itself', () => realm.warnings[0].should.contain('Loaded 2 times'));
|
|
25
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
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 { a_realm } from './given/a_realm';
|
|
5
|
+
|
|
6
|
+
/** A console that is missing the method the report writes to. */
|
|
7
|
+
type ConsoleWithoutWarn = { warn?: (...values: unknown[]) => void };
|
|
8
|
+
|
|
9
|
+
describe('when the runtime has no console', () => {
|
|
10
|
+
let realm: a_realm;
|
|
11
|
+
let error: unknown;
|
|
12
|
+
|
|
13
|
+
beforeEach(async () => {
|
|
14
|
+
realm = new a_realm();
|
|
15
|
+
(console as unknown as ConsoleWithoutWarn).warn = undefined;
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
(await realm.loadCopy()).registerModuleInstance();
|
|
19
|
+
(await realm.loadCopy()).registerModuleInstance();
|
|
20
|
+
} catch (thrown) {
|
|
21
|
+
error = thrown;
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
afterEach(() => realm.release());
|
|
26
|
+
|
|
27
|
+
// A diagnostic that takes the process down when it has nowhere to write is worse than the silence
|
|
28
|
+
// it was added to break.
|
|
29
|
+
it('should not throw', () => (error === undefined).should.be.true);
|
|
30
|
+
});
|
package/geospatial/LineString.ts
CHANGED
|
@@ -2,11 +2,14 @@
|
|
|
2
2
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
3
|
|
|
4
4
|
import { Point } from './Point';
|
|
5
|
+
import { typeKey } from '../typeKey';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Represents a line string composed of two or more points.
|
|
8
9
|
*/
|
|
9
10
|
export class LineString {
|
|
11
|
+
static readonly [typeKey] = 'LineString';
|
|
12
|
+
|
|
10
13
|
coordinates!: Point[];
|
|
11
14
|
|
|
12
15
|
/**
|
package/geospatial/Point.ts
CHANGED
|
@@ -2,11 +2,14 @@
|
|
|
2
2
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
3
|
|
|
4
4
|
import { field } from '../fieldDecorator';
|
|
5
|
+
import { typeKey } from '../typeKey';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Represents a geographic point with longitude and latitude.
|
|
8
9
|
*/
|
|
9
10
|
export class Point {
|
|
11
|
+
static readonly [typeKey] = 'Point';
|
|
12
|
+
|
|
10
13
|
@field(Number)
|
|
11
14
|
longitude!: number;
|
|
12
15
|
|
package/geospatial/Polygon.ts
CHANGED
|
@@ -2,11 +2,14 @@
|
|
|
2
2
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
3
|
|
|
4
4
|
import { LinearRing } from './LinearRing';
|
|
5
|
+
import { typeKey } from '../typeKey';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Represents a polygon with an outer shell and optional holes.
|
|
8
9
|
*/
|
|
9
10
|
export class Polygon {
|
|
11
|
+
static readonly [typeKey] = 'Polygon';
|
|
12
|
+
|
|
10
13
|
shell!: LinearRing;
|
|
11
14
|
holes!: LinearRing[];
|
|
12
15
|
|
package/index.ts
CHANGED
|
@@ -7,13 +7,16 @@ export * from './geospatial';
|
|
|
7
7
|
export * from './json';
|
|
8
8
|
export * from './Field';
|
|
9
9
|
export * from './Fields';
|
|
10
|
+
export * from './DateOnly';
|
|
10
11
|
export * from './Guid';
|
|
11
12
|
export * from './IEquatable';
|
|
12
13
|
export * from './PropertyAccessor';
|
|
13
14
|
export * from './PropertyAccessorDescriptor';
|
|
14
15
|
export * from './PropertyPathResolverProxyHandler';
|
|
16
|
+
export * from './TimeOnly';
|
|
15
17
|
export * from './TimeSpan';
|
|
16
18
|
export * from './ValueMap';
|
|
19
|
+
export * from './typeKey';
|
|
17
20
|
export * from './fieldDecorator';
|
|
18
21
|
export * from './derivedTypeDecorator';
|
|
19
22
|
export * from './JsonSerializer';
|
|
@@ -0,0 +1,28 @@
|
|
|
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 { Constructor } from '../Constructor';
|
|
5
|
+
import { DateOnly } from '../DateOnly';
|
|
6
|
+
import { JsonConverter } from './JsonConverter';
|
|
7
|
+
|
|
8
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* JSON converter for DateOnly type.
|
|
12
|
+
*/
|
|
13
|
+
export class DateOnlyJsonConverter extends JsonConverter<DateOnly> {
|
|
14
|
+
/** @inheritdoc */
|
|
15
|
+
get type(): Constructor<DateOnly> {
|
|
16
|
+
return DateOnly;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** @inheritdoc */
|
|
20
|
+
read(value: any): DateOnly {
|
|
21
|
+
return DateOnly.parse(value.toString());
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** @inheritdoc */
|
|
25
|
+
write(value: any): any {
|
|
26
|
+
return value?.toString() ?? '';
|
|
27
|
+
}
|
|
28
|
+
}
|
package/json/JsonConverter.ts
CHANGED
|
@@ -19,6 +19,11 @@ export abstract class JsonConverter<T = any> {
|
|
|
19
19
|
* Determines whether the converter can convert the specified type.
|
|
20
20
|
* @param {Constructor} typeToConvert - The type to check.
|
|
21
21
|
* @returns {boolean} True if the converter can convert the type; otherwise, false.
|
|
22
|
+
* @deprecated Not called by `JsonSerializer` and scheduled for removal in the next major version.
|
|
23
|
+
* A converter is resolved by the type it declares through {@link type}, which is what this would
|
|
24
|
+
* answer anyway, so overriding it has no effect - register a converter per type instead. Wiring it
|
|
25
|
+
* into the lookup is not the plan either: it would force a scan of every converter on the path
|
|
26
|
+
* taken by every plain object, to serve a case a second registration already covers.
|
|
22
27
|
*/
|
|
23
28
|
canConvert(typeToConvert: Constructor): boolean {
|
|
24
29
|
return typeToConvert === this.type;
|
|
@@ -0,0 +1,28 @@
|
|
|
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 { Constructor } from '../Constructor';
|
|
5
|
+
import { TimeOnly } from '../TimeOnly';
|
|
6
|
+
import { JsonConverter } from './JsonConverter';
|
|
7
|
+
|
|
8
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* JSON converter for TimeOnly type.
|
|
12
|
+
*/
|
|
13
|
+
export class TimeOnlyJsonConverter extends JsonConverter<TimeOnly> {
|
|
14
|
+
/** @inheritdoc */
|
|
15
|
+
get type(): Constructor<TimeOnly> {
|
|
16
|
+
return TimeOnly;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** @inheritdoc */
|
|
20
|
+
read(value: any): TimeOnly {
|
|
21
|
+
return TimeOnly.parse(value.toString());
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** @inheritdoc */
|
|
25
|
+
write(value: any): any {
|
|
26
|
+
return value?.toString() ?? '';
|
|
27
|
+
}
|
|
28
|
+
}
|
package/json/index.ts
CHANGED
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
|
|
4
4
|
export * from './JsonConverter';
|
|
5
5
|
export * from './DateJsonConverter';
|
|
6
|
+
export * from './DateOnlyJsonConverter';
|
|
6
7
|
export * from './GuidJsonConverter';
|
|
8
|
+
export * from './TimeOnlyJsonConverter';
|
|
7
9
|
export * from './TimeSpanJsonConverter';
|
|
8
10
|
export * from './PointJsonConverter';
|
|
9
11
|
export * from './LineStringJsonConverter';
|
package/package.json
CHANGED
package/typeKey.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
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 { Constructor } from './Constructor';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Identifies a type this package converts, by name rather than by the class object itself.
|
|
8
|
+
*
|
|
9
|
+
* `JsonSerializer` recognized a type by comparing constructors, which holds until a second copy of this
|
|
10
|
+
* package is loaded into the same realm - a nested install, or one install reached as both ESM and
|
|
11
|
+
* CommonJS. Each copy then had its own `Guid`, `ConceptAs` and `ValueMap` class objects and neither
|
|
12
|
+
* recognized the other's, so a `Guid` crossing that boundary was written as an object rather than a
|
|
13
|
+
* string, and a concept as `{"value": ...}` rather than its underlying value. Both silently.
|
|
14
|
+
*
|
|
15
|
+
* A name survives the boundary where a class object does not: the key is a registered symbol, so every
|
|
16
|
+
* copy resolves the same one, and the values are plain strings.
|
|
17
|
+
*
|
|
18
|
+
* The key is deliberately a symbol rather than a plain static: a string static would appear in
|
|
19
|
+
* `Object.keys(Guid)` and could be shadowed by an unrelated member of the same name.
|
|
20
|
+
*/
|
|
21
|
+
export const typeKey: unique symbol = Symbol.for('@cratis/fundamentals.typeKey');
|
|
22
|
+
|
|
23
|
+
/** A type carrying the key. Only this package's own convertible types declare one. */
|
|
24
|
+
type KeyedType = Constructor & { readonly [typeKey]?: string };
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Gets the key a type declares itself, ignoring any inherited from a base type.
|
|
28
|
+
*
|
|
29
|
+
* Converter lookup uses this, because a key is inherited: a consumer's `class CustomerId extends
|
|
30
|
+
* ConceptAs<string>` reports `ConceptAs`, and registering a converter under that would take the place
|
|
31
|
+
* of concept handling for every concept in the application.
|
|
32
|
+
* @param {Constructor} type The type to read the key from.
|
|
33
|
+
* @returns {string | undefined} The declared key, or undefined when the type declares none.
|
|
34
|
+
*/
|
|
35
|
+
export const declaredTypeKey = (type: Constructor | undefined): string | undefined =>
|
|
36
|
+
type && Object.prototype.hasOwnProperty.call(type, typeKey) ? (type as KeyedType)[typeKey] : undefined;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Gets the key a type carries, whether it declares the key or inherits it from a base type.
|
|
40
|
+
*
|
|
41
|
+
* This is what makes a concept recognizable across copies: the key is a static, and a static is
|
|
42
|
+
* inherited through the constructor's prototype chain, so every type deriving from `ConceptAs` reports
|
|
43
|
+
* `ConceptAs` without anything having to compare class objects.
|
|
44
|
+
* @param {Constructor} type The type to read the key from.
|
|
45
|
+
* @returns {string | undefined} The key carried, or undefined when the type carries none.
|
|
46
|
+
*/
|
|
47
|
+
export const typeKeyOf = (type: Constructor | undefined): string | undefined =>
|
|
48
|
+
type ? (type as KeyedType)[typeKey] : undefined;
|
|
49
|
+
|
|
50
|
+
/** The key every type deriving from `ConceptAs` carries. */
|
|
51
|
+
export const conceptAsTypeKey = 'ConceptAs';
|
|
52
|
+
|
|
53
|
+
/** The key `ValueMap` declares. */
|
|
54
|
+
export const valueMapTypeKey = 'ValueMap';
|