Skip to content

Commit 9009520

Browse files
committed
feat(custom serdes): allow Promise serialization
1 parent 06ab9eb commit 9009520

File tree

4 files changed

+33
-9
lines changed

4 files changed

+33
-9
lines changed

Diff for: .changeset/nasty-planes-jam.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@
55
FEAT: `useSerialized$(fn)` and `createSerialized$(fn)` allow serializing custom objects. You must provide a
66
function that converts the custom object to a serializable one via the `[SerializerSymbol]`
77
property, and then provide `use|createSerialized$(fn)` with the function that creates the custom object
8-
from the serialized data. This will lazily create the value when needed.
8+
from the serialized data. This will lazily create the value when needed. Note that the serializer
9+
function may return a Promise, which will be awaited. The deserializer must not return a Promise.

Diff for: packages/qwik/src/core/shared/error/error.ts

+2
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ export const codeToText = (code: number, ...parts: any[]): string => {
5757
'WrappedSignal is read-only', // 49
5858
'SsrError: Promises not expected here.', // 50
5959
'Attribute value is unsafe for SSR', // 51
60+
'SerializerSymbol function returned rejected promise', // 52
6061
];
6162
let text = MAP[code] ?? '';
6263
if (parts.length) {
@@ -128,6 +129,7 @@ export const enum QError {
128129
wrappedReadOnly = 49,
129130
promisesNotExpected = 50,
130131
unsafeAttr = 51,
132+
serializerSymbolRejectedPromise = 52,
131133
}
132134

133135
export const qError = (code: number, errorMessageArgs: any[] = []): Error => {

Diff for: packages/qwik/src/core/shared/shared-serialization.ts

+14-7
Original file line numberDiff line numberDiff line change
@@ -1053,13 +1053,11 @@ function serialize(serializationContext: SerializationContext): void {
10531053
output(TypeIds.Constant, Constants.EMPTY_ARRAY);
10541054
} else if (value === EMPTY_OBJ) {
10551055
output(TypeIds.Constant, Constants.EMPTY_OBJ);
1056+
} else if (value === null) {
1057+
output(TypeIds.Constant, Constants.Null);
10561058
} else {
10571059
depth++;
1058-
if (value === null) {
1059-
output(TypeIds.Constant, Constants.Null);
1060-
} else {
1061-
writeObjectValue(value, idx);
1062-
}
1060+
writeObjectValue(value, idx);
10631061
depth--;
10641062
}
10651063
} else if (typeof value === 'string') {
@@ -1148,8 +1146,17 @@ function serialize(serializationContext: SerializationContext): void {
11481146
}
11491147
output(Array.isArray(storeTarget) ? TypeIds.StoreArray : TypeIds.Store, out);
11501148
}
1151-
} else if (SerializerSymbol in value && typeof value[SerializerSymbol] === 'function') {
1152-
const result = serializationResults.get(value);
1149+
} else if (isSerializerObj(value)) {
1150+
let result = serializationResults.get(value);
1151+
// special case: we unwrap Promises
1152+
if (isPromise(result)) {
1153+
const promiseResult = promiseResults.get(result)!;
1154+
if (!promiseResult[0]) {
1155+
console.error(promiseResult[1]);
1156+
throw qError(QError.serializerSymbolRejectedPromise);
1157+
}
1158+
result = promiseResult[1];
1159+
}
11531160
depth--;
11541161
writeValue(result, idx);
11551162
depth++;

Diff for: packages/qwik/src/core/shared/shared-serialization.unit.ts

+15-1
Original file line numberDiff line numberDiff line change
@@ -863,7 +863,7 @@ describe('shared-serialization', () => {
863863
(27 chars)"
864864
`);
865865
});
866-
it('should not use SerializeSymbol if not function', async () => {
866+
it('should not use SerializerSymbol if not function', async () => {
867867
const obj = { hi: 'orig', [SerializerSymbol]: 'hey' };
868868
const state = await serialize(obj);
869869
expect(dumpState(state)).toMatchInlineSnapshot(`
@@ -875,6 +875,20 @@ describe('shared-serialization', () => {
875875
(22 chars)"
876876
`);
877877
});
878+
it('should unwrap promises from SerializerSymbol', async () => {
879+
class Foo {
880+
hi = 'promise';
881+
async [SerializerSymbol]() {
882+
return Promise.resolve(this.hi);
883+
}
884+
}
885+
const state = await serialize(new Foo());
886+
expect(dumpState(state)).toMatchInlineSnapshot(`
887+
"
888+
0 String "promise"
889+
(13 chars)"
890+
`);
891+
});
878892
});
879893
});
880894

0 commit comments

Comments
 (0)