Skip to content

Commit 77d7fcc

Browse files
committed
WIP most datatypes are working
1 parent c2bb018 commit 77d7fcc

File tree

8 files changed

+277
-22
lines changed

8 files changed

+277
-22
lines changed

src/library/connection.ts

+9-2
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,16 @@ export class WebsocketConnection implements Connection {
7575

7676
if (id) {
7777
this.emitter.emit(`rpc-${id}`, [res]);
78+
} else if (res.error) {
79+
// TODO Handle generic errors, should the connection close? If an error is thrown, where will it be catched?
7880
} else {
79-
const { id, action, result } = LiveResult.parse(res.result);
80-
this.emitter.emit(`live-${id}`, [action, result], true);
81+
const live = LiveResult.safeParse(res.result);
82+
if (live.success) {
83+
const { id, action, result } = live.data;
84+
this.emitter.emit(`live-${id}`, [action, result], true);
85+
} else {
86+
// TODO We received an unknown message, if an error is thrown, where will it be catched?
87+
}
8188
}
8289
})
8390
})

src/library/data/cbor.ts

+68-15
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,84 @@
11
import { TaggedValue, encode as encode_cbor, decode as decode_cbor } from "cbor-redux";
22
import { RecordId } from "./recordid.ts";
33
import { Uuid } from "./uuid.ts";
4-
import { Duration } from "./duration.ts";
4+
import { Duration, cborCustomDurationToDuration, durationToCborCustomDuration } from "./duration.ts";
55
import { Decimal } from "./decimal.ts"
6+
import {
7+
GeometryCollection,
8+
GeometryLine,
9+
GeometryMultiLine,
10+
GeometryMultiPoint,
11+
GeometryMultiPolygon,
12+
GeometryPoint,
13+
GeometryPolygon
14+
} from "./geometry.ts";
15+
import { Table } from "./table.ts";
16+
import { cborCustomDateToDate, dateToCborCustomDate } from "./datetime.ts";
17+
18+
// Tags from the spec - https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml
19+
const TAG_SPEC_DATETIME = 0;
20+
const TAG_SPEC_UUID = 37;
21+
22+
// Custom tags
23+
const TAG_NONE = 6;
24+
const TAG_TABLE = 7;
25+
const TAG_RECORDID = 8;
26+
const TAG_STRING_UUID = 9;
27+
const TAG_STRING_DECIMAL = 10;
28+
// const TAG_BINARY_DECIMAL = 11;
29+
const TAG_CUSTOM_DATETIME = 12;
30+
const TAG_STRING_DURATION = 13;
31+
const TAG_CUSTOM_DURATION = 14;
32+
33+
// Custom Geometries
34+
const TAG_GEOMETRY_POINT = 88;
35+
const TAG_GEOMETRY_LINE = 89;
36+
const TAG_GEOMETRY_POLYGON = 90;
37+
const TAG_GEOMETRY_MULTIPOINT = 91;
38+
const TAG_GEOMETRY_MULTILINE = 92;
39+
const TAG_GEOMETRY_MULTIPOLYGON = 93;
40+
const TAG_GEOMETRY_COLLECTION = 94;
641

