feat(serializer): add aot serializer

This commit is contained in:
2026-05-21 09:11:51 +00:00
parent 6f417ba514
commit f327e64a6a
30 changed files with 6720 additions and 0 deletions
+79
View File
@@ -0,0 +1,79 @@
import { test, expect } from 'vitest';
import {
clearRegistry,
defineSchema,
deserialize,
registerClass,
Serializable,
serialize,
} from '../plugin/index.ts';
test('class with [Serializable] static schema registers and round-trips', () => {
clearRegistry();
class Order {
id!: number;
price!: number;
qty!: number;
side!: 'buy' | 'sell';
static [Serializable] = defineSchema('OrderClass', (s) => ({
id: s.u53,
price: s.f64,
qty: s.f64,
side: s.enum(['buy', 'sell'] as const),
}));
}
const codec = registerClass(Order);
const v = { id: 42, price: 100.5, qty: 1.5, side: 'buy' as const };
const bytes = serialize(v, codec);
const decoded = deserialize<typeof v>(bytes);
expect(decoded).toEqual(v);
});
test('registerClass caches by constructor', () => {
clearRegistry();
class A {
static [Serializable] = defineSchema('AClass', (s) => ({ x: s.u8 }));
}
const c1 = registerClass(A);
const c2 = registerClass(A);
expect(c1).toBe(c2);
});
test('registerClass throws for class missing [Serializable]', () => {
clearRegistry();
class B {}
expect(() => registerClass(B)).toThrow(/\[Serializable\] schema/);
});
test('Symbol.serializable is shared across module boundaries via Symbol.for', () => {
const looked = Symbol.for('@perf/serializable');
expect(looked).toBe(Serializable);
});
test('codec.id is deterministic for the schema name', () => {
clearRegistry();
const A = defineSchema('SameName', (s) => ({ x: s.u8 }));
clearRegistry();
const codecA = registerClass(
class extends Object {
static [Serializable] = A;
},
);
clearRegistry();
const codecB = registerClass(
class extends Object {
static [Serializable] = A;
},
);
expect(codecA.id).toBe(codecB.id);
});