Skip to content

Commit d5eaa11

Browse files
authored
Merge pull request #51 from jsonjoy-com/value
Value
2 parents 4e64181 + 7dba33e commit d5eaa11

20 files changed

+314
-36
lines changed

package.json

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,26 @@
1616
"type": "github",
1717
"url": "https://github.com/sponsors/streamich"
1818
},
19-
"keywords": ["json-type", "type", "schema", "json-schema", "jtd", "json", "pointer", "jit"],
19+
"keywords": [
20+
"json-type",
21+
"type",
22+
"schema",
23+
"json-schema",
24+
"jtd",
25+
"json",
26+
"pointer",
27+
"jit"
28+
],
2029
"engines": {
2130
"node": ">=10.0"
2231
},
2332
"main": "lib/index.js",
2433
"types": "lib/index.d.ts",
2534
"typings": "lib/index.d.ts",
26-
"files": ["LICENSE", "lib/"],
35+
"files": [
36+
"LICENSE",
37+
"lib/"
38+
],
2739
"license": "Apache-2.0",
2840
"scripts": {
2941
"format": "biome format ./src",
@@ -51,6 +63,7 @@
5163
"@jsonjoy.com/json-random": "^1.2.0",
5264
"@jsonjoy.com/util": "^1.9.0",
5365
"sonic-forest": "^1.2.1",
66+
"thingies": "^2.5.0",
5467
"tree-dump": "^1.0.3"
5568
},
5669
"devDependencies": {
@@ -67,11 +80,16 @@
6780
"typescript": "^5.6.2"
6881
},
6982
"jest": {
70-
"moduleFileExtensions": ["ts", "js"],
83+
"moduleFileExtensions": [
84+
"ts",
85+
"js"
86+
],
7187
"transform": {
7288
"^.+\\.ts$": "ts-jest"
7389
},
74-
"transformIgnorePatterns": [".*/node_modules/.*"],
90+
"transformIgnorePatterns": [
91+
".*/node_modules/.*"
92+
],
7593
"testRegex": ".*/(__tests__|__jest__|demo)/.*\\.(test|spec)\\.ts$"
7694
}
7795
}