742
export function encode<T extends unknown>(data: T) {
843
return encode_cbor<T>(data, (_, v) => {
9-
if (v instanceof Date) return new TaggedValue(v.toISOString(), 0);
10-
if (v === undefined) return new TaggedValue(null, 6);
11-
if (v instanceof Uuid) return new TaggedValue(v.uuid, 7);
12-
if (v instanceof Decimal) return new TaggedValue(v.toString(), 8);
13-
if (v instanceof Duration) return new TaggedValue(v.toString(), 9);
14-
if (v instanceof RecordId) return new TaggedValue([v.tb, v.id], 10);
44+
if (v instanceof Date) return new TaggedValue(dateToCborCustomDate(v), TAG_CUSTOM_DATETIME);
45+
if (v === undefined) return new TaggedValue(null, TAG_NONE);
46+
if (v instanceof Uuid) return new TaggedValue(v.uuid, TAG_STRING_UUID);
47+
if (v instanceof Decimal) return new TaggedValue(v.toString(), TAG_STRING_DECIMAL);
48+
if (v instanceof Duration) return new TaggedValue(durationToCborCustomDuration(v), TAG_CUSTOM_DURATION);
49+
if (v instanceof RecordId) return new TaggedValue([v.tb, v.id], TAG_RECORDID);
50+
if (v instanceof Table) return new TaggedValue(v.tb, TAG_TABLE);
51+
if (v instanceof GeometryPoint) return new TaggedValue(v.point, TAG_GEOMETRY_POINT);
52+
if (v instanceof GeometryLine) return new TaggedValue(v.line, TAG_GEOMETRY_LINE);
53+
if (v instanceof GeometryPolygon) return new TaggedValue(v.polygon, TAG_GEOMETRY_POLYGON);
54+
if (v instanceof GeometryMultiPoint) return new TaggedValue(v.points, TAG_GEOMETRY_MULTIPOINT);
55+
if (v instanceof GeometryMultiLine) return new TaggedValue(v.lines, TAG_GEOMETRY_MULTILINE);
56+
if (v instanceof GeometryMultiPolygon) return new TaggedValue(v.polygons, TAG_GEOMETRY_MULTIPOLYGON);
57+
if (v instanceof GeometryCollection) return new TaggedValue(v.collection, TAG_GEOMETRY_COLLECTION);
1558
return v;
1659
});
1760
}
1861

