@cratis/fundamentals 7.19.3 → 7.19.5
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/DateOnly.ts +3 -3
- package/Guid.ts +1 -1
- package/IEquatable.ts +1 -1
- package/JsonSerializer.ts +13 -6
- package/StandardClassDecoratorContext.ts +1 -0
- package/StandardFieldDecoratorContext.ts +1 -0
- package/dist/JsonSerializer.d.ts.map +1 -1
- package/dist/JsonSerializer.js +15 -6
- package/dist/JsonSerializer.js.map +1 -1
- package/dist/StandardClassDecoratorContext.d.ts.map +1 -1
- package/dist/StandardFieldDecoratorContext.d.ts.map +1 -1
- package/dist/cjs/DateOnly.js.map +1 -1
- package/dist/cjs/Guid.js.map +1 -1
- package/dist/cjs/JsonSerializer.d.ts.map +1 -1
- package/dist/cjs/JsonSerializer.js +15 -6
- package/dist/cjs/JsonSerializer.js.map +1 -1
- package/dist/cjs/StandardClassDecoratorContext.d.ts.map +1 -1
- package/dist/cjs/StandardFieldDecoratorContext.d.ts.map +1 -1
- package/dist/cjs/fieldDecorator.d.ts.map +1 -1
- package/dist/cjs/fieldDecorator.js.map +1 -1
- package/dist/esm/DateOnly.js.map +1 -1
- package/dist/esm/Guid.js.map +1 -1
- package/dist/esm/JsonSerializer.d.ts.map +1 -1
- package/dist/esm/JsonSerializer.js +15 -6
- package/dist/esm/JsonSerializer.js.map +1 -1
- package/dist/esm/StandardClassDecoratorContext.d.ts.map +1 -1
- package/dist/esm/StandardFieldDecoratorContext.d.ts.map +1 -1
- package/dist/esm/fieldDecorator.d.ts.map +1 -1
- package/dist/esm/fieldDecorator.js.map +1 -1
- package/dist/fieldDecorator.d.ts.map +1 -1
- package/dist/fieldDecorator.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/fieldDecorator.ts +4 -0
- package/for_JsonSerializer/Types.ts +0 -1
- package/for_JsonSerializer/given/a_model_with_field_metadata.ts +63 -0
- package/for_JsonSerializer/given/given.ts +6 -0
- package/for_JsonSerializer/when_deserializing_fields_with_legacy_decorators.ts +41 -0
- package/for_JsonSerializer/when_deserializing_generic_array_without_a_value.ts +10 -0
- package/for_JsonSerializer/when_round_tripping_fields_with_legacy_decorators.ts +31 -0
- package/for_fieldDecorator/fixtures/standard_decorated_types.ts +53 -2
- package/for_fieldDecorator/when_executing_standard_typescript_decorator_emit.ts +46 -1
- package/package.json +2 -2
|
@@ -0,0 +1,10 @@
|
|
|
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 { JsonSerializer } from '../JsonSerializer';
|
|
5
|
+
import { Document } from './given/a_model_with_field_metadata';
|
|
6
|
+
|
|
7
|
+
describe('when deserializing a generic array without a value', () => {
|
|
8
|
+
it('should use an empty array for an absent collection', () => JsonSerializer.deserialize(Document, '{}').names.should.deep.equal([]));
|
|
9
|
+
it('should preserve an explicit null collection', () => (JsonSerializer.deserialize(Document, '{"names":null}').names === null).should.be.true);
|
|
10
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
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 { given } from './given/given';
|
|
5
|
+
import { JsonSerializer } from '../JsonSerializer';
|
|
6
|
+
import { Document, a_model_with_field_metadata } from './given/a_model_with_field_metadata';
|
|
7
|
+
|
|
8
|
+
describe('when round tripping fields with legacy decorators', given(a_model_with_field_metadata, context => {
|
|
9
|
+
let written: {
|
|
10
|
+
identifier: string;
|
|
11
|
+
label: string;
|
|
12
|
+
quantity: number;
|
|
13
|
+
enabled: boolean;
|
|
14
|
+
identifiers: string[];
|
|
15
|
+
children: { identifier: string }[];
|
|
16
|
+
};
|
|
17
|
+
let read: Document;
|
|
18
|
+
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
const document = JsonSerializer.deserialize(Document, JSON.stringify(context.payload));
|
|
21
|
+
const json = JsonSerializer.serialize(document);
|
|
22
|
+
written = JSON.parse(json);
|
|
23
|
+
read = JsonSerializer.deserialize(Document, json);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('should write a concept wrapping a Guid as a string', () => written.identifier.should.equal(context.identifier));
|
|
27
|
+
it('should write falsy concept payloads without object wrappers', () => [written.label, written.quantity, written.enabled].should.deep.equal(['', 0, false]));
|
|
28
|
+
it('should write generic arrays of concepts as underlying values', () => written.identifiers.should.deep.equal([context.identifier]));
|
|
29
|
+
it('should write generic arrays of nested models as objects', () => written.children.should.deep.equal([{ identifier: context.identifier }]));
|
|
30
|
+
it('should read back the nested array model and typed concept', () => read.children[0].identifier.value.toString().should.equal(context.identifier));
|
|
31
|
+
}));
|
|
@@ -3,7 +3,20 @@
|
|
|
3
3
|
|
|
4
4
|
declare const fundamentals: typeof import('../../index');
|
|
5
5
|
|
|
6
|
-
const { derivedType, field, JsonSerializer, ValueMap } = fundamentals;
|
|
6
|
+
const { ConceptAs, derivedType, field, Guid, JsonSerializer, ValueMap } = fundamentals;
|
|
7
|
+
|
|
8
|
+
class Identifier extends ConceptAs<InstanceType<typeof Guid>> {
|
|
9
|
+
static readonly valueType = Guid;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
class Label extends ConceptAs<string> {}
|
|
13
|
+
class Quantity extends ConceptAs<number> {}
|
|
14
|
+
class Enabled extends ConceptAs<boolean> {}
|
|
15
|
+
|
|
16
|
+
class Child {
|
|
17
|
+
@field(Identifier)
|
|
18
|
+
identifier!: Identifier;
|
|
19
|
+
}
|
|
7
20
|
|
|
8
21
|
function replaceClass<Target extends import('../../Constructor').Constructor>(target: Target): Target {
|
|
9
22
|
return class extends target { } as Target;
|
|
@@ -39,6 +52,33 @@ class Drawing {
|
|
|
39
52
|
@field(String)
|
|
40
53
|
title!: string;
|
|
41
54
|
|
|
55
|
+
@field(Array, { genericArguments: [String] })
|
|
56
|
+
names!: string[];
|
|
57
|
+
|
|
58
|
+
@field(Identifier)
|
|
59
|
+
identifier!: Identifier;
|
|
60
|
+
|
|
61
|
+
@field(Label)
|
|
62
|
+
label!: Label;
|
|
63
|
+
|
|
64
|
+
@field(Quantity)
|
|
65
|
+
quantity!: Quantity;
|
|
66
|
+
|
|
67
|
+
@field(Enabled)
|
|
68
|
+
enabled!: Enabled;
|
|
69
|
+
|
|
70
|
+
@field(Child)
|
|
71
|
+
child!: Child;
|
|
72
|
+
|
|
73
|
+
@field(Array, { genericArguments: [Identifier] })
|
|
74
|
+
identifiers!: Identifier[];
|
|
75
|
+
|
|
76
|
+
@field(Array, { genericArguments: [Child] })
|
|
77
|
+
children!: Child[];
|
|
78
|
+
|
|
79
|
+
@field(Label, true)
|
|
80
|
+
legacyLabels!: Label[];
|
|
81
|
+
|
|
42
82
|
@field(Date)
|
|
43
83
|
createdAt!: Date;
|
|
44
84
|
|
|
@@ -54,8 +94,18 @@ class Drawing {
|
|
|
54
94
|
}
|
|
55
95
|
|
|
56
96
|
const instanceCountBeforeDeserialize = Drawing.instanceCount;
|
|
97
|
+
const identifier = 'f0fa7c5e-9f7b-4688-8851-e0b6eeebe28b';
|
|
57
98
|
const drawing = JsonSerializer.deserialize(Drawing, JSON.stringify({
|
|
99
|
+
child: { identifier },
|
|
100
|
+
children: [{ identifier }],
|
|
58
101
|
createdAt: '2026-08-16T09:30:00.000Z',
|
|
102
|
+
enabled: false,
|
|
103
|
+
identifier,
|
|
104
|
+
identifiers: [identifier],
|
|
105
|
+
label: '',
|
|
106
|
+
legacyLabels: ['older'],
|
|
107
|
+
names: ['one', 'two'],
|
|
108
|
+
quantity: 0,
|
|
59
109
|
scores: {
|
|
60
110
|
first: 42
|
|
61
111
|
},
|
|
@@ -72,5 +122,6 @@ const drawing = JsonSerializer.deserialize(Drawing, JSON.stringify({
|
|
|
72
122
|
title: 'Standard decorators'
|
|
73
123
|
}));
|
|
74
124
|
const instanceCountAfterDeserialize = Drawing.instanceCount;
|
|
125
|
+
const serializedDrawing = JsonSerializer.serialize(drawing);
|
|
75
126
|
|
|
76
|
-
void [Circle, Rectangle, ReplacedShape, instanceCountBeforeDeserialize, drawing, instanceCountAfterDeserialize];
|
|
127
|
+
void [Child, Circle, Rectangle, ReplacedShape, instanceCountBeforeDeserialize, drawing, instanceCountAfterDeserialize, serializedDrawing];
|
|
@@ -19,19 +19,34 @@ describe('when executing standard TypeScript decorator emit', () => {
|
|
|
19
19
|
let symbolMetadataWasAbsent: boolean;
|
|
20
20
|
let symbolMetadataWasPolyfilled: boolean;
|
|
21
21
|
let executionResult: {
|
|
22
|
+
Child: fundamentals.Constructor;
|
|
22
23
|
Circle: fundamentals.Constructor;
|
|
23
24
|
Drawing: fundamentals.Constructor;
|
|
25
|
+
Enabled: fundamentals.Constructor;
|
|
26
|
+
Identifier: fundamentals.Constructor;
|
|
27
|
+
Label: fundamentals.Constructor;
|
|
28
|
+
Quantity: fundamentals.Constructor;
|
|
24
29
|
Rectangle: fundamentals.Constructor;
|
|
25
30
|
ReplacedShape: fundamentals.Constructor;
|
|
26
31
|
Shape: fundamentals.Constructor;
|
|
27
32
|
drawing: {
|
|
33
|
+
child: { identifier: { value: fundamentals.Guid } };
|
|
34
|
+
children: object[];
|
|
28
35
|
createdAt: Date;
|
|
36
|
+
enabled: { value: boolean };
|
|
37
|
+
identifier: { value: fundamentals.Guid };
|
|
38
|
+
identifiers: { value: fundamentals.Guid }[];
|
|
39
|
+
label: { value: string };
|
|
40
|
+
legacyLabels: { value: string }[];
|
|
41
|
+
names: string[];
|
|
42
|
+
quantity: { value: number };
|
|
29
43
|
scores: fundamentals.ValueMap<string, number>;
|
|
30
44
|
shapes: object[];
|
|
31
45
|
title: string;
|
|
32
46
|
};
|
|
33
47
|
instanceCountAfterDeserialize: number;
|
|
34
48
|
instanceCountBeforeDeserialize: number;
|
|
49
|
+
serializedDrawing: string;
|
|
35
50
|
};
|
|
36
51
|
let registeredDerivedTypes: fundamentals.Constructor[];
|
|
37
52
|
let legacySemanticDiagnosticCount: number;
|
|
@@ -86,7 +101,7 @@ describe('when executing standard TypeScript decorator emit', () => {
|
|
|
86
101
|
reportDiagnostics: true
|
|
87
102
|
}).outputText;
|
|
88
103
|
|
|
89
|
-
const script = new Script(`${emittedJavaScript}\n({ Circle, Drawing, Rectangle, ReplacedShape, Shape, drawing, instanceCountAfterDeserialize, instanceCountBeforeDeserialize })`);
|
|
104
|
+
const script = new Script(`${emittedJavaScript}\n({ Child, Circle, Drawing, Enabled, Identifier, Label, Quantity, Rectangle, ReplacedShape, Shape, drawing, instanceCountAfterDeserialize, instanceCountBeforeDeserialize, serializedDrawing })`);
|
|
90
105
|
executionResult = script.runInNewContext({ Array, Boolean, Date, fundamentals, JSON, Number, Object, String, Symbol }) as typeof executionResult;
|
|
91
106
|
registeredDerivedTypes = fundamentals.DerivedType.getDerivedTypesFor(executionResult.Shape);
|
|
92
107
|
});
|
|
@@ -104,6 +119,36 @@ describe('when executing standard TypeScript decorator emit', () => {
|
|
|
104
119
|
it('should expose field metadata before constructing an instance', () => executionResult.instanceCountBeforeDeserialize.should.equal(0));
|
|
105
120
|
it('should construct the target only during first-call deserialization', () => executionResult.instanceCountAfterDeserialize.should.equal(1));
|
|
106
121
|
it('should deserialize primitive fields', () => executionResult.drawing.title.should.equal('Standard decorators'));
|
|
122
|
+
it('should read array elements declared through generic arguments', () => executionResult.drawing.names.should.deep.equal(['one', 'two']));
|
|
123
|
+
it('should read a concept underlying Guid as a Guid', () => executionResult.drawing.identifier.value.should.be.instanceOf(fundamentals.Guid));
|
|
124
|
+
it('should wrap an empty string in its concept', () => {
|
|
125
|
+
(executionResult.drawing.label instanceof executionResult.Label).should.be.true;
|
|
126
|
+
executionResult.drawing.label.value.should.equal('');
|
|
127
|
+
});
|
|
128
|
+
it('should wrap zero in its concept', () => {
|
|
129
|
+
(executionResult.drawing.quantity instanceof executionResult.Quantity).should.be.true;
|
|
130
|
+
executionResult.drawing.quantity.value.should.equal(0);
|
|
131
|
+
});
|
|
132
|
+
it('should wrap false in its concept', () => {
|
|
133
|
+
(executionResult.drawing.enabled instanceof executionResult.Enabled).should.be.true;
|
|
134
|
+
executionResult.drawing.enabled.value.should.equal(false);
|
|
135
|
+
});
|
|
136
|
+
it('should read a nested model and its concept', () => {
|
|
137
|
+
(executionResult.drawing.child instanceof executionResult.Child).should.be.true;
|
|
138
|
+
executionResult.drawing.child.identifier.value.should.be.instanceOf(fundamentals.Guid);
|
|
139
|
+
});
|
|
140
|
+
it('should read an array of concepts', () => {
|
|
141
|
+
(executionResult.drawing.identifiers[0] instanceof executionResult.Identifier).should.be.true;
|
|
142
|
+
executionResult.drawing.identifiers[0].value.should.be.instanceOf(fundamentals.Guid);
|
|
143
|
+
});
|
|
144
|
+
it('should read an array of nested models', () => (executionResult.drawing.children[0] instanceof executionResult.Child).should.be.true);
|
|
145
|
+
it('should keep reading legacy enumerable fields', () => executionResult.drawing.legacyLabels[0].value.should.equal('older'));
|
|
146
|
+
it('should serialize generic arrays of concepts as values', () => JSON.parse(executionResult.serializedDrawing).identifiers.should.deep.equal(['f0fa7c5e-9f7b-4688-8851-e0b6eeebe28b']));
|
|
147
|
+
it('should serialize generic arrays of nested models', () => JSON.parse(executionResult.serializedDrawing).children.should.deep.equal([{ identifier: 'f0fa7c5e-9f7b-4688-8851-e0b6eeebe28b' }]));
|
|
148
|
+
it('should serialize falsy concept values', () => {
|
|
149
|
+
const written = JSON.parse(executionResult.serializedDrawing);
|
|
150
|
+
[written.label, written.quantity, written.enabled].should.deep.equal(['', 0, false]);
|
|
151
|
+
});
|
|
107
152
|
it('should deserialize date fields', () => executionResult.drawing.createdAt.should.be.instanceOf(Date));
|
|
108
153
|
it('should deserialize polymorphic array entries', () => (executionResult.drawing.shapes[0] instanceof executionResult.Circle).should.be.true);
|
|
109
154
|
it('should deserialize every registered derivative', () => (executionResult.drawing.shapes[1] instanceof executionResult.Rectangle).should.be.true);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cratis/fundamentals",
|
|
3
|
-
"version": "7.19.
|
|
3
|
+
"version": "7.19.5",
|
|
4
4
|
"description": "The shared building blocks beneath the Cratis stack — GUIDs, concepts, derived-type serialization, property-path proxies, and decorators for TypeScript",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cratis",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
58
|
"@typescript/native": "npm:typescript@^7.0.2",
|
|
59
|
-
"rollup": "4.63.
|
|
59
|
+
"rollup": "4.63.4",
|
|
60
60
|
"rollup-plugin-typescript2": "0.37.0",
|
|
61
61
|
"typescript": "npm:@typescript/typescript6@^6.0.2",
|
|
62
62
|
"typescript-programmatic-api": "npm:@typescript/typescript6@^6.0.2"
|