Serialize and restore compiled schema#115
Serialize and restore compiled schema#115Ahmedmhmud wants to merge 5 commits intohyperjump-io:mainfrom
Conversation
jdesrosiers
left a comment
There was a problem hiding this comment.
I think using replacer/reviver for plugins isn't the best approach. The pattern matching approach used by isPlugin feels clunky. Since those plugins are always in a known location, I think it makes more sense to convert them before serialization.
Converting RegEpx is different because they could be anywhere and determining if something is a RegExp is trivial.
Plugins isn't actually a Set, it's an array. That was a error in the type that has already been corrected. Rebase your branch to get the correct type.
The exported functions should be a the top of the file with helper functions below.
Using __type isn't going to work. If someone uses that as a property name in their schema, this approach could break. You'll need to pick a key that won't collide with anything that could end up in a schema. I suggest a UUID. That way it's effectively impossible for a user to accidentally create a schema with a conflict.
|
Converted plugin objects in plugins to UUID-marked references before serialization to avoid shape-matching and property collisions. |
| const REGEXP_MARKER = "a6d8f3e1-9b2c-4f7a-8d5b-1c2e3f4a5b6c"; | ||
|
|
||
| export const serialize = (compiledSchema) => { | ||
| const ast = compiledSchema?.ast ? { ...compiledSchema.ast } : undefined; |
There was a problem hiding this comment.
ast isn't optional on compiledSchema.
| if (ast && (Array.isArray(ast.plugins) || ast.plugins instanceof Set)) { | ||
| ast.plugins = [...ast.plugins].map((plugin) => { |
There was a problem hiding this comment.
ast.plugins will never be a Set.
| if (!plugin?.id) { | ||
| throw Error("Cannot serialize plugin without id"); | ||
| } | ||
| return { [PLUGIN_MARKER]: true, id: plugin.id }; |
There was a problem hiding this comment.
The PLUGIN_MARKER isn't necessary because we're not using replacer/reviver for this part. You should be able to just replace the plugin with its id.
| // Legacy support: old tests/serializations used a __type marker. | ||
| if (value.__type === "RegExp") { | ||
| return new RegExp(value.source, value.flags); | ||
| } |
There was a problem hiding this comment.
__type was introduced in this PR. No one is depending on it. This looks like a mistake an AI coding tool would make. If you use those tools make sure you check everything they do and make sure it makes sense.
| if (value[PLUGIN_MARKER]) { | ||
| return resolvePlugin(value.id, options); | ||
| } |
There was a problem hiding this comment.
Don't restore plugins in the reviver. Do it as a separate step like in serialize.
| if (value.__type === "Plugin") { | ||
| return resolvePlugin(value.id, options); | ||
| } | ||
|
|
||
| if (value.__type === "PluginSet") { | ||
| // Legacy PluginSet: restore to an array of plugin objects | ||
| return (value.values || []).map((p) => resolvePlugin(p.id, options)); | ||
| } |
|
Thanks for heads up, I will update it. |
This PR adds experimental helpers to serialize and deserialize
compiledSchemaso applications can persist and restore compiled validation logic more easily. It handles the non-JSON parts of a compiled schema by preservingRegExpvalues and restoringEvaluationPluginsby ID, first from built-in plugins and then from an optionalpluginsByIdmap for custom plugins, with a clear error when a plugin cannot be found. It also wires the helpers into the experimental API surface and includes focused tests and error cases.