1962
export function decode(data: ArrayBuffer) {
2063
return decode_cbor(data, (_, v) => {
21-
if (v instanceof TaggedValue) {
22-
if (v.tag === 0) return new Date(v.value);
23-
if (v.tag === 6) return undefined;
24-
if (v.tag === 7) return new Uuid(v.value);
25-
if (v.tag === 8) return new Decimal(v.value);
26-
if (v.tag === 9) return new Duration(v.value);
27-
if (v.tag === 10) return new RecordId(v.value[0], v.value[1]);
64+
if (!(v instanceof TaggedValue)) return v;
65+
66+
switch (v.tag) {
67+
case TAG_SPEC_DATETIME: return new Date(v.value);
68+
case TAG_CUSTOM_DATETIME: return cborCustomDateToDate(v.value);
69+
case TAG_NONE: return undefined;
70+
case TAG_STRING_UUID: return new Uuid(v.value);
71+
case TAG_STRING_DECIMAL: return new Decimal(v.value);
72+
case TAG_STRING_DURATION: return new Duration(v.value);
73+
case TAG_CUSTOM_DURATION: return cborCustomDurationToDuration(v.value);
74+
case TAG_RECORDID: return new RecordId(v.value[0], v.value[1]);
75+
case TAG_GEOMETRY_POINT: return new GeometryPoint(v.value);
76+
case TAG_GEOMETRY_LINE: return new GeometryLine(v.value);
77+
case TAG_GEOMETRY_POLYGON: return new GeometryPolygon(v.value);
78+
case TAG_GEOMETRY_MULTIPOINT: return new GeometryMultiPoint(v.value);
79+
case TAG_GEOMETRY_MULTILINE: return new GeometryMultiLine(v.value);
80+
case TAG_GEOMETRY_MULTIPOLYGON: return new GeometryMultiPolygon(v.value);
81+
case TAG_GEOMETRY_COLLECTION: return new GeometryCollection(v.value);
2882
}
29-
return v;
3083
});
3184
}

src/library/data/datetime.ts

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
export function msToNs(ms: number) {
2+
return ms * 1000000;
3+
}
4+
5+
export function nsToMs(ns: number) {
6+
return Math.floor(ns / 1000000);
7+
}
8+
9+
export function dateToCborCustomDate(date: Date) {
10+
const s = date.getUTCSeconds();
11+
const ms = date.getMilliseconds();
12+
return [s, ms * 1000000];
13+
}
14+
15+
export function cborCustomDateToDate([s, ns]: [number, number]) {
16+
const date = new Date(0);
17+
date.setUTCSeconds(Number(s));
18+
date.setMilliseconds(Math.floor(Number(ns) / 1000000));
19+
return date;
20+
}

src/library/data/duration.ts

+15
Original file line numberDiff line numberDiff line change
@@ -1 +1,16 @@
1+
import Duration from "@icholy/duration";
12
export { Duration } from "@icholy/duration";
3+
4+
export function durationToCborCustomDuration(duration: Duration) {
5+
const s = duration.seconds();
6+
const ns = duration.nanoseconds(); // need to calculate this separately...
7+
return ns > 0 ? [s, ns] : s > 0 ? [s] : [];
8+
}
9+
10+
export function cborCustomDurationToDuration([s, ns]: [number, number]) {
11+
s = s ?? 0;
12+
ns = ns ?? 0;
13+
console.log(ns);
14+
const ms = (s * 1000) + (ns / 1000000);
15+
return new Duration(ms);
16+
}

src/library/data/geometry.ts

+98
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import Decimal from "decimal.js";
2+
import { z } from "zod";
3+
4+
export class Geometry {}
5+
6+
export class GeometryPoint extends Geometry {
7+
readonly point: [Decimal, Decimal];
8+
9+
constructor(point: [number | Decimal, number | Decimal] | GeometryPoint) {
10+
super();
11+
point = point instanceof GeometryPoint ? point.point : point;
12+
this.point = [
13+
point[0] instanceof Decimal ? point[0] : new Decimal(point[0]),
14+
point[1] instanceof Decimal ? point[1] : new Decimal(point[1]),
15+
];
16+
}
17+
}
18+
19+
export class GeometryLine extends Geometry {
20+
readonly line: [GeometryPoint, GeometryPoint];
21+
22+
constructor(line: [GeometryPoint, GeometryPoint] | GeometryLine) {
23+
super();
24+
line = line instanceof GeometryLine ? line.line : line;
25+
this.line = [new GeometryPoint(line[0]), new GeometryPoint(line[1])];
26+
}
27+
}
28+
29+
export class GeometryPolygon extends Geometry {
30+
readonly polygon: [GeometryLine, GeometryLine, ...GeometryLine[]];
31+
32+
constructor(
33+
polygon:
34+
| [GeometryLine, GeometryLine, ...GeometryLine[]]
35+
| GeometryPolygon
36+
) {
37+
super();
38+
polygon =
39+
polygon instanceof GeometryPolygon ? polygon.polygon : polygon;
40+
this.polygon = polygon.map((line) => new GeometryLine(line)) as [
41+
GeometryLine,
42+
GeometryLine,
43+
...GeometryLine[]
44+
];
45+
}
46+
}
47+
48+
export class GeometryMultiPoint extends Geometry {
49+
readonly points: [GeometryPoint, ...GeometryPoint[]];
50+
51+
constructor(points: [GeometryPoint, ...GeometryPoint[]]) {
52+
super();
53+
points = points instanceof GeometryMultiPoint ? points.points : points;
54+
this.points = points.map((point) => new GeometryPoint(point)) as [
55+
GeometryPoint,
56+
...GeometryPoint[]
57+
];
58+
}
59+
}
60+
61+
export class GeometryMultiLine extends Geometry {
62+
readonly lines: [GeometryLine, ...GeometryLine[]];
63+
64+
constructor(lines: [GeometryLine, ...GeometryLine[]]) {
65+
super();
66+
lines = lines instanceof GeometryMultiLine ? lines.lines : lines;
67+
this.lines = lines.map((line) => new GeometryLine(line)) as [
68+
GeometryLine,
69+
...GeometryLine[]
70+
];
71+
}
72+
}
73+
74+
export class GeometryMultiPolygon extends Geometry {
75+
readonly polygons: [GeometryPolygon, ...GeometryPolygon[]];
76+
77+
constructor(polygons: [GeometryPolygon, ...GeometryPolygon[]]) {
78+
super();
79+
polygons =
80+
polygons instanceof GeometryMultiPolygon
81+
? polygons.polygons
82+
: polygons;
83+
84+
this.polygons = polygons.map(
85+
(polygon) => new GeometryPolygon(polygon)
86+
) as [GeometryPolygon, ...GeometryPolygon[]];
87+
}
88+
}
89+
90+
export class GeometryCollection extends Geometry {
91+
readonly collection: [Geometry, ...Geometry[]];
92+
93+
constructor(collection: [Geometry, ...Geometry[]]) {
94+
super();
95+
collection = collection instanceof GeometryCollection ? collection.collection : collection;
96+
this.collection = collection;
97+
}
98+
}

src/library/data/table.ts

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { z } from "zod";
2+
3+
export class Table<Tb extends string = string> {
4+
public readonly tb: Tb;
5+
6+
constructor(tb: Tb) {
7+
this.tb = z.string().parse(tb) as Tb;
8+
}
9+
10+
toJSON() {
11+
return {
12+
tb: this.tb,
13+
}
14+
}
15+
}

src/library/data/uuid.ts

+13
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,16 @@ export class Uuid {
99
this.uuid = uuid;
1010
}
1111
}
12+
13+
function fromBytes(bytes: ArrayBuffer) {
14+
return Array.from(bytes)
15+
.map((b) => ('00' + b.toString(16)).slice(-2))
16+
.join('')
17+
.replace(/(.{8})(.{4})(.{4})(.{4})(.{12})/, '$1-$2-$3-$4-$5');
18+
}
19+
20+
function toBytes(str: string) {
21+
return new Uint8Array((str.replace(/-/g, '').match(/.{2}/g) || []).map((b) =>
22+
parseInt(b, 16)
23+
)).buffer;
24+
}