src/codegen/binary/AbstractBinaryCodegen.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,32 @@ var uint8 = writer.uint8, view = writer.view;`,
133133
return this.codegen.compile();
134134
}
135135

136+
protected abstract linkGet(): void;
137+
136138
protected onAny(path: SchemaPath, r: JsExpression, type: AnyType): void {
137-
this.codegen.js(`encoder.writeAny(${r.use()});`);
139+
const codegen = this.codegen;
140+
const rv = codegen.var(r.use());
141+
codegen.link('Value');
142+
this.linkGet();
143+
codegen.if(
144+
/* js */ `${rv} instanceof Value`,
145+
() => {
146+
const rType = codegen.var(/* js */ `${rv}.type`);
147+
const rData = codegen.var(/* js */ `${rv}.data`);
148+
codegen.if(
149+
/* js */ `${rType}`,
150+
() => {
151+
codegen.js(/* js */ `get(${rType})(${rData},encoder);`);
152+
},
153+
() => {
154+
this.codegen.js(`encoder.writeAny(${rData});`);
155+
},
156+
);
157+
},
158+
() => {
159+
this.codegen.js(`encoder.writeAny(${rv});`);
160+
},
161+
);
138162
}
139163

140164
protected onCon(path: SchemaPath, r: JsExpression, type: ConType): void {

src/codegen/binary/cbor/CborCodegen.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ import {JsExpression} from '@jsonjoy.com/codegen/lib/util/JsExpression';
22
import {normalizeAccessor} from '@jsonjoy.com/codegen/lib/util/normalizeAccessor';
33
import {CborEncoder} from '@jsonjoy.com/json-pack/lib/cbor/CborEncoder';
44
import {KeyOptType, type KeyType, type ObjType, type Type} from '../../../type';
5-
import type {CompiledBinaryEncoder, SchemaPath} from '../../types';
65
import {lazyKeyedFactory} from '../../util';
76
import {AbstractBinaryCodegen} from '../AbstractBinaryCodegen';
87
import {writer} from '../writer';
8+
import {once} from 'thingies/lib/once';
9+
import type {CompiledBinaryEncoder, SchemaPath} from '../../types';
910

1011
export class CborCodegen extends AbstractBinaryCodegen<CborEncoder> {
1112
public static readonly get = lazyKeyedFactory((type: Type, name?: string) => {
@@ -18,6 +19,11 @@ export class CborCodegen extends AbstractBinaryCodegen<CborEncoder> {
1819

1920
protected encoder = new CborEncoder(writer);
2021

22+
@once
23+
protected linkGet(): void {
24+
this.codegen.linkDependency(CborCodegen.get, 'get');
25+
}
26+
2127
protected onObj(path: SchemaPath, value: JsExpression, type: ObjType): void {
2228
const codegen = this.codegen;
2329
const r = codegen.r();

src/codegen/binary/cbor/__tests__/CborCodegen.spec.ts

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,52 @@
11
import {Writer} from '@jsonjoy.com/buffers/lib/Writer';
22
import {CborDecoder} from '@jsonjoy.com/json-pack/lib/cbor/CborDecoder';
33
import {CborEncoder} from '@jsonjoy.com/json-pack/lib/cbor/CborEncoder';
4-
import type {Type} from '../../../../type';
5-
import type {ModuleType} from '../../../../type/classes/ModuleType';
4+
import {ModuleType} from '../../../../type/classes/ModuleType';
65
import {testBinaryCodegen} from '../../__tests__/testBinaryCodegen';
76
import {CborCodegen} from '../CborCodegen';
7+
import {unknown, Value} from '../../../../value';
8+
import type {Type} from '../../../../type';
89

910
const encoder = new CborEncoder(new Writer(16));
1011
const decoder = new CborDecoder();
1112

13+
describe('inline Value', () => {
14+
test('can encode "any" field', () => {
15+
const {t} = new ModuleType();
16+
const type = t.object({foo: t.any});
17+
const fn = CborCodegen.get(type);
18+
encoder.writer.reset();
19+
fn({foo: true}, encoder);
20+
const encoded = encoder.writer.flush();
21+
const decoded = decoder.decode(encoded);
22+
expect(decoded).toEqual({foo: true});
23+
});
24+
25+
test('can encode anon Value<unknown>', () => {
26+
const {t} = new ModuleType();
27+
const type = t.object({foo: t.any});
28+
const fn = CborCodegen.get(type);
29+
encoder.writer.reset();
30+
const value = unknown('test');
31+
fn({foo: value}, encoder);
32+
const encoded = encoder.writer.flush();
33+
const decoded = decoder.decode(encoded);
34+
expect(decoded).toEqual({foo: 'test'});
35+
});
36+
37+
test('can encode typed Value<T>', () => {
38+
const {t} = new ModuleType();
39+
const type = t.object({foo: t.any});
40+
const fn = CborCodegen.get(type);
41+
encoder.writer.reset();
42+
const value = new Value(123, t.con(123));
43+
fn({foo: value}, encoder);
44+
const encoded = encoder.writer.flush();
45+
const decoded = decoder.decode(encoded);
46+
expect(decoded).toEqual({foo: 123});
47+
});
48+
});
49+
1250
const transcode = (system: ModuleType, type: Type, value: unknown) => {
1351
const fn = CborCodegen.get(type);
1452
encoder.writer.reset();

src/codegen/binary/json/JsonCodegen.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ import {JsExpression} from '@jsonjoy.com/codegen/lib/util/JsExpression';
22
import {normalizeAccessor} from '@jsonjoy.com/codegen/lib/util/normalizeAccessor';
33
import {JsonEncoder} from '@jsonjoy.com/json-pack/lib/json/JsonEncoder';
44
import {type ArrType, type MapType, KeyOptType, type KeyType, type ObjType, type Type} from '../../../type';
5-
import type {CompiledBinaryEncoder, SchemaPath} from '../../types';
65
import {lazyKeyedFactory} from '../../util';
76
import {AbstractBinaryCodegen} from '../AbstractBinaryCodegen';
87
import {writer} from '../writer';
8+
import {once} from 'thingies/lib/once';
9+
import type {CompiledBinaryEncoder, SchemaPath} from '../../types';
910

1011
export class JsonCodegen extends AbstractBinaryCodegen<JsonEncoder> {
1112
public static readonly get = lazyKeyedFactory((type: Type, name?: string) => {
@@ -20,6 +21,11 @@ export class JsonCodegen extends AbstractBinaryCodegen<JsonEncoder> {
2021

2122
protected encoder = new JsonEncoder(writer);
2223

24+
@once
25+
protected linkGet(): void {
26+
this.codegen.linkDependency(JsonCodegen.get, 'get');
27+
}
28+
2329
protected onArr(path: SchemaPath, r: JsExpression, type: ArrType): void {
2430
const codegen = this.codegen;
2531
const rLen = codegen.var(/* js */ `${r.use()}.length`);

src/codegen/binary/json/__tests__/JsonCodegen.spec.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,52 @@
11
import {Writer} from '@jsonjoy.com/buffers/lib/Writer';
22
import {parse} from '@jsonjoy.com/json-pack/lib/json-binary';
33
import {JsonEncoder} from '@jsonjoy.com/json-pack/lib/json/JsonEncoder';
4+
import {JsonDecoder} from '@jsonjoy.com/json-pack/lib/json/JsonDecoder';
45
import {ModuleType} from '../../../../type/classes/ModuleType';
56
import {testBinaryCodegen} from '../../__tests__/testBinaryCodegen';
67
import {JsonCodegen} from '../JsonCodegen';
78
import type {Type} from '../../../../type';
9+
import {unknown, Value} from '../../../../value';
810

911
const encoder = new JsonEncoder(new Writer(16));
12+
const decoder = new JsonDecoder();
13+
14+
describe('inline Value', () => {
15+
test('can encode "any" field', () => {
16+
const {t} = new ModuleType();
17+
const type = t.object({foo: t.any});
18+
const fn = JsonCodegen.get(type);
19+
encoder.writer.reset();
20+
fn({foo: true}, encoder);
21+
const encoded = encoder.writer.flush();
22+
const decoded = decoder.decode(encoded);
23+
expect(decoded).toEqual({foo: true});
24+
});
25+
26+
test('can encode anon Value<unknown>', () => {
27+
const {t} = new ModuleType();
28+
const type = t.object({foo: t.any});
29+
const fn = JsonCodegen.get(type);
30+
encoder.writer.reset();
31+
const value = unknown('test');
32+
fn({foo: value}, encoder);
33+
const encoded = encoder.writer.flush();
34+
const decoded = decoder.decode(encoded);
35+
expect(decoded).toEqual({foo: 'test'});
36+
});
37+
38+
test('can encode typed Value<T>', () => {
39+
const {t} = new ModuleType();
40+
const type = t.object({foo: t.any});
41+
const fn = JsonCodegen.get(type);
42+
encoder.writer.reset();
43+
const value = new Value(123, t.con(123));
44+
fn({foo: value}, encoder);
45+
const encoded = encoder.writer.flush();
46+
const decoded = decoder.decode(encoded);
47+
expect(decoded).toEqual({foo: 123});
48+
});
49+
});
1050

1151
const transcode = (system: ModuleType, type: Type, value: unknown) => {
1252
const fn = JsonCodegen.get(type);

src/codegen/binary/msgpack/MsgPackCodegen.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ import {JsExpression} from '@jsonjoy.com/codegen/lib/util/JsExpression';
22
import {normalizeAccessor} from '@jsonjoy.com/codegen/lib/util/normalizeAccessor';
33
import {MsgPackEncoder} from '@jsonjoy.com/json-pack/lib/msgpack/MsgPackEncoder';
44
import {KeyOptType, type KeyType, type ObjType, type Type} from '../../../type';
5-
import type {CompiledBinaryEncoder, SchemaPath} from '../../types';
65
import {lazyKeyedFactory} from '../../util';
76
import {AbstractBinaryCodegen} from '../AbstractBinaryCodegen';
87
import {writer} from '../writer';
8+
import {once} from 'thingies/lib/once';
9+
import type {CompiledBinaryEncoder, SchemaPath} from '../../types';
910

1011
export class MsgPackCodegen extends AbstractBinaryCodegen<MsgPackEncoder> {
1112
public static readonly get = lazyKeyedFactory((type: Type, name?: string) => {
@@ -18,6 +19,11 @@ export class MsgPackCodegen extends AbstractBinaryCodegen<MsgPackEncoder> {
1819

1920
protected encoder = new MsgPackEncoder(writer);
2021

22+
@once
23+
protected linkGet(): void {
24+
this.codegen.linkDependency(MsgPackCodegen.get, 'get');
25+
}
26+
2127
protected onObj(path: SchemaPath, value: JsExpression, type: ObjType): void {
2228
const codegen = this.codegen;
2329
const r = codegen.r();

src/codegen/binary/msgpack/__tests__/MsgPackCodegen.spec.ts

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,51 @@
11
import {Writer} from '@jsonjoy.com/buffers/lib/Writer';
22
import {MsgPackDecoder} from '@jsonjoy.com/json-pack/lib/msgpack/MsgPackDecoder';
33
import {MsgPackEncoder} from '@jsonjoy.com/json-pack/lib/msgpack/MsgPackEncoder';
4-
import type {ModuleType, Type} from '../../../../type';
4+
import {ModuleType, type Type} from '../../../../type';
55
import {testBinaryCodegen} from '../../__tests__/testBinaryCodegen';
66
import {MsgPackCodegen} from '../MsgPackCodegen';
7+
import {unknown, Value} from '../../../../value';
78

89
const encoder = new MsgPackEncoder(new Writer(16));
910
const decoder = new MsgPackDecoder();
1011

12+
describe('inline Value', () => {
13+
test('can encode "any" field', () => {
14+
const {t} = new ModuleType();
15+
const type = t.object({foo: t.any});
16+
const fn = MsgPackCodegen.get(type);
17+
encoder.writer.reset();
18+
fn({foo: true}, encoder);
19+
const encoded = encoder.writer.flush();
20+
const decoded = decoder.decode(encoded);
21+
expect(decoded).toEqual({foo: true});
22+
});
23+
24+
test('can encode anon Value<unknown>', () => {
25+
const {t} = new ModuleType();
26+
const type = t.object({foo: t.any});
27+
const fn = MsgPackCodegen.get(type);
28+
encoder.writer.reset();
29+
const value = unknown('test');
30+
fn({foo: value}, encoder);
31+
const encoded = encoder.writer.flush();
32+
const decoded = decoder.decode(encoded);
33+
expect(decoded).toEqual({foo: 'test'});
34+
});
35+
36+
test('can encode typed Value<T>', () => {
37+
const {t} = new ModuleType();
38+
const type = t.object({foo: t.any});
39+
const fn = MsgPackCodegen.get(type);
40+
encoder.writer.reset();
41+
const value = new Value(123, t.con(123));
42+
fn({foo: value}, encoder);
43+
const encoded = encoder.writer.flush();
44+
const decoded = decoder.decode(encoded);
45+
expect(decoded).toEqual({foo: 123});
46+
});
47+
});
48+
1149
const transcode = (system: ModuleType, type: Type, value: unknown) => {
1250
const fn = MsgPackCodegen.get(type);
1351
encoder.writer.reset();

src/codegen/capacity/CapacityEstimatorCodegen.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {DiscriminatorCodegen} from '../discriminator';
88
import {lazyKeyedFactory} from '../util';
99
import {AbstractCodegen} from '../AbstractCodege';
1010
import type {SchemaPath} from '../types';
11+
import {Value} from '../../value';
1112

1213
export type CompiledCapacityEstimator = (value: unknown) => number;
1314

@@ -36,6 +37,10 @@ export class CapacityEstimatorCodegen extends AbstractCodegen<CompiledCapacityEs
3637
args: ['r0'],
3738
prologue: /* js */ `var size = 0;`,
3839
epilogue: /* js */ `return size;`,
40+
linkable: {
41+
Value,
42+
get: CapacityEstimatorCodegen.get,
43+
},
3944
processSteps: (steps) => {
4045
const stepsJoined: CodegenStepExecJs[] = [];
4146
for (let i = 0; i < steps.length; i++) {
@@ -58,7 +63,27 @@ export class CapacityEstimatorCodegen extends AbstractCodegen<CompiledCapacityEs
5863
protected onAny(path: SchemaPath, r: JsExpression, type: AnyType): void {
5964
const codegen = this.codegen;
6065
const rv = codegen.var(r.use());
61-
codegen.js(/* js */ `size += maxEncodingCapacity(${rv});`);
66+
codegen.link('Value');
67+
codegen.link('get');
68+
codegen.if(
69+
/* js */ `${rv} instanceof Value`,
70+
() => {
71+
const rType = codegen.var(/* js */ `${rv}.type`);
72+
const rData = codegen.var(/* js */ `${rv}.data`);
73+
codegen.if(
74+
/* js */ `${rType}`,
75+
() => {
76+
codegen.js(/* js */ `size += get(${rType})(${rData});`);
77+
},
78+
() => {
79+
codegen.js(/* js */ `size += maxEncodingCapacity(${rData});`);
80+
},
81+
);
82+
},
83+
() => {
84+
codegen.js(/* js */ `size += maxEncodingCapacity(${rv});`);
85+
},
86+
);
6287
}
6388

6489
protected onCon(path: SchemaPath, r: JsExpression, type: ConType): void {

0 commit comments

Comments
 (0)