@cratis/fundamentals 7.16.7 → 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 +108 -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 +45 -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 +46 -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 +46 -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_object_with_absent_collection.ts +36 -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
package/ConceptAs.ts
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
// Copyright (c) Cratis. All rights reserved.
|
|
2
2
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
import { typeKey } from './typeKey';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Expresses a Concept as another type, usually a primitive such as string or number.
|
|
6
7
|
* @template T The type of the underlying value.
|
|
7
8
|
*/
|
|
8
9
|
export abstract class ConceptAs<T> {
|
|
10
|
+
static readonly [typeKey] = 'ConceptAs';
|
|
11
|
+
|
|
9
12
|
/**
|
|
10
13
|
* Initializes a new instance of ConceptAs.
|
|
11
14
|
* @param {T} value The value to initialize the concept with.
|
package/DateOnly.ts
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
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 { field } from './fieldDecorator';
|
|
5
|
+
import { typeKey } from './typeKey';
|
|
6
|
+
|
|
7
|
+
const dateOnlyRegex = /^(\d{4})-(\d{2})-(\d{2})$/;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Represents a date with no time and no time zone - a day on a calendar.
|
|
11
|
+
* @remarks
|
|
12
|
+
* Deliberately not a JavaScript `Date`. A `Date` is an instant, and a calendar date is not one: turning
|
|
13
|
+
* `2026-05-12` into a `Date` produces UTC midnight, which every browser-local getter west of UTC reads back as
|
|
14
|
+
* the 11th. That is a wrong answer that looks right, and it is only wrong for users in some time zones - which is
|
|
15
|
+
* how it survives development in others.
|
|
16
|
+
*
|
|
17
|
+
* Holding the three parts the server sent means there is no instant to convert and nothing to shift. Where a
|
|
18
|
+
* `Date` is genuinely wanted - to feed a date picker, or to do arithmetic - {@link toDate} constructs one in the
|
|
19
|
+
* local time zone, and the choice is then visible at the call site making it.
|
|
20
|
+
*/
|
|
21
|
+
export class DateOnly {
|
|
22
|
+
static readonly [typeKey] = 'DateOnly';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The year.
|
|
26
|
+
*/
|
|
27
|
+
@field(Number)
|
|
28
|
+
year!: number;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The month, 1 through 12.
|
|
32
|
+
*/
|
|
33
|
+
@field(Number)
|
|
34
|
+
month!: number;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* The day of the month, 1 through 31.
|
|
38
|
+
*/
|
|
39
|
+
@field(Number)
|
|
40
|
+
day!: number;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Creates a {@link DateOnly} from its parts.
|
|
44
|
+
* @param year The year.
|
|
45
|
+
* @param month The month, 1 through 12.
|
|
46
|
+
* @param day The day of the month.
|
|
47
|
+
* @returns The {@link DateOnly}.
|
|
48
|
+
*/
|
|
49
|
+
static from(year: number, month: number, day: number): DateOnly {
|
|
50
|
+
const dateOnly = new DateOnly();
|
|
51
|
+
dateOnly.year = year;
|
|
52
|
+
dateOnly.month = month;
|
|
53
|
+
dateOnly.day = day;
|
|
54
|
+
return dateOnly;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Parses the ISO-8601 calendar date the server sends, `yyyy-MM-dd`.
|
|
59
|
+
* @param value The value to parse.
|
|
60
|
+
* @returns The {@link DateOnly}.
|
|
61
|
+
*/
|
|
62
|
+
static parse(value: string): DateOnly {
|
|
63
|
+
const match = dateOnlyRegex.exec(value);
|
|
64
|
+
if (match === null) {
|
|
65
|
+
throw new Error(`Invalid DateOnly format: ${value}`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return DateOnly.from(parseInt(match[1], 10), parseInt(match[2], 10), parseInt(match[3], 10));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Creates a {@link DateOnly} for the calendar date a {@link Date} falls on in the local time zone.
|
|
73
|
+
* @param date The date to take the calendar date of.
|
|
74
|
+
* @returns The {@link DateOnly}.
|
|
75
|
+
* @remarks
|
|
76
|
+
* Reads the local parts rather than the UTC ones, because the calendar date a moment falls on is a question
|
|
77
|
+
* only a time zone can answer, and the local one is the one the person looking at the screen is in.
|
|
78
|
+
*/
|
|
79
|
+
static fromDate(date: Date): DateOnly {
|
|
80
|
+
return DateOnly.from(date.getFullYear(), date.getMonth() + 1, date.getDate());
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Converts to a {@link Date} at midnight in the local time zone.
|
|
85
|
+
* @returns The {@link Date}.
|
|
86
|
+
* @remarks
|
|
87
|
+
* Local rather than UTC, so the date a caller reads back with `getDate()` is the one they started with. This
|
|
88
|
+
* invents a time that was never sent, which is why it is a method to call rather than what the value is.
|
|
89
|
+
*/
|
|
90
|
+
toDate(): Date {
|
|
91
|
+
return new Date(this.year, this.month - 1, this.day);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Gets the ISO-8601 representation, `yyyy-MM-dd` - the same form the server sent.
|
|
96
|
+
* @returns The string.
|
|
97
|
+
*/
|
|
98
|
+
toString(): string {
|
|
99
|
+
const month = this.month.toString().padStart(2, '0');
|
|
100
|
+
const day = this.day.toString().padStart(2, '0');
|
|
101
|
+
return `${this.year.toString().padStart(4, '0')}-${month}-${day}`;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Determines whether this is the same calendar date as another.
|
|
106
|
+
* @param other The other date.
|
|
107
|
+
* @returns True when they are the same date.
|
|
108
|
+
*/
|
|
109
|
+
equals(other: DateOnly | undefined | null): boolean {
|
|
110
|
+
return !!other && this.year === other.year && this.month === other.month && this.day === other.day;
|
|
111
|
+
}
|
|
112
|
+
}
|
package/Guid.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
3
|
|
|
4
4
|
import { IEquatable } from './IEquatable';
|
|
5
|
+
import { typeKey } from './typeKey';
|
|
5
6
|
|
|
6
7
|
const guidRegex = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
|
|
7
8
|
const lookUpTable: string[] = [];
|
|
@@ -22,6 +23,8 @@ const getString = (num: number) => {
|
|
|
22
23
|
* @class Guid
|
|
23
24
|
*/
|
|
24
25
|
export class Guid implements IEquatable {
|
|
26
|
+
static readonly [typeKey] = 'Guid';
|
|
27
|
+
|
|
25
28
|
|
|
26
29
|
/**
|
|
27
30
|
* Gets an empty {Guid}
|
package/JsonSerializer.ts
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
// Copyright (c) Cratis. All rights reserved.
|
|
2
2
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
3
|
|
|
4
|
-
import { ConceptAs } from './ConceptAs';
|
|
5
4
|
import { Constructor } from './Constructor';
|
|
6
5
|
import { DerivedType } from './DerivedType';
|
|
6
|
+
import { registerModuleInstance } from './duplicateInstanceGuard';
|
|
7
7
|
import { Field } from './Field';
|
|
8
8
|
import { Fields } from './Fields';
|
|
9
|
+
import { conceptAsTypeKey, declaredTypeKey, typeKeyOf, valueMapTypeKey } from './typeKey';
|
|
9
10
|
import { ValueMap } from './ValueMap';
|
|
10
11
|
import {
|
|
11
12
|
JsonConverter,
|
|
12
13
|
DateJsonConverter,
|
|
14
|
+
DateOnlyJsonConverter,
|
|
13
15
|
GuidJsonConverter,
|
|
16
|
+
TimeOnlyJsonConverter,
|
|
14
17
|
TimeSpanJsonConverter,
|
|
15
18
|
PointJsonConverter,
|
|
16
19
|
LineStringJsonConverter,
|
|
@@ -22,10 +25,19 @@ import {
|
|
|
22
25
|
|
|
23
26
|
type typeSerializer = (value: any) => any;
|
|
24
27
|
|
|
28
|
+
// A second copy of this package in the same realm builds its own registry here, and brings its own
|
|
29
|
+
// class objects. Convertible values still cross between the copies, because a type is recognized by
|
|
30
|
+
// the key it declares - but a converter registered on one copy reaches only that one, and a version
|
|
31
|
+
// pinned at the top level never reaches a nested copy. Announce it where the state that cannot be
|
|
32
|
+
// shared is built.
|
|
33
|
+
registerModuleInstance();
|
|
34
|
+
|
|
25
35
|
// Initialize converters
|
|
26
36
|
const converters: JsonConverter[] = [
|
|
27
37
|
new DateJsonConverter(),
|
|
38
|
+
new DateOnlyJsonConverter(),
|
|
28
39
|
new GuidJsonConverter(),
|
|
40
|
+
new TimeOnlyJsonConverter(),
|
|
29
41
|
new TimeSpanJsonConverter(),
|
|
30
42
|
new PointJsonConverter(),
|
|
31
43
|
new LineStringJsonConverter(),
|
|
@@ -33,14 +45,44 @@ const converters: JsonConverter[] = [
|
|
|
33
45
|
new ValueMapJsonConverter()
|
|
34
46
|
];
|
|
35
47
|
|
|
36
|
-
//
|
|
48
|
+
// Converters are found by the key a type declares, and only by the constructor when it declares none.
|
|
49
|
+
// Keying on the constructor alone is what made a second copy of this package silently fatal: each copy
|
|
50
|
+
// brings its own Guid, ConceptAs and ValueMap class objects, so neither recognized the other's. A key
|
|
51
|
+
// crosses that boundary; a class object does not. A consumer's own type has no key and is found by
|
|
52
|
+
// constructor, which is exact and needs nothing to cross.
|
|
53
|
+
const keyedConverters: Map<string, JsonConverter> = new Map<string, JsonConverter>();
|
|
37
54
|
const typeConverters: Map<Constructor, JsonConverter> = new Map<Constructor, JsonConverter>();
|
|
38
|
-
const typeSerializers: Map<Constructor, JsonConverter> = new Map<Constructor, JsonConverter>();
|
|
39
55
|
|
|
40
|
-
|
|
56
|
+
const registerConverterFor = (converter: JsonConverter) => {
|
|
57
|
+
const key = declaredTypeKey(converter.type);
|
|
58
|
+
if (key !== undefined) {
|
|
59
|
+
keyedConverters.set(key, converter);
|
|
60
|
+
}
|
|
61
|
+
|
|
41
62
|
typeConverters.set(converter.type, converter);
|
|
42
|
-
|
|
43
|
-
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
converters.forEach(registerConverterFor);
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Finds the converter registered for a type, if any.
|
|
69
|
+
* @param {Constructor} type The type to convert.
|
|
70
|
+
* @returns {JsonConverter | undefined} The converter, or undefined when the type has none.
|
|
71
|
+
*/
|
|
72
|
+
const converterFor = (type: Constructor | undefined): JsonConverter | undefined => {
|
|
73
|
+
if (!type) return undefined;
|
|
74
|
+
|
|
75
|
+
const key = declaredTypeKey(type);
|
|
76
|
+
const keyed = key === undefined ? undefined : keyedConverters.get(key);
|
|
77
|
+
return keyed ?? typeConverters.get(type);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Checks whether a type is a ValueMap, including one from another copy of this package.
|
|
82
|
+
* @param {Constructor} type The type to check.
|
|
83
|
+
* @returns {boolean} True when the type is a ValueMap.
|
|
84
|
+
*/
|
|
85
|
+
const isValueMap = (type: Constructor | undefined): boolean => declaredTypeKey(type) === valueMapTypeKey;
|
|
44
86
|
|
|
45
87
|
// Add primitive type converters that don't need a full JsonConverter class
|
|
46
88
|
const primitiveConverters: Map<Constructor, typeSerializer> = new Map<Constructor, typeSerializer>([
|
|
@@ -59,28 +101,19 @@ const primitiveSerializers: Map<Constructor, typeSerializer> = new Map<Construct
|
|
|
59
101
|
* Checks if a constructor is a ConceptAs type.
|
|
60
102
|
* @param {Constructor} type The constructor to check.
|
|
61
103
|
* @returns {boolean} True if the type extends ConceptAs.
|
|
104
|
+
* @remarks
|
|
105
|
+
* Reads the key rather than walking the prototype chain looking for this copy's ConceptAs. The key is a
|
|
106
|
+
* static and a static is inherited, so every type deriving from ConceptAs carries it - including one
|
|
107
|
+
* deriving from another copy's ConceptAs, which a prototype walk could never recognize.
|
|
62
108
|
*/
|
|
63
|
-
const isConceptAs = (type: Constructor): boolean =>
|
|
64
|
-
if (!type || !type.prototype) return false;
|
|
65
|
-
|
|
66
|
-
// Check if the prototype chain includes ConceptAs
|
|
67
|
-
let proto = type.prototype;
|
|
68
|
-
while (proto) {
|
|
69
|
-
// Check if this prototype is ConceptAs.prototype
|
|
70
|
-
if (proto === ConceptAs.prototype) {
|
|
71
|
-
return true;
|
|
72
|
-
}
|
|
73
|
-
proto = Object.getPrototypeOf(proto);
|
|
74
|
-
}
|
|
75
|
-
return false;
|
|
76
|
-
};
|
|
109
|
+
const isConceptAs = (type: Constructor): boolean => typeKeyOf(type) === conceptAsTypeKey;
|
|
77
110
|
|
|
78
111
|
const serializeValueForType = (type: Constructor, value: any) => {
|
|
79
112
|
if (!value) return value;
|
|
80
113
|
|
|
81
114
|
// If it's a ConceptAs instance, unwrap it and serialize the inner value recursively
|
|
82
115
|
// This follows the C# pattern: recognize as concept, unwrap, then call serializer
|
|
83
|
-
if (value
|
|
116
|
+
if (isConceptAs(value.constructor)) {
|
|
84
117
|
const innerValue = value.value;
|
|
85
118
|
// Recursively serialize the inner value to handle complex types
|
|
86
119
|
// Use .constructor directly which works reliably for both primitives and objects
|
|
@@ -88,8 +121,8 @@ const serializeValueForType = (type: Constructor, value: any) => {
|
|
|
88
121
|
}
|
|
89
122
|
|
|
90
123
|
// Check if there's a registered converter
|
|
91
|
-
|
|
92
|
-
|
|
124
|
+
const converter = converterFor(type);
|
|
125
|
+
if (converter) {
|
|
93
126
|
return converter.write(value);
|
|
94
127
|
}
|
|
95
128
|
|
|
@@ -108,8 +141,8 @@ const deserializeValueFromType = (type: Constructor, value: any) => {
|
|
|
108
141
|
}
|
|
109
142
|
|
|
110
143
|
// Check if there's a registered converter
|
|
111
|
-
|
|
112
|
-
|
|
144
|
+
const converter = converterFor(type);
|
|
145
|
+
if (converter) {
|
|
113
146
|
return converter.read(value);
|
|
114
147
|
}
|
|
115
148
|
|
|
@@ -122,7 +155,7 @@ const deserializeValueFromType = (type: Constructor, value: any) => {
|
|
|
122
155
|
};
|
|
123
156
|
|
|
124
157
|
const deserializeValueFromField = (field: Field, value: any) => {
|
|
125
|
-
if (field.type
|
|
158
|
+
if (isValueMap(field.type)) {
|
|
126
159
|
return deserializeValueMapFromField(field, value);
|
|
127
160
|
}
|
|
128
161
|
|
|
@@ -132,8 +165,8 @@ const deserializeValueFromField = (field: Field, value: any) => {
|
|
|
132
165
|
}
|
|
133
166
|
|
|
134
167
|
// Check if there's a registered converter
|
|
135
|
-
|
|
136
|
-
|
|
168
|
+
const converter = converterFor(field.type);
|
|
169
|
+
if (converter) {
|
|
137
170
|
return converter.read(value);
|
|
138
171
|
}
|
|
139
172
|
|
|
@@ -186,8 +219,8 @@ const deserializeMapKey = (keyType: Constructor, key: string): any => {
|
|
|
186
219
|
}
|
|
187
220
|
|
|
188
221
|
// Check if there's a converter for this type
|
|
189
|
-
|
|
190
|
-
|
|
222
|
+
const converter = converterFor(keyType);
|
|
223
|
+
if (converter) {
|
|
191
224
|
return converter.read(key);
|
|
192
225
|
}
|
|
193
226
|
|
|
@@ -199,13 +232,11 @@ const deserializeMapValue = (valueType: Constructor | undefined, value: any): an
|
|
|
199
232
|
return value;
|
|
200
233
|
}
|
|
201
234
|
|
|
202
|
-
//
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
return JsonSerializer.deserialize(valueType, JSON.stringify(value));
|
|
235
|
+
// A map value is an ordinary value of its declared type, so read it the way any other value of that
|
|
236
|
+
// type is read. Resolving it here instead left out the concept and primitive cases, and the
|
|
237
|
+
// primitive one lost data without a sound: a string value went through JsonSerializer.deserialize,
|
|
238
|
+
// which built an empty String object because a primitive has no fields to populate.
|
|
239
|
+
return deserializeValueFromType(valueType, value);
|
|
209
240
|
};
|
|
210
241
|
|
|
211
242
|
const deserializeValueMapFromField = (field: Field, value: any): ValueMap<any, any> => {
|
|
@@ -227,8 +258,16 @@ const deserializeValueMapFromField = (field: Field, value: any): ValueMap<any, a
|
|
|
227
258
|
};
|
|
228
259
|
|
|
229
260
|
const convertTypesOnInstance = (instance: any) => {
|
|
261
|
+
// A concept unwraps to its underlying value wherever it is reached, not only when it is reached
|
|
262
|
+
// through a declared field. An array element and a map value arrive here rather than at
|
|
263
|
+
// serializeValueForType, and without this they were written as the object a concept happens to be -
|
|
264
|
+
// {"value": ...} where the receiver declared the underlying type.
|
|
265
|
+
if (isConceptAs(instance.constructor)) {
|
|
266
|
+
return serializeValueForType(instance.constructor as Constructor, instance);
|
|
267
|
+
}
|
|
268
|
+
|
|
230
269
|
// Check if there's a converter for this type
|
|
231
|
-
if (
|
|
270
|
+
if (converterFor(instance.constructor)) {
|
|
232
271
|
return serializeValueForType(instance.constructor, instance);
|
|
233
272
|
}
|
|
234
273
|
|
|
@@ -277,6 +316,27 @@ ValueMapJsonConverter.setHelpers(serializeMapKey, convertTypesOnInstance);
|
|
|
277
316
|
export class JsonSerializer {
|
|
278
317
|
static readonly DerivedTypeIdProperty: string = "_derivedTypeId";
|
|
279
318
|
|
|
319
|
+
/**
|
|
320
|
+
* Register a converter for the type it declares through its `type` property.
|
|
321
|
+
* @param {JsonConverter} converter The converter to register.
|
|
322
|
+
* @remarks
|
|
323
|
+
* The converter is used for both directions - `read` on the way in, `write` on the way out - and
|
|
324
|
+
* takes the place of any converter already registered for the same type, including the built-in
|
|
325
|
+
* ones. Replacing a built-in changes the payloads the application produces and consumes, which is
|
|
326
|
+
* the caller's to decide; nothing here second-guesses it.
|
|
327
|
+
*
|
|
328
|
+
* Two shapes resolve ahead of the converters, so a converter registered for them is not reached:
|
|
329
|
+
*
|
|
330
|
+
* - A type deriving from `ConceptAs` is unwrapped to its underlying value in both directions, and
|
|
331
|
+
* converted as that value's type. Register for the underlying type to reach a concept.
|
|
332
|
+
* - A `ValueMap` is read back from the declaring field's generic arguments rather than through a
|
|
333
|
+
* converter. Note the asymmetry: writing a `ValueMap` *does* go through the registered converter,
|
|
334
|
+
* so replacing that one changes only the outbound half.
|
|
335
|
+
*/
|
|
336
|
+
static registerConverter(converter: JsonConverter): void {
|
|
337
|
+
registerConverterFor(converter);
|
|
338
|
+
}
|
|
339
|
+
|
|
280
340
|
/**
|
|
281
341
|
* Serialize with strong type information.
|
|
282
342
|
* @param {*} value The value to serialize.
|
|
@@ -318,7 +378,7 @@ export class JsonSerializer {
|
|
|
318
378
|
static deserializeFromInstance<TResult extends object>(targetType: Constructor<TResult>, instance: any): TResult {
|
|
319
379
|
const fields = Fields.getFieldsForType(targetType as Constructor);
|
|
320
380
|
|
|
321
|
-
if (
|
|
381
|
+
if (converterFor(targetType)) {
|
|
322
382
|
return deserializeValueFromType(targetType, instance);
|
|
323
383
|
}
|
|
324
384
|
|
|
@@ -331,6 +391,15 @@ export class JsonSerializer {
|
|
|
331
391
|
} else {
|
|
332
392
|
value = deserializeValueFromField(field, value);
|
|
333
393
|
}
|
|
394
|
+
} else if (field.enumerable && value === undefined) {
|
|
395
|
+
// A collection that is absent from the payload deserializes to an empty one rather than to
|
|
396
|
+
// undefined. The declared type says the field is an array, and a producer that leaves an
|
|
397
|
+
// empty collection out - which the Chronicle sink does deliberately, so that a parallel
|
|
398
|
+
// replay cannot overwrite children a sibling event already wrote - would otherwise hand back
|
|
399
|
+
// a value contradicting its own type, putting a null guard on every reader of every
|
|
400
|
+
// collection. An explicit null is left alone: that is the producer saying "no collection"
|
|
401
|
+
// rather than "an empty one".
|
|
402
|
+
value = [];
|
|
334
403
|
}
|
|
335
404
|
|
|
336
405
|
deserialized[field.name] = value;
|
package/TimeOnly.ts
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
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 { field } from './fieldDecorator';
|
|
5
|
+
import { typeKey } from './typeKey';
|
|
6
|
+
|
|
7
|
+
const timeOnlyRegex = /^(\d{2}):(\d{2})(?::(\d{2}))?(?:\.(\d{1,7}))?$/;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Represents a time of day with no date and no time zone.
|
|
11
|
+
* @remarks
|
|
12
|
+
* Deliberately not a JavaScript `Date`. A `Date` needs a date, and a time of day has none: `new Date('14:30:45')`
|
|
13
|
+
* is not an instant pinned to some default day, it is `Invalid Date`. The value is destroyed outright rather than
|
|
14
|
+
* merely shifted, which makes this the starker of the two temporal cases.
|
|
15
|
+
*/
|
|
16
|
+
export class TimeOnly {
|
|
17
|
+
static readonly [typeKey] = 'TimeOnly';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The hour, 0 through 23.
|
|
21
|
+
*/
|
|
22
|
+
@field(Number)
|
|
23
|
+
hour!: number;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The minute, 0 through 59.
|
|
27
|
+
*/
|
|
28
|
+
@field(Number)
|
|
29
|
+
minute!: number;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* The second, 0 through 59.
|
|
33
|
+
*/
|
|
34
|
+
@field(Number)
|
|
35
|
+
second!: number;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The millisecond, 0 through 999.
|
|
39
|
+
*/
|
|
40
|
+
@field(Number)
|
|
41
|
+
millisecond!: number;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Creates a {@link TimeOnly} from its parts.
|
|
45
|
+
* @param hour The hour.
|
|
46
|
+
* @param minute The minute.
|
|
47
|
+
* @param second The second.
|
|
48
|
+
* @param millisecond The millisecond.
|
|
49
|
+
* @returns The {@link TimeOnly}.
|
|
50
|
+
*/
|
|
51
|
+
static from(hour: number, minute: number, second: number = 0, millisecond: number = 0): TimeOnly {
|
|
52
|
+
const timeOnly = new TimeOnly();
|
|
53
|
+
timeOnly.hour = hour;
|
|
54
|
+
timeOnly.minute = minute;
|
|
55
|
+
timeOnly.second = second;
|
|
56
|
+
timeOnly.millisecond = millisecond;
|
|
57
|
+
return timeOnly;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Parses the ISO-8601 time of day the server sends, `HH:mm`, `HH:mm:ss` or `HH:mm:ss.fffffff`.
|
|
62
|
+
* @param value The value to parse.
|
|
63
|
+
* @returns The {@link TimeOnly}.
|
|
64
|
+
* @remarks
|
|
65
|
+
* The seconds and the fraction are both optional because the server omits them when they are zero, and the
|
|
66
|
+
* fraction carries up to seven digits where JavaScript holds three - so it is truncated rather than rounded,
|
|
67
|
+
* which keeps a parse followed by a render from moving the value forward.
|
|
68
|
+
*/
|
|
69
|
+
static parse(value: string): TimeOnly {
|
|
70
|
+
const match = timeOnlyRegex.exec(value);
|
|
71
|
+
if (match === null) {
|
|
72
|
+
throw new Error(`Invalid TimeOnly format: ${value}`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const fraction = match[4] ? match[4].padEnd(3, '0').substring(0, 3) : '0';
|
|
76
|
+
|
|
77
|
+
return TimeOnly.from(
|
|
78
|
+
parseInt(match[1], 10),
|
|
79
|
+
parseInt(match[2], 10),
|
|
80
|
+
match[3] ? parseInt(match[3], 10) : 0,
|
|
81
|
+
parseInt(fraction, 10));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Gets the ISO-8601 representation, `HH:mm:ss` - or `HH:mm:ss.fff` when there is a fractional part.
|
|
86
|
+
* @returns The string.
|
|
87
|
+
*/
|
|
88
|
+
toString(): string {
|
|
89
|
+
const hour = this.hour.toString().padStart(2, '0');
|
|
90
|
+
const minute = this.minute.toString().padStart(2, '0');
|
|
91
|
+
const second = this.second.toString().padStart(2, '0');
|
|
92
|
+
const time = `${hour}:${minute}:${second}`;
|
|
93
|
+
return this.millisecond > 0 ? `${time}.${this.millisecond.toString().padStart(3, '0')}` : time;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Determines whether this is the same time of day as another.
|
|
98
|
+
* @param other The other time.
|
|
99
|
+
* @returns True when they are the same time.
|
|
100
|
+
*/
|
|
101
|
+
equals(other: TimeOnly | undefined | null): boolean {
|
|
102
|
+
return !!other &&
|
|
103
|
+
this.hour === other.hour &&
|
|
104
|
+
this.minute === other.minute &&
|
|
105
|
+
this.second === other.second &&
|
|
106
|
+
this.millisecond === other.millisecond;
|
|
107
|
+
}
|
|
108
|
+
}
|
package/TimeSpan.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// Copyright (c) Cratis. All rights reserved.
|
|
2
2
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
3
|
|
|
4
|
+
import { typeKey } from './typeKey';
|
|
5
|
+
|
|
4
6
|
import { field } from './fieldDecorator';
|
|
5
7
|
|
|
6
8
|
const timeSpanRegex = /^(-)?(?:(\d+)\.)?(\d{1,2}):(\d{2}):(\d{2})(?:\.(\d{1,7}))?$/;
|
|
@@ -9,6 +11,8 @@ const timeSpanRegex = /^(-)?(?:(\d+)\.)?(\d{1,2}):(\d{2}):(\d{2})(?:\.(\d{1,7}))
|
|
|
9
11
|
* Represents a time interval.
|
|
10
12
|
*/
|
|
11
13
|
export class TimeSpan {
|
|
14
|
+
static readonly [typeKey] = 'TimeSpan';
|
|
15
|
+
|
|
12
16
|
|
|
13
17
|
@field(Number)
|
|
14
18
|
ticks!: number;
|
package/ValueMap.ts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
// Copyright (c) Cratis. All rights reserved.
|
|
2
2
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
import { typeKey } from './typeKey';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Represents a map that compares object keys by value.
|
|
6
7
|
*/
|
|
7
8
|
export class ValueMap<TKey, TValue> {
|
|
9
|
+
static readonly [typeKey] = 'ValueMap';
|
|
10
|
+
|
|
8
11
|
private readonly _entries: Array<[TKey, TValue]> = [];
|
|
9
12
|
|
|
10
13
|
/**
|
package/dist/ConceptAs.d.ts
CHANGED
package/dist/ConceptAs.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConceptAs.d.ts","sourceRoot":"","sources":["../ConceptAs.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ConceptAs.d.ts","sourceRoot":"","sources":["../ConceptAs.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,8BAAsB,SAAS,CAAC,CAAC;IAOjB,QAAQ,CAAC,KAAK,EAAE,CAAC;IAN7B,MAAM,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,eAAe;gBAMnB,KAAK,EAAE,CAAC;IAM7B,OAAO,IAAI,CAAC;IAQZ,QAAQ,IAAI,MAAM;CAGrB"}
|
package/dist/ConceptAs.js
CHANGED
package/dist/ConceptAs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConceptAs.js","sourceRoot":"","sources":["../ConceptAs.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ConceptAs.js","sourceRoot":"","sources":["../ConceptAs.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,MAAM,OAAgB,SAAS;IAON;IANrB,MAAM,CAAU,CAAC,OAAO,CAAC,GAAG,WAAW,CAAC;IAMxC,YAAqB,KAAQ;QAAR,UAAK,GAAL,KAAK,CAAG;IAAG,CAAC;IAMjC,OAAO;QACH,OAAO,IAAI,CAAC,KAAK,CAAC;IACtB,CAAC;IAMD,QAAQ;QACJ,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC9B,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { typeKey } from './typeKey';
|
|
2
|
+
export declare class DateOnly {
|
|
3
|
+
static readonly [typeKey] = "DateOnly";
|
|
4
|
+
year: number;
|
|
5
|
+
month: number;
|
|
6
|
+
day: number;
|
|
7
|
+
static from(year: number, month: number, day: number): DateOnly;
|
|
8
|
+
static parse(value: string): DateOnly;
|
|
9
|
+
static fromDate(date: Date): DateOnly;
|
|
10
|
+
toDate(): Date;
|
|
11
|
+
toString(): string;
|
|
12
|
+
equals(other: DateOnly | undefined | null): boolean;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=DateOnly.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DateOnly.d.ts","sourceRoot":"","sources":["../DateOnly.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAgBpC,qBAAa,QAAQ;IACjB,MAAM,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,cAAc;IAMvC,IAAI,EAAG,MAAM,CAAC;IAMd,KAAK,EAAG,MAAM,CAAC;IAMf,GAAG,EAAG,MAAM,CAAC;IASb,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,QAAQ;IAa/D,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ;IAiBrC,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,QAAQ;IAWrC,MAAM,IAAI,IAAI;IAQd,QAAQ,IAAI,MAAM;IAWlB,MAAM,CAAC,KAAK,EAAE,QAAQ,GAAG,SAAS,GAAG,IAAI,GAAG,OAAO;CAGtD"}
|
package/dist/DateOnly.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { __decorate, __metadata } from "tslib";
|
|
2
|
+
import { field } from './fieldDecorator';
|
|
3
|
+
import { typeKey } from './typeKey';
|
|
4
|
+
const dateOnlyRegex = /^(\d{4})-(\d{2})-(\d{2})$/;
|
|
5
|
+
export class DateOnly {
|
|
6
|
+
static [typeKey] = 'DateOnly';
|
|
7
|
+
year;
|
|
8
|
+
month;
|
|
9
|
+
day;
|
|
10
|
+
static from(year, month, day) {
|
|
11
|
+
const dateOnly = new DateOnly();
|
|
12
|
+
dateOnly.year = year;
|
|
13
|
+
dateOnly.month = month;
|
|
14
|
+
dateOnly.day = day;
|
|
15
|
+
return dateOnly;
|
|
16
|
+
}
|
|
17
|
+
static parse(value) {
|
|
18
|
+
const match = dateOnlyRegex.exec(value);
|
|
19
|
+
if (match === null) {
|
|
20
|
+
throw new Error(`Invalid DateOnly format: ${value}`);
|
|
21
|
+
}
|
|
22
|
+
return DateOnly.from(parseInt(match[1], 10), parseInt(match[2], 10), parseInt(match[3], 10));
|
|
23
|
+
}
|
|
24
|
+
static fromDate(date) {
|
|
25
|
+
return DateOnly.from(date.getFullYear(), date.getMonth() + 1, date.getDate());
|
|
26
|
+
}
|
|
27
|
+
toDate() {
|
|
28
|
+
return new Date(this.year, this.month - 1, this.day);
|
|
29
|
+
}
|
|
30
|
+
toString() {
|
|
31
|
+
const month = this.month.toString().padStart(2, '0');
|
|
32
|
+
const day = this.day.toString().padStart(2, '0');
|
|
33
|
+
return `${this.year.toString().padStart(4, '0')}-${month}-${day}`;
|
|
34
|
+
}
|
|
35
|
+
equals(other) {
|
|
36
|
+
return !!other && this.year === other.year && this.month === other.month && this.day === other.day;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
__decorate([
|
|
40
|
+
field(Number),
|
|
41
|
+
__metadata("design:type", Number)
|
|
42
|
+
], DateOnly.prototype, "year", void 0);
|
|
43
|
+
__decorate([
|
|
44
|
+
field(Number),
|
|
45
|
+
__metadata("design:type", Number)
|
|
46
|
+
], DateOnly.prototype, "month", void 0);
|
|
47
|
+
__decorate([
|
|
48
|
+
field(Number),
|
|
49
|
+
__metadata("design:type", Number)
|
|
50
|
+
], DateOnly.prototype, "day", void 0);
|
|
51
|
+
//# sourceMappingURL=DateOnly.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DateOnly.js","sourceRoot":"","sources":["../DateOnly.ts"],"names":[],"mappings":";AAGA,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,MAAM,aAAa,GAAG,2BAA2B,CAAC;AAclD,MAAM,OAAO,QAAQ;IACjB,MAAM,CAAU,CAAC,OAAO,CAAC,GAAG,UAAU,CAAC;IAMvC,IAAI,CAAU;IAMd,KAAK,CAAU;IAMf,GAAG,CAAU;IASb,MAAM,CAAC,IAAI,CAAC,IAAY,EAAE,KAAa,EAAE,GAAW;QAChD,MAAM,QAAQ,GAAG,IAAI,QAAQ,EAAE,CAAC;QAChC,QAAQ,CAAC,IAAI,GAAG,IAAI,CAAC;QACrB,QAAQ,CAAC,KAAK,GAAG,KAAK,CAAC;QACvB,QAAQ,CAAC,GAAG,GAAG,GAAG,CAAC;QACnB,OAAO,QAAQ,CAAC;IACpB,CAAC;IAOD,MAAM,CAAC,KAAK,CAAC,KAAa;QACtB,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACxC,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,4BAA4B,KAAK,EAAE,CAAC,CAAC;QACzD,CAAC;QAED,OAAO,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;IACjG,CAAC;IAUD,MAAM,CAAC,QAAQ,CAAC,IAAU;QACtB,OAAO,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;IAClF,CAAC;IASD,MAAM;QACF,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,GAAG,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;IACzD,CAAC;IAMD,QAAQ;QACJ,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QACrD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QACjD,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,KAAK,IAAI,GAAG,EAAE,CAAC;IACtE,CAAC;IAOD,MAAM,CAAC,KAAkC;QACrC,OAAO,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,IAAI,IAAI,CAAC,KAAK,KAAK,KAAK,CAAC,KAAK,IAAI,IAAI,CAAC,GAAG,KAAK,KAAK,CAAC,GAAG,CAAC;IACvG,CAAC;;AAnFD;IADC,KAAK,CAAC,MAAM,CAAC;;sCACA;AAMd;IADC,KAAK,CAAC,MAAM,CAAC;;uCACC;AAMf;IADC,KAAK,CAAC,MAAM,CAAC;;qCACD"}
|
package/dist/Guid.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { IEquatable } from './IEquatable';
|
|
2
|
+
import { typeKey } from './typeKey';
|
|
2
3
|
export declare class Guid implements IEquatable {
|
|
3
4
|
readonly bytes: number[] | Uint8Array | undefined;
|
|
5
|
+
static readonly [typeKey] = "Guid";
|
|
4
6
|
static readonly empty: Guid;
|
|
5
7
|
private _stringVersion;
|
|
6
8
|
constructor(bytes: number[] | Uint8Array | undefined);
|