test.ts

+39-5
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ import { Duration } from "./src/library/data/duration.ts";
33
import { Uuid } from "./src/library/data/uuid.ts";
44
import { Decimal } from "./src/library/data/decimal.ts";
55
import { Surreal } from "./src/index.ts";
6-
import { recordId } from "./src/library/orm/types.ts";
6+
import { encode } from "./src/library/data/cbor.ts";
7+
import { GeometryCollection, GeometryLine, GeometryPoint } from "./src/library/data/geometry.ts";
8+
// import { recordId } from "./src/library/orm/types.ts";
79

810
const surreal = new Surreal();
911
await surreal.connect('ws://127.0.0.1:8000/rpc');
@@ -14,7 +16,7 @@ await surreal.use({
1416

1517
// console.log(
1618
// await surreal.query<[unknown[]]>(
17-
// "[$d, $r, $c, $u, $dur, $none, NONE, $a, <string> $a]",
19+
// "[$d, $r, $c, $u, $dur, $none, NONE, $a, <string> $a, $nan, $geo]",
1820
// {
1921
// d: new Date(),
2022
// r: new RecordId('person', 'tobie'),
@@ -29,10 +31,42 @@ await surreal.use({
2931
// u: new Uuid(),
3032
// dur: new Duration('1d20m'),
3133
// none: undefined,
32-
// a: 9223372036854775807n
34+
// a: 9223372036854775807n,
35+
// nan: NaN,
36+
// geo: new GeometryCollection([
37+
// new GeometryPoint([1, 2]),
38+
// new GeometryLine([ new GeometryPoint([1, 2]), new GeometryPoint([3, 4]) ])
39+
// ])
3340
// }
3441
// )
3542
// )
3643

37-
const rid = recordId('test');
38-
console.log(rid.parse(new RecordId('tesst', 123) ))
44+
console.log(
45+
await surreal.query<[unknown, Duration]>(
46+
"$dur; 2d1ns",
47+
{
48+
dur: 123
49+
}
50+
).then(([_, r]) => r.toString()),
51+
new Duration("2d1ns").toString()
52+
)
53+
54+
// function bufferToHex (buffer: ArrayBuffer) {
55+
// return [...new Uint8Array (buffer)]
56+
// .map (b => b.toString (16).padStart (2, "0"))
57+
// .join ("");
58+
// }
59+
60+
// console.log(bufferToHex(encode(new RecordId('recording', [
61+
// new Date(),
62+
// 'London',
63+
// new Uuid(),
64+
// {
65+
// temparature: new Decimal("10.00003")
66+
// }
67+
// ]))))
68+
69+
// const rid = recordId('test');
70+
// console.log(rid.parse(new RecordId('tesst', 123) ))
71+
72+
// console.log()

0 commit comments

Comments
 (0)